fix(build): add transcript-watcher.cjs build target (closes #2450) (#2592)
* fix(build): add transcript-watcher.cjs build target (closes #2450)
src/npx-cli/commands/runtime.ts:230 hard-codes a reference to
plugin/scripts/transcript-watcher.cjs, but scripts/build-hooks.js
never compiled this entry. The fallback `spawnBunWorkerCommand`
path silently no-ops on systems where bun isn't reachable, so
`claude-mem transcript watch` is effectively dead in the published
npm bundle.
This adds:
- A thin entry (src/services/transcripts/transcript-watcher-entry.ts)
that dispatches process.argv to the existing runTranscriptCommand
function in cli.ts (which until now had no caller in the build).
- A new esbuild target in build-hooks.js modeled on context-generator
(Node 18 CJS, minified, bun:sqlite external) plus the standard
stripHardcodedDirname pass and chmod 0755.
- Updated build summary so the new artifact shows up in the
"compiled successfully" footer.
The compiled bundle is 137 KB. `node plugin/scripts/transcript-watcher.cjs`
prints the usage line and exits 1 as expected; `... validate`,
`... init`, and `... watch` all route correctly.
No behavior change on platforms that previously had a working bun
fallback - the explicit path now just wins the existsSync check.
* fix(build): externalize zod + add bundle-size guard for transcript-watcher
Addresses Greptile review on #2592:
1. Add `zod` to the external list. worker-service and
server-beta-service both externalize zod and rely on
plugin/package.json to provide it at runtime; the watcher
build was inconsistent. The current processor.ts chain
does not actually pull in zod (bundle size unchanged
at 137.64 KB), but the external entry is defensive —
any future import in src/services/transcripts/ that
touches zod will now resolve against the plugin's
declared dependency rather than getting silently
inlined (and potentially creating a duplicate-instance
hazard).
2. Add a 200 KB size guard mirroring the 600 KB ceiling
on mcp-server.cjs. The watcher is meant to be a thin
file-tail loop; if a transitive import ever drags a
heavy module (worker-service, an SDK runtime) into
the chain, this catches it at build time with an
informative error pointing at processor.ts / watcher.ts.
No functional change to the runtime artifact (size
identical, behavior identical). Both follow-ups recommended
by Greptile.
2026-06-06 14:21:25 +08:00
#!/usr/bin/env bun
2026-08-02 10:14:45 -07:00
"use strict" ; var tn = Object . create ; var Rt = Object . defineProperty ; var rn = Object . getOwnPropertyDescriptor ; var nn = Object . getOwnPropertyNames ; var on = Object . getPrototypeOf , sn = Object . prototype . hasOwnProperty ; var Se = ( r , e , t ) => ( ) => { if ( t ) throw t [ 0 ] ; try { return r && ( e = r ( r = 0 ) ) , e } catch ( n ) { throw t = [ n ] , n } } ; var an = ( r , e , t , n ) => { if ( e && typeof e == "object" || typeof e == "function" ) for ( let o of nn ( e ) ) ! sn . call ( r , o ) && o !== t && Rt ( r , o , { get : ( ) => e [ o ] , enumerable : ! ( n = rn ( e , o ) ) || n . enumerable } ) ; return r } ; var X = ( r , e , t ) => ( t = r != null ? tn ( on ( r ) ) : { } , an ( e || ! r || ! r . _ _esModule ? Rt ( t , "default" , { value : r , enumerable : ! 0 } ) : t , r ) ) ; function Ze ( r ) { return ( cn ? ? process . stderr . write . bind ( process . stderr ) ) ( r ) } function ee ( r ) { Ze ( r ) } function At ( r , e = { } ) { he && he . length > 0 && ( Ze ( he . join ( "" ) ) , he = [ ] ) , Ze ( r . endsWith ( `
chore: remove over-engineering findings from repo-wide ponytail audit (wave 1)
Cuts verified dead code, duplication, and speculative abstractions across
14 disjoint areas of the tree: sqlite layer, worker HTTP routes, search
pipeline, server, chroma sync, viewer UI, npx-cli, MCP server, shared/utils,
telemetry/infra, integrations, scripts, tests, and stale plans/evals docs.
Also unifies the Chroma search pipeline onto SearchOrchestrator/strategies
and ports dual-project (merged_into_project) scoping plus dateRange
filtering into ChromaSearchStrategy, which corpus builds need but had
silently lost.
Full audit trail: 65-agent verification workflow wf_160a7862-1a6, 14-agent
execution wf_c7e48c0f-164, 6-agent fixup wf_865f86a4-c6b.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-02 20:49:27 -07:00
` ) ? r : ` ${ r }
2026-08-02 10:14:45 -07:00
` ) , e . skipExit || process . exit ( 2 ) } var cn , he , ye = Se ( ( ) => { "use strict" ; cn = null ; he = null } ) ; function un ( r ) { return r . replace ( /^\uFEFF/ , "" ) } function K ( r ) { return JSON . parse ( un ( r ) ) } function dn ( r ) { ( 0 , p . existsSync ) ( r ) || ( 0 , p . mkdirSync ) ( r , { recursive : ! 0 } ) } function Te ( r , e ) { let t = r ; try { if ( ( 0 , p . lstatSync ) ( r ) . isSymbolicLink ( ) ) try { t = ( 0 , p . realpathSync ) ( r ) } catch ( d ) { let u = d instanceof Error ? d : new Error ( String ( d ) ) ; ee ( ` claude-mem: realpathSync failed for ${ r } , resolving symlink manually: ${ u . message }
2026-07-23 11:18:55 -07:00
` ) ; let E = ( 0 , p . readlinkSync ) ( r ) ; t = ( 0 , D . resolve ) ( ( 0 , D . dirname ) ( r ) , E ) } } catch ( d ) { let u = d . code ; if ( u !== "ENOENT" && u !== "ENOTDIR" ) throw d } dn ( ( 0 , D . dirname ) ( t ) ) ; let n = ( 0 , D . dirname ) ( t ) , o = ( 0 , D . basename ) ( t ) , i = ( 0 , D . join ) ( n , ` . ${ o } . ${ process . pid } . ${ ( 0 , kt . randomBytes ) ( 6 ) . toString ( "hex" ) } .tmp ` ) , s = Buffer . from ( JSON . stringify ( e , null , 2 ) + `
` , "utf-8" ) , c ; try { c = ( 0 , p . statSync ) ( t ) . mode & 511 } catch { } let l ; try { l = c !== void 0 ? ( 0 , p . openSync ) ( i , "w" , c ) : ( 0 , p . openSync ) ( i , "w" ) ; let d = 0 ; for ( ; d < s . length ; ) { let u = ( 0 , p . writeSync ) ( l , s , d , s . length - d ) ; if ( u === 0 ) throw new Error ( ` writeSync stalled at ${ d } / ${ s . length } bytes ` ) ; d += u } if ( ( 0 , p . fsyncSync ) ( l ) , ( 0 , p . closeSync ) ( l ) , l = void 0 , ( 0 , p . renameSync ) ( i , t ) , ! ln ) { let u ; try { u = ( 0 , p . openSync ) ( n , "r" ) , ( 0 , p . fsyncSync ) ( u ) } catch ( E ) { let g = E instanceof Error ? E : new Error ( String ( E ) ) ; ee ( ` claude-mem: directory fsync failed for ${ n } : ${ g . message }
2026-08-02 10:14:45 -07:00
` ) } finally { if ( u !== void 0 ) try { ( 0 , p . closeSync ) ( u ) } catch { } } } } catch ( d ) { if ( l !== void 0 ) try { ( 0 , p . closeSync ) ( l ) } catch { } try { ( 0 , p . unlinkSync ) ( i ) } catch { } throw d } } var p , kt , D , ln , ce = Se ( ( ) => { "use strict" ; p = require ( "fs" ) , kt = require ( "crypto" ) , D = require ( "path" ) ; ye ( ) ; ln = process . platform === "win32" } ) ; function pn ( ) { return typeof _ _dirname < "u" ? _ _dirname : ( 0 , m . dirname ) ( ( 0 , Ot . fileURLToPath ) ( En . url ) ) } function It ( r ) { return typeof r != "string" || r . length === 0 ? r : r === "~" ? ( 0 , le . homedir ) ( ) : r . startsWith ( "~/" ) ? ( 0 , m . join ) ( ( 0 , le . homedir ) ( ) , r . slice ( 2 ) ) : r } function te ( ) { if ( process . env . CLAUDE _MEM _DATA _DIR ) return It ( process . env . CLAUDE _MEM _DATA _DIR ) ; let r = ( 0 , m . join ) ( ( 0 , le . homedir ) ( ) , ".claude-mem" ) , e = ( 0 , m . join ) ( r , "settings.json" ) ; try { if ( ( 0 , ue . existsSync ) ( e ) ) { let t = K ( ( 0 , ue . readFileSync ) ( e , "utf-8" ) ) , n = t . env ? ? t ; if ( n . CLAUDE _MEM _DATA _DIR ) return It ( n . CLAUDE _MEM _DATA _DIR ) } } catch { } return r } var m , le , ue , Ot , En , Xo , S , fn , we , mn , re , zo , et , gn , y , w = Se ( ( ) => { "use strict" ; m = require ( "path" ) , le = require ( "os" ) , ue = require ( "fs" ) , Ot = require ( "url" ) ; ce ( ) ; En = { } ; Xo = pn ( ) ; S = te ( ) , fn = process . env . CLAUDE _CONFIG _DIR || ( 0 , m . join ) ( ( 0 , le . homedir ) ( ) , ".claude" ) , we = ( 0 , m . join ) ( fn , "plugins" , "marketplaces" , "thedotmack" ) , mn = ( 0 , m . join ) ( S , "logs" ) , re = ( 0 , m . join ) ( S , "settings.json" ) , zo = ( 0 , m . join ) ( S , "claude-mem.db" ) , et = ( 0 , m . join ) ( S , "observer-sessions" ) , gn = ( 0 , m . basename ) ( et ) , y = { dataDir : ( ) => S , workerPid : ( ) => ( 0 , m . join ) ( S , "worker.pid" ) , serverPid : ( ) => ( 0 , m . join ) ( S , ".server-beta.pid" ) , serverPort : ( ) => ( 0 , m . join ) ( S , ".server-beta.port" ) , serverRuntime : ( ) => ( 0 , m . join ) ( S , ".server-beta.runtime.json" ) , settings : ( ) => ( 0 , m . join ) ( S , "settings.json" ) , database : ( ) => ( 0 , m . join ) ( S , "claude-mem.db" ) , chroma : ( ) => ( 0 , m . join ) ( S , "chroma" ) , combinedCerts : ( ) => ( 0 , m . join ) ( S , "combined_certs.pem" ) , transcriptsConfig : ( ) => ( 0 , m . join ) ( S , "transcript-watch.json" ) , transcriptsState : ( ) => ( 0 , m . join ) ( S , "transcript-watch-state.json" ) , corpora : ( ) => ( 0 , m . join ) ( S , "corpora" ) , supervisorRegistry : ( ) => ( 0 , m . join ) ( S , "supervisor.json" ) , envFile : ( ) => ( 0 , m . join ) ( S , ".env" ) , logsDir : ( ) => mn } } ) ; var U , Pt , nt , rt , ot , a , f = Se ( ( ) => { "use strict" ; U = require ( "fs" ) , Pt = require ( "path" ) ; w ( ) ; ye ( ) ; ce ( ) ; nt = ( i => ( i [ i . DEBUG = 0 ] = "DEBUG" , i [ i . INFO = 1 ] = "INFO" , i [ i . WARN = 2 ] = "WARN" , i [ i . ERROR = 3 ] = "ERROR" , i [ i . SILENT = 4 ] = "SILENT" , i ) ) ( nt || { } ) , rt = null , ot = class { level = null ; useColor ; logFilePath = null ; logFileInitialized = ! 1 ; constructor ( ) { this . useColor = process . stdout . isTTY ? ? ! 1 } ensureLogFileInitialized ( ) { if ( ! this . logFileInitialized ) { this . logFileInitialized = ! 0 ; try { let e = y . logsDir ( ) ; ( 0 , U . existsSync ) ( e ) || ( 0 , U . mkdirSync ) ( e , { recursive : ! 0 } ) ; let t = new Date ( ) . toISOString ( ) . split ( "T" ) [ 0 ] ; this . logFilePath = ( 0 , Pt . join ) ( e , ` claude-mem- ${ t } .log ` ) } catch ( e ) { console . error ( "[LOGGER] Failed to initialize log file:" , e instanceof Error ? e . message : String ( e ) ) , this . logFilePath = null } } } getLevel ( ) { if ( this . level === null ) try { let e = y . settings ( ) ; if ( ( 0 , U . existsSync ) ( e ) ) { let t = ( 0 , U . readFileSync ) ( e , "utf-8" ) , o = ( K ( t ) . CLAUDE _MEM _LOG _LEVEL || "INFO" ) . toUpperCase ( ) ; this . level = nt [ o ] ? ? 1 } else this . level = 1 } catch ( e ) { console . error ( "[LOGGER] Failed to load log level from settings:" , e instanceof Error ? e . message : String ( e ) ) , this . level = 1 } return this . level } formatData ( e ) { if ( e == null ) return "" ; if ( typeof e == "string" ) return e ; if ( typeof e == "number" || typeof e == "boolean" ) return e . toString ( ) ; if ( typeof e == "object" ) { if ( e instanceof Error ) return this . getLevel ( ) === 0 ? ` ${ e . message }
2026-07-23 11:18:55 -07:00
${ e . stack } ` : e . message ; if ( Array . isArray ( e ) ) return ` [ ${ e . length } items] ` ; let t = Object . keys ( e ) ; return t . length === 0 ? "{}" : t . length <= 3 ? JSON . stringify ( e ) : ` { ${ t . length } keys: ${ t . slice ( 0 , 3 ) . join ( ", " ) } ...} ` } return String ( e ) } formatTool ( e , t ) { if ( ! t ) return e ; let n = t ; if ( typeof t == "string" ) try { n = JSON . parse ( t ) } catch { n = t } if ( e === "Bash" && n . command ) return ` ${ e } ( ${ n . command } ) ` ; if ( n . file _path ) return ` ${ e } ( ${ n . file _path } ) ` ; if ( n . notebook _path ) return ` ${ e } ( ${ n . notebook _path } ) ` ; if ( e === "Glob" && n . pattern ) return ` ${ e } ( ${ n . pattern } ) ` ; if ( e === "Grep" && n . pattern ) return ` ${ e } ( ${ n . pattern } ) ` ; if ( n . url ) return ` ${ e } ( ${ n . url } ) ` ; if ( n . query ) return ` ${ e } ( ${ n . query } ) ` ; if ( e === "Task" ) { if ( n . subagent _type ) return ` ${ e } ( ${ n . subagent _type } ) ` ; if ( n . description ) return ` ${ e } ( ${ n . description } ) ` } return e === "Skill" && n . skill ? ` ${ e } ( ${ n . skill } ) ` : e === "LSP" && n . operation ? ` ${ e } ( ${ n . operation } ) ` : e } formatTimestamp ( e ) { let t = e . getFullYear ( ) , n = String ( e . getMonth ( ) + 1 ) . padStart ( 2 , "0" ) , o = String ( e . getDate ( ) ) . padStart ( 2 , "0" ) , i = String ( e . getHours ( ) ) . padStart ( 2 , "0" ) , s = String ( e . getMinutes ( ) ) . padStart ( 2 , "0" ) , c = String ( e . getSeconds ( ) ) . padStart ( 2 , "0" ) , l = String ( e . getMilliseconds ( ) ) . padStart ( 3 , "0" ) ; return ` ${ t } - ${ n } - ${ o } ${ i } : ${ s } : ${ c } . ${ l } ` } log ( e , t , n , o , i ) { if ( e < this . getLevel ( ) ) return ; this . ensureLogFileInitialized ( ) ; let s = this . formatTimestamp ( new Date ) , c = nt [ e ] . padEnd ( 5 ) , l = t . padEnd ( 6 ) , d = "" ; o ? . correlationId ? d = ` [ ${ o . correlationId } ] ` : o ? . sessionId && ( d = ` [session- ${ o . sessionId } ] ` ) ; let u = "" ; if ( i != null ) if ( i instanceof Error ) u = this . getLevel ( ) === 0 ? `
chore: remove over-engineering findings from repo-wide ponytail audit (wave 1)
Cuts verified dead code, duplication, and speculative abstractions across
14 disjoint areas of the tree: sqlite layer, worker HTTP routes, search
pipeline, server, chroma sync, viewer UI, npx-cli, MCP server, shared/utils,
telemetry/infra, integrations, scripts, tests, and stale plans/evals docs.
Also unifies the Chroma search pipeline onto SearchOrchestrator/strategies
and ports dual-project (merged_into_project) scoping plus dateRange
filtering into ChromaSearchStrategy, which corpus builds need but had
silently lost.
Full audit trail: 65-agent verification workflow wf_160a7862-1a6, 14-agent
execution wf_c7e48c0f-164, 6-agent fixup wf_865f86a4-c6b.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-02 20:49:27 -07:00
${ i . message }
2026-07-23 11:18:55 -07:00
${ i . stack } ` : ` ${ i . message } ` ; else if ( this . getLevel ( ) === 0 && typeof i == "object" ) try { u = `
2026-08-02 10:14:45 -07:00
` + JSON . stringify ( i , null , 2 ) } catch { u = " " + this . formatData ( i ) } else u = " " + this . formatData ( i ) ; let E = "" ; if ( o ) { let { sessionId : M , memorySessionId : _ , correlationId : Y , ... _e } = o ; Object . keys ( _e ) . length > 0 && ( E = ` { ${ Object . entries ( _e ) . map ( ( [ q , en ] ) => ` ${ q } = ${ en } ` ) . join ( ", " ) } } ` ) } let g = ` [ ${ s } ] [ ${ c } ] [ ${ l } ] ${ d } ${ n } ${ E } ${ u } ` ; if ( this . logFilePath ) try { ( 0 , U . appendFileSync ) ( this . logFilePath , g + `
` , "utf8" ) } catch ( M ) { let _ = M instanceof Error ? M : new Error ( String ( M ) ) ; ee ( ` [LOGGER] Failed to write to log file: ${ _ . message }
fix(worker): single version oracle — never rank plugin cache dirs by mtime
The 2026-07-22 restart storm: claude-mem had four resolvers that ranked
plugin cache dirs by directory mtime (worker-utils
cacheWorkerScriptCandidates, the inline node L() and Codex Windows
resolvers in hook-shell-template, and the POSIX `ls -dt` hook resolver),
while the staleness detector compared the live worker against the
marketplace package.json. When Claude Code stamped the superseded
13.11.0 cache dir with .orphaned_at, its mtime became newest, so every
recycle respawned 13.11.0 while hooks running 13.12.0 kept demanding a
restart — 485 respawns in ~2 hours, each spawning SDK and Chroma
children, until the host exhausted its process table.
The fix is an invariant, not a mitigation: one deterministic version
oracle shared by detection and respawn.
- resolveWorkerScript() ranks candidates by version descending (numeric
major.minor.patch, release ahead of prerelease at the same base,
reverse-lexical tiebreak) and skips .orphaned_at-stamped dirs; stable
sort keeps cache → marketplace → cwd precedence on ties.
- ensureWorkerRunning() resolves once and feeds the same result to both
checkVersionMatch(port, expectedVersion) and the lazy-spawn script, so
a mismatch can only ever resolve toward the version the respawn will
actually produce — the loop is structurally impossible.
- checkVersionMatch now takes the caller's expected version;
getInstalledPluginVersion (the second oracle) is deleted.
- All three generated bootstrap resolvers (mcp node launcher, Codex
Windows launcher, POSIX hook prelude) embed the identical ordering;
the POSIX variant uses a zero-padded sort key and balanced (pattern)
case forms (unmatched parens are a parser error inside $(...)).
- scripts/build-hooks.js gains --write-shell-templates, the regeneration
mode its hand-edit tripwire error message points at.
- Regression coverage: comparator/selection unit tests plus a functional
shell-matrix test that rebuilds the exact storm layout (orphaned old
dir with newest mtime) and asserts the new version wins.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 22:30:35 -07:00
${ _ . stack ? ? "" }
2026-07-22 23:57:24 -07:00
` ) } else ee ( g + `
2026-08-02 10:14:45 -07:00
` ) } debug ( e , t , n , o ) { this . log ( 0 , e , t , n , o ) } info ( e , t , n , o ) { this . log ( 1 , e , t , n , o ) } warn ( e , t , n , o ) { this . log ( 2 , e , t , n , o ) } setErrorSink ( e ) { rt = e } error ( e , t , n , o ) { this . log ( 3 , e , t , n , o ) , this . routeErrorToSink ( t , n , o ) } routeErrorToSink ( e , t , n ) { try { if ( ! rt || ! ( n instanceof Error ) ) return ; rt ( n ) } catch { } } dataIn ( e , t , n , o ) { this . info ( e , ` \u 2192 ${ t } ` , n , o ) } dataOut ( e , t , n , o ) { this . info ( e , ` \u 2190 ${ t } ` , n , o ) } success ( e , t , n , o ) { this . info ( e , ` \u 2713 ${ t } ` , n , o ) } failure ( e , t , n , o ) { this . error ( e , ` \u 2717 ${ t } ` , n , o ) } } , a = new ot } ) ; var N = require ( "fs" ) , tt = require ( "os" ) , de = require ( "path" ) ; w ( ) ; var ne = y . transcriptsConfig ( ) , Me = y . transcriptsState ( ) , _n = { version : 1 , schemas : { } , watches : [ ] , stateFile : Me } ; function Sn ( r ) { let e = typeof r . schema == "string" ? r . schema : r . schema ? . name ; if ( ! ( r . name === "codex" || e === "codex" ) || ! r . path ) return ! 1 ; let n = b ( r . path ) . replace ( /\\/g , "/" ) , o = ( 0 , de . join ) ( ( 0 , tt . homedir ) ( ) , ".codex" , "sessions" ) . replace ( /\\/g , "/" ) ; return n === ` ${ o } /**/*.jsonl ` } function Dt ( r ) { let e = typeof r . schema == "string" ? r . schema : r . schema ? . name , t = r . name === "codex" && ( ! e || e === "codex" ) ; return r . context ? . mode === "agents" && t && Sn ( r ) } function b ( r ) { return r && ( r . startsWith ( "~" ) ? ( 0 , de . join ) ( ( 0 , tt . homedir ) ( ) , r . slice ( 1 ) ) : r ) } function pe ( r = ne ) { let e = b ( r ) ; if ( ! ( 0 , N . existsSync ) ( e ) ) throw new Error ( ` Transcript watch config not found: ${ e } ` ) ; let t = ( 0 , N . readFileSync ) ( e , "utf-8" ) , n = JSON . parse ( t ) ; if ( ! n . version || ! n . watches ) throw new Error ( ` Invalid transcript watch config: ${ e } ` ) ; return n . stateFile || ( n . stateFile = Me ) , n } function Ce ( r = ne ) { let e = b ( r ) , t = ( 0 , de . dirname ) ( e ) ; ( 0 , N . existsSync ) ( t ) || ( 0 , N . mkdirSync ) ( t , { recursive : ! 0 } ) , ( 0 , N . writeFileSync ) ( e , JSON . stringify ( _n , null , 2 ) ) } var C = require ( "fs" ) , O = require ( "path" ) ; f ( ) ; var F = require ( "fs" ) , Lt = require ( "path" ) ; f ( ) ; function xt ( r ) { try { if ( ! ( 0 , F . existsSync ) ( r ) ) return { offsets : { } } ; let e = ( 0 , F . readFileSync ) ( r , "utf-8" ) , t = JSON . parse ( e ) ; return t . offsets ? t : { offsets : { } } } catch ( e ) { return a . warn ( "TRANSCRIPT" , "Failed to load watch state, starting fresh" , { statePath : r , error : e instanceof Error ? e . message : String ( e ) } ) , { offsets : { } } } } function Nt ( r , e ) { try { let t = ( 0 , Lt . dirname ) ( r ) ; ( 0 , F . existsSync ) ( t ) || ( 0 , F . mkdirSync ) ( t , { recursive : ! 0 } ) , ( 0 , F . writeFileSync ) ( r , JSON . stringify ( e , null , 2 ) ) } catch ( t ) { a . warn ( "TRANSCRIPT" , "Failed to save watch state" , { statePath : r , error : t instanceof Error ? t . message : String ( t ) } ) } } var Ee = X ( require ( "path" ) , 1 ) ; var T = X ( require ( "path" ) , 1 ) , h = require ( "fs" ) ; var st = require ( "node:child_process" ) ; function be ( r , e , t ) { return ( 0 , st . spawn ) ( r , e ? ? [ ] , { windowsHide : ! 0 , ... t } ) } var hn = new Set ( [ ".cmd" , ".bat" ] ) , yn = new Set ( [ ".exe" , ".com" ] ) , as = new Set ( [ ... yn , ... hn ] ) ; f ( ) ; var k = { HEALTH _CHECK : 3e3 , API _REQUEST : 3e4 , HOOK _READINESS _WAIT : 1e4 , POST _SPAWN _WAIT : 15e3 , READINESS _WAIT : 3e4 , PORT _IN _USE _WAIT : 3e3 , POWERSHELL _COMMAND : 1e4 , WINDOWS _MULTIPLIER : 1.5 } , B = { SUCCESS : 0 , BLOCKING _ERROR : 2 } ; function J ( r ) { return process . platform === "win32" ? Math . round ( r * k . WINDOWS _MULTIPLIER ) : r } var ve = require ( "fs" ) , it = require ( "path" ) , fe = require ( "os" ) ; ce ( ) ; var Tn = "security_alert" , v = class { static DEFAULTS = { CLAUDE _MEM _MODEL : "claude-haiku-4-5-20251001" , CLAUDE _MEM _CONTEXT _OBSERVATIONS : "50" , CLAUDE _MEM _WORKER _PORT : String ( 37700 + ( process . getuid ? . ( ) ? ? 77 ) % 100 ) , CLAUDE _MEM _WORKER _HOST : "127.0.0.1" , CLAUDE _MEM _API _TIMEOUT _MS : String ( J ( k . API _REQUEST ) ) , CLAUDE _MEM _SKIP _TOOLS : "ListMcpResourcesTool,SlashCommand,Skill,TodoWrite,AskUserQuestion" , CLAUDE _MEM _PROVIDER : "claude" , CLAUDE _MEM _CLAUDE _AUTH _METHOD : "subscription" , CLAUDE _MEM _GEMINI _API _KEY : "" , CLAUDE _MEM _GEMINI _MODEL : "gemini-flash-latest" , CLAUDE _MEM _GEMINI _RATE _LIMITING _ENABLED : "true" , CLAUDE _MEM _OPENROUTER _API _KEY : "" , CLAUDE _MEM _OPENROUTER _MODEL : "xiaomi/mimo-v2-flash:free" , CLAUDE _MEM _OPENROUTER _BASE _URL : "" , CLAUDE _MEM _OPENROUTER _SITE _URL : "" , CLAUDE _MEM _OPENROUTER _APP _NAME : "claude-mem" , CLAUDE _MEM _DATA _DIR : ( 0 , it . join ) ( ( 0 , fe . homedir ) ( ) , ".claude-mem" ) , CLAUDE _MEM _LOG _LEVEL : "INFO" , CLAUDE _MEM _PYTHON _VERSION : "3.13" , CLAUDE _CODE _PATH : "" , CLAUDE _MEM _MODE : "code" , CLAUDE _MEM _CONTEXT _SHOW _READ _TOKENS : "false" , CLAUDE _MEM _CONTEXT _SHOW _WORK _TOKENS : "false" , CLAUDE _MEM _CONTEXT _SHOW _SAVINGS _AMOUNT : "false" , CLAUDE _MEM _CONTEXT _SHOW _SAVINGS _PERCENT : "true" , CLAUDE _MEM _CONTEXT _FULL _COUNT : "0" , CLAUDE _MEM _CONTEXT _FULL _FIELD : "narrative" , CLAUDE _MEM _CONTEXT _SESSION _COUNT : "10" , CLAUDE _MEM _CON
2026-08-03 14:26:10 -07:00
` )}function Qt(){let r=gt();if(r?.installId)return r.installId;let e=(0,qt.randomUUID)();return Wn({installId:e,decidedAt:""}),e}var Hn=new Set(["version","os","os_version","is_wsl","arch","runtime","runtime_version","node_version","duration_ms","outcome","error_category","locale","is_ci","endpoint","ide","provider","runtime_mode","trigger","count","has_summary","is_update","install_method","interactive","bun_version","uv_version","claude_code_version","observation_count","session_count","timeline_depth_days","has_session_summary","obs_type_bugfix","obs_type_discovery","obs_type_decision","obs_type_refactor","obs_type_other","tokens_injected","tokens_saved_vs_naive","mode","search_strategy","observation_type","hook","compression_ms","tokens_input","tokens_output","compression_ratio","model","cost_usd","endpoint_class","db_observation_count","db_session_count","db_summary_count","db_project_count","db_size_mb","install_age_days","obs_count_7d","obs_count_30d","days_since_last_obs","result_count","chroma_available","fallback_reason","invalid_output_class","consecutive_invalid_outputs","respawn_triggered","abort_reason","previous_shutdown","previous_uptime_seconds","uptime_seconds","shutdown_reason","process_rss_mb","heap_used_mb","hook_type","error_mode","consecutive_failures","threshold_tripped","discovery_tokens","read_tokens","summary_count","prompt_count","project_count","backfilled","first_active_date","session_completed_count","session_failed_count","sessions_claude_count","sessions_codex_count","sessions_gemini_count","sessions_other_platform_count","subagent_obs_count","total_tokens_input","total_tokens_output","total_cost_usd","avg_duration_ms","avg_compression_ms","outcomes_ok","outcomes_error","outcomes_aborted","outcomes_invalid_output","top_model","window_start_ts","rollup_reason","window_seq","total_tokens","avg_tokens","observations_created","total_observations_injected","total_tokens_saved_vs_naive"]),Zt=200;function jn(r,e){if(!(!r||typeof r!="object"))for(let t of Object.keys(r)){if(!Hn.has(t))continue;let n=r[t];typeof n=="string"?e[t]=n.length>Zt?n.slice(0,Zt):n:(typeof n=="number"&&Number.isFinite(n)||typeof n=="boolean")&&(e[t]=n)}}function er(r){let e={};try{jn(r,e)}catch{}return e}var Et=X(require("os"),1);f();var $ n="13.13.1",Gn="phc_BKJAeNbpj932N9qEiU6qhutZEiu6LLfRpXfTbLM9MLaG",Yn="https://us.i.posthog.com";function tr(){return process.env.CLAUDE_MEM_TELEMETRY_KEY||Gn}function rr(){return process.env.CLAUDE_MEM_TELEMETRY_HOST||Yn}var Kn=["version","os","os_version","is_wsl","arch","runtime","locale","ide","provider","runtime_mode","install_method","claude_code_version","first_active_date","db_observation_count","db_session_count","db_summary_count","db_project_count","db_size_mb","install_age_days","obs_count_7d","obs_count_30d","days_since_last_obs"];function nr(r){let e={};for(let t of Kn)r[t]!==void 0&&(e[t]=r[t]);return e}function Bn(){if(process.platform!=="linux")return!1;try{return!!process.env.WSL_DISTRO_NAME||Et.default.release().toLowerCase().includes("microsoft")}catch(r){let e=r instanceof Error?r:new Error(String(r));return a.warn("SYSTEM","Telemetry: WSL detection failed; reporting is_wsl=false",void 0,e),!1}}function or(){return{version: $ n,os:process.platform,os_version:Et.default.release(),is_wsl:Bn(),arch:process.arch,runtime:process.versions.bun?"bun":"node",runtime_version:process.versions.bun??process.versions.node,node_version:process.versions.node,is_ci:!!process.env.CI,locale:Intl.DateTimeFormat().resolvedOptions().locale}}var Jn=2e3;async function sr(r,e,t){try{if(!zt(process.env,gt()))return;let n=er({...or(),...e??{}});if(t?.person?n. $ set=nr(n):n. $ process_person_profile=!1,process.env.CLAUDE_MEM_TELEMETRY_DEBUG==="1"){process.stderr.write("[telemetry] "+JSON.stringify({event:r,properties:n})+ `
2026-08-02 10:14:45 -07:00
` );return}let o=tr(),i=new AbortController,s=setTimeout(()=>i.abort(),Jn);try{await Vn(o,r,n,i.signal)}finally{clearTimeout(s)}}catch{}}async function Vn(r,e,t,n){await fetch( ` $ { rr ( ) } / capture / ` ,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({api_key:r,event:e,distinct_id:Qt(),properties:t}),signal:n})}var z=X(require("path"),1),ar=require("os"),j=require("fs"),St=require("child_process");f();f();f();w();var mi=y.dataDir(),gi=y.workerPid();function ir(r){return r?/(^|[ \\ /])bun( \. exe)? $ /i.test(r.trim()):!1}function Xn(r,e){let t=e==="win32"? ` where $ { r } ` : ` which $ { r } ` ,n;try{n=(0,St.execSync)(t,{stdio:["ignore","pipe","ignore"],encoding:"utf-8",windowsHide:!0})}catch(i){return i instanceof Error?a.debug("SYSTEM", ` Binary lookup failed for $ { r } ` ,{command:t},i):a.debug("SYSTEM", ` Binary lookup failed for $ { r } ` ,{command:t},new Error(String(i))),null}return n.split(/ \r ? \n /).map(i=>i.trim()).find(i=>i.length>0)||null}var _t;function cr(r={}){let e=Object.keys(r).length===0;if(e&&_t!==void 0)return _t;let t=zn(r);return e&&t!==null&&(_t=t),t}function zn(r){let e=r.platform??process.platform,t=r.execPath??process.execPath;if(ir(t))return t;let n=r.env??process.env,o=r.homeDirectory??(0,ar.homedir)(),i=r.pathExists??j.existsSync,s=r.lookupInPath??Xn,c=e==="win32"?[n.BUN,n.BUN_PATH,z.default.join(o,".bun","bin","bun.exe"),z.default.join(o,".bun","bin","bun"),n.USERPROFILE?z.default.join(n.USERPROFILE,".bun","bin","bun.exe"):void 0,n.LOCALAPPDATA?z.default.join(n.LOCALAPPDATA,"bun","bun.exe"):void 0,n.LOCALAPPDATA?z.default.join(n.LOCALAPPDATA,"bun","bin","bun.exe"):void 0]:[n.BUN,n.BUN_PATH,z.default.join(o,".bun","bin","bun"),"/usr/local/bin/bun","/opt/homebrew/bin/bun","/home/linuxbrew/.linuxbrew/bin/bun","/usr/bin/bun","/snap/bin/bun"];for(let l of c){let d=l?.trim();if(d&&(ir(d)&&i(d)||d.toLowerCase()==="bun"))return d}return s("bun",e)}f();w();function Qn(){return v.loadFromFile(re).CLAUDE_MEM_WORKER_HOST}function Zn(r){return r.startsWith("[")&&r.endsWith("]")?r:r.includes(":")? ` [ $ { r } ] ` :r}async function eo(r,e,t="GET"){let n=await fetch( ` http : //${Zn(Qn())}:${r}${e}`,{method:t}),o="";try{o=await n.text()}catch{}return{ok:n.ok,statusCode:n.status,body:o}}async function to(r){try{let e=await eo(r,"/api/health");return e.ok?JSON.parse(e.body).version:null}catch{return a.debug("SYSTEM","Could not fetch worker version",{}),null}}async function lr(r,e){let t=e??"unknown",n=await to(r);return!n||t==="unknown"?{matches:!0,pluginVersion:t,workerVersion:n}:{matches:t===n,pluginVersion:t,workerVersion:n}}f();var xe=require("path"),R=require("fs");w();f();var ro=9e4;function ur(){return(0,xe.join)(te(),"spawn.lock")}function dr(){let r=ur(),e=JSON.stringify({pid:process.pid,startedAt:new Date().toISOString()});for(let t=0;t<2;t++)try{return(0,R.mkdirSync)((0,xe.dirname)(r),{recursive:!0}),(0,R.writeFileSync)(r,e,{flag:"wx"}),!0}catch(n){let o=n instanceof Error?n:new Error(String(n)),i=o.code;if(i!=="EEXIST")return a.warn("SYSTEM","Spawn lock write failed for a non-contention reason; failing open (spawning unlocked)",{lockPath:r,code:i},o),!0;if(t>0)return!1;let s;try{s=(0,R.statSync)(r).mtimeMs}catch{continue}if(Date.now()-s<=ro)return!1;let c;try{c=(0,R.statSync)(r).mtimeMs}catch{continue}if(c!==s)return!1;try{(0,R.unlinkSync)(r)}catch{return!1}}return!1}function pr(){let r=ur();try{if(JSON.parse((0,R.readFileSync)(r,"utf-8")).pid!==process.pid)return;(0,R.unlinkSync)(r)}catch{}}function yr(r,e,t){let n=process.env[r];if(n){let o=parseInt(n,10);if(Number.isFinite(o)&&o>=t.min&&o<=t.max)return o;a.warn("SYSTEM",`Invalid ${r}, using default`,{value:n,min:t.min,max:t.max})}return e}var je=yr("CLAUDE_MEM_HEALTH_TIMEOUT_MS",J(k.HEALTH_CHECK),{min:500,max:3e5}),no=yr("CLAUDE_MEM_HOOK_READINESS_TIMEOUT_MS",J(k.HOOK_READINESS_WAIT),{min:0,max:3e5}),oo={min:500,max:3e5};async function yt(r,e={},t){try{return await fetch(r,{...e,signal:AbortSignal.timeout(t)})}catch(n){throw n instanceof DOMException&&n.name==="TimeoutError"?new Error(`Request timed out after ${t}ms`):n}}var Ne=null,Ue=null,Fe=null,We=null;function so(
feat(telemetry): reliability signals — retrieval quality, compression trust, worker lifecycle, hook failures (Plan 14) (#2874)
* feat(telemetry): disclose 19 reliability-signal fields and 2 new events across all surfaces
Whitelist (scrub.ts), scrub tests, public docs (telemetry.mdx), and CLI
disclosure (COLLECTED_FIELDS/EVENT_NAMES) for the Plan 14 reliability
signals: search retrieval quality, compression trust, worker lifecycle,
and hook failure keys, plus the worker_stopped and hook_failed events.
Includes the plan document.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(telemetry): retrieval-quality signals on search_performed
SearchManager.search() fills an optional telemetry envelope
(result_count, search_strategy, chroma_available, fallback_reason)
across all three search paths; handlers stash it on
res.locals.searchTelemetry and the existing finish-middleware spreads it
into the search_performed capture. Zero-result searches report
result_count: 0; Chroma fallback reasons are a closed enum, never the
error message. Response shapes unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(telemetry): compression trust signals on session_compressed
fabrication_detected/fabricated_count flow through compressionProps (all
three emit paths); invalid-output respawns emit a respawn-gated
session_compressed with outcome invalid_output and the classifier value;
aborted generators emit outcome aborted with abort_reason normalized to
a closed enum in the .finally where all five abort flows converge (the
.catch path can never observe a non-null abortReason).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(telemetry): worker lifecycle signals — worker_stopped, crash detection, memory metrics
Clean-shutdown sentinel written before telemetry flush and consumed at
startup; worker_started gains previous_shutdown (crash/clean/unknown)
and previous_uptime_seconds derived from the stale PID file; new
worker_stopped event (uptime_seconds, shutdown_reason stop/restart/
signal) emitted before shutdownTelemetry(); the CLI restart path tags
/api/admin/shutdown?reason=restart so restarts are distinguishable;
buildLifecycleProps adds integer process_rss_mb/heap_used_mb.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(telemetry): threshold-gated hook_failed distress signal via CLI transport
recordWorkerUnreachable emits hook_failed exactly when the consecutive-
failure count reaches the fail-loud threshold; the generic blocking-error
branch emits error_mode blocking_error. Both emits are awaited before
the process.exit paths so the 2s-capped CLI POST survives; hook_type is
a closed enum registered at hookCommand entry. Exit codes unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(build): regenerate plugin artifacts with Plan 14 telemetry signals
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(tests): make PostHog client regression test order-independent via global preload mock
The disableGeoip regression test mocked posthog-node per-file, but
telemetry.ts is imported transitively by many test files in the shared
bun process, so the mock registered too late and the test failed in
full-suite runs — CI on main has been red since v13.5.4. The mock now
registers in a bunfig [test].preload before any module loads, which
also guarantees test runs can never construct a real PostHog client and
flush fabricated events into production analytics (consent is
default-on and the suite outlives flushInterval). telemetry.ts gains a
test-only state reset so construction is observed deterministically
regardless of suite order.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(telemetry): forward shutdown reason in Windows-managed IPC message
Review follow-up: the wrapper IPC path discarded the restart tag, so an
external Windows wrapper could only ever report shutdown_reason 'stop'.
No wrapper in this repo listens for the message, but the reason now
travels with it for any that does.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 02:51:45 -07:00
$ { e }
chore: remove over-engineering findings from repo-wide ponytail audit (wave 1)
Cuts verified dead code, duplication, and speculative abstractions across
14 disjoint areas of the tree: sqlite layer, worker HTTP routes, search
pipeline, server, chroma sync, viewer UI, npx-cli, MCP server, shared/utils,
telemetry/infra, integrations, scripts, tests, and stale plans/evals docs.
Also unifies the Chroma search pipeline onto SearchOrchestrator/strategies
and ports dual-project (merged_into_project) scoping plus dateRange
filtering into ChromaSearchStrategy, which corpus builds need but had
silently lost.
Full audit trail: 65-agent verification workflow wf_160a7862-1a6, 14-agent
execution wf_c7e48c0f-164, 6-agent fixup wf_865f86a4-c6b.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-02 20:49:27 -07:00
$ { n } ` ;let o=r.indexOf(t),i=r.indexOf(n);return o!==-1&&i!==-1?r.substring(0,o)+ ` $ { t }
feat(telemetry): reliability signals — retrieval quality, compression trust, worker lifecycle, hook failures (Plan 14) (#2874)
* feat(telemetry): disclose 19 reliability-signal fields and 2 new events across all surfaces
Whitelist (scrub.ts), scrub tests, public docs (telemetry.mdx), and CLI
disclosure (COLLECTED_FIELDS/EVENT_NAMES) for the Plan 14 reliability
signals: search retrieval quality, compression trust, worker lifecycle,
and hook failure keys, plus the worker_stopped and hook_failed events.
Includes the plan document.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(telemetry): retrieval-quality signals on search_performed
SearchManager.search() fills an optional telemetry envelope
(result_count, search_strategy, chroma_available, fallback_reason)
across all three search paths; handlers stash it on
res.locals.searchTelemetry and the existing finish-middleware spreads it
into the search_performed capture. Zero-result searches report
result_count: 0; Chroma fallback reasons are a closed enum, never the
error message. Response shapes unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(telemetry): compression trust signals on session_compressed
fabrication_detected/fabricated_count flow through compressionProps (all
three emit paths); invalid-output respawns emit a respawn-gated
session_compressed with outcome invalid_output and the classifier value;
aborted generators emit outcome aborted with abort_reason normalized to
a closed enum in the .finally where all five abort flows converge (the
.catch path can never observe a non-null abortReason).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(telemetry): worker lifecycle signals — worker_stopped, crash detection, memory metrics
Clean-shutdown sentinel written before telemetry flush and consumed at
startup; worker_started gains previous_shutdown (crash/clean/unknown)
and previous_uptime_seconds derived from the stale PID file; new
worker_stopped event (uptime_seconds, shutdown_reason stop/restart/
signal) emitted before shutdownTelemetry(); the CLI restart path tags
/api/admin/shutdown?reason=restart so restarts are distinguishable;
buildLifecycleProps adds integer process_rss_mb/heap_used_mb.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(telemetry): threshold-gated hook_failed distress signal via CLI transport
recordWorkerUnreachable emits hook_failed exactly when the consecutive-
failure count reaches the fail-loud threshold; the generic blocking-error
branch emits error_mode blocking_error. Both emits are awaited before
the process.exit paths so the 2s-capped CLI POST survives; hook_type is
a closed enum registered at hookCommand entry. Exit codes unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(build): regenerate plugin artifacts with Plan 14 telemetry signals
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(tests): make PostHog client regression test order-independent via global preload mock
The disableGeoip regression test mocked posthog-node per-file, but
telemetry.ts is imported transitively by many test files in the shared
bun process, so the mock registered too late and the test failed in
full-suite runs — CI on main has been red since v13.5.4. The mock now
registers in a bunfig [test].preload before any module loads, which
also guarantees test runs can never construct a real PostHog client and
flush fabricated events into production analytics (consent is
default-on and the suite outlives flushInterval). telemetry.ts gains a
test-only state reset so construction is observed deterministically
regardless of suite order.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(telemetry): forward shutdown reason in Windows-managed IPC message
Review follow-up: the wrapper IPC path discarded the restart tag, so an
external Windows wrapper could only ever report shutdown_reason 'stop'.
No wrapper in this repo listens for the message, but the reason now
travels with it for any that does.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 02:51:45 -07:00
$ { e }
chore: remove over-engineering findings from repo-wide ponytail audit (wave 1)
Cuts verified dead code, duplication, and speculative abstractions across
14 disjoint areas of the tree: sqlite layer, worker HTTP routes, search
pipeline, server, chroma sync, viewer UI, npx-cli, MCP server, shared/utils,
telemetry/infra, integrations, scripts, tests, and stale plans/evals docs.
Also unifies the Chroma search pipeline onto SearchOrchestrator/strategies
and ports dual-project (merged_into_project) scoping plus dateRange
filtering into ChromaSearchStrategy, which corpus builds need but had
silently lost.
Full audit trail: 65-agent verification workflow wf_160a7862-1a6, 14-agent
execution wf_c7e48c0f-164, 6-agent fixup wf_865f86a4-c6b.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-02 20:49:27 -07:00
$ { n } ` +r.substring(i+n.length):r+ `
fix(build): add transcript-watcher.cjs build target (closes #2450) (#2592)
* fix(build): add transcript-watcher.cjs build target (closes #2450)
src/npx-cli/commands/runtime.ts:230 hard-codes a reference to
plugin/scripts/transcript-watcher.cjs, but scripts/build-hooks.js
never compiled this entry. The fallback `spawnBunWorkerCommand`
path silently no-ops on systems where bun isn't reachable, so
`claude-mem transcript watch` is effectively dead in the published
npm bundle.
This adds:
- A thin entry (src/services/transcripts/transcript-watcher-entry.ts)
that dispatches process.argv to the existing runTranscriptCommand
function in cli.ts (which until now had no caller in the build).
- A new esbuild target in build-hooks.js modeled on context-generator
(Node 18 CJS, minified, bun:sqlite external) plus the standard
stripHardcodedDirname pass and chmod 0755.
- Updated build summary so the new artifact shows up in the
"compiled successfully" footer.
The compiled bundle is 137 KB. `node plugin/scripts/transcript-watcher.cjs`
prints the usage line and exits 1 as expected; `... validate`,
`... init`, and `... watch` all route correctly.
No behavior change on platforms that previously had a working bun
fallback - the explicit path now just wins the existsSync check.
* fix(build): externalize zod + add bundle-size guard for transcript-watcher
Addresses Greptile review on #2592:
1. Add `zod` to the external list. worker-service and
server-beta-service both externalize zod and rely on
plugin/package.json to provide it at runtime; the watcher
build was inconsistent. The current processor.ts chain
does not actually pull in zod (bundle size unchanged
at 137.64 KB), but the external entry is defensive —
any future import in src/services/transcripts/ that
touches zod will now resolve against the plugin's
declared dependency rather than getting silently
inlined (and potentially creating a duplicate-instance
hazard).
2. Add a 200 KB size guard mirroring the 600 KB ceiling
on mcp-server.cjs. The watcher is meant to be a thin
file-tail loop; if a transitive import ever drags a
heavy module (worker-service, an SDK runtime) into
the chain, this catches it at build time with an
informative error pointing at processor.ts / watcher.ts.
No functional change to the runtime artifact (size
identical, behavior identical). Both follow-ups recommended
by Greptile.
2026-06-06 14:21:25 +08:00
chore: remove over-engineering findings from repo-wide ponytail audit (wave 1)
Cuts verified dead code, duplication, and speculative abstractions across
14 disjoint areas of the tree: sqlite layer, worker HTTP routes, search
pipeline, server, chroma sync, viewer UI, npx-cli, MCP server, shared/utils,
telemetry/infra, integrations, scripts, tests, and stale plans/evals docs.
Also unifies the Chroma search pipeline onto SearchOrchestrator/strategies
and ports dual-project (merged_into_project) scoping plus dateRange
filtering into ChromaSearchStrategy, which corpus builds need but had
silently lost.
Full audit trail: 65-agent verification workflow wf_160a7862-1a6, 14-agent
execution wf_c7e48c0f-164, 6-agent fixup wf_865f86a4-c6b.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-02 20:49:27 -07:00
$ { t }
feat(telemetry): reliability signals — retrieval quality, compression trust, worker lifecycle, hook failures (Plan 14) (#2874)
* feat(telemetry): disclose 19 reliability-signal fields and 2 new events across all surfaces
Whitelist (scrub.ts), scrub tests, public docs (telemetry.mdx), and CLI
disclosure (COLLECTED_FIELDS/EVENT_NAMES) for the Plan 14 reliability
signals: search retrieval quality, compression trust, worker lifecycle,
and hook failure keys, plus the worker_stopped and hook_failed events.
Includes the plan document.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(telemetry): retrieval-quality signals on search_performed
SearchManager.search() fills an optional telemetry envelope
(result_count, search_strategy, chroma_available, fallback_reason)
across all three search paths; handlers stash it on
res.locals.searchTelemetry and the existing finish-middleware spreads it
into the search_performed capture. Zero-result searches report
result_count: 0; Chroma fallback reasons are a closed enum, never the
error message. Response shapes unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(telemetry): compression trust signals on session_compressed
fabrication_detected/fabricated_count flow through compressionProps (all
three emit paths); invalid-output respawns emit a respawn-gated
session_compressed with outcome invalid_output and the classifier value;
aborted generators emit outcome aborted with abort_reason normalized to
a closed enum in the .finally where all five abort flows converge (the
.catch path can never observe a non-null abortReason).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(telemetry): worker lifecycle signals — worker_stopped, crash detection, memory metrics
Clean-shutdown sentinel written before telemetry flush and consumed at
startup; worker_started gains previous_shutdown (crash/clean/unknown)
and previous_uptime_seconds derived from the stale PID file; new
worker_stopped event (uptime_seconds, shutdown_reason stop/restart/
signal) emitted before shutdownTelemetry(); the CLI restart path tags
/api/admin/shutdown?reason=restart so restarts are distinguishable;
buildLifecycleProps adds integer process_rss_mb/heap_used_mb.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(telemetry): threshold-gated hook_failed distress signal via CLI transport
recordWorkerUnreachable emits hook_failed exactly when the consecutive-
failure count reaches the fail-loud threshold; the generic blocking-error
branch emits error_mode blocking_error. Both emits are awaited before
the process.exit paths so the 2s-capped CLI POST survives; hook_type is
a closed enum registered at hookCommand entry. Exit codes unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(build): regenerate plugin artifacts with Plan 14 telemetry signals
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(tests): make PostHog client regression test order-independent via global preload mock
The disableGeoip regression test mocked posthog-node per-file, but
telemetry.ts is imported transitively by many test files in the shared
bun process, so the mock registered too late and the test failed in
full-suite runs — CI on main has been red since v13.5.4. The mock now
registers in a bunfig [test].preload before any module loads, which
also guarantees test runs can never construct a real PostHog client and
flush fabricated events into production analytics (consent is
default-on and the suite outlives flushInterval). telemetry.ts gains a
test-only state reset so construction is observed deterministically
regardless of suite order.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(telemetry): forward shutdown reason in Windows-managed IPC message
Review follow-up: the wrapper IPC path discarded the restart tag, so an
external Windows wrapper could only ever report shutdown_reason 'stop'.
No wrapper in this repo listens for the message, but the reason now
travels with it for any that does.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 02:51:45 -07:00
$ { e }
2026-08-02 10:14:45 -07:00
$ { n } ` }f();function Br(r,e){if(!r)return;let t=(0,qe.resolve)(r);if(t.includes("/.git/")||t.includes(" \\ .git \\ ")||t.endsWith("/.git")||t.endsWith(" \\ .git"))return;let n=(0,qe.dirname)(r);(0,I.existsSync)(n)||(0,I.mkdirSync)(n,{recursive:!0});let o="";(0,I.existsSync)(r)&&(o=(0,I.readFileSync)(r,"utf-8"));let i= ` # Memory Context
fix(build): add transcript-watcher.cjs build target (closes #2450) (#2592)
* fix(build): add transcript-watcher.cjs build target (closes #2450)
src/npx-cli/commands/runtime.ts:230 hard-codes a reference to
plugin/scripts/transcript-watcher.cjs, but scripts/build-hooks.js
never compiled this entry. The fallback `spawnBunWorkerCommand`
path silently no-ops on systems where bun isn't reachable, so
`claude-mem transcript watch` is effectively dead in the published
npm bundle.
This adds:
- A thin entry (src/services/transcripts/transcript-watcher-entry.ts)
that dispatches process.argv to the existing runTranscriptCommand
function in cli.ts (which until now had no caller in the build).
- A new esbuild target in build-hooks.js modeled on context-generator
(Node 18 CJS, minified, bun:sqlite external) plus the standard
stripHardcodedDirname pass and chmod 0755.
- Updated build summary so the new artifact shows up in the
"compiled successfully" footer.
The compiled bundle is 137 KB. `node plugin/scripts/transcript-watcher.cjs`
prints the usage line and exits 1 as expected; `... validate`,
`... init`, and `... watch` all route correctly.
No behavior change on platforms that previously had a working bun
fallback - the explicit path now just wins the existsSync check.
* fix(build): externalize zod + add bundle-size guard for transcript-watcher
Addresses Greptile review on #2592:
1. Add `zod` to the external list. worker-service and
server-beta-service both externalize zod and rely on
plugin/package.json to provide it at runtime; the watcher
build was inconsistent. The current processor.ts chain
does not actually pull in zod (bundle size unchanged
at 137.64 KB), but the external entry is defensive —
any future import in src/services/transcripts/ that
touches zod will now resolve against the plugin's
declared dependency rather than getting silently
inlined (and potentially creating a duplicate-instance
hazard).
2. Add a 200 KB size guard mirroring the 600 KB ceiling
on mcp-server.cjs. The watcher is meant to be a thin
file-tail loop; if a transitive import ever drags a
heavy module (worker-service, an SDK runtime) into
the chain, this catches it at build time with an
informative error pointing at processor.ts / watcher.ts.
No functional change to the runtime artifact (size
identical, behavior identical). Both follow-ups recommended
by Greptile.
2026-06-06 14:21:25 +08:00
2026-08-02 10:14:45 -07:00
$ { e } ` ,s=Kr(o,i),c= ` $ { r } . tmp ` ;try{(0,I.writeFileSync)(c,s),(0,I.renameSync)(c,r)}catch(l){a.error("AGENTS_MD","Failed to write AGENTS.md",{agentsPath:r},l instanceof Error?l:new Error(String(l)))}}f();function Ho(r){let e=r.trim().replace(/^ \$ \. ?/,"");if(!e)return[];let t=[],n=e.split(".");for(let o of n){let i=/([^[ \] ]+)| \[ ( \d +) \] /g,s;for(;(s=i.exec(o))!==null;)s[1]?t.push(s[1]):s[2]&&t.push(parseInt(s[2],10))}return t}function Ct(r,e){if(!e)return;let t=Ho(e),n=r;for(let o of t){if(n==null)return;n=n[o]}return n}function Jr(r){return r==null||r===""}function Vr(r,e){if(r.startsWith(" $ watch.")){let t=r.slice(7);return e.watch[t]}if(r.startsWith(" $ schema.")){let t=r.slice(8);return e.schema[t]}if(r.startsWith(" $ session.")){let t=r.slice(9);return e.session?e.session[t]:void 0}if(r===" $ cwd")return e.watch.workspace;if(r===" $ project")return e.watch.project}function ae(r,e,t){if(r!==void 0){if(typeof r=="string"){let n=Vr(r,t);return n!==void 0?n:Ct(e,r)}if(r.coalesce&&Array.isArray(r.coalesce))for(let n of r.coalesce){let o=ae(n,e,t);if(!Jr(o))return o}if(r.path){let n=Vr(r.path,t);if(n!==void 0)return n;let o=Ct(e,r.path);if(!Jr(o))return o}if(r.value!==void 0)return r.value;if(r.default!==void 0)return r.default}}function qr(r,e,t){let n={};if(!r)return n;for(let[o,i]of Object.entries(r))n[o]=ae(i,e,t);return n}function Xr(r,e,t){if(!e)return!0;let n=e.path||t.eventTypePath||"type",o=n?Ct(r,n):void 0,i=o==null||o==="";if(e.exists!==void 0&&(e.exists&&i||!e.exists&&!i)||e.equals!==void 0&&o!==e.equals||e.not_equals!==void 0&&o===e.not_equals||e.in&&Array.isArray(e.in)&&!e.in.includes(o)||e.not_in&&Array.isArray(e.not_in)&&e.not_in.includes(o)||e.contains!==void 0&&(typeof o!="string"||!o.includes(e.contains))||e.not_contains!==void 0&&typeof o=="string"&&o.includes(e.not_contains))return!1;if(e.regex)try{if(!new RegExp(e.regex).test(String(o??"")))return!1}catch(s){return a.debug("WORKER","Invalid regex in match rule",{regex:e.regex},s instanceof Error?s:void 0),!1}return!0}f();w();f();var Xe=class{static checkUserPromptPrivacy(e,t,n,o,i,s){let c=e.getUserPrompt(t,n,i);return c===null?(a.warn("HOOK", ` $ { o } : no user _prompts row for prompt # $ { n } \u2014 ingesting anyway ( session - init likely raced worker boot ; see # 2794 / # 2795 ) ` ,{sessionId:i,contentSessionId:t,promptNumber:n,...s}),{allow:!0,prompt:""}):c.trim()===""?(a.debug("HOOK", ` Skipping $ { o } - user prompt was entirely private ` ,{sessionId:i,promptNumber:n,...s}),{allow:!1,reason:"private"}):{allow:!0,prompt:c}}};var zr=null;function jo(){if(!zr)throw new Error("ingest helpers used before setIngestContext() \u 2014 wiring bug");return zr}async function Qr(r){let{sessionManager:e,dbManager:t,eventBroadcaster:n,ensureGeneratorRunning:o}=jo(),i=x(r.platformSource),s=typeof r.cwd=="string"?r.cwd:"",c=s.trim()?Q(s).primary:"",l=v.loadFromFile(re);if(s&&Be(s,l.CLAUDE_MEM_EXCLUDED_PROJECTS))return{ok:!0,status:"skipped",reason:"project_excluded"};if(new Set(l.CLAUDE_MEM_SKIP_TOOLS.split(",").map(A=>A.trim()).filter(Boolean)).has(r.toolName))return{ok:!0,status:"skipped",reason:"tool_excluded"};if(new Set(["Edit","Write","Read","NotebookEdit"]).has(r.toolName)&&r.toolInput&&typeof r.toolInput=="object"){let A=r.toolInput,q=A.file_path||A.notebook_path;if(q&&q.includes("session-memory"))return{ok:!0,status:"skipped",reason:"session_memory_meta"}}let E=t.getSessionStore(),g,M;try{g=E.createSDKSession(r.contentSessionId,c,"",void 0,i),M=E.getPromptNumberFromUserPrompts(r.contentSessionId,g)}catch(A){let q=A instanceof Error?A.message:String(A);return a.error("INGEST","Observation session resolution failed",{contentSessionId:r.contentSessionId,toolName:r.toolName},A instanceof Error?A:new Error(q)),{ok:!1,reason:q,status:500}}if(!Xe.checkUserPromptPrivacy(E,r.contentSessionId,M,"observation",g,{tool_name:r.toolName}).allow)return{ok:!0,status:"skipped",reason:"private"};let Y=r.toolInput!==void 0?wt(JSON.stringify(r.toolInput)):"{}",_e=r.toolResponse!==void 0?wt(JSON.stringify(r.toolResponse)):"{}";return await e.queueObservation(g,{tool_name:r.toolName,tool_input:Y,tool_response:_e,pro
` ) ; for ( let o of n ) { let i = o . trim ( ) ; if ( i . startsWith ( "*** Update File: " ) ) t . push ( i . replace ( "*** Update File: " , "" ) . trim ( ) ) ; else if ( i . startsWith ( "*** Add File: " ) ) t . push ( i . replace ( "*** Add File: " , "" ) . trim ( ) ) ; else if ( i . startsWith ( "*** Delete File: " ) ) t . push ( i . replace ( "*** Delete File: " , "" ) . trim ( ) ) ; else if ( i . startsWith ( "*** Move to: " ) ) t . push ( i . replace ( "*** Move to: " , "" ) . trim ( ) ) ; else if ( i . startsWith ( "+++ " ) ) { let s = i . replace ( "+++ " , "" ) . replace ( /^b\// , "" ) . trim ( ) ; s && s !== "/dev/null" && t . push ( s ) } } return Array . from ( new Set ( t ) ) } async handleSessionEnd ( e , t ) { await this . queueSummary ( e ) , await this . updateContext ( e , t ) , e . pendingTools ? . clear ( ) ; let n = this . getSessionKey ( t , e . sessionId ) ; this . sessions . delete ( n ) } async queueSummary ( e ) { if ( ! await $e ( ) ) return ; let n = e . lastAssistantMessage ? ? "" , o = JSON . stringify ( { contentSessionId : e . sessionId , last _assistant _message : n , platformSource : e . platformSource } ) ; try { await $ ( "/api/sessions/summarize" , { method : "POST" , headers : { "Content-Type" : "application/json" } , body : o } ) } catch ( i ) { a . warn ( "TRANSCRIPT" , "Summary request failed" , { error : i instanceof Error ? i . message : String ( i ) } ) } } async updateContext ( e , t ) { if ( ! t . context || t . context . mode !== "agents" || Dt ( t ) || ! await $e ( ) ) return ; let o = e . cwd ? ? t . workspace ; if ( ! o ) return ; let s = Q ( o ) . allProjects . join ( "," ) , c = ` /api/context/inject?projects= ${ encodeURIComponent ( s ) } &platformSource= ${ encodeURIComponent ( e . platformSource ) } ` , l = b ( t . context . path ? ? ` ${ o } /AGENTS.md ` ) , d = Ee . default . resolve ( l ) , u = [ Ee . default . resolve ( o ) , Ee . default . resolve ( S ) ] ; if ( ! u . some ( _ => d . startsWith ( _ + Ee . default . sep ) || d === _ ) ) { a . warn ( "SECURITY" , "Rejected path traversal attempt in watch.context.path" , { original : t . context . path , resolved : d , allowedRoots : u } ) ; return } let g ; try { g = await $ ( c ) } catch ( _ ) { a . warn ( "TRANSCRIPT" , "Failed to fetch AGENTS.md context" , { error : _ instanceof Error ? _ . message : String ( _ ) } ) ; return } if ( ! g . ok ) return ; let M = ( await g . text ( ) ) . trim ( ) ; M && ( Br ( l , M ) , a . debug ( "TRANSCRIPT" , "Updated AGENTS.md context" , { agentsPath : l , watch : t . name } ) ) } } ; var bt = class { constructor ( e , t , n , o ) { this . filePath = e ; this . onLine = n ; this . onOffset = o ; this . tailState = { offset : t , partial : "" } } filePath ; onLine ; onOffset ; watcher = null ; tailState ; start ( ) { this . readNewData ( ) . catch ( ( ) => { } ) , this . watcher = ( 0 , C . watch ) ( this . filePath , { persistent : ! 0 } , ( ) => { this . readNewData ( ) . catch ( ( ) => { } ) } ) } close ( ) { this . watcher ? . close ( ) , this . watcher = null } poke ( ) { this . readNewData ( ) . catch ( ( ) => { } ) } async readNewData ( ) { if ( ! ( 0 , C . existsSync ) ( this . filePath ) ) return ; let e = 0 ; try { e = ( 0 , C . statSync ) ( this . filePath ) . size } catch ( s ) { a . debug ( "WORKER" , "Failed to stat transcript file" , { file : this . filePath } , s instanceof Error ? s : void 0 ) ; return } if ( e < this . tailState . offset && ( this . tailState . offset = 0 ) , e === this . tailState . offset ) return ; let t = ( 0 , C . createReadStream ) ( this . filePath , { start : this . tailState . offset , end : e - 1 , encoding : "utf8" } ) , n = "" ; for await ( let s of t ) n += s ; this . tailState . offset = e , this . onOffset ( this . tailState . offset ) ; let i = ( this . tailState . partial + n ) . split ( `
` ) ; this . tailState . partial = i . pop ( ) ? ? "" ; for ( let s of i ) { let c = s . trim ( ) ; c && await this . onLine ( c ) } } } , Qe = class { constructor ( e , t ) { this . config = e ; this . statePath = t ; this . state = xt ( t ) } config ; statePath ; processor = new ze ; tailers = new Map ; state ; rootWatchers = [ ] ; async start ( ) { for ( let e of this . config . watches ) await this . setupWatch ( e ) } stop ( ) { for ( let e of this . tailers . values ( ) ) e . close ( ) ; this . tailers . clear ( ) ; for ( let e of this . rootWatchers ) e . close ( ) ; this . rootWatchers = [ ] } async setupWatch ( e ) { let t = this . resolveSchema ( e ) ; if ( ! t ) { a . warn ( "TRANSCRIPT" , "Missing schema for watch" , { watch : e . name } ) ; return } let n = b ( e . path ) , o = this . resolveWatchFiles ( n ) ; for ( let s of o ) await this . addTailer ( s , e , t ) ; let i = this . deepestNonGlobAncestor ( n ) ; if ( ! i || ! ( 0 , C . existsSync ) ( i ) ) { a . debug ( "TRANSCRIPT" , "Watch root does not exist, skipping fs.watch" , { watch : e . name , watchRoot : i } ) ; return } try { let s = ( 0 , C . watch ) ( i , { recursive : ! 0 , persistent : ! 0 } , ( c , l ) => { this . handleRootWatchEvent ( i , n , e , t , l ) } ) ; this . rootWatchers . push ( s ) , a . info ( "TRANSCRIPT" , "Watching transcript root recursively" , { watch : e . name , watchRoot : i } ) } catch ( s ) { a . warn ( "TRANSCRIPT" , "Failed to start recursive fs.watch on transcript root" , { watch : e . name , watchRoot : i } , s instanceof Error ? s : void 0 ) } } handleRootWatchEvent ( e , t , n , o , i ) { if ( ! i ) return ; let s = ( 0 , O . resolve ) ( e , i ) . replace ( /\\/g , "/" ) , c = this . tailers . get ( s ) ; if ( c ) { c . poke ( ) ; return } let l = this . resolveWatchFiles ( t ) ; for ( let d of l ) this . tailers . has ( d ) || this . addTailer ( d , n , o ) } deepestNonGlobAncestor ( e ) { if ( ! this . hasGlob ( e ) ) { if ( ( 0 , C . existsSync ) ( e ) ) try { return ( 0 , C . statSync ) ( e ) . isDirectory ( ) ? e : ( 0 , O . resolve ) ( e , ".." ) } catch ( o ) { return a . debug ( "TRANSCRIPT" , "Failed to stat watch path ancestor, falling back to parent directory" , { path : e } , o instanceof Error ? o : new Error ( String ( o ) ) ) , ( 0 , O . resolve ) ( e , ".." ) } return e } let t = e . split ( /[/\\]/ ) , n = [ ] ; for ( let o of t ) { if ( /[*?[\]{}()]/ . test ( o ) ) break ; n . push ( o ) } return n . length === 0 || n . length === 1 && n [ 0 ] === "" ? "" : n . join ( O . sep ) } resolveSchema ( e ) { return typeof e . schema == "string" ? this . config . schemas ? . [ e . schema ] ? ? null : e . schema } resolveWatchFiles ( e ) { if ( this . hasGlob ( e ) ) return this . scanGlob ( this . normalizeGlobPattern ( e ) ) ; if ( ( 0 , C . existsSync ) ( e ) ) try { if ( ( 0 , C . statSync ) ( e ) . isDirectory ( ) ) { let n = ( 0 , O . join ) ( e , "**" , "*.jsonl" ) ; return this . scanGlob ( this . normalizeGlobPattern ( n ) ) } return [ e ] } catch ( t ) { return a . debug ( "WORKER" , "Failed to stat watch path" , { path : e } , t instanceof Error ? t : void 0 ) , [ ] } return [ ] } scanGlob ( e ) { return Array . from ( new Bun . Glob ( e ) . scanSync ( { absolute : ! 0 , onlyFiles : ! 0 } ) ) } normalizeGlobPattern ( e ) { return e . replace ( /\\/g , "/" ) } hasGlob ( e ) { return /[*?[\]{}()]/ . test ( e ) } async addTailer ( e , t , n ) { if ( this . tailers . has ( e ) ) return ; let o = this . extractSessionIdFromPath ( e ) , i = this . state . offsets [ e ] ? ? 0 ; if ( i === 0 && t . startAtEnd ) try { i = ( 0 , C . statSync ) ( e ) . size } catch ( c ) { a . debug ( "WORKER" , "Failed to stat file for startAtEnd offset" , { file : e } , c instanceof Error ? c : void 0 ) , i = 0 } let s = new bt ( e , i , async c => { await this . handleLine ( c , t , n , e , o ) } , c => { this . state . offsets [ e ] = c , Nt ( this . statePath , this . state ) } ) ; s . start ( ) , this . tailers . set ( e , s ) , a . info ( "TRANSCRIPT" , "Watching transcript file" , { file : e , watch : t . name , schema : n . name } ) } async handleLine ( e , t , n , o , i ) { try { let s = JSON . parse ( e ) ; await this . processor . processEntry ( s , t , n , i ? ? void 0 ) } catch ( s ) { s instanceof Error ? a . debug ( "TRANSCRIPT" , "Failed to parse transcript line" , { watch : t . name , file : ( 0 , O . basename ) ( o ) } , s ) : a . warn ( "TRANSCRIPT" , "Failed to parse transcript line (non-Error thrown)" , { watch : t . name , file : ( 0 , O . basename ) ( o ) , error : String ( s ) } ) } } extractSessionIdFromPath ( e ) { let t = e . match ( /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i ) ; return t ? t [ 0 ] : null } } ; function vt ( r , e ) { let t = r . indexOf ( e ) ; return t === - 1 ? null : r [ t + 1 ] ? ? null } async function Zr ( r , e ) { switch ( r ) { case "init" : { let t = vt ( e , "--config" ) ? ? ne ; return Ce ( t ) , console . log ( ` Created sample config: ${ b ( t ) } ` ) , 0 } case "watch" : { let t = vt ( e , "--config" ) ? ? ne , n ; try { n = pe ( t ) } catch ( c ) { if ( c instanceof Error && c . message . includes ( "not found" ) ) Ce ( t ) , console . log ( ` Created sample config: ${ b ( t ) } ` ) , n = pe ( t ) ; else throw c } let o = b ( n . stateFile ? ? Me ) , i = new Qe ( n , o ) ; await i . start ( ) , console . log ( "Transcript watcher running. Press Ctrl+C to stop." ) ; let s = ( ) => { i . stop ( ) , process . exit ( 0 ) } ; return process . on ( "SIGINT" ,