11import { z } from "zod"
22import type { Logger } from "./logger"
3- import type { StateManager , SessionStats } from "./state"
43import { buildAnalysisPrompt } from "./prompt"
54import { selectModel , extractModelFromSession } from "./model-selector"
65import { estimateTokensBatch , formatTokenCount } from "./tokenizer"
76import { detectDuplicates , extractParameterKey } from "./deduplicator"
87
8+ export interface SessionStats {
9+ totalToolsPruned : number
10+ totalTokensSaved : number
11+ }
12+
913export class Janitor {
1014 constructor (
1115 private client : any ,
12- private stateManager : StateManager ,
16+ private prunedIdsState : Map < string , string [ ] > ,
17+ private statsState : Map < string , SessionStats > ,
1318 private logger : Logger ,
1419 private toolParametersCache : Map < string , any > ,
1520 private protectedTools : string [ ] ,
@@ -114,7 +119,7 @@ export class Janitor {
114119 }
115120
116121 // Get already pruned IDs to filter them out
117- const alreadyPrunedIds = await this . stateManager . get ( sessionID )
122+ const alreadyPrunedIds = this . prunedIdsState . get ( sessionID ) ?? [ ]
118123 const unprunedToolCallIds = toolCallIds . filter ( id => ! alreadyPrunedIds . includes ( id ) )
119124
120125 // If there are no unpruned tool calls, skip analysis
@@ -269,7 +274,12 @@ export class Janitor {
269274 const tokensSaved = await this . calculateTokensSaved ( finalNewlyPrunedIds , toolOutputs )
270275
271276 // Accumulate session stats (for showing cumulative totals in UI)
272- const sessionStats = await this . stateManager . addStats ( sessionID , finalNewlyPrunedIds . length , tokensSaved )
277+ const currentStats = this . statsState . get ( sessionID ) ?? { totalToolsPruned : 0 , totalTokensSaved : 0 }
278+ const sessionStats : SessionStats = {
279+ totalToolsPruned : currentStats . totalToolsPruned + finalNewlyPrunedIds . length ,
280+ totalTokensSaved : currentStats . totalTokensSaved + tokensSaved
281+ }
282+ this . statsState . set ( sessionID , sessionStats )
273283
274284 if ( this . pruningMode === "auto" ) {
275285 await this . sendAutoModeNotification (
@@ -296,7 +306,7 @@ export class Janitor {
296306 // ============================================================
297307 // Merge newly pruned IDs with existing ones (using expanded IDs)
298308 const allPrunedIds = [ ...new Set ( [ ...alreadyPrunedIds , ...finalPrunedIds ] ) ]
299- await this . stateManager . set ( sessionID , allPrunedIds )
309+ this . prunedIdsState . set ( sessionID , allPrunedIds )
300310
301311 // Log final summary
302312 // Format: "Pruned 5/5 tools (~4.2K tokens), 0 kept" or with breakdown if both duplicate and llm
@@ -318,9 +328,38 @@ export class Janitor {
318328 /**
319329 * Helper function to shorten paths for display
320330 */
321- private shortenPath ( path : string ) : string {
322- // Replace home directory with ~
331+ private shortenPath ( input : string ) : string {
332+ // Handle compound strings like: "pattern" in /absolute/path
333+ // Extract and shorten just the path portion
334+ const inPathMatch = input . match ( / ^ ( .+ ) i n ( .+ ) $ / )
335+ if ( inPathMatch ) {
336+ const prefix = inPathMatch [ 1 ]
337+ const pathPart = inPathMatch [ 2 ]
338+ const shortenedPath = this . shortenSinglePath ( pathPart )
339+ return `${ prefix } in ${ shortenedPath } `
340+ }
341+
342+ return this . shortenSinglePath ( input )
343+ }
344+
345+ /**
346+ * Shorten a single path string
347+ */
348+ private shortenSinglePath ( path : string ) : string {
323349 const homeDir = require ( 'os' ) . homedir ( )
350+
351+ // Strip working directory FIRST (before ~ replacement) for cleaner relative paths
352+ if ( this . workingDirectory ) {
353+ if ( path . startsWith ( this . workingDirectory + '/' ) ) {
354+ return path . slice ( this . workingDirectory . length + 1 )
355+ }
356+ // Exact match (the directory itself)
357+ if ( path === this . workingDirectory ) {
358+ return '.'
359+ }
360+ }
361+
362+ // Replace home directory with ~
324363 if ( path . startsWith ( homeDir ) ) {
325364 path = '~' + path . slice ( homeDir . length )
326365 }
@@ -331,21 +370,18 @@ export class Janitor {
331370 return `${ nodeModulesMatch [ 1 ] } /${ nodeModulesMatch [ 2 ] } `
332371 }
333372
334- // Strip working directory to show relative paths
373+ // Try matching against ~ version of working directory (for paths already with ~)
335374 if ( this . workingDirectory ) {
336- // Try to match against the absolute working directory first
337- if ( path . startsWith ( this . workingDirectory + '/' ) ) {
338- return path . slice ( this . workingDirectory . length + 1 )
339- }
340-
341- // Also try matching against ~ version of working directory
342375 const workingDirWithTilde = this . workingDirectory . startsWith ( homeDir )
343376 ? '~' + this . workingDirectory . slice ( homeDir . length )
344377 : null
345378
346379 if ( workingDirWithTilde && path . startsWith ( workingDirWithTilde + '/' ) ) {
347380 return path . slice ( workingDirWithTilde . length + 1 )
348381 }
382+ if ( workingDirWithTilde && path === workingDirWithTilde ) {
383+ return '.'
384+ }
349385 }
350386
351387 return path
0 commit comments