@clauderecallhq/cli 0.77.1 → 0.92.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /* Claude Recall (proprietary). See LICENSE for terms. */
3
- var Qp=Object.defineProperty;var N=(e,t)=>()=>(e&&(t=e(e=0)),t);var he=(e,t)=>{for(var s in t)Qp(e,s,{get:t[s],enumerable:!0})};import{createRequire as em}from"node:module";var tm,sm,nm,jn,Un,bs,Bn=N(()=>{"use strict";{let e=process.emit.bind(process);process.emit=function(t,...s){let n=s[0];return t==="warning"&&n instanceof Error&&n.name==="ExperimentalWarning"&&/SQLite/i.test(n.message)?!1:e(t,...s)}}tm=em(import.meta.url),sm=["node","sqlite"].join(":"),nm=tm(sm),jn=class{inner;constructor(t){this.inner=t}get(...t){return t.length===0?this.inner.get():this.inner.get(...t)}all(...t){return t.length===0?this.inner.all():this.inner.all(...t)}run(...t){let s=t.length===0?this.inner.run():this.inner.run(...t);return{changes:s.changes,lastInsertRowid:s.lastInsertRowid}}iterate(...t){return t.length===0?this.inner.iterate():this.inner.iterate(...t)}},Un=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,s={}){this.inner=new nm.DatabaseSync(t,{readOnly:s.readonly??!1,allowExtension:!0})}prepare(t){return new jn(this.inner.prepare(t))}exec(t){this.inner.exec(t)}close(){this.inner.close()}pragma(t,s={}){if(t.includes("=")){this.inner.exec(`PRAGMA ${t}`);return}if(s.simple){let n=this.inner.prepare(`PRAGMA ${t}`).get();return n&&typeof n=="object"?Object.values(n)[0]:void 0}return this.inner.prepare(`PRAGMA ${t}`).all()}transaction(t){return((...n)=>{this.txDepth===0?this.inner.exec("BEGIN"):this.inner.exec(`SAVEPOINT sp_${this.txDepth}`),this.txDepth+=1;try{let r=t(...n);return this.txDepth-=1,this.txDepth===0?this.inner.exec("COMMIT"):this.inner.exec(`RELEASE sp_${this.txDepth}`),r}catch(r){this.txDepth-=1;try{this.txDepth===0?this.inner.exec("ROLLBACK"):(this.inner.exec(`ROLLBACK TO sp_${this.txDepth}`),this.inner.exec(`RELEASE sp_${this.txDepth}`))}catch{}throw r}})}loadExtension(t,s){this.extensionLoadingEnabled||(this.inner.enableLoadExtension(!0),this.extensionLoadingEnabled=!0),s===void 0?this.inner.loadExtension(t):this.inner.loadExtension(t,s)}},bs=Un});import{homedir as ui}from"node:os";import{join as Gt,basename as rm}from"node:path";import{existsSync as pi,mkdirSync as om,chmodSync as im,readdirSync as di,statSync as am}from"node:fs";function F(){pi(x)||om(x,{recursive:!0,mode:448}),process.platform!=="win32"&&im(x,448)}function Hn(e){return e.replace(/^-/,"/").replace(/-/g,"/")}function mi(e){let t=Hn(e);return rm(t)||t}function gi(){if(!pi(Lt))return[];let e=[],t=di(Lt,{withFileTypes:!0}).filter(s=>s.isDirectory()).map(s=>s.name);for(let s of t){let n=Gt(Lt,s),r=di(n,{withFileTypes:!0});for(let o of r){if(!o.isFile()||!o.name.endsWith(".jsonl"))continue;let i=Gt(n,o.name),a=am(i);e.push({sessionFile:i,encodedProject:s,mtime:a.mtimeMs,size:a.size})}}return e}var Lt,x,se,P=N(()=>{"use strict";Lt=process.env.CLAUDE_PROJECTS_DIR?process.env.CLAUDE_PROJECTS_DIR:Gt(ui(),".claude","projects"),x=process.env.RECALL_HOME?process.env.RECALL_HOME:Gt(ui(),".recall"),se=Gt(x,"db.sqlite")});function _i(e){let t=e.prepare("PRAGMA table_info(sessions)").all(),s=new Set(t.map(g=>g.name)),n=[["total_input_tokens","INTEGER"],["total_output_tokens","INTEGER"],["total_cache_create_tokens","INTEGER"],["total_cache_read_tokens","INTEGER"],["primary_model","TEXT"],["auto_title","TEXT"],["auto_title_source","TEXT"],["auto_title_generated_at","INTEGER"],["auto_title_history","TEXT"],["verification_status","TEXT"],["verification_computed_at","INTEGER"],["title_quality","TEXT"],["title_quality_computed_at","INTEGER"],["archive_status","TEXT NOT NULL DEFAULT 'live'"],["archived_at","TEXT"],["skipped_reason","TEXT"]];for(let[g,f]of n)s.has(g)||e.exec(`ALTER TABLE sessions ADD COLUMN ${g} ${f}`);let r=e.prepare("PRAGMA table_info(collection_sessions)").all(),o=new Set(r.map(g=>g.name)),i=[["source","TEXT NOT NULL DEFAULT 'manual'"],["rule_id","TEXT"]];for(let[g,f]of i)o.has(g)||e.exec(`ALTER TABLE collection_sessions ADD COLUMN ${g} ${f}`);let a=e.prepare("PRAGMA table_info(session_notes)").all(),d=new Set(a.map(g=>g.name)),l=[["auto_synopsis","TEXT"],["auto_synopsis_generated_at","INTEGER"],["auto_synopsis_history","TEXT"]];for(let[g,f]of l)d.has(g)||e.exec(`ALTER TABLE session_notes ADD COLUMN ${g} ${f}`);let u=e.prepare("PRAGMA table_info(threads)").all();new Set(u.map(g=>g.name)).has("folder_id")||(e.exec("ALTER TABLE threads ADD COLUMN folder_id TEXT REFERENCES thread_folders(id) ON DELETE SET NULL"),e.exec("CREATE INDEX IF NOT EXISTS idx_threads_folder ON threads(folder_id)"));let p=e.prepare("PRAGMA table_info(thread_folders)").all(),m=new Set(p.map(g=>g.name));m.has("project_scope")||(e.exec("ALTER TABLE thread_folders ADD COLUMN project_scope TEXT"),e.exec("CREATE INDEX IF NOT EXISTS idx_thread_folders_project ON thread_folders(project_scope)")),m.has("sort_order")||e.exec("ALTER TABLE thread_folders ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0"),e.exec(`
3
+ var sf=Object.defineProperty;var C=(e,t)=>()=>(e&&(t=e(e=0)),t);var Z=(e,t)=>{for(var n in t)sf(e,n,{get:t[n],enumerable:!0})};import{createRequire as rf}from"node:module";var of,af,cf,Ar,Lr,Dt,Un=C(()=>{"use strict";{let e=process.emit.bind(process);process.emit=function(t,...n){let s=n[0];return t==="warning"&&s instanceof Error&&s.name==="ExperimentalWarning"&&/SQLite/i.test(s.message)?!1:e(t,...n)}}of=rf(import.meta.url),af=["node","sqlite"].join(":"),cf=of(af),Ar=class{inner;constructor(t){this.inner=t}get(...t){return t.length===0?this.inner.get():this.inner.get(...t)}all(...t){return t.length===0?this.inner.all():this.inner.all(...t)}run(...t){let n=t.length===0?this.inner.run():this.inner.run(...t);return{changes:n.changes,lastInsertRowid:n.lastInsertRowid}}iterate(...t){return t.length===0?this.inner.iterate():this.inner.iterate(...t)}},Lr=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,n={}){this.inner=new cf.DatabaseSync(t,{readOnly:n.readonly??!1,allowExtension:!0})}prepare(t){return new Ar(this.inner.prepare(t))}exec(t){this.inner.exec(t)}close(){this.inner.close()}pragma(t,n={}){if(t.includes("=")){this.inner.exec(`PRAGMA ${t}`);return}if(n.simple){let s=this.inner.prepare(`PRAGMA ${t}`).get();return s&&typeof s=="object"?Object.values(s)[0]:void 0}return this.inner.prepare(`PRAGMA ${t}`).all()}transaction(t){return((...s)=>{this.txDepth===0?this.inner.exec("BEGIN"):this.inner.exec(`SAVEPOINT sp_${this.txDepth}`),this.txDepth+=1;try{let r=t(...s);return this.txDepth-=1,this.txDepth===0?this.inner.exec("COMMIT"):this.inner.exec(`RELEASE sp_${this.txDepth}`),r}catch(r){this.txDepth-=1;try{this.txDepth===0?this.inner.exec("ROLLBACK"):(this.inner.exec(`ROLLBACK TO sp_${this.txDepth}`),this.inner.exec(`RELEASE sp_${this.txDepth}`))}catch{}throw r}})}loadExtension(t,n){this.extensionLoadingEnabled||(this.inner.enableLoadExtension(!0),this.extensionLoadingEnabled=!0),n===void 0?this.inner.loadExtension(t):this.inner.loadExtension(t,n)}},Dt=Lr});import{homedir as ba}from"node:os";import{join as sn,basename as lf}from"node:path";import{existsSync as Sa,mkdirSync as df,chmodSync as uf,readdirSync as Ea,statSync as mf}from"node:fs";function j(){Sa(x)||df(x,{recursive:!0,mode:448}),process.platform!=="win32"&&uf(x,448)}function Nr(e){return e.replace(/^-/,"/").replace(/-/g,"/")}function wa(e){let t=Nr(e);return lf(t)||t}function ya(){if(!Sa($t))return[];let e=[],t=Ea($t,{withFileTypes:!0}).filter(n=>n.isDirectory()).map(n=>n.name);for(let n of t){let s=sn($t,n),r=Ea(s,{withFileTypes:!0});for(let o of r){if(!o.isFile()||!o.name.endsWith(".jsonl"))continue;let i=sn(s,o.name),a=mf(i);e.push({sessionFile:i,encodedProject:n,mtime:a.mtimeMs,size:a.size})}}return e}var $t,x,ee,D=C(()=>{"use strict";$t=process.env.CLAUDE_PROJECTS_DIR?process.env.CLAUDE_PROJECTS_DIR:sn(ba(),".claude","projects"),x=process.env.RECALL_HOME?process.env.RECALL_HOME:sn(ba(),".recall"),ee=sn(x,"db.sqlite")});function Ra(e){let t=e.prepare("PRAGMA table_info(sessions)").all(),n=new Set(t.map(_=>_.name)),s=[["total_input_tokens","INTEGER"],["total_output_tokens","INTEGER"],["total_cache_create_tokens","INTEGER"],["total_cache_read_tokens","INTEGER"],["primary_model","TEXT"],["auto_title","TEXT"],["auto_title_source","TEXT"],["auto_title_generated_at","INTEGER"],["auto_title_history","TEXT"],["verification_status","TEXT"],["verification_computed_at","INTEGER"],["title_quality","TEXT"],["title_quality_computed_at","INTEGER"],["archive_status","TEXT NOT NULL DEFAULT 'live'"],["archived_at","TEXT"],["skipped_reason","TEXT"]];for(let[_,b]of s)n.has(_)||e.exec(`ALTER TABLE sessions ADD COLUMN ${_} ${b}`);let r=e.prepare("PRAGMA table_info(collection_sessions)").all(),o=new Set(r.map(_=>_.name)),i=[["source","TEXT NOT NULL DEFAULT 'manual'"],["rule_id","TEXT"]];for(let[_,b]of i)o.has(_)||e.exec(`ALTER TABLE collection_sessions ADD COLUMN ${_} ${b}`);let a=e.prepare("PRAGMA table_info(session_notes)").all(),d=new Set(a.map(_=>_.name)),l=[["auto_synopsis","TEXT"],["auto_synopsis_generated_at","INTEGER"],["auto_synopsis_history","TEXT"]];for(let[_,b]of l)d.has(_)||e.exec(`ALTER TABLE session_notes ADD COLUMN ${_} ${b}`);let u=e.prepare("PRAGMA table_info(threads)").all();new Set(u.map(_=>_.name)).has("folder_id")||(e.exec("ALTER TABLE threads ADD COLUMN folder_id TEXT REFERENCES thread_folders(id) ON DELETE SET NULL"),e.exec("CREATE INDEX IF NOT EXISTS idx_threads_folder ON threads(folder_id)"));let m=e.prepare("PRAGMA table_info(thread_folders)").all(),p=new Set(m.map(_=>_.name));p.has("project_scope")||(e.exec("ALTER TABLE thread_folders ADD COLUMN project_scope TEXT"),e.exec("CREATE INDEX IF NOT EXISTS idx_thread_folders_project ON thread_folders(project_scope)")),p.has("sort_order")||e.exec("ALTER TABLE thread_folders ADD COLUMN sort_order INTEGER NOT NULL DEFAULT 0"),e.exec(`
4
4
  CREATE TABLE IF NOT EXISTS message_embeddings (
5
5
  message_uuid TEXT PRIMARY KEY REFERENCES messages(uuid) ON DELETE CASCADE,
6
6
  session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
@@ -12,7 +12,7 @@ var Qp=Object.defineProperty;var N=(e,t)=>()=>(e&&(t=e(e=0)),t);var he=(e,t)=>{f
12
12
  );
13
13
  CREATE INDEX IF NOT EXISTS idx_message_embeddings_session ON message_embeddings(session_id);
14
14
  CREATE INDEX IF NOT EXISTS idx_message_embeddings_generated ON message_embeddings(generated_at DESC);
15
- `)}var fi,hi=N(()=>{"use strict";fi=`
15
+ `);let g=e.prepare("PRAGMA table_info(projects)").all(),f=new Set(g.map(_=>_.name)),h=[["repo_root","TEXT"],["main_repo","TEXT"],["is_repo","INTEGER NOT NULL DEFAULT 0"],["is_worktree","INTEGER NOT NULL DEFAULT 0"]];for(let[_,b]of h)f.has(_)||e.exec(`ALTER TABLE projects ADD COLUMN ${_} ${b}`)}var Ta,ka=C(()=>{"use strict";Ta=`
16
16
  PRAGMA journal_mode = WAL;
17
17
  PRAGMA synchronous = NORMAL;
18
18
  PRAGMA foreign_keys = ON;
@@ -21,7 +21,11 @@ CREATE TABLE IF NOT EXISTS projects (
21
21
  id INTEGER PRIMARY KEY AUTOINCREMENT,
22
22
  encoded_path TEXT UNIQUE NOT NULL,
23
23
  decoded_path TEXT NOT NULL,
24
- name TEXT NOT NULL
24
+ name TEXT NOT NULL,
25
+ repo_root TEXT,
26
+ main_repo TEXT,
27
+ is_repo INTEGER NOT NULL DEFAULT 0,
28
+ is_worktree INTEGER NOT NULL DEFAULT 0
25
29
  );
26
30
 
27
31
  CREATE TABLE IF NOT EXISTS sessions (
@@ -242,15 +246,62 @@ CREATE TABLE IF NOT EXISTS chunk_meta (
242
246
  CREATE INDEX IF NOT EXISTS idx_chunk_meta_session ON chunk_meta(session_id);
243
247
  CREATE INDEX IF NOT EXISTS idx_chunk_meta_stale ON chunk_meta(stale) WHERE stale = 1;
244
248
 
249
+ -- chunk_queue action CHECK constraint expanded in v0.78 to include
250
+ -- 'pending_post_migration'. New databases accept this value; existing
251
+ -- databases retain the older 3-action check (SQLite cannot ALTER a
252
+ -- CHECK constraint in place). The Phase 2 migrate command (src/semantic/
253
+ -- migrate.ts, TBD) pauses the embedder worker before enqueueing any
254
+ -- 'pending_post_migration' rows; on an older DB the watcher (D1) catches
255
+ -- the resulting CHECK violation and falls back to 'embed' \u2014 safe because
256
+ -- the worker is paused on the migration lock during the window.
245
257
  CREATE TABLE IF NOT EXISTS chunk_queue (
246
258
  id INTEGER PRIMARY KEY AUTOINCREMENT,
247
259
  session_id TEXT NOT NULL,
248
260
  message_uuid TEXT,
249
- action TEXT NOT NULL CHECK (action IN ('embed', 'reembed', 'delete')),
261
+ action TEXT NOT NULL CHECK (action IN ('embed', 'reembed', 'delete', 'pending_post_migration')),
250
262
  enqueued_at TEXT NOT NULL DEFAULT (datetime('now'))
251
263
  );
252
264
  CREATE INDEX IF NOT EXISTS idx_chunk_queue_session ON chunk_queue(session_id);
253
265
 
266
+ -- v0.78 \u2014 semantic migration state (Phase 2 of Tier 2 modernization).
267
+ -- Tracks the lifecycle of a \`recall semantic migrate\` run: in-progress with
268
+ -- a cursor, completed (vec_chunks now holds the new-backend vectors and
269
+ -- vec_chunks_v1_backup retains the prior set for 30 days), failed (the
270
+ -- partial vec_chunks_v2 is left in place for forensic inspection then
271
+ -- discarded by a future migrate retry), or rolled_back (the backup was
272
+ -- swapped back in via \`recall semantic rollback-migration\`).
273
+ --
274
+ -- schema_version lets a future CLI version refuse to resume a cursor it
275
+ -- cannot interpret rather than corrupt the migration state silently.
276
+ CREATE TABLE IF NOT EXISTS migration_state (
277
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
278
+ schema_version INTEGER NOT NULL,
279
+ status TEXT NOT NULL CHECK (status IN (
280
+ 'in_progress',
281
+ 'paused',
282
+ 'completed',
283
+ 'rolled_back',
284
+ 'failed'
285
+ )),
286
+ started_at TEXT NOT NULL,
287
+ completed_at TEXT,
288
+ lock_taken_at TEXT,
289
+ lock_taken_by_pid INTEGER,
290
+ cursor_session_id TEXT,
291
+ cursor_chunk_id INTEGER,
292
+ model_id_old TEXT NOT NULL,
293
+ model_id_new TEXT NOT NULL
294
+ );
295
+ CREATE INDEX IF NOT EXISTS idx_migration_state_status ON migration_state(status);
296
+
297
+ -- Belt-and-suspenders: at most one active (in-progress or paused) row.
298
+ -- Partial UNIQUE INDEX on schema_version with predicate status IN ('in_progress','paused')
299
+ -- means "at most one row per schema_version that is active." Since schema_version
300
+ -- is monotonic, this effectively caps active migrations at one regardless of
301
+ -- application-layer race conditions.
302
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_migration_state_single_active
303
+ ON migration_state(schema_version) WHERE status IN ('in_progress', 'paused');
304
+
254
305
  -- v0.67.0 -- retention/archive (P6). Power-user durability: sessions older
255
306
  -- than the user chosen window can be moved to messages_archive so the hot
256
307
  -- DB stays small without dropping data. Sessions metadata stays hot for
@@ -292,11 +343,22 @@ INSERT OR IGNORE INTO app_settings(key, value) VALUES ('semantic_enabled', '0');
292
343
  -- Drop unconditional v0.10/v0.11-era triggers; replaced below with the gated
293
344
  -- versions. Idempotent: SQLite no-ops when the trigger does not exist, so
294
345
  -- this is safe to run on every boot and on fresh databases.
346
+ --
347
+ -- 2026-05-23 \u2014 added IF NOT EXISTS guards on the CREATE side too. Without
348
+ -- them, any failure between the DROPs and the CREATEs (e.g. a partially
349
+ -- applied prior boot, or a future statement inserted between them) left
350
+ -- the live DB with the gated triggers but the next SCHEMA_SQL re-exec
351
+ -- still threw "trigger messages_vec_ai already exists". That threw all the
352
+ -- way out of getDb(), which caused syncSemanticEnabledToDb to silently
353
+ -- skip the gate flip \u2014 and the live (still-present) triggers fired with
354
+ -- the prior semantic_enabled='1' value, enqueueing 90k+ chunk_queue rows
355
+ -- in minutes on a daemon the operator had explicitly disabled. Belt and
356
+ -- suspenders: drop-then-create-if-not-exists is fully idempotent.
295
357
  DROP TRIGGER IF EXISTS messages_vec_ai;
296
358
  DROP TRIGGER IF EXISTS messages_vec_ad;
297
359
  DROP TRIGGER IF EXISTS messages_vec_au;
298
360
 
299
- CREATE TRIGGER messages_vec_ai AFTER INSERT ON messages
361
+ CREATE TRIGGER IF NOT EXISTS messages_vec_ai AFTER INSERT ON messages
300
362
  WHEN new.is_sidechain = 0
301
363
  AND new.content_text IS NOT NULL
302
364
  AND (SELECT value FROM app_settings WHERE key = 'semantic_enabled') = '1'
@@ -305,14 +367,14 @@ BEGIN
305
367
  VALUES (new.session_id, new.uuid, 'embed', datetime('now'));
306
368
  END;
307
369
 
308
- CREATE TRIGGER messages_vec_ad AFTER DELETE ON messages
370
+ CREATE TRIGGER IF NOT EXISTS messages_vec_ad AFTER DELETE ON messages
309
371
  WHEN (SELECT value FROM app_settings WHERE key = 'semantic_enabled') = '1'
310
372
  BEGIN
311
373
  INSERT INTO chunk_queue(session_id, message_uuid, action, enqueued_at)
312
374
  VALUES (old.session_id, old.uuid, 'delete', datetime('now'));
313
375
  END;
314
376
 
315
- CREATE TRIGGER messages_vec_au AFTER UPDATE OF content_text ON messages
377
+ CREATE TRIGGER IF NOT EXISTS messages_vec_au AFTER UPDATE OF content_text ON messages
316
378
  WHEN new.is_sidechain = 0
317
379
  AND (SELECT value FROM app_settings WHERE key = 'semantic_enabled') = '1'
318
380
  BEGIN
@@ -668,33 +730,37 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_target
668
730
  ON bug_synthesis_results(scope, target_id, created_at DESC);
669
731
  CREATE INDEX IF NOT EXISTS idx_synth_results_created
670
732
  ON bug_synthesis_results(created_at DESC);
671
- `});import*as Ei from"sqlite-vec";function _(){if(le)return le;F(),le=new bs(se),Ei.load(le),le.pragma("cache_size = -64000"),le.pragma("mmap_size = 268435456"),le.pragma("temp_store = MEMORY"),le.pragma("busy_timeout = 5000"),le.pragma("journal_size_limit = 67108864"),le.pragma("wal_autocheckpoint = 1000"),le.exec(fi),_i(le);try{le.exec("PRAGMA optimize")}catch{}return le}var le,w=N(()=>{"use strict";Bn();P();hi();le=null});import Le from"chalk";import{formatDistanceToNowStrict as gm,parseISO as fm}from"date-fns";function K(e,t){if(!e)return"";let s=e.replace(/\s+/g," ").trim();return s.length<=t?s:s.slice(0,t-1)+"\u2026"}function J(e){if(!e)return"";try{return gm(fm(e),{addSuffix:!0})}catch{return""}}function X(e){return e.slice(0,8)}function Yn(e,t){if(!t)return e;let s=t.split(/\s+/).filter(r=>r.length>1).map(r=>r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));if(s.length===0)return e;let n=new RegExp(`(${s.join("|")})`,"gi");return e.replace(n,r=>Le.bgYellow.black(r))}var c,v=N(()=>{"use strict";c={dim:Le.gray,bold:Le.bold,project:Le.cyan,user:Le.blue,assistant:Le.green,tool:Le.magenta,warn:Le.yellow,err:Le.red,ok:Le.green,accent:Le.hex("#f97316")}});import{existsSync as Di,readFileSync as pg,writeFileSync as mg,unlinkSync as gg}from"node:fs";import{join as fg}from"node:path";function Kt(){if(!Di(ht))return null;try{let e=pg(ht,"utf8"),t=JSON.parse(e);return typeof t.license_jwt!="string"||t.license_jwt.length===0?null:t}catch{return null}}function Ts(e){F(),mg(ht,JSON.stringify(e,null,2)+`
672
- `,{mode:384})}function $i(){Di(ht)&&gg(ht)}var ht,At=N(()=>{"use strict";P();ht=fg(x,"license.json")});var Pi,or,Fi,ji,Ui=N(()=>{"use strict";Pi=`-----BEGIN PUBLIC KEY-----
733
+ `});var Or={};Z(Or,{closeDb:()=>pf,getDb:()=>E});import*as xa from"sqlite-vec";function E(){if(V)return V;j(),V=new Dt(ee),xa.load(V),V.pragma("cache_size = -64000"),V.pragma("mmap_size = 268435456"),V.pragma("temp_store = MEMORY"),V.pragma("busy_timeout = 5000"),V.pragma("journal_size_limit = 67108864"),V.pragma("wal_autocheckpoint = 1000"),V.exec(Ta),Ra(V);try{V.exec("PRAGMA optimize")}catch{}return V}function pf(){if(V){try{V.exec("PRAGMA optimize")}catch{}try{V.exec("INSERT INTO messages_fts(messages_fts, rank) VALUES('merge', 4);")}catch{}try{V.exec("INSERT INTO sessions_fts(sessions_fts, rank) VALUES('merge', 4);")}catch{}try{V.pragma("wal_checkpoint(TRUNCATE)")}catch{}V.close(),V=null}}var V,R=C(()=>{"use strict";Un();D();ka();V=null});import Me from"chalk";import{formatDistanceToNowStrict as Sf,parseISO as wf}from"date-fns";function K(e,t){if(!e)return"";let n=e.replace(/\s+/g," ").trim();return n.length<=t?n:n.slice(0,t-1)+"\u2026"}function G(e){if(!e)return"";try{return Sf(wf(e),{addSuffix:!0})}catch{return""}}function X(e){return e.slice(0,8)}function $r(e,t){if(!t)return e;let n=t.split(/\s+/).filter(r=>r.length>1).map(r=>r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"));if(n.length===0)return e;let s=new RegExp(`(${n.join("|")})`,"gi");return e.replace(s,r=>Me.bgYellow.black(r))}var c,$=C(()=>{"use strict";c={dim:Me.gray,bold:Me.bold,project:Me.cyan,user:Me.blue,assistant:Me.green,tool:Me.magenta,warn:Me.yellow,err:Me.red,ok:Me.green,accent:Me.hex("#f97316")}});import{existsSync as Wa,readFileSync as E_,writeFileSync as b_,unlinkSync as S_}from"node:fs";import{join as w_}from"node:path";function cn(){if(!Wa(kt))return null;try{let e=E_(kt,"utf8"),t=JSON.parse(e);return typeof t.license_jwt!="string"||t.license_jwt.length===0?null:t}catch{return null}}function Xn(e){j(),b_(kt,JSON.stringify(e,null,2)+`
734
+ `,{mode:384})}function Xa(){Wa(kt)&&S_(kt)}var kt,Ft=C(()=>{"use strict";D();kt=w_(x,"license.json")});var Ga,Yr,Ja,za,Ya=C(()=>{"use strict";Ga=`-----BEGIN PUBLIC KEY-----
673
735
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZysO2FffTLdyxQnTmnt78/ayvqz9
674
736
  kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
675
737
  -----END PUBLIC KEY-----
676
- `,or="ES256",Fi="clauderecall.com",ji="clauderecall-cli"});import{jwtVerify as _g,importSPKI as hg}from"jose";async function Eg(){return Rs||(Rs=await hg(Pi,or),Rs)}async function Ot(e){try{let t=await Eg(),{payload:s}=await _g(e,t,{issuer:Fi,audience:ji,algorithms:[or]});return{valid:!0,claims:s}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}var Rs,xs=N(()=>{"use strict";Ui();Rs=null});import{createHash as bg}from"node:crypto";import{hostname as Sg,userInfo as yg,platform as wg,arch as Tg}from"node:os";function vt(){let e="unknown";try{e=yg().username}catch{}let t=[Sg(),e,wg(),Tg()];return bg("sha256").update(t.join("\0")).digest("hex")}var ks=N(()=>{"use strict"});function tt(){let e=process.env.RECALL_API_BASE;if(e&&e.length>0){let t=e.replace(/\/$/,""),s;try{s=new URL(t)}catch{throw new Error(`RECALL_API_BASE is not a valid URL: ${t}`)}let n=s.hostname==="127.0.0.1"||s.hostname==="localhost"||s.hostname==="::1";if(s.protocol==="https:"||s.protocol==="http:"&&n)return t;throw new Error(`RECALL_API_BASE must be HTTPS, or HTTP with loopback hostname. Got: ${t}`)}return"https://clauderecall.com"}var Vt=N(()=>{"use strict"});import{existsSync as Rg,readFileSync as xg,writeFileSync as kg}from"node:fs";import{join as Cg}from"node:path";function Cs(){if(!Rg(ir))return null;try{let e=JSON.parse(xg(ir,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function Og(e){F(),kg(ir,JSON.stringify(e,null,2)+`
677
- `,{mode:384})}async function vg(e,t){let s=null,n=null;try{s=new AbortController,n=setTimeout(()=>s?.abort(),Ag);let r=await fetch(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({license_key:e}),signal:s.signal});if(!r.ok)return null;let o=await r.json();return typeof o?.revoked!="boolean"?null:o}catch{return null}finally{n&&clearTimeout(n)}}async function Bi(e,t={}){let s=Cs(),n=t.apiUrl??`${tt()}/api/license/check`,r=s?.license_key===e,o=!s||!r||Date.now()-new Date(s.last_checked_at).getTime()>=Lg;if(!t.force&&!o)return s;let i=await vg(e,n);if(!i)return r?s:null;let a={license_key:e,last_checked_at:new Date().toISOString(),revoked:i.revoked,reason:i.reason??null};return Og(a),a}function Hi(e){let t=Cs();return!t||t.license_key!==e?null:t.revoked?{revoked:!0,reason:t.reason?`license revoked: ${t.reason}`:"license revoked by issuer"}:Date.now()-new Date(t.last_checked_at).getTime()>Ng?{revoked:!0,reason:"license has not been validated with the server in 30+ days. Reconnect to the internet and run `recall license check`"}:null}var ir,Lg,Ng,Ag,ar=N(()=>{"use strict";P();Vt();ir=Cg(x,"license-check.json"),Lg=1440*60*1e3,Ng=720*60*60*1e3,Ag=1e4});function Mg(e=Date.now()){return e<cr}function Xi(e=Date.now()){return Mg(e)?`(one-time purchase, $${Ig} through May 2026 Founder pricing, $${Wi} from June, lifetime updates either way)`:`(one-time purchase, $${Wi}, lifetime updates)`}var cr,Ig,Wi,lr=N(()=>{"use strict";cr=Date.UTC(2026,5,1,7,0,0),Ig="29.69",Wi="49.69"});function pr(e){let t=e.now??Date.now(),s=e.status.expires_at??null,n=s?new Date(s).getTime():null,r=n!==null&&n-t<Dg;if(e.status.tier==="pro"&&r&&n!==null){let o=Math.max(0,Math.floor((n-t)/36e5));if(o<=24)return{kind:"trial-near-expiry",message:`Your Pro trial ends in ${o} hours. Lock in Founder pricing ($29.69 lifetime, ends May 31): ${dr}`}}if(e.status.tier==="free"&&r&&n!==null&&n<t&&Math.floor((t-n)/36e5)<720)return{kind:"trial-expired",message:`Your Pro trial ended. Your data is intact; only Pro features are gated. Founder pricing ($29.69 lifetime, ends May 31): ${dr}`};if(e.status.tier==="free"){let o=cr-t;if(o>0&&o<=3*ur){let i=Math.max(1,Math.ceil(o/ur));return{kind:"founder-near-deadline",message:`Founder pricing ($29.69 lifetime) ends in ${i} day${i===1?"":"s"}. After May 31, lifetime is $49.69: ${dr}`}}}return null}var dr,ur,Dg,Ji=N(()=>{"use strict";lr();dr="https://clauderecall.com/pricing",ur=1440*60*1e3,Dg=60*ur});var Zt={};he(Zt,{getLicenseStatus:()=>Te,isPro:()=>Pg,performRevocationCheck:()=>mr,printTrialBannerIfAny:()=>Fg,requireProOrExit:()=>Ne});async function Te(){let e=Kt();if(!e)return{tier:"free"};let t=await Ot(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==vt())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let s=Hi(e.license_key);return s?.revoked?{tier:"free",invalid_reason:s.reason}:$g(e,t.claims)}async function mr(e){let t=Kt();if(!t)return{ran:!1,revoked:!1,reason:null,last_checked_at:null};let s=await Bi(t.license_key,{force:e?.force??!1});return s?{ran:!0,revoked:s.revoked,reason:s.reason,last_checked_at:s.last_checked_at}:{ran:!0,revoked:!1,reason:null,last_checked_at:null}}function $g(e,t){let s=t.test_mode===!0&&process.env.NODE_ENV==="production";return{tier:s?"free":"pro",key_short:e.key_short,customer_email:e.customer_email,activated_at:e.activated_at,test_mode:e.test_mode,...s?{test_mode_blocked:!0}:{},expires_at:typeof t.exp=="number"?new Date(t.exp*1e3).toISOString():null}}async function Pg(){return(await Te()).tier==="pro"}async function Ne(e){let t=await Te();if(t.tier==="pro")return;let s=pr({status:t});s&&process.stderr.write(`
678
- ${s.message}
738
+ `,Yr="ES256",Ja="clauderecall.com",za="clauderecall-cli"});import{jwtVerify as y_,importSPKI as T_}from"jose";async function R_(){return Gn||(Gn=await T_(Ga,Yr),Gn)}async function jt(e){try{let t=await R_(),{payload:n}=await y_(e,t,{issuer:Ja,audience:za,algorithms:[Yr]});return{valid:!0,claims:n}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}var Gn,Jn=C(()=>{"use strict";Ya();Gn=null});import{createHash as k_}from"node:crypto";import{hostname as x_,userInfo as C_,platform as A_,arch as L_}from"node:os";function Ut(){let e="unknown";try{e=C_().username}catch{}let t=[x_(),e,A_(),L_()];return k_("sha256").update(t.join("\0")).digest("hex")}var zn=C(()=>{"use strict"});function lt(){let e=process.env.RECALL_API_BASE;if(e&&e.length>0){let t=e.replace(/\/$/,""),n;try{n=new URL(t)}catch{throw new Error(`RECALL_API_BASE is not a valid URL: ${t}`)}let s=n.hostname==="127.0.0.1"||n.hostname==="localhost"||n.hostname==="::1";if(n.protocol==="https:"||n.protocol==="http:"&&s)return t;throw new Error(`RECALL_API_BASE must be HTTPS, or HTTP with loopback hostname. Got: ${t}`)}return"https://clauderecall.com"}var ln=C(()=>{"use strict"});import{existsSync as N_,readFileSync as O_,writeFileSync as v_}from"node:fs";import{join as I_}from"node:path";function Yn(){if(!N_(qr))return null;try{let e=JSON.parse(O_(qr,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function P_(e){j(),v_(qr,JSON.stringify(e,null,2)+`
739
+ `,{mode:384})}async function F_(e,t){let n=null,s=null;try{n=new AbortController,s=setTimeout(()=>n?.abort(),$_);let r=await fetch(t,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({license_key:e}),signal:n.signal});if(!r.ok)return null;let o=await r.json();return typeof o?.revoked!="boolean"?null:o}catch{return null}finally{s&&clearTimeout(s)}}async function qa(e,t={}){let n=Yn(),s=t.apiUrl??`${lt()}/api/license/check`,r=n?.license_key===e,o=!n||!r||Date.now()-new Date(n.last_checked_at).getTime()>=M_;if(!t.force&&!o)return n;let i=await F_(e,s);if(!i)return r?n:null;let a={license_key:e,last_checked_at:new Date().toISOString(),revoked:i.revoked,reason:i.reason??null};return P_(a),a}function Va(e){let t=Yn();return!t||t.license_key!==e?null:t.revoked?{revoked:!0,reason:t.reason?`license revoked: ${t.reason}`:"license revoked by issuer"}:Date.now()-new Date(t.last_checked_at).getTime()>D_?{revoked:!0,reason:"license has not been validated with the server in 30+ days. Reconnect to the internet and run `recall license check`"}:null}var qr,M_,D_,$_,Vr=C(()=>{"use strict";D();ln();qr=I_(x,"license-check.json"),M_=1440*60*1e3,D_=720*60*60*1e3,$_=1e4});function U_(e=Date.now()){return e<Kr}function Qa(e=Date.now()){return U_(e)?`(one-time purchase, $${j_} through May 2026 Founder pricing, $${Ka} from June, lifetime updates either way)`:`(one-time purchase, $${Ka}, lifetime updates)`}var Kr,j_,Ka,Qr=C(()=>{"use strict";Kr=Date.UTC(2026,5,1,7,0,0),j_="29.69",Ka="49.69"});function to(e){let t=e.now??Date.now(),n=e.status.expires_at??null,s=n?new Date(n).getTime():null,r=s!==null&&s-t<B_;if(e.status.tier==="pro"&&r&&s!==null){let o=Math.max(0,Math.floor((s-t)/36e5));if(o<=24)return{kind:"trial-near-expiry",message:`Your Pro trial ends in ${o} hours. Lock in Founder pricing ($29.69 lifetime, ends May 31): ${Zr}`}}if(e.status.tier==="free"&&r&&s!==null&&s<t&&Math.floor((t-s)/36e5)<720)return{kind:"trial-expired",message:`Your Pro trial ended. Your data is intact; only Pro features are gated. Founder pricing ($29.69 lifetime, ends May 31): ${Zr}`};if(e.status.tier==="free"){let o=Kr-t;if(o>0&&o<=3*eo){let i=Math.max(1,Math.ceil(o/eo));return{kind:"founder-near-deadline",message:`Founder pricing ($29.69 lifetime) ends in ${i} day${i===1?"":"s"}. After May 31, lifetime is $49.69: ${Zr}`}}}return null}var Zr,eo,B_,Za=C(()=>{"use strict";Qr();Zr="https://clauderecall.com/pricing",eo=1440*60*1e3,B_=60*eo});var dn={};Z(dn,{getLicenseStatus:()=>Ae,isPro:()=>W_,performRevocationCheck:()=>no,printTrialBannerIfAny:()=>X_,requireProOrExit:()=>pe});async function Ae(){let e=cn();if(!e)return{tier:"free"};let t=await jt(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==Ut())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let n=Va(e.license_key);return n?.revoked?{tier:"free",invalid_reason:n.reason}:H_(e,t.claims)}async function no(e){let t=cn();if(!t)return{ran:!1,revoked:!1,reason:null,last_checked_at:null};let n=await qa(t.license_key,{force:e?.force??!1});return n?{ran:!0,revoked:n.revoked,reason:n.reason,last_checked_at:n.last_checked_at}:{ran:!0,revoked:!1,reason:null,last_checked_at:null}}function H_(e,t){let n=t.test_mode===!0&&process.env.NODE_ENV==="production";return{tier:n?"free":"pro",key_short:e.key_short,customer_email:e.customer_email,activated_at:e.activated_at,test_mode:e.test_mode,...n?{test_mode_blocked:!0}:{},expires_at:typeof t.exp=="number"?new Date(t.exp*1e3).toISOString():null}}async function W_(){return(await Ae()).tier==="pro"}async function pe(e){let t=await Ae();if(t.tier==="pro")return;let n=to({status:t});n&&process.stderr.write(`
740
+ ${n.message}
679
741
  `),process.stderr.write(`
680
742
  ${e} is a Pro feature.
681
743
 
682
744
  Buy a license at https://clauderecall.com/pricing
683
- ${Xi()}.
745
+ ${Qa()}.
684
746
  Then run: recall activate <license-key>
685
747
 
686
748
  `),t.invalid_reason&&process.stderr.write(`(stored license is invalid: ${t.invalid_reason})
687
749
 
688
- `),process.exit(1)}async function Fg(){let e=await Te(),t=pr({status:e});return t?(process.stderr.write(`
750
+ `),process.exit(1)}async function X_(){let e=await Ae(),t=to({status:e});return t?(process.stderr.write(`
689
751
  ${t.message}
690
752
 
691
- `),!0):!1}var de=N(()=>{"use strict";At();xs();ks();ar();lr();Ji()});import{writeFileSync as Nk,readFileSync as jg,existsSync as Ug}from"node:fs";import{join as Bg}from"node:path";function Gi(){if(!Ug(Ls))return null;try{return jg(Ls,"utf8").trim()}catch{return null}}var Ls,gr=N(()=>{"use strict";P();Ls=Bg(x,"daemon.token")});import{existsSync as Ki,readFileSync as Gg,writeFileSync as Xk,unlinkSync as Yg}from"node:fs";import{join as _r}from"node:path";function qg(){if(!Ki(fr))return null;try{let e=JSON.parse(Gg(fr,"utf8"));return typeof e.pid!="number"||typeof e.port!="number"?null:e}catch{return null}}function hr(){for(let e of[fr,zg,Ls])if(Ki(e))try{Yg(e)}catch{}}function Er(e){try{return process.kill(e,0),!0}catch{return!1}}function Q(){let e=qg();return e?Er(e.pid)?e:(hr(),null):null}var fr,zg,Ns,qe=N(()=>{"use strict";P();gr();fr=_r(x,"daemon.pid"),zg=_r(x,"daemon.port"),Ns=_r(x,"daemon.log")});import{existsSync as Qg}from"node:fs";import{dirname as Qi}from"node:path";import{fileURLToPath as ef}from"node:url";function ee(){if(As)return As;let e=Qi(ef(import.meta.url));for(;!Qg(`${e}/package.json`);){let t=Qi(e);if(t===e)throw new Error(`package.json not found walking up from ${import.meta.url}`);e=t}return As=e,As}var As,Ke=N(()=>{"use strict";As=null});import{writeFileSync as Vf}from"node:fs";import{join as Zf}from"node:path";function xr(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Ps(e){return _().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e)?.alias??null}function e_(){return _().prepare("SELECT session_id, alias, updated_at, previous_aliases FROM session_aliases").all().map(t=>({session_id:t.session_id,alias:t.alias,updated_at:t.updated_at,previous_aliases:xr(t.previous_aliases)}))}function Mt(e,t){let s=t.trim();if(!s)throw new Error("alias must be non-empty");let n=_(),r=new Date().toISOString(),o=n.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e),i=[];return o&&(i=xr(o.previous_aliases),o.alias!==s&&i.push({alias:o.alias,replaced_at:r})),n.prepare(`INSERT INTO session_aliases (session_id, alias, updated_at, previous_aliases)
753
+ `),!0):!1}var ge=C(()=>{"use strict";Ft();Jn();zn();Vr();Qr();Za()});import{appendFileSync as G_}from"node:fs";import{join as J_}from"node:path";function Bt(e,t){let s=(new Error().stack??"").split(`
754
+ `).slice(2,4).map(i=>i.trim().replace(/file:\/\/.*?(\bdist\b|\bsrc\b)/g,"$1")).join(" << "),o=`${new Date().toISOString()} ${Y_} ${e}: ${t} | caller: ${s}
755
+ `;try{G_(z_,o)}catch{}}var z_,Y_,so=C(()=>{"use strict";D();z_=J_(x,"daemon.log"),Y_="[state-files-audit]"});import{writeFileSync as $N,readFileSync as q_,existsSync as V_}from"node:fs";import{join as K_}from"node:path";function ec(){if(!V_(qn))return null;try{return q_(qn,"utf8").trim()}catch{return null}}var qn,ro=C(()=>{"use strict";D();so();qn=K_(x,"daemon.token")});import{execFileSync as nh}from"node:child_process";function rh(e){for(let t of sh)if(e.includes(t))return!0;return!1}function oh(e){if(e.length<5)return!1;let t=[e[5]??"",e[4]??""];for(let n of t)if(n&&(n.endsWith("/grep")||n==="grep"||n.endsWith("/awk")||n==="awk"||n.endsWith("/rg")||n==="rg"))return!0;return!1}function st(e={}){let t=e.psOutput??ih(),n=new Set(e.excludePids??[]),s=[];for(let r of t.split(`
756
+ `)){let o=r.trim();if(!o||!rh(o))continue;let i=o.split(/\s+/);if(oh(i)||i.length<5)continue;let a=Number(i[0]),d=Number(i[1]),l=i[2];!Number.isFinite(a)||!Number.isFinite(d)||n.has(a)||s.push({pid:a,ppid:d,etimeSeconds:ah(l),etime:l,command:o})}return s.sort((r,o)=>r.etimeSeconds-o.etimeSeconds),s}function ih(){try{return nh("ps",["-axo","pid,ppid,etime,rss,pcpu,command"],{encoding:"utf8",timeout:2e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[daemon-processes] ps -axo failed: ${t}
757
+ `),""}}function ah(e){if(!e)return 0;let t=0,n=e,s=e.indexOf("-");s>=0&&(t=rc(e.slice(0,s)),n=e.slice(s+1));let r=n.split(":").map(rc),o=0,i=0,a=0;return r.length===3?[o,i,a]=r:r.length===2?[i,a]=r:r.length===1&&(a=r[0]),t*86400+o*3600+i*60+a}function rc(e){let t=Number(e);return Number.isFinite(t)?t:0}function un(e,t=new Date){if(!Number.isFinite(e)||e<0)return null;let n=t.getTime()-e*1e3;if(!Number.isFinite(n))return null;let s=new Date(n),r=String(s.getHours()).padStart(2,"0"),o=String(s.getMinutes()).padStart(2,"0"),i=String(s.getSeconds()).padStart(2,"0");return`${r}:${o}:${i}`}var sh,mn=C(()=>{"use strict";sh=["dist/daemon/entrypoint.js"]});import{existsSync as oc,readFileSync as ch,writeFileSync as eO,unlinkSync as lh}from"node:fs";import{basename as dh,join as oo}from"node:path";function Kn(){return{pid:ic,port:uh,token:qn}}function mh(){return ac(ic)}function ac(e){if(!oc(e))return null;try{let t=JSON.parse(ch(e,"utf8"));return typeof t.pid!="number"||typeof t.port!="number"?null:t}catch{return null}}function cc(e={}){let t=e.paths??Kn(),n=e.listDaemons??(()=>st({excludePids:[process.pid]})),s=e.isProcessAlive??dc,r=n();if(r.length>0){let i=r.map(a=>a.pid).join(",");return Bt("clear-refused",`${r.length} live daemon(s) visible via ps: ${i}`),{deleted:!1,reason:`refused: ${r.length} live daemon(s) in ps (${i})`,cleared:[]}}let o=ac(t.pid);return o&&s(o.pid)?(Bt("clear-refused",`pidfile owner pid=${o.pid} alive but not surfaced by ps`),{deleted:!1,reason:`refused: pidfile owner pid=${o.pid} alive`,cleared:[]}):ph({paths:t})}function ph(e={}){let t=e.paths??Kn(),n=[];for(let s of[t.pid,t.port,t.token])if(oc(s))try{lh(s),n.push(dh(s))}catch{}return Bt("clear-force",`cleared: ${n.join(",")||"(none -- files already absent)"}`),{deleted:!0,reason:`cleared ${n.length} file(s)`,cleared:n}}function lc(){let e=cc();e.deleted||Bt("clear-refused-shim",e.reason)}function pn(e){return dc(e)}function dc(e){try{return process.kill(e,0),!0}catch{return!1}}function oe(){let e=mh();return e?pn(e.pid)?e:(cc(),null):null}var ic,uh,Vn,Xe=C(()=>{"use strict";D();ro();mn();so();ic=oo(x,"daemon.pid"),uh=oo(x,"daemon.port"),Vn=oo(x,"daemon.log")});import{existsSync as hh}from"node:fs";import{dirname as pc}from"node:path";import{fileURLToPath as Eh}from"node:url";function te(){if(Qn)return Qn;let e=pc(Eh(import.meta.url));for(;!hh(`${e}/package.json`);){let t=pc(e);if(t===e)throw new Error(`package.json not found walking up from ${import.meta.url}`);e=t}return Qn=e,Qn}var Qn,Ge=C(()=>{"use strict";Qn=null});import{writeFileSync as wE}from"node:fs";import{join as yE}from"node:path";function po(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function os(e){return E().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e)?.alias??null}function RE(){return E().prepare("SELECT session_id, alias, updated_at, previous_aliases FROM session_aliases").all().map(t=>({session_id:t.session_id,alias:t.alias,updated_at:t.updated_at,previous_aliases:po(t.previous_aliases)}))}function Ht(e,t){let n=t.trim();if(!n)throw new Error("alias must be non-empty");let s=E(),r=new Date().toISOString(),o=s.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e),i=[];return o&&(i=po(o.previous_aliases),o.alias!==n&&i.push({alias:o.alias,replaced_at:r})),s.prepare(`INSERT INTO session_aliases (session_id, alias, updated_at, previous_aliases)
692
758
  VALUES (?, ?, ?, ?)
693
759
  ON CONFLICT(session_id) DO UPDATE SET
694
760
  alias = excluded.alias,
695
761
  updated_at = excluded.updated_at,
696
- previous_aliases = excluded.previous_aliases`).run(e,s,r,JSON.stringify(i)),Da(),{session_id:e,alias:s,updated_at:r,previous_aliases:i}}function Ma(e){let t=_(),s=new Date().toISOString(),n=t.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e);if(!n)return;let r=xr(n.previous_aliases);r.push({alias:n.alias,replaced_at:s}),t.prepare(`UPDATE session_aliases SET alias = '', updated_at = ?, previous_aliases = ?
697
- WHERE session_id = ?`).run(s,JSON.stringify(r),e),Da()}function Da(){try{F();let e=e_(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};Vf(Qf,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}var Qf,Et=N(()=>{"use strict";w();P();Qf=Zf(x,"aliases.json")});import{writeFileSync as x_}from"node:fs";import{join as k_}from"node:path";function L_(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function Ga(e,t){let s=L_(t);if(!s)throw new Error("tag must contain at least one alphanumeric character");let n=_(),r=new Date().toISOString();return n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,s)?{tag:s,added:!1}:(n.transaction(()=>{n.prepare("INSERT INTO session_tags (session_id, tag, created_at) VALUES (?, ?, ?)").run(e,s,r),n.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'add', ?)").run(e,s,r)})(),N_(),{tag:s,added:!0})}function Ya(e){return _().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function N_(){try{F();let e=_(),t=e.prepare("SELECT session_id, tag, created_at FROM session_tags ORDER BY session_id, tag").all(),s=e.prepare("SELECT id, session_id, tag, action, at FROM tag_events ORDER BY at ASC, id ASC").all(),n={schema:"claude-recall.tags.v1",backed_up_at:new Date().toISOString(),current:t,events:s};x_(C_,JSON.stringify(n,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var C_,Cr=N(()=>{"use strict";w();P();C_=k_(x,"tags.json")});function A_(e,t){let s=e.filter(o=>o.content_text&&o.content_text.trim().length>0);if(s.length<=t)return s;let n=new Set;n.add(0),n.add(s.length-1);let r=(s.length-2)/Math.max(1,t-2);for(let o=1;o<t-1;o++)n.add(Math.floor(o*r));return Array.from(n).sort((o,i)=>o-i).slice(0,t).map(o=>s[o])}function za(e){let t=_(),s={limit:e.limit??500},n=e.sessionIds&&e.sessionIds.length>0,r=n?"1=1":"s.message_count > 2";if(n){let i=e.sessionIds.map((a,d)=>`@sid_${d}`).join(", ");r+=` AND s.id IN (${i})`,e.sessionIds.forEach((a,d)=>{s[`sid_${d}`]=a})}return e.untaggedOnly&&(r+=" AND NOT EXISTS (SELECT 1 FROM session_tags st WHERE st.session_id = s.id)"),e.project&&(r+=" AND p.name = @project",s.project=e.project),e.collectionId&&(r+=" AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id = @col)",s.col=e.collectionId),t.prepare(`SELECT s.id, p.name AS project, s.git_branch,
762
+ previous_aliases = excluded.previous_aliases`).run(e,n,r,JSON.stringify(i)),Kc(),{session_id:e,alias:n,updated_at:r,previous_aliases:i}}function Vc(e){let t=E(),n=new Date().toISOString(),s=t.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e);if(!s)return;let r=po(s.previous_aliases);r.push({alias:s.alias,replaced_at:n}),t.prepare(`UPDATE session_aliases SET alias = '', updated_at = ?, previous_aliases = ?
763
+ WHERE session_id = ?`).run(n,JSON.stringify(r),e),Kc()}function Kc(){try{j();let e=RE(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};wE(TE,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}var TE,Ct=C(()=>{"use strict";R();D();TE=yE(x,"aliases.json")});import{writeFileSync as qE}from"node:fs";import{join as VE}from"node:path";function QE(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function cl(e,t){let n=QE(t);if(!n)throw new Error("tag must contain at least one alphanumeric character");let s=E(),r=new Date().toISOString();return s.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,n)?{tag:n,added:!1}:(s.transaction(()=>{s.prepare("INSERT INTO session_tags (session_id, tag, created_at) VALUES (?, ?, ?)").run(e,n,r),s.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'add', ?)").run(e,n,r)})(),ZE(),{tag:n,added:!0})}function ll(e){return E().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function ZE(){try{j();let e=E(),t=e.prepare("SELECT session_id, tag, created_at FROM session_tags ORDER BY session_id, tag").all(),n=e.prepare("SELECT id, session_id, tag, action, at FROM tag_events ORDER BY at ASC, id ASC").all(),s={schema:"claude-recall.tags.v1",backed_up_at:new Date().toISOString(),current:t,events:n};qE(KE,JSON.stringify(s,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var KE,fo=C(()=>{"use strict";R();D();KE=VE(x,"tags.json")});function eb(e,t){let n=e.filter(o=>o.content_text&&o.content_text.trim().length>0);if(n.length<=t)return n;let s=new Set;s.add(0),s.add(n.length-1);let r=(n.length-2)/Math.max(1,t-2);for(let o=1;o<t-1;o++)s.add(Math.floor(o*r));return Array.from(s).sort((o,i)=>o-i).slice(0,t).map(o=>n[o])}function dl(e){let t=E(),n={limit:e.limit??500},s=e.sessionIds&&e.sessionIds.length>0,r=s?"1=1":"s.message_count > 2";if(s){let i=e.sessionIds.map((a,d)=>`@sid_${d}`).join(", ");r+=` AND s.id IN (${i})`,e.sessionIds.forEach((a,d)=>{n[`sid_${d}`]=a})}return e.untaggedOnly&&(r+=" AND NOT EXISTS (SELECT 1 FROM session_tags st WHERE st.session_id = s.id)"),e.project&&(r+=" AND p.name = @project",n.project=e.project),e.collectionId&&(r+=" AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id = @col)",n.col=e.collectionId),t.prepare(`SELECT s.id, p.name AS project, s.git_branch,
698
764
  NULLIF(sa.alias, '') AS alias,
699
765
  COALESCE(s.first_user_message, '') AS first_user_message
700
766
  FROM sessions s
@@ -702,19 +768,83 @@ ${t.message}
702
768
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
703
769
  WHERE ${r}
704
770
  ORDER BY COALESCE(s.started_at, '') DESC
705
- LIMIT @limit`).all(s).map(i=>{let a=t.prepare(`SELECT role, COALESCE(content_text, '') AS content_text
771
+ LIMIT @limit`).all(n).map(i=>{let a=t.prepare(`SELECT role, COALESCE(content_text, '') AS content_text
706
772
  FROM messages WHERE session_id = ?
707
- ORDER BY COALESCE(timestamp, ''), rowid`).all(i.id),l=A_(a,5).map(u=>`${u.role}: ${u.content_text.slice(0,400)}`).join(`
773
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(i.id),l=eb(a,5).map(u=>`${u.role}: ${u.content_text.slice(0,400)}`).join(`
708
774
  ---
709
- `);return{id:i.id,project:i.project,git_branch:i.git_branch,alias:i.alias,first_user_message:i.first_user_message,message_sample:l,current_tags:Ya(i.id)}})}var qa=N(()=>{"use strict";w();Cr()});import{z as Re}from"zod";function Ka(e){let t=e.minTags??2,s=e.maxTags??4,n=e.untaggedOnly??!e.sessionId,r=["Auto-tag my Claude Recall sessions using the MCP tools available to you.","","1. Call `list_tags` first to see which tags I already use (prefer those for consistency over inventing new ones).","2. Call `list_sessions_to_tag` with these filters:"],o=[];return e.sessionId?(o.push("limit: 1"),r.push(` ${o.join(", ")}`),r.push(` Then match the session id ${e.sessionId} from the returned list.`)):(n&&o.push("untaggedOnly: true"),e.project&&o.push(`project: "${e.project}"`),e.collectionId&&o.push(`collectionId: "${e.collectionId}"`),o.push(`limit: ${e.limit??100}`),r.push(` ${o.join(", ")}`)),r.push(""),r.push(`3. For each session returned, look at the alias, first user message, git branch, and sampled messages. Pick ${t}-${s} concise, lowercase, hyphen-separated tags describing:`),r.push(" - domain/subsystem (auth, db, frontend, billing, etc.)"),r.push(" - kind of work (bugfix, feature, refactor, research)"),r.push(" - prominent tools or libraries if relevant"),r.push(""),r.push("4. Call `apply_tags` once per session to write the tags."),r.push(""),r.push("Work through EVERY session returned \u2014 do not stop partway. When done, reply with a short summary of how many sessions were tagged. Do not ask clarifying questions; just do the work."),r.join(`
710
- `)}var pN,mN,gN,fN,Va=N(()=>{"use strict";pN={project:Re.string().optional().describe("Exact project name match (optional)."),collectionId:Re.string().optional().describe("Restrict to sessions in this collection (optional)."),sessionId:Re.string().optional().describe("Full session UUID to tag just one session (optional)."),untaggedOnly:Re.boolean().optional().describe("Skip sessions that already have any tag (default: true)."),limit:Re.number().int().min(1).max(500).optional().describe("Max sessions to process (default: 100)."),minTags:Re.number().int().min(1).max(10).optional().describe("Minimum tags per session (default: 2)."),maxTags:Re.number().int().min(1).max(10).optional().describe("Maximum tags per session (default: 4).")};mN={sessionId:Re.string().describe("Session UUID (or 8+-char prefix) to summarize."),mode:Re.enum(["brief","detailed"]).optional().describe("brief = 3-5 bullets; detailed = paragraph + bullets. Default: brief.")},gN={sessionId:Re.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")},fN={sessionId:Re.string().describe("Session UUID (or 8+-char prefix) to find similar sessions to."),limit:Re.number().int().min(1).max(20).optional().describe("How many similar sessions to surface (default: 5).")}});function Za(e,t){let s=O_.get(e);if(!(!s||s.size===0))for(let n of s)try{n(t)}catch{}}var O_,Qa=N(()=>{"use strict";O_=new Map});import{existsSync as v_,statSync as I_}from"node:fs";import{delimiter as M_,join as D_}from"node:path";function ec(e){if(e.includes("/")||e.includes("\\")||e.includes(".."))return null;let t=(process.env.PATH??"").split(M_).filter(Boolean),s=process.platform==="win32"?[e,`${e}.exe`,`${e}.cmd`,`${e}.bat`]:[e];for(let n of t)for(let r of s){let o=D_(n,r);try{if(v_(o)&&I_(o).isFile())return o}catch{}}return null}function tc(e){if(process.platform!=="win32"||!e)return!1;let t=e.toLowerCase();return t.endsWith(".cmd")||t.endsWith(".bat")}var sc=N(()=>{"use strict"});var Us={};he(Us,{_resetClaudePathCacheForTests:()=>j_,buildScanPrompt:()=>rc,isClaudeCliAvailable:()=>ne,runClaudeCliScan:()=>X_,spawnClaudePrompt:()=>bt});import{spawn as $_}from"node:child_process";function nc(){if(Dt!==void 0&&ts!==void 0)return{path:Dt,available:ts};let e=ec("claude");return Dt=e??"claude",ts=e!==null,{path:Dt,available:ts}}function F_(){return nc().path}function ne(){return nc().available}function j_(){Dt=void 0,ts=void 0}function rc(e){return Ka({project:e.project,collectionId:e.collectionId,sessionId:e.sessionIds&&e.sessionIds.length===1?e.sessionIds[0]:void 0,untaggedOnly:e.untaggedOnly,limit:e.limit})}function U_(e,t){let s=t.get(e);return s||e.slice(0,8)}function B_(e){try{return za(e).map(s=>({id:s.id,label:s.alias&&s.alias.trim().length>0?s.alias:s.first_user_message&&s.first_user_message.trim().length>0?s.first_user_message.slice(0,60):s.id.slice(0,8)}))}catch{return[]}}function H_(e){let{scanId:t,total:s,labelTable:n}=e,r=new Set;return o=>{let i=o.trim();if(!i.startsWith("{"))return;let a;try{a=JSON.parse(i)}catch{return}if(!a||typeof a!="object")return;let d=a;if(!(d.type!=="assistant"||!d.message?.content))for(let l of d.message.content){if(l?.type!=="tool_use"||l.name!=="mcp__recall__apply_tags")continue;let u=l.input,p=typeof u?.sessionId=="string"?u.sessionId:null;!p||r.has(p)||(r.add(p),Za(t,{type:"progress",current:r.size,total:s,sessionId:p,sessionLabel:U_(p,n)}))}}}function W_(e){let t="";return s=>{t+=s.toString("utf8");let n=t.indexOf(`
711
- `);for(;n!==-1;){let r=t.slice(0,n);t=t.slice(n+1),r.length>0&&e(r),n=t.indexOf(`
712
- `)}}}async function X_(e,t={},s){let n=!!t.scanId,r=n?B_(e):[],o=new Map(r.map(d=>[d.id,d.label])),i=r.length,a;return n&&t.scanId&&(a=H_({scanId:t.scanId,total:i,labelTable:o})),oc({prompt:rc(e),allowedTools:P_.split(","),opts:t,onProgress:s,onStdoutLine:a,outputFormat:n?"stream-json":"json"})}async function bt(e,t,s={},n){return oc({prompt:e,allowedTools:t,opts:s,onProgress:n,outputFormat:"json"})}function oc(e){let{prompt:t,allowedTools:s,opts:n,onProgress:r,onStdoutLine:o,outputFormat:i}=e,a=["-p",t,"--output-format",i,"--allowedTools",s.join(","),"--permission-mode","bypassPermissions","--no-session-persistence"];return i==="stream-json"&&a.push("--verbose"),n.model&&a.push("--model",n.model),new Promise(d=>{let l=F_(),u=$_(l,a,{stdio:["ignore","pipe","pipe"],shell:tc(l)||process.platform==="win32"&&Dt==="claude"}),p=[],m=[],g=o?W_(o):void 0;u.stdout.on("data",h=>{p.push(h),g&&g(h)}),u.stderr.on("data",h=>{if(m.push(h),r){let E=h.toString("utf8").trim();E&&r(E)}});let f=setTimeout(()=>{u.kill("SIGKILL")},1800*1e3);u.on("close",h=>{clearTimeout(f),d({success:h===0,stdout:Buffer.concat(p).toString("utf8"),stderr:Buffer.concat(m).toString("utf8"),exitCode:h})}),u.on("error",h=>{clearTimeout(f),d({success:!1,stdout:"",stderr:String(h),exitCode:null})})})}var P_,Dt,ts,Be=N(()=>{"use strict";qa();Va();Qa();sc();P_=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});var dc={};he(dc,{downloadModel:()=>Or,getModelDir:()=>Xs,isModelInstalled:()=>St,uninstallModel:()=>vr});import{existsSync as Ar,mkdirSync as cc,rmSync as Nr,createWriteStream as lh,statSync as dh}from"node:fs";import{join as Ws}from"node:path";import{createHash as uh}from"node:crypto";import{readFile as ph}from"node:fs/promises";function Xs(){return Ws(x,"models","BAAI","bge-base-en-v1.5")}function St(){let e=Xs();return lc.every(t=>Ar(Ws(e,t.path)))}async function Or(e){let t=Xs();cc(t,{recursive:!0}),cc(Ws(t,"onnx"),{recursive:!0});for(let s of lc){let n=Ws(t,s.path),r=mh+s.path,o=0;Ar(n)&&(o=dh(n).size);let i={};o>0&&(i.Range=`bytes=${o}-`);let a=await fetch(r,{headers:i});if(!a.ok&&a.status!==206)throw new Error(`Failed to download ${s.path}: HTTP ${a.status}`);let d=a.headers.get("content-length"),l=d?o+Number(d):0,u=a.body;if(!u)throw new Error(`No response body for ${s.path}`);let p=lh(n,{flags:o>0?"a":"w"}),m=u.getReader(),g=o;for(;;){let{done:E,value:b}=await m.read();if(E)break;p.write(Buffer.from(b)),g+=b.byteLength,e?.(s.path,g,l)}if(p.end(),await new Promise((E,b)=>{p.on("finish",E),p.on("error",b)}),s.sha256==="TODO_PLACEHOLDER")throw Nr(n),new Error(`Refusing to install: SHA-256 not pinned for ${s.path}. Update model-download.ts.`);let f=await ph(n);if(uh("sha256").update(f).digest("hex")!==s.sha256)throw Nr(n),new Error(`SHA-256 mismatch for ${s.path}`)}}function vr(){let e=Xs();Ar(e)&&Nr(e,{recursive:!0,force:!0})}var mh,lc,Js=N(()=>{"use strict";P();mh="https://huggingface.co/BAAI/bge-base-en-v1.5/resolve/main/",lc=[{path:"config.json",sha256:"bc00af31a4a31b74040d73370aa83b62da34c90b75eb77bfa7db039d90abd591"},{path:"tokenizer.json",sha256:"d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"},{path:"tokenizer_config.json",sha256:"9261e7d79b44c8195c1cada2b453e55b00aeb81e907a6664974b4d7776172ab3"},{path:"onnx/model.onnx",sha256:"9bc579acdba21c253c62a9bf866891355a63ffa3442b52c8a37d75b2ccb91848"}]});var uc,pc=N(()=>{"use strict";uc="BAAI/bge-base-en-v1.5"});var Dr={};he(Dr,{EmbedderUnavailableError:()=>ns,embed:()=>$t,embedQuery:()=>yh,getEmbedderStatus:()=>Ze,loadEmbedder:()=>nt,unloadEmbedder:()=>wh});import{Worker as fh}from"node:worker_threads";import{join as _h}from"node:path";import{existsSync as hh}from"node:fs";function Eh(){return _h(ee(),"dist","daemon","embedder-worker.js")}function bh(e){return["Semantic search is unavailable on this platform.","",`Reason: ${e.detail}`,"",`Platform: ${process.platform}/${process.arch}, Node ${process.version}`,"","Claude Recall supports macOS (arm64/x64), Linux (x64/arm64), and Windows (x64).","Core CLI features (search, list, context, daemon) work everywhere.","Only `recall semantic *` requires the on-device embedder.","","See: https://clauderecall.com/docs (Supported platforms) \u2014 or file an issue at","https://gitlab.com/clauderecallhq/clauderecallhq/-/issues with the platform line above."].join(`
713
- `)}function mc(e){for(let t of ss.values())t.reject(e);ss.clear()}function Sh(){if(Ve)return Ve;let e=Eh();if(!hh(e))throw new ns({kind:"bundle-missing",detail:"embedder-worker bundle not found \u2014 run `npm run build:cli` to emit it",path:e});let t=new fh(e);return t.on("message",s=>{let n=ss.get(s.id);n&&(ss.delete(s.id),n.resolve(s))}),t.on("error",s=>{console.error("[embedder-worker] thread error:",s);let n=s instanceof Error?s:new Error(String(s));mc(n),Ve=null,He=!1}),t.on("exit",s=>{s!==0&&(console.error(`[embedder-worker] exited with code ${s}`),mc(new Error(`embedder worker exited with code ${s}`))),Ve=null,He=!1}),Ve=t,t}function Gs(e){return new Promise((t,s)=>{let n;try{n=Sh()}catch(r){s(r instanceof Error?r:new Error(String(r)));return}ss.set(e.id,{resolve:t,reject:s}),n.postMessage(e)})}function Ys(){return Ir=Ir+1>>>0,String(Ir)}function Mr(e){if(!e.ok)throw e.unavailable?new ns({kind:"platform-unsupported",detail:"embedder worker reports the runtime is unavailable on this platform",underlying:new Error(e.error)}):new Error(e.error);return e}async function nt(){if(!(He&&Ve))try{Mr(await Gs({id:Ys(),type:"load"})),He=!0}catch(e){throw He=!1,e}}function Ze(){return{loaded:He,modelId:uc,dim:768}}async function $t(e){if(!He)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return Mr(await Gs({id:Ys(),type:"embed",texts:e})).embeddings.map(s=>new Float32Array(s))}async function yh(e){if(!He)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=Mr(await Gs({id:Ys(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function wh(){if(!Ve){He=!1;return}try{await Gs({id:Ys(),type:"unload"})}catch{}He=!1;let e=Ve;Ve=null;try{await e.terminate()}catch{}}var Ve,ss,Ir,He,ns,yt=N(()=>{"use strict";Ke();pc();Ve=null,ss=new Map,Ir=0,He=!1,ns=class extends Error{kind;path;underlying;cause;constructor(t){super(bh(t)),this.name="EmbedderUnavailableError",this.kind=t.kind,this.path=t.path,this.underlying=t.underlying,this.cause=t}}});var hc={};he(hc,{detectRunningClaudeOnWindows:()=>_c,ensureTransformersInstalled:()=>Fh});import{execFileSync as Lh,spawn as Nh}from"node:child_process";import{existsSync as Ah}from"node:fs";import{dirname as Oh,resolve as vh}from"node:path";import{fileURLToPath as Ih}from"node:url";function Dh(){let e=Oh(Ih(import.meta.url));return vh(e,"..","..")}async function $h(){try{return await import("@huggingface/transformers"),!0}catch{return!1}}function Ph(e){return new Promise(t=>{let s=["install","--no-save","--no-audit","--no-fund","--prefix",e,`@huggingface/transformers@${Mh}`],n=Nh("npm",s,{stdio:["ignore","inherit","inherit"],cwd:e,shell:process.platform==="win32"});n.on("error",r=>{t({ok:!1,error:`npm spawn failed: ${r instanceof Error?r.message:String(r)}`})}),n.on("exit",r=>{t(r===0?{ok:!0,action:"installed"}:{ok:!1,error:`npm install exited with code ${r}`})})})}function _c(){if(process.platform!=="win32")return!1;try{return Lh("tasklist",["/FI","IMAGENAME eq claude.exe"],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).toLowerCase().includes("claude.exe")}catch{return!1}}async function Fh(){if(await $h())return{ok:!0,action:"already-installed"};if(_c())return{ok:!1,error:"Close all Claude Code windows before running `recall semantic install` on Windows. claude.exe holds exclusive locks on shared dependency files (Windows kernel default), which causes the underlying npm install to fail with EBUSY. After closing Claude Code, rerun `recall semantic install` to complete the install."};let e=Dh();return Ah(e)?(console.log(`Installing @huggingface/transformers into ${e} ...`),Ph(e)):{ok:!1,error:`package root not found at ${e}`}}var Mh,Ec=N(()=>{"use strict";Mh="^4.2.0"});import{writeFileSync as vc,readFileSync as WA,existsSync as Ic,mkdirSync as Mc,readdirSync as XA}from"node:fs";import{join as Ks}from"node:path";function RE(){F(),Ic(Hr)||Mc(Hr,{recursive:!0})}function xE(){F(),Ic(Wr)||Mc(Wr,{recursive:!0})}function Dc(e){try{return JSON.parse(e)}catch{return e}}function Xr(e){return{id:e.id,source_session_id:e.source_session_id,target_session_id:e.target_session_id,link_type:e.link_type,confidence:e.confidence,source:e.source,evidence:Dc(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function Jr(e){return{id:e.id,source_session_id:e.source_session_id,target_session_id:e.target_session_id,link_type:e.link_type,confidence:e.confidence,evidence:Dc(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function kE(e){if(!Number.isFinite(e)||e<0||e>1)throw new Error("confidence must be a number in [0, 1]")}function $c(e){if(!bE.has(e))throw new Error(`invalid link_type: ${e}`)}function Pc(e){if(!wE.has(e))throw new Error(`invalid inferred_by: ${e}`)}function CE(e,t){if(!e||!t)throw new Error("source_session_id and target_session_id are required");if(e===t)throw new Error("a session cannot link to itself")}function LE(e={}){let t=_(),s=[],n=[];e.sourceSessionId&&(s.push("source_session_id = ?"),n.push(e.sourceSessionId)),e.targetSessionId&&(s.push("target_session_id = ?"),n.push(e.targetSessionId)),e.linkType&&($c(e.linkType),s.push("link_type = ?"),n.push(e.linkType)),e.approvedOnly&&s.push("approved = 1");let r=s.length?`WHERE ${s.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_links ${r}
775
+ `);return{id:i.id,project:i.project,git_branch:i.git_branch,alias:i.alias,first_user_message:i.first_user_message,message_sample:l,current_tags:ll(i.id)}})}var ul=C(()=>{"use strict";R();fo()});import{z as Le}from"zod";function ml(e){let t=e.minTags??2,n=e.maxTags??4,s=e.untaggedOnly??!e.sessionId,r=["Auto-tag my Claude Recall sessions using the MCP tools available to you.","","1. Call `list_tags` first to see which tags I already use (prefer those for consistency over inventing new ones).","2. Call `list_sessions_to_tag` with these filters:"],o=[];return e.sessionId?(o.push("limit: 1"),r.push(` ${o.join(", ")}`),r.push(` Then match the session id ${e.sessionId} from the returned list.`)):(s&&o.push("untaggedOnly: true"),e.project&&o.push(`project: "${e.project}"`),e.collectionId&&o.push(`collectionId: "${e.collectionId}"`),o.push(`limit: ${e.limit??100}`),r.push(` ${o.join(", ")}`)),r.push(""),r.push(`3. For each session returned, look at the alias, first user message, git branch, and sampled messages. Pick ${t}-${n} concise, lowercase, hyphen-separated tags describing:`),r.push(" - domain/subsystem (auth, db, frontend, billing, etc.)"),r.push(" - kind of work (bugfix, feature, refactor, research)"),r.push(" - prominent tools or libraries if relevant"),r.push(""),r.push("4. Call `apply_tags` once per session to write the tags."),r.push(""),r.push("Work through EVERY session returned \u2014 do not stop partway. When done, reply with a short summary of how many sessions were tagged. Do not ask clarifying questions; just do the work."),r.join(`
776
+ `)}var AI,LI,NI,OI,pl=C(()=>{"use strict";AI={project:Le.string().optional().describe("Exact project name match (optional)."),collectionId:Le.string().optional().describe("Restrict to sessions in this collection (optional)."),sessionId:Le.string().optional().describe("Full session UUID to tag just one session (optional)."),untaggedOnly:Le.boolean().optional().describe("Skip sessions that already have any tag (default: true)."),limit:Le.number().int().min(1).max(500).optional().describe("Max sessions to process (default: 100)."),minTags:Le.number().int().min(1).max(10).optional().describe("Minimum tags per session (default: 2)."),maxTags:Le.number().int().min(1).max(10).optional().describe("Maximum tags per session (default: 4).")};LI={sessionId:Le.string().describe("Session UUID (or 8+-char prefix) to summarize."),mode:Le.enum(["brief","detailed"]).optional().describe("brief = 3-5 bullets; detailed = paragraph + bullets. Default: brief.")},NI={sessionId:Le.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")},OI={sessionId:Le.string().describe("Session UUID (or 8+-char prefix) to find similar sessions to."),limit:Le.number().int().min(1).max(20).optional().describe("How many similar sessions to surface (default: 5).")}});function gl(e,t){let n=tb.get(e);if(!(!n||n.size===0))for(let s of n)try{s(t)}catch{}}var tb,fl=C(()=>{"use strict";tb=new Map});import{existsSync as nb,statSync as sb}from"node:fs";import{delimiter as rb,join as ob}from"node:path";function _l(e){if(e.includes("/")||e.includes("\\")||e.includes(".."))return null;let t=(process.env.PATH??"").split(rb).filter(Boolean),n=process.platform==="win32"?[e,`${e}.exe`,`${e}.cmd`,`${e}.bat`]:[e];for(let s of t)for(let r of n){let o=ob(s,r);try{if(nb(o)&&sb(o).isFile())return o}catch{}}return null}function hl(e){if(process.platform!=="win32"||!e)return!1;let t=e.toLowerCase();return t.endsWith(".cmd")||t.endsWith(".bat")}var El=C(()=>{"use strict"});var cs={};Z(cs,{_resetClaudePathCacheForTests:()=>lb,buildScanPrompt:()=>Sl,isClaudeCliAvailable:()=>ie,runClaudeCliScan:()=>gb,spawnClaudePrompt:()=>At});import{spawn as ib}from"node:child_process";function bl(){if(Wt!==void 0&&fn!==void 0)return{path:Wt,available:fn};let e=_l("claude");return Wt=e??"claude",fn=e!==null,{path:Wt,available:fn}}function cb(){return bl().path}function ie(){return bl().available}function lb(){Wt=void 0,fn=void 0}function Sl(e){return ml({project:e.project,collectionId:e.collectionId,sessionId:e.sessionIds&&e.sessionIds.length===1?e.sessionIds[0]:void 0,untaggedOnly:e.untaggedOnly,limit:e.limit})}function db(e,t){let n=t.get(e);return n||e.slice(0,8)}function ub(e){try{return dl(e).map(n=>({id:n.id,label:n.alias&&n.alias.trim().length>0?n.alias:n.first_user_message&&n.first_user_message.trim().length>0?n.first_user_message.slice(0,60):n.id.slice(0,8)}))}catch{return[]}}function mb(e){let{scanId:t,total:n,labelTable:s}=e,r=new Set;return o=>{let i=o.trim();if(!i.startsWith("{"))return;let a;try{a=JSON.parse(i)}catch{return}if(!a||typeof a!="object")return;let d=a;if(!(d.type!=="assistant"||!d.message?.content))for(let l of d.message.content){if(l?.type!=="tool_use"||l.name!=="mcp__recall__apply_tags")continue;let u=l.input,m=typeof u?.sessionId=="string"?u.sessionId:null;!m||r.has(m)||(r.add(m),gl(t,{type:"progress",current:r.size,total:n,sessionId:m,sessionLabel:db(m,s)}))}}}function pb(e){let t="";return n=>{t+=n.toString("utf8");let s=t.indexOf(`
777
+ `);for(;s!==-1;){let r=t.slice(0,s);t=t.slice(s+1),r.length>0&&e(r),s=t.indexOf(`
778
+ `)}}}async function gb(e,t={},n){let s=!!t.scanId,r=s?ub(e):[],o=new Map(r.map(d=>[d.id,d.label])),i=r.length,a;return s&&t.scanId&&(a=mb({scanId:t.scanId,total:i,labelTable:o})),wl({prompt:Sl(e),allowedTools:ab.split(","),opts:t,onProgress:n,onStdoutLine:a,outputFormat:s?"stream-json":"json"})}async function At(e,t,n={},s){return wl({prompt:e,allowedTools:t,opts:n,onProgress:s,outputFormat:"json"})}function wl(e){let{prompt:t,allowedTools:n,opts:s,onProgress:r,onStdoutLine:o,outputFormat:i}=e,a=["-p",t,"--output-format",i,"--allowedTools",n.join(","),"--permission-mode","bypassPermissions","--no-session-persistence"];return i==="stream-json"&&a.push("--verbose"),s.model&&a.push("--model",s.model),new Promise(d=>{let l=cb(),u=ib(l,a,{stdio:["ignore","pipe","pipe"],shell:hl(l)||process.platform==="win32"&&Wt==="claude"}),m=[],p=[],g=o?pb(o):void 0;u.stdout.on("data",h=>{m.push(h),g&&g(h)}),u.stderr.on("data",h=>{if(p.push(h),r){let _=h.toString("utf8").trim();_&&r(_)}});let f=setTimeout(()=>{u.kill("SIGKILL")},1800*1e3);u.on("close",h=>{clearTimeout(f),d({success:h===0,stdout:Buffer.concat(m).toString("utf8"),stderr:Buffer.concat(p).toString("utf8"),exitCode:h})}),u.on("error",h=>{clearTimeout(f),d({success:!1,stdout:"",stderr:String(h),exitCode:null})})})}var ab,Wt,fn,Ye=C(()=>{"use strict";ul();pl();fl();El();ab=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});var xl={};Z(xl,{downloadModel:()=>bo,getModelDir:()=>ms,isModelInstalled:()=>rt,uninstallModel:()=>So});import{existsSync as Eo,mkdirSync as Rl,rmSync as ho,createWriteStream as Ib,statSync as Mb}from"node:fs";import{join as us}from"node:path";import{createHash as Db}from"node:crypto";import{readFile as $b}from"node:fs/promises";function ms(){return us(x,"models","BAAI","bge-base-en-v1.5")}function rt(){let e=ms();return kl.every(t=>Eo(us(e,t.path)))}async function bo(e){let t=ms();Rl(t,{recursive:!0}),Rl(us(t,"onnx"),{recursive:!0});for(let n of kl){let s=us(t,n.path),r=Pb+n.path,o=0;Eo(s)&&(o=Mb(s).size);let i={};o>0&&(i.Range=`bytes=${o}-`);let a=await fetch(r,{headers:i});if(!a.ok&&a.status!==206)throw new Error(`Failed to download ${n.path}: HTTP ${a.status}`);let d=a.headers.get("content-length"),l=d?o+Number(d):0,u=a.body;if(!u)throw new Error(`No response body for ${n.path}`);let m=Ib(s,{flags:o>0?"a":"w"}),p=u.getReader(),g=o;for(;;){let{done:_,value:b}=await p.read();if(_)break;m.write(Buffer.from(b)),g+=b.byteLength,e?.(n.path,g,l)}if(m.end(),await new Promise((_,b)=>{m.on("finish",_),m.on("error",b)}),n.sha256==="TODO_PLACEHOLDER")throw ho(s),new Error(`Refusing to install: SHA-256 not pinned for ${n.path}. Update model-download.ts.`);let f=await $b(s);if(Db("sha256").update(f).digest("hex")!==n.sha256)throw ho(s),new Error(`SHA-256 mismatch for ${n.path}`)}}function So(){let e=ms();Eo(e)&&ho(e,{recursive:!0,force:!0})}var Pb,kl,_n=C(()=>{"use strict";D();Pb="https://huggingface.co/BAAI/bge-base-en-v1.5/resolve/main/",kl=[{path:"config.json",sha256:"bc00af31a4a31b74040d73370aa83b62da34c90b75eb77bfa7db039d90abd591"},{path:"tokenizer.json",sha256:"d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"},{path:"tokenizer_config.json",sha256:"9261e7d79b44c8195c1cada2b453e55b00aeb81e907a6664974b4d7776172ab3"},{path:"onnx/model.onnx",sha256:"9bc579acdba21c253c62a9bf866891355a63ffa3442b52c8a37d75b2ccb91848"}]});function Fb(e){return["Semantic search is unavailable on this platform.","",`Reason: ${e.detail}`,"",`Platform: ${process.platform}/${process.arch}, Node ${process.version}`,"","Claude Recall supports macOS (arm64/x64), Linux (x64/arm64), and Windows (x64).","Core CLI features (search, list, context, daemon) work everywhere.","Only `recall semantic *` requires the on-device embedder.","","See: https://clauderecall.com/docs (Supported platforms) - or file an issue at","https://gitlab.com/clauderecallhq/clauderecallhq/-/issues with the platform line above."].join(`
779
+ `)}var ps,fe,Xt=C(()=>{"use strict";ps="BAAI/bge-base-en-v1.5",fe=class extends Error{kind;path;underlying;cause;constructor(t){super(Fb(t)),this.name="EmbedderUnavailableError",this.kind=t.kind,this.path=t.path,this.underlying=t.underlying,this.cause=t}}});var _s={};Z(_s,{embed:()=>Jb,embedQuery:()=>zb,getEmbedderStatus:()=>Gb,loadEmbedder:()=>Xb,unloadEmbedder:()=>Yb});import{Worker as jb}from"node:worker_threads";import{join as Ub}from"node:path";import{existsSync as Bb}from"node:fs";function Hb(){return Ub(te(),"dist","daemon","embedder-worker.js")}function Cl(e){for(let t of hn.values())t.reject(e);hn.clear()}function Wb(){if(ot)return ot;let e=Hb();if(!Bb(e))throw new fe({kind:"bundle-missing",detail:"embedder-worker bundle not found - run `npm run build:cli` to emit it",path:e});let t=new jb(e);return t.on("message",n=>{let s=hn.get(n.id);s&&(hn.delete(n.id),s.resolve(n))}),t.on("error",n=>{console.error("[embedder-worker] thread error:",n);let s=n instanceof Error?n:new Error(String(n));Cl(s),ot=null,qe=!1}),t.on("exit",n=>{n!==0&&(console.error(`[embedder-worker] exited with code ${n}`),Cl(new Error(`embedder worker exited with code ${n}`))),ot=null,qe=!1}),ot=t,t}function gs(e){return new Promise((t,n)=>{let s;try{s=Wb()}catch(r){n(r instanceof Error?r:new Error(String(r)));return}hn.set(e.id,{resolve:t,reject:n}),s.postMessage(e)})}function fs(){return wo=wo+1>>>0,String(wo)}function To(e){if(!e.ok)throw e.unavailable?new fe({kind:"platform-unsupported",detail:"embedder worker reports the runtime is unavailable on this platform",underlying:new Error(e.error)}):new Error(e.error);return e}async function Xb(){if(!(qe&&ot))try{To(await gs({id:fs(),type:"load"})),qe=!0}catch(e){throw qe=!1,e}}function Gb(){return{loaded:qe,modelId:ps,dim:768}}async function Jb(e){if(!qe)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return To(await gs({id:fs(),type:"embed",texts:e})).embeddings.map(n=>new Float32Array(n))}async function zb(e){if(!qe)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=To(await gs({id:fs(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function Yb(){if(!ot){qe=!1;return}try{await gs({id:fs(),type:"unload"})}catch{}qe=!1;let e=ot;ot=null;try{await e.terminate()}catch{}}var ot,hn,wo,qe,Al=C(()=>{"use strict";Ge();Xt();ot=null,hn=new Map,wo=0,qe=!1});var xo={};Z(xo,{LLAMACPP_MODEL_ID:()=>Nl,MODEL_ID:()=>ps,embed:()=>nS,embedQuery:()=>sS,getEmbedderStatus:()=>tS,loadEmbedder:()=>eS,unloadEmbedder:()=>rS});import{Worker as qb}from"node:worker_threads";import{join as Vb}from"node:path";import{existsSync as Kb}from"node:fs";function Qb(){return Vb(te(),"dist","daemon","embedder-worker-llamacpp.js")}function Ll(e){for(let t of En.values())t.reject(e);En.clear()}function Zb(){if(it)return it;let e=Qb();if(!Kb(e))throw new fe({kind:"bundle-missing",detail:"embedder-worker-llamacpp bundle not found - run `npm run build:cli` to emit it",path:e});let t=new qb(e);return t.on("message",n=>{let s=En.get(n.id);s&&(En.delete(n.id),s.resolve(n))}),t.on("error",n=>{console.error("[embedder-worker-llamacpp] thread error:",n);let s=n instanceof Error?n:new Error(String(n));Ll(s),it=null,Ve=!1}),t.on("exit",n=>{n!==0&&(console.error(`[embedder-worker-llamacpp] exited with code ${n}`),Ll(new Error(`embedder worker exited with code ${n}`))),it=null,Ve=!1}),it=t,t}function hs(e){return new Promise((t,n)=>{let s;try{s=Zb()}catch(r){n(r instanceof Error?r:new Error(String(r)));return}En.set(e.id,{resolve:t,reject:n}),s.postMessage(e)})}function Es(){return Ro=Ro+1>>>0,String(Ro)}function ko(e){if(!e.ok)throw e.unavailable?new fe({kind:"platform-unsupported",detail:"embedder worker reports the llama.cpp runtime is unavailable on this platform",underlying:new Error(e.error)}):new Error(e.error);return e}async function eS(){if(!(Ve&&it))try{ko(await hs({id:Es(),type:"load"})),Ve=!0}catch(e){throw Ve=!1,e}}function tS(){return{loaded:Ve,modelId:Nl,dim:768}}async function nS(e){if(!Ve)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return ko(await hs({id:Es(),type:"embed",texts:e})).embeddings.map(n=>new Float32Array(n))}async function sS(e){if(!Ve)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=ko(await hs({id:Es(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function rS(){if(!it){Ve=!1;return}try{await hs({id:Es(),type:"unload"})}catch{}Ve=!1;let e=it;it=null;try{await e.terminate()}catch{}}var Nl,it,En,Ro,Ve,Ol=C(()=>{"use strict";Ge();Xt();Xt();Nl="BAAI/bge-base-en-v1.5-gguf-q8_0";it=null,En=new Map,Ro=0,Ve=!1});var bs={};Z(bs,{EmbedderUnavailableError:()=>fe,embed:()=>Ne,embedQuery:()=>iS,getEmbedderStatus:()=>Q,loadEmbedder:()=>Ke,unloadEmbedder:()=>aS});function oS(){let e=(process.env.RECALL_EMBEDDER_BACKEND??"").trim().toLowerCase();return e==="llama"||e==="llamacpp"?xo:(e===""||e==="onnx"||process.stderr.write(`[embedder] RECALL_EMBEDDER_BACKEND="${e}" is not recognized; falling back to onnx. Valid values: onnx, llama.
780
+ `),_s)}var bn,Ke,Q,Ne,iS,aS,Pe=C(()=>{"use strict";Al();Ol();Xt();bn=oS(),Ke=bn.loadEmbedder,Q=bn.getEmbedderStatus,Ne=bn.embed,iS=bn.embedQuery,aS=bn.unloadEmbedder});function vl(e){return e.replace(/```json[\s\S]*?```/g,"[tool-call]").replace(/\{[\s\S]{200,}?\}/g,"[json-object]")}function cS(e){let t=[],o=0;for(;o<e.length;){let i=[],a=0;for(;i.length<5&&o<e.length;){let l=e[o],u=vl(l.content_text??"");if(a+u.length>2e3&&i.length>=3)break;i.push(l),a+=u.length,o++}if(i.length===0)break;let d=i.map(l=>{let u=l.role??"system",m=vl(l.content_text??"");return`[${u}] ${m}`}).join(`
781
+
782
+ `);t.push({messageUuids:i.map(l=>l.uuid),text:d}),o<e.length&&i.length>=3&&(o=Math.max(o-1,o-1))}return t}function Co(e){let n=E().prepare("SELECT uuid, role, content_text FROM messages WHERE session_id = ? AND is_sidechain = 0 AND content_text IS NOT NULL ORDER BY rowid").all(e);return cS(n)}var Ao=C(()=>{"use strict";R()});function Lo(e){let t=E();t.transaction(()=>{t.prepare("DELETE FROM vec_chunks WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(e),t.prepare("SELECT name FROM sqlite_master WHERE name='vec_chunks_v1_backup'").get()&&t.prepare("DELETE FROM vec_chunks_v1_backup WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(e),t.prepare("DELETE FROM chunk_meta WHERE session_id = ?").run(e)})()}var No=C(()=>{"use strict";R()});function Il(){try{return!!E().prepare("SELECT 1 AS held FROM migration_state WHERE status IN ('in_progress', 'paused') LIMIT 1").get()}catch(e){if((e instanceof Error?e.message:String(e)).includes("no such table: migration_state"))return!1;throw e}}function Oo(){return E().prepare("UPDATE chunk_queue SET action = 'embed' WHERE action = 'pending_post_migration'").run().changes}function mS(){return E().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}function Ml(){return{running:lS,queueDepth:mS(),lastProcessedAt:dS,blacklistedCount:uS.size,pausedForMigration:Il()}}var lS,dS,uS,Ss=C(()=>{"use strict";R();Pe();Ao();No();lS=!1,dS=null,uS=new Set});var Dl={};Z(Dl,{ensureLlamaCppInstalled:()=>yS});import{spawn as pS}from"node:child_process";import{existsSync as gS}from"node:fs";import{dirname as fS,resolve as _S}from"node:path";import{fileURLToPath as hS}from"node:url";function bS(){let e=fS(hS(import.meta.url));return _S(e,"..","..")}async function SS(){try{return await import("node-llama-cpp"),!0}catch{return!1}}function wS(e){return new Promise(t=>{let n=["install","--no-save","--no-audit","--no-fund","--prefix",e,`node-llama-cpp@${ES}`],s=pS("npm",n,{stdio:["ignore","inherit","inherit"],cwd:e,shell:process.platform==="win32"});s.on("error",r=>{t({ok:!1,error:`npm spawn failed: ${r instanceof Error?r.message:String(r)}`})}),s.on("exit",r=>{t(r===0?{ok:!0,action:"installed"}:{ok:!1,error:`npm install exited with code ${r}`})})})}async function yS(){if(await SS())return{ok:!0,action:"already-installed"};let e=bS();return gS(e)?(console.log(`Installing node-llama-cpp into ${e} ...`),await wS(e)):{ok:!1,error:`package root not found at ${e}`}}var ES,$l=C(()=>{"use strict";ES="3.18.1"});var Rs={};Z(Rs,{BGE_GGUF_Q8_EXPECTED_BYTES:()=>Ul,BGE_GGUF_Q8_SHA256:()=>Gt,BGE_GGUF_Q8_URL:()=>jl,downloadGgufModel:()=>vS,getGgufModelDir:()=>Sn,getGgufModelPath:()=>Ts,isGgufModelInstalled:()=>OS,uninstallGgufModel:()=>IS,verifyGgufModelHash:()=>Wl});import{createHash as Pl}from"node:crypto";import{createWriteStream as TS,existsSync as ys,mkdirSync as RS,readFileSync as kS,rmSync as ws,statSync as Fl,writeFileSync as xS}from"node:fs";import{createReadStream as CS}from"node:fs";import{join as vo}from"node:path";function Sn(){return vo(x,"models","gguf")}function Ts(){return vo(Sn(),AS)}function Bl(){return vo(Sn(),LS)}function NS(){try{let e=kS(Bl(),"utf8"),t=JSON.parse(e);return typeof t.size=="number"&&typeof t.mtimeMs=="number"&&typeof t.sha256=="string"?t:null}catch{return null}}function Hl(e){try{xS(Bl(),JSON.stringify(e)+`
783
+ `,"utf8")}catch(t){console.warn("[recall] gguf verify-cache write failed (will rehash on next start):",t instanceof Error?t.message:String(t))}}function OS(){return ys(Ts())}async function Wl(){let e=Ts();if(!ys(e))return{ok:!1,reason:"model file missing"};let t=Fl(e),n=NS();if(n!==null&&n.size===t.size&&n.mtimeMs===t.mtimeMs&&n.sha256===Gt)return{ok:!0};let s=Pl("sha256"),r=CS(e);await new Promise((i,a)=>{r.on("data",d=>s.update(d)),r.on("end",()=>i()),r.on("error",d=>a(d))});let o=s.digest("hex");return o!==Gt?(console.error(`[recall] GGUF integrity check failed at ${e}: computed ${o}, expected ${Gt}`),{ok:!1,reason:"model integrity check failed"}):(Hl({size:t.size,mtimeMs:t.mtimeMs,sha256:o}),{ok:!0})}async function vS(e){let t=Sn();RS(t,{recursive:!0});let n=Ts();if(ys(n)){if((await Wl()).ok)return;ws(n)}let s=await fetch(jl);if(!s.ok)throw new Error(`Failed to download GGUF model: HTTP ${s.status} ${s.statusText}`);let r=s.body;if(!r)throw new Error("Failed to download GGUF model: no response body");let o=s.headers.get("content-length"),i=o?Number(o):Ul,a=Pl("sha256"),d=TS(n,{flags:"w"}),l=r.getReader(),u=0,m=!1;try{for(;;){let{done:f,value:h}=await l.read();if(f)break;let _=Buffer.from(h);a.update(_),d.write(_)||await new Promise(b=>d.once("drain",b)),u+=_.byteLength,e?.(u,i)}d.end(),await new Promise((f,h)=>{d.on("finish",()=>f()),d.on("error",_=>h(_))})}catch(f){throw m=!0,f}finally{if(m){try{d.destroy()}catch{}try{ws(n,{force:!0})}catch{}}}let p=a.digest("hex");if(p!==Gt)throw ws(n,{force:!0}),console.error(`[recall] GGUF integrity check failed during download: computed ${p}, expected ${Gt}`),new fe({kind:"platform-unsupported",detail:"GGUF model integrity check failed during download"});let g=Fl(n);Hl({size:g.size,mtimeMs:g.mtimeMs,sha256:p})}function IS(){let e=Sn();ys(e)&&ws(e,{recursive:!0,force:!0})}var Gt,jl,Ul,AS,LS,ks=C(()=>{"use strict";D();Xt();Gt="ad1afe72cd6654a558667a3db10878b049a75bfd72912e1dabb91310d671173c",jl="https://huggingface.co/CompendiumLabs/bge-base-en-v1.5-gguf/resolve/main/bge-base-en-v1.5-q8_0.gguf",Ul=118438752,AS="bge-base-en-v1.5-q8_0.gguf",LS=".gguf-verify-cache.json"});var Vl={};Z(Vl,{applyIntelMacOnnxFallback:()=>Do,detectRunningClaudeOnWindows:()=>ql,ensureTransformersInstalled:()=>XS});import{execFileSync as Cs,spawn as MS}from"node:child_process";import{existsSync as Io,mkdtempSync as DS,mkdirSync as $S,readdirSync as Yl,rmSync as Xl}from"node:fs";import{createRequire as PS}from"node:module";import{tmpdir as FS}from"node:os";import{dirname as Mo,join as As,resolve as jS}from"node:path";import{fileURLToPath as US}from"node:url";function Jl(e){let t=As(e,"bin");if(!Io(t))return!1;try{for(let n of Yl(t)){if(!n.startsWith("napi-"))continue;let s=As(t,n,"darwin","x64","onnxruntime_binding.node");if(Io(s))return!0}}catch{return!1}return!1}function zl(){let e=Mo(US(import.meta.url));return jS(e,"..","..")}async function HS(){try{return await import("@huggingface/transformers"),!0}catch{return!1}}function WS(e){return new Promise(t=>{let n=["install","--no-save","--no-audit","--no-fund","--prefix",e,`@huggingface/transformers@${BS}`],s=MS("npm",n,{stdio:["ignore","inherit","inherit"],cwd:e,shell:process.platform==="win32"});s.on("error",r=>{t({ok:!1,error:`npm spawn failed: ${r instanceof Error?r.message:String(r)}`})}),s.on("exit",r=>{t(r===0?{ok:!0,action:"installed"}:{ok:!1,error:`npm install exited with code ${r}`})})})}function ql(){if(process.platform!=="win32")return!1;try{return Cs("tasklist",["/FI","IMAGENAME eq claude.exe"],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).toLowerCase().includes("claude.exe")}catch{return!1}}async function Do(e){if(process.platform!=="darwin"||process.arch!=="x64")return;let t;try{let s=Gl.resolve("@huggingface/transformers/package.json",{paths:[e]}),r=Gl.resolve("onnxruntime-node/package.json",{paths:[Mo(s)]});t=Mo(r)}catch{return}if(Jl(t))return;console.log(`[recall] Intel macOS detected: onnxruntime-node at ${t} is missing the darwin/x64 binding. Applying ${xs} fallback (npm overrides did not propagate through the scoped-package hoisting).`);let n=DS(As(FS(),"recall-ort-pin-"));try{Cs("npm",["pack","--pack-destination",n,`onnxruntime-node@${xs}`],{cwd:n,stdio:["ignore","inherit","inherit"]});let s=Yl(n).find(r=>r.startsWith("onnxruntime-node-")&&r.endsWith(".tgz"));if(!s){console.warn("[recall] npm pack did not produce a tarball; Intel-Mac fallback skipped. Tier 2 may be unavailable.");return}if(Jl(t)){console.log("[recall] onnxruntime-node@"+xs+" already in place (concurrent install raced us). Nothing to do.");return}Xl(t,{recursive:!0}),$S(t,{recursive:!0}),Cs("tar",["-xzf",As(n,s),"-C",t,"--strip-components=1"],{stdio:["ignore","inherit","inherit"]});try{Cs("node",["./script/install"],{cwd:t,stdio:["ignore","inherit","inherit"]})}catch{console.warn("[recall] onnxruntime-node postinstall returned non-zero; binding files are still in place. Continuing.")}console.log(`[recall] onnxruntime-node@${xs} fallback applied at ${t}.`)}catch(s){console.error("[recall] Intel macOS onnxruntime-node fallback failed:",s instanceof Error?s.message:String(s)),console.error("[recall] Tier 2 (vector search) will be unavailable on this machine. Core CLI features remain functional.")}finally{try{Xl(n,{recursive:!0,force:!0})}catch{}}}async function XS(){if(await HS())return await Do(zl()),{ok:!0,action:"already-installed"};if(ql())return{ok:!1,error:"Close all Claude Code windows before running `recall semantic install` on Windows. claude.exe holds exclusive locks on shared dependency files (Windows kernel default), which causes the underlying npm install to fail with EBUSY. After closing Claude Code, rerun `recall semantic install` to complete the install."};let e=zl();if(!Io(e))return{ok:!1,error:`package root not found at ${e}`};console.log(`Installing @huggingface/transformers into ${e} ...`);let t=await WS(e);return t.ok&&await Do(e),t}var Gl,BS,xs,Kl=C(()=>{"use strict";Gl=PS(import.meta.url),BS="^4.2.0",xs="1.23.2"});var vs={};Z(vs,{CURRENT_MIGRATION_SCHEMA_VERSION:()=>Ns,UnsupportedMigrationSchemaError:()=>Ls,completeMigration:()=>Po,detectStaleLock:()=>Uo,failMigration:()=>Fo,getActiveMigration:()=>wn,pauseMigration:()=>GS,releaseStaleLock:()=>Bo,rollbackMigration:()=>jo,rowToState:()=>Jt,startMigration:()=>Os,updateCursor:()=>$o});function Jt(e){for(let t of["id","schema_version","status","started_at","model_id_old","model_id_new"])if(!(t in e))throw new Error(`migration_state row missing required column '${t}' (got: ${Object.keys(e).join(", ")})`);if(e.schema_version!==Ns)throw new Ls(typeof e.schema_version=="number"?e.schema_version:-1);return{id:e.id,schemaVersion:e.schema_version,status:e.status,startedAt:e.started_at,completedAt:e.completed_at??null,lockTakenAt:e.lock_taken_at??null,lockTakenByPid:e.lock_taken_by_pid??null,cursorSessionId:e.cursor_session_id??null,cursorChunkId:e.cursor_chunk_id??null,modelIdOld:e.model_id_old,modelIdNew:e.model_id_new}}function wn(){let t=E().prepare("SELECT * FROM migration_state WHERE status IN ('in_progress', 'paused') ORDER BY id DESC LIMIT 1").get();return t?Jt(t):null}function Os(e){let t=E(),n=wn();if(n)throw new Error(`migration already in progress (id=${n.id})`);let s=new Date().toISOString(),r;try{r=t.prepare("INSERT INTO migration_state(schema_version, status, started_at, lock_taken_at, lock_taken_by_pid, model_id_old, model_id_new) VALUES (?, 'in_progress', ?, ?, ?, ?, ?)").run(Ns,s,s,process.pid,e.modelIdOld,e.modelIdNew)}catch(i){throw i instanceof Error&&i.message.includes("UNIQUE constraint failed")?new Error("migration already in progress (started by a parallel process)"):i}let o=t.prepare("SELECT * FROM migration_state WHERE id = ?").get(Number(r.lastInsertRowid));return Jt(o)}function yn(e,t,n){if(e.changes!==1)throw new Error(`${t}: expected exactly 1 row to update, got ${e.changes}. migrationId=${n} likely refers to a row that does not exist. This typically means the caller is holding a stale id from a previous daemon process.`)}function $o(e,t){let s=E().prepare("UPDATE migration_state SET cursor_session_id = ?, cursor_chunk_id = ? WHERE id = ?").run(t.sessionId,t.chunkId,e);yn(s,"updateCursor",e)}function GS(e){let n=E().prepare("UPDATE migration_state SET status = 'paused' WHERE id = ? AND status = 'in_progress'").run(e);yn(n,"pauseMigration",e)}function Po(e){let n=E().prepare("UPDATE migration_state SET status = 'completed', completed_at = datetime('now'), lock_taken_at = NULL, lock_taken_by_pid = NULL WHERE id = ?").run(e);yn(n,"completeMigration",e)}function Fo(e,t){let n=E();console.error(`[migration] failed: ${t}`);let s=n.prepare("UPDATE migration_state SET status = 'failed', completed_at = datetime('now'), lock_taken_at = NULL, lock_taken_by_pid = NULL WHERE id = ?").run(e);yn(s,"failMigration",e)}function jo(e){let n=E().prepare("UPDATE migration_state SET status = 'rolled_back', completed_at = datetime('now') WHERE id = ?").run(e);yn(n,"rollbackMigration",e)}function JS(e){try{return process.kill(e,0),!0}catch(t){return t.code==="EPERM"}}function Uo(){let t=E().prepare("SELECT * FROM migration_state WHERE status = 'in_progress' AND lock_taken_by_pid IS NOT NULL LIMIT 1").get();if(!t)return null;let n=Jt(t);return n.lockTakenByPid===null||n.lockTakenByPid===process.pid||JS(n.lockTakenByPid)?null:n}function Bo(e,t={preserveCursor:!0}){let n=E(),s=n.prepare("SELECT id, cursor_chunk_id FROM migration_state WHERE status = 'in_progress' AND lock_taken_by_pid = ? LIMIT 1").get(e);if(!s)return console.warn(`[migration] releaseStaleLock: no row matched pid=${e} (already released or never existed)`),{released:!1,status:"no-op"};let r=t.preserveCursor&&s.cursor_chunk_id!==null,o=r?"paused":"failed",i=n.prepare(o==="paused"?"UPDATE migration_state SET status = 'paused', lock_taken_at = NULL, lock_taken_by_pid = NULL WHERE id = ? AND status = 'in_progress'":"UPDATE migration_state SET status = 'failed', completed_at = datetime('now'), lock_taken_at = NULL, lock_taken_by_pid = NULL WHERE id = ? AND status = 'in_progress'").run(s.id);return console.warn(`[migration] released stale lock pid=${e}, transitioned to '${o}' (${i.changes} row(s) updated; cursor preserved=${r})`),{released:i.changes===1,status:o}}var Ns,Ls,zt=C(()=>{"use strict";R();Ns=1,Ls=class extends Error{constructor(t){super(`unsupported migration schema_version: found ${t}, this CLI understands ${Ns}. Upgrade or run 'recall semantic migrate --reset' to discard the cursor and start fresh.`),this.name="UnsupportedMigrationSchemaError"}}});async function Wo(e){let t=E(),n=t.pragma("busy_timeout",{simple:!0})??5e3;t.pragma("busy_timeout = 60000");try{if(t.prepare("SELECT name FROM sqlite_master WHERE name = 'vec_chunks_v1_backup'").get()){let i=t.prepare("SELECT COUNT(*) AS n FROM vec_chunks_v1_backup").get();console.warn(`[migration-swap] Existing rollback backup from prior migration replaced (vec_chunks_v1_backup had ${i.n} rows). The prior migration's 30-day rollback window is implicitly superseded by this new migration.`)}t.transaction(()=>{t.exec("DROP TABLE IF EXISTS vec_chunks_v1_backup;"),t.exec(`
784
+ CREATE TABLE IF NOT EXISTS vec_chunks_v1_backup (
785
+ rowid INTEGER PRIMARY KEY,
786
+ embedding BLOB NOT NULL
787
+ );
788
+ `),t.exec("INSERT INTO vec_chunks_v1_backup(rowid, embedding) SELECT rowid, embedding FROM vec_chunks;"),t.exec("DELETE FROM vec_chunks;"),t.exec("INSERT INTO vec_chunks(rowid, embedding) SELECT rowid, embedding FROM vec_chunks_v2;"),t.exec("DROP TABLE vec_chunks_v2;"),t.prepare("UPDATE chunk_meta SET embedding_model_id = (SELECT model_id_new FROM migration_state WHERE id = ?) WHERE rowid IN (SELECT rowid FROM vec_chunks)").run(e)})();let o;try{o=await zS()}catch(i){let{failMigration:a}=await Promise.resolve().then(()=>(zt(),vs)),d=i instanceof Error?i.message:String(i);throw a(e,d),i}if(!o.ok){let{failMigration:i}=await Promise.resolve().then(()=>(zt(),vs));throw i(e,o.message),new Error(`atomic swap verification failed: ${o.message}`)}try{Po(e)}catch(i){console.error(`[migration-swap] CRITICAL: swap + verify succeeded but completeMigration(${e}) threw: ${i instanceof Error?i.message:String(i)}. Live vec_chunks holds the new backend's vectors and vec_chunks_v1_backup retains the old set, but migration_state row is stuck in_progress. Recover via: sqlite3 ~/.recall/db.sqlite "UPDATE migration_state SET status='completed', completed_at=datetime('now'), lock_taken_at=NULL, lock_taken_by_pid=NULL WHERE id=${e};"`)}}finally{t.pragma(`busy_timeout = ${n}`)}}async function zS(){let e=E();try{let t=e.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get(),n=e.prepare("SELECT COUNT(*) AS n FROM chunk_meta").get();if(n.n!==t.n)return{ok:!1,message:`post-swap row count mismatch: vec_chunks=${t.n}, chunk_meta=${n.n}, delta=${n.n-t.n} (chunk_meta rows without matching vec_chunks vectors)`,rowCount:t.n};if(t.n===0)return{ok:!0,message:"vec_chunks empty post-swap (no data to migrate)",rowCount:0};let s=e.prepare("SELECT embedding FROM vec_chunks LIMIT 1").get();return e.prepare("SELECT rowid, distance FROM vec_chunks WHERE embedding MATCH ? ORDER BY distance LIMIT 1").all(s.embedding).length===0?{ok:!1,message:"post-swap kNN returned no rows",rowCount:t.n}:{ok:!0,message:`post-swap smoke kNN ok, ${t.n} rows (matches chunk_meta)`,rowCount:t.n}}catch(t){throw process.stderr.write(`[migration-swap] verifyPostSwap threw after swap commit: ${t instanceof Error?t.stack??t.message:String(t)}
789
+ `),new Ho(`post-swap verification failed: ${t instanceof Error?t.message:String(t)}`,t)}}var Ho,Ql=C(()=>{"use strict";R();zt();Ho=class extends Error{cause;constructor(t,n){super(t),this.name="MigrationVerificationError",this.cause=n}}});import{promises as YS}from"node:fs";import{dirname as Xo}from"node:path";function Zl(e){return e<=0?0:Math.ceil(e*(qS+VS)*KS)}async function ed(e){let t=e.dbPath??ee,n;try{n=await YS.statfs(Xo(t))}catch(i){return{ok:!1,message:`disk space check failed: could not read filesystem at ${Xo(t)}: ${i instanceof Error?i.message:String(i)}`,availableBytes:0,requiredBytes:e.requiredBytes}}let s=Number(n.bavail)*Number(n.bsize),r=s>=e.requiredBytes,o=r?`disk space check: required: ${e.requiredBytes} bytes, available: ${s} bytes`:`disk space check FAILED. required: ${e.requiredBytes} bytes, available: ${s} bytes. Free up space on ${Xo(t)} or run on a different filesystem.`;return{ok:r,message:o,availableBytes:s,requiredBytes:e.requiredBytes}}function td(e={}){let t=E(),n=Q().modelId;if(!n)throw new Error("countChunksToMigrate: embedder not loaded or modelId is empty");return e.scopeProjectId!==void 0?t.prepare(`SELECT COUNT(*) AS n FROM chunk_meta cm
790
+ JOIN sessions s ON s.id = cm.session_id
791
+ WHERE s.project_id = ?
792
+ AND cm.embedding_model_id != ?
793
+ AND NOT EXISTS (
794
+ SELECT 1 FROM vec_chunks_v2 v WHERE v.rowid = cm.rowid
795
+ )`).get(e.scopeProjectId,n).n:t.prepare(`SELECT COUNT(*) AS n FROM chunk_meta
796
+ WHERE embedding_model_id != ?
797
+ AND NOT EXISTS (
798
+ SELECT 1 FROM vec_chunks_v2 v WHERE v.rowid = chunk_meta.rowid
799
+ )`).get(n).n}async function nd(e){if(e==="onnx"){let r=rt();return{ok:r,message:r?"ONNX backend ready (bge-base-en-v1.5 model present)":"ONNX backend not installed. Run `recall semantic install` first."}}let{isGgufModelInstalled:t,verifyGgufModelHash:n}=await Promise.resolve().then(()=>(ks(),Rs));if(!t())return{ok:!1,message:"llama.cpp backend not installed. Set RECALL_EMBEDDER_BACKEND=llama and run `recall semantic install` first."};let s=await n();return{ok:s.ok,message:s.ok?"llama.cpp backend ready (GGUF model present + hash verified)":`llama.cpp GGUF model integrity check failed: ${s.reason}. Re-run \`recall semantic install\` to fetch a clean copy.`}}function sd(e,t){return e<=0?0:t<=0?null:Math.ceil(e/t/60)}async function rd(e={}){let t=e.sampleSize??Go,n=E(),s=Q().modelId;if(!s)throw new Error("sampleEmbedderThroughput: embedder not loaded or modelId is empty");let r=e.scopeProjectId!==void 0?n.prepare(`SELECT cm.text FROM chunk_meta cm
800
+ JOIN sessions s ON s.id = cm.session_id
801
+ WHERE s.project_id = ?
802
+ AND cm.embedding_model_id != ?
803
+ AND cm.text IS NOT NULL
804
+ AND length(cm.text) > 0
805
+ LIMIT ?`).all(e.scopeProjectId,s,t):n.prepare("SELECT text FROM chunk_meta WHERE embedding_model_id != ? AND text IS NOT NULL AND length(text) > 0 LIMIT ?").all(s,t);if(r.length===0)return{ok:!0,chunksPerSec:null,sampleSize:0,elapsedMs:0,message:"throughput probe skipped: no chunks to sample"};let o=r.map(u=>u.text),i=process.hrtime.bigint();await Ne(o);let a=Number(process.hrtime.bigint()-i)/1e6,d=a/1e3,l=d>0?r.length/d:null;return{ok:!0,chunksPerSec:l,sampleSize:r.length,elapsedMs:a,message:l===null?`throughput probe: ${r.length} chunks in <1ms (n/a)`:`throughput probe: ${r.length} chunks in ${a.toFixed(0)}ms \u2192 ${l.toFixed(1)} chunks/sec`}}var qS,VS,KS,Go,od=C(()=>{"use strict";R();Pe();_n();D();qS=768*4,VS=64,KS=1.2;Go=50});var Is={};Z(Is,{MigrationLoopError:()=>Tn,createParallelVectorTable:()=>ad,migrateChunkRange:()=>cd,migrateOneChunk:()=>rw,pruneRollbackBackup:()=>aw,rollbackMigration:()=>iw,runMigration:()=>ow});import{createInterface as QS}from"node:readline/promises";import{stdin as ZS,stdout as id,stderr as ew}from"node:process";async function tw(e,t){if(!id.isTTY)return ew.write(`Interactive confirmation requested but stdout is not a TTY. Re-run with --yes.
806
+ `),!1;let n=QS({input:ZS,output:id}),s=t===null?"":` (~${t} min)`,r=await n.question(`Migrate ${e} chunks${s}? Type YES to proceed: `);return n.close(),r.trim()==="YES"}function ad(){E().exec(sw)}async function rw(e){let t=E(),n=t.prepare("SELECT text FROM chunk_meta WHERE rowid = ?").get(e);if(!n)throw new Error(`chunk_meta row not found: rowid=${e}`);let[s]=await Ne([n.text]),r=Buffer.from(s.buffer,s.byteOffset,s.byteLength);t.prepare("DELETE FROM vec_chunks_v2 WHERE rowid = ?").run(BigInt(e)),t.prepare("INSERT INTO vec_chunks_v2(rowid, embedding) VALUES (?, ?)").run(BigInt(e),r)}async function cd(e){let t=E(),n=e.batchSize??50,s=wn();if(!s||s.id!==e.migrationId)throw new Error(`no active migration with id=${e.migrationId}`);let r=s.cursorChunkId??0,i=t.prepare(`SELECT COUNT(*) AS n FROM chunk_meta
807
+ WHERE rowid > ?
808
+ AND embedding_model_id != ?
809
+ AND NOT EXISTS (
810
+ SELECT 1 FROM vec_chunks_v2 v WHERE v.rowid = chunk_meta.rowid
811
+ )`).get(r,s.modelIdNew).n,a=0,d=Date.now(),l=0,u=t.prepare(`SELECT rowid, session_id, text FROM chunk_meta
812
+ WHERE rowid > ?
813
+ AND embedding_model_id != ?
814
+ AND NOT EXISTS (
815
+ SELECT 1 FROM vec_chunks_v2 v WHERE v.rowid = chunk_meta.rowid
816
+ )
817
+ ORDER BY rowid LIMIT ?`),m=r;for(;;){let p=u.all(m,s.modelIdNew,n);if(p.length===0)break;let g;try{g=await Ne(p.map(h=>h.text))}catch(h){throw new Tn(`embed() failed at cursor=${m}, migrated=${a}: ${h instanceof Error?h.message:String(h)}`,a,h)}if(t.transaction(()=>{let h=t.prepare("DELETE FROM vec_chunks_v2 WHERE rowid = ?"),_=t.prepare("INSERT INTO vec_chunks_v2(rowid, embedding) VALUES (?, ?)");for(let b=0;b<p.length;b++){let S=p[b],w=g[b],y=Buffer.from(w.buffer,w.byteOffset,w.byteLength);h.run(BigInt(S.rowid)),_.run(BigInt(S.rowid),y)}})(),m=p[p.length-1].rowid,a+=p.length,$o(s.id,{sessionId:p[p.length-1].session_id,chunkId:m}),e.signal?.aborted)return{migrated:a,total:i};if(a-l>=nw&&(t.exec("PRAGMA wal_checkpoint(PASSIVE);"),l=a),e.onProgress){let h=(Date.now()-d)/1e3;e.onProgress({migrated:a,total:i,chunksPerSec:h>0?a/h:0})}}return{migrated:a,total:i}}async function ow(e){let t=Uo();t&&t.lockTakenByPid!==null&&(console.warn(`[migrate] Stale lock detected from pid ${t.lockTakenByPid}. Releasing.`),Bo(t.lockTakenByPid,{preserveCursor:!0}));let n=wn(),r=Q().modelId.includes("gguf")?"llama":"onnx",o=await nd(r);if(!o.ok)return{ok:!1,message:o.message,migrated:0,backupRetained:!1};if(!Q().loaded)try{await Ke()}catch(g){if(g instanceof fe)return{ok:!1,message:g.message.split(`
818
+ `)[0],migrated:0,backupRetained:!1};throw g}ad();let i;if(e.projectName){let g=E().prepare("SELECT id FROM projects WHERE name = ?").get(e.projectName);if(!g)return{ok:!1,message:`project not found: ${e.projectName}`,migrated:0,backupRetained:!1};i=g.id}let a=td({scopeProjectId:i});if(a===0){if(i===void 0){let g=E().prepare("SELECT COUNT(*) AS n FROM vec_chunks_v2").get();if(g.n>0){if(!n){let f=Q().modelId,_=E().prepare("SELECT embedding_model_id FROM chunk_meta WHERE embedding_model_id != ? LIMIT 1").get(f)?.embedding_model_id??"unknown";n=Os({modelIdOld:_,modelIdNew:f})}try{await Wo(n.id)}catch(f){return{ok:!1,message:f instanceof Error?f.message:String(f),migrated:0,backupRetained:!1}}try{Oo()}catch(f){console.warn(`[migrate] post-swap promote failed (daemon will drain on next tick): ${f instanceof Error?f.message:String(f)}`)}return{ok:!0,message:`swap committed: ${g.n} staged chunks promoted to live vec_chunks`,migrated:0,backupRetained:e.keepBackup}}}return{ok:!0,message:i!==void 0?`nothing to migrate for project ${e.projectName} (all chunks already staged)`:"nothing to migrate (all chunks already on active backend)",migrated:0,backupRetained:!1}}let d=Zl(a),l=await ed({requiredBytes:d});if(!l.ok)return{ok:!1,message:l.message,migrated:0,backupRetained:!1};(e.interactive||e.dryRun)&&console.log(`Measuring embedder throughput (sampling up to ${Go} chunks)...`);let u=await rd({scopeProjectId:i}),m=u.chunksPerSec===null?null:sd(a,u.chunksPerSec);if(e.dryRun){let g=E().prepare("SELECT embedding_model_id, COUNT(*) AS n FROM chunk_meta WHERE embedding_model_id != ? GROUP BY embedding_model_id ORDER BY n DESC").all(Q().modelId),f=h=>(h/1024**3).toFixed(2);console.log(""),console.log("=== Dry run: migration plan ==="),console.log(`Target backend: ${Q().modelId}`),console.log(`Chunks to migrate: ${a.toLocaleString()}`),console.log(`Disk required: ${f(d)} GB (${f(l.availableBytes)} GB available)`),u.chunksPerSec!==null?(console.log(`Measured throughput: ${u.chunksPerSec.toFixed(1)} chunks/sec (live sample of ${u.sampleSize} in ${u.elapsedMs.toFixed(0)}ms)`),console.log(`Projected duration: ~${m} min`)):(console.log("Measured throughput: n/a (probe returned no signal)"),console.log("Projected duration: n/a")),console.log(""),console.log("Source breakdown:");for(let h of g)console.log(` ${h.n.toLocaleString().padStart(10)} ${h.embedding_model_id}`);return console.log(""),console.log("No writes performed. Re-run without --dry-run to execute."),{ok:!0,message:"dry run complete \u2014 no changes made",migrated:0,backupRetained:!1}}if(e.interactive){let g=u.chunksPerSec===null?"":` (based on ${u.sampleSize}-chunk live sample @ ${u.chunksPerSec.toFixed(1)} chunks/sec)`;if(console.log(`About to migrate ${a} chunks${m===null?"":` (~${m} min${g})`}.`),console.log(`Disk: ${l.message}`),!await tw(a,m))return{ok:!1,message:"user declined confirmation (re-run with --yes to skip the prompt)",migrated:0,backupRetained:!1}}if(i!==void 0){let g=E(),f=Q().modelId,h=50,_=g.prepare(`SELECT cm.rowid, cm.session_id, cm.text FROM chunk_meta cm
819
+ JOIN sessions s ON s.id = cm.session_id
820
+ WHERE s.project_id = ?
821
+ AND cm.embedding_model_id != ?
822
+ AND NOT EXISTS (
823
+ SELECT 1 FROM vec_chunks_v2 v WHERE v.rowid = cm.rowid
824
+ )
825
+ ORDER BY cm.rowid
826
+ LIMIT ?`),S=g.prepare(`SELECT COUNT(*) AS n FROM chunk_meta cm
827
+ JOIN sessions s ON s.id = cm.session_id
828
+ WHERE s.project_id = ?
829
+ AND cm.embedding_model_id != ?
830
+ AND NOT EXISTS (
831
+ SELECT 1 FROM vec_chunks_v2 v WHERE v.rowid = cm.rowid
832
+ )`).get(i,f).n,w=0,y=Date.now();for(;;){let T=_.all(i,f,h);if(T.length===0)break;let B;try{B=await Ne(T.map(v=>v.text))}catch(v){return{ok:!1,message:`embed() failed at scoped migrated=${w}: ${v instanceof Error?v.message:String(v)}`,migrated:w,backupRetained:!1}}if(g.transaction(()=>{let v=g.prepare("INSERT INTO vec_chunks_v2(rowid, embedding) VALUES (?, ?)");for(let O=0;O<T.length;O++){let k=T[O],I=B[O],F=Buffer.from(I.buffer,I.byteOffset,I.byteLength);v.run(BigInt(k.rowid),F)}})(),w+=T.length,e.onProgress){let v=(Date.now()-y)/1e3;e.onProgress({migrated:w,total:S,chunksPerSec:v>0?w/v:0})}}return{ok:!0,message:`scoped: ${w} chunks staged for project ${e.projectName}. Run \`recall semantic migrate\` (no --project) to finalize across the corpus.`,migrated:w,backupRetained:!1}}if(!n){let g=Q().modelId,h=(await Promise.resolve().then(()=>(R(),Or))).getDb().prepare("SELECT embedding_model_id FROM chunk_meta WHERE embedding_model_id != ? LIMIT 1").get(g)?.embedding_model_id??"unknown";n=Os({modelIdOld:h,modelIdNew:g})}E().prepare(`INSERT INTO vec_chunks_v2(rowid, embedding)
833
+ SELECT v.rowid, v.embedding
834
+ FROM vec_chunks v
835
+ JOIN chunk_meta cm ON cm.rowid = v.rowid
836
+ WHERE cm.embedding_model_id = ?
837
+ AND NOT EXISTS (
838
+ SELECT 1 FROM vec_chunks_v2 v2 WHERE v2.rowid = v.rowid
839
+ )`).run(n.modelIdNew);let p;try{p=await cd({migrationId:n.id,onProgress:e.onProgress})}catch(g){Fo(n.id,g instanceof Error?g.message:String(g));let f=g instanceof Tn?g.partialMigrated:0;return{ok:!1,message:g instanceof Error?g.message:String(g),migrated:f,backupRetained:!1}}try{await Wo(n.id)}catch(g){return{ok:!1,message:g instanceof Error?g.message:String(g),migrated:p.migrated,backupRetained:!1}}try{Oo()}catch(g){let f="";try{f=`${E().prepare("SELECT COUNT(*) AS n FROM chunk_queue WHERE action = 'pending_post_migration'").get().n} chunk_queue rows still flagged pending_post_migration; `}catch{}console.warn(`[migrate] post-swap promotePendingPostMigration failed: ${g instanceof Error?g.message:String(g)}. ${f}the daemon will drain on next tick.`)}return e.keepBackup||(await Promise.resolve().then(()=>(R(),Or))).getDb().exec("DROP TABLE IF EXISTS vec_chunks_v1_backup;"),{ok:!0,message:"migration complete",migrated:p.migrated,backupRetained:e.keepBackup}}async function iw(e){let t=E();if(!t.prepare("SELECT name FROM sqlite_master WHERE type IN ('table','virtual') AND name='vec_chunks_v1_backup'").get())return{ok:!1,message:"no vec_chunks_v1_backup found (already pruned, or no migration has run)"};let s=t.prepare("SELECT * FROM migration_state WHERE status = 'completed' ORDER BY id DESC LIMIT 1").get();if(!s)return{ok:!1,message:"no completed migration found to roll back"};let r=Jt(s);return e.force?(t.transaction(()=>{t.exec("DELETE FROM vec_chunks;"),t.exec("INSERT INTO vec_chunks(rowid, embedding) SELECT rowid, embedding FROM vec_chunks_v1_backup;"),t.prepare("UPDATE chunk_meta SET embedding_model_id = ? WHERE rowid IN (SELECT rowid FROM vec_chunks)").run(r.modelIdOld)})(),jo(r.id),{ok:!0,message:"rollback complete"}):{ok:!1,message:"rollback requires --force (any chunks indexed AFTER the migration completed will be lost on rollback because they were embedded with the new backend)"}}async function aw(e){let t=E();return t.prepare("SELECT name FROM sqlite_master WHERE name='vec_chunks_v1_backup'").get()?!e.force&&t.prepare("SELECT completed_at FROM migration_state WHERE status = 'completed' AND completed_at > datetime('now', '-30 days') ORDER BY id DESC LIMIT 1").get()?{ok:!1,message:"most recent migration completed less than 30 days ago. Use --force to prune anyway (rollback will no longer be possible)."}:(t.exec("DROP TABLE vec_chunks_v1_backup;"),{ok:!0,message:"rollback backup pruned"}):{ok:!1,message:"no vec_chunks_v1_backup to prune"}}var Tn,nw,sw,Ms=C(()=>{"use strict";R();Pe();zt();Ql();od();Pe();Ss();Tn=class extends Error{partialMigrated;cause;constructor(t,n,s){super(t),this.name="MigrationLoopError",this.partialMigrated=n,this.cause=s}};nw=5e3,sw=`
840
+ CREATE VIRTUAL TABLE IF NOT EXISTS vec_chunks_v2 USING vec0(
841
+ embedding float[768] distance_metric=cosine
842
+ );
843
+ `});import{writeFileSync as yd,readFileSync as YD,existsSync as Td,mkdirSync as Rd,readdirSync as qD}from"node:fs";import{join as Ps}from"node:path";function zw(){j(),Td(Ko)||Rd(Ko,{recursive:!0})}function Yw(){j(),Td(Qo)||Rd(Qo,{recursive:!0})}function kd(e){try{return JSON.parse(e)}catch{return e}}function Zo(e){return{id:e.id,source_session_id:e.source_session_id,target_session_id:e.target_session_id,link_type:e.link_type,confidence:e.confidence,source:e.source,evidence:kd(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function ei(e){return{id:e.id,source_session_id:e.source_session_id,target_session_id:e.target_session_id,link_type:e.link_type,confidence:e.confidence,evidence:kd(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function qw(e){if(!Number.isFinite(e)||e<0||e>1)throw new Error("confidence must be a number in [0, 1]")}function xd(e){if(!Hw.has(e))throw new Error(`invalid link_type: ${e}`)}function Cd(e){if(!Gw.has(e))throw new Error(`invalid inferred_by: ${e}`)}function Vw(e,t){if(!e||!t)throw new Error("source_session_id and target_session_id are required");if(e===t)throw new Error("a session cannot link to itself")}function Kw(e={}){let t=E(),n=[],s=[];e.sourceSessionId&&(n.push("source_session_id = ?"),s.push(e.sourceSessionId)),e.targetSessionId&&(n.push("target_session_id = ?"),s.push(e.targetSessionId)),e.linkType&&(xd(e.linkType),n.push("link_type = ?"),s.push(e.linkType)),e.approvedOnly&&n.push("approved = 1");let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_links ${r}
714
844
  ORDER BY confidence DESC, updated_at DESC
715
- LIMIT ?`).all(...n,o).map(Xr)}function Gr(e){return _().prepare(`SELECT * FROM session_links
845
+ LIMIT ?`).all(...s,o).map(Zo)}function ti(e){return E().prepare(`SELECT * FROM session_links
716
846
  WHERE source_session_id = ? OR target_session_id = ?
717
- ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(Xr)}function ot(e){CE(e.source_session_id,e.target_session_id),$c(e.link_type),kE(e.confidence),Pc(e.inferred_by);let t=_(),s=new Date().toISOString(),n=JSON.stringify(e.evidence??null);t.prepare(`INSERT INTO session_link_suggestions
847
+ ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(Zo)}function pt(e){Vw(e.source_session_id,e.target_session_id),xd(e.link_type),qw(e.confidence),Cd(e.inferred_by);let t=E(),n=new Date().toISOString(),s=JSON.stringify(e.evidence??null);t.prepare(`INSERT INTO session_link_suggestions
718
848
  (source_session_id, target_session_id, link_type,
719
849
  confidence, evidence, status, inferred_by,
720
850
  created_at, decided_at)
@@ -730,13 +860,13 @@ ${t.message}
730
860
  THEN excluded.evidence
731
861
  ELSE session_link_suggestions.evidence
732
862
  END,
733
- created_at = session_link_suggestions.created_at`).run(e.source_session_id,e.target_session_id,e.link_type,e.confidence,n,e.inferred_by,s);let r=t.prepare(`SELECT * FROM session_link_suggestions
863
+ created_at = session_link_suggestions.created_at`).run(e.source_session_id,e.target_session_id,e.link_type,e.confidence,s,e.inferred_by,n);let r=t.prepare(`SELECT * FROM session_link_suggestions
734
864
  WHERE source_session_id = ?
735
865
  AND target_session_id = ?
736
866
  AND link_type = ?
737
- AND inferred_by = ?`).get(e.source_session_id,e.target_session_id,e.link_type,e.inferred_by);if(!r)throw new Error("createSuggestion succeeded but read-back failed");return jc(),Jr(r)}function Vs(e={}){let t=_(),s=[],n=[];if(e.status){if(!yE.has(e.status))throw new Error(`invalid status: ${e.status}`);s.push("status = ?"),n.push(e.status)}e.sourceSessionId&&(s.push("source_session_id = ?"),n.push(e.sourceSessionId)),e.targetSessionId&&(s.push("target_session_id = ?"),n.push(e.targetSessionId)),e.inferredBy&&(Pc(e.inferredBy),s.push("inferred_by = ?"),n.push(e.inferredBy));let r=s.length?`WHERE ${s.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_link_suggestions ${r}
867
+ AND inferred_by = ?`).get(e.source_session_id,e.target_session_id,e.link_type,e.inferred_by);if(!r)throw new Error("createSuggestion succeeded but read-back failed");return Ld(),ei(r)}function Fs(e={}){let t=E(),n=[],s=[];if(e.status){if(!Xw.has(e.status))throw new Error(`invalid status: ${e.status}`);n.push("status = ?"),s.push(e.status)}e.sourceSessionId&&(n.push("source_session_id = ?"),s.push(e.sourceSessionId)),e.targetSessionId&&(n.push("target_session_id = ?"),s.push(e.targetSessionId)),e.inferredBy&&(Cd(e.inferredBy),n.push("inferred_by = ?"),s.push(e.inferredBy));let r=n.length?`WHERE ${n.join(" AND ")}`:"",o=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_link_suggestions ${r}
738
868
  ORDER BY confidence DESC, created_at DESC
739
- LIMIT ?`).all(...n,o).map(Jr)}function Fc(e,t,s={}){if(t!=="approved"&&t!=="rejected")throw new Error(`invalid decision: ${t}`);let n=s.source??"manual";if(!SE.has(n))throw new Error(`invalid source: ${n}`);let r=_(),o=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);if(!o)throw new Error(`suggestion ${e} not found`);if(o.status!=="pending")throw new Error(`suggestion ${e} already decided as ${o.status}`);let i=new Date().toISOString(),a;r.transaction(()=>{r.prepare(`UPDATE session_link_suggestions
869
+ LIMIT ?`).all(...s,o).map(ei)}function Ad(e,t,n={}){if(t!=="approved"&&t!=="rejected")throw new Error(`invalid decision: ${t}`);let s=n.source??"manual";if(!Ww.has(s))throw new Error(`invalid source: ${s}`);let r=E(),o=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);if(!o)throw new Error(`suggestion ${e} not found`);if(o.status!=="pending")throw new Error(`suggestion ${e} already decided as ${o.status}`);let i=new Date().toISOString(),a;r.transaction(()=>{r.prepare(`UPDATE session_link_suggestions
740
870
  SET status = ?, decided_at = ?
741
871
  WHERE id = ?`).run(t,i,e),t==="approved"&&(r.prepare(`INSERT INTO session_links
742
872
  (source_session_id, target_session_id, link_type,
@@ -748,10 +878,10 @@ ${t.message}
748
878
  source = excluded.source,
749
879
  evidence = excluded.evidence,
750
880
  approved = 1,
751
- updated_at = excluded.updated_at`).run(o.source_session_id,o.target_session_id,o.link_type,o.confidence,n,o.evidence,i,i),a=r.prepare(`SELECT * FROM session_links
881
+ updated_at = excluded.updated_at`).run(o.source_session_id,o.target_session_id,o.link_type,o.confidence,s,o.evidence,i,i),a=r.prepare(`SELECT * FROM session_links
752
882
  WHERE source_session_id = ?
753
883
  AND target_session_id = ?
754
- AND link_type = ?`).get(o.source_session_id,o.target_session_id,o.link_type))})(),jc(),t==="approved"&&NE(o.source_session_id);let d=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);return{suggestion:Jr(d),link:a?Xr(a):null}}function NE(e){try{RE();let t=LE({sourceSessionId:e}),s=Ks(Hr,`${e}.json`);if(t.length===0)return;let n={schema:"claude-recall.session-links.v1",source_session_id:e,backed_up_at:new Date().toISOString(),links:t};vc(s,JSON.stringify(n,null,2))}catch(t){console.error("[session-links] backup failed:",t)}}function jc(){try{xE();let e=Vs({limit:5e3}),t={schema:"claude-recall.session-link-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:e};vc(TE,JSON.stringify(t,null,2))}catch(e){console.error("[session-links] suggestions backup failed:",e)}}var bE,SE,yE,wE,Hr,Wr,TE,as=N(()=>{"use strict";w();P();bE=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),SE=new Set(["regex","llm","embedding","manual","auto","citation","git","terminal-registry"]),yE=new Set(["pending","approved","rejected"]),wE=new Set(["L1","L2","L3","L4","user"]),Hr=Ks(x,"links"),Wr=Ks(x,"suggestions"),TE=Ks(Wr,"index.json")});function sn(e){return e?Math.ceil(e.length/4):0}function dl(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/Yb);return Math.max(zb,t)}function ul(e,t){if(!e||!t)return 0;let s=Date.parse(e),n=Date.parse(t);return!Number.isFinite(s)||!Number.isFinite(n)?0:Math.abs(n-s)/(1e3*60*60*24)}function pl(e){return _().prepare(`SELECT s.id,
884
+ AND link_type = ?`).get(o.source_session_id,o.target_session_id,o.link_type))})(),Ld(),t==="approved"&&Qw(o.source_session_id);let d=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);return{suggestion:ei(d),link:a?Zo(a):null}}function Qw(e){try{zw();let t=Kw({sourceSessionId:e}),n=Ps(Ko,`${e}.json`);if(t.length===0)return;let s={schema:"claude-recall.session-links.v1",source_session_id:e,backed_up_at:new Date().toISOString(),links:t};yd(n,JSON.stringify(s,null,2))}catch(t){console.error("[session-links] backup failed:",t)}}function Ld(){try{Yw();let e=Fs({limit:5e3}),t={schema:"claude-recall.session-link-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:e};yd(Jw,JSON.stringify(t,null,2))}catch(e){console.error("[session-links] suggestions backup failed:",e)}}var Hw,Ww,Xw,Gw,Ko,Qo,Jw,Cn=C(()=>{"use strict";R();D();Hw=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),Ww=new Set(["regex","llm","embedding","manual","auto","citation","git","terminal-registry"]),Xw=new Set(["pending","approved","rejected"]),Gw=new Set(["L1","L2","L3","L4","user"]),Ko=Ps(x,"links"),Qo=Ps(x,"suggestions"),Jw=Ps(Qo,"index.json")});function Ws(e){return e?Math.ceil(e.length/4):0}function eu(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/_T);return Math.max(hT,t)}function tu(e,t){if(!e||!t)return 0;let n=Date.parse(e),s=Date.parse(t);return!Number.isFinite(n)||!Number.isFinite(s)?0:Math.abs(s-n)/(1e3*60*60*24)}function nu(e){return E().prepare(`SELECT s.id,
755
885
  NULLIF(sa.alias, '') AS alias,
756
886
  s.auto_title,
757
887
  s.auto_title_source,
@@ -762,37 +892,37 @@ ${t.message}
762
892
  FROM sessions s
763
893
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
764
894
  LEFT JOIN projects p ON p.id = s.project_id
765
- WHERE s.id = ?`).get(e)??null}function ml(e){let s=_().prepare("SELECT summary FROM session_semantic WHERE session_id = ?").get(e);if(!s||!s.summary)return null;let n=s.summary.trim();return n.length>0?n:null}function gl(e){let t=e.alias?.trim(),s=e.auto_title?.trim(),n=e.first_user_message?.trim();return s&&e.auto_title_source==="agent"?s:t||s||(n?n.slice(0,80):e.id.slice(0,8))}function Kb(e){let s=_().prepare(`SELECT id, auto_title, started_at
895
+ WHERE s.id = ?`).get(e)??null}function su(e){let n=E().prepare("SELECT summary FROM session_semantic WHERE session_id = ?").get(e);if(!n||!n.summary)return null;let s=n.summary.trim();return s.length>0?s:null}function ru(e){let t=e.alias?.trim(),n=e.auto_title?.trim(),s=e.first_user_message?.trim();return n&&e.auto_title_source==="agent"?n:t||n||(s?s.slice(0,80):e.id.slice(0,8))}function bT(e){let n=E().prepare(`SELECT id, auto_title, started_at
766
896
  FROM sessions
767
897
  WHERE project_id = ?
768
- ORDER BY COALESCE(started_at, ''), id`).all(e),n=new Set,r=new Set,o=[];for(let m of s){if(!m.auto_title||!m.auto_title.startsWith("/")){o.push({id:m.id,brand:null,skill:null});continue}let g=m.auto_title.split(" \xB7 "),f=g[0].trim(),h=g.length>1?g.slice(1).join(" \xB7 ").trim():null;o.push({id:m.id,brand:h||null,skill:f||null}),h&&n.add(h),f&&r.add(f)}let i=[...n].sort(),a=new Map;i.forEach((m,g)=>a.set(m,g));let d=[...r].sort(),l=new Map;d.forEach((m,g)=>l.set(m,g));let u=new Map,p=new Map;for(let m of o){if(!m.brand||!m.skill)continue;let g=a.get(m.brand),f=l.get(m.skill);if(g===void 0||f===void 0)continue;let h=`${g}.${f}`,E=(u.get(h)??0)+1;u.set(h,E),p.set(m.id,`${g}.${f}.${E}`)}return{byId:p}}function Vb(e){return{table:e!==null?Kb(e):null,originProjectId:e,cache:new Map}}function nn(e,t){let s=e.cache.get(t);if(s)return s;let n=pl(t);if(!n)return null;let r=e.table&&n.project_id===e.originProjectId?e.table.byId.get(t)??null:null,o={session_id:n.id,title:gl(n),decimal:r,summary:ml(n.id),project:n.project,started_at:n.started_at};return e.cache.set(t,o),o}function Zb(e,t){let n=_().prepare(`SELECT DISTINCT te.parent_session_id AS pid
898
+ ORDER BY COALESCE(started_at, ''), id`).all(e),s=new Set,r=new Set,o=[];for(let p of n){if(!p.auto_title||!p.auto_title.startsWith("/")){o.push({id:p.id,brand:null,skill:null});continue}let g=p.auto_title.split(" \xB7 "),f=g[0].trim(),h=g.length>1?g.slice(1).join(" \xB7 ").trim():null;o.push({id:p.id,brand:h||null,skill:f||null}),h&&s.add(h),f&&r.add(f)}let i=[...s].sort(),a=new Map;i.forEach((p,g)=>a.set(p,g));let d=[...r].sort(),l=new Map;d.forEach((p,g)=>l.set(p,g));let u=new Map,m=new Map;for(let p of o){if(!p.brand||!p.skill)continue;let g=a.get(p.brand),f=l.get(p.skill);if(g===void 0||f===void 0)continue;let h=`${g}.${f}`,_=(u.get(h)??0)+1;u.set(h,_),m.set(p.id,`${g}.${f}.${_}`)}return{byId:m}}function ST(e){return{table:e!==null?bT(e):null,originProjectId:e,cache:new Map}}function Xs(e,t){let n=e.cache.get(t);if(n)return n;let s=nu(t);if(!s)return null;let r=e.table&&s.project_id===e.originProjectId?e.table.byId.get(t)??null:null,o={session_id:s.id,title:ru(s),decimal:r,summary:su(s.id),project:s.project,started_at:s.started_at};return e.cache.set(t,o),o}function wT(e,t){let s=E().prepare(`SELECT DISTINCT te.parent_session_id AS pid
769
899
  FROM thread_edges te
770
900
  WHERE te.session_id = ?
771
- AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let o of n){if(!o.pid)continue;let i=nn(e,o.pid);i&&r.push(i)}return r}function Qb(e,t){let n=_().prepare(`SELECT DISTINCT te.session_id AS sid
901
+ AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let o of s){if(!o.pid)continue;let i=Xs(e,o.pid);i&&r.push(i)}return r}function yT(e,t){let s=E().prepare(`SELECT DISTINCT te.session_id AS sid
772
902
  FROM thread_edges te
773
- WHERE te.parent_session_id = ?`).all(t),r=[];for(let o of n){if(!o.sid)continue;let i=nn(e,o.sid);i&&r.push(i)}return r}function fl(e){let t=qb[e.linkType]??.5,s=Pt(e.confidence),n=t*s,r=dl(e.daysApart),o=e.embeddingCosine??.5,i=Pt(e.pagerank);if(e.scoring==="pagerank")return Pt(i);if(e.scoring==="embedding-rerank")return e.embeddingCosine===null?Pt(n):Pt(o);let a=.35*n+.2*r+.2*o+.25*i;return Pt(a)}function Pt(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}function eS(e,t,s,n,r){let o=new Map;function i(a,d){if(a===d)return;let l=o.get(a);l||(l=new Set,o.set(a,l)),l.add(d)}for(let a of t)i(a.source_session_id,a.target_session_id),i(a.target_session_id,a.source_session_id);for(let a of s)i(e,a.session_id);for(let a of s)i(a.session_id,e);for(let a of n)i(e,a.session_id);for(let a of n)i(a.session_id,e);if(r>1){let a=new Set([e]),d=new Set([e]);for(let l=1;l<r;l++){let u=new Set;for(let p of a){let m=o.get(p);if(m)for(let g of m){if(d.has(g))continue;let f=Gr(g).filter(h=>h.approved);for(let h of f)i(h.source_session_id,h.target_session_id),i(h.target_session_id,h.source_session_id);d.add(g),u.add(g)}}if(u.size===0)break;for(let p of u)a.add(p)}}return{edges:o}}function tS(e,t={}){let s=t.iterations??12,n=t.damping??.85,r=Array.from(e.edges.keys());if(r.length===0)return new Map;let o=1/r.length,i=new Map(r.map(l=>[l,o]));for(let l=0;l<s;l++){let u=new Map(r.map(p=>[p,(1-n)/r.length]));for(let p of r){let m=e.edges.get(p);if(!m||m.size===0)continue;let g=(i.get(p)??0)/m.size;for(let f of m)u.set(f,(u.get(f)??0)+n*g)}i=u}let a=0;for(let l of i.values())l>a&&(a=l);if(a<=0)return i;let d=new Map;for(let[l,u]of i)d.set(l,u/a);return d}function hl(e,t){let s=e.replace(/\s+/g," ").trim();return s.length<=t?s:`${s.slice(0,t-1).trimEnd()}\u2026`}function sS(e){let t=e.decimal?`${e.decimal} `:"",s=e.session_id.slice(0,8),n="evidence"in e&&e.evidence?` \u2014 ${e.evidence}`:"",r=`- ${t}${e.title} (${s})${n}`;if(e.summary){let o=hl(e.summary,_l);return`${r}
774
- ${o}`}return r}function nS(e,t,s){let n=[],r=[],o=0,i=e.decimal?`${e.decimal}: `:"",a=`# Neighborhood for ${e.session_id} (${i}${e.title})`;if(n.push(a),o+=sn(a),e.summary){let d=hl(e.summary,_l*4);n.push(d),o+=sn(d)}n.push("");for(let d of t){if(d.refs.length===0)continue;let l=`## ${d.heading}`,u=sn(l),p=[],m=0;for(let g of d.refs){let f=sS(g),h=sn(f);if(o+u+m+h>s){r.push({session_id:g.session_id,title:g.title,decimal:g.decimal,summary:g.summary,project:g.project,started_at:g.started_at});continue}p.push(f),m+=h}if(p.length>0){n.push(l);for(let g of p)n.push(g);n.push(""),o+=u+m}}for(;n.length>0&&n[n.length-1]==="";)n.pop();return{bundle:n.join(`
903
+ WHERE te.parent_session_id = ?`).all(t),r=[];for(let o of s){if(!o.sid)continue;let i=Xs(e,o.sid);i&&r.push(i)}return r}function ou(e){let t=ET[e.linkType]??.5,n=Yt(e.confidence),s=t*n,r=eu(e.daysApart),o=e.embeddingCosine??.5,i=Yt(e.pagerank);if(e.scoring==="pagerank")return Yt(i);if(e.scoring==="embedding-rerank")return e.embeddingCosine===null?Yt(s):Yt(o);let a=.35*s+.2*r+.2*o+.25*i;return Yt(a)}function Yt(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}function TT(e,t,n,s,r){let o=new Map;function i(a,d){if(a===d)return;let l=o.get(a);l||(l=new Set,o.set(a,l)),l.add(d)}for(let a of t)i(a.source_session_id,a.target_session_id),i(a.target_session_id,a.source_session_id);for(let a of n)i(e,a.session_id);for(let a of n)i(a.session_id,e);for(let a of s)i(e,a.session_id);for(let a of s)i(a.session_id,e);if(r>1){let a=new Set([e]),d=new Set([e]);for(let l=1;l<r;l++){let u=new Set;for(let m of a){let p=o.get(m);if(p)for(let g of p){if(d.has(g))continue;let f=ti(g).filter(h=>h.approved);for(let h of f)i(h.source_session_id,h.target_session_id),i(h.target_session_id,h.source_session_id);d.add(g),u.add(g)}}if(u.size===0)break;for(let m of u)a.add(m)}}return{edges:o}}function RT(e,t={}){let n=t.iterations??12,s=t.damping??.85,r=Array.from(e.edges.keys());if(r.length===0)return new Map;let o=1/r.length,i=new Map(r.map(l=>[l,o]));for(let l=0;l<n;l++){let u=new Map(r.map(m=>[m,(1-s)/r.length]));for(let m of r){let p=e.edges.get(m);if(!p||p.size===0)continue;let g=(i.get(m)??0)/p.size;for(let f of p)u.set(f,(u.get(f)??0)+s*g)}i=u}let a=0;for(let l of i.values())l>a&&(a=l);if(a<=0)return i;let d=new Map;for(let[l,u]of i)d.set(l,u/a);return d}function au(e,t){let n=e.replace(/\s+/g," ").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function kT(e){let t=e.decimal?`${e.decimal} `:"",n=e.session_id.slice(0,8),s="evidence"in e&&e.evidence?` \u2014 ${e.evidence}`:"",r=`- ${t}${e.title} (${n})${s}`;if(e.summary){let o=au(e.summary,iu);return`${r}
904
+ ${o}`}return r}function xT(e,t,n){let s=[],r=[],o=0,i=e.decimal?`${e.decimal}: `:"",a=`# Neighborhood for ${e.session_id} (${i}${e.title})`;if(s.push(a),o+=Ws(a),e.summary){let d=au(e.summary,iu*4);s.push(d),o+=Ws(d)}s.push("");for(let d of t){if(d.refs.length===0)continue;let l=`## ${d.heading}`,u=Ws(l),m=[],p=0;for(let g of d.refs){let f=kT(g),h=Ws(f);if(o+u+p+h>n){r.push({session_id:g.session_id,title:g.title,decimal:g.decimal,summary:g.summary,project:g.project,started_at:g.started_at});continue}m.push(f),p+=h}if(m.length>0){s.push(l);for(let g of m)s.push(g);s.push(""),o+=u+p}}for(;s.length>0&&s[s.length-1]==="";)s.pop();return{bundle:s.join(`
775
905
  `)+`
776
- `,budgetUsed:o,truncated:r}}function rS(e,t,s,n,r,o){let i=[];for(let a of s){if(n&&!n.has(a.link_type))continue;let d=null;if(a.source_session_id===t.session_id?d=a.target_session_id:a.target_session_id===t.session_id&&(d=a.source_session_id),!d)continue;let l=nn(e,d);if(!l)continue;let u=ul(t.started_at,l.started_at),p=fl({confidence:a.confidence,linkType:a.link_type,daysApart:u,embeddingCosine:null,pagerank:o.get(d)??0,scoring:r});i.push({...l,score:p,evidence:`(suggestion, ${a.inferred_by}) confidence=${a.confidence.toFixed(2)} ${Math.round(u)}d apart`,link_type:a.link_type})}return i}function rn(e,t={}){let s=Math.max(100,Math.floor(t.budget??Jb)),n=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??Gb)),o=t.includeWikiLinks??!0,i=t.includeSuggestions??!1,a=t.edgeTypes?new Set(t.edgeTypes):null,d=pl(e);if(!d)throw new Error(`session not found: ${e}`);let l=Vb(d.project_id),u={session_id:d.id,title:gl(d),decimal:l.table?.byId.get(d.id)??null,summary:ml(d.id),project:d.project,started_at:d.started_at};l.cache.set(d.id,u);let p=Zb(l,e),m=Qb(l,e),g=Gr(e).filter(A=>A.approved).filter(A=>!a||a.has(A.link_type)).filter(A=>o||A.link_type!=="wiki_link"),f=eS(e,g,p,m,r),h=tS(f),E=[],b=[],S=[],R=[];for(let A of g){let $=A.source_session_id===e?A.target_session_id:A.source_session_id,U=nn(l,$);if(!U)continue;let y=ul(u.started_at,U.started_at),B=fl({confidence:A.confidence,linkType:A.link_type,daysApart:y,embeddingCosine:null,pagerank:h.get($)??0,scoring:n}),M=dl(y),V=`${A.link_type} confidence=${A.confidence.toFixed(2)} recency=${M.toFixed(2)} (${Math.round(y)}d apart)`,Ge={...U,score:B,evidence:V,link_type:A.link_type};A.link_type==="citation"?E.push(Ge):A.link_type==="similar"?b.push(Ge):A.link_type==="wiki_link"?R.push(Ge):S.push(Ge)}if(i){let A=Vs({sourceSessionId:e,status:"pending",limit:100}),$=Vs({targetSessionId:e,status:"pending",limit:100}),U=[...A,...$],y=new Set,B=U.filter(V=>y.has(V.id)?!1:(y.add(V.id),!0)),M=rS(l,u,B,a,n,h);for(let V of M)V.link_type==="citation"?E.push(V):V.link_type==="similar"?b.push(V):V.link_type==="wiki_link"?R.push(V):S.push(V)}let T=(A,$)=>$.score-A.score;E.sort(T),b.sort(T),S.sort(T),R.sort(T);let D=nS(u,[{heading:"Parents",refs:p},{heading:"Children",refs:m},{heading:"Citations (approved)",refs:E},{heading:"Similar sessions",refs:b},{heading:"Cousins (skill track + temporal)",refs:S},{heading:"Wiki links (manual)",refs:R}],s);return{origin:u,parents:p,children:m,citations:E,similar:b,cousins:S,wikiLinks:R,bundle:D.bundle,budgetUsed:D.budgetUsed,budgetRemaining:Math.max(0,s-D.budgetUsed),truncated:D.truncated}}var Jb,Gb,Yb,zb,qb,_l,eo=N(()=>{"use strict";w();as();Jb=4e3,Gb=2,Yb=30,zb=.2,qb={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};_l=240});var Td={};he(Td,{computeAllHealthScores:()=>Ro,computeHealthScore:()=>wo,computeHealthScoreByName:()=>To});function ls(e){return Math.max(0,Math.min(1,e))}function wo(e){let t=_(),s=t.prepare("SELECT id, name FROM projects WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT COUNT(*) AS cnt,
906
+ `,budgetUsed:o,truncated:r}}function CT(e,t,n,s,r,o){let i=[];for(let a of n){if(s&&!s.has(a.link_type))continue;let d=null;if(a.source_session_id===t.session_id?d=a.target_session_id:a.target_session_id===t.session_id&&(d=a.source_session_id),!d)continue;let l=Xs(e,d);if(!l)continue;let u=tu(t.started_at,l.started_at),m=ou({confidence:a.confidence,linkType:a.link_type,daysApart:u,embeddingCosine:null,pagerank:o.get(d)??0,scoring:r});i.push({...l,score:m,evidence:`(suggestion, ${a.inferred_by}) confidence=${a.confidence.toFixed(2)} ${Math.round(u)}d apart`,link_type:a.link_type})}return i}function Gs(e,t={}){let n=Math.max(100,Math.floor(t.budget??gT)),s=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??fT)),o=t.includeWikiLinks??!0,i=t.includeSuggestions??!1,a=t.edgeTypes?new Set(t.edgeTypes):null,d=nu(e);if(!d)throw new Error(`session not found: ${e}`);let l=ST(d.project_id),u={session_id:d.id,title:ru(d),decimal:l.table?.byId.get(d.id)??null,summary:su(d.id),project:d.project,started_at:d.started_at};l.cache.set(d.id,u);let m=wT(l,e),p=yT(l,e),g=ti(e).filter(A=>A.approved).filter(A=>!a||a.has(A.link_type)).filter(A=>o||A.link_type!=="wiki_link"),f=TT(e,g,m,p,r),h=RT(f),_=[],b=[],S=[],w=[];for(let A of g){let v=A.source_session_id===e?A.target_session_id:A.source_session_id,O=Xs(l,v);if(!O)continue;let k=tu(u.started_at,O.started_at),I=ou({confidence:A.confidence,linkType:A.link_type,daysApart:k,embeddingCosine:null,pagerank:h.get(v)??0,scoring:s}),F=eu(k),ne=`${A.link_type} confidence=${A.confidence.toFixed(2)} recency=${F.toFixed(2)} (${Math.round(k)}d apart)`,et={...O,score:I,evidence:ne,link_type:A.link_type};A.link_type==="citation"?_.push(et):A.link_type==="similar"?b.push(et):A.link_type==="wiki_link"?w.push(et):S.push(et)}if(i){let A=Fs({sourceSessionId:e,status:"pending",limit:100}),v=Fs({targetSessionId:e,status:"pending",limit:100}),O=[...A,...v],k=new Set,I=O.filter(ne=>k.has(ne.id)?!1:(k.add(ne.id),!0)),F=CT(l,u,I,a,s,h);for(let ne of F)ne.link_type==="citation"?_.push(ne):ne.link_type==="similar"?b.push(ne):ne.link_type==="wiki_link"?w.push(ne):S.push(ne)}let y=(A,v)=>v.score-A.score;_.sort(y),b.sort(y),S.sort(y),w.sort(y);let B=xT(u,[{heading:"Parents",refs:m},{heading:"Children",refs:p},{heading:"Citations (approved)",refs:_},{heading:"Similar sessions",refs:b},{heading:"Cousins (skill track + temporal)",refs:S},{heading:"Wiki links (manual)",refs:w}],n);return{origin:u,parents:m,children:p,citations:_,similar:b,cousins:S,wikiLinks:w,bundle:B.bundle,budgetUsed:B.budgetUsed,budgetRemaining:Math.max(0,n-B.budgetUsed),truncated:B.truncated}}var gT,fT,_T,hT,ET,iu,li=C(()=>{"use strict";R();Cn();gT=4e3,fT=2,_T=30,hT=.2,ET={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};iu=240});var km={};Z(km,{computeAllHealthScores:()=>vi,computeHealthScore:()=>Ni,computeHealthScoreByName:()=>Oi});function Nn(e){return Math.max(0,Math.min(1,e))}function Ni(e){let t=E(),n=t.prepare("SELECT id, name FROM projects WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT COUNT(*) AS cnt,
777
907
  MAX(started_at) AS latest
778
- FROM sessions WHERE project_id = ?`).get(e),r=n.cnt;if(r===0)return{projectId:e,projectName:s.name,score:0,breakdown:{sessionCount:{raw:0,score:0,weight:.2},recency:{daysSinceLastSession:1/0,score:0,weight:.25},fragmentation:{avgMessages:0,score:0,weight:.15},searchCoverage:{ratio:0,score:0,weight:.2},tagCoverage:{ratio:0,score:0,weight:.2}}};let o=ls(r/10),i=n.latest?(Date.now()-new Date(n.latest).getTime())/(1e3*60*60*24):90,a=ls(1-i/90),l=t.prepare(`SELECT AVG(message_count) AS avg_msgs
779
- FROM sessions WHERE project_id = ?`).get(e).avg_msgs??0,u=ls((l-2)/3),p=t.prepare(`SELECT COUNT(*) AS total,
908
+ FROM sessions WHERE project_id = ?`).get(e),r=s.cnt;if(r===0)return{projectId:e,projectName:n.name,score:0,breakdown:{sessionCount:{raw:0,score:0,weight:.2},recency:{daysSinceLastSession:1/0,score:0,weight:.25},fragmentation:{avgMessages:0,score:0,weight:.15},searchCoverage:{ratio:0,score:0,weight:.2},tagCoverage:{ratio:0,score:0,weight:.2}}};let o=Nn(r/10),i=s.latest?(Date.now()-new Date(s.latest).getTime())/(1e3*60*60*24):90,a=Nn(1-i/90),l=t.prepare(`SELECT AVG(message_count) AS avg_msgs
909
+ FROM sessions WHERE project_id = ?`).get(e).avg_msgs??0,u=Nn((l-2)/3),m=t.prepare(`SELECT COUNT(*) AS total,
780
910
  SUM(CASE WHEN m.content_text IS NOT NULL AND m.content_text != '' THEN 1 ELSE 0 END) AS covered
781
911
  FROM messages m
782
912
  JOIN sessions s ON s.id = m.session_id
783
- WHERE s.project_id = ?`).get(e),m=p.total>0?p.covered/p.total:.5,g=ls(m),f=t.prepare(`SELECT COUNT(DISTINCT s.id) AS total,
913
+ WHERE s.project_id = ?`).get(e),p=m.total>0?m.covered/m.total:.5,g=Nn(p),f=t.prepare(`SELECT COUNT(DISTINCT s.id) AS total,
784
914
  COUNT(DISTINCT st.session_id) AS tagged
785
915
  FROM sessions s
786
916
  LEFT JOIN session_tags st ON st.session_id = s.id
787
- WHERE s.project_id = ?`).get(e),h=f.total>0?f.tagged/f.total:0,E=ls(h),b=Math.round((o*.2+a*.25+u*.15+g*.2+E*.2)*100);return{projectId:e,projectName:s.name,score:b,breakdown:{sessionCount:{raw:r,score:o,weight:.2},recency:{daysSinceLastSession:Math.round(i),score:a,weight:.25},fragmentation:{avgMessages:Math.round(l*10)/10,score:u,weight:.15},searchCoverage:{ratio:Math.round(m*100)/100,score:g,weight:.2},tagCoverage:{ratio:Math.round(h*100)/100,score:E,weight:.2}}}}function To(e){let s=_().prepare("SELECT id FROM projects WHERE name = ?").get(e);return s?wo(s.id):null}function Ro(){let t=_().prepare("SELECT id FROM projects ORDER BY name").all(),s=[];for(let n of t){let r=wo(n.id);r&&s.push(r)}return s}var xo=N(()=>{"use strict";w()});function Iu(e,t){return{type:"svg",props:{width:e,height:e,viewBox:"0 0 64 64",style:{display:"flex"},children:[{type:"line",props:{x1:14,y1:16,x2:46,y2:16,stroke:t,strokeWidth:3,strokeLinecap:"round"}},{type:"line",props:{x1:14,y1:24,x2:38,y2:24,stroke:t,strokeWidth:3,strokeLinecap:"round"}},{type:"line",props:{x1:10,y1:32,x2:54,y2:32,stroke:xt,strokeWidth:5,strokeLinecap:"round"}},{type:"line",props:{x1:14,y1:40,x2:42,y2:40,stroke:t,strokeWidth:3,strokeLinecap:"round"}},{type:"line",props:{x1:14,y1:48,x2:36,y2:48,stroke:t,strokeWidth:3,strokeLinecap:"round"}}]}}}function at(e,t){return{type:"div",props:{style:{display:"flex",alignItems:"center",justifyContent:"space-between",width:"100%"},children:[{type:"div",props:{style:{display:"flex",alignItems:"center",gap:"18px"},children:[Iu(72,e.markStroke),{type:"span",props:{style:{fontSize:"36px",fontWeight:700,color:e.wordmarkFg,letterSpacing:"-0.01em"},children:"Claude Recall"}}]}},{type:"span",props:{style:{fontSize:"17px",color:e.wordmarkFg,opacity:.6,textTransform:"uppercase",letterSpacing:"0.18em",fontWeight:600},children:t}}]}}}function ct(e){return{type:"div",props:{style:{display:"flex",alignItems:"center",justifyContent:"space-between",width:"100%"},children:[{type:"div",props:{style:{display:"flex",alignItems:"center",gap:"14px"},children:[Iu(52,e.markStroke),{type:"span",props:{style:{fontSize:"24px",fontWeight:700,color:e.wordmarkFg},children:"clauderecall.com"}}]}},{type:"span",props:{style:{fontSize:"20px",color:e.wordmarkFg,opacity:.7,fontWeight:500},children:"local memory for Claude Code"}}]}}}function YT(e){return e>=1e9?`${(e/1e9).toFixed(2).replace(/\.?0+$/,"")}B`:e>=1e6?`${(e/1e6).toFixed(2).replace(/\.?0+$/,"")}M`:e>=1e3?`${(e/1e3).toFixed(1).replace(/\.0$/,"")}k`:String(e)}function zT(e){return e.toLocaleString("en-US")}function lt(e){return e<1e3?String(e):`${YT(e)} (${zT(e)})`}function dt(e){return new Date(e).toLocaleDateString("en-US",{month:"long",day:"numeric",year:"numeric"})}var xt,us=N(()=>{"use strict";xt="#f97316"});var Du={};he(Du,{render:()=>VT});function Tn(e,t){return{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"4px"},children:[{type:"span",props:{style:{fontSize:"34px",fontWeight:700,color:ps,fontFamily:"JetBrains Mono"},children:e}},{type:"span",props:{style:{fontSize:"14px",color:Rn,textTransform:"uppercase",letterSpacing:"0.16em"},children:t}}]}}}function VT(e){let s=[dt(e.sessionDate)];e.durationLabel&&s.push(e.durationLabel);let n=s.join(" \xB7 "),r=e.costDollars??"$\u2014",o=typeof e.cachedTokens=="number"&&e.cachedTokens>=1e5?`+ ${lt(e.cachedTokens)} cached`:null,i=[at(Mu,"session receipt"),{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"8px"},children:[{type:"div",props:{style:{fontSize:"44px",fontWeight:700,color:ps,lineHeight:1.15,wordBreak:"break-word"},children:e.sessionTitle}},{type:"div",props:{style:{fontSize:"20px",color:Rn},children:n}}]}},{type:"div",props:{style:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",padding:"32px 0"},children:[{type:"span",props:{style:{fontSize:"180px",fontWeight:700,color:xt,fontFamily:"JetBrains Mono",lineHeight:1,letterSpacing:"-0.04em"},children:r}},{type:"span",props:{style:{marginTop:"16px",fontSize:"16px",color:Rn,textTransform:"uppercase",letterSpacing:"0.24em",fontWeight:600},children:"session cost"}}]}},{type:"div",props:{style:{display:"flex",width:"100%",height:"1px",backgroundColor:KT}}},{type:"div",props:{style:{display:"flex",justifyContent:"space-between",gap:"24px"},children:[Tn(String(e.messageCount),"msgs"),Tn(String(e.filesReferenced),"files"),Tn(String(e.toolCallCount),"tools"),Tn(e.model??"\u2014","model")]}}];return o&&i.push({type:"div",props:{style:{fontSize:"14px",color:Rn,fontFamily:"JetBrains Mono",opacity:.7},children:o}}),e.verdict&&i.push({type:"div",props:{style:{display:"flex",fontSize:"24px",fontStyle:"italic",color:ps,borderLeft:`3px solid ${xt}`,paddingLeft:"20px",marginTop:"8px"},children:`"${e.verdict}"`}}),i.push({type:"div",props:{style:{display:"flex",flex:1}}}),i.push(ct(Mu)),{type:"div",props:{style:{display:"flex",flexDirection:"column",width:"100%",height:"100%",backgroundColor:qT,padding:"64px 72px",fontFamily:"Inter",gap:"28px"},children:i}}}var qT,ps,Rn,KT,Mu,$u=N(()=>{"use strict";us();qT="#1a1b1e",ps="#e7e9ee",Rn="#8b9098",KT="#2a2c33",Mu={markStroke:ps,wordmarkFg:ps}});var Fu={};he(Fu,{render:()=>eR});function xn(e,t){return{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"4px"},children:[{type:"span",props:{style:{fontSize:"34px",fontWeight:700,color:ms,fontFamily:"JetBrains Mono"},children:e}},{type:"span",props:{style:{fontSize:"14px",color:kn,fontFamily:"JetBrains Mono",textTransform:"lowercase"},children:`// ${t}`}}]}}}function eR(e){let t=[dt(e.sessionDate),e.durationLabel].filter(Boolean).join(" \xB7 "),s=e.costDollars??"$\u2014",n=typeof e.cachedTokens=="number"&&e.cachedTokens>=1e5?`# ${lt(e.cachedTokens)} tokens cache-replayed (free)`:null,r=[at(Pu,"recall.cli/share"),{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"6px"},children:[{type:"div",props:{style:{fontSize:"18px",color:Po,fontFamily:"JetBrains Mono"},children:"$ recall share --session"}},{type:"div",props:{style:{fontSize:"40px",fontWeight:700,color:ms,lineHeight:1.15,wordBreak:"break-word",fontFamily:"JetBrains Mono"},children:e.sessionTitle}},{type:"div",props:{style:{fontSize:"18px",color:kn,fontFamily:"JetBrains Mono"},children:t}}]}},{type:"div",props:{style:{display:"flex",flexDirection:"column",alignItems:"flex-start",padding:"12px 0"},children:[{type:"span",props:{style:{fontSize:"20px",color:kn,fontFamily:"JetBrains Mono"},children:"> total_cost:"}},{type:"span",props:{style:{fontSize:"180px",fontWeight:700,color:Po,fontFamily:"JetBrains Mono",lineHeight:1,letterSpacing:"-0.04em"},children:s}}]}},{type:"div",props:{style:{display:"flex",width:"100%",height:"1px",backgroundColor:QT}}},{type:"div",props:{style:{display:"flex",justifyContent:"space-between",gap:"24px"},children:[xn(String(e.messageCount),"msgs"),xn(String(e.filesReferenced),"files"),xn(String(e.toolCallCount),"tools"),xn(e.model??"\u2014","model")]}}];return n&&r.push({type:"div",props:{style:{fontSize:"14px",color:kn,fontFamily:"JetBrains Mono",opacity:.7},children:n}}),e.verdict&&r.push({type:"div",props:{style:{display:"flex",fontSize:"22px",color:ms,fontFamily:"JetBrains Mono",borderLeft:`3px solid ${Po}`,paddingLeft:"20px",marginTop:"8px"},children:`// ${e.verdict}`}}),r.push({type:"div",props:{style:{display:"flex",flex:1}}}),r.push(ct(Pu)),{type:"div",props:{style:{display:"flex",flexDirection:"column",width:"100%",height:"100%",backgroundColor:ZT,padding:"64px 72px",fontFamily:"Inter",gap:"28px"},children:r}}}var ZT,ms,kn,QT,Po,Pu,ju=N(()=>{"use strict";us();ZT="#0d0d0f",ms="#e1e7ee",kn="#6b7480",QT="#1f2229",Po="#7ee787",Pu={markStroke:ms,wordmarkFg:ms}});var Bu={};he(Bu,{render:()=>sR});function Cn(e,t){return{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"4px"},children:[{type:"span",props:{style:{fontSize:"34px",fontWeight:700,color:kt,fontFamily:"JetBrains Mono"},children:e}},{type:"span",props:{style:{fontSize:"14px",color:Ln,textTransform:"uppercase",letterSpacing:"0.16em",fontWeight:600},children:t}}]}}}function sR(e){let t=[dt(e.sessionDate),e.durationLabel].filter(Boolean).join(" \xB7 "),s=e.costDollars??"$\u2014",n=typeof e.cachedTokens=="number"&&e.cachedTokens>=1e5?`+ ${lt(e.cachedTokens)} cached`:null,r=[at(Uu,"session receipt"),{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"8px"},children:[{type:"div",props:{style:{fontSize:"44px",fontWeight:700,color:kt,lineHeight:1.15,wordBreak:"break-word"},children:e.sessionTitle}},{type:"div",props:{style:{fontSize:"20px",color:Ln},children:t}}]}},{type:"div",props:{style:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",padding:"32px 0"},children:[{type:"span",props:{style:{fontSize:"180px",fontWeight:700,color:kt,fontFamily:"JetBrains Mono",lineHeight:1,letterSpacing:"-0.04em"},children:s}},{type:"span",props:{style:{marginTop:"16px",fontSize:"16px",color:Ln,textTransform:"uppercase",letterSpacing:"0.24em",fontWeight:600},children:"session cost"}}]}},{type:"div",props:{style:{display:"flex",width:"100%",height:"1px",backgroundColor:tR}}},{type:"div",props:{style:{display:"flex",justifyContent:"space-between",gap:"24px"},children:[Cn(String(e.messageCount),"msgs"),Cn(String(e.filesReferenced),"files"),Cn(String(e.toolCallCount),"tools"),Cn(e.model??"\u2014","model")]}}];return n&&r.push({type:"div",props:{style:{fontSize:"14px",color:Ln,fontFamily:"JetBrains Mono",opacity:.85},children:n}}),e.verdict&&r.push({type:"div",props:{style:{display:"flex",fontSize:"24px",fontStyle:"italic",color:kt,borderLeft:`3px solid ${kt}`,paddingLeft:"20px",marginTop:"8px"},children:`"${e.verdict}"`}}),r.push({type:"div",props:{style:{display:"flex",flex:1}}}),r.push(ct(Uu)),{type:"div",props:{style:{display:"flex",flexDirection:"column",width:"100%",height:"100%",backgroundImage:"linear-gradient(135deg, #b1370f 0%, #f97316 55%, #fbbf24 100%)",padding:"64px 72px",fontFamily:"Inter",gap:"28px"},children:r}}}var kt,Ln,tR,Uu,Hu=N(()=>{"use strict";us();kt="#ffffff",Ln="rgba(255,255,255,0.7)",tR="rgba(255,255,255,0.18)",Uu={markStroke:kt,wordmarkFg:kt}});var Xu={};he(Xu,{render:()=>aR});function Nn(e,t){return{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"4px"},children:[{type:"span",props:{style:{fontSize:"34px",fontWeight:800,color:Bt,fontFamily:"JetBrains Mono"},children:e}},{type:"span",props:{style:{fontSize:"14px",color:gs,textTransform:"uppercase",letterSpacing:"0.16em",fontWeight:600},children:t}}]}}}function iR(e){let t=e.reduce((s,n)=>s+n.value,0)||1;return{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"8px"},children:[{type:"div",props:{style:{display:"flex",width:"100%",height:"10px",borderRadius:"5px",backgroundColor:oR,overflow:"hidden"},children:e.map(s=>({type:"div",props:{style:{display:"flex",width:`${s.value/t*100}%`,height:"100%",backgroundColor:s.color}}}))}},{type:"div",props:{style:{display:"flex",justifyContent:"space-between",fontSize:"12px",color:gs,fontWeight:600,textTransform:"uppercase",letterSpacing:"0.14em"},children:e.map(s=>({type:"span",props:{style:{display:"flex"},children:`${s.label} \xB7 ${s.value}`}}))}}]}}}function aR(e){let t=[dt(e.sessionDate),e.durationLabel].filter(Boolean).join(" \xB7 "),s=e.costDollars??"$\u2014",n=typeof e.cachedTokens=="number"&&e.cachedTokens>=1e5?`+ ${lt(e.cachedTokens)} cached`:null,r=[at(Wu,"session receipt"),{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"8px"},children:[{type:"div",props:{style:{fontSize:"40px",fontWeight:700,color:Bt,lineHeight:1.15,wordBreak:"break-word"},children:e.sessionTitle}},{type:"div",props:{style:{fontSize:"18px",color:gs},children:t}}]}},{type:"div",props:{style:{display:"flex",flexDirection:"column",alignItems:"flex-start",padding:"8px 0"},children:[{type:"span",props:{style:{fontSize:"14px",color:gs,textTransform:"uppercase",letterSpacing:"0.24em",fontWeight:700},children:"session cost"}},{type:"span",props:{style:{fontSize:"180px",fontWeight:800,color:Bt,fontFamily:"JetBrains Mono",lineHeight:1,letterSpacing:"-0.04em"},children:s}}]}},{type:"div",props:{style:{display:"flex",width:"100%",height:"1px",backgroundColor:rR}}},{type:"div",props:{style:{display:"flex",justifyContent:"space-between",gap:"24px"},children:[Nn(String(e.messageCount),"msgs"),Nn(String(e.filesReferenced),"files"),Nn(String(e.toolCallCount),"tools"),Nn(e.model??"\u2014","model")]}},iR([{value:e.messageCount,color:xt,label:"msgs"},{value:e.filesReferenced,color:"#0a0a0a",label:"files"},{value:e.toolCallCount,color:"#5a6068",label:"tools"}])];return n&&r.push({type:"div",props:{style:{fontSize:"14px",color:gs,fontFamily:"JetBrains Mono",opacity:.85},children:n}}),e.verdict&&r.push({type:"div",props:{style:{display:"flex",fontSize:"22px",fontStyle:"italic",color:Bt,borderLeft:`3px solid ${xt}`,paddingLeft:"20px",marginTop:"8px"},children:`"${e.verdict}"`}}),r.push({type:"div",props:{style:{display:"flex",flex:1}}}),r.push(ct(Wu)),{type:"div",props:{style:{display:"flex",flexDirection:"column",width:"100%",height:"100%",backgroundColor:nR,padding:"64px 72px",fontFamily:"Inter",gap:"24px"},children:r}}}var nR,Bt,gs,rR,oR,Wu,Ju=N(()=>{"use strict";us();nR="#f6f7f9",Bt="#0a0a0a",gs="#5a6068",rR="#e3e6eb",oR="#dde1e7",Wu={markStroke:Bt,wordmarkFg:Bt}});var Gu={};he(Gu,{render:()=>dR});function Fo(e,t){return{type:"div",props:{style:{display:"flex",flexDirection:"column",alignItems:"center",gap:"8px",flex:1},children:[{type:"div",props:{style:{fontSize:"52px",fontWeight:700,color:An},children:t}},{type:"div",props:{style:{fontSize:"16px",color:Wt,textTransform:"uppercase",letterSpacing:"2px"},children:e}}]}}}function jo(e,t){return{type:"div",props:{style:{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"16px 24px",backgroundColor:Uo,borderRadius:"12px"},children:[{type:"span",props:{style:{fontSize:"18px",color:Wt},children:e}},{type:"span",props:{style:{fontSize:"20px",fontWeight:700,color:An},children:t}}]}}}function dR(e){let t=Math.round(e.healthScore),s=[{type:"div",props:{style:{display:"flex",flexDirection:"column",alignItems:"center",gap:"8px"},children:[{type:"div",props:{style:{fontSize:"16px",color:Wt,textTransform:"uppercase",letterSpacing:"6px"},children:"YOUR MONTH IN CODE"}},{type:"div",props:{style:{fontSize:"44px",fontWeight:700,color:An},children:e.month}}]}},{type:"div",props:{style:{display:"flex",flexDirection:"column",alignItems:"center",gap:"12px",backgroundColor:Uo,borderRadius:"16px",padding:"28px",border:`1px solid ${Ht}40`},children:[{type:"div",props:{style:{fontSize:"16px",color:lR,textTransform:"uppercase",letterSpacing:"2px"},children:"You are a"}},{type:"div",props:{style:{fontSize:"36px",fontWeight:700,color:Ht},children:e.archetype}}]}},{type:"div",props:{style:{display:"flex",gap:"16px",width:"100%"},children:[Fo("Recalls",String(e.totalRecalls)),Fo("Tokens piped",e.totalTokensPiped>999999?`${(e.totalTokensPiped/1e6).toFixed(1)}M`:e.totalTokensPiped.toLocaleString()),Fo("Sessions",String(e.sessionsIndexed))]}},{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"8px",width:"100%"},children:[{type:"div",props:{style:{fontSize:"14px",color:Wt,textTransform:"uppercase",letterSpacing:"2px",marginBottom:"4px"},children:"Highlights"}},jo("Most recalled",e.mostRecalledSession),jo("Biggest recall",`${e.biggestRecallTokens.toLocaleString()} tokens`),jo("Peak hours",e.mostActiveHours)]}},{type:"div",props:{style:{display:"flex",flexDirection:"column",alignItems:"center",gap:"12px"},children:[{type:"div",props:{style:{width:"100px",height:"100px",borderRadius:"50%",border:`6px solid ${Ht}`,display:"flex",alignItems:"center",justifyContent:"center"},children:{type:"span",props:{style:{fontSize:"32px",fontWeight:700,color:Ht},children:`${t}`}}}},{type:"div",props:{style:{fontSize:"14px",color:Wt,textTransform:"uppercase",letterSpacing:"2px"},children:"Memory health"}}]}}];return e.verdict&&s.push({type:"div",props:{style:{backgroundColor:Uo,borderLeft:`3px solid ${Ht}`,borderRadius:"8px",padding:"20px 24px",fontSize:"20px",color:An,fontStyle:"italic"},children:`"${e.verdict}"`}}),s.push({type:"div",props:{style:{display:"flex",alignItems:"center",justifyContent:"center",gap:"12px",marginTop:"auto"},children:[{type:"span",props:{style:{fontSize:"22px",fontWeight:700,color:Ht},children:"Claude Recall"}},{type:"span",props:{style:{fontSize:"18px",color:Wt},children:"clauderecall.com"}}]}}),{type:"div",props:{style:{display:"flex",flexDirection:"column",width:"100%",height:"100%",backgroundColor:cR,padding:"72px 64px",fontFamily:"Inter",gap:"36px"},children:s}}}var cR,Uo,An,Wt,Ht,lR,Yu=N(()=>{"use strict";cR="#0b0c0f",Uo="#15171c",An="#e7e9ee",Wt="#8b9098",Ht="#f97316",lR="#fb923c"});var Jo,ap,z,k,te,De,cp,lp,dp,up,Go,Yo,ut=N(()=>{"use strict";Jo=[String.raw` ___ _ _ _ _ ___ ___ ___ ___ ___ _ _ _ `,String.raw` / __| | /_\ | | | | \| __| | _ \ __/ __| /_\ | | | | `,String.raw`| (__| |__ / _ \| |_| | |) | _| | / _| (__ / _ \| |__| |__ `,String.raw` \___|____/_/ \_\\___/|___/|___| |_|_\___\___/_/ \_\____|____|`],ap=Jo[0]?.length??64,z="#f97316",k="#8b9098",te="#10b981",De="#f59e0b",cp="#60a5fa",lp="#34d399",dp="#c084fc",up="CLAUDE RECALL",Go=100,Yo=30});import{useEffect as BR,useState as HR}from"react";import{Box as pp,Text as ge}from"ink";import{createRequire as WR}from"node:module";import{existsSync as XR}from"node:fs";import{jsx as ie,jsxs as gp}from"react/jsx-runtime";function mp({cols:e}){let[t,s]=HR(YR);BR(()=>{let r=!1,o=Q(),i=0,a=0;if(XR(se))try{let l=_().prepare(`SELECT
917
+ WHERE s.project_id = ?`).get(e),h=f.total>0?f.tagged/f.total:0,_=Nn(h),b=Math.round((o*.2+a*.25+u*.15+g*.2+_*.2)*100);return{projectId:e,projectName:n.name,score:b,breakdown:{sessionCount:{raw:r,score:o,weight:.2},recency:{daysSinceLastSession:Math.round(i),score:a,weight:.25},fragmentation:{avgMessages:Math.round(l*10)/10,score:u,weight:.15},searchCoverage:{ratio:Math.round(p*100)/100,score:g,weight:.2},tagCoverage:{ratio:Math.round(h*100)/100,score:_,weight:.2}}}}function Oi(e){let n=E().prepare("SELECT id FROM projects WHERE name = ?").get(e);return n?Ni(n.id):null}function vi(){let t=E().prepare("SELECT id FROM projects ORDER BY name").all(),n=[];for(let s of t){let r=Ni(s.id);r&&n.push(r)}return n}var Ii=C(()=>{"use strict";R()});function Pp(e,t){return{type:"svg",props:{width:e,height:e,viewBox:"0 0 64 64",style:{display:"flex"},children:[{type:"line",props:{x1:14,y1:16,x2:46,y2:16,stroke:t,strokeWidth:3,strokeLinecap:"round"}},{type:"line",props:{x1:14,y1:24,x2:38,y2:24,stroke:t,strokeWidth:3,strokeLinecap:"round"}},{type:"line",props:{x1:10,y1:32,x2:54,y2:32,stroke:vt,strokeWidth:5,strokeLinecap:"round"}},{type:"line",props:{x1:14,y1:40,x2:42,y2:40,stroke:t,strokeWidth:3,strokeLinecap:"round"}},{type:"line",props:{x1:14,y1:48,x2:36,y2:48,stroke:t,strokeWidth:3,strokeLinecap:"round"}}]}}}function ft(e,t){return{type:"div",props:{style:{display:"flex",alignItems:"center",justifyContent:"space-between",width:"100%"},children:[{type:"div",props:{style:{display:"flex",alignItems:"center",gap:"18px"},children:[Pp(72,e.markStroke),{type:"span",props:{style:{fontSize:"36px",fontWeight:700,color:e.wordmarkFg,letterSpacing:"-0.01em"},children:"Claude Recall"}}]}},{type:"span",props:{style:{fontSize:"17px",color:e.wordmarkFg,opacity:.6,textTransform:"uppercase",letterSpacing:"0.18em",fontWeight:600},children:t}}]}}}function _t(e){return{type:"div",props:{style:{display:"flex",alignItems:"center",justifyContent:"space-between",width:"100%"},children:[{type:"div",props:{style:{display:"flex",alignItems:"center",gap:"14px"},children:[Pp(52,e.markStroke),{type:"span",props:{style:{fontSize:"24px",fontWeight:700,color:e.wordmarkFg},children:"clauderecall.com"}}]}},{type:"span",props:{style:{fontSize:"20px",color:e.wordmarkFg,opacity:.7,fontWeight:500},children:"local memory for Claude Code"}}]}}}function Kx(e){return e>=1e9?`${(e/1e9).toFixed(2).replace(/\.?0+$/,"")}B`:e>=1e6?`${(e/1e6).toFixed(2).replace(/\.?0+$/,"")}M`:e>=1e3?`${(e/1e3).toFixed(1).replace(/\.0$/,"")}k`:String(e)}function Qx(e){return e.toLocaleString("en-US")}function ht(e){return e<1e3?String(e):`${Kx(e)} (${Qx(e)})`}function Et(e){return new Date(e).toLocaleDateString("en-US",{month:"long",day:"numeric",year:"numeric"})}var vt,vn=C(()=>{"use strict";vt="#f97316"});var jp={};Z(jp,{render:()=>tC});function mr(e,t){return{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"4px"},children:[{type:"span",props:{style:{fontSize:"34px",fontWeight:700,color:In,fontFamily:"JetBrains Mono"},children:e}},{type:"span",props:{style:{fontSize:"14px",color:pr,textTransform:"uppercase",letterSpacing:"0.16em"},children:t}}]}}}function tC(e){let n=[Et(e.sessionDate)];e.durationLabel&&n.push(e.durationLabel);let s=n.join(" \xB7 "),r=e.costDollars??"$\u2014",o=typeof e.cachedTokens=="number"&&e.cachedTokens>=1e5?`+ ${ht(e.cachedTokens)} cached`:null,i=[ft(Fp,"session receipt"),{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"8px"},children:[{type:"div",props:{style:{fontSize:"44px",fontWeight:700,color:In,lineHeight:1.15,wordBreak:"break-word"},children:e.sessionTitle}},{type:"div",props:{style:{fontSize:"20px",color:pr},children:s}}]}},{type:"div",props:{style:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",padding:"32px 0"},children:[{type:"span",props:{style:{fontSize:"180px",fontWeight:700,color:vt,fontFamily:"JetBrains Mono",lineHeight:1,letterSpacing:"-0.04em"},children:r}},{type:"span",props:{style:{marginTop:"16px",fontSize:"16px",color:pr,textTransform:"uppercase",letterSpacing:"0.24em",fontWeight:600},children:"session cost"}}]}},{type:"div",props:{style:{display:"flex",width:"100%",height:"1px",backgroundColor:eC}}},{type:"div",props:{style:{display:"flex",justifyContent:"space-between",gap:"24px"},children:[mr(String(e.messageCount),"msgs"),mr(String(e.filesReferenced),"files"),mr(String(e.toolCallCount),"tools"),mr(e.model??"\u2014","model")]}}];return o&&i.push({type:"div",props:{style:{fontSize:"14px",color:pr,fontFamily:"JetBrains Mono",opacity:.7},children:o}}),e.verdict&&i.push({type:"div",props:{style:{display:"flex",fontSize:"24px",fontStyle:"italic",color:In,borderLeft:`3px solid ${vt}`,paddingLeft:"20px",marginTop:"8px"},children:`"${e.verdict}"`}}),i.push({type:"div",props:{style:{display:"flex",flex:1}}}),i.push(_t(Fp)),{type:"div",props:{style:{display:"flex",flexDirection:"column",width:"100%",height:"100%",backgroundColor:Zx,padding:"64px 72px",fontFamily:"Inter",gap:"28px"},children:i}}}var Zx,In,pr,eC,Fp,Up=C(()=>{"use strict";vn();Zx="#1a1b1e",In="#e7e9ee",pr="#8b9098",eC="#2a2c33",Fp={markStroke:In,wordmarkFg:In}});var Hp={};Z(Hp,{render:()=>rC});function gr(e,t){return{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"4px"},children:[{type:"span",props:{style:{fontSize:"34px",fontWeight:700,color:Mn,fontFamily:"JetBrains Mono"},children:e}},{type:"span",props:{style:{fontSize:"14px",color:fr,fontFamily:"JetBrains Mono",textTransform:"lowercase"},children:`// ${t}`}}]}}}function rC(e){let t=[Et(e.sessionDate),e.durationLabel].filter(Boolean).join(" \xB7 "),n=e.costDollars??"$\u2014",s=typeof e.cachedTokens=="number"&&e.cachedTokens>=1e5?`# ${ht(e.cachedTokens)} tokens cache-replayed (free)`:null,r=[ft(Bp,"recall.cli/share"),{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"6px"},children:[{type:"div",props:{style:{fontSize:"18px",color:Gi,fontFamily:"JetBrains Mono"},children:"$ recall share --session"}},{type:"div",props:{style:{fontSize:"40px",fontWeight:700,color:Mn,lineHeight:1.15,wordBreak:"break-word",fontFamily:"JetBrains Mono"},children:e.sessionTitle}},{type:"div",props:{style:{fontSize:"18px",color:fr,fontFamily:"JetBrains Mono"},children:t}}]}},{type:"div",props:{style:{display:"flex",flexDirection:"column",alignItems:"flex-start",padding:"12px 0"},children:[{type:"span",props:{style:{fontSize:"20px",color:fr,fontFamily:"JetBrains Mono"},children:"> total_cost:"}},{type:"span",props:{style:{fontSize:"180px",fontWeight:700,color:Gi,fontFamily:"JetBrains Mono",lineHeight:1,letterSpacing:"-0.04em"},children:n}}]}},{type:"div",props:{style:{display:"flex",width:"100%",height:"1px",backgroundColor:sC}}},{type:"div",props:{style:{display:"flex",justifyContent:"space-between",gap:"24px"},children:[gr(String(e.messageCount),"msgs"),gr(String(e.filesReferenced),"files"),gr(String(e.toolCallCount),"tools"),gr(e.model??"\u2014","model")]}}];return s&&r.push({type:"div",props:{style:{fontSize:"14px",color:fr,fontFamily:"JetBrains Mono",opacity:.7},children:s}}),e.verdict&&r.push({type:"div",props:{style:{display:"flex",fontSize:"22px",color:Mn,fontFamily:"JetBrains Mono",borderLeft:`3px solid ${Gi}`,paddingLeft:"20px",marginTop:"8px"},children:`// ${e.verdict}`}}),r.push({type:"div",props:{style:{display:"flex",flex:1}}}),r.push(_t(Bp)),{type:"div",props:{style:{display:"flex",flexDirection:"column",width:"100%",height:"100%",backgroundColor:nC,padding:"64px 72px",fontFamily:"Inter",gap:"28px"},children:r}}}var nC,Mn,fr,sC,Gi,Bp,Wp=C(()=>{"use strict";vn();nC="#0d0d0f",Mn="#e1e7ee",fr="#6b7480",sC="#1f2229",Gi="#7ee787",Bp={markStroke:Mn,wordmarkFg:Mn}});var Gp={};Z(Gp,{render:()=>iC});function _r(e,t){return{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"4px"},children:[{type:"span",props:{style:{fontSize:"34px",fontWeight:700,color:It,fontFamily:"JetBrains Mono"},children:e}},{type:"span",props:{style:{fontSize:"14px",color:hr,textTransform:"uppercase",letterSpacing:"0.16em",fontWeight:600},children:t}}]}}}function iC(e){let t=[Et(e.sessionDate),e.durationLabel].filter(Boolean).join(" \xB7 "),n=e.costDollars??"$\u2014",s=typeof e.cachedTokens=="number"&&e.cachedTokens>=1e5?`+ ${ht(e.cachedTokens)} cached`:null,r=[ft(Xp,"session receipt"),{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"8px"},children:[{type:"div",props:{style:{fontSize:"44px",fontWeight:700,color:It,lineHeight:1.15,wordBreak:"break-word"},children:e.sessionTitle}},{type:"div",props:{style:{fontSize:"20px",color:hr},children:t}}]}},{type:"div",props:{style:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",padding:"32px 0"},children:[{type:"span",props:{style:{fontSize:"180px",fontWeight:700,color:It,fontFamily:"JetBrains Mono",lineHeight:1,letterSpacing:"-0.04em"},children:n}},{type:"span",props:{style:{marginTop:"16px",fontSize:"16px",color:hr,textTransform:"uppercase",letterSpacing:"0.24em",fontWeight:600},children:"session cost"}}]}},{type:"div",props:{style:{display:"flex",width:"100%",height:"1px",backgroundColor:oC}}},{type:"div",props:{style:{display:"flex",justifyContent:"space-between",gap:"24px"},children:[_r(String(e.messageCount),"msgs"),_r(String(e.filesReferenced),"files"),_r(String(e.toolCallCount),"tools"),_r(e.model??"\u2014","model")]}}];return s&&r.push({type:"div",props:{style:{fontSize:"14px",color:hr,fontFamily:"JetBrains Mono",opacity:.85},children:s}}),e.verdict&&r.push({type:"div",props:{style:{display:"flex",fontSize:"24px",fontStyle:"italic",color:It,borderLeft:`3px solid ${It}`,paddingLeft:"20px",marginTop:"8px"},children:`"${e.verdict}"`}}),r.push({type:"div",props:{style:{display:"flex",flex:1}}}),r.push(_t(Xp)),{type:"div",props:{style:{display:"flex",flexDirection:"column",width:"100%",height:"100%",backgroundImage:"linear-gradient(135deg, #b1370f 0%, #f97316 55%, #fbbf24 100%)",padding:"64px 72px",fontFamily:"Inter",gap:"28px"},children:r}}}var It,hr,oC,Xp,Jp=C(()=>{"use strict";vn();It="#ffffff",hr="rgba(255,255,255,0.7)",oC="rgba(255,255,255,0.18)",Xp={markStroke:It,wordmarkFg:It}});var Yp={};Z(Yp,{render:()=>uC});function Er(e,t){return{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"4px"},children:[{type:"span",props:{style:{fontSize:"34px",fontWeight:800,color:Qt,fontFamily:"JetBrains Mono"},children:e}},{type:"span",props:{style:{fontSize:"14px",color:Dn,textTransform:"uppercase",letterSpacing:"0.16em",fontWeight:600},children:t}}]}}}function dC(e){let t=e.reduce((n,s)=>n+s.value,0)||1;return{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"8px"},children:[{type:"div",props:{style:{display:"flex",width:"100%",height:"10px",borderRadius:"5px",backgroundColor:lC,overflow:"hidden"},children:e.map(n=>({type:"div",props:{style:{display:"flex",width:`${n.value/t*100}%`,height:"100%",backgroundColor:n.color}}}))}},{type:"div",props:{style:{display:"flex",justifyContent:"space-between",fontSize:"12px",color:Dn,fontWeight:600,textTransform:"uppercase",letterSpacing:"0.14em"},children:e.map(n=>({type:"span",props:{style:{display:"flex"},children:`${n.label} \xB7 ${n.value}`}}))}}]}}}function uC(e){let t=[Et(e.sessionDate),e.durationLabel].filter(Boolean).join(" \xB7 "),n=e.costDollars??"$\u2014",s=typeof e.cachedTokens=="number"&&e.cachedTokens>=1e5?`+ ${ht(e.cachedTokens)} cached`:null,r=[ft(zp,"session receipt"),{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"8px"},children:[{type:"div",props:{style:{fontSize:"40px",fontWeight:700,color:Qt,lineHeight:1.15,wordBreak:"break-word"},children:e.sessionTitle}},{type:"div",props:{style:{fontSize:"18px",color:Dn},children:t}}]}},{type:"div",props:{style:{display:"flex",flexDirection:"column",alignItems:"flex-start",padding:"8px 0"},children:[{type:"span",props:{style:{fontSize:"14px",color:Dn,textTransform:"uppercase",letterSpacing:"0.24em",fontWeight:700},children:"session cost"}},{type:"span",props:{style:{fontSize:"180px",fontWeight:800,color:Qt,fontFamily:"JetBrains Mono",lineHeight:1,letterSpacing:"-0.04em"},children:n}}]}},{type:"div",props:{style:{display:"flex",width:"100%",height:"1px",backgroundColor:cC}}},{type:"div",props:{style:{display:"flex",justifyContent:"space-between",gap:"24px"},children:[Er(String(e.messageCount),"msgs"),Er(String(e.filesReferenced),"files"),Er(String(e.toolCallCount),"tools"),Er(e.model??"\u2014","model")]}},dC([{value:e.messageCount,color:vt,label:"msgs"},{value:e.filesReferenced,color:"#0a0a0a",label:"files"},{value:e.toolCallCount,color:"#5a6068",label:"tools"}])];return s&&r.push({type:"div",props:{style:{fontSize:"14px",color:Dn,fontFamily:"JetBrains Mono",opacity:.85},children:s}}),e.verdict&&r.push({type:"div",props:{style:{display:"flex",fontSize:"22px",fontStyle:"italic",color:Qt,borderLeft:`3px solid ${vt}`,paddingLeft:"20px",marginTop:"8px"},children:`"${e.verdict}"`}}),r.push({type:"div",props:{style:{display:"flex",flex:1}}}),r.push(_t(zp)),{type:"div",props:{style:{display:"flex",flexDirection:"column",width:"100%",height:"100%",backgroundColor:aC,padding:"64px 72px",fontFamily:"Inter",gap:"24px"},children:r}}}var aC,Qt,Dn,cC,lC,zp,qp=C(()=>{"use strict";vn();aC="#f6f7f9",Qt="#0a0a0a",Dn="#5a6068",cC="#e3e6eb",lC="#dde1e7",zp={markStroke:Qt,wordmarkFg:Qt}});var Vp={};Z(Vp,{render:()=>gC});function Ji(e,t){return{type:"div",props:{style:{display:"flex",flexDirection:"column",alignItems:"center",gap:"8px",flex:1},children:[{type:"div",props:{style:{fontSize:"52px",fontWeight:700,color:br},children:t}},{type:"div",props:{style:{fontSize:"16px",color:en,textTransform:"uppercase",letterSpacing:"2px"},children:e}}]}}}function zi(e,t){return{type:"div",props:{style:{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"16px 24px",backgroundColor:Yi,borderRadius:"12px"},children:[{type:"span",props:{style:{fontSize:"18px",color:en},children:e}},{type:"span",props:{style:{fontSize:"20px",fontWeight:700,color:br},children:t}}]}}}function gC(e){let t=Math.round(e.healthScore),n=[{type:"div",props:{style:{display:"flex",flexDirection:"column",alignItems:"center",gap:"8px"},children:[{type:"div",props:{style:{fontSize:"16px",color:en,textTransform:"uppercase",letterSpacing:"6px"},children:"YOUR MONTH IN CODE"}},{type:"div",props:{style:{fontSize:"44px",fontWeight:700,color:br},children:e.month}}]}},{type:"div",props:{style:{display:"flex",flexDirection:"column",alignItems:"center",gap:"12px",backgroundColor:Yi,borderRadius:"16px",padding:"28px",border:`1px solid ${Zt}40`},children:[{type:"div",props:{style:{fontSize:"16px",color:pC,textTransform:"uppercase",letterSpacing:"2px"},children:"You are a"}},{type:"div",props:{style:{fontSize:"36px",fontWeight:700,color:Zt},children:e.archetype}}]}},{type:"div",props:{style:{display:"flex",gap:"16px",width:"100%"},children:[Ji("Recalls",String(e.totalRecalls)),Ji("Tokens piped",e.totalTokensPiped>999999?`${(e.totalTokensPiped/1e6).toFixed(1)}M`:e.totalTokensPiped.toLocaleString()),Ji("Sessions",String(e.sessionsIndexed))]}},{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"8px",width:"100%"},children:[{type:"div",props:{style:{fontSize:"14px",color:en,textTransform:"uppercase",letterSpacing:"2px",marginBottom:"4px"},children:"Highlights"}},zi("Most recalled",e.mostRecalledSession),zi("Biggest recall",`${e.biggestRecallTokens.toLocaleString()} tokens`),zi("Peak hours",e.mostActiveHours)]}},{type:"div",props:{style:{display:"flex",flexDirection:"column",alignItems:"center",gap:"12px"},children:[{type:"div",props:{style:{width:"100px",height:"100px",borderRadius:"50%",border:`6px solid ${Zt}`,display:"flex",alignItems:"center",justifyContent:"center"},children:{type:"span",props:{style:{fontSize:"32px",fontWeight:700,color:Zt},children:`${t}`}}}},{type:"div",props:{style:{fontSize:"14px",color:en,textTransform:"uppercase",letterSpacing:"2px"},children:"Memory health"}}]}}];return e.verdict&&n.push({type:"div",props:{style:{backgroundColor:Yi,borderLeft:`3px solid ${Zt}`,borderRadius:"8px",padding:"20px 24px",fontSize:"20px",color:br,fontStyle:"italic"},children:`"${e.verdict}"`}}),n.push({type:"div",props:{style:{display:"flex",alignItems:"center",justifyContent:"center",gap:"12px",marginTop:"auto"},children:[{type:"span",props:{style:{fontSize:"22px",fontWeight:700,color:Zt},children:"Claude Recall"}},{type:"span",props:{style:{fontSize:"18px",color:en},children:"clauderecall.com"}}]}}),{type:"div",props:{style:{display:"flex",flexDirection:"column",width:"100%",height:"100%",backgroundColor:mC,padding:"72px 64px",fontFamily:"Inter",gap:"36px"},children:n}}}var mC,Yi,br,en,Zt,pC,Kp=C(()=>{"use strict";mC="#0b0c0f",Yi="#15171c",br="#e7e9ee",en="#8b9098",Zt="#f97316",pC="#fb923c"});var Zi,ug,Y,L,re,Ue,mg,pg,gg,fg,ea,ta,bt=C(()=>{"use strict";Zi=[String.raw` ___ _ _ _ _ ___ ___ ___ ___ ___ _ _ _ `,String.raw` / __| | /_\ | | | | \| __| | _ \ __/ __| /_\ | | | | `,String.raw`| (__| |__ / _ \| |_| | |) | _| | / _| (__ / _ \| |__| |__ `,String.raw` \___|____/_/ \_\\___/|___/|___| |_|_\___\___/_/ \_\____|____|`],ug=Zi[0]?.length??64,Y="#f97316",L="#8b9098",re="#10b981",Ue="#f59e0b",mg="#60a5fa",pg="#34d399",gg="#c084fc",fg="CLAUDE RECALL",ea=100,ta=30});import{useEffect as GC,useState as JC}from"react";import{Box as _g,Text as be}from"ink";import{createRequire as zC}from"node:module";import{existsSync as YC}from"node:fs";import{jsx as de,jsxs as Eg}from"react/jsx-runtime";function hg({cols:e}){let[t,n]=JC(KC);GC(()=>{let r=!1,o=oe(),i=0,a=0;if(YC(ee))try{let l=E().prepare(`SELECT
788
918
  (SELECT COUNT(*) FROM sessions) AS sessions,
789
- (SELECT COUNT(*) FROM projects) AS projects`).get();i=l.sessions,a=l.projects}catch{}return s(d=>({...d,daemon:o,sessions:i,projects:a})),Te().then(d=>{r||s(l=>({...l,tier:d.tier}))}).catch(()=>{}),()=>{r=!0}},[]);let n=e>=ap+2;return gp(pp,{flexDirection:"column",children:[n?Jo.map((r,o)=>ie(ge,{color:z,bold:!0,children:r},o)):ie(ge,{color:z,bold:!0,children:up}),ie(zR,{state:t})]})}function zR({state:e}){let t=ie(ge,{color:k,children:" \xB7 "});return gp(pp,{children:[ie(ge,{color:k,children:"v"}),ie(ge,{color:z,children:GR}),t,ie(ge,{color:k,children:"daemon: "}),e.daemon?ie(ge,{color:te,children:`running 127.0.0.1:${e.daemon.port}`}):ie(ge,{color:De,children:"stopped"}),t,ie(ge,{color:k,children:"sessions: "}),ie(ge,{color:z,children:e.sessions}),e.projects>0?ie(ge,{color:k,children:` across ${e.projects} ${e.projects===1?"project":"projects"}`}):null,t,ie(ge,{color:k,children:"license: "}),e.tier==="pro"?ie(ge,{color:te,children:"Pro"}):ie(ge,{color:k,children:"Free"})]})}var JR,GR,YR,fp=N(()=>{"use strict";qe();P();w();de();ut();JR=WR(import.meta.url),GR=JR("../../../package.json").version,YR={daemon:null,sessions:0,projects:0,tier:"free"}});import{Box as et,Text as ae}from"ink";import{jsx as q,jsxs as In}from"react/jsx-runtime";function KR(e,t,s){if(t<=s)return{start:0,end:t};let n=e-Math.floor(s/2);return n<0&&(n=0),n+s>t&&(n=t-s),{start:n,end:n+s}}function pt(e,t){if(t<=0)return"";let s=e.replace(/\s+/g," ").trim();return s.length<=t?s:s.slice(0,Math.max(0,t-1))+"\u2026"}function _p({sessions:e,total:t,selected:s,width:n,height:r,loading:o,dbExists:i,filter:a,sortMode:d,groupByProject:l}){let u=Math.max(20,n-2),g=Math.max(1,r-1-1-2),f=KR(s,e.length,g),h=e.slice(f.start,f.end);if(!i)return In(et,{flexDirection:"column",width:n,height:r,borderStyle:"round",borderColor:k,children:[q(ae,{color:z,bold:!0,children:" Sessions "}),q(et,{paddingX:1,paddingY:1,children:q(ae,{color:k,wrap:"wrap",children:"No sessions indexed yet. Quit (q) and run `recall index` first."})})]});let S=[qR[d],l?"grouped":""].filter(Boolean).join(" \xB7 "),R=o?" Sessions \xB7 loading\u2026 ":a?` Sessions \xB7 ${e.length} of ${t} match "${pt(a,18)}" \xB7 ${S} `:` Sessions \xB7 ${e.length} of ${t} \xB7 ${S} `,T=null;return l&&f.start>0&&(T=e[f.start-1]?.project_name??null),In(et,{flexDirection:"column",width:n,height:r,borderStyle:"round",borderColor:k,children:[q(et,{width:u+2,paddingLeft:1,children:q(ae,{color:z,bold:!0,children:pt(R,u)})}),q(et,{flexDirection:"column",flexGrow:1,paddingX:1,children:h.length===0?q(ae,{color:k,children:a?"no matches.":"no sessions."}):h.flatMap((L,D)=>{let A=f.start+D,$=A===s,U=[];return l&&L.project_name!==T&&(U.push(q(ZR,{project:L.project_name,width:u},`hdr-${L.project_name}-${A}`)),T=L.project_name),U.push(q(VR,{session:L,width:u,isSelected:$,indented:l},L.id)),U})}),q(et,{width:u+2,paddingLeft:1,children:q(ae,{color:k,children:e.length>0?pt(` ${s+1} / ${e.length}${f.end<e.length?" \xB7 scroll for more":""} `,u):""})})]})}function VR({session:e,width:t,isSelected:s,indented:n}){let r=J(e.started_at)||"",o=n?" ":"",i=s?"\u258C ":" ";if(n){let g=e.alias||e.auto_title||e.first_user_message||"(no title)",f=Math.min(14,Math.max(6,Math.floor(t*.25))),h=Math.max(10,t-i.length-o.length-f-1),E=pt(g,h),b=pt(r,f),S=Math.max(1,t-i.length-o.length-E.length-b.length);return In(et,{children:[q(ae,{children:o}),q(ae,{color:s?z:k,bold:s,children:i}),q(ae,{color:s?z:"#fefce8",bold:s,children:E}),q(ae,{children:" ".repeat(S)}),q(ae,{color:k,children:b})]})}let a=e.project_name||"?",d=Math.min(28,Math.floor(t*.7)),l=Math.min(14,t-d-i.length-1),u=pt(a,d),p=pt(r,l),m=Math.max(1,t-i.length-u.length-p.length);return In(et,{children:[q(ae,{color:s?z:k,bold:s,children:i}),q(ae,{color:s?z:"#67e8f9",bold:s,children:u}),q(ae,{children:" ".repeat(m)}),q(ae,{color:k,children:p})]})}function ZR({project:e,width:t}){return q(et,{children:q(ae,{color:"#67e8f9",bold:!0,children:pt(`\u25BE ${e}`,t)})})}var qR,hp=N(()=>{"use strict";v();ut();qR={recent:"recent",longest:"longest",busiest:"busiest project"}});import{useEffect as QR,useState as zo}from"react";function Ep(e,t,s){let n=s?"":"AND is_sidechain = 0";return e.prepare(`SELECT uuid, role, type, timestamp, content_text
919
+ (SELECT COUNT(*) FROM projects) AS projects`).get();i=l.sessions,a=l.projects}catch{}return n(d=>({...d,daemon:o,sessions:i,projects:a})),Ae().then(d=>{r||n(l=>({...l,tier:d.tier}))}).catch(()=>{}),()=>{r=!0}},[]);let s=e>=ug+2;return Eg(_g,{flexDirection:"column",children:[s?Zi.map((r,o)=>de(be,{color:Y,bold:!0,children:r},o)):de(be,{color:Y,bold:!0,children:fg}),de(QC,{state:t})]})}function QC({state:e}){let t=de(be,{color:L,children:" \xB7 "});return Eg(_g,{children:[de(be,{color:L,children:"v"}),de(be,{color:Y,children:VC}),t,de(be,{color:L,children:"daemon: "}),e.daemon?de(be,{color:re,children:`running 127.0.0.1:${e.daemon.port}`}):de(be,{color:Ue,children:"stopped"}),t,de(be,{color:L,children:"sessions: "}),de(be,{color:Y,children:e.sessions}),e.projects>0?de(be,{color:L,children:` across ${e.projects} ${e.projects===1?"project":"projects"}`}):null,t,de(be,{color:L,children:"license: "}),e.tier==="pro"?de(be,{color:re,children:"Pro"}):de(be,{color:L,children:"Free"})]})}var qC,VC,KC,bg=C(()=>{"use strict";Xe();D();R();ge();bt();qC=zC(import.meta.url),VC=qC("../../../package.json").version,KC={daemon:null,sessions:0,projects:0,tier:"free"}});import{Box as ct,Text as ue}from"ink";import{jsx as q,jsxs as yr}from"react/jsx-runtime";function eA(e,t,n){if(t<=n)return{start:0,end:t};let s=e-Math.floor(n/2);return s<0&&(s=0),s+n>t&&(s=t-n),{start:s,end:s+n}}function St(e,t){if(t<=0)return"";let n=e.replace(/\s+/g," ").trim();return n.length<=t?n:n.slice(0,Math.max(0,t-1))+"\u2026"}function Sg({sessions:e,total:t,selected:n,width:s,height:r,loading:o,dbExists:i,filter:a,sortMode:d,groupByProject:l}){let u=Math.max(20,s-2),g=Math.max(1,r-1-1-2),f=eA(n,e.length,g),h=e.slice(f.start,f.end);if(!i)return yr(ct,{flexDirection:"column",width:s,height:r,borderStyle:"round",borderColor:L,children:[q(ue,{color:Y,bold:!0,children:" Sessions "}),q(ct,{paddingX:1,paddingY:1,children:q(ue,{color:L,wrap:"wrap",children:"No sessions indexed yet. Quit (q) and run `recall index` first."})})]});let S=[ZC[d],l?"grouped":""].filter(Boolean).join(" \xB7 "),w=o?" Sessions \xB7 loading\u2026 ":a?` Sessions \xB7 ${e.length} of ${t} match "${St(a,18)}" \xB7 ${S} `:` Sessions \xB7 ${e.length} of ${t} \xB7 ${S} `,y=null;return l&&f.start>0&&(y=e[f.start-1]?.project_name??null),yr(ct,{flexDirection:"column",width:s,height:r,borderStyle:"round",borderColor:L,children:[q(ct,{width:u+2,paddingLeft:1,children:q(ue,{color:Y,bold:!0,children:St(w,u)})}),q(ct,{flexDirection:"column",flexGrow:1,paddingX:1,children:h.length===0?q(ue,{color:L,children:a?"no matches.":"no sessions."}):h.flatMap((T,B)=>{let A=f.start+B,v=A===n,O=[];return l&&T.project_name!==y&&(O.push(q(nA,{project:T.project_name,width:u},`hdr-${T.project_name}-${A}`)),y=T.project_name),O.push(q(tA,{session:T,width:u,isSelected:v,indented:l},T.id)),O})}),q(ct,{width:u+2,paddingLeft:1,children:q(ue,{color:L,children:e.length>0?St(` ${n+1} / ${e.length}${f.end<e.length?" \xB7 scroll for more":""} `,u):""})})]})}function tA({session:e,width:t,isSelected:n,indented:s}){let r=G(e.started_at)||"",o=s?" ":"",i=n?"\u258C ":" ";if(s){let g=e.alias||e.auto_title||e.first_user_message||"(no title)",f=Math.min(14,Math.max(6,Math.floor(t*.25))),h=Math.max(10,t-i.length-o.length-f-1),_=St(g,h),b=St(r,f),S=Math.max(1,t-i.length-o.length-_.length-b.length);return yr(ct,{children:[q(ue,{children:o}),q(ue,{color:n?Y:L,bold:n,children:i}),q(ue,{color:n?Y:"#fefce8",bold:n,children:_}),q(ue,{children:" ".repeat(S)}),q(ue,{color:L,children:b})]})}let a=e.project_name||"?",d=Math.min(28,Math.floor(t*.7)),l=Math.min(14,t-d-i.length-1),u=St(a,d),m=St(r,l),p=Math.max(1,t-i.length-u.length-m.length);return yr(ct,{children:[q(ue,{color:n?Y:L,bold:n,children:i}),q(ue,{color:n?Y:"#67e8f9",bold:n,children:u}),q(ue,{children:" ".repeat(p)}),q(ue,{color:L,children:m})]})}function nA({project:e,width:t}){return q(ct,{children:q(ue,{color:"#67e8f9",bold:!0,children:St(`\u25BE ${e}`,t)})})}var ZC,wg=C(()=>{"use strict";$();bt();ZC={recent:"recent",longest:"longest",busiest:"busiest project"}});import{useEffect as sA,useState as na}from"react";function yg(e,t,n){let s=n?"":"AND is_sidechain = 0";return e.prepare(`SELECT uuid, role, type, timestamp, content_text
790
920
  FROM messages
791
921
  WHERE session_id = @id
792
- ${n}
922
+ ${s}
793
923
  ORDER BY timestamp DESC
794
- LIMIT @limit`).all({id:t,limit:e0})}function bp(e){let[t,s]=zo([]),[n,r]=zo(!1),[o,i]=zo(!1);return QR(()=>{if(!e){s([]),i(!1);return}r(!0);try{let a=_(),d=Ep(a,e,!1),l=!1;d.length===0&&(d=Ep(a,e,!0),l=d.length>0),s([...d].reverse()),i(l)}catch{s([]),i(!1)}finally{r(!1)}},[e]),{messages:t,loading:n,fallbackToSidechain:o}}var e0,Sp=N(()=>{"use strict";w();e0=6});import{Box as $e,Text as Xe}from"ink";import{Fragment as r0,jsx as Z,jsxs as Jt}from"react/jsx-runtime";function t0(e){let t=(e.role??"").toLowerCase();return t==="user"?{label:"user",color:cp}:t==="assistant"?{label:"asst",color:lp}:t==="tool"?{label:"tool",color:dp}:t==="system"?{label:"sys ",color:k}:e.type==="summary"?{label:"sum ",color:k}:{label:"----",color:k}}function s0(e,t){return e?e.replace(/\s+/g," ").trim().slice(0,t+1).replace(/.{1}$/,s=>e.length>t?"\u2026":s):"(empty)"}function yp({session:e,width:t,height:s}){let n=e?.id??null,{messages:r,loading:o,fallbackToSidechain:i}=bp(n),a=Math.max(20,t-2),l=Math.max(4,s-2-2),u=Math.max(2,Math.floor(l/Math.max(1,r.length)));return e?Jt($e,{flexDirection:"column",width:t,height:s,borderStyle:"round",borderColor:k,children:[Jt($e,{paddingX:1,flexDirection:"column",children:[Z($e,{width:a,children:Z(Xe,{color:z,bold:!0,wrap:"truncate-end",children:e.alias||e.auto_title||e.first_user_message||"(no title)"})}),Z($e,{width:a,children:Z(Xe,{color:k,wrap:"truncate-end",children:`${e.id.slice(0,8)} \xB7 ${e.message_count} msgs \xB7 ${J(e.started_at)}`})})]}),Z($e,{flexDirection:"column",paddingX:1,flexGrow:1,children:o?Z(Xe,{color:k,children:"loading messages\u2026"}):r.length===0?Z(Xe,{color:k,children:"no messages indexed for this session."}):Jt(r0,{children:[i?Z(Xe,{color:k,children:"(showing sidechain / subagent messages)"}):null,r.map(p=>Z(n0,{message:p,width:a,perMessageLines:u},p.uuid))]})})]}):Jt($e,{flexDirection:"column",width:t,height:s,borderStyle:"round",borderColor:k,children:[Z($e,{paddingX:1,children:Z(Xe,{color:z,bold:!0,children:"Preview"})}),Z($e,{paddingX:1,paddingY:1,children:Z(Xe,{color:k,children:"Select a session on the left to preview."})})]})}function n0({message:e,width:t,perMessageLines:s}){let n=t0(e),r=Math.max(1,s-1),o=Math.max(20,t-2),i=r*o,a=s0(e.content_text,i);return Jt($e,{flexDirection:"column",marginBottom:0,children:[Jt($e,{children:[Z(Xe,{color:n.color,bold:!0,children:n.label}),Z(Xe,{color:k,children:` ${e.timestamp?J(e.timestamp):""}`})]}),Z($e,{width:t,children:Z(Xe,{wrap:"truncate-end",children:a})})]})}var wp=N(()=>{"use strict";v();Sp();ut()});import{useMemo as o0}from"react";import{Box as Pe,Text as mt}from"ink";import{jsx as fe,jsxs as fs}from"react/jsx-runtime";function i0(e,t){try{return{ok:!0,result:rn(e,{budget:t})}}catch(s){return{ok:!1,error:s instanceof Error?s.message:String(s)}}}function Tp({session:e,width:t,height:s,budget:n}){let r=o0(()=>e?i0(e.id,n):null,[e?.id,n]),o=Math.max(20,t-2),a=Math.max(4,s-2-2);if(!e)return fs(Pe,{flexDirection:"column",width:t,height:s,borderStyle:"round",borderColor:k,children:[fe(Pe,{paddingX:1,children:fe(mt,{color:z,bold:!0,children:"Neighborhood"})}),fe(Pe,{paddingX:1,paddingY:1,children:fe(mt,{color:k,children:"Select a session and press `n` to assemble its neighborhood."})})]});if(!r)return null;if(!r.ok)return fs(Pe,{flexDirection:"column",width:t,height:s,borderStyle:"round",borderColor:De,children:[fe(Pe,{paddingX:1,children:fe(mt,{color:De,bold:!0,children:"Neighborhood error"})}),fe(Pe,{paddingX:1,paddingY:1,children:fe(mt,{color:De,children:r.error})})]});let{result:d}=r,l=d.bundle.replace(/\n+$/,"").split(`
795
- `),u=l.slice(0,Math.max(1,a-1)),p=l.length-u.length;return fs(Pe,{flexDirection:"column",width:t,height:s,borderStyle:"round",borderColor:te,children:[fs(Pe,{paddingX:1,flexDirection:"column",children:[fe(Pe,{width:o,children:fe(mt,{color:te,bold:!0,wrap:"truncate-end",children:"Neighborhood"})}),fe(Pe,{width:o,children:fe(mt,{color:k,wrap:"truncate-end",children:`budget=${n} used=${d.budgetUsed} remaining=${d.budgetRemaining} truncated=${d.truncated.length}`})})]}),fs(Pe,{flexDirection:"column",paddingX:1,flexGrow:1,children:[u.map((m,g)=>fe(mt,{wrap:"truncate-end",children:m||" "},g)),p>0?fe(mt,{color:k,children:`\u2026 ${p} more line${p===1?"":"s"} (resize the terminal or use \`recall neighborhood ${e.id.slice(0,8)}\` for the full bundle)`}):null]})]})}var Rp=N(()=>{"use strict";eo();ut()});import{useEffect as a0,useState as xp}from"react";import{Box as _s,Text as gt}from"ink";import qo from"ink-text-input";import{jsx as Se,jsxs as Ko}from"react/jsx-runtime";function kp({mode:e,query:t,onQueryChange:s,onSearchSubmit:n,onAliasSubmit:r,onTagSubmit:o,aliasInitial:i,toast:a}){let[d,l]=xp(""),[u,p]=xp("");return a0(()=>{e==="alias"&&l(i),e==="tag"&&p("")},[e,i]),a&&e==="normal"?Se(_s,{children:Se(gt,{color:De,children:`\u25B6 ${a}`})}):e==="search"?Ko(_s,{children:[Se(gt,{color:z,bold:!0,children:"/ "}),Se(qo,{value:t,onChange:s,onSubmit:n,placeholder:"filter sessions by project, alias, or opening message..."}),Se(gt,{color:k,children:" esc clear \xB7 enter confirm"})]}):e==="alias"?Ko(_s,{children:[Se(gt,{color:te,bold:!0,children:"alias \u203A "}),Se(qo,{value:d,onChange:l,onSubmit:m=>r(m),placeholder:"terminal-tab name for this session"}),Se(gt,{color:k,children:" esc cancel \xB7 enter save"})]}):e==="tag"?Ko(_s,{children:[Se(gt,{color:te,bold:!0,children:"tag \u203A "}),Se(qo,{value:u,onChange:p,onSubmit:m=>o(m),placeholder:"add a tag to this session"}),Se(gt,{color:k,children:" esc cancel \xB7 enter save"})]}):Se(_s,{children:Se(gt,{color:k,children:"\u2191\u2193 nav \xB7 / filter \xB7 enter view \xB7 a alias \xB7 t tag \xB7 s sort \xB7 g group \xB7 n bundle \xB7 o browser \xB7 ? help \xB7 q quit"})})}var Cp=N(()=>{"use strict";ut()});import{Box as _e,Text as W}from"ink";import{jsx as j,jsxs as ye}from"react/jsx-runtime";function Lp({width:e,height:t}){return ye(_e,{flexDirection:"column",width:e,height:t,borderStyle:"round",borderColor:z,paddingX:2,paddingY:1,children:[ye(_e,{children:[j(W,{color:z,bold:!0,children:"Claude Recall TUI \u2014 Help"}),j(W,{color:k,children:" \xB7 press ? or esc to close"})]}),ye(_e,{marginTop:1,flexDirection:"column",children:[j(W,{color:te,bold:!0,children:"Navigation"}),c0.map(s=>j(Vo,{row:s},s.key))]}),ye(_e,{marginTop:1,flexDirection:"column",children:[j(W,{color:te,bold:!0,children:"Actions on the selected session"}),l0.map(s=>j(Vo,{row:s},s.key))]}),ye(_e,{marginTop:1,flexDirection:"column",children:[j(W,{color:te,bold:!0,children:"Right-pane views"}),d0.map(s=>j(Vo,{row:s},s.key))]}),ye(_e,{marginTop:1,flexDirection:"column",children:[j(W,{color:te,bold:!0,children:"What is Neighborhood?"}),j(W,{color:k,wrap:"wrap",children:"Neighborhood assembles a ranked, token-budgeted bundle of context around the selected session: its parent and child sessions, citations (sessions it referenced), similar sessions (by embedding), and any related bug-pattern matches. Press `n` in the TUI to preview it on the right."})]}),ye(_e,{marginTop:1,flexDirection:"column",children:[j(W,{color:te,bold:!0,children:"Maintenance & diagnostics"}),j(W,{color:k,wrap:"wrap",children:"Two commands cover everything. Quit the TUI with `q` first, then:"}),ye(_e,{marginTop:1,children:[j(W,{color:k,children:" \u2022 "}),j(W,{children:"recall doctor"})]}),j(W,{color:k,wrap:"wrap",children:" Health report: DB size, WAL, FTS fragmentation, integrity, disk free."}),j(W,{color:k,wrap:"wrap",children:" Run this first if anything feels slow or wrong. Add --json for machine output."}),ye(_e,{marginTop:1,children:[j(W,{color:k,children:" \u2022 "}),j(W,{children:"recall optimize"})]}),j(W,{color:k,wrap:"wrap",children:" Maintenance: WAL truncate + FTS5 merge + planner stats. Safe while daemon is running."}),j(W,{color:k,wrap:"wrap",children:" Add --vacuum (with daemon stopped) to also reclaim disk pages from deleted rows."})]}),ye(_e,{marginTop:1,flexDirection:"column",children:[j(W,{color:te,bold:!0,children:"How to pipe a session into Claude"}),j(W,{color:k,wrap:"wrap",children:"Two recipes. Both work in any shell \u2014 exit this TUI with `q` first, then run them."}),ye(_e,{marginTop:1,children:[j(W,{color:k,children:" 1. "}),j(W,{children:"recall context <id> | claude"})]}),j(W,{color:k,wrap:"wrap",children:" Pipes one session's full transcript as markdown into a fresh `claude` chat."}),j(W,{color:k,wrap:"wrap",children:" Use when you want to continue exactly where one session left off."}),ye(_e,{marginTop:1,children:[j(W,{color:k,children:" 2. "}),j(W,{children:"recall neighborhood <id> | claude"})]}),j(W,{color:k,wrap:"wrap",children:" Pipes the bundle (parents + children + citations + similar + bug matches)."}),j(W,{color:k,wrap:"wrap",children:" Use when you want broader context, not just one transcript."})]})]})}function Vo({row:e}){return ye(_e,{children:[j(_e,{width:16,children:j(W,{color:z,children:e.key})}),j(W,{color:k,wrap:"truncate-end",children:e.description})]})}var c0,l0,d0,Np=N(()=>{"use strict";ut();c0=[{key:"\u2191 / k",description:"move selection up"},{key:"\u2193 / j",description:"move selection down"},{key:"PgUp / PgDn",description:"jump 10 sessions"},{key:"/",description:"filter by project, alias, or first user message"},{key:"g",description:"toggle project-grouped view"},{key:"s",description:"cycle sort order: recent \u2192 longest \u2192 most-active"}],l0=[{key:"enter",description:"open the full transcript (`recall show <id>`) and exit the TUI"},{key:"a",description:"set or update the alias for the selected session"},{key:"t",description:"add a tag to the selected session"},{key:"o",description:"open the selected session in your browser (daemon required)"}],d0=[{key:"n",description:"toggle the right pane between Preview and Neighborhood"},{key:"?",description:"open this help screen"},{key:"esc",description:"dismiss filter or this help screen"},{key:"q / Ctrl-C",description:"quit the TUI"}]});import{useEffect as u0,useMemo as p0,useState as Mn}from"react";import{existsSync as m0}from"node:fs";function Ap(e){let[t,s]=Mn([]),[n,r]=Mn(!0),[o,i]=Mn(null),[a,d]=Mn(!0);return u0(()=>{if(!m0(se)){d(!1),r(!1);return}try{let p=_().prepare(`SELECT s.id, p.name AS project_name, s.started_at,
924
+ LIMIT @limit`).all({id:t,limit:rA})}function Tg(e){let[t,n]=na([]),[s,r]=na(!1),[o,i]=na(!1);return sA(()=>{if(!e){n([]),i(!1);return}r(!0);try{let a=E(),d=yg(a,e,!1),l=!1;d.length===0&&(d=yg(a,e,!0),l=d.length>0),n([...d].reverse()),i(l)}catch{n([]),i(!1)}finally{r(!1)}},[e]),{messages:t,loading:s,fallbackToSidechain:o}}var rA,Rg=C(()=>{"use strict";R();rA=6});import{Box as Be,Text as Qe}from"ink";import{Fragment as cA,jsx as se,jsxs as nn}from"react/jsx-runtime";function oA(e){let t=(e.role??"").toLowerCase();return t==="user"?{label:"user",color:mg}:t==="assistant"?{label:"asst",color:pg}:t==="tool"?{label:"tool",color:gg}:t==="system"?{label:"sys ",color:L}:e.type==="summary"?{label:"sum ",color:L}:{label:"----",color:L}}function iA(e,t){return e?e.replace(/\s+/g," ").trim().slice(0,t+1).replace(/.{1}$/,n=>e.length>t?"\u2026":n):"(empty)"}function kg({session:e,width:t,height:n}){let s=e?.id??null,{messages:r,loading:o,fallbackToSidechain:i}=Tg(s),a=Math.max(20,t-2),l=Math.max(4,n-2-2),u=Math.max(2,Math.floor(l/Math.max(1,r.length)));return e?nn(Be,{flexDirection:"column",width:t,height:n,borderStyle:"round",borderColor:L,children:[nn(Be,{paddingX:1,flexDirection:"column",children:[se(Be,{width:a,children:se(Qe,{color:Y,bold:!0,wrap:"truncate-end",children:e.alias||e.auto_title||e.first_user_message||"(no title)"})}),se(Be,{width:a,children:se(Qe,{color:L,wrap:"truncate-end",children:`${e.id.slice(0,8)} \xB7 ${e.message_count} msgs \xB7 ${G(e.started_at)}`})})]}),se(Be,{flexDirection:"column",paddingX:1,flexGrow:1,children:o?se(Qe,{color:L,children:"loading messages\u2026"}):r.length===0?se(Qe,{color:L,children:"no messages indexed for this session."}):nn(cA,{children:[i?se(Qe,{color:L,children:"(showing sidechain / subagent messages)"}):null,r.map(m=>se(aA,{message:m,width:a,perMessageLines:u},m.uuid))]})})]}):nn(Be,{flexDirection:"column",width:t,height:n,borderStyle:"round",borderColor:L,children:[se(Be,{paddingX:1,children:se(Qe,{color:Y,bold:!0,children:"Preview"})}),se(Be,{paddingX:1,paddingY:1,children:se(Qe,{color:L,children:"Select a session on the left to preview."})})]})}function aA({message:e,width:t,perMessageLines:n}){let s=oA(e),r=Math.max(1,n-1),o=Math.max(20,t-2),i=r*o,a=iA(e.content_text,i);return nn(Be,{flexDirection:"column",marginBottom:0,children:[nn(Be,{children:[se(Qe,{color:s.color,bold:!0,children:s.label}),se(Qe,{color:L,children:` ${e.timestamp?G(e.timestamp):""}`})]}),se(Be,{width:t,children:se(Qe,{wrap:"truncate-end",children:a})})]})}var xg=C(()=>{"use strict";$();Rg();bt()});import{useMemo as lA}from"react";import{Box as He,Text as wt}from"ink";import{jsx as Se,jsxs as $n}from"react/jsx-runtime";function dA(e,t){try{return{ok:!0,result:Gs(e,{budget:t})}}catch(n){return{ok:!1,error:n instanceof Error?n.message:String(n)}}}function Cg({session:e,width:t,height:n,budget:s}){let r=lA(()=>e?dA(e.id,s):null,[e?.id,s]),o=Math.max(20,t-2),a=Math.max(4,n-2-2);if(!e)return $n(He,{flexDirection:"column",width:t,height:n,borderStyle:"round",borderColor:L,children:[Se(He,{paddingX:1,children:Se(wt,{color:Y,bold:!0,children:"Neighborhood"})}),Se(He,{paddingX:1,paddingY:1,children:Se(wt,{color:L,children:"Select a session and press `n` to assemble its neighborhood."})})]});if(!r)return null;if(!r.ok)return $n(He,{flexDirection:"column",width:t,height:n,borderStyle:"round",borderColor:Ue,children:[Se(He,{paddingX:1,children:Se(wt,{color:Ue,bold:!0,children:"Neighborhood error"})}),Se(He,{paddingX:1,paddingY:1,children:Se(wt,{color:Ue,children:r.error})})]});let{result:d}=r,l=d.bundle.replace(/\n+$/,"").split(`
925
+ `),u=l.slice(0,Math.max(1,a-1)),m=l.length-u.length;return $n(He,{flexDirection:"column",width:t,height:n,borderStyle:"round",borderColor:re,children:[$n(He,{paddingX:1,flexDirection:"column",children:[Se(He,{width:o,children:Se(wt,{color:re,bold:!0,wrap:"truncate-end",children:"Neighborhood"})}),Se(He,{width:o,children:Se(wt,{color:L,wrap:"truncate-end",children:`budget=${s} used=${d.budgetUsed} remaining=${d.budgetRemaining} truncated=${d.truncated.length}`})})]}),$n(He,{flexDirection:"column",paddingX:1,flexGrow:1,children:[u.map((p,g)=>Se(wt,{wrap:"truncate-end",children:p||" "},g)),m>0?Se(wt,{color:L,children:`\u2026 ${m} more line${m===1?"":"s"} (resize the terminal or use \`recall neighborhood ${e.id.slice(0,8)}\` for the full bundle)`}):null]})]})}var Ag=C(()=>{"use strict";li();bt()});import{useEffect as uA,useState as Lg}from"react";import{Box as Pn,Text as yt}from"ink";import sa from"ink-text-input";import{jsx as ke,jsxs as ra}from"react/jsx-runtime";function Ng({mode:e,query:t,onQueryChange:n,onSearchSubmit:s,onAliasSubmit:r,onTagSubmit:o,aliasInitial:i,toast:a}){let[d,l]=Lg(""),[u,m]=Lg("");return uA(()=>{e==="alias"&&l(i),e==="tag"&&m("")},[e,i]),a&&e==="normal"?ke(Pn,{children:ke(yt,{color:Ue,children:`\u25B6 ${a}`})}):e==="search"?ra(Pn,{children:[ke(yt,{color:Y,bold:!0,children:"/ "}),ke(sa,{value:t,onChange:n,onSubmit:s,placeholder:"filter sessions by project, alias, or opening message..."}),ke(yt,{color:L,children:" esc clear \xB7 enter confirm"})]}):e==="alias"?ra(Pn,{children:[ke(yt,{color:re,bold:!0,children:"alias \u203A "}),ke(sa,{value:d,onChange:l,onSubmit:p=>r(p),placeholder:"terminal-tab name for this session"}),ke(yt,{color:L,children:" esc cancel \xB7 enter save"})]}):e==="tag"?ra(Pn,{children:[ke(yt,{color:re,bold:!0,children:"tag \u203A "}),ke(sa,{value:u,onChange:m,onSubmit:p=>o(p),placeholder:"add a tag to this session"}),ke(yt,{color:L,children:" esc cancel \xB7 enter save"})]}):ke(Pn,{children:ke(yt,{color:L,children:"\u2191\u2193 nav \xB7 / filter \xB7 enter view \xB7 a alias \xB7 t tag \xB7 s sort \xB7 g group \xB7 n bundle \xB7 o browser \xB7 ? help \xB7 q quit"})})}var Og=C(()=>{"use strict";bt()});import{Box as we,Text as W}from"ink";import{jsx as U,jsxs as xe}from"react/jsx-runtime";function vg({width:e,height:t}){return xe(we,{flexDirection:"column",width:e,height:t,borderStyle:"round",borderColor:Y,paddingX:2,paddingY:1,children:[xe(we,{children:[U(W,{color:Y,bold:!0,children:"Claude Recall TUI \u2014 Help"}),U(W,{color:L,children:" \xB7 press ? or esc to close"})]}),xe(we,{marginTop:1,flexDirection:"column",children:[U(W,{color:re,bold:!0,children:"Navigation"}),mA.map(n=>U(oa,{row:n},n.key))]}),xe(we,{marginTop:1,flexDirection:"column",children:[U(W,{color:re,bold:!0,children:"Actions on the selected session"}),pA.map(n=>U(oa,{row:n},n.key))]}),xe(we,{marginTop:1,flexDirection:"column",children:[U(W,{color:re,bold:!0,children:"Right-pane views"}),gA.map(n=>U(oa,{row:n},n.key))]}),xe(we,{marginTop:1,flexDirection:"column",children:[U(W,{color:re,bold:!0,children:"What is Neighborhood?"}),U(W,{color:L,wrap:"wrap",children:"Neighborhood assembles a ranked, token-budgeted bundle of context around the selected session: its parent and child sessions, citations (sessions it referenced), similar sessions (by embedding), and any related bug-pattern matches. Press `n` in the TUI to preview it on the right."})]}),xe(we,{marginTop:1,flexDirection:"column",children:[U(W,{color:re,bold:!0,children:"Maintenance & diagnostics"}),U(W,{color:L,wrap:"wrap",children:"Two commands cover everything. Quit the TUI with `q` first, then:"}),xe(we,{marginTop:1,children:[U(W,{color:L,children:" \u2022 "}),U(W,{children:"recall doctor"})]}),U(W,{color:L,wrap:"wrap",children:" Health report: DB size, WAL, FTS fragmentation, integrity, disk free."}),U(W,{color:L,wrap:"wrap",children:" Run this first if anything feels slow or wrong. Add --json for machine output."}),xe(we,{marginTop:1,children:[U(W,{color:L,children:" \u2022 "}),U(W,{children:"recall optimize"})]}),U(W,{color:L,wrap:"wrap",children:" Maintenance: WAL truncate + FTS5 merge + planner stats. Safe while daemon is running."}),U(W,{color:L,wrap:"wrap",children:" Add --vacuum (with daemon stopped) to also reclaim disk pages from deleted rows."})]}),xe(we,{marginTop:1,flexDirection:"column",children:[U(W,{color:re,bold:!0,children:"How to pipe a session into Claude"}),U(W,{color:L,wrap:"wrap",children:"Two recipes. Both work in any shell \u2014 exit this TUI with `q` first, then run them."}),xe(we,{marginTop:1,children:[U(W,{color:L,children:" 1. "}),U(W,{children:"recall context <id> | claude"})]}),U(W,{color:L,wrap:"wrap",children:" Pipes one session's full transcript as markdown into a fresh `claude` chat."}),U(W,{color:L,wrap:"wrap",children:" Use when you want to continue exactly where one session left off."}),xe(we,{marginTop:1,children:[U(W,{color:L,children:" 2. "}),U(W,{children:"recall neighborhood <id> | claude"})]}),U(W,{color:L,wrap:"wrap",children:" Pipes the bundle (parents + children + citations + similar + bug matches)."}),U(W,{color:L,wrap:"wrap",children:" Use when you want broader context, not just one transcript."})]})]})}function oa({row:e}){return xe(we,{children:[U(we,{width:16,children:U(W,{color:Y,children:e.key})}),U(W,{color:L,wrap:"truncate-end",children:e.description})]})}var mA,pA,gA,Ig=C(()=>{"use strict";bt();mA=[{key:"\u2191 / k",description:"move selection up"},{key:"\u2193 / j",description:"move selection down"},{key:"PgUp / PgDn",description:"jump 10 sessions"},{key:"/",description:"filter by project, alias, or first user message"},{key:"g",description:"toggle project-grouped view"},{key:"s",description:"cycle sort order: recent \u2192 longest \u2192 most-active"}],pA=[{key:"enter",description:"open the full transcript (`recall show <id>`) and exit the TUI"},{key:"a",description:"set or update the alias for the selected session"},{key:"t",description:"add a tag to the selected session"},{key:"o",description:"open the selected session in your browser (daemon required)"}],gA=[{key:"n",description:"toggle the right pane between Preview and Neighborhood"},{key:"?",description:"open this help screen"},{key:"esc",description:"dismiss filter or this help screen"},{key:"q / Ctrl-C",description:"quit the TUI"}]});import{useEffect as fA,useMemo as _A,useState as Tr}from"react";import{existsSync as hA}from"node:fs";function Mg(e){let[t,n]=Tr([]),[s,r]=Tr(!0),[o,i]=Tr(null),[a,d]=Tr(!0);return fA(()=>{if(!hA(ee)){d(!1),r(!1);return}try{let m=E().prepare(`SELECT s.id, p.name AS project_name, s.started_at,
796
926
  s.message_count, s.first_user_message,
797
927
  NULLIF(sa.alias, '') AS alias,
798
928
  s.auto_title
@@ -804,20 +934,20 @@ ${t.message}
804
934
  AND COALESCE(s.auto_title, '') NOT LIKE '[output-index]%'
805
935
  AND COALESCE(s.auto_title, '') NOT LIKE '[skill]%'
806
936
  ORDER BY COALESCE(s.started_at, '') DESC
807
- LIMIT @limit`).all({limit:g0});s(p)}catch(u){i(u instanceof Error?u.message:String(u))}finally{r(!1)}},[]),{sessions:p0(()=>{let u=e.trim().toLowerCase();return u?t.filter(p=>{let m=p.project_name?.toLowerCase()??"",g=p.first_user_message?.toLowerCase()??"";return m.includes(u)||g.includes(u)}):t},[t,e]),total:t.length,loading:n,error:o,dbExists:a}}var g0,Op=N(()=>{"use strict";w();P();g0=200});import{useEffect as Zo,useMemo as f0,useState as Je}from"react";import{Box as ft,Text as Dn,useApp as _0,useInput as h0}from"ink";import{spawn as E0}from"node:child_process";import{platform as $n}from"node:os";import{jsx as ce,jsxs as ei}from"react/jsx-runtime";function Ip(){return{cols:process.stdout.columns??100,rows:process.stdout.rows??30}}function T0(e){let t=$n()==="darwin"?"open":$n()==="win32"?"start":"xdg-open",s=$n()==="win32"?["",e]:[e];E0(t,s,{detached:!0,stdio:"ignore",shell:$n()==="win32"}).unref()}function Mp({onShowSession:e}){let{exit:t}=_0(),s=Ip(),[n,r]=Je(s.cols),[o,i]=Je(s.rows),[a,d]=Je(0),[l,u]=Je(""),[p,m]=Je("normal"),[g,f]=Je(null),[h,E]=Je("preview"),[b,S]=Je("recent"),[R,T]=Je(!1),[L,D]=Je(!1),{sessions:A,total:$,loading:U,error:y,dbExists:B}=Ap(l),M=f0(()=>{if(A.length===0)return A;let H=new Map;if(b==="busiest"||R)for(let O of A)H.set(O.project_name,(H.get(O.project_name)??0)+1);let G=[...A];if(b==="longest"?G.sort((O,Y)=>(Y.message_count??0)-(O.message_count??0)):b==="busiest"&&G.sort((O,Y)=>{let ke=H.get(O.project_name)??0,Ce=H.get(Y.project_name)??0;return Ce!==ke?Ce-ke:(Y.started_at??"").localeCompare(O.started_at??"")}),R){let O=new Map;for(let Ce of G){let Ye=O.get(Ce.project_name);Ye||(Ye=[],O.set(Ce.project_name,Ye)),Ye.push(Ce)}let Y=Array.from(O.keys()).sort((Ce,Ye)=>{if(b==="busiest"){let ci=H.get(Ce)??0,li=H.get(Ye)??0;if(li!==ci)return li-ci}return Ce.localeCompare(Ye)}),ke=[];for(let Ce of Y)for(let Ye of O.get(Ce)??[])ke.push(Ye);return ke}return G},[A,b,R]);Zo(()=>{let H=()=>{let G=Ip();r(G.cols),i(G.rows)};return process.stdout.on("resize",H),()=>{process.stdout.off("resize",H)}},[]),Zo(()=>{if(M.length===0){a!==0&&d(0);return}a>=M.length&&d(M.length-1)},[M.length,a]),Zo(()=>{if(!g)return;let H=setTimeout(()=>f(null),2500);return()=>clearTimeout(H)},[g]),h0((H,G)=>{if(p==="search"){G.escape&&(m("normal"),u(""));return}if(p==="alias"||p==="tag"){G.escape&&m("normal");return}if(L){(H==="?"||G.escape||H==="q")&&D(!1);return}if(H==="q"||G.ctrl&&H==="c"){t();return}if(H==="?"){D(!0);return}if(!(M.length===0&&H!=="/")){if(G.upArrow||H==="k"){d(O=>Math.max(0,O-1));return}if(G.downArrow||H==="j"){d(O=>Math.min(M.length-1,O+1));return}if(G.pageUp){d(O=>Math.max(0,O-10));return}if(G.pageDown){d(O=>Math.min(M.length-1,O+10));return}if(G.return){let O=M[a];O&&(e(O.id),t());return}if(H==="o"){let O=M[a];if(!O)return;let Y=Q();if(!Y){f("start the daemon first (`recall start`)");return}let ke=`http://127.0.0.1:${Y.port}/sessions/${O.id}`;T0(ke),f(`opened ${ke}`);return}if(H==="n"){E(Y=>Y==="neighborhood"?"preview":"neighborhood");let O=M[a];O&&f(h==="neighborhood"?"preview view":`neighborhood for ${O.id.slice(0,8)}`);return}if(H==="/"){m("search");return}if(H==="a"){M[a]&&m("alias");return}if(H==="t"){M[a]&&m("tag");return}if(H==="s"){S(O=>{let Y=Qo.indexOf(O),ke=Qo[(Y+1)%Qo.length];return f(`sort: ${R0[ke]}`),ke});return}if(H==="g"){T(O=>(f(O?"flat view":"grouped by project"),!O));return}}});function V(H){m("normal");let G=M[a];if(!G)return;let O=H.trim();if(!O){f("alias unchanged (empty input)");return}try{Mt(G.id,O),f(`alias set: ${O}`)}catch(Y){f(`alias failed: ${Y instanceof Error?Y.message:"unknown"}`)}}function Ge(H){m("normal");let G=M[a];if(!G)return;let O=H.trim();if(!O){f("tag unchanged (empty input)");return}try{let Y=Ga(G.id,O);f(Y.added?`tag added: ${Y.tag}`:`tag exists: ${Y.tag}`)}catch(Y){f(`tag failed: ${Y instanceof Error?Y.message:"unknown"}`)}}if(n<Go||o<Yo)return ei(ft,{flexDirection:"column",padding:1,children:[ce(Dn,{color:De,children:"Terminal too small."}),ce(Dn,{color:k,children:`Resize to at least ${Go} cols x ${Yo} rows. Current: ${n} x ${o}.`}),ce(Dn,{color:k,children:"Press q to quit."})]});let Fe=Math.max(10,o-w0-vp),_t=Math.max(36,Math.floor(n*.4)),ai=n-_t-1,Fn=M[a]??null;return ei(ft,{flexDirection:"column",width:n,height:o,children:[ce(mp,{cols:n}),ce(ft,{height:1}),y?ce(ft,{paddingX:1,children:ce(Dn,{color:De,children:`Error loading sessions: ${y}`})}):L?ce(ft,{height:Fe,children:ce(Lp,{width:n,height:Fe})}):ei(ft,{flexDirection:"row",height:Fe,children:[ce(_p,{sessions:M,total:$,selected:a,width:_t,height:Fe,loading:U,dbExists:B,filter:l,sortMode:b,groupByProject:R}),ce(ft,{width:1,height:Fe}),h==="neighborhood"?ce(Tp,{session:Fn,width:ai,height:Fe,budget:4e3}):ce(yp,{session:Fn,width:ai,height:Fe})]}),ce(ft,{height:vp,width:n,paddingX:1,children:ce(kp,{mode:p,query:l,onQueryChange:u,onSearchSubmit:()=>m("normal"),onAliasSubmit:V,onTagSubmit:Ge,aliasInitial:Fn?.alias??"",toast:g})})]})}var b0,S0,y0,vp,w0,Qo,R0,Dp=N(()=>{"use strict";fp();hp();wp();Rp();Cp();Np();Op();qe();Et();Cr();ut();b0=4,S0=1,y0=1,vp=1,w0=b0+S0+y0,Qo=["recent","longest","busiest"];R0={recent:"most recent first",longest:"longest sessions first",busiest:"busiest project first"}});import{render as x0}from"ink";import{jsx as k0}from"react/jsx-runtime";async function $p(){let e={showSessionId:null};return await x0(k0(Mp,{onShowSession:n=>{e.showSessionId=n}})).waitUntilExit(),e}var Pp=N(()=>{"use strict";Dp()});var Fp={};he(Fp,{runTui:()=>L0});import{spawn as C0}from"node:child_process";async function L0(){if(!process.stdin.isTTY||!process.stdout.isTTY){console.error("recall tui requires an interactive terminal. Run it directly (no pipes)."),process.exitCode=1;return}let e=await $p();if(!e.showSessionId)return;let t=process.argv[1];t&&await new Promise(s=>{let n=C0(process.execPath,[t,"show",e.showSessionId],{stdio:"inherit"});n.on("exit",()=>s()),n.on("error",()=>s())})}var jp=N(()=>{"use strict";Pp()});function si(e,t={}){let s=t.limit??N0()??75e3,r=e.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get()?.n??0;if(r>s)throw new ti(r,s)}function N0(){let e=process.env.RECALL_KNN_MAX_CORPUS;if(!e)return;let t=Number(e);if(!(!Number.isFinite(t)||t<=0||t>1e7))return Math.floor(t)}var ti,Up=N(()=>{"use strict";ti=class extends Error{name="CorpusTooLargeError";code="CORPUS_TOO_LARGE";rowCount;limit;constructor(t,s){super(`semantic search refused: vec_chunks has ${t} rows (cap ${s}). An unindexed kNN over this corpus can pin the SQLite WAL for hours. Workarounds: (a) scope the search to a smaller project, (b) raise the cap via RECALL_KNN_MAX_CORPUS at your own risk, (c) wait for ANN indexing (tracked in the WAL death-spiral plan).`),this.rowCount=t,this.limit=s}}});import{Worker as A0}from"node:worker_threads";import{join as O0}from"node:path";function I0(){let e=process.env.RECALL_KNN_TIMEOUT_MS;if(!e)return;let t=Number(e);if(!(!Number.isFinite(t)||t<=0||t>36e5))return Math.floor(t)}async function ri(e){let t=e.timeoutMs??I0()??v0,s=(e.workerFactory??M0)();return new Promise((n,r)=>{let o=setTimeout(()=>{s.terminate().catch(()=>{}),r(new ni(t))},t);s.once("message",i=>{clearTimeout(o);let a=i;a&&a.ok===!0&&Array.isArray(a.hits)?n(a.hits):r(new Error(a?.error??"worker returned malformed reply")),s.terminate().catch(()=>{})}),s.once("error",i=>{clearTimeout(o),s.terminate().catch(()=>{}),r(i)}),s.postMessage({query:e.query,precomputedVector:e.precomputedVector,limit:e.limit})})}function M0(){let e=O0(ee(),"dist","daemon","query-worker.js");return new A0(e)}var ni,v0,Bp=N(()=>{"use strict";Ke();ni=class extends Error{constructor(s){super(`semantic kNN exceeded ${s}ms wall-clock budget -- query aborted`);this.timeoutMs=s}timeoutMs;name="KnnTimeoutError";code="KNN_TIMEOUT"},v0=1e4});var Hp={};he(Hp,{findSimilarSessions:()=>$0,vectorSearch:()=>D0});async function D0(e,t=50){return si(_()),ri({query:e,limit:t})}async function $0(e,t=10,s=.65){let n=_();si(n);let r=n.prepare("SELECT rowid FROM chunk_meta WHERE session_id = ? ORDER BY rowid LIMIT 1").get(e);if(!r)return[];let o=n.prepare("SELECT embedding FROM vec_chunks WHERE rowid = ?").get(r.rowid);if(!o)return[];let i=await ri({precomputedVector:o.embedding,limit:t*5}),a=new Map;for(let l of i){if(l.sessionId===e)continue;let u=a.get(l.sessionId);(u===void 0||l.distance<u)&&a.set(l.sessionId,l.distance)}let d=[];for(let[l,u]of a){let p=1-u;p>=s&&d.push({sessionId:l,similarity:p})}return d.sort((l,u)=>u.similarity-l.similarity),d.slice(0,t)}var Wp=N(()=>{"use strict";w();Up();Bp()});import{createRequire as P0}from"module";import{Command as F0}from"commander";w();P();import{basename as Jm}from"node:path";import{createReadStream as cm}from"node:fs";import{createInterface as lm}from"node:readline";function Ss(e){return typeof e=="number"&&Number.isFinite(e)?e:0}function Jn(e){let t=e?.usage;if(!t||typeof t!="object")return null;let s={inputTokens:Ss(t.input_tokens),outputTokens:Ss(t.output_tokens),cacheCreateTokens:Ss(t.cache_creation_input_tokens),cacheReadTokens:Ss(t.cache_read_input_tokens)};return s.inputTokens===0&&s.outputTokens===0&&s.cacheCreateTokens===0&&s.cacheReadTokens===0?null:s}var dm=/\x1B\[[0-9;]*[a-zA-Z]/g;function Wn(e){return e.replace(dm,"")}var Xn=12e3;function bi(e,t){if(e.length<=Xn)return e;let s=e.slice(0,Xn),n=e.length-Xn;return`${s}
937
+ LIMIT @limit`).all({limit:EA});n(m)}catch(u){i(u instanceof Error?u.message:String(u))}finally{r(!1)}},[]),{sessions:_A(()=>{let u=e.trim().toLowerCase();return u?t.filter(m=>{let p=m.project_name?.toLowerCase()??"",g=m.first_user_message?.toLowerCase()??"";return p.includes(u)||g.includes(u)}):t},[t,e]),total:t.length,loading:s,error:o,dbExists:a}}var EA,Dg=C(()=>{"use strict";R();D();EA=200});import{useEffect as ia,useMemo as bA,useState as Ze}from"react";import{Box as Tt,Text as Rr,useApp as SA,useInput as wA}from"ink";import{spawn as yA}from"node:child_process";import{platform as kr}from"node:os";import{jsx as me,jsxs as ca}from"react/jsx-runtime";function Pg(){return{cols:process.stdout.columns??100,rows:process.stdout.rows??30}}function CA(e){let t=kr()==="darwin"?"open":kr()==="win32"?"start":"xdg-open",n=kr()==="win32"?["",e]:[e];yA(t,n,{detached:!0,stdio:"ignore",shell:kr()==="win32"}).unref()}function Fg({onShowSession:e}){let{exit:t}=SA(),n=Pg(),[s,r]=Ze(n.cols),[o,i]=Ze(n.rows),[a,d]=Ze(0),[l,u]=Ze(""),[m,p]=Ze("normal"),[g,f]=Ze(null),[h,_]=Ze("preview"),[b,S]=Ze("recent"),[w,y]=Ze(!1),[T,B]=Ze(!1),{sessions:A,total:v,loading:O,error:k,dbExists:I}=Mg(l),F=bA(()=>{if(A.length===0)return A;let H=new Map;if(b==="busiest"||w)for(let M of A)H.set(M.project_name,(H.get(M.project_name)??0)+1);let J=[...A];if(b==="longest"?J.sort((M,z)=>(z.message_count??0)-(M.message_count??0)):b==="busiest"&&J.sort((M,z)=>{let ve=H.get(M.project_name)??0,Ie=H.get(z.project_name)??0;return Ie!==ve?Ie-ve:(z.started_at??"").localeCompare(M.started_at??"")}),w){let M=new Map;for(let Ie of J){let tt=M.get(Ie.project_name);tt||(tt=[],M.set(Ie.project_name,tt)),tt.push(Ie)}let z=Array.from(M.keys()).sort((Ie,tt)=>{if(b==="busiest"){let _a=H.get(Ie)??0,ha=H.get(tt)??0;if(ha!==_a)return ha-_a}return Ie.localeCompare(tt)}),ve=[];for(let Ie of z)for(let tt of M.get(Ie)??[])ve.push(tt);return ve}return J},[A,b,w]);ia(()=>{let H=()=>{let J=Pg();r(J.cols),i(J.rows)};return process.stdout.on("resize",H),()=>{process.stdout.off("resize",H)}},[]),ia(()=>{if(F.length===0){a!==0&&d(0);return}a>=F.length&&d(F.length-1)},[F.length,a]),ia(()=>{if(!g)return;let H=setTimeout(()=>f(null),2500);return()=>clearTimeout(H)},[g]),wA((H,J)=>{if(m==="search"){J.escape&&(p("normal"),u(""));return}if(m==="alias"||m==="tag"){J.escape&&p("normal");return}if(T){(H==="?"||J.escape||H==="q")&&B(!1);return}if(H==="q"||J.ctrl&&H==="c"){t();return}if(H==="?"){B(!0);return}if(!(F.length===0&&H!=="/")){if(J.upArrow||H==="k"){d(M=>Math.max(0,M-1));return}if(J.downArrow||H==="j"){d(M=>Math.min(F.length-1,M+1));return}if(J.pageUp){d(M=>Math.max(0,M-10));return}if(J.pageDown){d(M=>Math.min(F.length-1,M+10));return}if(J.return){let M=F[a];M&&(e(M.id),t());return}if(H==="o"){let M=F[a];if(!M)return;let z=oe();if(!z){f("start the daemon first (`recall start`)");return}let ve=`http://127.0.0.1:${z.port}/sessions/${M.id}`;CA(ve),f(`opened ${ve}`);return}if(H==="n"){_(z=>z==="neighborhood"?"preview":"neighborhood");let M=F[a];M&&f(h==="neighborhood"?"preview view":`neighborhood for ${M.id.slice(0,8)}`);return}if(H==="/"){p("search");return}if(H==="a"){F[a]&&p("alias");return}if(H==="t"){F[a]&&p("tag");return}if(H==="s"){S(M=>{let z=aa.indexOf(M),ve=aa[(z+1)%aa.length];return f(`sort: ${AA[ve]}`),ve});return}if(H==="g"){y(M=>(f(M?"flat view":"grouped by project"),!M));return}}});function ne(H){p("normal");let J=F[a];if(!J)return;let M=H.trim();if(!M){f("alias unchanged (empty input)");return}try{Ht(J.id,M),f(`alias set: ${M}`)}catch(z){f(`alias failed: ${z instanceof Error?z.message:"unknown"}`)}}function et(H){p("normal");let J=F[a];if(!J)return;let M=H.trim();if(!M){f("tag unchanged (empty input)");return}try{let z=cl(J.id,M);f(z.added?`tag added: ${z.tag}`:`tag exists: ${z.tag}`)}catch(z){f(`tag failed: ${z instanceof Error?z.message:"unknown"}`)}}if(s<ea||o<ta)return ca(Tt,{flexDirection:"column",padding:1,children:[me(Rr,{color:Ue,children:"Terminal too small."}),me(Rr,{color:L,children:`Resize to at least ${ea} cols x ${ta} rows. Current: ${s} x ${o}.`}),me(Rr,{color:L,children:"Press q to quit."})]});let We=Math.max(10,o-xA-$g),Rt=Math.max(36,Math.floor(s*.4)),fa=s-Rt-1,Cr=F[a]??null;return ca(Tt,{flexDirection:"column",width:s,height:o,children:[me(hg,{cols:s}),me(Tt,{height:1}),k?me(Tt,{paddingX:1,children:me(Rr,{color:Ue,children:`Error loading sessions: ${k}`})}):T?me(Tt,{height:We,children:me(vg,{width:s,height:We})}):ca(Tt,{flexDirection:"row",height:We,children:[me(Sg,{sessions:F,total:v,selected:a,width:Rt,height:We,loading:O,dbExists:I,filter:l,sortMode:b,groupByProject:w}),me(Tt,{width:1,height:We}),h==="neighborhood"?me(Cg,{session:Cr,width:fa,height:We,budget:4e3}):me(kg,{session:Cr,width:fa,height:We})]}),me(Tt,{height:$g,width:s,paddingX:1,children:me(Ng,{mode:m,query:l,onQueryChange:u,onSearchSubmit:()=>p("normal"),onAliasSubmit:ne,onTagSubmit:et,aliasInitial:Cr?.alias??"",toast:g})})]})}var TA,RA,kA,$g,xA,aa,AA,jg=C(()=>{"use strict";bg();wg();xg();Ag();Og();Ig();Dg();Xe();Ct();fo();bt();TA=4,RA=1,kA=1,$g=1,xA=TA+RA+kA,aa=["recent","longest","busiest"];AA={recent:"most recent first",longest:"longest sessions first",busiest:"busiest project first"}});import{render as LA}from"ink";import{jsx as NA}from"react/jsx-runtime";async function Ug(){let e={showSessionId:null};return await LA(NA(Fg,{onShowSession:s=>{e.showSessionId=s}})).waitUntilExit(),e}var Bg=C(()=>{"use strict";jg()});var Hg={};Z(Hg,{runTui:()=>vA});import{spawn as OA}from"node:child_process";async function vA(){if(!process.stdin.isTTY||!process.stdout.isTTY){console.error("recall tui requires an interactive terminal. Run it directly (no pipes)."),process.exitCode=1;return}let e=await Ug();if(!e.showSessionId)return;let t=process.argv[1];t&&await new Promise(n=>{let s=OA(process.execPath,[t,"show",e.showSessionId],{stdio:"inherit"});s.on("exit",()=>n()),s.on("error",()=>n())})}var Wg=C(()=>{"use strict";Bg()});function da(e,t={}){let n=t.limit??IA()??75e3,r=e.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get()?.n??0;if(r>n)throw new la(r,n)}function IA(){let e=process.env.RECALL_KNN_MAX_CORPUS;if(!e)return;let t=Number(e);if(!(!Number.isFinite(t)||t<=0||t>1e7))return Math.floor(t)}var la,Xg=C(()=>{"use strict";la=class extends Error{name="CorpusTooLargeError";code="CORPUS_TOO_LARGE";rowCount;limit;constructor(t,n){super(`semantic search refused: vec_chunks has ${t} rows (cap ${n}). An unindexed kNN over this corpus can pin the SQLite WAL for hours. Workarounds: (a) scope the search to a smaller project, (b) raise the cap via RECALL_KNN_MAX_CORPUS at your own risk, (c) wait for ANN indexing (tracked in the WAL death-spiral plan).`),this.rowCount=t,this.limit=n}}});import{Worker as MA}from"node:worker_threads";import{join as DA}from"node:path";function PA(){let e=process.env.RECALL_KNN_TIMEOUT_MS;if(!e)return;let t=Number(e);if(!(!Number.isFinite(t)||t<=0||t>36e5))return Math.floor(t)}async function ma(e){let t=e.timeoutMs??PA()??$A,n=(e.workerFactory??FA)();return new Promise((s,r)=>{let o=setTimeout(()=>{n.terminate().catch(()=>{}),r(new ua(t))},t);n.once("message",i=>{clearTimeout(o);let a=i;a&&a.ok===!0&&Array.isArray(a.hits)?s(a.hits):r(new Error(a?.error??"worker returned malformed reply")),n.terminate().catch(()=>{})}),n.once("error",i=>{clearTimeout(o),n.terminate().catch(()=>{}),r(i)}),n.postMessage({query:e.query,precomputedVector:e.precomputedVector,limit:e.limit})})}function FA(){let e=DA(te(),"dist","daemon","query-worker.js");return new MA(e)}var ua,$A,Gg=C(()=>{"use strict";Ge();ua=class extends Error{constructor(n){super(`semantic kNN exceeded ${n}ms wall-clock budget -- query aborted`);this.timeoutMs=n}timeoutMs;name="KnnTimeoutError";code="KNN_TIMEOUT"},$A=1e4});var Jg={};Z(Jg,{findSimilarSessions:()=>UA,vectorSearch:()=>jA});async function jA(e,t=50){return da(E()),ma({query:e,limit:t})}async function UA(e,t=10,n=.65){let s=E();da(s);let r=s.prepare("SELECT rowid FROM chunk_meta WHERE session_id = ? ORDER BY rowid LIMIT 1").get(e);if(!r)return[];let o=s.prepare("SELECT embedding FROM vec_chunks WHERE rowid = ?").get(r.rowid);if(!o)return[];let i=await ma({precomputedVector:o.embedding,limit:t*5}),a=new Map;for(let l of i){if(l.sessionId===e)continue;let u=a.get(l.sessionId);(u===void 0||l.distance<u)&&a.set(l.sessionId,l.distance)}let d=[];for(let[l,u]of a){let m=1-u;m>=n&&d.push({sessionId:l,similarity:m})}return d.sort((l,u)=>u.similarity-l.similarity),d.slice(0,t)}var zg=C(()=>{"use strict";R();Xg();Gg()});import{createRequire as BA}from"module";import{Command as HA}from"commander";R();D();import{basename as Kf}from"node:path";import{createReadStream as gf}from"node:fs";import{createInterface as ff}from"node:readline";function Bn(e){return typeof e=="number"&&Number.isFinite(e)?e:0}function Mr(e){let t=e?.usage;if(!t||typeof t!="object")return null;let n={inputTokens:Bn(t.input_tokens),outputTokens:Bn(t.output_tokens),cacheCreateTokens:Bn(t.cache_creation_input_tokens),cacheReadTokens:Bn(t.cache_read_input_tokens)};return n.inputTokens===0&&n.outputTokens===0&&n.cacheCreateTokens===0&&n.cacheReadTokens===0?null:n}var _f=/\x1B\[[0-9;]*[a-zA-Z]/g;function vr(e){return e.replace(_f,"")}var Ir=12e3;function Ca(e,t){if(e.length<=Ir)return e;let n=e.slice(0,Ir),s=e.length-Ir;return`${n}
808
938
 
809
- \u27E8\u2026 ${n.toLocaleString()} more chars in ${t}; see raw JSONL for full content \u27E9`}function um(e){try{return JSON.stringify(e,null,2)}catch{return String(e)}}function pm(e){if(typeof e.content=="string")return e.content;if(Array.isArray(e.content)){let t=[];for(let s of e.content)if(s&&typeof s=="object"){let n=s;n.type==="text"&&typeof n.text=="string"?t.push(n.text):n.type==="image"&&t.push("[image]")}return t.join(`
810
- `)}return""}function mm(e){if(!e)return{text:"",toolNames:[]};if(typeof e.content=="string")return{text:Wn(e.content),toolNames:[]};if(!Array.isArray(e.content))return{text:"",toolNames:[]};let t=[],s=[];for(let n of e.content)if(!(!n||typeof n!="object")){if(n.type==="text"&&typeof n.text=="string"){t.push(Wn(n.text));continue}if(n.type==="tool_use"&&typeof n.name=="string"){s.push(n.name);let r=n.input!=null?um(n.input):"",o=bi(r,"tool input");t.push(`\u26A1 **Tool call \xB7 \`${n.name}\`**
939
+ \u27E8\u2026 ${s.toLocaleString()} more chars in ${t}; see raw JSONL for full content \u27E9`}function hf(e){try{return JSON.stringify(e,null,2)}catch{return String(e)}}function Ef(e){if(typeof e.content=="string")return e.content;if(Array.isArray(e.content)){let t=[];for(let n of e.content)if(n&&typeof n=="object"){let s=n;s.type==="text"&&typeof s.text=="string"?t.push(s.text):s.type==="image"&&t.push("[image]")}return t.join(`
940
+ `)}return""}function bf(e){if(!e)return{text:"",toolNames:[]};if(typeof e.content=="string")return{text:vr(e.content),toolNames:[]};if(!Array.isArray(e.content))return{text:"",toolNames:[]};let t=[],n=[];for(let s of e.content)if(!(!s||typeof s!="object")){if(s.type==="text"&&typeof s.text=="string"){t.push(vr(s.text));continue}if(s.type==="tool_use"&&typeof s.name=="string"){n.push(s.name);let r=s.input!=null?hf(s.input):"",o=Ca(r,"tool input");t.push(`\u26A1 **Tool call \xB7 \`${s.name}\`**
811
941
 
812
942
  \`\`\`json
813
943
  ${o}
814
- \`\`\``);continue}if(n.type==="tool_result"){let r=Wn(pm(n));if(r){let o=bi(r,"tool result");t.push(`**Tool result**
944
+ \`\`\``);continue}if(s.type==="tool_result"){let r=vr(Ef(s));if(r){let o=Ca(r,"tool result");t.push(`**Tool result**
815
945
 
816
946
  \`\`\`
817
947
  ${o}
818
- \`\`\``)}else t.push("_(tool result was empty or image-only)_");continue}if(n.type==="image"){t.push("_(image)_");continue}t.push(`_(unknown block: ${n.type})_`)}return{text:t.join(`
948
+ \`\`\``)}else t.push("_(tool result was empty or image-only)_");continue}if(s.type==="image"){t.push("_(image)_");continue}t.push(`_(unknown block: ${s.type})_`)}return{text:t.join(`
819
949
 
820
- `),toolNames:s}}async function*Gn(e){let t=cm(e,{encoding:"utf8"}),s=lm({input:t,crlfDelay:1/0});for await(let n of s){if(!n.trim())continue;let r;try{r=JSON.parse(n)}catch{continue}if(!r.uuid||!r.sessionId)continue;let{text:o,toolNames:i}=mm(r.message);yield{uuid:r.uuid,parentUuid:r.parentUuid??null,sessionId:r.sessionId,type:r.type??"unknown",role:r.message?.role??null,timestamp:r.timestamp??null,isSidechain:r.isSidechain===!0,cwd:r.cwd??null,gitBranch:r.gitBranch??null,version:r.version??null,contentText:o,toolNames:i,raw:n,usage:Jn(r.message),model:r.message?.model??null}}}v();var Si=[{name:"Anthropic API key",regex:/sk-ant-[a-zA-Z0-9_\-]{40,}/g,severity:"high"},{name:"OpenAI API key",regex:/sk-(?:proj-)?[a-zA-Z0-9]{32,}/g,severity:"high"},{name:"AWS access key ID",regex:/AKIA[0-9A-Z]{16}/g,severity:"high"},{name:"GitHub PAT",regex:/gh[pousr]_[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Stripe live/test key",regex:/(?:sk|rk|pk)_(?:live|test)_[a-zA-Z0-9]{24,}/g,severity:"high"},{name:"Slack token",regex:/xox[abprs]-[A-Za-z0-9\-]{10,}/g,severity:"high"},{name:"Google API key",regex:/AIza[0-9A-Za-z_\-]{35}/g,severity:"high"},{name:"Private key block",regex:/-----BEGIN (?:RSA |DSA |EC |OPENSSH |ENCRYPTED )?PRIVATE KEY-----/g,severity:"high"},{name:"Apify token",regex:/apify_api_[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Notion integration",regex:/(?:secret_|ntn_)[A-Za-z0-9]{40,}/g,severity:"high"},{name:"Vercel token",regex:/vercel_[A-Za-z0-9]{24,}/g,severity:"high"},{name:"Supabase service key",regex:/sbp_[A-Za-z0-9]{40,}/g,severity:"high"},{name:"SendGrid key",regex:/SG\.[A-Za-z0-9_\-]{20,}\.[A-Za-z0-9_\-]{20,}/g,severity:"high"},{name:"Mailgun key",regex:/key-[a-f0-9]{32}/g,severity:"high"},{name:"Twilio SID",regex:/AC[a-f0-9]{32}/g,severity:"high"},{name:"Discord bot token",regex:/[MN][A-Za-z\d]{23}\.[\w-]{6}\.[\w-]{27,38}/g,severity:"high"},{name:"npm token",regex:/npm_[A-Za-z0-9]{36}/g,severity:"high"},{name:"HuggingFace token",regex:/hf_[A-Za-z0-9]{30,}/g,severity:"high"},{name:"Replicate token",regex:/r8_[A-Za-z0-9]{32,}/g,severity:"high"},{name:"Figma token",regex:/figd_[A-Za-z0-9_\-]{30,}/g,severity:"high"},{name:"Linear key",regex:/lin_api_[A-Za-z0-9]{30,}/g,severity:"high"},{name:"DigitalOcean token",regex:/dop_v1_[a-f0-9]{64}/g,severity:"high"},{name:"Generic provider token",regex:/\b[a-z][a-z0-9]{2,20}_(?:api|pat|token|sk|pk|key|auth)_(?=[A-Za-z0-9_\-]*\d)[A-Za-z0-9_\-]{20,}\b/g,severity:"high"},{name:"Bearer token",regex:/\b[Bb]earer\s+[A-Za-z0-9_\-\.=]{24,}\b/g,severity:"medium"},{name:"Slack webhook URL",regex:/https:\/\/hooks\.slack\.com\/services\/T[A-Z0-9]+\/B[A-Z0-9]+\/[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Discord webhook URL",regex:/https:\/\/(?:ptb\.|canary\.)?discord(?:app)?\.com\/api\/webhooks\/\d+\/[A-Za-z0-9_\-]{40,}/g,severity:"high"},{name:"Teams webhook URL",regex:/https:\/\/[a-zA-Z0-9.\-]+\.webhook\.office\.com\/webhookb2\/[A-Za-z0-9@_\-\/]{30,}/g,severity:"high"},{name:"Secret near keyword",regex:/\b(?:webhook[_\s\-]?secret|signing[_\s\-]?secret|webhook[_\s\-]?signing[_\s\-]?secret|api[_\s\-]?secret|client[_\s\-]?secret|private[_\s\-]?key|access[_\s\-]?token|auth[_\s\-]?token|api[_\s\-]?key)\b[\s\S]{0,200}?\b(?:[a-fA-F0-9]{32,}|[A-Za-z0-9+/_\-]{20,}(?:\.[A-Za-z0-9+/_\-]{10,}){1,2}|[A-Za-z0-9+/_\-]{40,}={0,2})\b/gi,severity:"high"},{name:"JWT",regex:/eyJ[a-zA-Z0-9_\-]{10,}\.[a-zA-Z0-9_\-]{10,}\.[a-zA-Z0-9_\-]{10,}/g,severity:"medium"},{name:"URL with password",regex:/https?:\/\/[^:\s/@]+:[^@\s]{6,}@[^\s/]+/g,severity:"high"},{name:"Password assignment",regex:/(?<![A-Za-z])(?:password|passwd|pwd|secret|token|api[_\-]?key|access[_\-]?key|auth[_\-]?token|webhook[_\-]?secret|client[_\-]?secret|private[_\-]?key)\b\s*[:=]\s*["']?[A-Za-z0-9_\-+/=]{16,}/gi,severity:"high"}];function yi(e){if(e.length<=8)return e.slice(0,2)+"\u2022".repeat(Math.max(0,e.length-4))+e.slice(-2);let t=e.slice(0,Math.min(6,Math.floor(e.length/3))),s=e.slice(-Math.min(4,Math.floor(e.length/4)));return`${t}${"\u2022".repeat(Math.max(3,e.length-t.length-s.length))}${s}`}function Yt(e){if(!e)return[];let t=new Set,s=[];for(let n of Si){n.regex.lastIndex=0;for(let r of e.matchAll(n.regex)){let o=r[0],i=`${n.name}::${wi(o)}`;t.has(i)||(t.add(i),s.push({pattern:n.name,maskedPreview:yi(o),offset:r.index??0,severity:n.severity}))}}return s}function wi(e){let t=5381;for(let s=0;s<e.length;s++)t=(t<<5)+t+e.charCodeAt(s)|0;return(t>>>0).toString(36)}function we(e){if(!e)return{redacted:e,count:0};let t=e,s=0,n=new Set;for(let r of Si)r.regex.lastIndex=0,t=t.replace(r.regex,o=>{let i=`${r.name}::${wi(o)}`;return n.has(i)||(n.add(i),s+=1),`[REDACTED ${r.name}: ${yi(o)}]`});return{redacted:t,count:s}}function zn(e,t,s){e.prepare("DELETE FROM message_usage WHERE session_id = ?").run(t);let n=e.prepare(`
950
+ `),toolNames:n}}async function*Dr(e){let t=gf(e,{encoding:"utf8"}),n=ff({input:t,crlfDelay:1/0});for await(let s of n){if(!s.trim())continue;let r;try{r=JSON.parse(s)}catch{continue}if(!r.uuid||!r.sessionId)continue;let{text:o,toolNames:i}=bf(r.message);yield{uuid:r.uuid,parentUuid:r.parentUuid??null,sessionId:r.sessionId,type:r.type??"unknown",role:r.message?.role??null,timestamp:r.timestamp??null,isSidechain:r.isSidechain===!0,cwd:r.cwd??null,gitBranch:r.gitBranch??null,version:r.version??null,contentText:o,toolNames:i,raw:s,usage:Mr(r.message),model:r.message?.model??null}}}$();var Aa=[{name:"Anthropic API key",regex:/sk-ant-[a-zA-Z0-9_\-]{40,}/g,severity:"high"},{name:"OpenAI API key",regex:/sk-(?:proj-)?[a-zA-Z0-9]{32,}/g,severity:"high"},{name:"AWS access key ID",regex:/AKIA[0-9A-Z]{16}/g,severity:"high"},{name:"GitHub PAT",regex:/gh[pousr]_[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Stripe live/test key",regex:/(?:sk|rk|pk)_(?:live|test)_[a-zA-Z0-9]{24,}/g,severity:"high"},{name:"Slack token",regex:/xox[abprs]-[A-Za-z0-9\-]{10,}/g,severity:"high"},{name:"Google API key",regex:/AIza[0-9A-Za-z_\-]{35}/g,severity:"high"},{name:"Private key block",regex:/-----BEGIN (?:RSA |DSA |EC |OPENSSH |ENCRYPTED )?PRIVATE KEY-----/g,severity:"high"},{name:"Apify token",regex:/apify_api_[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Notion integration",regex:/(?:secret_|ntn_)[A-Za-z0-9]{40,}/g,severity:"high"},{name:"Vercel token",regex:/vercel_[A-Za-z0-9]{24,}/g,severity:"high"},{name:"Supabase service key",regex:/sbp_[A-Za-z0-9]{40,}/g,severity:"high"},{name:"SendGrid key",regex:/SG\.[A-Za-z0-9_\-]{20,}\.[A-Za-z0-9_\-]{20,}/g,severity:"high"},{name:"Mailgun key",regex:/key-[a-f0-9]{32}/g,severity:"high"},{name:"Twilio SID",regex:/AC[a-f0-9]{32}/g,severity:"high"},{name:"Discord bot token",regex:/[MN][A-Za-z\d]{23}\.[\w-]{6}\.[\w-]{27,38}/g,severity:"high"},{name:"npm token",regex:/npm_[A-Za-z0-9]{36}/g,severity:"high"},{name:"HuggingFace token",regex:/hf_[A-Za-z0-9]{30,}/g,severity:"high"},{name:"Replicate token",regex:/r8_[A-Za-z0-9]{32,}/g,severity:"high"},{name:"Figma token",regex:/figd_[A-Za-z0-9_\-]{30,}/g,severity:"high"},{name:"Linear key",regex:/lin_api_[A-Za-z0-9]{30,}/g,severity:"high"},{name:"DigitalOcean token",regex:/dop_v1_[a-f0-9]{64}/g,severity:"high"},{name:"Generic provider token",regex:/\b[a-z][a-z0-9]{2,20}_(?:api|pat|token|sk|pk|key|auth)_(?=[A-Za-z0-9_\-]*\d)[A-Za-z0-9_\-]{20,}\b/g,severity:"high"},{name:"Bearer token",regex:/\b[Bb]earer\s+[A-Za-z0-9_\-\.=]{24,}\b/g,severity:"medium"},{name:"Slack webhook URL",regex:/https:\/\/hooks\.slack\.com\/services\/T[A-Z0-9]+\/B[A-Z0-9]+\/[A-Za-z0-9]{20,}/g,severity:"high"},{name:"Discord webhook URL",regex:/https:\/\/(?:ptb\.|canary\.)?discord(?:app)?\.com\/api\/webhooks\/\d+\/[A-Za-z0-9_\-]{40,}/g,severity:"high"},{name:"Teams webhook URL",regex:/https:\/\/[a-zA-Z0-9.\-]+\.webhook\.office\.com\/webhookb2\/[A-Za-z0-9@_\-\/]{30,}/g,severity:"high"},{name:"Secret near keyword",regex:/\b(?:webhook[_\s\-]?secret|signing[_\s\-]?secret|webhook[_\s\-]?signing[_\s\-]?secret|api[_\s\-]?secret|client[_\s\-]?secret|private[_\s\-]?key|access[_\s\-]?token|auth[_\s\-]?token|api[_\s\-]?key)\b[\s\S]{0,200}?\b(?:[a-fA-F0-9]{32,}|[A-Za-z0-9+/_\-]{20,}(?:\.[A-Za-z0-9+/_\-]{10,}){1,2}|[A-Za-z0-9+/_\-]{40,}={0,2})\b/gi,severity:"high"},{name:"JWT",regex:/eyJ[a-zA-Z0-9_\-]{10,}\.[a-zA-Z0-9_\-]{10,}\.[a-zA-Z0-9_\-]{10,}/g,severity:"medium"},{name:"URL with password",regex:/https?:\/\/[^:\s/@]+:[^@\s]{6,}@[^\s/]+/g,severity:"high"},{name:"Password assignment",regex:/(?<![A-Za-z])(?:password|passwd|pwd|secret|token|api[_\-]?key|access[_\-]?key|auth[_\-]?token|webhook[_\-]?secret|client[_\-]?secret|private[_\-]?key)\b\s*[:=]\s*["']?[A-Za-z0-9_\-+/=]{16,}/gi,severity:"high"}];function La(e){if(e.length<=8)return e.slice(0,2)+"\u2022".repeat(Math.max(0,e.length-4))+e.slice(-2);let t=e.slice(0,Math.min(6,Math.floor(e.length/3))),n=e.slice(-Math.min(4,Math.floor(e.length/4)));return`${t}${"\u2022".repeat(Math.max(3,e.length-t.length-n.length))}${n}`}function rn(e){if(!e)return[];let t=new Set,n=[];for(let s of Aa){s.regex.lastIndex=0;for(let r of e.matchAll(s.regex)){let o=r[0],i=`${s.name}::${Na(o)}`;t.has(i)||(t.add(i),n.push({pattern:s.name,maskedPreview:La(o),offset:r.index??0,severity:s.severity}))}}return n}function Na(e){let t=5381;for(let n=0;n<e.length;n++)t=(t<<5)+t+e.charCodeAt(n)|0;return(t>>>0).toString(36)}function Ce(e){if(!e)return{redacted:e,count:0};let t=e,n=0,s=new Set;for(let r of Aa)r.regex.lastIndex=0,t=t.replace(r.regex,o=>{let i=`${r.name}::${Na(o)}`;return s.has(i)||(s.add(i),n+=1),`[REDACTED ${r.name}: ${La(o)}]`});return{redacted:t,count:n}}function Pr(e,t,n){e.prepare("DELETE FROM message_usage WHERE session_id = ?").run(t);let s=e.prepare(`
821
951
  INSERT INTO message_usage (
822
952
  message_uuid, session_id, model,
823
953
  input_tokens, output_tokens, cache_create_tokens, cache_read_tokens,
@@ -834,12 +964,12 @@ ${o}
834
964
  cache_create_tokens = excluded.cache_create_tokens,
835
965
  cache_read_tokens = excluded.cache_read_tokens,
836
966
  timestamp = excluded.timestamp
837
- `);for(let r of s)r.usage&&r.role==="assistant"&&n.run({uuid:r.uuid,session_id:t,model:r.model,input:r.usage.inputTokens,output:r.usage.outputTokens,cc:r.usage.cacheCreateTokens,cr:r.usage.cacheReadTokens,ts:r.timestamp})}function zt(e,t){let s=e.prepare(`SELECT
967
+ `);for(let r of n)r.usage&&r.role==="assistant"&&s.run({uuid:r.uuid,session_id:t,model:r.model,input:r.usage.inputTokens,output:r.usage.outputTokens,cc:r.usage.cacheCreateTokens,cr:r.usage.cacheReadTokens,ts:r.timestamp})}function on(e,t){let n=e.prepare(`SELECT
838
968
  COALESCE(SUM(input_tokens), 0) AS input_tokens,
839
969
  COALESCE(SUM(output_tokens), 0) AS output_tokens,
840
970
  COALESCE(SUM(cache_create_tokens), 0) AS cache_create_tokens,
841
971
  COALESCE(SUM(cache_read_tokens), 0) AS cache_read_tokens
842
- FROM message_usage WHERE session_id = ?`).get(t),n=e.prepare(`SELECT model, SUM(output_tokens) AS out
972
+ FROM message_usage WHERE session_id = ?`).get(t),s=e.prepare(`SELECT model, SUM(output_tokens) AS out
843
973
  FROM message_usage
844
974
  WHERE session_id = ? AND model IS NOT NULL
845
975
  GROUP BY model
@@ -849,17 +979,17 @@ ${o}
849
979
  total_cache_create_tokens = @cc,
850
980
  total_cache_read_tokens = @cr,
851
981
  primary_model = @model
852
- WHERE id = @id`).run({id:t,input:s.input_tokens,output:s.output_tokens,cc:s.cache_create_tokens,cr:s.cache_read_tokens,model:n?.model??null})}w();P();import{writeFileSync as Sm,mkdirSync as ym,existsSync as wm}from"node:fs";import{join as xi}from"node:path";w();var _m=[/^You will receive a sample of user messages from a Claude Cod/i,/^You will receive the first \d+ user messages from a Claude/i,/^Base directory for this skill:/i,/^You are Claude Code in a fresh terminal/i,/^You are extracting a structured Output Index from a Claude/i];var Nt=[/^You are summarizing a Claude Code session/i,/^You are extracting a structured Output Index from a Claude/i,/^You will receive a sample of user messages from a Claude Cod/i,/^You will receive the first \d+ user messages from a Claude/i,/^Thread context:\n- This session is part of thread/i];var hm=[/^Score this person'?s relevance/i,/^You are a [^\n.]{1,60}co-pilot/i,/^Return ONLY valid JSON/i,/^You are summarizing a Claude Code session/i],Em=[/^Draft (a|an|marketing) [a-z]/i,/^Read the file at /i,/^Read this and then begin/i,/^read this and then begin/i],bm=20;function Ti(e){if(e.auto_title_source==="agent"&&e.auto_title)return"agent";if(e.has_alias)return"manual_alias";if(e.auto_title&&e.auto_title.includes(" \xB7 "))return"fixed_v0.16.1";if(!e.auto_title||e.auto_title.length<bm)return"low_signal";for(let t of _m)if(t.test(e.auto_title))return"recursive_meta";for(let t of hm)if(t.test(e.auto_title))return"programmatic";for(let t of Em)if(t.test(e.auto_title))return"template_pending";return"clean"}function qn(e){if(!e)return"";let t=e.split("|")[0];return t=t.replace(/\s*\([^)]*\)\s*$/,""),t=t.replace(/\s+/g," ").trim(),t}var Vn=xi(x,"titles"),Tm=80,Rm=60,xm=100;function km(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(s=>!!s&&typeof s=="object"&&typeof s.title=="string"&&typeof s.replaced_at=="string")}catch{}return[]}function Qn(e){if(!e)return null;let t=e;if(t=t.replace(/```[\s\S]*?```/g," "),t=t.replace(/`[^`]+`/g," "),t=t.replace(/https?:\/\/\S+/g,"[url]"),t=t.replace(/\s+/g," ").trim(),!t)return null;let s=Cm(t,e);if(s)return s;let n=t.match(/^[^.!?\n]{8,}?[.!?]/)?.[0]?.trim();return(n&&n.length<=Tm?n:t.slice(0,Rm)).trim()||null}function Cm(e,t){let s=e.match(/^\/([A-Za-z0-9][A-Za-z0-9_-]*)\s+([\s\S]*)$/);if(s){let n=`/${s[1]}`,r=t.replace(/^\s*\/[A-Za-z0-9][A-Za-z0-9_-]*\s+/,""),o=Zn(r);return o?qt(`${n} \xB7 ${o}`):n}for(let n of Lm){if(!e.match(n.match))continue;let o=n.prefix,i=n.extract?n.extract(e,t):Zn(t);return i?n.completeFromExtract?qt(i):qt(`${o} \xB7 ${i}`):o}for(let n of Om){if(!e.match(n.match))continue;let o=n.extract?n.extract(e,t):ys(t);return o?n.completeFromExtract?qt(o):qt(`${n.prefix} \xB7 ${o}`):n.prefix}return null}var Lm=[{match:/^Draft (?:a|an) brand brief\b/i,prefix:"Draft brand brief"},{match:/^Draft (?:a|an) sales brief\b/i,prefix:"Draft sales brief"},{match:/^Draft (?:a|an) leave-behind\b/i,prefix:"Draft leave-behind"},{match:/^Draft (?:a|an) audio identity brief\b/i,prefix:"Draft audio identity brief"},{match:/^Draft marketing ideas\b/i,prefix:"Draft marketing ideas"},{match:/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i,prefix:"Draft",extract:(e,t)=>{let n=e.match(/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i)?.[1]?.trim(),r=Zn(t);return n&&r?`${n} \xB7 ${r}`:n||r}},{match:/^Read the file at /i,prefix:"Read",extract:e=>{let s=e.match(/^Read the file at\s+([^\s]+)/i)?.[1];return s?s.split("/").filter(Boolean).slice(-2).join("/")||s:null}},{match:/^(?:read|inspect) this and then begin\b/i,prefix:"Begin from preamble",completeFromExtract:!0,extract:(e,t)=>Am(t)},{match:/^Base directory for this skill:/i,prefix:"[skill]",completeFromExtract:!0,extract:(e,t)=>{let s=t.match(/\.claude\/skills\/([^/\s]+)/);return s?.[1]?`[skill] ${s[1]}`:null}},{match:/^You are extracting a structured Output Index/i,prefix:"[output-index]",completeFromExtract:!0,extract:(e,t)=>Nm(t)},{match:/^You will receive a sample of user messages from a Claude Code session:/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>Ri(t,"[meta] auto-title (full-arc)")},{match:/^You will receive the first \d+ user messages from a Claude Code session/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>Ri(t,"[meta] auto-title (initial)")},{match:/^You are implementing v\d+\.\d+/i,prefix:"Implementing",completeFromExtract:!0,extract:e=>{let t=e.match(/^You are implementing (v\d+\.\d+\w*)\s*(?:\(([^)]+)\))?/i);if(!t)return null;let s=t[1],n=t[2]?.trim();return n?`Implementing ${s} \xB7 ${n}`:`Implementing ${s}`}}];function Nm(e){if(!e)return"[output-index] extractor";let t=e.match(/^Session:\s*([0-9a-f]{8})\b/im),s=e.match(/^Opening prompt:\s*([^\n]{1,140})/im),n=t?.[1]?.trim(),r=s?.[1]?.trim().replace(/\s+/g," "),o=r&&r.length>70?r.slice(0,67)+"\u2026":r;return n&&o?`[output-index] \xB7 ${n} \xB7 ${o}`:n?`[output-index] \xB7 ${n}`:o?`[output-index] \xB7 ${o}`:"[output-index] extractor"}function Ri(e,t){if(!e)return t;let s=e.indexOf("Messages:");if(s===-1)return t;let n=e.slice(s+9),r=/^\s*\d+\.\s*([^\n]+)/gm;for(let o of n.matchAll(r)){let i=o[1]?.trim()??"";if(!i)continue;let a=i.replace(/^<[^>]+>[\s\S]*?<\/[^>]+>\s*/g,"").replace(/^\[Pasted text[^\]]*\]\s*/i,"").trim();if(!a||/^<local-command-/.test(a))continue;let d=a.length>60?a.slice(0,57)+"\u2026":a;return`${t} \xB7 ${d}`}return t}function Am(e){if(!e)return null;let t=e.match(/([\/\w.\-]+\.(?:md|markdown|txt|json|yaml|yml|toml|ts|tsx|js|jsx|py|sql))(?=[\s,;:)\]"'`]|$)/i);if(!t)return null;let s=t[1].split("/").filter(Boolean);return(s[s.length-1]??"").replace(/\.[^.]+$/,"")||null}var Om=[{match:/^Score this person'?s relevance/i,prefix:"Score relevance",extract:(e,t)=>ys(t)},{match:/^You are a [^\n.]{1,60}co-pilot/i,prefix:"co-pilot",completeFromExtract:!0,extract:(e,t)=>{let n=e.match(/^You are a ([^\n.]{1,60}?)co-pilot/i)?.[1]?.trim(),r=n?`${n} co-pilot`:"co-pilot",o=ys(t);return o?`${r} \xB7 ${o}`:r}},{match:/^Return ONLY valid JSON/i,prefix:"JSON-only",extract:(e,t)=>ys(t)}];function ys(e){let t=/^\s*(Name|Company|Prospect|Author|Brand|Client)\s*:\s*([^\n]+)/gim,s;for(;(s=t.exec(e))!==null;){let n=s[2].trim();if(!n||/^(null|none|n\/a|—|-)$/i.test(n))continue;let r=n.replace(/\s+/g," ");return qn(r)||r}return null}function qt(e){return e.slice(0,xm).trim()}var vm=[/^deep research$/i,/^reference(?: spec)?$/i,/^canonical spec/i,/^product context/i,/^services?(?: overview)?$/i,/^overview$/i,/^(?:introduction|template|instructions|context)$/i],Im=[/^(?:brand|brand brief|brand summary)$/i,/^(?:known facts?|facts)$/i,/^(?:client|prospect|customer|account)$/i,/^(?:topic|subject|brief|task)$/i,/^(?:about|for|re)$/i];function Kn(e){let t=e.trim();return t.length<3?!0:vm.some(s=>s.test(t))}function Mm(e){let t=e.trim().replace(/\s*\([^)]*\)\s*$/,"").trim();return Im.some(s=>s.test(t))}function Zn(e){let t=Dm(e);return t===null?null:qn(t)||t}function Dm(e){if(/^\s*Skill smoke test\b/i.test(e))return"smoke test";let t=/(^|\n)#{1,3}\s+([^\n]+?)\s+(?:—|–|-|:)\s+([^\n]{2,80})/g,s,n=[];for(;(s=t.exec(e))!==null;){let d=s[2].trim(),l=s[3].trim().replace(/\s+/g," ");if(!Kn(l)){if(Mm(d))return l;n.push(l)}}if(n.length>0)return n[0];let r=e.match(/\b(?:Topic|Subject|Brand|About|Client|For|Re)\s*:\s*([^\n]{2,80})/i);if(r?.[1]&&!Kn(r[1]))return r[1].trim().replace(/\s+/g," ");let o=/===\s*([^=\n]{2,120}?)\s*===/g;for(;(s=o.exec(e))!==null;){let d=s[1].trim().replace(/\.(md|txt|json)$/i,""),l=d.replace(/\s*\([^)]*\)\s*$/,"").trim();if(l&&!Kn(l)&&!/product context|reference/i.test(d))return l}let i=e.replace(/^Context for this run[^:]*:\s*/i,"");if(i!==e){let d=i.split(`
982
+ WHERE id = @id`).run({id:t,input:n.input_tokens,output:n.output_tokens,cc:n.cache_create_tokens,cr:n.cache_read_tokens,model:s?.model??null})}R();D();import{writeFileSync as xf,mkdirSync as Cf,existsSync as Af}from"node:fs";import{join as Ia}from"node:path";R();var yf=[/^You will receive a sample of user messages from a Claude Cod/i,/^You will receive the first \d+ user messages from a Claude/i,/^Base directory for this skill:/i,/^You are Claude Code in a fresh terminal/i,/^You are extracting a structured Output Index from a Claude/i];var Pt=[/^You are summarizing a Claude Code session/i,/^You are extracting a structured Output Index from a Claude/i,/^You will receive a sample of user messages from a Claude Cod/i,/^You will receive the first \d+ user messages from a Claude/i,/^Thread context:\n- This session is part of thread/i];var Tf=[/^Score this person'?s relevance/i,/^You are a [^\n.]{1,60}co-pilot/i,/^Return ONLY valid JSON/i,/^You are summarizing a Claude Code session/i],Rf=[/^Draft (a|an|marketing) [a-z]/i,/^Read the file at /i,/^Read this and then begin/i,/^read this and then begin/i],kf=20;function Oa(e){if(e.auto_title_source==="agent"&&e.auto_title)return"agent";if(e.has_alias)return"manual_alias";if(e.auto_title&&e.auto_title.includes(" \xB7 "))return"fixed_v0.16.1";if(!e.auto_title||e.auto_title.length<kf)return"low_signal";for(let t of yf)if(t.test(e.auto_title))return"recursive_meta";for(let t of Tf)if(t.test(e.auto_title))return"programmatic";for(let t of Rf)if(t.test(e.auto_title))return"template_pending";return"clean"}function Fr(e){if(!e)return"";let t=e.split("|")[0];return t=t.replace(/\s*\([^)]*\)\s*$/,""),t=t.replace(/\s+/g," ").trim(),t}var Ur=Ia(x,"titles"),Lf=80,Nf=60,Of=100;function vf(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(n=>!!n&&typeof n=="object"&&typeof n.title=="string"&&typeof n.replaced_at=="string")}catch{}return[]}function Hr(e){if(!e)return null;let t=e;if(t=t.replace(/```[\s\S]*?```/g," "),t=t.replace(/`[^`]+`/g," "),t=t.replace(/https?:\/\/\S+/g,"[url]"),t=t.replace(/\s+/g," ").trim(),!t)return null;let n=If(t,e);if(n)return n;let s=t.match(/^[^.!?\n]{8,}?[.!?]/)?.[0]?.trim();return(s&&s.length<=Lf?s:t.slice(0,Nf)).trim()||null}function If(e,t){let n=e.match(/^\/([A-Za-z0-9][A-Za-z0-9_-]*)\s+([\s\S]*)$/);if(n){let s=`/${n[1]}`,r=t.replace(/^\s*\/[A-Za-z0-9][A-Za-z0-9_-]*\s+/,""),o=Br(r);return o?an(`${s} \xB7 ${o}`):s}for(let s of Mf){if(!e.match(s.match))continue;let o=s.prefix,i=s.extract?s.extract(e,t):Br(t);return i?s.completeFromExtract?an(i):an(`${o} \xB7 ${i}`):o}for(let s of Pf){if(!e.match(s.match))continue;let o=s.extract?s.extract(e,t):Hn(t);return o?s.completeFromExtract?an(o):an(`${s.prefix} \xB7 ${o}`):s.prefix}return null}var Mf=[{match:/^Draft (?:a|an) brand brief\b/i,prefix:"Draft brand brief"},{match:/^Draft (?:a|an) sales brief\b/i,prefix:"Draft sales brief"},{match:/^Draft (?:a|an) leave-behind\b/i,prefix:"Draft leave-behind"},{match:/^Draft (?:a|an) audio identity brief\b/i,prefix:"Draft audio identity brief"},{match:/^Draft marketing ideas\b/i,prefix:"Draft marketing ideas"},{match:/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i,prefix:"Draft",extract:(e,t)=>{let s=e.match(/^Draft (?:a|an) ([a-z]{3,30}(?:\s+[a-z]{3,30}){0,2})\b/i)?.[1]?.trim(),r=Br(t);return s&&r?`${s} \xB7 ${r}`:s||r}},{match:/^Read the file at /i,prefix:"Read",extract:e=>{let n=e.match(/^Read the file at\s+([^\s]+)/i)?.[1];return n?n.split("/").filter(Boolean).slice(-2).join("/")||n:null}},{match:/^(?:read|inspect) this and (?:then )?begin\b/i,prefix:"Begin from preamble",completeFromExtract:!0,extract:(e,t)=>$f(t)},{match:/^Base directory for this skill:/i,prefix:"[skill]",completeFromExtract:!0,extract:(e,t)=>{let n=t.match(/\.claude\/skills\/([^/\s]+)/);return n?.[1]?`[skill] ${n[1]}`:null}},{match:/^You are extracting a structured Output Index/i,prefix:"[output-index]",completeFromExtract:!0,extract:(e,t)=>Df(t)},{match:/^You will receive a sample of user messages from a Claude Code session:/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>va(t,"[meta] auto-title (full-arc)")},{match:/^You will receive the first \d+ user messages from a Claude Code session/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>va(t,"[meta] auto-title (initial)")},{match:/^You are implementing v\d+\.\d+/i,prefix:"Implementing",completeFromExtract:!0,extract:e=>{let t=e.match(/^You are implementing (v\d+\.\d+\w*)\s*(?:\(([^)]+)\))?/i);if(!t)return null;let n=t[1],s=t[2]?.trim();return s?`Implementing ${n} \xB7 ${s}`:`Implementing ${n}`}}];function Df(e){if(!e)return"[output-index] extractor";let t=e.match(/^Session:\s*([0-9a-f]{8})\b/im),n=e.match(/^Opening prompt:\s*([^\n]{1,140})/im),s=t?.[1]?.trim(),r=n?.[1]?.trim().replace(/\s+/g," "),o=r&&r.length>70?r.slice(0,67)+"\u2026":r;return s&&o?`[output-index] \xB7 ${s} \xB7 ${o}`:s?`[output-index] \xB7 ${s}`:o?`[output-index] \xB7 ${o}`:"[output-index] extractor"}function va(e,t){if(!e)return t;let n=e.indexOf("Messages:");if(n===-1)return t;let s=e.slice(n+9),r=/^\s*\d+\.\s*([^\n]+)/gm;for(let o of s.matchAll(r)){let i=o[1]?.trim()??"";if(!i)continue;let a=i.replace(/^<[^>]+>[\s\S]*?<\/[^>]+>\s*/g,"").replace(/^\[Pasted text[^\]]*\]\s*/i,"").trim();if(!a||/^<local-command-/.test(a))continue;let d=a.length>60?a.slice(0,57)+"\u2026":a;return`${t} \xB7 ${d}`}return t}function $f(e){if(!e)return null;let t=e.match(/([\/\w.\-]+\.(?:md|markdown|txt|json|yaml|yml|toml|ts|tsx|js|jsx|py|sql))(?=[\s,;:)\]"'`]|$)/i);if(!t)return null;let n=t[1].split("/").filter(Boolean);return(n[n.length-1]??"").replace(/\.[^.]+$/,"")||null}var Pf=[{match:/^Score this person'?s relevance/i,prefix:"Score relevance",extract:(e,t)=>Hn(t)},{match:/^You are a [^\n.]{1,60}co-pilot/i,prefix:"co-pilot",completeFromExtract:!0,extract:(e,t)=>{let s=e.match(/^You are a ([^\n.]{1,60}?)co-pilot/i)?.[1]?.trim(),r=s?`${s} co-pilot`:"co-pilot",o=Hn(t);return o?`${r} \xB7 ${o}`:r}},{match:/^Return ONLY valid JSON/i,prefix:"JSON-only",extract:(e,t)=>Hn(t)}];function Hn(e){let t=/^\s*(Name|Company|Prospect|Author|Brand|Client)\s*:\s*([^\n]+)/gim,n;for(;(n=t.exec(e))!==null;){let s=n[2].trim();if(!s||/^(null|none|n\/a|—|-)$/i.test(s))continue;let r=s.replace(/\s+/g," ");return Fr(r)||r}return null}function an(e){return e.slice(0,Of).trim()}var Ff=[/^deep research$/i,/^reference(?: spec)?$/i,/^canonical spec/i,/^product context/i,/^services?(?: overview)?$/i,/^overview$/i,/^(?:introduction|template|instructions|context)$/i],jf=[/^(?:brand|brand brief|brand summary)$/i,/^(?:known facts?|facts)$/i,/^(?:client|prospect|customer|account)$/i,/^(?:topic|subject|brief|task)$/i,/^(?:about|for|re)$/i];function jr(e){let t=e.trim();return t.length<3?!0:Ff.some(n=>n.test(t))}function Uf(e){let t=e.trim().replace(/\s*\([^)]*\)\s*$/,"").trim();return jf.some(n=>n.test(t))}function Br(e){let t=Bf(e);return t===null?null:Fr(t)||t}function Bf(e){if(/^\s*Skill smoke test\b/i.test(e))return"smoke test";let t=/(^|\n)#{1,3}\s+([^\n]+?)\s+(?:—|–|-|:)\s+([^\n]{2,80})/g,n,s=[];for(;(n=t.exec(e))!==null;){let d=n[2].trim(),l=n[3].trim().replace(/\s+/g," ");if(!jr(l)){if(Uf(d))return l;s.push(l)}}if(s.length>0)return s[0];let r=e.match(/\b(?:Topic|Subject|Brand|About|Client|For|Re)\s*:\s*([^\n]{2,80})/i);if(r?.[1]&&!jr(r[1]))return r[1].trim().replace(/\s+/g," ");let o=/===\s*([^=\n]{2,120}?)\s*===/g;for(;(n=o.exec(e))!==null;){let d=n[1].trim().replace(/\.(md|txt|json)$/i,""),l=d.replace(/\s*\([^)]*\)\s*$/,"").trim();if(l&&!jr(l)&&!/product context|reference/i.test(d))return l}let i=e.replace(/^Context for this run[^:]*:\s*/i,"");if(i!==e){let d=i.split(`
853
983
  `).map(l=>l.trim()).find(l=>l.length>=4);if(d)return d.slice(0,60)}let a=e.split(`
854
- `).map(d=>d.trim()).find(d=>d.length>=4);return a?a.slice(0,60):null}function er(e,t,s){let n=t.trim();if(!n)return;let r=_(),o=r.prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
855
- FROM sessions WHERE id = ?`).get(e);if(!o||s==="heuristic"&&o.auto_title_source==="agent"&&o.auto_title||o.auto_title===n&&o.auto_title_source===s)return;let i=km(o.auto_title_history),a=new Date().toISOString();o.auto_title&&o.auto_title_source&&i.push({title:o.auto_title,source:o.auto_title_source,replaced_at:a}),r.prepare(`UPDATE sessions
984
+ `).map(d=>d.trim()).find(d=>d.length>=4);return a?a.slice(0,60):null}function Wr(e,t,n){let s=t.trim();if(!s)return;let r=E(),o=r.prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
985
+ FROM sessions WHERE id = ?`).get(e);if(!o||n==="heuristic"&&o.auto_title_source==="agent"&&o.auto_title||o.auto_title===s&&o.auto_title_source===n)return;let i=vf(o.auto_title_history),a=new Date().toISOString();o.auto_title&&o.auto_title_source&&i.push({title:o.auto_title,source:o.auto_title_source,replaced_at:a}),r.prepare(`UPDATE sessions
856
986
  SET auto_title = ?,
857
987
  auto_title_source = ?,
858
988
  auto_title_generated_at = ?,
859
989
  auto_title_history = ?
860
- WHERE id = ?`).run(n,s,Date.now(),JSON.stringify(i),e),Pm(e,n,s,a)}function $m(){F(),wm(Vn)||ym(Vn,{recursive:!0})}function Pm(e,t,s,n){try{$m();let r=xi(Vn,`${e}.txt`),o=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${s} \xB7 updated ${n}
861
- `;Sm(r,o+t+`
862
- `)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}import{existsSync as Fm,mkdirSync as Rx,readFileSync as jm,writeFileSync as xx}from"node:fs";import{homedir as Um}from"node:os";import{join as ki}from"node:path";import{z as tr}from"zod";function Bm(){return process.env.RECALL_HOME??ki(Um(),".recall")}function Hm(){return ki(Bm(),"config.json")}var Wm=tr.object({heuristicEnabled:tr.boolean().default(!0),agentEnabled:tr.boolean().default(!1)}),sr={heuristicEnabled:!0,agentEnabled:!1};function Xm(){let e=Hm();if(!Fm(e))return{};try{return JSON.parse(jm(e,"utf8"))}catch(t){return console.error("[auto-title-config] failed to parse config.json, using defaults:",t),{}}}function nr(){let e=Xm().autoTitle;if(!e)return{...sr};let t=Wm.safeParse({...sr,...e});return t.success?t.data:{...sr}}var Gm=[/^<local-command-caveat>/,/^<command-name>/,/^<command-message>/,/^<system-reminder>/,/^<user-prompt-submit-hook>/,/^Caveat: The messages below were generated/i];function Ym(e){let t=e.trim();return t?Gm.some(s=>s.test(t)):!0}function zm(e){let t=e;return t=t.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim(),t=t.replace(/^<command-[^>]+>[^<]*<\/command-[^>]+>\s*/gm,"").trim(),!t||Ym(t)?null:t}function qm(e,t,s){let n=Hn(t),r=s??n,o=s?Jm(s)||s:mi(t),i=e.prepare("SELECT id, decoded_path FROM projects WHERE encoded_path = ?").get(t);if(i)return s&&i.decoded_path!==s&&e.prepare("UPDATE projects SET decoded_path = ?, name = ? WHERE id = ?").run(s,o,i.id),i.id;let a=e.prepare("INSERT INTO projects (encoded_path, decoded_path, name) VALUES (?, ?, ?)").run(t,r,o);return Number(a.lastInsertRowid)}async function Km(e,t,s){let n=e.prepare("SELECT id, file_mtime FROM sessions WHERE file_path = ? LIMIT 1").get(t.sessionFile);if(!s&&n&&n.file_mtime>=t.mtime)return{inserted:!1,sessionCount:0,messageCount:0};let r=new Map,o=null;for await(let f of Gn(t.sessionFile)){let h=r.get(f.sessionId);if(h||(h={sessionId:f.sessionId,entries:[],earliestTimestamp:null,latestTimestamp:null,firstUserMessage:null,userCount:0,assistantCount:0,cwd:null,gitBranch:null,version:null},r.set(f.sessionId,h)),h.entries.push(f),f.timestamp&&((!h.earliestTimestamp||f.timestamp<h.earliestTimestamp)&&(h.earliestTimestamp=f.timestamp),(!h.latestTimestamp||f.timestamp>h.latestTimestamp)&&(h.latestTimestamp=f.timestamp)),f.role==="user"&&!f.isSidechain){if(h.userCount+=1,!h.firstUserMessage&&f.contentText){let E=zm(f.contentText);E&&(h.firstUserMessage=K(we(E).redacted,200))}}else f.role==="assistant"&&!f.isSidechain&&(h.assistantCount+=1);!h.cwd&&f.cwd&&(h.cwd=f.cwd),!h.gitBranch&&f.gitBranch&&(h.gitBranch=f.gitBranch),!h.version&&f.version&&(h.version=f.version),!o&&f.cwd&&(o=f.cwd)}let i=qm(e,t.encodedProject,o),a=new Date().toISOString(),d=e.prepare(`
990
+ WHERE id = ?`).run(s,n,Date.now(),JSON.stringify(i),e),Wf(e,s,n,a)}function Hf(){j(),Af(Ur)||Cf(Ur,{recursive:!0})}function Wf(e,t,n,s){try{Hf();let r=Ia(Ur,`${e}.txt`),o=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${n} \xB7 updated ${s}
991
+ `;xf(r,o+t+`
992
+ `)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}import{existsSync as Xf,mkdirSync as CL,readFileSync as Gf,writeFileSync as AL}from"node:fs";import{homedir as Jf}from"node:os";import{join as Ma}from"node:path";import{z as Xr}from"zod";function zf(){return process.env.RECALL_HOME??Ma(Jf(),".recall")}function Yf(){return Ma(zf(),"config.json")}var qf=Xr.object({heuristicEnabled:Xr.boolean().default(!0),agentEnabled:Xr.boolean().default(!1)}),Gr={heuristicEnabled:!0,agentEnabled:!1};function Vf(){let e=Yf();if(!Xf(e))return{};try{return JSON.parse(Gf(e,"utf8"))}catch(t){return console.error("[auto-title-config] failed to parse config.json, using defaults:",t),{}}}function Jr(){let e=Vf().autoTitle;if(!e)return{...Gr};let t=qf.safeParse({...Gr,...e});return t.success?t.data:{...Gr}}var Qf=[/^<local-command-caveat>/,/^<command-name>/,/^<command-message>/,/^<system-reminder>/,/^<user-prompt-submit-hook>/,/^Caveat: The messages below were generated/i];function Zf(e){let t=e.trim();return t?Qf.some(n=>n.test(t)):!0}function e_(e){let t=e;return t=t.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim(),t=t.replace(/^<command-[^>]+>[^<]*<\/command-[^>]+>\s*/gm,"").trim(),!t||Zf(t)?null:t}function t_(e,t,n){let s=Nr(t),r=n??s,o=n?Kf(n)||n:wa(t),i=e.prepare("SELECT id, decoded_path FROM projects WHERE encoded_path = ?").get(t);if(i)return n&&i.decoded_path!==n&&e.prepare("UPDATE projects SET decoded_path = ?, name = ? WHERE id = ?").run(n,o,i.id),i.id;let a=e.prepare("INSERT INTO projects (encoded_path, decoded_path, name) VALUES (?, ?, ?)").run(t,r,o);return Number(a.lastInsertRowid)}async function n_(e,t,n){let s=e.prepare("SELECT id, file_mtime FROM sessions WHERE file_path = ? LIMIT 1").get(t.sessionFile);if(!n&&s&&s.file_mtime>=t.mtime)return{inserted:!1,sessionCount:0,messageCount:0};let r=new Map,o=null;for await(let f of Dr(t.sessionFile)){let h=r.get(f.sessionId);if(h||(h={sessionId:f.sessionId,entries:[],earliestTimestamp:null,latestTimestamp:null,firstUserMessage:null,userCount:0,assistantCount:0,cwd:null,gitBranch:null,version:null},r.set(f.sessionId,h)),h.entries.push(f),f.timestamp&&((!h.earliestTimestamp||f.timestamp<h.earliestTimestamp)&&(h.earliestTimestamp=f.timestamp),(!h.latestTimestamp||f.timestamp>h.latestTimestamp)&&(h.latestTimestamp=f.timestamp)),f.role==="user"&&!f.isSidechain){if(h.userCount+=1,!h.firstUserMessage&&f.contentText){let _=e_(f.contentText);_&&(h.firstUserMessage=K(Ce(_).redacted,200))}}else f.role==="assistant"&&!f.isSidechain&&(h.assistantCount+=1);!h.cwd&&f.cwd&&(h.cwd=f.cwd),!h.gitBranch&&f.gitBranch&&(h.gitBranch=f.gitBranch),!h.version&&f.version&&(h.version=f.version),!o&&f.cwd&&(o=f.cwd)}let i=t_(e,t.encodedProject,o),a=new Date().toISOString(),d=e.prepare(`
863
993
  INSERT INTO sessions (
864
994
  id, project_id, file_path, file_mtime,
865
995
  started_at, ended_at, message_count,
@@ -894,30 +1024,30 @@ ${o}
894
1024
  @is_sidechain, @content_text, @tool_names, @raw_json
895
1025
  )
896
1026
  ON CONFLICT(uuid) DO NOTHING
897
- `),u=e.prepare("DELETE FROM messages WHERE session_id = ?"),p=0,m=0;if(e.transaction(()=>{for(let f of r.values()){d.run({id:f.sessionId,project_id:i,file_path:t.sessionFile,file_mtime:t.mtime,started_at:f.earliestTimestamp,ended_at:f.latestTimestamp,message_count:f.entries.length,user_message_count:f.userCount,assistant_message_count:f.assistantCount,first_user_message:f.firstUserMessage,cwd:f.cwd,git_branch:f.gitBranch,version:f.version,indexed_at:a}),u.run(f.sessionId);for(let h of f.entries){let{redacted:E}=we(h.contentText),{redacted:b}=we(h.raw);l.run({uuid:h.uuid,session_id:h.sessionId,parent_uuid:h.parentUuid,type:h.type,role:h.role,timestamp:h.timestamp,is_sidechain:h.isSidechain?1:0,content_text:E,tool_names:h.toolNames.join(","),raw_json:b}),m+=1}zn(e,f.sessionId,f.entries),zt(e,f.sessionId),p+=1}})(),nr().heuristicEnabled)for(let f of r.values()){let h=Qn(f.firstUserMessage);h&&er(f.sessionId,h,"heuristic")}return{inserted:!0,sessionCount:p,messageCount:m}}async function Ci(e){let t=_(),s=gi();console.log(c.dim(`Scanning ${s.length} JSONL session files under ~/.claude/projects/`));let n=0,r=0,o=0,i=0,a=Date.now();for(let l of s)try{let u=await Km(t,l,e.force??!1);u.inserted?(n+=1,o+=u.sessionCount,i+=u.messageCount,e.verbose&&console.log(c.dim(` + ${l.sessionFile.split("/").slice(-2).join("/")} (${u.messageCount} msgs)`))):r+=1}catch(u){console.error(c.err(` ! failed: ${l.sessionFile}`),u)}let d=((Date.now()-a)/1e3).toFixed(1);console.log(""),console.log(`${c.ok("indexed")}: ${c.bold(String(n))} files, ${c.bold(String(o))} sessions, ${c.bold(String(i))} messages ${c.dim(`in ${d}s`)}`),r>0&&console.log(c.dim(`skipped: ${r} unchanged files (use --force to reindex)`))}w();v();import Vm from"cli-table3";function Li(e){let t=_(),n={limit:Math.max(1,Math.min(500,parseInt(e.limit??"30",10)||30))},r="1=1";e.project&&(r+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj OR p.encoded_path LIKE @proj)",n.proj=`%${e.project}%`),e.all||(r+=" AND s.message_count > 2");let o=t.prepare(`SELECT s.id, p.name AS project_name, s.started_at,
1027
+ `),u=e.prepare("DELETE FROM messages WHERE session_id = ?"),m=0,p=0;if(e.transaction(()=>{for(let f of r.values()){d.run({id:f.sessionId,project_id:i,file_path:t.sessionFile,file_mtime:t.mtime,started_at:f.earliestTimestamp,ended_at:f.latestTimestamp,message_count:f.entries.length,user_message_count:f.userCount,assistant_message_count:f.assistantCount,first_user_message:f.firstUserMessage,cwd:f.cwd,git_branch:f.gitBranch,version:f.version,indexed_at:a}),u.run(f.sessionId);for(let h of f.entries){let{redacted:_}=Ce(h.contentText),{redacted:b}=Ce(h.raw);l.run({uuid:h.uuid,session_id:h.sessionId,parent_uuid:h.parentUuid,type:h.type,role:h.role,timestamp:h.timestamp,is_sidechain:h.isSidechain?1:0,content_text:_,tool_names:h.toolNames.join(","),raw_json:b}),p+=1}Pr(e,f.sessionId,f.entries),on(e,f.sessionId),m+=1}})(),Jr().heuristicEnabled)for(let f of r.values()){let h=Hr(f.firstUserMessage);h&&Wr(f.sessionId,h,"heuristic")}return{inserted:!0,sessionCount:m,messageCount:p}}async function Da(e){let t=E(),n=ya();console.log(c.dim(`Scanning ${n.length} JSONL session files under ~/.claude/projects/`));let s=0,r=0,o=0,i=0,a=Date.now();for(let l of n)try{let u=await n_(t,l,e.force??!1);u.inserted?(s+=1,o+=u.sessionCount,i+=u.messageCount,e.verbose&&console.log(c.dim(` + ${l.sessionFile.split("/").slice(-2).join("/")} (${u.messageCount} msgs)`))):r+=1}catch(u){console.error(c.err(` ! failed: ${l.sessionFile}`),u)}let d=((Date.now()-a)/1e3).toFixed(1);console.log(""),console.log(`${c.ok("indexed")}: ${c.bold(String(s))} files, ${c.bold(String(o))} sessions, ${c.bold(String(i))} messages ${c.dim(`in ${d}s`)}`),r>0&&console.log(c.dim(`skipped: ${r} unchanged files (use --force to reindex)`))}R();$();import s_ from"cli-table3";function $a(e){let t=E(),s={limit:Math.max(1,Math.min(500,parseInt(e.limit??"30",10)||30))},r="1=1";e.project&&(r+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj OR p.encoded_path LIKE @proj)",s.proj=`%${e.project}%`),e.all||(r+=" AND s.message_count > 2");let o=t.prepare(`SELECT s.id, p.name AS project_name, s.started_at,
898
1028
  s.message_count, s.first_user_message, s.git_branch
899
1029
  FROM sessions s
900
1030
  JOIN projects p ON p.id = s.project_id
901
1031
  WHERE ${r}
902
1032
  ORDER BY COALESCE(s.started_at, '') DESC
903
- LIMIT @limit`).all(n);if(o.length===0){console.log(c.dim("no sessions found. run `recall index` first."));return}let i=new Vm({head:[c.bold("id"),c.bold("project"),c.bold("when"),c.bold("msgs"),c.bold("opening prompt")],colWidths:[10,22,14,6,70],wordWrap:!0,style:{head:[],border:["grey"]}});for(let a of o)i.push([c.accent(X(a.id)),c.project(K(a.project_name,20)),c.dim(J(a.started_at)),String(a.message_count),K(a.first_user_message,200)]);console.log(i.toString()),console.log(c.dim(`showing ${o.length} session${o.length===1?"":"s"}. use \`recall show <id>\` to view one.`))}w();v();import{spawn as ag}from"node:child_process";import{readFileSync as Zm,writeFileSync as Qm,mkdirSync as eg,chmodSync as tg}from"node:fs";import{join as Ni}from"node:path";import{homedir as Ai}from"node:os";function Oi(){return Ni(Ai(),".recall","config.json")}function vi(){try{return JSON.parse(Zm(Oi(),"utf-8"))}catch{return{}}}function sg(e){let t=Oi();eg(Ni(Ai(),".recall"),{recursive:!0}),Qm(t,JSON.stringify(e,null,2)+`
904
- `,"utf-8"),tg(t,384)}function ws(){let t=vi().verification;return typeof t=="object"&&t!==null&&"enabled"in t?!!t.enabled:!1}function rr(e){let t=vi();t.verification={...typeof t.verification=="object"&&t.verification!==null?t.verification:{},enabled:e},sg(t)}w();var ng=[/\btask\s+complete/i,/\bdone\b/i,/\bfinished\b/i,/\bimplemented\b/i,/\bcompleted\b/i,/\bshipped\b/i,/\ball\s+(?:tests?\s+)?pass/i,/\bsuccessfully\b/i],rg=1440*60*1e3;function og(e){let t=_(),s=t.prepare(`SELECT content_text, tool_names FROM messages
1033
+ LIMIT @limit`).all(s);if(o.length===0){console.log(c.dim("no sessions found. run `recall index` first."));return}let i=new s_({head:[c.bold("id"),c.bold("project"),c.bold("when"),c.bold("msgs"),c.bold("opening prompt")],colWidths:[10,22,14,6,70],wordWrap:!0,style:{head:[],border:["grey"]}});for(let a of o)i.push([c.accent(X(a.id)),c.project(K(a.project_name,20)),c.dim(G(a.started_at)),String(a.message_count),K(a.first_user_message,200)]);console.log(i.toString()),console.log(c.dim(`showing ${o.length} session${o.length===1?"":"s"}. use \`recall show <id>\` to view one.`))}R();$();import{spawn as p_}from"node:child_process";import{readFileSync as r_,writeFileSync as o_,mkdirSync as i_,chmodSync as a_}from"node:fs";import{join as Pa}from"node:path";import{homedir as Fa}from"node:os";function ja(){return Pa(Fa(),".recall","config.json")}function Ua(){try{return JSON.parse(r_(ja(),"utf-8"))}catch{return{}}}function c_(e){let t=ja();i_(Pa(Fa(),".recall"),{recursive:!0}),o_(t,JSON.stringify(e,null,2)+`
1034
+ `,"utf-8"),a_(t,384)}function Wn(){let t=Ua().verification;return typeof t=="object"&&t!==null&&"enabled"in t?!!t.enabled:!1}function zr(e){let t=Ua();t.verification={...typeof t.verification=="object"&&t.verification!==null?t.verification:{},enabled:e},c_(t)}R();var l_=[/\btask\s+complete/i,/\bdone\b/i,/\bfinished\b/i,/\bimplemented\b/i,/\bcompleted\b/i,/\bshipped\b/i,/\ball\s+(?:tests?\s+)?pass/i,/\bsuccessfully\b/i],d_=1440*60*1e3;function u_(e){let t=E(),n=t.prepare(`SELECT content_text, tool_names FROM messages
905
1035
  WHERE session_id = ? AND role = 'assistant'
906
- ORDER BY timestamp DESC LIMIT 5`).all(e),n=!1;for(let d of s)if(d.content_text&&ng.some(l=>l.test(d.content_text))){n=!0;break}let r=t.prepare(`SELECT content_text, tool_names FROM messages
907
- WHERE session_id = ?`).all(e),o={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};for(let d of r){let l=d.tool_names??"",u=d.content_text??"";/\bWrite\b|\bEdit\b/.test(l)&&(o.fileWrites=!0),/\b(?:jest|pytest|vitest|mocha|test|spec)\b/i.test(u)&&/pass|ok|✓/i.test(u)&&(o.testRuns=!0),/\bgit\s+commit\b/i.test(u)&&(o.commits=!0),(/\bbuild\s+(?:succeeded|success|passed)\b/i.test(u)||/tsc.*(?:0 errors|no errors)/i.test(u))&&(o.buildSuccess=!0)}return n?{status:[o.fileWrites,o.testRuns,o.commits,o.buildSuccess].filter(Boolean).length>=2?"verified":"unverified",evidence:o,claimFound:n}:{status:"neutral",evidence:o,claimFound:n}}function ig(e){let t=og(e);return _().prepare("UPDATE sessions SET verification_status = ?, verification_computed_at = ? WHERE id = ?").run(t.status,Date.now(),e),t}function Ii(e){let s=_().prepare("SELECT verification_status, verification_computed_at FROM sessions WHERE id = ?").get(e);if(s?.verification_status&&s.verification_computed_at&&Date.now()-s.verification_computed_at<rg){let n={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};return{status:s.verification_status,evidence:n,claimFound:s.verification_status!=="neutral"}}return ig(e)}function cg(e,t){if(t.length>=32)return e.prepare("SELECT id FROM sessions WHERE id = ?").get(t)?.id??null;let s=e.prepare("SELECT id FROM sessions WHERE id LIKE ? LIMIT 2").all(t+"%");return s.length===1?s[0].id:(s.length===0||console.error(c.err(`ambiguous id prefix "${t}". be more specific.`)),null)}function lg(e,t){let s=_(),n=s.prepare(`SELECT s.id, p.name AS project_name, p.decoded_path,
1036
+ ORDER BY timestamp DESC LIMIT 5`).all(e),s=!1;for(let d of n)if(d.content_text&&l_.some(l=>l.test(d.content_text))){s=!0;break}let r=t.prepare(`SELECT content_text, tool_names FROM messages
1037
+ WHERE session_id = ?`).all(e),o={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};for(let d of r){let l=d.tool_names??"",u=d.content_text??"";/\bWrite\b|\bEdit\b/.test(l)&&(o.fileWrites=!0),/\b(?:jest|pytest|vitest|mocha|test|spec)\b/i.test(u)&&/pass|ok|✓/i.test(u)&&(o.testRuns=!0),/\bgit\s+commit\b/i.test(u)&&(o.commits=!0),(/\bbuild\s+(?:succeeded|success|passed)\b/i.test(u)||/tsc.*(?:0 errors|no errors)/i.test(u))&&(o.buildSuccess=!0)}return s?{status:[o.fileWrites,o.testRuns,o.commits,o.buildSuccess].filter(Boolean).length>=2?"verified":"unverified",evidence:o,claimFound:s}:{status:"neutral",evidence:o,claimFound:s}}function m_(e){let t=u_(e);return E().prepare("UPDATE sessions SET verification_status = ?, verification_computed_at = ? WHERE id = ?").run(t.status,Date.now(),e),t}function Ba(e){let n=E().prepare("SELECT verification_status, verification_computed_at FROM sessions WHERE id = ?").get(e);if(n?.verification_status&&n.verification_computed_at&&Date.now()-n.verification_computed_at<d_){let s={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};return{status:n.verification_status,evidence:s,claimFound:n.verification_status!=="neutral"}}return m_(e)}function g_(e,t){if(t.length>=32)return e.prepare("SELECT id FROM sessions WHERE id = ?").get(t)?.id??null;let n=e.prepare("SELECT id FROM sessions WHERE id LIKE ? LIMIT 2").all(t+"%");return n.length===1?n[0].id:(n.length===0||console.error(c.err(`ambiguous id prefix "${t}". be more specific.`)),null)}function f_(e,t){let n=E(),s=n.prepare(`SELECT s.id, p.name AS project_name, p.decoded_path,
908
1038
  s.started_at, s.ended_at, s.message_count,
909
1039
  s.git_branch, s.version, s.cwd
910
1040
  FROM sessions s
911
1041
  JOIN projects p ON p.id = s.project_id
912
- WHERE s.id = ?`).get(e);if(!n)return null;let r=[],o=c.dim("\u2500".repeat(78));if(r.push(""),r.push(c.bold(c.project(n.project_name))+c.dim(` ${n.decoded_path}`)),r.push(c.dim(`session ${n.id} \xB7 ${n.message_count} msgs \xB7 ${J(n.started_at)}`+(n.git_branch?` \xB7 branch: ${n.git_branch}`:""))),ws()){let d=Ii(e);d.status==="verified"?r.push(c.ok("\u2713 verified")):d.status==="unverified"&&r.push(c.warn("\u26A0 unverified"))}r.push(o);let i=t.limit?Math.max(1,parseInt(t.limit,10)||9999):9999,a=s.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names, raw_json
1042
+ WHERE s.id = ?`).get(e);if(!s)return null;let r=[],o=c.dim("\u2500".repeat(78));if(r.push(""),r.push(c.bold(c.project(s.project_name))+c.dim(` ${s.decoded_path}`)),r.push(c.dim(`session ${s.id} \xB7 ${s.message_count} msgs \xB7 ${G(s.started_at)}`+(s.git_branch?` \xB7 branch: ${s.git_branch}`:""))),Wn()){let d=Ba(e);d.status==="verified"?r.push(c.ok("\u2713 verified")):d.status==="unverified"&&r.push(c.warn("\u26A0 unverified"))}r.push(o);let i=t.limit?Math.max(1,parseInt(t.limit,10)||9999):9999,a=n.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names, raw_json
913
1043
  FROM messages
914
1044
  WHERE session_id = ?
915
1045
  ORDER BY COALESCE(timestamp, ''), rowid
916
- LIMIT ?`).all(e,i);for(let d of a){if(t.raw){r.push(d.raw_json);continue}let l=d.is_sidechain===1?c.dim(" [subagent]"):"",u=d.role==="user"?c.user("\u25B8 user"):d.role==="assistant"?c.assistant("\u25B8 assistant"):c.dim(`\u25B8 ${d.type}`);if(r.push(`${u}${l} ${c.dim(d.timestamp??"")}`),d.tool_names&&d.tool_names.length>0&&r.push(c.tool(` tools: ${d.tool_names}`)),d.content_text&&d.content_text.trim()){let p=d.content_text.trim().split(`
917
- `).map(m=>" "+m).join(`
918
- `);r.push(p)}r.push("")}return r.push(o),r.push(c.dim(`end of session ${n.id}`)),r.join(`
919
- `)}function dg(e,t){if(!t||!process.stdout.isTTY)return!1;let s=process.stdout.rows||24;return e.split(`
920
- `).length>s}function ug(e){let t=ag("less",["-R","-F","-X"],{stdio:["pipe","inherit","inherit"]});t.stdin.on("error",()=>{}),t.stdin.write(e),t.stdin.end(),t.on("exit",()=>process.exit(0))}function Mi(e,t){let s=_(),n=cg(s,e);if(!n){e.length>=32&&console.error(c.err(`session not found: ${e}`)),process.exitCode=1;return}let r=lg(n,t);if(r===null){console.error(c.err(`session metadata missing for ${n}`)),process.exitCode=1;return}let o=t.pager!==!1;dg(r,o)?ug(r):console.log(r)}w();v();de();P();import{existsSync as Hg,readFileSync as Wg}from"node:fs";import{join as Xg}from"node:path";gr();function Yi(e){let t=Gi(),s={...e};return t&&(s["x-recall-token"]=t),s}async function ze(e,t){let s=t?.headers??{};return fetch(e,{...t,headers:Yi(s)})}async function st(e,t,s,n){let r=s!==void 0?{"content-type":"application/json",...n}:{...n};return fetch(t,{method:e,headers:Yi(r),body:s!==void 0?JSON.stringify(s):void 0})}function zi(e){let t=e.split(/\s+/).filter(Boolean).map(s=>s.replace(/"/g,"")).filter(s=>s.length>0);return t.length===0?"":t.map(s=>`"${s}"`).join(" ")}async function Jg(e,t){let s=Xg(x,"daemon.port");Hg(s)||(console.error(c.err("Semantic search requires the daemon to be running.")),console.error(c.dim(" Start it with: recall start")),console.error(c.dim(' Then re-run: recall search "your query" --semantic')),process.exit(1));let n;try{n=Wg(s,"utf8").trim(),/^\d+$/.test(n)||(console.error(c.err(`Daemon port file is corrupt at ${s}.`)),console.error(c.dim(" Restart the daemon: recall stop && recall start")),process.exit(1))}catch(p){console.error(c.err(`Could not read daemon port: ${p instanceof Error?p.message:String(p)}`)),process.exit(1)}let r=Math.max(1,Math.min(200,parseInt(t.limit??"20",10)||20)),o=zi(e);if(!o){console.log(c.dim("empty query"));return}let i=new URLSearchParams({q:o,mode:"semantic",limit:String(r)});t.project&&i.set("project",t.project);let a;try{a=await ze(`http://127.0.0.1:${n}/api/search?${i.toString()}`)}catch(p){console.error(c.err("Could not reach the daemon for semantic search.")),console.error(c.dim(` ${p instanceof Error?p.message:String(p)}`)),console.error(c.dim(" Is the daemon still running? Try: recall status")),process.exit(1)}if(!a.ok){console.error(c.err(`Semantic search failed: HTTP ${a.status}`));try{let p=await a.text();p&&console.error(c.dim(` ${p.slice(0,500)}`))}catch{}process.exit(1)}let d=await a.json(),l=d.hits??[];if(l.length===0){console.log(c.dim(`no matches for "${e}"`));return}let u=d.fusion==="rrf"?"semantic (BM25 + summary + vector, RRF-fused)":"semantic";console.log(c.dim(`${l.length} match${l.length===1?"":"es"} for "${e}" ${c.dim("\xB7 mode:")} ${u}`)),console.log("");for(let p of l){let m=(p.snippet??"").replace(/<<([\s\S]*?)>>/g,(E,b)=>Yn(b,b).replace(/\n/g," ")).replace(/\n/g," "),g=p.role==="user"?c.user("user"):p.role==="assistant"?c.assistant("asst"):c.dim("----"),f=p.lanes&&p.lanes.length>0?c.dim(`[${p.lanes.join("+")}]`):p.matched_via?c.dim(`[${p.matched_via}]`):"",h=p.project??"(unknown)";console.log(`${c.accent(X(p.session_id))} ${c.project(K(h,20))} ${c.dim(J(p.started_at))} ${g} ${f}`),console.log(` ${K(m,200)}`),console.log("")}console.log(c.dim("`recall show <id>` to read any session."))}async function qi(e,t){if(await Ne("Full-text search"),t.semantic){await Jg(e,t);return}let s=_(),n=zi(e);if(!n){console.log(c.dim("empty query"));return}let r=Math.max(1,Math.min(200,parseInt(t.limit??"20",10)||20)),o={q:n,limit:r},i="";t.project&&(i=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj) ",o.proj=`%${t.project}%`);let a=s.prepare(`SELECT m.session_id AS session_id,
1046
+ LIMIT ?`).all(e,i);for(let d of a){if(t.raw){r.push(d.raw_json);continue}let l=d.is_sidechain===1?c.dim(" [subagent]"):"",u=d.role==="user"?c.user("\u25B8 user"):d.role==="assistant"?c.assistant("\u25B8 assistant"):c.dim(`\u25B8 ${d.type}`);if(r.push(`${u}${l} ${c.dim(d.timestamp??"")}`),d.tool_names&&d.tool_names.length>0&&r.push(c.tool(` tools: ${d.tool_names}`)),d.content_text&&d.content_text.trim()){let m=d.content_text.trim().split(`
1047
+ `).map(p=>" "+p).join(`
1048
+ `);r.push(m)}r.push("")}return r.push(o),r.push(c.dim(`end of session ${s.id}`)),r.join(`
1049
+ `)}function __(e,t){if(!t||!process.stdout.isTTY)return!1;let n=process.stdout.rows||24;return e.split(`
1050
+ `).length>n}function h_(e){let t=p_("less",["-R","-F","-X"],{stdio:["pipe","inherit","inherit"]});t.stdin.on("error",()=>{}),t.stdin.write(e),t.stdin.end(),t.on("exit",()=>process.exit(0))}function Ha(e,t){let n=E(),s=g_(n,e);if(!s){e.length>=32&&console.error(c.err(`session not found: ${e}`)),process.exitCode=1;return}let r=f_(s,t);if(r===null){console.error(c.err(`session metadata missing for ${s}`)),process.exitCode=1;return}let o=t.pager!==!1;__(r,o)?h_(r):console.log(r)}R();$();ge();D();import{existsSync as Q_,readFileSync as Z_}from"node:fs";import{join as eh}from"node:path";ro();function tc(e){let t=ec(),n={...e};return t&&(n["x-recall-token"]=t),n}async function nt(e,t){let n=t?.headers??{};return fetch(e,{...t,headers:tc(n)})}async function dt(e,t,n,s){let r=n!==void 0?{"content-type":"application/json",...s}:{...s};return fetch(t,{method:e,headers:tc(r),body:n!==void 0?JSON.stringify(n):void 0})}function nc(e){let t=e.split(/\s+/).filter(Boolean).map(n=>n.replace(/"/g,"")).filter(n=>n.length>0);return t.length===0?"":t.map(n=>`"${n}"`).join(" ")}async function th(e,t){let n=eh(x,"daemon.port");Q_(n)||(console.error(c.err("Semantic search requires the daemon to be running.")),console.error(c.dim(" Start it with: recall start")),console.error(c.dim(' Then re-run: recall search "your query" --semantic')),process.exit(1));let s;try{s=Z_(n,"utf8").trim(),/^\d+$/.test(s)||(console.error(c.err(`Daemon port file is corrupt at ${n}.`)),console.error(c.dim(" Restart the daemon: recall stop && recall start")),process.exit(1))}catch(m){console.error(c.err(`Could not read daemon port: ${m instanceof Error?m.message:String(m)}`)),process.exit(1)}let r=Math.max(1,Math.min(200,parseInt(t.limit??"20",10)||20)),o=nc(e);if(!o){console.log(c.dim("empty query"));return}let i=new URLSearchParams({q:o,mode:"semantic",limit:String(r)});t.project&&i.set("project",t.project);let a;try{a=await nt(`http://127.0.0.1:${s}/api/search?${i.toString()}`)}catch(m){console.error(c.err("Could not reach the daemon for semantic search.")),console.error(c.dim(` ${m instanceof Error?m.message:String(m)}`)),console.error(c.dim(" Is the daemon still running? Try: recall status")),process.exit(1)}if(!a.ok){console.error(c.err(`Semantic search failed: HTTP ${a.status}`));try{let m=await a.text();m&&console.error(c.dim(` ${m.slice(0,500)}`))}catch{}process.exit(1)}let d=await a.json(),l=d.hits??[];if(l.length===0){console.log(c.dim(`no matches for "${e}"`));return}let u=d.fusion==="rrf"?"semantic (BM25 + summary + vector, RRF-fused)":"semantic";console.log(c.dim(`${l.length} match${l.length===1?"":"es"} for "${e}" ${c.dim("\xB7 mode:")} ${u}`)),console.log("");for(let m of l){let p=(m.snippet??"").replace(/<<([\s\S]*?)>>/g,(_,b)=>$r(b,b).replace(/\n/g," ")).replace(/\n/g," "),g=m.role==="user"?c.user("user"):m.role==="assistant"?c.assistant("asst"):c.dim("----"),f=m.lanes&&m.lanes.length>0?c.dim(`[${m.lanes.join("+")}]`):m.matched_via?c.dim(`[${m.matched_via}]`):"",h=m.project??"(unknown)";console.log(`${c.accent(X(m.session_id))} ${c.project(K(h,20))} ${c.dim(G(m.started_at))} ${g} ${f}`),console.log(` ${K(p,200)}`),console.log("")}console.log(c.dim("`recall show <id>` to read any session."))}async function sc(e,t){if(await pe("Full-text search"),t.semantic){await th(e,t);return}let n=E(),s=nc(e);if(!s){console.log(c.dim("empty query"));return}let r=Math.max(1,Math.min(200,parseInt(t.limit??"20",10)||20)),o={q:s,limit:r},i="";t.project&&(i=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj) ",o.proj=`%${t.project}%`);let a=n.prepare(`SELECT m.session_id AS session_id,
921
1051
  p.name AS project_name,
922
1052
  s.started_at AS started_at,
923
1053
  snippet(messages_fts, 0, '<<', '>>', '\u2026', 16) AS snippet,
@@ -930,13 +1060,13 @@ ${o}
930
1060
  WHERE messages_fts MATCH @q
931
1061
  ${i}
932
1062
  ORDER BY bm25(messages_fts)
933
- LIMIT @limit`).all(o);if(a.length===0){console.log(c.dim(`no matches for "${e}"`));return}console.log(c.dim(`${a.length} match${a.length===1?"":"es"} for "${e}"`)),console.log("");for(let d of a){let l=d.snippet.replace(/<<([\s\S]*?)>>/g,(p,m)=>Yn(m,m).replace(/\n/g," ")).replace(/\n/g," "),u=d.role==="user"?c.user("user"):d.role==="assistant"?c.assistant("asst"):c.dim("----");console.log(`${c.accent(X(d.session_id))} ${c.project(K(d.project_name,20))} ${c.dim(J(d.started_at))} ${u}`),console.log(` ${K(l,200)}`),console.log("")}console.log(c.dim("`recall show <id>` to read any session."))}w();P();qe();v();import{statSync as Kg,existsSync as Vg}from"node:fs";function Vi(){if(console.log(""),console.log(c.bold("Claude Recall status")),console.log(c.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),!Vg(se)){console.log(c.dim(" no database yet. run `recall index`."));return}let t=_().prepare(`SELECT
1063
+ LIMIT @limit`).all(o);if(a.length===0){console.log(c.dim(`no matches for "${e}"`));return}console.log(c.dim(`${a.length} match${a.length===1?"":"es"} for "${e}"`)),console.log("");for(let d of a){let l=d.snippet.replace(/<<([\s\S]*?)>>/g,(m,p)=>$r(p,p).replace(/\n/g," ")).replace(/\n/g," "),u=d.role==="user"?c.user("user"):d.role==="assistant"?c.assistant("asst"):c.dim("----");console.log(`${c.accent(X(d.session_id))} ${c.project(K(d.project_name,20))} ${c.dim(G(d.started_at))} ${u}`),console.log(` ${K(l,200)}`),console.log("")}console.log(c.dim("`recall show <id>` to read any session."))}R();D();Xe();$();import{statSync as gh,existsSync as fh}from"node:fs";function uc(){if(console.log(""),console.log(c.bold("Claude Recall status")),console.log(c.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),!fh(ee)){console.log(c.dim(" no database yet. run `recall index`."));return}let t=E().prepare(`SELECT
934
1064
  (SELECT COUNT(*) FROM projects) AS projects,
935
1065
  (SELECT COUNT(*) FROM sessions) AS sessions,
936
1066
  (SELECT COUNT(*) FROM messages) AS messages,
937
1067
  (SELECT MIN(started_at) FROM sessions WHERE started_at IS NOT NULL) AS earliest,
938
1068
  (SELECT MAX(started_at) FROM sessions WHERE started_at IS NOT NULL) AS latest,
939
- (SELECT MAX(indexed_at) FROM sessions) AS last_indexed`).get(),n=(Kg(se).size/1024/1024).toFixed(1);console.log(` db path ${c.dim(se)}`),console.log(` db size ${c.accent(n+" MB")}`),console.log(` projects ${c.accent(String(t.projects))}`),console.log(` sessions ${c.accent(String(t.sessions))}`),console.log(` messages ${c.accent(String(t.messages))}`),console.log(` earliest ${c.dim(t.earliest??"n/a")}`),console.log(` latest ${c.dim(t.latest??"n/a")} ${c.dim(J(t.latest))}`),console.log(` last index ${c.dim(t.last_indexed??"never")}`),console.log("");let r=Q();r?(console.log(` daemon ${c.ok("running")} pid ${r.pid} \xB7 http://127.0.0.1:${r.port}`),console.log(` started ${c.dim(r.startedAt)} ${c.dim(J(r.startedAt))}`)):console.log(` daemon ${c.dim("stopped")} (run \`recall start\` or \`recall open\`)`),console.log("")}w();v();import Zg from"cli-table3";function Zi(){let t=_().prepare(`SELECT p.name,
1069
+ (SELECT MAX(indexed_at) FROM sessions) AS last_indexed`).get(),s=(gh(ee).size/1024/1024).toFixed(1);console.log(` db path ${c.dim(ee)}`),console.log(` db size ${c.accent(s+" MB")}`),console.log(` projects ${c.accent(String(t.projects))}`),console.log(` sessions ${c.accent(String(t.sessions))}`),console.log(` messages ${c.accent(String(t.messages))}`),console.log(` earliest ${c.dim(t.earliest??"n/a")}`),console.log(` latest ${c.dim(t.latest??"n/a")} ${c.dim(G(t.latest))}`),console.log(` last index ${c.dim(t.last_indexed??"never")}`),console.log("");let r=oe();r?(console.log(` daemon ${c.ok("running")} pid ${r.pid} \xB7 http://127.0.0.1:${r.port}`),console.log(` started ${c.dim(r.startedAt)} ${c.dim(G(r.startedAt))}`)):console.log(` daemon ${c.dim("stopped")} (run \`recall start\` or \`recall open\`)`),console.log("")}R();$();import _h from"cli-table3";function mc(){let t=E().prepare(`SELECT p.name,
940
1070
  p.decoded_path,
941
1071
  COUNT(s.id) AS session_count,
942
1072
  COALESCE(SUM(s.message_count), 0) AS message_count,
@@ -944,21 +1074,22 @@ ${o}
944
1074
  FROM projects p
945
1075
  LEFT JOIN sessions s ON s.project_id = p.id
946
1076
  GROUP BY p.id
947
- ORDER BY MAX(COALESCE(s.started_at, '')) DESC`).all();if(t.length===0){console.log(c.dim("no projects indexed yet."));return}let s=new Zg({head:[c.bold("project"),c.bold("sessions"),c.bold("msgs"),c.bold("latest"),c.bold("path")],style:{head:[],border:["grey"]}});for(let n of t)s.push([c.project(K(n.name,30)),String(n.session_count),String(n.message_count),c.dim(J(n.latest)),c.dim(K(n.decoded_path,50))]);console.log(s.toString())}qe();qe();P();v();Ke();import{spawn as tf}from"node:child_process";import{openSync as sf}from"node:fs";import{join as nf}from"node:path";function rf(){return nf(ee(),"dist","daemon","entrypoint.js")}async function Os(){let e=Q();if(e){console.log(`${c.ok("already running")} pid ${e.pid} \xB7 http://127.0.0.1:${e.port}`);return}F();let t=sf(Ns,"a"),s=rf();tf(process.execPath,[s],{detached:!0,stdio:["ignore",t,t],env:{...process.env}}).unref();let r=Date.now();for(;Date.now()-r<5e3;){await new Promise(i=>setTimeout(i,150));let o=Q();if(o){console.log(`${c.ok("daemon started")} pid ${o.pid} \xB7 http://127.0.0.1:${o.port}`),console.log(c.dim(`logs: ${Ns}`));return}}console.error(c.err("daemon did not come up within 5s \u2014 check the log file")),console.error(c.dim(` ${Ns}`)),process.exitCode=1}qe();import*as na from"node:readline";import{execFileSync as ta}from"node:child_process";function Qt(e={}){let t=e.psOutput??of(),s=e.isProcessAlive??af,n=e.getParentCommand??cf,r=[];for(let o of t.split(`
948
- `)){let i=o.trim();if(!i||!i.includes("dist/mcp/server.js")||lf(i))continue;let a=i.split(/\s+/);if(a.length<5)continue;let d=Number(a[0]),l=Number(a[1]),u=a[2],p=Number(a[3]);if(!Number.isFinite(d)||!Number.isFinite(l))continue;let m=l>1&&s(l);r.push({pid:d,ppid:l,parentAlive:m,etimeSeconds:df(u),pcpu:Number.isFinite(p)?p:0,orphan:!m,parentCommand:m?n(l):null})}return r}function of(){try{return ta("ps",["-eo","pid,ppid,etime,pcpu,command"],{encoding:"utf8",timeout:2e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[mcp-processes] ps -eo failed: ${t}
949
- `),""}}function af(e){if(!Number.isFinite(e)||e<=1)return!1;try{return process.kill(e,0),!0}catch{return!1}}function cf(e){if(!Number.isFinite(e)||e<=1)return null;try{let s=ta("ps",["-p",String(e),"-o","command="],{encoding:"utf8",timeout:1e3,maxBuffer:1048576}).trim();return s.length?s.slice(0,200):null}catch{return null}}function lf(e){let t=e.split(/\s+/);if(t.length<5)return!1;let s=t[4]??"";return s.endsWith("/grep")||s==="grep"||s.endsWith("/awk")||s==="awk"||s.endsWith("/rg")||s==="rg"}function df(e){if(!e)return 0;let t=0,s=e,n=e.indexOf("-");n>=0&&(t=ea(e.slice(0,n)),s=e.slice(n+1));let r=s.split(":").map(ea),o=0,i=0,a=0;return r.length===3?[o,i,a]=r:r.length===2?[i,a]=r:r.length===1&&(a=r[0]),t*86400+o*3600+i*60+a}function ea(e){let t=Number(e);return Number.isFinite(t)?t:0}var uf=50,pf=60;function vs(e){return e.pcpu>=uf&&e.etimeSeconds>=pf}w();function It(e){if(e<60)return`${e}s`;if(e<3600)return`${Math.floor(e/60)}m`;if(e<86400)return`${Math.floor(e/3600)}h`;let t=Math.floor(e/86400),s=Math.floor(e%86400/3600);return s>0?`${t}d ${s}h`:`${t}d`}var RC=5*6e4,sa=1073741824;var mf=100;function Is(e,t="PASSIVE",s){let n=Date.now(),r=e.pragma(`wal_checkpoint(${t})`),o=gf(r),i={busy:o.busy,log:o.log,moved:o.checkpointed,mode:t,durationMs:Date.now()-n};return s&&(t==="PASSIVE"&&i.log>=mf&&i.moved===0?s(`[wal-maintenance] PASSIVE checkpoint blocked: log=${i.log} frames pending, moved=0 (readers holding snapshots)`):i.moved>0&&s(`[wal-maintenance] ${t} checkpoint: log=${i.log} moved=${i.moved} busy=${i.busy} (${i.durationMs}ms)`)),i}function gf(e){let t=Array.isArray(e)?e[0]??{}:e??{};return{busy:br(t.busy),log:br(t.log),checkpointed:br(t.checkpointed)}}function br(e){return typeof e=="number"&&Number.isFinite(e)?e:typeof e=="bigint"?Number(e):0}var ff=2e3;async function Ms(e={}){let t=e.list??Qt,s=e.kill??_f,n=e.confirm??bf,r=e.sleep??hf,o=e.logger??(g=>{process.stdout.write(g+`
950
- `)}),i=e.truncateWal??Ef,a=!!e.json,d=a||!!e.yes,l=!!e.all,u=t(),p=l?u:u.filter(g=>g.orphan),m={found:p.length,killed:[],failed:[],walTruncated:!1};if(p.length===0)return o(a?JSON.stringify(m):l?"No MCP children running.":"No orphaned MCP children to prune."),0;if(!a){o(l?`Found ${p.length} MCP child${p.length===1?"":"ren"} (will prune all):`:`Found ${p.length} orphaned MCP child${p.length===1?"":"ren"}:`);for(let g of p){let f=g.orphan?" (orphan)":"";o(` pid ${g.pid} ppid ${g.ppid} age ${It(g.etimeSeconds)}${f}`)}}if(!d&&!await n("Kill these processes? [y/N] "))return o("Aborted."),0;for(let g of p)try{s(g.pid,"SIGTERM"),m.killed.push(g.pid)}catch(f){m.failed.push({pid:g.pid,reason:Sr(f)})}await r(ff);for(let g of[...m.killed])try{s(g,0);try{s(g,"SIGKILL")}catch(f){m.killed=m.killed.filter(h=>h!==g),m.failed.push({pid:g,reason:Sr(f)})}}catch{}if(m.killed.length>0)try{i(),m.walTruncated=!0}catch(g){a||o(` (WAL truncate skipped: ${Sr(g)})`)}if(a)o(JSON.stringify(m));else{o(""),o(`Pruned ${m.killed.length}/${p.length}`+(m.failed.length>0?` -- ${m.failed.length} failed`:"")+(m.walTruncated?". WAL truncated.":"."));for(let g of m.failed)o(` pid ${g.pid}: ${g.reason}`)}return m.failed.length===0?0:1}function _f(e,t){process.kill(e,t)}function hf(e){return new Promise(t=>{setTimeout(t,e)})}function Ef(){Is(_(),"TRUNCATE")}function bf(e){return new Promise(t=>{let s=na.createInterface({input:process.stdin,output:process.stderr});s.question(e,n=>{s.close();let r=n.trim().toLowerCase();t(r==="y"||r==="yes")})})}function Sr(e){if(e instanceof Error){let t=e.code;return t?`${t} ${e.message}`:e.message}return String(e)}w();v();async function ra(e={}){let t=e.logger??(u=>console.log(u)),s=e.logErr??(u=>console.error(u)),n=e.getRunningDaemon??Q,r=e.isProcessAlive??Er,o=e.clearDaemonInfo??hr,i=e.sleep??(u=>new Promise(p=>setTimeout(p,u))),a=e.kill??((u,p)=>{process.kill(u,p)}),d=e.truncateWal??yf,l=await Sf({log:t,logErr:s,getDaemon:n,isAlive:r,clearInfo:o,kill:a,sleep:i});return e.all&&(await Ms({all:!0,yes:!0,list:e.listMcpProcesses,kill:e.kill,sleep:e.sleep,logger:t,truncateWal:d}),d()),l?0:1}async function Sf(e){let t=e.getDaemon();if(!t)return e.log(c.dim("no daemon running.")),e.clearInfo(),!0;try{e.kill(t.pid,"SIGTERM")}catch(s){return e.logErr(c.err(`failed to signal pid ${t.pid}: ${s.message}`)),!1}for(let s=0;s<50;s++)if(await e.sleep(100),!e.isAlive(t.pid))return e.clearInfo(),e.log(c.ok(`stopped daemon pid ${t.pid}`)),!0;e.log(c.dim(`pid ${t.pid} ignored SIGTERM after 5s -- escalating to SIGKILL`));try{e.kill(t.pid,"SIGKILL")}catch(s){return s.code==="ESRCH"?(e.clearInfo(),e.log(c.ok(`stopped daemon pid ${t.pid}`)),!0):(e.logErr(c.err(`failed to SIGKILL pid ${t.pid}: ${s.message}`)),!1)}for(let s=0;s<20;s++)if(await e.sleep(100),!e.isAlive(t.pid))return e.clearInfo(),e.log(c.ok(`stopped daemon pid ${t.pid} (forced)`)),!0;return e.logErr(c.err(`pid ${t.pid} survived SIGKILL -- kill manually: kill -9 ${t.pid}`)),!1}function yf(){try{Is(_(),"TRUNCATE")}catch(e){console.warn(c.dim(`WAL truncate skipped: ${e.message}`))}}qe();import{spawn as wf}from"node:child_process";import{platform as Ds}from"node:os";v();function Tf(e){let t=Ds()==="darwin"?"open":Ds()==="win32"?"start":"xdg-open",s=Ds()==="win32"?["",e]:[e];wf(t,s,{detached:!0,stdio:"ignore",shell:Ds()==="win32"}).unref()}async function oa(){let e=Q();if(!e&&(console.log(c.dim("daemon not running \u2014 starting it\u2026")),await Os(),e=Q(),!e)){console.error(c.err("could not start the daemon. run `recall start` manually and check logs.")),process.exitCode=1;return}let t=`http://127.0.0.1:${e.port}`;console.log(`${c.ok("opening")} ${t}`),Tf(t)}w();var Rf=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,xf=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,kf=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function Cf(e){return e.replace(Rf,"").trim()}function Lf(e){let t=e.replace(xf,"[tool call]");return t=t.replace(kf,"[tool result]"),t=t.replace(/_\(unknown block: thinking\)_/g,""),t=t.replace(/(?:\[tool call\]|\[tool result\])(?:\s*(?:\[tool call\]|\[tool result\]))+/g,"[tool activity]"),t=t.replace(/\n{3,}/g,`
951
-
952
- `),t.trim()}function Nf(e){return e.role??e.type??"message"}function ia(e,t,s={}){let n=s.mode??"condensed",r=s.includeSidechain===!0,o=s.since?Date.parse(s.since):0,i=t.filter(u=>!(!r&&u.is_sidechain===1||o&&u.timestamp&&Date.parse(u.timestamp)<o)),a=[];s.prelude&&(a.push(s.prelude.trim()),a.push("")),a.push(`# Claude Recall, past session context (${n})`),a.push(""),a.push(`- **Project**: ${e.project_name}`),e.decoded_path&&a.push(`- **Path**: \`${e.decoded_path}\``),a.push(`- **Session ID**: \`${e.id}\``),e.started_at&&a.push(`- **Started**: ${e.started_at}`),e.ended_at&&a.push(`- **Ended**: ${e.ended_at}`),e.git_branch&&a.push(`- **Branch**: \`${e.git_branch}\``),a.push(`- **Messages**: ${i.length}`),a.push(""),a.push("> This is a transcript of a previous Claude Code session, included for context. Refer back to it when the user asks about past decisions, code written, or problems debugged in this work."),a.push(""),a.push("---"),a.push("");let d=0,l=0;for(let u of i){let p=u.content_text??"",m=Cf(p);n==="condensed"&&(m=Lf(m));let g=m.length>0,f=!!u.tool_names&&u.tool_names.length>0;if(!g&&!f){l+=1;continue}let h=Nf(u),E=u.timestamp?` \`${u.timestamp}\``:"";a.push(`## ${h}${E}`),a.push(""),f&&n==="condensed"&&(a.push(`_tools used: ${u.tool_names}_`),a.push("")),g&&(a.push(m),a.push("")),d+=1}return a.push("---"),a.push(""),a.push(`_${d} messages included_`+(l?`, ${l} empty skipped`:"")+(r?"":", subagent/sidechain hidden")+"."),a.join(`
953
- `)}w();P();import{randomUUID as aa}from"node:crypto";import{writeFileSync as ca,readFileSync as GC,existsSync as Af,mkdirSync as Of}from"node:fs";import{join as yr}from"node:path";var $s=yr(x,"threads"),vf=yr($s,"index.json");function la(){F(),Af($s)||Of($s,{recursive:!0})}function da(e,t,s){return{id:e.id,name:e.name,summary:e.summary,created_at:e.created_at,closed_at:e.closed_at,archived:e.archived===1,session_count:t.session_count,origin_count:t.origin_count,project:s?.project??null,project_count:s?.project_count??0,folder_id:e.folder_id??null}}function ua(e){let t=new Map;if(e.length===0)return t;let s=_(),n=e.map(()=>"?").join(","),r=s.prepare(`SELECT te.thread_id AS thread_id,
1077
+ ORDER BY MAX(COALESCE(s.started_at, '')) DESC`).all();if(t.length===0){console.log(c.dim("no projects indexed yet."));return}let n=new _h({head:[c.bold("project"),c.bold("sessions"),c.bold("msgs"),c.bold("latest"),c.bold("path")],style:{head:[],border:["grey"]}});for(let s of t)n.push([c.project(K(s.name,30)),String(s.session_count),String(s.message_count),c.dim(G(s.latest)),c.dim(K(s.decoded_path,50))]);console.log(n.toString())}Xe();Xe();D();$();Ge();mn();import{spawn as bh}from"node:child_process";import{openSync as Sh}from"node:fs";import{join as wh}from"node:path";function yh(){return wh(te(),"dist","daemon","entrypoint.js")}function Th(e){return e.list({excludePids:[e.selfPid]})}function Rh(e){let t=[];t.push("ERROR: another Recall daemon is already running:");for(let n of e){let s=un(n.etimeSeconds)??n.etime,r=n.command.length>200?`${n.command.slice(0,197)}...`:n.command;t.push(` pid=${n.pid} started=${s} command=${r}`)}if(t.push("Only one daemon may run at a time (both would write to the same DB and corrupt state)."),t.push("Stop it first: recall stop"),e.length>0){let n=e[0].pid;t.push(`If 'recall stop' didn't kill it, use: kill ${n}`)}return t.join(`
1078
+ `)}async function Zn(e={}){let t=e.listDaemonProcesses??st,n=e.selfPid??process.pid,s=e.getRunningDaemon??oe,r=e.logger??(p=>console.log(p)),o=e.logErr??(p=>console.error(p)),i=Th({list:t,selfPid:n});if(i.length>0){o(Rh(i)),process.exitCode=1;return}let a=s();if(a){r(`${c.ok("already running")} pid ${a.pid} \xB7 http://127.0.0.1:${a.port}`);return}j();let d=Sh(Vn,"a"),l=yh();bh(process.execPath,[l],{detached:!0,stdio:["ignore",d,d],env:{...process.env}}).unref();let m=Date.now();for(;Date.now()-m<5e3;){await new Promise(g=>setTimeout(g,150));let p=s();if(p){r(`${c.ok("daemon started")} pid ${p.pid} \xB7 http://127.0.0.1:${p.port}`),r(c.dim(`logs: ${Vn}`));return}}o(c.err("daemon did not come up within 5s \u2014 check the log file")),o(c.dim(` ${Vn}`)),process.exitCode=1}Xe();mn();import*as bc from"node:readline";import{execFileSync as _c}from"node:child_process";var kh=["dist/mcp-server.js","dist/mcp/server.js"];function xh(e){for(let t of kh)if(e.includes(t))return!0;return!1}function xt(e={}){let t=e.psOutput??Ch(),n=e.isProcessAlive??Ah,s=e.getParentCommand??Lh,r=[];for(let o of t.split(`
1079
+ `)){let i=o.trim();if(!i||!xh(i)||Nh(i))continue;let a=i.split(/\s+/);if(a.length<5)continue;let d=Number(a[0]),l=Number(a[1]),u=a[2],m=a[3],p=a[4],g=0,f=0;if(/^\d+$/.test(m)&&(p.includes(".")||/^\d+$/.test(p))?(g=Number(m),f=Number(p)):f=Number(m),!Number.isFinite(d)||!Number.isFinite(l))continue;let _=l>1&&n(l);r.push({pid:d,ppid:l,parentAlive:_,etimeSeconds:Oh(u),pcpu:Number.isFinite(f)?f:0,rssKb:Number.isFinite(g)?g:0,orphan:!_,parentCommand:_?s(l):null})}return r}function Ch(){try{return _c("ps",["-axo","pid,ppid,etime,rss,pcpu,command"],{encoding:"utf8",timeout:2e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[mcp-processes] ps -axo failed: ${t}
1080
+ `),""}}function Ah(e){if(!Number.isFinite(e)||e<=1)return!1;try{return process.kill(e,0),!0}catch{return!1}}function Lh(e){if(!Number.isFinite(e)||e<=1)return null;try{let n=_c("ps",["-p",String(e),"-o","command="],{encoding:"utf8",timeout:1e3,maxBuffer:1048576}).trim();return n.length?n.slice(0,200):null}catch{return null}}function Nh(e){let t=e.split(/\s+/);if(t.length<5)return!1;let n=[t[5]??"",t[4]??""];for(let s of n)if(s&&(s.endsWith("/grep")||s==="grep"||s.endsWith("/awk")||s==="awk"||s.endsWith("/rg")||s==="rg"))return!0;return!1}function Oh(e){if(!e)return 0;let t=0,n=e,s=e.indexOf("-");s>=0&&(t=gc(e.slice(0,s)),n=e.slice(s+1));let r=n.split(":").map(gc),o=0,i=0,a=0;return r.length===3?[o,i,a]=r:r.length===2?[i,a]=r:r.length===1&&(a=r[0]),t*86400+o*3600+i*60+a}function gc(e){let t=Number(e);return Number.isFinite(t)?t:0}var vh=50,Ih=60;function es(e){return e.pcpu>=vh&&e.etimeSeconds>=Ih}var fc=4,Mh=1024*1024;function hc(e){let t=e??xt(),n=t.length,s=t.reduce((l,u)=>l+(Number.isFinite(u.rssKb)&&u.rssKb>0?u.rssKb:0),0),r=n>fc,o=s>Mh;if(!(r||o))return{flagged:!1,severity:"ok",count:n,aggregateRssKb:s,message:null};let a=[];r&&a.push(`${n} MCP children (threshold ${fc})`),o&&a.push(`${Dh(s)} aggregate RSS (threshold 1 GiB)`);let d=`Zombie MCP threshold breached: ${a.join(" + ")}. Each MCP child holds a SQLite read connection and can pin WAL checkpoints. Run \`recall mcp-prune --all\` to reap (note: pre-2026-05-23 builds may miss processes from stale install paths -- \`pkill -f mcp-server.js\` is the nuclear option).`;return{flagged:!0,severity:"high",count:n,aggregateRssKb:s,message:d}}function Dh(e){return e<1024?`${e} KB`:e<1024*1024?`${(e/1024).toFixed(1)} MB`:`${(e/(1024*1024)).toFixed(2)} GB`}R();function ut(e){if(e<60)return`${e}s`;if(e<3600)return`${Math.floor(e/60)}m`;if(e<86400)return`${Math.floor(e/3600)}h`;let t=Math.floor(e/86400),n=Math.floor(e%86400/3600);return n>0?`${t}d ${n}h`:`${t}d`}var PO=5*6e4,Ec=1073741824;var $h=100;function ts(e,t="PASSIVE",n){let s=Date.now(),r=e.pragma(`wal_checkpoint(${t})`),o=Ph(r),i={busy:o.busy,log:o.log,moved:o.checkpointed,mode:t,durationMs:Date.now()-s};return n&&(t==="PASSIVE"&&i.log>=$h&&i.moved===0?n(`[wal-maintenance] PASSIVE checkpoint blocked: log=${i.log} frames pending, moved=0 (readers holding snapshots)`):i.moved>0&&n(`[wal-maintenance] ${t} checkpoint: log=${i.log} moved=${i.moved} busy=${i.busy} (${i.durationMs}ms)`)),i}function Ph(e){let t=Array.isArray(e)?e[0]??{}:e??{};return{busy:io(t.busy),log:io(t.log),checkpointed:io(t.checkpointed)}}function io(e){return typeof e=="number"&&Number.isFinite(e)?e:typeof e=="bigint"?Number(e):0}var Fh=2e3;async function ns(e={}){let t=e.list??xt,n=e.kill??jh,s=e.confirm??Hh,r=e.sleep??Uh,o=e.logger??(g=>{process.stdout.write(g+`
1081
+ `)}),i=e.truncateWal??Bh,a=!!e.json,d=a||!!e.yes,l=!!e.all,u=t(),m=l?u:u.filter(g=>g.orphan),p={found:m.length,killed:[],failed:[],walTruncated:!1};if(m.length===0)return o(a?JSON.stringify(p):l?"No MCP children running.":"No orphaned MCP children to prune."),0;if(!a){o(l?`Found ${m.length} MCP child${m.length===1?"":"ren"} (will prune all):`:`Found ${m.length} orphaned MCP child${m.length===1?"":"ren"}:`);for(let g of m){let f=g.orphan?" (orphan)":"";o(` pid ${g.pid} ppid ${g.ppid} age ${ut(g.etimeSeconds)}${f}`)}}if(!d&&!await s("Kill these processes? [y/N] "))return o("Aborted."),0;for(let g of m)try{n(g.pid,"SIGTERM"),p.killed.push(g.pid)}catch(f){p.failed.push({pid:g.pid,reason:ao(f)})}await r(Fh);for(let g of[...p.killed])try{n(g,0);try{n(g,"SIGKILL")}catch(f){p.killed=p.killed.filter(h=>h!==g),p.failed.push({pid:g,reason:ao(f)})}}catch{}if(p.killed.length>0)try{i(),p.walTruncated=!0}catch(g){a||o(` (WAL truncate skipped: ${ao(g)})`)}if(a)o(JSON.stringify(p));else{o(""),o(`Pruned ${p.killed.length}/${m.length}`+(p.failed.length>0?` -- ${p.failed.length} failed`:"")+(p.walTruncated?". WAL truncated.":"."));for(let g of p.failed)o(` pid ${g.pid}: ${g.reason}`)}return p.failed.length===0?0:1}function jh(e,t){process.kill(e,t)}function Uh(e){return new Promise(t=>{setTimeout(t,e)})}function Bh(){ts(E(),"TRUNCATE")}function Hh(e){return new Promise(t=>{let n=bc.createInterface({input:process.stdin,output:process.stderr});n.question(e,s=>{n.close();let r=s.trim().toLowerCase();t(r==="y"||r==="yes")})})}function ao(e){if(e instanceof Error){let t=e.code;return t?`${t} ${e.message}`:e.message}return String(e)}R();$();async function Sc(e={}){let t=e.logger??(w=>console.log(w)),n=e.logErr??(w=>console.error(w)),s=e.getRunningDaemon??oe,r=e.isProcessAlive??pn,o=e.clearDaemonInfo??lc,i=e.sleep??(w=>new Promise(y=>setTimeout(y,w))),a=e.kill??((w,y)=>{process.kill(w,y)}),d=e.truncateWal??Gh,l=e.listDaemonProcesses??st,u=e.selfPid??process.pid,p=s()?.pid,{ok:g,killed:f}=await Xh({log:t,logErr:n,getDaemon:s,isAlive:r,clearInfo:o,kill:a,sleep:i}),h=p!=null?[u,p]:[u],_=await Wh({log:t,logErr:n,listDaemons:l,isAlive:r,kill:a,sleep:i,excludePids:h}),S=(f&&g?1:0)+_;return t(S===0?c.dim("no daemons running."):c.ok(`stopped ${S} daemon${S===1?"":"s"}`)),e.all&&(await ns({all:!0,yes:!0,list:e.listMcpProcesses,kill:e.kill,sleep:e.sleep,logger:t,truncateWal:d}),d()),g?0:1}async function Wh(e){let t=e.listDaemons({excludePids:e.excludePids});if(t.length===0)return 0;let n=0;for(let s of t){try{e.kill(s.pid,"SIGTERM")}catch(o){if(o.code==="ESRCH"){e.log(c.ok(`stopped daemon pid ${s.pid}`)),n+=1;continue}e.logErr(c.err(`failed to signal pid ${s.pid}: ${o.message}`));continue}let r=!1;for(let o=0;o<50;o++)if(await e.sleep(100),!e.isAlive(s.pid)){r=!0;break}if(!r){e.log(c.dim(`pid ${s.pid} ignored SIGTERM after 5s -- escalating to SIGKILL`));try{e.kill(s.pid,"SIGKILL")}catch(o){if(o.code==="ESRCH"){e.log(c.ok(`stopped daemon pid ${s.pid}`)),n+=1;continue}e.logErr(c.err(`failed to SIGKILL pid ${s.pid}: ${o.message}`));continue}for(let o=0;o<20;o++)if(await e.sleep(100),!e.isAlive(s.pid)){r=!0;break}if(!r){e.logErr(c.err(`pid ${s.pid} survived SIGKILL -- kill manually: kill -9 ${s.pid}`));continue}}e.log(c.ok(`stopped daemon pid ${s.pid}`)),n+=1}return n}async function Xh(e){let t=e.getDaemon();if(!t)return e.clearInfo(),{ok:!0,killed:!1};try{e.kill(t.pid,"SIGTERM")}catch(n){return e.logErr(c.err(`failed to signal pid ${t.pid}: ${n.message}`)),{ok:!1,killed:!1}}for(let n=0;n<50;n++)if(await e.sleep(100),!e.isAlive(t.pid))return e.clearInfo(),e.log(c.ok(`stopped daemon pid ${t.pid}`)),{ok:!0,killed:!0};e.log(c.dim(`pid ${t.pid} ignored SIGTERM after 5s -- escalating to SIGKILL`));try{e.kill(t.pid,"SIGKILL")}catch(n){return n.code==="ESRCH"?(e.clearInfo(),e.log(c.ok(`stopped daemon pid ${t.pid}`)),{ok:!0,killed:!0}):(e.logErr(c.err(`failed to SIGKILL pid ${t.pid}: ${n.message}`)),{ok:!1,killed:!1})}for(let n=0;n<20;n++)if(await e.sleep(100),!e.isAlive(t.pid))return e.clearInfo(),e.log(c.ok(`stopped daemon pid ${t.pid} (forced)`)),{ok:!0,killed:!0};return e.logErr(c.err(`pid ${t.pid} survived SIGKILL -- kill manually: kill -9 ${t.pid}`)),{ok:!1,killed:!1}}function Gh(){try{ts(E(),"TRUNCATE")}catch(e){console.warn(c.dim(`WAL truncate skipped: ${e.message}`))}}Xe();import{spawn as Jh}from"node:child_process";import{platform as ss}from"node:os";$();function zh(e){let t=ss()==="darwin"?"open":ss()==="win32"?"start":"xdg-open",n=ss()==="win32"?["",e]:[e];Jh(t,n,{detached:!0,stdio:"ignore",shell:ss()==="win32"}).unref()}async function wc(){let e=oe();if(!e&&(console.log(c.dim("daemon not running \u2014 starting it\u2026")),await Zn(),e=oe(),!e)){console.error(c.err("could not start the daemon. run `recall start` manually and check logs.")),process.exitCode=1;return}let t=`http://127.0.0.1:${e.port}`;console.log(`${c.ok("opening")} ${t}`),zh(t)}R();var Yh=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,qh=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,Vh=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function Kh(e){return e.replace(Yh,"").trim()}function Qh(e){let t=e.replace(qh,"[tool call]");return t=t.replace(Vh,"[tool result]"),t=t.replace(/_\(unknown block: thinking\)_/g,""),t=t.replace(/(?:\[tool call\]|\[tool result\])(?:\s*(?:\[tool call\]|\[tool result\]))+/g,"[tool activity]"),t=t.replace(/\n{3,}/g,`
1082
+
1083
+ `),t.trim()}function Zh(e){return e.role??e.type??"message"}function yc(e,t,n={}){let s=n.mode??"condensed",r=n.includeSidechain===!0,o=n.since?Date.parse(n.since):0,i=t.filter(u=>!(!r&&u.is_sidechain===1||o&&u.timestamp&&Date.parse(u.timestamp)<o)),a=[];n.prelude&&(a.push(n.prelude.trim()),a.push("")),a.push(`# Claude Recall, past session context (${s})`),a.push(""),a.push(`- **Project**: ${e.project_name}`),e.decoded_path&&a.push(`- **Path**: \`${e.decoded_path}\``),a.push(`- **Session ID**: \`${e.id}\``),e.started_at&&a.push(`- **Started**: ${e.started_at}`),e.ended_at&&a.push(`- **Ended**: ${e.ended_at}`),e.git_branch&&a.push(`- **Branch**: \`${e.git_branch}\``),a.push(`- **Messages**: ${i.length}`),a.push(""),a.push("> This is a transcript of a previous Claude Code session, included for context. Refer back to it when the user asks about past decisions, code written, or problems debugged in this work."),a.push(""),a.push("---"),a.push("");let d=0,l=0;for(let u of i){let m=u.content_text??"",p=Kh(m);s==="condensed"&&(p=Qh(p));let g=p.length>0,f=!!u.tool_names&&u.tool_names.length>0;if(!g&&!f){l+=1;continue}let h=Zh(u),_=u.timestamp?` \`${u.timestamp}\``:"";a.push(`## ${h}${_}`),a.push(""),f&&s==="condensed"&&(a.push(`_tools used: ${u.tool_names}_`),a.push("")),g&&(a.push(p),a.push("")),d+=1}return a.push("---"),a.push(""),a.push(`_${d} messages included_`+(l?`, ${l} empty skipped`:"")+(r?"":", subagent/sidechain hidden")+"."),a.join(`
1084
+ `)}R();D();import{randomUUID as Tc}from"node:crypto";import{writeFileSync as Rc,readFileSync as iv,existsSync as eE,mkdirSync as tE}from"node:fs";import{join as co}from"node:path";var rs=co(x,"threads"),nE=co(rs,"index.json");function kc(){j(),eE(rs)||tE(rs,{recursive:!0})}function xc(e,t,n){return{id:e.id,name:e.name,summary:e.summary,created_at:e.created_at,closed_at:e.closed_at,archived:e.archived===1,session_count:t.session_count,origin_count:t.origin_count,project:n?.project??null,project_count:n?.project_count??0,folder_id:e.folder_id??null}}function Cc(e){let t=new Map;if(e.length===0)return t;let n=E(),s=e.map(()=>"?").join(","),r=n.prepare(`SELECT te.thread_id AS thread_id,
954
1085
  p.name AS project,
955
1086
  COUNT(*) AS n,
956
1087
  SUM(CASE WHEN te.role = 'origin' THEN 1 ELSE 0 END) AS origin_n
957
1088
  FROM thread_edges te
958
1089
  LEFT JOIN sessions s ON s.id = te.session_id
959
1090
  LEFT JOIN projects p ON p.id = s.project_id
960
- WHERE te.thread_id IN (${n})
961
- GROUP BY te.thread_id, p.name`).all(...e),o=new Map;for(let i of r){let a=o.get(i.thread_id);a||(a=[],o.set(i.thread_id,a)),a.push(i)}for(let[i,a]of o){let d=a.filter(p=>p.project!==null),l=d.length,u=null;d.length>0&&(u=[...d].sort((m,g)=>g.n-m.n||g.origin_n-m.origin_n||(m.project??"").localeCompare(g.project??""))[0].project),t.set(i,{project:u,project_count:l})}return t}function pa(e){let t=e.auto_title_source;return{thread_id:e.thread_id,session_id:e.session_id,parent_session_id:e.parent_session_id,role:e.role,confidence:e.confidence,source:e.source,added_at:e.added_at,alias:e.alias,auto_title:e.auto_title,auto_title_source:t==="agent"||t==="heuristic"?t:null,alias_source:e.alias?"manual":null,first_user_message:e.first_user_message,project:e.project}}function ma(e){let s=_().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
1091
+ WHERE te.thread_id IN (${s})
1092
+ GROUP BY te.thread_id, p.name`).all(...e),o=new Map;for(let i of r){let a=o.get(i.thread_id);a||(a=[],o.set(i.thread_id,a)),a.push(i)}for(let[i,a]of o){let d=a.filter(m=>m.project!==null),l=d.length,u=null;d.length>0&&(u=[...d].sort((p,g)=>g.n-p.n||g.origin_n-p.origin_n||(p.project??"").localeCompare(g.project??""))[0].project),t.set(i,{project:u,project_count:l})}return t}function Ac(e){let t=e.auto_title_source;return{thread_id:e.thread_id,session_id:e.session_id,parent_session_id:e.parent_session_id,role:e.role,confidence:e.confidence,source:e.source,added_at:e.added_at,alias:e.alias,auto_title:e.auto_title,auto_title_source:t==="agent"||t==="heuristic"?t:null,alias_source:e.alias?"manual":null,first_user_message:e.first_user_message,project:e.project}}function Lc(e){let n=E().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
962
1093
  s.auto_title AS auto_title,
963
1094
  s.auto_title_source AS auto_title_source,
964
1095
  s.first_user_message AS first_user_message,
@@ -966,11 +1097,11 @@ ${o}
966
1097
  FROM (SELECT ? AS sid) q
967
1098
  LEFT JOIN sessions s ON s.id = q.sid
968
1099
  LEFT JOIN session_aliases sa ON sa.session_id = q.sid
969
- LEFT JOIN projects p ON p.id = s.project_id`).get(e),n=s?.auto_title_source??null;return{alias:s?.alias??null,auto_title:s?.auto_title??null,auto_title_source:n==="agent"||n==="heuristic"?n:null,first_user_message:s?.first_user_message??null,project:s?.project??null}}function ga(e){let s=_().prepare(`SELECT
1100
+ LEFT JOIN projects p ON p.id = s.project_id`).get(e),s=n?.auto_title_source??null;return{alias:n?.alias??null,auto_title:n?.auto_title??null,auto_title_source:s==="agent"||s==="heuristic"?s:null,first_user_message:n?.first_user_message??null,project:n?.project??null}}function Nc(e){let n=E().prepare(`SELECT
970
1101
  COUNT(*) AS session_count,
971
1102
  SUM(CASE WHEN role = 'origin' THEN 1 ELSE 0 END) AS origin_count
972
- FROM thread_edges WHERE thread_id = ?`).get(e);return{session_count:s?.session_count??0,origin_count:s?.origin_count??0}}function Ae(e){let t=Oe(e);t&&(la(),ca(yr($s,`${e}.json`),JSON.stringify(t,null,2)),fa())}function fa(){la();let e=wr({includeArchived:!0});ca(vf,JSON.stringify({threads:e},null,2))}function _a(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let s=_(),n=aa(),r=new Date().toISOString();s.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(n,t,e.summary?.trim()||null,r),e.originSessionId&&s.prepare(`INSERT INTO thread_edges (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
973
- VALUES (?, ?, NULL, 'origin', 1.0, 'manual', ?)`).run(n,e.originSessionId,r),Ae(n);let o=Oe(n);if(!o)throw new Error("thread creation succeeded but read-back failed");return o}function wr(e={}){let t=_(),s=e.includeArchived?"":"WHERE archived = 0",n=t.prepare(`SELECT * FROM threads ${s} ORDER BY created_at DESC`).all(),r=ua(n.map(o=>o.id));return n.map(o=>da(o,ga(o.id),r.get(o.id)))}function Oe(e){let t=_(),s=t.prepare("SELECT * FROM threads WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT e.*,
1103
+ FROM thread_edges WHERE thread_id = ?`).get(e);return{session_count:n?.session_count??0,origin_count:n?.origin_count??0}}function De(e){let t=$e(e);t&&(kc(),Rc(co(rs,`${e}.json`),JSON.stringify(t,null,2)),Oc())}function Oc(){kc();let e=lo({includeArchived:!0});Rc(nE,JSON.stringify({threads:e},null,2))}function vc(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let n=E(),s=Tc(),r=new Date().toISOString();n.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(s,t,e.summary?.trim()||null,r),e.originSessionId&&n.prepare(`INSERT INTO thread_edges (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1104
+ VALUES (?, ?, NULL, 'origin', 1.0, 'manual', ?)`).run(s,e.originSessionId,r),De(s);let o=$e(s);if(!o)throw new Error("thread creation succeeded but read-back failed");return o}function lo(e={}){let t=E(),n=e.includeArchived?"":"WHERE archived = 0",s=t.prepare(`SELECT * FROM threads ${n} ORDER BY created_at DESC`).all(),r=Cc(s.map(o=>o.id));return s.map(o=>xc(o,Nc(o.id),r.get(o.id)))}function $e(e){let t=E(),n=t.prepare("SELECT * FROM threads WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT e.*,
974
1105
  NULLIF(sa.alias, '') AS alias,
975
1106
  s.auto_title AS auto_title,
976
1107
  s.auto_title_source AS auto_title_source,
@@ -981,7 +1112,7 @@ ${o}
981
1112
  LEFT JOIN session_aliases sa ON sa.session_id = e.session_id
982
1113
  LEFT JOIN projects p ON p.id = s.project_id
983
1114
  WHERE e.thread_id = ?
984
- ORDER BY e.added_at ASC`).all(e).map(pa),r=ua([e]).get(e);return{...da(s,ga(s.id),r),edges:n}}function ha(e){let t=_();if(!t.prepare("SELECT * FROM threads WHERE id = ?").get(e.threadId))throw new Error(`thread ${e.threadId} not found`);let n=new Date().toISOString(),r=e.parentSessionId??null,o=e.role??(r?"child":"origin"),i=e.confidence??1,a=e.source??"manual";if(i<0||i>1)throw new Error("confidence must be 0..1");t.prepare(`INSERT INTO thread_edges
1115
+ ORDER BY e.added_at ASC`).all(e).map(Ac),r=Cc([e]).get(e);return{...xc(n,Nc(n.id),r),edges:s}}function Ic(e){let t=E();if(!t.prepare("SELECT * FROM threads WHERE id = ?").get(e.threadId))throw new Error(`thread ${e.threadId} not found`);let s=new Date().toISOString(),r=e.parentSessionId??null,o=e.role??(r?"child":"origin"),i=e.confidence??1,a=e.source??"manual";if(i<0||i>1)throw new Error("confidence must be 0..1");t.prepare(`INSERT INTO thread_edges
985
1116
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
986
1117
  VALUES (?, ?, ?, ?, ?, ?, ?)
987
1118
  ON CONFLICT(thread_id, session_id) DO UPDATE SET
@@ -989,115 +1120,119 @@ ${o}
989
1120
  role = excluded.role,
990
1121
  confidence = excluded.confidence,
991
1122
  source = excluded.source,
992
- added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,o,i,a,n),Ae(e.threadId);let d=ma(e.sessionId);return{thread_id:e.threadId,session_id:e.sessionId,parent_session_id:r,role:o,confidence:i,source:a,added_at:n,alias:d.alias,auto_title:d.auto_title,auto_title_source:d.auto_title_source,alias_source:d.alias?"manual":null,first_user_message:d.first_user_message,project:d.project}}function Ea(e,t){let n=_().prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e,t);return n.changes>0&&Ae(e),{removed:n.changes}}function ba(e,t,s){let n=_(),r=n.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e,t);if(!r)throw new Error("edge not found; add the session first");if(s!==null){if(s===t)throw new Error("cycle detected: session cannot be its own parent");let a=n.prepare("SELECT parent_session_id FROM thread_edges WHERE thread_id = ? AND session_id = ?"),d=s,l=new Set;for(;d!==null;){if(d===t)throw new Error(`cycle detected: setting parent of ${t} to ${s} would create a loop`);if(l.has(d))break;l.add(d),d=a.get(e,d)?.parent_session_id??null}}let o=s?"child":"origin";n.prepare(`UPDATE thread_edges
1123
+ added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,o,i,a,s),De(e.threadId);let d=Lc(e.sessionId);return{thread_id:e.threadId,session_id:e.sessionId,parent_session_id:r,role:o,confidence:i,source:a,added_at:s,alias:d.alias,auto_title:d.auto_title,auto_title_source:d.auto_title_source,alias_source:d.alias?"manual":null,first_user_message:d.first_user_message,project:d.project}}function Mc(e,t){let s=E().prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e,t);return s.changes>0&&De(e),{removed:s.changes}}function Dc(e,t,n){let s=E(),r=s.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e,t);if(!r)throw new Error("edge not found; add the session first");if(n!==null){if(n===t)throw new Error("cycle detected: session cannot be its own parent");let a=s.prepare("SELECT parent_session_id FROM thread_edges WHERE thread_id = ? AND session_id = ?"),d=n,l=new Set;for(;d!==null;){if(d===t)throw new Error(`cycle detected: setting parent of ${t} to ${n} would create a loop`);if(l.has(d))break;l.add(d),d=a.get(e,d)?.parent_session_id??null}}let o=n?"child":"origin";s.prepare(`UPDATE thread_edges
993
1124
  SET parent_session_id = ?, role = ?, added_at = ?
994
- WHERE thread_id = ? AND session_id = ?`).run(s,o,new Date().toISOString(),e,t),Ae(e);let i=ma(t);return pa({...r,parent_session_id:s,role:o,added_at:new Date().toISOString(),alias:i.alias,auto_title:i.auto_title,auto_title_source:i.auto_title_source,first_user_message:i.first_user_message,project:i.project})}function Sa(e,t){let s=t.trim();if(!s)throw new Error("name cannot be empty");_().prepare("UPDATE threads SET name = ? WHERE id = ?").run(s,e),Ae(e);let r=Oe(e);if(!r)throw new Error(`thread ${e} not found`);return r}function ya(e){_().prepare("UPDATE threads SET closed_at = ? WHERE id = ?").run(new Date().toISOString(),e),Ae(e);let s=Oe(e);if(!s)throw new Error(`thread ${e} not found`);return s}function wa(e){_().prepare("UPDATE threads SET closed_at = NULL WHERE id = ?").run(e),Ae(e);let s=Oe(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Ta(e){_().prepare("UPDATE threads SET archived = 1 WHERE id = ?").run(e),Ae(e);let s=Oe(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Ra(e,t){if(e===t)throw new Error("cannot merge a thread into itself");let s=_(),n=new Date().toISOString();s.transaction(()=>{let o=s.prepare("SELECT * FROM thread_edges WHERE thread_id = ?").all(e);for(let i of o)s.prepare(`INSERT INTO thread_edges
1125
+ WHERE thread_id = ? AND session_id = ?`).run(n,o,new Date().toISOString(),e,t),De(e);let i=Lc(t);return Ac({...r,parent_session_id:n,role:o,added_at:new Date().toISOString(),alias:i.alias,auto_title:i.auto_title,auto_title_source:i.auto_title_source,first_user_message:i.first_user_message,project:i.project})}function $c(e,t){let n=t.trim();if(!n)throw new Error("name cannot be empty");E().prepare("UPDATE threads SET name = ? WHERE id = ?").run(n,e),De(e);let r=$e(e);if(!r)throw new Error(`thread ${e} not found`);return r}function Pc(e){E().prepare("UPDATE threads SET closed_at = ? WHERE id = ?").run(new Date().toISOString(),e),De(e);let n=$e(e);if(!n)throw new Error(`thread ${e} not found`);return n}function Fc(e){E().prepare("UPDATE threads SET closed_at = NULL WHERE id = ?").run(e),De(e);let n=$e(e);if(!n)throw new Error(`thread ${e} not found`);return n}function jc(e){E().prepare("UPDATE threads SET archived = 1 WHERE id = ?").run(e),De(e);let n=$e(e);if(!n)throw new Error(`thread ${e} not found`);return n}function Uc(e,t){if(e===t)throw new Error("cannot merge a thread into itself");let n=E(),s=new Date().toISOString();n.transaction(()=>{let o=n.prepare("SELECT * FROM thread_edges WHERE thread_id = ?").all(e);for(let i of o)n.prepare(`INSERT INTO thread_edges
995
1126
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
996
1127
  VALUES (?, ?, ?, ?, ?, ?, ?)
997
1128
  ON CONFLICT(thread_id, session_id) DO UPDATE SET
998
1129
  parent_session_id = COALESCE(thread_edges.parent_session_id, excluded.parent_session_id),
999
1130
  role = CASE WHEN thread_edges.role = 'origin' OR excluded.role = 'origin' THEN 'origin' ELSE 'child' END,
1000
1131
  confidence = MAX(thread_edges.confidence, excluded.confidence),
1001
- source = thread_edges.source`).run(t,i.session_id,i.parent_session_id,i.role,i.confidence,i.source,n);s.prepare("DELETE FROM threads WHERE id = ?").run(e)})(),Ae(t),fa();let r=Oe(t);if(!r)throw new Error("merge destination disappeared");return r}function xa(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=_(),s=new Date().toISOString(),n=aa();t.transaction(()=>{t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, NULL, ?)").run(n,e.newThreadName.trim(),s);for(let o of e.sessionIds){let i=t.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e.threadId,o);i&&(t.prepare(`INSERT INTO thread_edges
1132
+ source = thread_edges.source`).run(t,i.session_id,i.parent_session_id,i.role,i.confidence,i.source,s);n.prepare("DELETE FROM threads WHERE id = ?").run(e)})(),De(t),Oc();let r=$e(t);if(!r)throw new Error("merge destination disappeared");return r}function Bc(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=E(),n=new Date().toISOString(),s=Tc();t.transaction(()=>{t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, NULL, ?)").run(s,e.newThreadName.trim(),n);for(let o of e.sessionIds){let i=t.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e.threadId,o);i&&(t.prepare(`INSERT INTO thread_edges
1002
1133
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1003
- VALUES (?, ?, ?, ?, ?, ?, ?)`).run(n,o,i.parent_session_id,i.role,i.confidence,i.source,s),t.prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e.threadId,o))}})(),Ae(e.threadId),Ae(n);let r=Oe(n);if(!r)throw new Error("split destination disappeared");return r}function ka(e){let t=Oe(e);if(!t)return[];let s=t.edges.filter(r=>r.role==="origin").map(r=>r.session_id),n=t.edges.filter(r=>r.role==="child").map(r=>r.session_id);return[...s,...n]}v();de();w();P();import{writeFileSync as If}from"node:fs";import{join as Mf}from"node:path";var Df=Mf(x,"recall-events.json");function Tr(e,t,s,n="cli"){_().prepare(`
1134
+ VALUES (?, ?, ?, ?, ?, ?, ?)`).run(s,o,i.parent_session_id,i.role,i.confidence,i.source,n),t.prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e.threadId,o))}})(),De(e.threadId),De(s);let r=$e(s);if(!r)throw new Error("split destination disappeared");return r}function Hc(e){let t=$e(e);if(!t)return[];let n=t.edges.filter(r=>r.role==="origin").map(r=>r.session_id),s=t.edges.filter(r=>r.role==="child").map(r=>r.session_id);return[...n,...s]}$();ge();R();D();import{writeFileSync as sE}from"node:fs";import{join as rE}from"node:path";var oE=rE(x,"recall-events.json");function uo(e,t,n,s="cli"){E().prepare(`
1004
1135
  INSERT INTO recall_events (session_id, recalled_at, token_count, mode, caller)
1005
1136
  VALUES (?, datetime('now'), ?, ?, ?)
1006
- `).run(e,t,s,n),$f()}function $f(){F();let t=_().prepare("SELECT id, session_id, recalled_at, token_count, mode, caller FROM recall_events ORDER BY recalled_at DESC").all();If(Df,JSON.stringify(t,null,2)+`
1007
- `,"utf-8")}function Pf(e,t){if(t.length>=32)return e.prepare("SELECT id FROM sessions WHERE id = ?").get(t)?.id??null;let s=e.prepare("SELECT id FROM sessions WHERE id LIKE ? LIMIT 2").all(t+"%");return s.length===1?s[0].id:(s.length===0||process.stderr.write(`ambiguous id prefix "${t}". be more specific.
1008
- `),null)}function Ff(e){if(!e)return null;let t=e.match(/^(\d+)\s*(s|m|h|d)$/i);if(t){let n=parseInt(t[1],10),r=t[2].toLowerCase(),o=r==="s"?1e3:r==="m"?6e4:r==="h"?36e5:864e5;return new Date(Date.now()-n*o).toISOString()}if(/^\d{4}-\d{2}-\d{2}$/.test(e))return`${e}T00:00:00.000Z`;let s=Date.parse(e);return Number.isNaN(s)?null:new Date(s).toISOString()}function jf(e,t){if(t.length>=32)return e.prepare("SELECT id FROM threads WHERE id = ?").get(t)?.id??null;let s=e.prepare("SELECT id, name FROM threads WHERE id LIKE ? ORDER BY created_at DESC").all(t+"%");if(s.length===1)return s[0].id;if(s.length===0)return null;process.stderr.write(`ambiguous thread prefix "${t}" \u2014 candidates:
1009
- `);for(let n of s)process.stderr.write(` ${X(n.id)} ${n.name}
1010
- `);return null}function Ca(e,t,s){let n=e.prepare(`SELECT s.id, p.name AS project_name, p.decoded_path,
1137
+ `).run(e,t,n,s),iE()}function iE(){j();let t=E().prepare("SELECT id, session_id, recalled_at, token_count, mode, caller FROM recall_events ORDER BY recalled_at DESC").all();sE(oE,JSON.stringify(t,null,2)+`
1138
+ `,"utf-8")}function aE(e,t){if(t.length>=32)return e.prepare("SELECT id FROM sessions WHERE id = ?").get(t)?.id??null;let n=e.prepare("SELECT id FROM sessions WHERE id LIKE ? LIMIT 2").all(t+"%");return n.length===1?n[0].id:(n.length===0||process.stderr.write(`ambiguous id prefix "${t}". be more specific.
1139
+ `),null)}function cE(e){if(!e)return null;let t=e.match(/^(\d+)\s*(s|m|h|d)$/i);if(t){let s=parseInt(t[1],10),r=t[2].toLowerCase(),o=r==="s"?1e3:r==="m"?6e4:r==="h"?36e5:864e5;return new Date(Date.now()-s*o).toISOString()}if(/^\d{4}-\d{2}-\d{2}$/.test(e))return`${e}T00:00:00.000Z`;let n=Date.parse(e);return Number.isNaN(n)?null:new Date(n).toISOString()}function lE(e,t){if(t.length>=32)return e.prepare("SELECT id FROM threads WHERE id = ?").get(t)?.id??null;let n=e.prepare("SELECT id, name FROM threads WHERE id LIKE ? ORDER BY created_at DESC").all(t+"%");if(n.length===1)return n[0].id;if(n.length===0)return null;process.stderr.write(`ambiguous thread prefix "${t}" \u2014 candidates:
1140
+ `);for(let s of n)process.stderr.write(` ${X(s.id)} ${s.name}
1141
+ `);return null}function Wc(e,t,n){let s=e.prepare(`SELECT s.id, p.name AS project_name, p.decoded_path,
1011
1142
  s.started_at, s.ended_at, s.message_count, s.git_branch
1012
1143
  FROM sessions s JOIN projects p ON p.id = s.project_id
1013
- WHERE s.id = ?`).get(t);if(!n)return process.stderr.write(`session metadata missing for ${t}
1144
+ WHERE s.id = ?`).get(t);if(!s)return process.stderr.write(`session metadata missing for ${t}
1014
1145
  `),null;let r=e.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
1015
1146
  FROM messages
1016
1147
  WHERE session_id = ?
1017
- ORDER BY COALESCE(timestamp, ''), rowid`).all(t),o=s.full?"full":"condensed";return ia(n,r,{mode:o,includeSidechain:s.subagents===!0,prelude:s.prelude??null,since:Ff(s.since)})}async function La(e,t){await Ne("Context re-injection");let s=_();if(e.startsWith("thread:")){let i=e.slice(7).trim();if(!i){process.stderr.write(`thread: target requires an id or prefix
1018
- `),process.exitCode=1;return}let a=jf(s,i);if(!a){process.stderr.write(`thread not found: ${i}
1019
- `),process.exitCode=1;return}let d=ka(a);if(d.length===0){process.stderr.write(`thread ${i} has no linked sessions
1020
- `),process.exitCode=1;return}for(let l=0;l<d.length;l+=1){let u=d[l],p=Ca(s,u,{...t,prelude:l===0?t.prelude:void 0});if(p===null){process.exitCode=1;continue}l>0&&process.stdout.write(`
1148
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(t),o=n.full?"full":"condensed";return yc(s,r,{mode:o,includeSidechain:n.subagents===!0,prelude:n.prelude??null,since:cE(n.since)})}async function Xc(e,t){await pe("Context re-injection");let n=E();if(e.startsWith("thread:")){let i=e.slice(7).trim();if(!i){process.stderr.write(`thread: target requires an id or prefix
1149
+ `),process.exitCode=1;return}let a=lE(n,i);if(!a){process.stderr.write(`thread not found: ${i}
1150
+ `),process.exitCode=1;return}let d=Hc(a);if(d.length===0){process.stderr.write(`thread ${i} has no linked sessions
1151
+ `),process.exitCode=1;return}for(let l=0;l<d.length;l+=1){let u=d[l],m=Wc(n,u,{...t,prelude:l===0?t.prelude:void 0});if(m===null){process.exitCode=1;continue}l>0&&process.stdout.write(`
1021
1152
  ---
1022
1153
 
1023
- `),process.stdout.write(p),p.endsWith(`
1154
+ `),process.stdout.write(m),m.endsWith(`
1024
1155
  `)||process.stdout.write(`
1025
- `),Tr(u,Math.ceil(p.length/4),"thread","cli")}return}let n=Pf(s,e);if(!n){e.length>=32&&process.stderr.write(`session not found: ${e}
1026
- `),process.exitCode=1;return}let r=Ca(s,n,t);if(r===null){process.exitCode=1;return}process.stdout.write(r),r.endsWith(`
1156
+ `),uo(u,Math.ceil(m.length/4),"thread","cli")}return}let s=aE(n,e);if(!s){e.length>=32&&process.stderr.write(`session not found: ${e}
1157
+ `),process.exitCode=1;return}let r=Wc(n,s,t);if(r===null){process.exitCode=1;return}process.stdout.write(r),r.endsWith(`
1027
1158
  `)||process.stdout.write(`
1028
- `);let o=t.since?"since":t.full?"full":"condensed";Tr(n,Math.ceil(r.length/4),o,"cli"),process.stderr.isTTY&&process.stderr.write(`\x1B[2mShare this recall? \u2192 recall share\x1B[0m
1029
- `)}de();Ke();import{join as Uf}from"node:path";import{spawn as Bf}from"node:child_process";async function Na(e={}){await Ne("MCP server");let t=Uf(ee(),"dist","mcp-server.js"),s={...process.env};e.allowWrites&&(s.RECALL_MCP_ALLOW_WRITES="1");let n=Bf(process.execPath,[t],{stdio:"inherit",env:s});return new Promise((r,o)=>{n.on("error",o),n.on("exit",(i,a)=>{if(a){process.kill(process.pid,a);return}process.exitCode=i??0,r()})})}w();import{execSync as Rr}from"node:child_process";import{randomUUID as Hf}from"node:crypto";import Wf from"node:readline/promises";v();async function Aa(e){if(e.list){Xf();return}if(e.purge){Jf(e.purge);return}let t=await Gf();t||(process.stderr.write(c.err(`clipboard empty / nothing on stdin
1030
- `)),process.exit(1));let s=Yt(t);if(s.length>0&&!e.force){process.stderr.write(c.warn(`\u26A0 detected ${s.length} possible secret${s.length===1?"":"s"} in content:
1031
- `));for(let a of s.slice(0,8))process.stderr.write(` ${c.err(a.pattern)} ${c.dim("\u2192")} ${a.maskedPreview}
1032
- `);s.length>8&&process.stderr.write(c.dim(` \u2026 ${s.length-8} more
1159
+ `);let o=t.since?"since":t.full?"full":"condensed";uo(s,Math.ceil(r.length/4),o,"cli"),process.stderr.isTTY&&process.stderr.write(`\x1B[2mShare this recall? \u2192 recall share\x1B[0m
1160
+ `)}ge();Ge();import{join as dE}from"node:path";import{spawn as uE}from"node:child_process";async function Gc(e={}){await pe("MCP server");let t=dE(te(),"dist","mcp-server.js"),n={...process.env};e.allowWrites&&(n.RECALL_MCP_ALLOW_WRITES="1");let s=uE(process.execPath,[t],{stdio:"inherit",env:n});return new Promise((r,o)=>{s.on("error",o),s.on("exit",(i,a)=>{if(a){process.kill(process.pid,a);return}process.exitCode=i??0,r()})})}R();import{execSync as mo}from"node:child_process";import{randomUUID as mE}from"node:crypto";import pE from"node:readline/promises";$();async function Jc(e){if(e.list){gE();return}if(e.purge){fE(e.purge);return}let t=await _E();t||(process.stderr.write(c.err(`clipboard empty / nothing on stdin
1161
+ `)),process.exit(1));let n=rn(t);if(n.length>0&&!e.force){process.stderr.write(c.warn(`\u26A0 detected ${n.length} possible secret${n.length===1?"":"s"} in content:
1162
+ `));for(let a of n.slice(0,8))process.stderr.write(` ${c.err(a.pattern)} ${c.dim("\u2192")} ${a.maskedPreview}
1163
+ `);n.length>8&&process.stderr.write(c.dim(` \u2026 ${n.length-8} more
1033
1164
  `)),e.dryRun&&(process.stderr.write(c.dim(`
1034
1165
  (dry run \u2014 nothing archived)
1035
1166
  `)),process.exit(1)),!process.stdin.isTTY&&!process.stderr.isTTY&&(process.stderr.write(c.err(`refusing to archive secret content in non-interactive mode. use --force to override.
1036
- `)),e.pipe&&process.stdout.write(t),process.exit(1));let o=Wf.createInterface({input:process.stdin,output:process.stderr}),i=await o.question(c.accent("archive anyway? [y/N] "));o.close(),i.trim().toLowerCase()!=="y"&&(process.stderr.write(c.dim(`cancelled \u2014 nothing archived.
1167
+ `)),e.pipe&&process.stdout.write(t),process.exit(1));let o=pE.createInterface({input:process.stdin,output:process.stderr}),i=await o.question(c.accent("archive anyway? [y/N] "));o.close(),i.trim().toLowerCase()!=="y"&&(process.stderr.write(c.dim(`cancelled \u2014 nothing archived.
1037
1168
  `)),e.pipe&&process.stdout.write(t),process.exit(0))}e.dryRun&&(process.stderr.write(c.dim(`(dry run \u2014 would archive ${t.length.toLocaleString()} chars)
1038
- `)),e.pipe&&process.stdout.write(t),process.exit(0));let n=Hf(),r=new Date().toISOString();_().prepare(`INSERT INTO paste_archives (id, created_at, content, size_bytes, source, label)
1039
- VALUES (?, ?, ?, ?, ?, ?)`).run(n,r,t,Buffer.byteLength(t,"utf8"),e.pipe?"cli-piped":"cli",e.label??null),process.stderr.write(c.ok(`\u2713 archived ${t.length.toLocaleString()} chars as ${n.slice(0,8)}
1169
+ `)),e.pipe&&process.stdout.write(t),process.exit(0));let s=mE(),r=new Date().toISOString();E().prepare(`INSERT INTO paste_archives (id, created_at, content, size_bytes, source, label)
1170
+ VALUES (?, ?, ?, ?, ?, ?)`).run(s,r,t,Buffer.byteLength(t,"utf8"),e.pipe?"cli-piped":"cli",e.label??null),process.stderr.write(c.ok(`\u2713 archived ${t.length.toLocaleString()} chars as ${s.slice(0,8)}
1040
1171
  `)),e.label&&process.stderr.write(c.dim(` label: ${e.label}
1041
- `)),process.stderr.write(c.dim(` purge any time with: recall paste --purge ${n.slice(0,8)}
1042
- `)),e.pipe&&process.stdout.write(t)}function Xf(){let e=_().prepare(`SELECT id, created_at, size_bytes, source, label,
1172
+ `)),process.stderr.write(c.dim(` purge any time with: recall paste --purge ${s.slice(0,8)}
1173
+ `)),e.pipe&&process.stdout.write(t)}function gE(){let e=E().prepare(`SELECT id, created_at, size_bytes, source, label,
1043
1174
  substr(replace(replace(content, char(10), '\u21B5'), char(13), ''), 1, 80) AS preview
1044
1175
  FROM paste_archives
1045
1176
  ORDER BY created_at DESC
1046
1177
  LIMIT 100`).all();if(e.length===0){process.stdout.write(c.dim("no pastes archived yet. use `pbpaste | recall paste` to archive.\n"));return}process.stdout.write(`
1047
1178
  ${c.bold(`${e.length} archived paste${e.length===1?"":"s"}`)}
1048
1179
  `),process.stdout.write(c.dim(`\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1049
- `));for(let t of e){let s=t.size_bytes<1024?`${t.size_bytes}b`:t.size_bytes<1048576?`${(t.size_bytes/1024).toFixed(1)}K`:`${(t.size_bytes/1024/1024).toFixed(1)}M`,n=t.label?` ${c.project(t.label)}`:"";process.stdout.write(` ${c.accent(t.id.slice(0,8))} ${c.dim(J(t.created_at).padEnd(12))} ${s.padStart(6)}${n} ${c.dim(t.preview)}
1180
+ `));for(let t of e){let n=t.size_bytes<1024?`${t.size_bytes}b`:t.size_bytes<1048576?`${(t.size_bytes/1024).toFixed(1)}K`:`${(t.size_bytes/1024/1024).toFixed(1)}M`,s=t.label?` ${c.project(t.label)}`:"";process.stdout.write(` ${c.accent(t.id.slice(0,8))} ${c.dim(G(t.created_at).padEnd(12))} ${n.padStart(6)}${s} ${c.dim(t.preview)}
1050
1181
  `)}process.stdout.write(c.dim(`
1051
1182
  show full content: recall paste --show <id>
1052
1183
  `)+c.dim(`purge one (permanent): recall paste --purge <id>
1053
1184
 
1054
- `))}function Jf(e){e.length<8&&(process.stderr.write(c.err(`provide at least 8 characters of the paste id to purge.
1055
- `)),process.exit(1));let t=_(),s=t.prepare("SELECT id FROM paste_archives WHERE id = ? OR id LIKE ? LIMIT 2").all(e,`${e}%`);s.length===0&&(process.stderr.write(c.err(`no paste matches ${e}
1056
- `)),process.exit(1)),s.length>1&&(process.stderr.write(c.err(`prefix ${e} is ambiguous. be more specific.
1057
- `)),process.exit(1)),t.prepare("DELETE FROM paste_archives WHERE id = ?").run(s[0].id),process.stderr.write(c.ok(`\u2713 purged ${s[0].id.slice(0,8)} \u2014 content permanently destroyed
1058
- `))}async function Gf(){if(!process.stdin.isTTY)return await Yf();try{return Rr("pbpaste",{encoding:"utf8",maxBuffer:16*1024*1024})}catch{}try{return Rr("xclip -o -selection clipboard",{encoding:"utf8",maxBuffer:16*1024*1024})}catch{}try{return Rr("powershell.exe -command Get-Clipboard",{encoding:"utf8",maxBuffer:16*1024*1024})}catch{}throw new Error("Could not read clipboard. On macOS pbpaste should be built in; on Linux install xclip; on Windows pipe into this command instead.")}async function Yf(){let e=[];for await(let t of process.stdin)e.push(Buffer.isBuffer(t)?t:Buffer.from(t));return Buffer.concat(e).toString("utf8")}w();v();async function Oa(e){let t=_(),s=new Map,n=0,r=0,o=0,i=new Set,a=t.prepare(`SELECT uuid, session_id, content_text, raw_json
1185
+ `))}function fE(e){e.length<8&&(process.stderr.write(c.err(`provide at least 8 characters of the paste id to purge.
1186
+ `)),process.exit(1));let t=E(),n=t.prepare("SELECT id FROM paste_archives WHERE id = ? OR id LIKE ? LIMIT 2").all(e,`${e}%`);n.length===0&&(process.stderr.write(c.err(`no paste matches ${e}
1187
+ `)),process.exit(1)),n.length>1&&(process.stderr.write(c.err(`prefix ${e} is ambiguous. be more specific.
1188
+ `)),process.exit(1)),t.prepare("DELETE FROM paste_archives WHERE id = ?").run(n[0].id),process.stderr.write(c.ok(`\u2713 purged ${n[0].id.slice(0,8)} \u2014 content permanently destroyed
1189
+ `))}async function _E(){if(!process.stdin.isTTY)return await hE();try{return mo("pbpaste",{encoding:"utf8",maxBuffer:16*1024*1024})}catch{}try{return mo("xclip -o -selection clipboard",{encoding:"utf8",maxBuffer:16*1024*1024})}catch{}try{return mo("powershell.exe -command Get-Clipboard",{encoding:"utf8",maxBuffer:16*1024*1024})}catch{}throw new Error("Could not read clipboard. On macOS pbpaste should be built in; on Linux install xclip; on Windows pipe into this command instead.")}async function hE(){let e=[];for await(let t of process.stdin)e.push(Buffer.isBuffer(t)?t:Buffer.from(t));return Buffer.concat(e).toString("utf8")}R();$();async function zc(e){let t=E(),n=new Map,s=0,r=0,o=0,i=new Set,a=t.prepare(`SELECT uuid, session_id, content_text, raw_json
1059
1190
  FROM messages
1060
- WHERE content_text IS NOT NULL`).all(),d=t.prepare("UPDATE messages SET content_text = ?, raw_json = ? WHERE uuid = ?"),l=(f,h)=>{let E=Yt(f);if(E.length===0)return!1;for(let b of E){let S=s.get(b.pattern)??{pattern:b.pattern,severity:b.severity,hits:0,sessions:new Set};S.hits+=1,S.sessions.add(h),s.set(b.pattern,S)}return!0};process.stdout.write(c.dim(`Scanning ${a.length.toLocaleString()} messages\u2026
1061
- `));for(let f of a)if(n+=1,(l(f.content_text??"",f.session_id)||l(f.raw_json??"",f.session_id))&&(r+=1,i.add(f.session_id),e.verbose&&process.stderr.write(c.dim(` hit in session ${f.session_id.slice(0,8)} message ${f.uuid.slice(0,8)}
1062
- `)),e.redact)){let E=we(f.content_text??"").redacted,b=f.raw_json?we(f.raw_json).redacted:null;d.run(E,b,f.uuid),o+=1}let u=t.prepare(`SELECT id, first_user_message FROM sessions
1063
- WHERE first_user_message IS NOT NULL`).all(),p=t.prepare("UPDATE sessions SET first_user_message = ? WHERE id = ?"),m=0;for(let f of u)Yt(f.first_user_message).length>0&&(i.add(f.id),e.redact&&(p.run(we(f.first_user_message).redacted,f.id),m+=1));if(process.stdout.write(`
1064
- `),s.size===0){process.stdout.write(c.ok(`\u2713 clean \u2014 no secrets detected across ${n.toLocaleString()} messages.
1065
- `));return}let g=[...s.values()].sort((f,h)=>h.hits-f.hits);process.stdout.write(c.warn(`\u26A0 ${r.toLocaleString()} message${r===1?"":"s"} across ${i.size.toLocaleString()} session${i.size===1?"":"s"} contain detected secrets.
1191
+ WHERE content_text IS NOT NULL`).all(),d=t.prepare("UPDATE messages SET content_text = ?, raw_json = ? WHERE uuid = ?"),l=(f,h)=>{let _=rn(f);if(_.length===0)return!1;for(let b of _){let S=n.get(b.pattern)??{pattern:b.pattern,severity:b.severity,hits:0,sessions:new Set};S.hits+=1,S.sessions.add(h),n.set(b.pattern,S)}return!0};process.stdout.write(c.dim(`Scanning ${a.length.toLocaleString()} messages\u2026
1192
+ `));for(let f of a)if(s+=1,(l(f.content_text??"",f.session_id)||l(f.raw_json??"",f.session_id))&&(r+=1,i.add(f.session_id),e.verbose&&process.stderr.write(c.dim(` hit in session ${f.session_id.slice(0,8)} message ${f.uuid.slice(0,8)}
1193
+ `)),e.redact)){let _=Ce(f.content_text??"").redacted,b=f.raw_json?Ce(f.raw_json).redacted:null;d.run(_,b,f.uuid),o+=1}let u=t.prepare(`SELECT id, first_user_message FROM sessions
1194
+ WHERE first_user_message IS NOT NULL`).all(),m=t.prepare("UPDATE sessions SET first_user_message = ? WHERE id = ?"),p=0;for(let f of u)rn(f.first_user_message).length>0&&(i.add(f.id),e.redact&&(m.run(Ce(f.first_user_message).redacted,f.id),p+=1));if(process.stdout.write(`
1195
+ `),n.size===0){process.stdout.write(c.ok(`\u2713 clean \u2014 no secrets detected across ${s.toLocaleString()} messages.
1196
+ `));return}let g=[...n.values()].sort((f,h)=>h.hits-f.hits);process.stdout.write(c.warn(`\u26A0 ${r.toLocaleString()} message${r===1?"":"s"} across ${i.size.toLocaleString()} session${i.size===1?"":"s"} contain detected secrets.
1066
1197
 
1067
1198
  `)),process.stdout.write(c.bold(` Pattern Hits Sessions
1068
1199
  `)),process.stdout.write(c.dim(` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1069
1200
  `));for(let f of g){let h=f.severity==="high"?c.err("\u25CF"):c.warn("\u25CF");process.stdout.write(` ${h} ${f.pattern.padEnd(30)} ${String(f.hits).padStart(5)} ${String(f.sessions.size).padStart(4)}
1070
1201
  `)}process.stdout.write(`
1071
- `),e.redact?(process.stdout.write(c.ok(`\u2713 redacted in place: ${o.toLocaleString()} messages, ${m.toLocaleString()} session previews.
1202
+ `),e.redact?(process.stdout.write(c.ok(`\u2713 redacted in place: ${o.toLocaleString()} messages, ${p.toLocaleString()} session previews.
1072
1203
  `)),process.stdout.write(c.dim(` (The source JSONL files at ~/.claude/projects/ were not modified.)
1073
- `))):process.stdout.write(c.dim(" Rerun with --redact to scrub these in place, or run `recall index --force`.\n"))}w();v();import{basename as zf}from"node:path";async function Ia(e){let t=_(),s=await qf(t,e.project);if(!s){console.error(c.err(`No project found${e.project?` matching "${e.project}"`:" for current cwd"}. Run \`recall projects\` to list available projects.`)),process.exitCode=1;return}let n=t.prepare(`SELECT s.id,
1204
+ `))):process.stdout.write(c.dim(" Rerun with --redact to scrub these in place, or run `recall index --force`.\n"))}R();$();import{basename as EE}from"node:path";async function qc(e){let t=E(),n=await bE(t,e.project);if(!n){console.error(c.err(`No project found${e.project?` matching "${e.project}"`:" for current cwd"}. Run \`recall projects\` to list available projects.`)),process.exitCode=1;return}let s=t.prepare(`SELECT s.id,
1074
1205
  s.auto_title,
1075
1206
  s.auto_title_source,
1076
1207
  CASE WHEN sa.session_id IS NOT NULL THEN 1 ELSE 0 END AS has_alias
1077
1208
  FROM sessions s
1078
1209
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1079
1210
  WHERE s.project_id = ?
1080
- ORDER BY s.started_at`).all(s.id);if(n.length===0){console.error(c.warn(`Project "${s.name}" has 0 sessions.`));return}let r={clean:0,"fixed_v0.16.1":0,agent:0,manual_alias:0,template_pending:0,programmatic:0,recursive_meta:0,low_signal:0},o=[];for(let u of n){let p={auto_title:u.auto_title,auto_title_source:u.auto_title_source??null,has_alias:u.has_alias===1},m=Ti(p);r[m]+=1,o.push({id:u.id,quality:m})}if(!e.dryRun){let u=t.prepare(`UPDATE sessions
1211
+ ORDER BY s.started_at`).all(n.id);if(s.length===0){console.error(c.warn(`Project "${n.name}" has 0 sessions.`));return}let r={clean:0,"fixed_v0.16.1":0,agent:0,manual_alias:0,template_pending:0,programmatic:0,recursive_meta:0,low_signal:0},o=[];for(let u of s){let m={auto_title:u.auto_title,auto_title_source:u.auto_title_source??null,has_alias:u.has_alias===1},p=Oa(m);r[p]+=1,o.push({id:u.id,quality:p})}if(!e.dryRun){let u=t.prepare(`UPDATE sessions
1081
1212
  SET title_quality = ?,
1082
1213
  title_quality_computed_at = ?
1083
- WHERE id = ?`),p=Date.now();t.transaction(g=>{for(let f of g)u.run(f.quality,p,f.id)})(o)}if(e.json){console.log(JSON.stringify({project:s.name,project_id:s.id,total_sessions:n.length,dry_run:!!e.dryRun,counts:r},null,2));return}let i=n.length;console.log(""),console.log(c.project(`Title quality audit \u2014 project ${c.bold(s.name)}`)),s.decoded_path&&console.log(c.dim(` ${s.decoded_path}`)),console.log(c.dim(` ${i} sessions${e.dryRun?" (DRY RUN \u2014 no DB writes)":""}`)),console.log("");let a=["clean","fixed_v0.16.1","agent","manual_alias","template_pending","programmatic","recursive_meta","low_signal"],d=Math.max(...a.map(u=>u.length));for(let u of a){let p=r[u];if(p===0)continue;let m=(p/i*100).toFixed(1).padStart(5),g=Kf(p,i);console.log(` ${u.padEnd(d)} ${c.bold(String(p).padStart(5))} ${m}% ${g}`)}console.log("");let l=r.template_pending+r.programmatic+r.recursive_meta+r.low_signal;if(l>0){let u=(l/i*100).toFixed(1);console.log(c.dim(` ${l} sessions (${u}%) eligible for cleanup phases L1/L3/L4.`))}else console.log(c.ok(" No cleanup-eligible sessions remain in this project. \u2713"));console.log("")}async function qf(e,t){if(t){let o=e.prepare("SELECT id, name, decoded_path FROM projects WHERE name = ?").get(t);if(o)return o;let i=e.prepare("SELECT id, name, decoded_path FROM projects WHERE name LIKE ? ORDER BY name LIMIT 1").get(`%${t}%`);return i||null}let s=process.cwd(),n=e.prepare("SELECT id, name, decoded_path FROM projects WHERE decoded_path = ?").get(s);if(n)return n;let r=e.prepare("SELECT id, name, decoded_path FROM projects WHERE name = ? LIMIT 1").get(zf(s));return r||null}var va=28;function Kf(e,t){let s=Math.round(e/t*va);return c.dim("\u2588".repeat(s)+"\xB7".repeat(va-s))}Et();v();w();Bn();w();Et();import{readFileSync as t_,existsSync as kr,statSync as s_,readdirSync as n_}from"node:fs";import{join as Fs}from"node:path";import{homedir as r_}from"node:os";var es=["vscode","cursor","windsurf"],o_={vscode:"Code",cursor:"Cursor",windsurf:"Windsurf"};var i_=.7,a_=300*1e3;function c_(e){let t=e?.homeDir??r_(),s=e?.sources??es,n=[];for(let r of s){let o=Fs(t,"Library","Application Support",o_[r],"User","workspaceStorage");kr(o)&&n.push({source:r,root:o})}return n}function l_(e,t){let s=Fs(e,"workspace.json"),n=Fs(e,"state.vscdb");if(!kr(s)||!kr(n))return[];let r;try{let d=JSON.parse(t_(s,"utf8"));if(!d.folder||!d.folder.startsWith("file://"))return[];r=decodeURIComponent(d.folder.replace(/^file:\/\//,""))}catch{return[]}let o;try{o=s_(n).mtime.toISOString()}catch{return[]}let i;try{i=new bs(n,{readonly:!0})}catch{return[]}let a=[];try{let d=i.prepare("SELECT value FROM ItemTable WHERE key = 'terminal.integrated.bufferState' LIMIT 1").get(),l={};if(d?.value)try{l=JSON.parse(d.value)}catch{}let u=l.state??[];if(u.length===0)a.push({workspace_path:r,workspace_storage_dir:e,tab_name:null,cwd_hint:null,last_seen_at:o,source:t});else for(let p of u){let m=p.shellLaunchConfig??{},g=typeof m.name=="string"?m.name.trim():"";a.push({workspace_path:r,workspace_storage_dir:e,tab_name:g||null,cwd_hint:typeof m.cwd=="string"?m.cwd:null,last_seen_at:o,source:t})}}finally{i.close()}return a}function d_(e,t){let s=[],n=0;if(e.cwd&&t.workspace_path){let r=e.cwd,o=t.workspace_path;(r===o||r.startsWith(o+"/")||o.startsWith(r+"/"))&&(n+=.5,s.push("cwd_prefix"))}if(e.started_at&&t.last_seen_at){let r=Date.parse(e.started_at),o=Date.parse(t.last_seen_at);Number.isFinite(r)&&Number.isFinite(o)&&Math.abs(r-o)<=a_&&(n+=.4,s.push("time_window"))}return e.cwd&&t.cwd_hint&&e.cwd===t.cwd_hint&&(n+=.1,s.push("cwd_exact")),t.tab_name&&(n+=.2,s.push("has_name")),n>1&&(n=1),{score:n,matchedOn:s}}var u_=new Set(["zsh","bash","fish","sh","dash","ksh","tcsh","csh","pwsh","powershell","cmd","nu","node","deno","bun","python","python3","ruby","ts-node","tsx","go","cargo","java","php","irb","pry","iex","task","tasks","terminal","copilot","cascade","composer","claude","claude code"]);function p_(e){return u_.has(e.trim().toLowerCase())}function m_(e){let t=e.tab_name?.trim();return!t||p_(t)?null:t}function $a(e){let t=e?.minScore??i_,s=e?.limit,n=_(),r=e?.projectId?" AND s.project_id = ?":"",o=e?.projectId?[e.projectId]:[],i=n.prepare(`SELECT s.id, p.decoded_path AS cwd, s.started_at
1214
+ WHERE id = ?`),m=Date.now();t.transaction(g=>{for(let f of g)u.run(f.quality,m,f.id)})(o)}if(e.json){console.log(JSON.stringify({project:n.name,project_id:n.id,total_sessions:s.length,dry_run:!!e.dryRun,counts:r},null,2));return}let i=s.length;console.log(""),console.log(c.project(`Title quality audit \u2014 project ${c.bold(n.name)}`)),n.decoded_path&&console.log(c.dim(` ${n.decoded_path}`)),console.log(c.dim(` ${i} sessions${e.dryRun?" (DRY RUN \u2014 no DB writes)":""}`)),console.log("");let a=["clean","fixed_v0.16.1","agent","manual_alias","template_pending","programmatic","recursive_meta","low_signal"],d=Math.max(...a.map(u=>u.length));for(let u of a){let m=r[u];if(m===0)continue;let p=(m/i*100).toFixed(1).padStart(5),g=SE(m,i);console.log(` ${u.padEnd(d)} ${c.bold(String(m).padStart(5))} ${p}% ${g}`)}console.log("");let l=r.template_pending+r.programmatic+r.recursive_meta+r.low_signal;if(l>0){let u=(l/i*100).toFixed(1);console.log(c.dim(` ${l} sessions (${u}%) eligible for cleanup phases L1/L3/L4.`))}else console.log(c.ok(" No cleanup-eligible sessions remain in this project. \u2713"));console.log("")}async function bE(e,t){if(t){let o=e.prepare("SELECT id, name, decoded_path FROM projects WHERE name = ?").get(t);if(o)return o;let i=e.prepare("SELECT id, name, decoded_path FROM projects WHERE name LIKE ? ORDER BY name LIMIT 1").get(`%${t}%`);return i||null}let n=process.cwd(),s=e.prepare("SELECT id, name, decoded_path FROM projects WHERE decoded_path = ?").get(n);if(s)return s;let r=e.prepare("SELECT id, name, decoded_path FROM projects WHERE name = ? LIMIT 1").get(EE(n));return r||null}var Yc=28;function SE(e,t){let n=Math.round(e/t*Yc);return c.dim("\u2588".repeat(n)+"\xB7".repeat(Yc-n))}Ct();$();R();Un();R();Ct();import{readFileSync as kE,existsSync as go,statSync as xE,readdirSync as CE}from"node:fs";import{join as is}from"node:path";import{homedir as AE}from"node:os";var gn=["vscode","cursor","windsurf"],LE={vscode:"Code",cursor:"Cursor",windsurf:"Windsurf"};var NE=.7,OE=300*1e3;function vE(e){let t=e?.homeDir??AE(),n=e?.sources??gn,s=[];for(let r of n){let o=is(t,"Library","Application Support",LE[r],"User","workspaceStorage");go(o)&&s.push({source:r,root:o})}return s}function IE(e,t){let n=is(e,"workspace.json"),s=is(e,"state.vscdb");if(!go(n)||!go(s))return[];let r;try{let d=JSON.parse(kE(n,"utf8"));if(!d.folder||!d.folder.startsWith("file://"))return[];r=decodeURIComponent(d.folder.replace(/^file:\/\//,""))}catch{return[]}let o;try{o=xE(s).mtime.toISOString()}catch{return[]}let i;try{i=new Dt(s,{readonly:!0})}catch{return[]}let a=[];try{let d=i.prepare("SELECT value FROM ItemTable WHERE key = 'terminal.integrated.bufferState' LIMIT 1").get(),l={};if(d?.value)try{l=JSON.parse(d.value)}catch{}let u=l.state??[];if(u.length===0)a.push({workspace_path:r,workspace_storage_dir:e,tab_name:null,cwd_hint:null,last_seen_at:o,source:t});else for(let m of u){let p=m.shellLaunchConfig??{},g=typeof p.name=="string"?p.name.trim():"";a.push({workspace_path:r,workspace_storage_dir:e,tab_name:g||null,cwd_hint:typeof p.cwd=="string"?p.cwd:null,last_seen_at:o,source:t})}}finally{i.close()}return a}function ME(e,t){let n=[],s=0;if(e.cwd&&t.workspace_path){let r=e.cwd,o=t.workspace_path;(r===o||r.startsWith(o+"/")||o.startsWith(r+"/"))&&(s+=.5,n.push("cwd_prefix"))}if(e.started_at&&t.last_seen_at){let r=Date.parse(e.started_at),o=Date.parse(t.last_seen_at);Number.isFinite(r)&&Number.isFinite(o)&&Math.abs(r-o)<=OE&&(s+=.4,n.push("time_window"))}return e.cwd&&t.cwd_hint&&e.cwd===t.cwd_hint&&(s+=.1,n.push("cwd_exact")),t.tab_name&&(s+=.2,n.push("has_name")),s>1&&(s=1),{score:s,matchedOn:n}}var DE=new Set(["zsh","bash","fish","sh","dash","ksh","tcsh","csh","pwsh","powershell","cmd","nu","node","deno","bun","python","python3","ruby","ts-node","tsx","go","cargo","java","php","irb","pry","iex","task","tasks","terminal","copilot","cascade","composer","claude","claude code"]);function $E(e){return DE.has(e.trim().toLowerCase())}function PE(e){let t=e.tab_name?.trim();return!t||$E(t)?null:t}function Qc(e){let t=e?.minScore??NE,n=e?.limit,s=E(),r=e?.projectId?" AND s.project_id = ?":"",o=e?.projectId?[e.projectId]:[],i=s.prepare(`SELECT s.id, p.decoded_path AS cwd, s.started_at
1084
1215
  FROM sessions s
1085
1216
  JOIN projects p ON p.id = s.project_id
1086
1217
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1087
- WHERE (sa.alias IS NULL OR sa.alias = '')${r}`).all(...o),a=e?.sources??es,d=c_({sources:a,homeDir:e?.homeDir}),l=[];for(let{source:p,root:m}of d){let g;try{g=n_(m)}catch{continue}for(let f of g){let h=Fs(m,f);l.push(...l_(h,p))}}if(l.length===0)return[];let u=[];for(let p of i){let m=null;for(let f of l){let{score:h,matchedOn:E}=d_(p,f);h<t||(!m||h>m.score)&&(m={entry:f,score:h,matched:E})}if(!m)continue;let g=m_(m.entry);g&&u.push({session_id:p.id,proposed_alias:g,score:m.score,evidence:{source:m.entry.source,workspace_path:m.entry.workspace_path,workspace_storage_dir:m.entry.workspace_storage_dir,tab_name:m.entry.tab_name,cwd_hint:m.entry.cwd_hint,last_seen_at:m.entry.last_seen_at,matched_on:m.matched}})}return u.sort((p,m)=>m.score-p.score||p.session_id.localeCompare(m.session_id)),typeof s=="number"&&s>=0&&u.length>s?u.slice(0,s):u}function Pa(e){let t=[],s=[];for(let n of e){let r=Ps(n.session_id);if(r&&r.trim()!==""){s.push(n);continue}t.push(n)}return{applicable:t,skipped:s}}import{basename as g_}from"node:path";async function ja(e){let t=__(e.source);if(!t){console.error(c.err("Invalid --source. Allowed: vscode | cursor | windsurf | all (default).")),process.exitCode=1;return}let s=h_(e.minScore,.7);if(s===null||s<0||s>1){console.error(c.err("Invalid --min-score. Must be a number in [0, 1].")),process.exitCode=1;return}let n=e.limit?parseInt(e.limit,10):void 0;if(n!==void 0&&(!Number.isFinite(n)||n<0)){console.error(c.err("Invalid --limit. Must be a non-negative integer.")),process.exitCode=1;return}let r=e.project?E_(e.project):void 0;if(e.project&&r===null){console.error(c.err(`No project found matching "${e.project}". Run \`recall projects\`.`)),process.exitCode=1;return}let o=$a({projectId:r??void 0,sources:t,minScore:s,limit:n}),{applicable:i,skipped:a}=Pa(o);if(e.apply){let d=0;for(let l of i)try{Mt(l.session_id,l.proposed_alias),d+=1}catch(u){console.error(c.err(`apply failed for ${l.session_id.slice(0,8)}: ${u.message}`))}if(e.json){console.log(JSON.stringify({mode:"apply",sources:t,min_score:s,project_id:r??null,applied:d,skipped:a.length,proposals:i.map(Fa)},null,2));return}console.log(""),console.log(c.ok(`Applied ${d} alias backfill${d===1?"":"s"}`+(a.length?` \xB7 skipped ${a.length} (alias appeared since proposal)`:""))),console.log("");return}if(e.json){console.log(JSON.stringify({mode:"dry-run",sources:t,min_score:s,project_id:r??null,proposals:i.map(Fa),skipped_due_to_existing_alias:a.length},null,2));return}f_(i,t,s,a.length)}function f_(e,t,s,n){if(console.log(""),console.log(c.project(`recall import-vscode-state \xB7 sources=${t.join("+")} min-score=${s.toFixed(2)} \xB7 DRY RUN`)),console.log(c.dim(" Pass --apply to write proposals via setAlias().")),console.log(""),e.length===0){console.log(c.warn(" No proposals \u2014 either no matching workspaces or every candidate session already has an alias.")),n>0&&console.log(c.dim(` ${n} sessions were skipped because they already have aliases.`)),console.log("");return}for(let r of e){let o=c.bold(r.session_id.slice(0,8)),i=c.bold(r.score.toFixed(2)),a=c.dim(`[${r.evidence.source}]`),d=r.evidence.matched_on.join("+");console.log(` ${o} score=${i} ${a} matched=${d}`),console.log(` proposed alias: ${c.bold(r.proposed_alias)}`),console.log(` workspace: ${c.dim(K(r.evidence.workspace_path,70))}`),r.evidence.tab_name&&console.log(` tab name: ${r.evidence.tab_name}`),r.evidence.cwd_hint&&console.log(` cwd hint: ${c.dim(K(r.evidence.cwd_hint,70))}`),console.log(` last seen: ${c.dim(r.evidence.last_seen_at)}`),console.log("")}console.log(c.dim(` ${e.length} proposal${e.length===1?"":"s"}`+(n>0?` \xB7 ${n} skipped (existing alias)`:"")+" \xB7 pass --apply to write via setAlias()")),console.log("")}function Fa(e){return e}function __(e){if(!e||e==="all")return es;let t=e.split(",").map(n=>n.trim().toLowerCase()).filter(Boolean),s=[];for(let n of t)if(es.includes(n))s.push(n);else return null;return s.length>0?s:null}function h_(e,t){if(e===void 0)return t;let s=Number.parseFloat(e);return Number.isFinite(s)?s:null}function E_(e){let t=_(),s=t.prepare("SELECT id FROM projects WHERE name = ? LIMIT 1").get(e);if(s)return s.id;let n=t.prepare("SELECT id FROM projects WHERE name LIKE ? ORDER BY name LIMIT 1").get(`%${e}%`);if(n)return n.id;let r=g_(e);if(r&&r!==e){let o=t.prepare("SELECT id FROM projects WHERE name = ? LIMIT 1").get(r);if(o)return o.id}return null}w();import{existsSync as Ua,mkdirSync as b_,readFileSync as S_,writeFileSync as y_}from"node:fs";import{homedir as w_}from"node:os";import{join as Ba}from"node:path";import{z as je}from"zod";function Ha(){return process.env.RECALL_HOME??Ba(w_(),".recall")}function T_(){let e=Ha();Ua(e)||b_(e,{recursive:!0})}function Wa(){return Ba(Ha(),"config.json")}var Xa=je.object({enabled:je.boolean().default(!1),model:je.string().optional(),ratePerMinute:je.number().int().min(1).max(600).default(30),lastProcessedSessionId:je.string().nullable().default(null),backfillPaused:je.boolean().default(!1),autoExtractEnabled:je.boolean().default(!1),autoExtractIntervalMinutes:je.number().int().min(5).max(720).default(60),autoExtractBatchSize:je.number().int().min(1).max(20).default(1),autoResumeWorker:je.boolean().default(!1)}),js={enabled:!1,ratePerMinute:30,lastProcessedSessionId:null,backfillPaused:!1,autoExtractEnabled:!1,autoExtractIntervalMinutes:60,autoExtractBatchSize:1,autoResumeWorker:!1};function Ja(){let e=Wa();if(!Ua(e))return{};try{return JSON.parse(S_(e,"utf8"))}catch(t){return console.error("[semantic-config] failed to parse config.json, using defaults:",t),{}}}function ve(){let e=Ja().semantic;if(!e)return{...js};let t=Xa.safeParse({...js,...e});return t.success?t.data:{...js}}function Ue(e){T_();let t=Ja(),s=Xa.parse({...js,...t.semantic??{},...e}),n={...t,semantic:s};return y_(Wa(),JSON.stringify(n,null,2)),R_(s.enabled),s}function R_(e){try{_().prepare(`INSERT INTO app_settings(key, value) VALUES ('semantic_enabled', ?)
1088
- ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(e?"1":"0")}catch(t){let s=t instanceof Error?t.message:String(t);console.error(`[semantic-config] failed to sync semantic_enabled: ${s}`)}}w();Be();import{existsSync as J_,mkdirSync as G_,writeFileSync as Y_}from"node:fs";import{homedir as z_}from"node:os";import{join as Lr}from"node:path";var q_=1,K_=12e3,V_=3,Z_=[];function Q_(){return process.env.RECALL_HOME??Lr(z_(),".recall")}function ic(){return Lr(Q_(),"semantic")}function eh(){let e=ic();J_(e)||G_(e,{recursive:!0})}function th(e){let t=_(),s=t.prepare(`SELECT s.id, s.message_count, s.first_user_message,
1218
+ WHERE (sa.alias IS NULL OR sa.alias = '')${r}`).all(...o),a=e?.sources??gn,d=vE({sources:a,homeDir:e?.homeDir}),l=[];for(let{source:m,root:p}of d){let g;try{g=CE(p)}catch{continue}for(let f of g){let h=is(p,f);l.push(...IE(h,m))}}if(l.length===0)return[];let u=[];for(let m of i){let p=null;for(let f of l){let{score:h,matchedOn:_}=ME(m,f);h<t||(!p||h>p.score)&&(p={entry:f,score:h,matched:_})}if(!p)continue;let g=PE(p.entry);g&&u.push({session_id:m.id,proposed_alias:g,score:p.score,evidence:{source:p.entry.source,workspace_path:p.entry.workspace_path,workspace_storage_dir:p.entry.workspace_storage_dir,tab_name:p.entry.tab_name,cwd_hint:p.entry.cwd_hint,last_seen_at:p.entry.last_seen_at,matched_on:p.matched}})}return u.sort((m,p)=>p.score-m.score||m.session_id.localeCompare(p.session_id)),typeof n=="number"&&n>=0&&u.length>n?u.slice(0,n):u}function Zc(e){let t=[],n=[];for(let s of e){let r=os(s.session_id);if(r&&r.trim()!==""){n.push(s);continue}t.push(s)}return{applicable:t,skipped:n}}import{basename as FE}from"node:path";async function tl(e){let t=UE(e.source);if(!t){console.error(c.err("Invalid --source. Allowed: vscode | cursor | windsurf | all (default).")),process.exitCode=1;return}let n=BE(e.minScore,.7);if(n===null||n<0||n>1){console.error(c.err("Invalid --min-score. Must be a number in [0, 1].")),process.exitCode=1;return}let s=e.limit?parseInt(e.limit,10):void 0;if(s!==void 0&&(!Number.isFinite(s)||s<0)){console.error(c.err("Invalid --limit. Must be a non-negative integer.")),process.exitCode=1;return}let r=e.project?HE(e.project):void 0;if(e.project&&r===null){console.error(c.err(`No project found matching "${e.project}". Run \`recall projects\`.`)),process.exitCode=1;return}let o=Qc({projectId:r??void 0,sources:t,minScore:n,limit:s}),{applicable:i,skipped:a}=Zc(o);if(e.apply){let d=0;for(let l of i)try{Ht(l.session_id,l.proposed_alias),d+=1}catch(u){console.error(c.err(`apply failed for ${l.session_id.slice(0,8)}: ${u.message}`))}if(e.json){console.log(JSON.stringify({mode:"apply",sources:t,min_score:n,project_id:r??null,applied:d,skipped:a.length,proposals:i.map(el)},null,2));return}console.log(""),console.log(c.ok(`Applied ${d} alias backfill${d===1?"":"s"}`+(a.length?` \xB7 skipped ${a.length} (alias appeared since proposal)`:""))),console.log("");return}if(e.json){console.log(JSON.stringify({mode:"dry-run",sources:t,min_score:n,project_id:r??null,proposals:i.map(el),skipped_due_to_existing_alias:a.length},null,2));return}jE(i,t,n,a.length)}function jE(e,t,n,s){if(console.log(""),console.log(c.project(`recall import-vscode-state \xB7 sources=${t.join("+")} min-score=${n.toFixed(2)} \xB7 DRY RUN`)),console.log(c.dim(" Pass --apply to write proposals via setAlias().")),console.log(""),e.length===0){console.log(c.warn(" No proposals \u2014 either no matching workspaces or every candidate session already has an alias.")),s>0&&console.log(c.dim(` ${s} sessions were skipped because they already have aliases.`)),console.log("");return}for(let r of e){let o=c.bold(r.session_id.slice(0,8)),i=c.bold(r.score.toFixed(2)),a=c.dim(`[${r.evidence.source}]`),d=r.evidence.matched_on.join("+");console.log(` ${o} score=${i} ${a} matched=${d}`),console.log(` proposed alias: ${c.bold(r.proposed_alias)}`),console.log(` workspace: ${c.dim(K(r.evidence.workspace_path,70))}`),r.evidence.tab_name&&console.log(` tab name: ${r.evidence.tab_name}`),r.evidence.cwd_hint&&console.log(` cwd hint: ${c.dim(K(r.evidence.cwd_hint,70))}`),console.log(` last seen: ${c.dim(r.evidence.last_seen_at)}`),console.log("")}console.log(c.dim(` ${e.length} proposal${e.length===1?"":"s"}`+(s>0?` \xB7 ${s} skipped (existing alias)`:"")+" \xB7 pass --apply to write via setAlias()")),console.log("")}function el(e){return e}function UE(e){if(!e||e==="all")return gn;let t=e.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean),n=[];for(let s of t)if(gn.includes(s))n.push(s);else return null;return n.length>0?n:null}function BE(e,t){if(e===void 0)return t;let n=Number.parseFloat(e);return Number.isFinite(n)?n:null}function HE(e){let t=E(),n=t.prepare("SELECT id FROM projects WHERE name = ? LIMIT 1").get(e);if(n)return n.id;let s=t.prepare("SELECT id FROM projects WHERE name LIKE ? ORDER BY name LIMIT 1").get(`%${e}%`);if(s)return s.id;let r=FE(e);if(r&&r!==e){let o=t.prepare("SELECT id FROM projects WHERE name = ? LIMIT 1").get(r);if(o)return o.id}return null}Un();R();D();import{existsSync as nl,mkdirSync as WE,readFileSync as XE,writeFileSync as GE}from"node:fs";import{homedir as JE}from"node:os";import{join as sl}from"node:path";import{z as Je}from"zod";function rl(){return process.env.RECALL_HOME??sl(JE(),".recall")}function zE(){let e=rl();nl(e)||WE(e,{recursive:!0})}function ol(){return sl(rl(),"config.json")}var il=Je.object({enabled:Je.boolean().default(!1),model:Je.string().optional(),ratePerMinute:Je.number().int().min(1).max(600).default(30),lastProcessedSessionId:Je.string().nullable().default(null),backfillPaused:Je.boolean().default(!1),autoExtractEnabled:Je.boolean().default(!1),autoExtractIntervalMinutes:Je.number().int().min(5).max(720).default(60),autoExtractBatchSize:Je.number().int().min(1).max(20).default(1),autoResumeWorker:Je.boolean().default(!1)}),as={enabled:!1,ratePerMinute:30,lastProcessedSessionId:null,backfillPaused:!1,autoExtractEnabled:!1,autoExtractIntervalMinutes:60,autoExtractBatchSize:1,autoResumeWorker:!1};function al(){let e=ol();if(!nl(e))return{};try{return JSON.parse(XE(e,"utf8"))}catch(t){return console.error("[semantic-config] failed to parse config.json, using defaults:",t),{}}}function ye(){let e=al().semantic;if(!e)return{...as};let t=il.safeParse({...as,...e});return t.success?t.data:{...as}}function ze(e,t="unknown"){zE();let n=al(),s=il.parse({...as,...n.semantic??{},...e}),r={...n,semantic:s};return GE(ol(),JSON.stringify(r,null,2)),YE(s.enabled,t),s}function YE(e,t="unknown"){let n=e?"1":"0",s=null;try{E().prepare(`INSERT INTO app_settings(key, value) VALUES ('semantic_enabled', ?)
1219
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(n),console.log(`[semantic-config] gate flip: source=${t} value=${n} path=pooled at=${new Date().toISOString()}`);return}catch(r){s=r;let o=r instanceof Error?r.message:String(r);console.error(`[semantic-config] pooled getDb() failed while syncing semantic_enabled=${n}: ${o} \u2014 falling back to raw connection`)}try{let r=new Dt(ee);try{r.exec(`CREATE TABLE IF NOT EXISTS app_settings (
1220
+ key TEXT PRIMARY KEY,
1221
+ value TEXT NOT NULL
1222
+ );`),r.prepare(`INSERT INTO app_settings(key, value) VALUES ('semantic_enabled', ?)
1223
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(n),console.log(`[semantic-config] gate flip: source=${t} value=${n} path=fallback at=${new Date().toISOString()}`)}finally{r.close()}}catch(r){let o=r instanceof Error?r.message:String(r);console.error(`[semantic-config] raw-connection fallback ALSO failed for semantic_enabled=${n}: ${o} (original: ${s instanceof Error?s.message:String(s)})`)}}R();Ye();import{existsSync as fb,mkdirSync as _b,writeFileSync as hb}from"node:fs";import{homedir as Eb}from"node:os";import{join as _o}from"node:path";var bb=1,Sb=12e3,wb=3,yb=[];function Tb(){return process.env.RECALL_HOME??_o(Eb(),".recall")}function yl(){return _o(Tb(),"semantic")}function Rb(){let e=yl();fb(e)||_b(e,{recursive:!0})}function kb(e){let t=E(),n=t.prepare(`SELECT s.id, s.message_count, s.first_user_message,
1089
1224
  p.name AS project,
1090
1225
  NULLIF(sa.alias, '') AS alias
1091
1226
  FROM sessions s
1092
1227
  JOIN projects p ON p.id = s.project_id
1093
1228
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1094
- WHERE s.id = ?`).get(e);if(!s)return null;let n=t.prepare(`SELECT role, content_text
1229
+ WHERE s.id = ?`).get(e);if(!n)return null;let s=t.prepare(`SELECT role, content_text
1095
1230
  FROM messages
1096
1231
  WHERE session_id = ? AND is_sidechain = 0
1097
- ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let i of n){if(!i.content_text)continue;let a=i.role??"system",d=i.content_text.replace(/```[\s\S]*?```/g,"[code]").replace(/<[^>]+>[\s\S]*?<\/[^>]+>/g,"").trim();if(!d)continue;let l=d.length>1500?d.slice(0,1500)+"\u2026":d,u=`${a}: ${l}`;if(o+u.length>K_)break;r.push(u),o+=u.length}return{id:s.id,alias:s.alias,project:s.project,firstUserMessage:s.first_user_message,excerpt:r.join(`
1232
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let i of s){if(!i.content_text)continue;let a=i.role??"system",d=i.content_text.replace(/```[\s\S]*?```/g,"[code]").replace(/<[^>]+>[\s\S]*?<\/[^>]+>/g,"").trim();if(!d)continue;let l=d.length>1500?d.slice(0,1500)+"\u2026":d,u=`${a}: ${l}`;if(o+u.length>Sb)break;r.push(u),o+=u.length}return{id:n.id,alias:n.alias,project:n.project,firstUserMessage:n.first_user_message,excerpt:r.join(`
1098
1233
 
1099
- `),messageCount:s.message_count}}function sh(e){return["You are summarizing a Claude Code session for a local semantic-search index. The summary will be stored as plain text and matched against future natural-language queries.","",`Session: ${e.alias??e.id}`,`Project: ${e.project}`,e.firstUserMessage?`Opening prompt: ${e.firstUserMessage}`:"","","Transcript excerpt (truncated):","---",e.excerpt,"---","","Output a single JSON object on one line, with no Markdown fences and no commentary:",'{"summary": "<3 sentences describing what the user was trying to do, what was built or debugged, and the outcome>", "keywords": ["<concept>", "<technology>", "<problem>", ...]}',"","Constraints:","- summary: 3 sentences, plain prose, no bullet points",'- keywords: 10\u201315 lowercase tokens, multi-word entries hyphenated (e.g. "memory-leak"); no duplicates; no generic words like "code" or "session"',"- Output JSON only. Do not echo this prompt."].filter(Boolean).join(`
1100
- `)}function nh(e){let t=e.trim();try{let o=JSON.parse(t);typeof o.result=="string"&&(t=o.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let s=t.indexOf("{"),n=t.lastIndexOf("}");if(s===-1||n===-1||n<=s)return null;let r=t.slice(s,n+1);try{let o=JSON.parse(r),i=typeof o.summary=="string"?o.summary.trim():"",d=(Array.isArray(o.keywords)?o.keywords:[]).filter(l=>typeof l=="string").map(l=>l.trim().toLowerCase()).filter(l=>l.length>0&&l.length<64);return!i||d.length===0?null:{summary:i,keywords:Array.from(new Set(d)).slice(0,20)}}catch{return null}}function rh(e){let t=_(),s=e.keywords.join(",");t.prepare(`INSERT INTO session_semantic
1234
+ `),messageCount:n.message_count}}function xb(e){return["You are summarizing a Claude Code session for a local semantic-search index. The summary will be stored as plain text and matched against future natural-language queries.","",`Session: ${e.alias??e.id}`,`Project: ${e.project}`,e.firstUserMessage?`Opening prompt: ${e.firstUserMessage}`:"","","Transcript excerpt (truncated):","---",e.excerpt,"---","","Output a single JSON object on one line, with no Markdown fences and no commentary:",'{"summary": "<3 sentences describing what the user was trying to do, what was built or debugged, and the outcome>", "keywords": ["<concept>", "<technology>", "<problem>", ...]}',"","Constraints:","- summary: 3 sentences, plain prose, no bullet points",'- keywords: 10\u201315 lowercase tokens, multi-word entries hyphenated (e.g. "memory-leak"); no duplicates; no generic words like "code" or "session"',"- Output JSON only. Do not echo this prompt."].filter(Boolean).join(`
1235
+ `)}function Cb(e){let t=e.trim();try{let o=JSON.parse(t);typeof o.result=="string"&&(t=o.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let n=t.indexOf("{"),s=t.lastIndexOf("}");if(n===-1||s===-1||s<=n)return null;let r=t.slice(n,s+1);try{let o=JSON.parse(r),i=typeof o.summary=="string"?o.summary.trim():"",d=(Array.isArray(o.keywords)?o.keywords:[]).filter(l=>typeof l=="string").map(l=>l.trim().toLowerCase()).filter(l=>l.length>0&&l.length<64);return!i||d.length===0?null:{summary:i,keywords:Array.from(new Set(d)).slice(0,20)}}catch{return null}}function Ab(e){let t=E(),n=e.keywords.join(",");t.prepare(`INSERT INTO session_semantic
1101
1236
  (session_id, summary, keywords, model, source_message_count, generated_at)
1102
1237
  VALUES (@session_id, @summary, @keywords, @model, @source_message_count, @generated_at)
1103
1238
  ON CONFLICT(session_id) DO UPDATE SET
@@ -1105,23 +1240,25 @@ show full content: recall paste --show <id>
1105
1240
  keywords = excluded.keywords,
1106
1241
  model = excluded.model,
1107
1242
  source_message_count = excluded.source_message_count,
1108
- generated_at = excluded.generated_at`).run({session_id:e.sessionId,summary:e.summary,keywords:s,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt}),eh();let n=Lr(ic(),`${e.sessionId}.json`);Y_(n,JSON.stringify({version:q_,session_id:e.sessionId,summary:e.summary,keywords:e.keywords,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt},null,2))}var Bs=null;function oh(){let t=ve().ratePerMinute,s=t/6e4;return(!Bs||Bs.capacity!==t)&&(Bs={tokens:t,capacity:t,refillPerMs:s,lastRefill:Date.now()}),Bs}function ih(e){let t=Date.now(),s=t-e.lastRefill;s>0&&(e.tokens=Math.min(e.capacity,e.tokens+s*e.refillPerMs),e.lastRefill=t)}async function ah(e){for(;;){if(e?.aborted)throw new Error("aborted");let t=oh();if(ih(t),t.tokens>=1){t.tokens-=1;return}let s=1-t.tokens,n=Math.max(50,Math.ceil(s/t.refillPerMs));await new Promise(r=>setTimeout(r,Math.min(n,5e3)))}}async function ch(e,t={}){let s=ve();if(!s.enabled)return{sessionId:e,ok:!1,reason:"disabled"};if(!ne())return{sessionId:e,ok:!1,reason:"claude-cli-missing"};let n=th(e);if(!n)return{sessionId:e,ok:!1,reason:"session-not-found"};if(n.messageCount<V_)return{sessionId:e,ok:!1,reason:"too-short"};if(!n.excerpt.trim())return{sessionId:e,ok:!1,reason:"empty-excerpt"};await ah(t.signal);let r=sh(n),o=await bt(r,Z_,{model:s.model});if(!o.success)return{sessionId:e,ok:!1,reason:`claude-cli-exit-${o.exitCode??"null"}`,model:s.model??null};let i=nh(o.stdout);return i?(rh({sessionId:n.id,summary:i.summary,keywords:i.keywords,model:s.model??null,sourceMessageCount:n.messageCount,generatedAt:new Date().toISOString()}),{sessionId:e,ok:!0,model:s.model??null}):{sessionId:e,ok:!1,reason:"parse-failed",model:s.model??null}}async function Hs(e={}){let t=ve();if(!t.enabled)return{total:0,processed:0,ok:0,failed:0,currentSessionId:null};let s=_(),r={limit:e.limit??1e3},o="s.message_count >= 3";e.force||(o+=" AND ss.session_id IS NULL"),typeof e.projectId=="number"&&(o+=" AND s.project_id = @projectId",r.projectId=e.projectId),t.lastProcessedSessionId&&e.force;let i=s.prepare(`SELECT s.id
1243
+ generated_at = excluded.generated_at`).run({session_id:e.sessionId,summary:e.summary,keywords:n,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt}),Rb();let s=_o(yl(),`${e.sessionId}.json`);hb(s,JSON.stringify({version:bb,session_id:e.sessionId,summary:e.summary,keywords:e.keywords,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt},null,2))}var ls=null;function Lb(){let t=ye().ratePerMinute,n=t/6e4;return(!ls||ls.capacity!==t)&&(ls={tokens:t,capacity:t,refillPerMs:n,lastRefill:Date.now()}),ls}function Nb(e){let t=Date.now(),n=t-e.lastRefill;n>0&&(e.tokens=Math.min(e.capacity,e.tokens+n*e.refillPerMs),e.lastRefill=t)}async function Ob(e){for(;;){if(e?.aborted)throw new Error("aborted");let t=Lb();if(Nb(t),t.tokens>=1){t.tokens-=1;return}let n=1-t.tokens,s=Math.max(50,Math.ceil(n/t.refillPerMs));await new Promise(r=>setTimeout(r,Math.min(s,5e3)))}}async function vb(e,t={}){let n=ye();if(!n.enabled)return{sessionId:e,ok:!1,reason:"disabled"};if(!ie())return{sessionId:e,ok:!1,reason:"claude-cli-missing"};let s=kb(e);if(!s)return{sessionId:e,ok:!1,reason:"session-not-found"};if(s.messageCount<wb)return{sessionId:e,ok:!1,reason:"too-short"};if(!s.excerpt.trim())return{sessionId:e,ok:!1,reason:"empty-excerpt"};await Ob(t.signal);let r=xb(s),o=await At(r,yb,{model:n.model});if(!o.success)return{sessionId:e,ok:!1,reason:`claude-cli-exit-${o.exitCode??"null"}`,model:n.model??null};let i=Cb(o.stdout);return i?(Ab({sessionId:s.id,summary:i.summary,keywords:i.keywords,model:n.model??null,sourceMessageCount:s.messageCount,generatedAt:new Date().toISOString()}),{sessionId:e,ok:!0,model:n.model??null}):{sessionId:e,ok:!1,reason:"parse-failed",model:n.model??null}}async function ds(e={}){let t=ye();if(!t.enabled)return{total:0,processed:0,ok:0,failed:0,currentSessionId:null};let n=E(),r={limit:e.limit??1e3},o="s.message_count >= 3";e.force||(o+=" AND ss.session_id IS NULL"),typeof e.projectId=="number"&&(o+=" AND s.project_id = @projectId",r.projectId=e.projectId),t.lastProcessedSessionId&&e.force;let i=n.prepare(`SELECT s.id
1109
1244
  FROM sessions s
1110
1245
  LEFT JOIN session_semantic ss ON ss.session_id = s.id
1111
1246
  WHERE ${o}
1112
1247
  ORDER BY COALESCE(s.started_at, '') ASC, s.id ASC
1113
- LIMIT @limit`).all(r),a={total:i.length,processed:0,ok:0,failed:0,currentSessionId:null};e.onProgress?.(a);for(let{id:d}of i){if(e.signal?.aborted||ve().backfillPaused)break;a.currentSessionId=d,e.onProgress?.({...a});try{(await ch(d,{signal:e.signal})).ok?a.ok+=1:a.failed+=1}catch(u){a.failed+=1,console.error("[semantic.backfill] failed for",d,u)}a.processed+=1,Ue({lastProcessedSessionId:d}),e.onProgress?.({...a})}return a.currentSessionId=null,e.onProgress?.({...a}),a}function ac(){let e=ve(),t=_(),s=t.prepare("SELECT COUNT(*) AS n FROM sessions WHERE message_count >= 3").get().n,n=t.prepare("SELECT COUNT(*) AS n FROM session_semantic").get().n;return{enabled:e.enabled,claudeCliAvailable:ne(),ratePerMinute:e.ratePerMinute,model:e.model??null,totalSessions:s,processedSessions:n,pendingSessions:Math.max(0,s-n),lastProcessedSessionId:e.lastProcessedSessionId,backfillPaused:e.backfillPaused}}Be();Js();yt();w();yt();w();function gc(e){return e.replace(/```json[\s\S]*?```/g,"[tool-call]").replace(/\{[\s\S]{200,}?\}/g,"[json-object]")}function Th(e){let t=[],o=0;for(;o<e.length;){let i=[],a=0;for(;i.length<5&&o<e.length;){let l=e[o],u=gc(l.content_text??"");if(a+u.length>2e3&&i.length>=3)break;i.push(l),a+=u.length,o++}if(i.length===0)break;let d=i.map(l=>{let u=l.role??"system",p=gc(l.content_text??"");return`[${u}] ${p}`}).join(`
1114
-
1115
- `);t.push({messageUuids:i.map(l=>l.uuid),text:d}),o<e.length&&i.length>=3&&(o=Math.max(o-1,o-1))}return t}function $r(e){let s=_().prepare("SELECT uuid, role, content_text FROM messages WHERE session_id = ? AND is_sidechain = 0 AND content_text IS NOT NULL ORDER BY rowid").all(e);return Th(s)}var Rh=!1,xh=null;var kh=new Set;function Ch(){return _().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}function fc(){return{running:Rh,queueDepth:Ch(),lastProcessedAt:xh,blacklistedCount:kh.size}}w();de();async function bc(e,t){let s=(e??"status").toLowerCase();if(s==="on"||s==="enable"){if(!ne()){console.error("claude CLI not found on PATH. Install Claude Code first, then re-run."),process.exitCode=1;return}let m={enabled:!0};t.rate&&(m.ratePerMinute=Number(t.rate)),t.model&&(m.model=t.model);let g=Ue(m);console.log("Semantic search: ENABLED"),console.log(` Rate: ${g.ratePerMinute}/min`),g.model&&console.log(` Model: ${g.model}`),console.log(""),console.log("Disclosure: enabling semantic search sends condensed session"),console.log("summaries to Claude via your local `claude` CLI using your existing"),console.log("plan. New sessions are summarized on close. Run `recall semantic backfill`"),console.log("to summarize the existing archive.");return}if(s==="off"||s==="disable"){Ue({enabled:!1}),console.log("Semantic search: DISABLED"),console.log("Existing summaries are kept in the database; the pipeline will not run.");return}if(s==="pause"){Ue({backfillPaused:!0}),console.log("Backfill paused. Resume with `recall semantic resume`.");return}if(s==="resume"){Ue({backfillPaused:!1}),console.log("Backfill resumed.");return}if(s==="backfill"){let m=ve();if(!m.enabled){console.error("Semantic search is disabled. Run `recall semantic on` first."),process.exitCode=1;return}m.backfillPaused&&Ue({backfillPaused:!1});let g=t.limit?Math.max(1,Number(t.limit)):1e3,f=!!t.force;console.log(`Backfilling up to ${g} sessions${f?" (force)":""}\u2026`);let h=Date.now(),E=0,b=await Hs({limit:g,force:f,onProgress:R=>{if(R.processed===E)return;E=R.processed;let T=R.total>0?` (${Math.round(R.processed/R.total*100)}%)`:"";process.stdout.write(`\r ${R.processed}/${R.total}${T} ok=${R.ok} failed=${R.failed} `)}});process.stdout.write(`
1116
- `);let S=((Date.now()-h)/1e3).toFixed(1);console.log(`Done in ${S}s \u2014 processed=${b.processed} ok=${b.ok} failed=${b.failed}`);return}if(s==="install"){let{ensureTransformersInstalled:m}=await Promise.resolve().then(()=>(Ec(),hc)),g=await m();if(!g.ok){console.error("Failed to install @huggingface/transformers:"),console.error(` ${g.error}`),console.error("Vector search not enabled. Other features unaffected."),process.exitCode=1;return}g.action==="installed"&&console.log("Installed @huggingface/transformers."),St()?console.log("Model already installed."):(console.log("Downloading bge-base-en-v1.5 (~110MB)..."),await Or((f,h,E)=>{let b=E>0?Math.round(h/E*100):0;process.stdout.write(`\r ${f}: ${b}% `)}),process.stdout.write(`
1117
- `)),console.log("Loading embedder...");try{await nt(),console.log("Done. Vector search is now active.")}catch(f){console.log("Model files installed."),console.log(` Embedder load skipped: ${f instanceof Error?f.message.split(`
1118
- `)[0]:String(f)}`),console.log(" Vector features remain available; runtime load happens on first use.")}return}if(s==="uninstall"){vr(),console.log("Model removed. Vector search will fall back to keyword search.");return}if(s==="auto-extract"){let m=t._autoExtractAction;if(m==="on"||m==="enable"){let g=Ue({autoExtractEnabled:!0});console.log("Auto-extract: ENABLED"),console.log(` Cadence: 1 batch every ${g.autoExtractIntervalMinutes} minutes`),console.log(` Batch size: ${g.autoExtractBatchSize} session(s) per tick`),console.log(" Honors your 5-hour Claude plan window \u2014 at 60min/1, that is"),console.log(" ~5 extractions per window, leaving 95%+ of the rate budget free."),console.log(" The daemon will start nibbling through un-extracted sessions on"),console.log(" the next tick (within 15 minutes). Status: `recall semantic status`.");return}if(m==="off"||m==="disable"){Ue({autoExtractEnabled:!1}),console.log("Auto-extract: DISABLED. The daemon will stop running extract-outputs.");return}console.error("Usage: recall semantic auto-extract <on|off>"),process.exitCode=1;return}if(s==="reindex"){if(await Ne("Vector reindex"),!Ze().loaded){if(!St()){console.error("Model not installed. Run `recall semantic install` first."),process.exitCode=1;return}await nt()}let m=_(),g=m.prepare(`
1248
+ LIMIT @limit`).all(r),a={total:i.length,processed:0,ok:0,failed:0,currentSessionId:null};e.onProgress?.(a);for(let{id:d}of i){if(e.signal?.aborted||ye().backfillPaused)break;a.currentSessionId=d,e.onProgress?.({...a});try{(await vb(d,{signal:e.signal})).ok?a.ok+=1:a.failed+=1}catch(u){a.failed+=1,console.error("[semantic.backfill] failed for",d,u)}a.processed+=1,ze({lastProcessedSessionId:d},"pipeline"),e.onProgress?.({...a})}return a.currentSessionId=null,e.onProgress?.({...a}),a}function Tl(){let e=ye(),t=E(),n=t.prepare("SELECT COUNT(*) AS n FROM sessions WHERE message_count >= 3").get().n,s=t.prepare("SELECT COUNT(*) AS n FROM session_semantic").get().n;return{enabled:e.enabled,claudeCliAvailable:ie(),ratePerMinute:e.ratePerMinute,model:e.model??null,totalSessions:n,processedSessions:s,pendingSessions:Math.max(0,n-s),lastProcessedSessionId:e.lastProcessedSessionId,backfillPaused:e.backfillPaused}}Ye();_n();Pe();Ss();Ao();R();ge();No();async function ld(e,t){let n=(e??"status").toLowerCase();if(n==="on"||n==="enable"){if(!ie()){console.error("claude CLI not found on PATH. Install Claude Code first, then re-run."),process.exitCode=1;return}let p={enabled:!0};t.rate&&(p.ratePerMinute=Number(t.rate)),t.model&&(p.model=t.model);let g=ze(p,"cli");console.log("Semantic search: ENABLED"),console.log(` Rate: ${g.ratePerMinute}/min`),g.model&&console.log(` Model: ${g.model}`),console.log(""),console.log("Disclosure: enabling semantic search sends condensed session"),console.log("summaries to Claude via your local `claude` CLI using your existing"),console.log("plan. New sessions are summarized on close. Run `recall semantic backfill`"),console.log("to summarize the existing archive.");return}if(n==="off"||n==="disable"){ze({enabled:!1},"cli"),console.log("Semantic search: DISABLED"),console.log("Existing summaries are kept in the database; the pipeline will not run.");return}if(n==="pause"){ze({backfillPaused:!0},"cli"),console.log("Backfill paused. Resume with `recall semantic resume`.");return}if(n==="resume"){ze({backfillPaused:!1},"cli"),console.log("Backfill resumed.");return}if(n==="backfill"){let p=ye();if(!p.enabled){console.error("Semantic search is disabled. Run `recall semantic on` first."),process.exitCode=1;return}p.backfillPaused&&ze({backfillPaused:!1},"cli");let g=t.limit?Math.max(1,Number(t.limit)):1e3,f=!!t.force;console.log(`Backfilling up to ${g} sessions${f?" (force)":""}\u2026`);let h=Date.now(),_=0,b=await ds({limit:g,force:f,onProgress:w=>{if(w.processed===_)return;_=w.processed;let y=w.total>0?` (${Math.round(w.processed/w.total*100)}%)`:"";process.stdout.write(`\r ${w.processed}/${w.total}${y} ok=${w.ok} failed=${w.failed} `)}});process.stdout.write(`
1249
+ `);let S=((Date.now()-h)/1e3).toFixed(1);console.log(`Done in ${S}s \u2014 processed=${b.processed} ok=${b.ok} failed=${b.failed}`);return}if(n==="install"){let p=(process.env.RECALL_EMBEDDER_BACKEND??"").trim().toLowerCase();if(p==="llama"||p==="llamacpp"){let{ensureLlamaCppInstalled:f}=await Promise.resolve().then(()=>($l(),Dl)),h=await f();if(!h.ok){console.error("Failed to install node-llama-cpp:"),console.error(` ${h.error}`),console.error("Vector search not enabled. Other features unaffected."),process.exitCode=1;return}h.action==="installed"&&console.log("Installed node-llama-cpp.");let{isGgufModelInstalled:_,downloadGgufModel:b,verifyGgufModelHash:S}=await Promise.resolve().then(()=>(ks(),Rs));if(_()){let w=await S();w.ok?console.log("GGUF model already installed and verified."):(console.log(`Re-downloading GGUF model (existing file ${w.reason}).`),await b((y,T)=>{let B=T>0?Math.round(y/T*100):0;process.stdout.write(`\r bge-base-en-v1.5-q8_0.gguf: ${B}% `)}),process.stdout.write(`
1250
+ `))}else console.log("Downloading bge-base-en-v1.5-q8_0.gguf (~113MB)..."),await b((w,y)=>{let T=y>0?Math.round(w/y*100):0;process.stdout.write(`\r bge-base-en-v1.5-q8_0.gguf: ${T}% `)}),process.stdout.write(`
1251
+ `);console.log("Loading embedder (llamacpp backend)...")}else{let{ensureTransformersInstalled:f}=await Promise.resolve().then(()=>(Kl(),Vl)),h=await f();if(!h.ok){console.error("Failed to install @huggingface/transformers:"),console.error(` ${h.error}`),console.error("Vector search not enabled. Other features unaffected."),process.exitCode=1;return}h.action==="installed"&&console.log("Installed @huggingface/transformers."),rt()?console.log("Model already installed."):(console.log("Downloading bge-base-en-v1.5 (~110MB)..."),await bo((_,b,S)=>{let w=S>0?Math.round(b/S*100):0;process.stdout.write(`\r ${_}: ${w}% `)}),process.stdout.write(`
1252
+ `)),console.log("Loading embedder...")}try{await Ke(),console.log("Done. Vector search is now active.")}catch(f){let{EmbedderUnavailableError:h}=await Promise.resolve().then(()=>(Pe(),bs));f instanceof h?(console.log("Model files installed."),console.log(` Embedder load skipped: ${f.message.split(`
1253
+ `)[0]}`),console.log(" Vector features remain available; runtime load happens on first use.")):(console.error("Model files installed but embedder load failed:"),console.error(` ${f instanceof Error?f.message.split(`
1254
+ `)[0]:String(f)}`),console.error(" This is unexpected. Run `recall doctor` for diagnostics."),process.exitCode=1)}return}if(n==="uninstall"){So();let{isGgufModelInstalled:p,uninstallGgufModel:g}=await Promise.resolve().then(()=>(ks(),Rs));p()?(g(),console.log("Model removed (ONNX + GGUF). Vector search will fall back to keyword search.")):console.log("Model removed. Vector search will fall back to keyword search.");return}if(n==="auto-extract"){let p=t._autoExtractAction;if(p==="on"||p==="enable"){let g=ze({autoExtractEnabled:!0},"cli");console.log("Auto-extract: ENABLED"),console.log(` Cadence: 1 batch every ${g.autoExtractIntervalMinutes} minutes`),console.log(` Batch size: ${g.autoExtractBatchSize} session(s) per tick`),console.log(" Honors your 5-hour Claude plan window \u2014 at 60min/1, that is"),console.log(" ~5 extractions per window, leaving 95%+ of the rate budget free."),console.log(" The daemon will start nibbling through un-extracted sessions on"),console.log(" the next tick (within 15 minutes). Status: `recall semantic status`.");return}if(p==="off"||p==="disable"){ze({autoExtractEnabled:!1},"cli"),console.log("Auto-extract: DISABLED. The daemon will stop running extract-outputs.");return}console.error("Usage: recall semantic auto-extract <on|off>"),process.exitCode=1;return}if(n==="reindex"){if(await pe("Vector reindex"),!Q().loaded){if(!rt()){console.error("Model not installed. Run `recall semantic install` first."),process.exitCode=1;return}await Ke()}let p=E(),g=p.prepare(`
1119
1255
  SELECT s.id FROM sessions s
1120
1256
  WHERE s.message_count >= 3
1121
1257
  AND NOT EXISTS (SELECT 1 FROM chunk_meta cm WHERE cm.session_id = s.id)
1122
- `).all(),f=Number(process.env.RECALL_REINDEX_MAX_CHUNKS??"0"),h=f>0?` (cap ${f} chunks/session)`:" (no cap)";console.log(`Reindexing ${g.length} sessions (skipping already-indexed)${h}...`);let E=0;for(let{id:b}of g){let S=$r(b),R=f>0?S.slice(0,f):S;if(R.length===0){E++;continue}let T=R.map($=>$.text),L=await $t(T);m.prepare("DELETE FROM vec_chunks WHERE rowid IN (SELECT rowid FROM chunk_meta WHERE session_id = ?)").run(b),m.prepare("DELETE FROM chunk_meta WHERE session_id = ?").run(b);let D=m.prepare(`INSERT INTO chunk_meta(session_id, message_uuids, text, embedding_model_id, embedding_dim, stale, generated_at)
1123
- VALUES (?, ?, ?, 'bge-base-en-v1.5', 768, 0, datetime('now'))`),A=m.prepare("INSERT INTO vec_chunks(rowid, embedding) VALUES (?, ?)");for(let $=0;$<R.length;$++){let U=D.run(b,JSON.stringify(R[$].messageUuids),R[$].text),y=Buffer.from(L[$].buffer,L[$].byteOffset,L[$].byteLength);A.run(BigInt(U.lastInsertRowid),y)}E++,E%10===0&&process.stdout.write(`\r ${E}/${g.length} `)}process.stdout.write(`
1124
- `),console.log(`Reindexed ${E} sessions.`);return}if(s==="verify-spawn"){if(!ne()){console.error("claude CLI not found on PATH. Install Claude Code first, then re-run."),process.exitCode=1;return}let{spawnClaudePrompt:m}=await Promise.resolve().then(()=>(Be(),Us)),{homedir:g}=await import("node:os"),{join:f}=await import("node:path"),{readdirSync:h}=await import("node:fs"),E=f(g(),".claude","projects"),b=()=>{let $=new Set;try{for(let U of h(E,{withFileTypes:!0})){if(!U.isDirectory())continue;let y=f(E,U.name);for(let B of h(y,{withFileTypes:!0}))B.isFile()&&B.name.endsWith(".jsonl")&&$.add(f(y,B.name))}}catch{}return $};console.log("Snapshotting JSONL files in ~/.claude/projects/ ...");let S=b();console.log(` Before: ${S.size} JSONL file(s)`),console.log('Spawning a tiny `claude -p --no-session-persistence "ok"`...');let R=Date.now(),T=await m("Reply with the single word: ok",[],{}),L=((Date.now()-R)/1e3).toFixed(1);if(console.log(` CLI exit: ${T.exitCode}, ${L}s`),!T.success){console.error(` stderr: ${T.stderr.slice(-500)}`),console.error("FAIL: claude CLI exited non-zero. Cannot validate spawn behavior."),process.exitCode=1;return}console.log("Snapshotting again...");let D=b();console.log(` After: ${D.size} JSONL file(s)`);let A=[];for(let $ of D)S.has($)||A.push($);if(A.length===0){console.log(""),console.log("PASS: --no-session-persistence is honored on this claude CLI version."),console.log("It is safe to re-enable Tier-1 features (autoTitle.agentEnabled,"),console.log("semantic.enabled, semantic.autoExtractEnabled) in ~/.recall/config.json.");return}console.error(""),console.error(`FAIL: ${A.length} new JSONL file(s) appeared despite the flag.`),console.error("Tier-1 features WILL produce phantom sessions if re-enabled.");for(let $ of A.slice(0,5))console.error(` - ${$}`);A.length>5&&console.error(` ... and ${A.length-5} more`),console.error(""),console.error("Possible causes:"),console.error(" 1. Your `claude` CLI is older than the version that added the flag."),console.error(" 2. The flag is in --help but not actually wired up in your version."),console.error("Mitigation: keep autoTitle.agentEnabled=false until claude is upgraded."),process.exitCode=1;return}let n=ac(),r=St(),o=Ze(),i=fc(),a=ve(),d=0;try{d=_().prepare("SELECT COUNT(*) AS n FROM vec_chunks").get()?.n??0}catch{}let l=r||d>0;function u(){console.log("--- Tier 1 (inference \u2014 costs plan tokens) ---"),console.log(`Semantic search: ${n.enabled?"ENABLED":"disabled"}`),console.log(` claude CLI: ${n.claudeCliAvailable?"available":"NOT FOUND"}`),console.log(` Rate: ${n.ratePerMinute}/min`),console.log(` Model: ${n.model??"claude default"}`),console.log(` Sessions: ${n.processedSessions}/${n.totalSessions} summarized`),console.log(` Pending: ${n.pendingSessions}`),n.lastProcessedSessionId&&console.log(` Cursor: ${n.lastProcessedSessionId}`),n.backfillPaused&&console.log(" Backfill: PAUSED (run `recall semantic resume`)")}function p(){console.log("--- Tier 2 (vectors \u2014 free, local, zero tokens) ---"),console.log("Vector tier (Pro):"),console.log(` Model: ${r?"installed":"not installed"}`),console.log(` Embedder: ${o.loaded?"loaded":"not loaded"} (${o.modelId}, ${o.dim}d)`),console.log(` Worker: ${i.running?"running":"stopped"} (queue: ${i.queueDepth})`),console.log(` Chunks: ${d} indexed`)}l?(p(),console.log(""),u()):(u(),console.log(""),p()),console.log(""),console.log("Auto-extract:"),console.log(` Enabled: ${a.autoExtractEnabled?"YES":"no"}`),a.autoExtractEnabled?console.log(` Cadence: 1 batch every ${a.autoExtractIntervalMinutes} min \xD7 ${a.autoExtractBatchSize} session(s)`):console.log(" Run `recall semantic auto-extract on` to populate Patterns / Galaxy automatically."),process.env.RECALL_RRF_K&&console.log(` RRF k: ${process.env.RECALL_RRF_K}`)}w();w();w();yt();var rt=400,zs=768,jh="bge-base-en-v1.5";function Uh(e){return Buffer.from(e.buffer,e.byteOffset,e.byteLength)}function Bh(e){if(!e.message_uuid)throw new Error("message_uuid is required");if(!e.session_id)throw new Error("session_id is required");if(!(e.embedding instanceof Float32Array))throw new Error("embedding must be a Float32Array");if(e.embedding.length!==zs)throw new Error(`embedding dim mismatch: got ${e.embedding.length}, expected ${zs}`);let t=e.embedding_model_id??jh,s=new Date().toISOString();return _().prepare(`INSERT INTO message_embeddings
1258
+ `).all(),f=Number(process.env.RECALL_REINDEX_MAX_CHUNKS??"0"),h=f>0?` (cap ${f} chunks/session)`:" (no cap)";console.log(`Reindexing ${g.length} sessions (skipping already-indexed)${h}...`);let _=0;for(let{id:b}of g){let S=Co(b),w=f>0?S.slice(0,f):S;if(w.length===0){_++;continue}let y=w.map(O=>O.text),T=await Ne(y);Lo(b);let B=Q().modelId,A=p.prepare(`INSERT INTO chunk_meta(session_id, message_uuids, text, embedding_model_id, embedding_dim, stale, generated_at)
1259
+ VALUES (?, ?, ?, ?, 768, 0, datetime('now'))`),v=p.prepare("INSERT INTO vec_chunks(rowid, embedding) VALUES (?, ?)");for(let O=0;O<w.length;O++){let k=A.run(b,JSON.stringify(w[O].messageUuids),w[O].text,B),I=Buffer.from(T[O].buffer,T[O].byteOffset,T[O].byteLength);v.run(BigInt(k.lastInsertRowid),I)}_++,_%10===0&&process.stdout.write(`\r ${_}/${g.length} `)}process.stdout.write(`
1260
+ `),console.log(`Reindexed ${_} sessions.`);return}if(n==="migrate"){await pe("Vector migration");let{runMigration:p}=await Promise.resolve().then(()=>(Ms(),Is)),{getActiveMigration:g,pauseMigration:f}=await Promise.resolve().then(()=>(zt(),vs)),h=t.yes??!1,_=t.noBackup??!1,b=t.dryRun??!1,S=()=>{try{let T=g();T&&(f(T.id),console.error("\n[migrate] received SIGINT; migration paused (cursor preserved). Re-run `recall semantic migrate` to resume."))}catch(T){console.error("[migrate] failed to pause migration on SIGINT:",T)}process.exit(130)};b||process.on("SIGINT",S);let w;try{w=await p({interactive:!h,keepBackup:!_,dryRun:b,projectName:t.project,onProgress:T=>{let A=(T.total>0?T.migrated/T.total*100:0).toFixed(1);process.stdout.write(`\r ${T.migrated}/${T.total} (${A}%) ${T.chunksPerSec.toFixed(1)} chunks/sec `)}})}finally{b||process.off("SIGINT",S)}process.stdout.write(`
1261
+ `);let y=0;w.ok?t.project?console.log(w.message):(console.log(`Migration complete. ${w.migrated} chunks re-embedded.`),w.backupRetained&&(console.log("Old vectors retained in vec_chunks_v1_backup for 30 days."),console.log("Run `recall semantic rollback-migration` to revert if needed."))):(console.error(`Migration: ${w.message}`),/nothing to migrate|user declined confirmation/.test(w.message)||(y=1)),process.exit(y)}if(n==="rollback-migration"){await pe("Vector migration rollback");let{rollbackMigration:p}=await Promise.resolve().then(()=>(Ms(),Is)),g=t.force??!1,f=await p({force:g});f.ok?console.log(f.message):(console.error(f.message),process.exitCode=1),process.exit(process.exitCode??0)}if(n==="prune-rollback"){await pe("Vector migration rollback pruning");let{pruneRollbackBackup:p}=await Promise.resolve().then(()=>(Ms(),Is)),g=t.force??!1,f=await p({force:g});f.ok?console.log(f.message):(console.error(f.message),process.exitCode=1),process.exit(process.exitCode??0)}if(n==="verify-spawn"){if(!ie()){console.error("claude CLI not found on PATH. Install Claude Code first, then re-run."),process.exitCode=1;return}let{spawnClaudePrompt:p}=await Promise.resolve().then(()=>(Ye(),cs)),{homedir:g}=await import("node:os"),{join:f}=await import("node:path"),{readdirSync:h}=await import("node:fs"),_=f(g(),".claude","projects"),b=()=>{let v=new Set;try{for(let O of h(_,{withFileTypes:!0})){if(!O.isDirectory())continue;let k=f(_,O.name);for(let I of h(k,{withFileTypes:!0}))I.isFile()&&I.name.endsWith(".jsonl")&&v.add(f(k,I.name))}}catch{}return v};console.log("Snapshotting JSONL files in ~/.claude/projects/ ...");let S=b();console.log(` Before: ${S.size} JSONL file(s)`),console.log('Spawning a tiny `claude -p --no-session-persistence "ok"`...');let w=Date.now(),y=await p("Reply with the single word: ok",[],{}),T=((Date.now()-w)/1e3).toFixed(1);if(console.log(` CLI exit: ${y.exitCode}, ${T}s`),!y.success){console.error(` stderr: ${y.stderr.slice(-500)}`),console.error("FAIL: claude CLI exited non-zero. Cannot validate spawn behavior."),process.exitCode=1;return}console.log("Snapshotting again...");let B=b();console.log(` After: ${B.size} JSONL file(s)`);let A=[];for(let v of B)S.has(v)||A.push(v);if(A.length===0){console.log(""),console.log("PASS: --no-session-persistence is honored on this claude CLI version."),console.log("It is safe to re-enable Tier-1 features (autoTitle.agentEnabled,"),console.log("semantic.enabled, semantic.autoExtractEnabled) in ~/.recall/config.json.");return}console.error(""),console.error(`FAIL: ${A.length} new JSONL file(s) appeared despite the flag.`),console.error("Tier-1 features WILL produce phantom sessions if re-enabled.");for(let v of A.slice(0,5))console.error(` - ${v}`);A.length>5&&console.error(` ... and ${A.length-5} more`),console.error(""),console.error("Possible causes:"),console.error(" 1. Your `claude` CLI is older than the version that added the flag."),console.error(" 2. The flag is in --help but not actually wired up in your version."),console.error("Mitigation: keep autoTitle.agentEnabled=false until claude is upgraded."),process.exitCode=1;return}let s=Tl(),r=rt(),o=Q(),i=Ml(),a=ye(),d=0;try{d=E().prepare("SELECT COUNT(*) AS n FROM vec_chunks").get()?.n??0}catch{}let l=r||d>0;function u(){console.log("--- Tier 1 (inference \u2014 costs plan tokens) ---"),console.log(`Semantic search: ${s.enabled?"ENABLED":"disabled"}`),console.log(` claude CLI: ${s.claudeCliAvailable?"available":"NOT FOUND"}`),console.log(` Rate: ${s.ratePerMinute}/min`),console.log(` Model: ${s.model??"claude default"}`),console.log(` Sessions: ${s.processedSessions}/${s.totalSessions} summarized`),console.log(` Pending: ${s.pendingSessions}`),s.lastProcessedSessionId&&console.log(` Cursor: ${s.lastProcessedSessionId}`),s.backfillPaused&&console.log(" Backfill: PAUSED (run `recall semantic resume`)")}function m(){console.log("--- Tier 2 (vectors \u2014 free, local, zero tokens) ---"),console.log("Vector tier (Pro):"),console.log(` Model: ${r?"installed":"not installed"}`),console.log(` Embedder: ${o.loaded?"loaded":"not loaded"} (${o.modelId}, ${o.dim}d)`),console.log(` Worker: ${i.running?"running":"stopped"} (queue: ${i.queueDepth})`),console.log(` Chunks: ${d} indexed`)}l?(m(),console.log(""),u()):(u(),console.log(""),m()),console.log(""),console.log("Auto-extract:"),console.log(` Enabled: ${a.autoExtractEnabled?"YES":"no"}`),a.autoExtractEnabled?console.log(` Cadence: 1 batch every ${a.autoExtractIntervalMinutes} min \xD7 ${a.autoExtractBatchSize} session(s)`):console.log(" Run `recall semantic auto-extract on` to populate Patterns / Galaxy automatically."),process.env.RECALL_RRF_K&&console.log(` RRF k: ${process.env.RECALL_RRF_K}`)}R();R();R();Pe();var mt=400,Ds=768,cw="bge-base-en-v1.5";function lw(e){return Buffer.from(e.buffer,e.byteOffset,e.byteLength)}function dw(e){if(!e.message_uuid)throw new Error("message_uuid is required");if(!e.session_id)throw new Error("session_id is required");if(!(e.embedding instanceof Float32Array))throw new Error("embedding must be a Float32Array");if(e.embedding.length!==Ds)throw new Error(`embedding dim mismatch: got ${e.embedding.length}, expected ${Ds}`);let t=e.embedding_model_id??cw,n=new Date().toISOString();return E().prepare(`INSERT INTO message_embeddings
1125
1262
  (message_uuid, session_id, embedding,
1126
1263
  embedding_model_id, embedding_dim, text_length, generated_at)
1127
1264
  VALUES (?, ?, ?, ?, ?, ?, ?)
@@ -1131,27 +1268,27 @@ show full content: recall paste --show <id>
1131
1268
  embedding_model_id = excluded.embedding_model_id,
1132
1269
  embedding_dim = excluded.embedding_dim,
1133
1270
  text_length = excluded.text_length,
1134
- generated_at = excluded.generated_at`).run(e.message_uuid,e.session_id,Uh(e.embedding),t,zs,e.text_length,s),{message_uuid:e.message_uuid,session_id:e.session_id,embedding_model_id:t,embedding_dim:zs,text_length:e.text_length,generated_at:s}}function Hh(e,t={}){let s=_(),n=t.force?`m.session_id = ? AND m.is_sidechain = 0
1271
+ generated_at = excluded.generated_at`).run(e.message_uuid,e.session_id,lw(e.embedding),t,Ds,e.text_length,n),{message_uuid:e.message_uuid,session_id:e.session_id,embedding_model_id:t,embedding_dim:Ds,text_length:e.text_length,generated_at:n}}function uw(e,t={}){let n=E(),s=t.force?`m.session_id = ? AND m.is_sidechain = 0
1135
1272
  AND m.content_text IS NOT NULL
1136
1273
  AND length(m.content_text) > ?`:`m.session_id = ? AND m.is_sidechain = 0
1137
1274
  AND m.content_text IS NOT NULL
1138
1275
  AND length(m.content_text) > ?
1139
- AND me.message_uuid IS NULL`,r=Math.max(1,Math.min(1e4,t.limit??1e3));return s.prepare(`SELECT m.uuid AS uuid, m.session_id AS session_id, m.content_text AS content_text
1276
+ AND me.message_uuid IS NULL`,r=Math.max(1,Math.min(1e4,t.limit??1e3));return n.prepare(`SELECT m.uuid AS uuid, m.session_id AS session_id, m.content_text AS content_text
1140
1277
  FROM messages m
1141
1278
  LEFT JOIN message_embeddings me ON me.message_uuid = m.uuid
1142
- WHERE ${n}
1279
+ WHERE ${s}
1143
1280
  ORDER BY m.timestamp ASC, m.rowid ASC
1144
- LIMIT ?`).all(e,rt,r)}var Sc=null;async function Wh(){return Sc||(Ze().loaded||await nt(),$t)}async function Xh(e,t={}){let s={embedded:0,skipped:0,failed:0,failures:[]},n=Hh(e,{limit:t.limit,force:t.force});if(n.length===0)return s;let r=await Wh(),o=n.map(a=>a.content_text),i;try{if(t.signal?.aborted)return s;i=await r(o)}catch(a){return s.failed=n.length,s.failures=n.map(d=>({uuid:d.uuid,error:a instanceof Error?a.message:String(a)})),s}if(i.length!==n.length)return s.failed=n.length,s.failures=n.map(a=>({uuid:a.uuid,error:`embedder returned ${i.length} vectors for ${n.length} inputs`})),s;for(let a=0;a<n.length&&!t.signal?.aborted;a++)try{Bh({message_uuid:n[a].uuid,session_id:n[a].session_id,embedding:i[a],text_length:o[a].length}),s.embedded+=1}catch(d){s.failed+=1,s.failures.push({uuid:n[a].uuid,error:d instanceof Error?d.message:String(d)})}return s.skipped=0,s}async function yc(e={}){let t=_(),s=[],n="s.message_count >= 1";typeof e.projectId=="number"&&(n+=" AND s.project_id = ?",s.push(e.projectId));let r=t.prepare(`SELECT DISTINCT s.id AS id
1281
+ LIMIT ?`).all(e,mt,r)}var dd=null;async function mw(){return dd||(Q().loaded||await Ke(),Ne)}async function pw(e,t={}){let n={embedded:0,skipped:0,failed:0,failures:[]},s=uw(e,{limit:t.limit,force:t.force});if(s.length===0)return n;let r=await mw(),o=s.map(a=>a.content_text),i;try{if(t.signal?.aborted)return n;i=await r(o)}catch(a){return n.failed=s.length,n.failures=s.map(d=>({uuid:d.uuid,error:a instanceof Error?a.message:String(a)})),n}if(i.length!==s.length)return n.failed=s.length,n.failures=s.map(a=>({uuid:a.uuid,error:`embedder returned ${i.length} vectors for ${s.length} inputs`})),n;for(let a=0;a<s.length&&!t.signal?.aborted;a++)try{dw({message_uuid:s[a].uuid,session_id:s[a].session_id,embedding:i[a],text_length:o[a].length}),n.embedded+=1}catch(d){n.failed+=1,n.failures.push({uuid:s[a].uuid,error:d instanceof Error?d.message:String(d)})}return n.skipped=0,n}async function ud(e={}){let t=E(),n=[],s="s.message_count >= 1";typeof e.projectId=="number"&&(s+=" AND s.project_id = ?",n.push(e.projectId));let r=t.prepare(`SELECT DISTINCT s.id AS id
1145
1282
  FROM sessions s
1146
1283
  JOIN messages m ON m.session_id = s.id
1147
1284
  LEFT JOIN message_embeddings me ON me.message_uuid = m.uuid
1148
- WHERE ${n}
1285
+ WHERE ${s}
1149
1286
  AND m.is_sidechain = 0
1150
1287
  AND m.content_text IS NOT NULL
1151
1288
  AND length(m.content_text) > ?
1152
1289
  AND me.message_uuid IS NULL
1153
1290
  ORDER BY s.started_at ASC
1154
- LIMIT ?`).all(...s,rt,e.limitSessions??1e3),o={total_sessions:r.length,processed_sessions:0,embedded_messages:0,failed_messages:0,current_session_id:null};e.onProgress?.({...o});for(let{id:i}of r){if(e.signal?.aborted)break;o.current_session_id=i,e.onProgress?.({...o});let a=await Xh(i,{limit:e.limitMessagesPerSession,signal:e.signal});o.embedded_messages+=a.embedded,o.failed_messages+=a.failed,o.processed_sessions+=1,e.onProgress?.({...o})}return o.current_session_id=null,e.onProgress?.({...o}),o}function qs(e,t){return t===0?0:Math.round(e/t*1e3)/10}function Ee(e){return _().prepare("SELECT id, name FROM projects WHERE name = ? LIMIT 1").get(e)??null}function wc(e={}){let t=_(),s=null;if(typeof e.projectId=="number"){let S=t.prepare("SELECT id, name FROM projects WHERE id = ?").get(e.projectId);S&&(s=S)}else e.projectName&&(s=Ee(e.projectName));let n=s?" WHERE s.project_id = ?":"",r=s?[s.id]:[],o=t.prepare(`SELECT
1291
+ LIMIT ?`).all(...n,mt,e.limitSessions??1e3),o={total_sessions:r.length,processed_sessions:0,embedded_messages:0,failed_messages:0,current_session_id:null};e.onProgress?.({...o});for(let{id:i}of r){if(e.signal?.aborted)break;o.current_session_id=i,e.onProgress?.({...o});let a=await pw(i,{limit:e.limitMessagesPerSession,signal:e.signal});o.embedded_messages+=a.embedded,o.failed_messages+=a.failed,o.processed_sessions+=1,e.onProgress?.({...o})}return o.current_session_id=null,e.onProgress?.({...o}),o}function $s(e,t){return t===0?0:Math.round(e/t*1e3)/10}function Te(e){return E().prepare("SELECT id, name FROM projects WHERE name = ? LIMIT 1").get(e)??null}function md(e={}){let t=E(),n=null;if(typeof e.projectId=="number"){let S=t.prepare("SELECT id, name FROM projects WHERE id = ?").get(e.projectId);S&&(n=S)}else e.projectName&&(n=Te(e.projectName));let s=n?" WHERE s.project_id = ?":"",r=n?[n.id]:[],o=t.prepare(`SELECT
1155
1292
  COUNT(*) AS total,
1156
1293
  SUM(CASE WHEN ss.session_id IS NOT NULL THEN 1 ELSE 0 END) AS with_semantic,
1157
1294
  SUM(CASE WHEN cm_count.session_id IS NOT NULL THEN 1 ELSE 0 END) AS with_chunks,
@@ -1164,23 +1301,23 @@ show full content: recall paste --show <id>
1164
1301
  LEFT JOIN (
1165
1302
  SELECT DISTINCT session_id FROM message_embeddings
1166
1303
  ) me_count ON me_count.session_id = s.id
1167
- ${n}`).get(...r),i=o.total??0,a=o.with_semantic??0,d=o.with_chunks??0,l=o.with_msg_emb??0,u=s?" JOIN sessions s ON s.id = m.session_id WHERE s.project_id = ?":"",p=t.prepare(`SELECT
1304
+ ${s}`).get(...r),i=o.total??0,a=o.with_semantic??0,d=o.with_chunks??0,l=o.with_msg_emb??0,u=n?" JOIN sessions s ON s.id = m.session_id WHERE s.project_id = ?":"",m=t.prepare(`SELECT
1168
1305
  COUNT(*) AS total,
1169
1306
  SUM(CASE
1170
1307
  WHEN m.is_sidechain = 0
1171
1308
  AND m.content_text IS NOT NULL
1172
- AND length(m.content_text) > ${rt}
1309
+ AND length(m.content_text) > ${mt}
1173
1310
  THEN 1 ELSE 0 END) AS eligible
1174
1311
  FROM messages m
1175
- ${u}`).get(...r),m=s?" JOIN sessions s ON s.id = me.session_id WHERE s.project_id = ?":"",g=t.prepare(`SELECT COUNT(*) AS embedded
1312
+ ${u}`).get(...r),p=n?" JOIN sessions s ON s.id = me.session_id WHERE s.project_id = ?":"",g=t.prepare(`SELECT COUNT(*) AS embedded
1176
1313
  FROM message_embeddings me
1177
- ${m}`).get(...r),f=s?" JOIN sessions s ON s.id = cm.session_id WHERE s.project_id = ?":"",h=t.prepare(`SELECT COUNT(*) AS n
1314
+ ${p}`).get(...r),f=n?" JOIN sessions s ON s.id = cm.session_id WHERE s.project_id = ?":"",h=t.prepare(`SELECT COUNT(*) AS n
1178
1315
  FROM chunk_meta cm
1179
- ${f}`).get(...r),E=s?" JOIN sessions s ON s.id = cq.session_id WHERE s.project_id = ?":"",b=t.prepare(`SELECT COUNT(*) AS n
1316
+ ${f}`).get(...r),_=n?" JOIN sessions s ON s.id = cq.session_id WHERE s.project_id = ?":"",b=t.prepare(`SELECT COUNT(*) AS n
1180
1317
  FROM chunk_queue cq
1181
- ${E}`).get(...r);return{project:s,sessions:{total:i,with_semantic:a,with_semantic_pct:qs(a,i),with_chunks:d,with_chunks_pct:qs(d,i),with_message_embeddings:l,with_message_embeddings_pct:qs(l,i)},messages:{total:p.total??0,eligible:p.eligible??0,embedded:g.embedded,embedded_pct:qs(g.embedded,p.eligible??0),threshold_chars:rt},chunks:{chunk_meta_rows:h.n,chunk_queue_pending:b.n},generated_at:new Date().toISOString()}}function Tc(e){let s=e.sessions.with_semantic_pct,n=e.sessions.total-e.sessions.with_semantic,r=e.sessions.total>0&&s>=95,o=r?`${s}% session_semantic coverage (${e.sessions.with_semantic} of ${e.sessions.total})`:e.sessions.total===0?"no sessions in scope":`${s}% session_semantic coverage (need \u226595%); ${n} sessions missing summaries`;return{passes:r,required_pct:95,actual_pct:s,missing_sessions:n,reason:o}}de();Js();yt();async function Rc(e,t){let s=(e??"audit").toLowerCase();if(s==="audit")return Jh(t);if(s==="backfill-summaries")return Yh(t);if(s==="backfill-messages")return zh(t);console.error(`Unknown embeddings action: ${e}. Supported: audit | backfill-summaries | backfill-messages`),process.exitCode=1}function Pr(e){if(!e)return null;let t=Ee(e);return t||"not-found"}async function Jh(e){let t=Pr(e.project);if(t==="not-found"){console.error(`Project "${e.project}" not found.`),process.exitCode=1;return}let s=wc({projectId:t?t.id:void 0}),n=Tc(s);if(e.json){console.log(JSON.stringify({report:s,verdict:n},null,2));return}Gh(s,n),n.passes||(process.exitCode=1)}function Gh(e,t){let s=e.project?`project "${e.project.name}"`:"all projects";console.log(`Embeddings coverage \u2014 ${s}`),console.log(""),console.log(` Sessions: ${e.sessions.total} total`),console.log(` session_semantic: ${e.sessions.with_semantic}/${e.sessions.total} (${e.sessions.with_semantic_pct}%)`),console.log(` chunk_meta (any): ${e.sessions.with_chunks}/${e.sessions.total} (${e.sessions.with_chunks_pct}%)`),console.log(` message_embeddings: ${e.sessions.with_message_embeddings}/${e.sessions.total} (${e.sessions.with_message_embeddings_pct}%)`),console.log(""),console.log(` Messages: ${e.messages.total} total`),console.log(` eligible (>${e.messages.threshold_chars} chars): ${e.messages.eligible}`),console.log(` embedded: ${e.messages.embedded}${e.messages.eligible>0?` (${e.messages.embedded_pct}% of eligible)`:""}`),console.log(""),console.log(" Chunks (Pro tier):"),console.log(` chunk_meta rows: ${e.chunks.chunk_meta_rows}`),console.log(` chunk_queue pending: ${e.chunks.chunk_queue_pending}`),e.chunks.chunk_queue_pending>1e3&&e.chunks.chunk_meta_rows===0&&(console.log(""),console.log(" \u26A0 chunk_queue is backed up but no chunks have been processed."),console.log(" Activate Pro and run `recall semantic install` to drain the queue.")),console.log(""),t.passes?console.log(` \u2705 Acceptance: ${t.reason}`):(console.log(` \u274C Acceptance: ${t.reason}`),console.log(` Run \`recall embeddings backfill-summaries${e.project?` --project ${e.project.name}`:""}\` to close the gap.`))}async function Yh(e){if(!ve().enabled){console.error("Semantic search is disabled. Run `recall semantic on` first."),process.exitCode=1;return}let s=Pr(e.project);if(s==="not-found"){console.error(`Project "${e.project}" not found.`),process.exitCode=1;return}let n=e.limit?Math.max(1,Number(e.limit)):1e3,r=s?`project "${s.name}"`:"all projects";console.log(`Backfilling session_semantic \u2014 ${r} \u2014 up to ${n} sessions\u2026`);let o=Date.now(),i=0,a=await Hs({limit:n,projectId:s?s.id:void 0,onProgress:l=>{if(e.json||l.processed===i)return;i=l.processed;let u=l.total>0?` (${Math.round(l.processed/l.total*100)}%)`:"";process.stdout.write(`\r ${l.processed}/${l.total}${u} ok=${l.ok} failed=${l.failed} `)}});e.json||process.stdout.write(`
1182
- `);let d=((Date.now()-o)/1e3).toFixed(1);e.json?console.log(JSON.stringify({...a,elapsed_seconds:Number(d)},null,2)):console.log(`Done in ${d}s \u2014 processed=${a.processed} ok=${a.ok} failed=${a.failed}`)}async function zh(e){if(await Ne("Per-message embeddings"),!St()){console.error("Model not installed. Run `recall semantic install` first."),process.exitCode=1;return}Ze().loaded||await nt();let t=Pr(e.project);if(t==="not-found"){console.error(`Project "${e.project}" not found.`),process.exitCode=1;return}let s=e.limit?Math.max(1,Number(e.limit)):200,n=t?`project "${t.name}"`:"all projects";console.log(`Embedding messages > ${rt} chars \u2014 ${n} \u2014 up to ${s} sessions\u2026`);let r=Date.now(),o=0,i=await yc({projectId:t?t.id:void 0,limitSessions:s,onProgress:m=>{if(e.json||m.processed_sessions===o)return;o=m.processed_sessions;let g=m.total_sessions>0?` (${Math.round(m.processed_sessions/m.total_sessions*100)}%)`:"";process.stdout.write(`\r ${m.processed_sessions}/${m.total_sessions} sessions${g} embedded=${m.embedded_messages} failed=${m.failed_messages} `)}});e.json||process.stdout.write(`
1183
- `);let a=((Date.now()-r)/1e3).toFixed(1);e.json?console.log(JSON.stringify({...i,elapsed_seconds:Number(a)},null,2)):console.log(`Done in ${a}s \u2014 sessions=${i.processed_sessions}/${i.total_sessions} embedded=${i.embedded_messages} failed=${i.failed_messages}`);let d=_(),l=t?" AND s.project_id = ?":"",u=t?[t.id]:[],p=d.prepare(`SELECT COUNT(DISTINCT s.id) AS n
1318
+ ${_}`).get(...r);return{project:n,sessions:{total:i,with_semantic:a,with_semantic_pct:$s(a,i),with_chunks:d,with_chunks_pct:$s(d,i),with_message_embeddings:l,with_message_embeddings_pct:$s(l,i)},messages:{total:m.total??0,eligible:m.eligible??0,embedded:g.embedded,embedded_pct:$s(g.embedded,m.eligible??0),threshold_chars:mt},chunks:{chunk_meta_rows:h.n,chunk_queue_pending:b.n},generated_at:new Date().toISOString()}}function pd(e){let n=e.sessions.with_semantic_pct,s=e.sessions.total-e.sessions.with_semantic,r=e.sessions.total>0&&n>=95,o=r?`${n}% session_semantic coverage (${e.sessions.with_semantic} of ${e.sessions.total})`:e.sessions.total===0?"no sessions in scope":`${n}% session_semantic coverage (need \u226595%); ${s} sessions missing summaries`;return{passes:r,required_pct:95,actual_pct:n,missing_sessions:s,reason:o}}ge();_n();Pe();async function gd(e,t){let n=(e??"audit").toLowerCase();if(n==="audit")return gw(t);if(n==="backfill-summaries")return _w(t);if(n==="backfill-messages")return hw(t);console.error(`Unknown embeddings action: ${e}. Supported: audit | backfill-summaries | backfill-messages`),process.exitCode=1}function Jo(e){if(!e)return null;let t=Te(e);return t||"not-found"}async function gw(e){let t=Jo(e.project);if(t==="not-found"){console.error(`Project "${e.project}" not found.`),process.exitCode=1;return}let n=md({projectId:t?t.id:void 0}),s=pd(n);if(e.json){console.log(JSON.stringify({report:n,verdict:s},null,2));return}fw(n,s),s.passes||(process.exitCode=1)}function fw(e,t){let n=e.project?`project "${e.project.name}"`:"all projects";console.log(`Embeddings coverage \u2014 ${n}`),console.log(""),console.log(` Sessions: ${e.sessions.total} total`),console.log(` session_semantic: ${e.sessions.with_semantic}/${e.sessions.total} (${e.sessions.with_semantic_pct}%)`),console.log(` chunk_meta (any): ${e.sessions.with_chunks}/${e.sessions.total} (${e.sessions.with_chunks_pct}%)`),console.log(` message_embeddings: ${e.sessions.with_message_embeddings}/${e.sessions.total} (${e.sessions.with_message_embeddings_pct}%)`),console.log(""),console.log(` Messages: ${e.messages.total} total`),console.log(` eligible (>${e.messages.threshold_chars} chars): ${e.messages.eligible}`),console.log(` embedded: ${e.messages.embedded}${e.messages.eligible>0?` (${e.messages.embedded_pct}% of eligible)`:""}`),console.log(""),console.log(" Chunks (Pro tier):"),console.log(` chunk_meta rows: ${e.chunks.chunk_meta_rows}`),console.log(` chunk_queue pending: ${e.chunks.chunk_queue_pending}`),e.chunks.chunk_queue_pending>1e3&&e.chunks.chunk_meta_rows===0&&(console.log(""),console.log(" \u26A0 chunk_queue is backed up but no chunks have been processed."),console.log(" Activate Pro and run `recall semantic install` to drain the queue.")),console.log(""),t.passes?console.log(` \u2705 Acceptance: ${t.reason}`):(console.log(` \u274C Acceptance: ${t.reason}`),console.log(` Run \`recall embeddings backfill-summaries${e.project?` --project ${e.project.name}`:""}\` to close the gap.`))}async function _w(e){if(!ye().enabled){console.error("Semantic search is disabled. Run `recall semantic on` first."),process.exitCode=1;return}let n=Jo(e.project);if(n==="not-found"){console.error(`Project "${e.project}" not found.`),process.exitCode=1;return}let s=e.limit?Math.max(1,Number(e.limit)):1e3,r=n?`project "${n.name}"`:"all projects";console.log(`Backfilling session_semantic \u2014 ${r} \u2014 up to ${s} sessions\u2026`);let o=Date.now(),i=0,a=await ds({limit:s,projectId:n?n.id:void 0,onProgress:l=>{if(e.json||l.processed===i)return;i=l.processed;let u=l.total>0?` (${Math.round(l.processed/l.total*100)}%)`:"";process.stdout.write(`\r ${l.processed}/${l.total}${u} ok=${l.ok} failed=${l.failed} `)}});e.json||process.stdout.write(`
1319
+ `);let d=((Date.now()-o)/1e3).toFixed(1);e.json?console.log(JSON.stringify({...a,elapsed_seconds:Number(d)},null,2)):console.log(`Done in ${d}s \u2014 processed=${a.processed} ok=${a.ok} failed=${a.failed}`)}async function hw(e){if(await pe("Per-message embeddings"),!rt()){console.error("Model not installed. Run `recall semantic install` first."),process.exitCode=1;return}Q().loaded||await Ke();let t=Jo(e.project);if(t==="not-found"){console.error(`Project "${e.project}" not found.`),process.exitCode=1;return}let n=e.limit?Math.max(1,Number(e.limit)):200,s=t?`project "${t.name}"`:"all projects";console.log(`Embedding messages > ${mt} chars \u2014 ${s} \u2014 up to ${n} sessions\u2026`);let r=Date.now(),o=0,i=await ud({projectId:t?t.id:void 0,limitSessions:n,onProgress:p=>{if(e.json||p.processed_sessions===o)return;o=p.processed_sessions;let g=p.total_sessions>0?` (${Math.round(p.processed_sessions/p.total_sessions*100)}%)`:"";process.stdout.write(`\r ${p.processed_sessions}/${p.total_sessions} sessions${g} embedded=${p.embedded_messages} failed=${p.failed_messages} `)}});e.json||process.stdout.write(`
1320
+ `);let a=((Date.now()-r)/1e3).toFixed(1);e.json?console.log(JSON.stringify({...i,elapsed_seconds:Number(a)},null,2)):console.log(`Done in ${a}s \u2014 sessions=${i.processed_sessions}/${i.total_sessions} embedded=${i.embedded_messages} failed=${i.failed_messages}`);let d=E(),l=t?" AND s.project_id = ?":"",u=t?[t.id]:[],m=d.prepare(`SELECT COUNT(DISTINCT s.id) AS n
1184
1321
  FROM sessions s
1185
1322
  JOIN messages m ON m.session_id = s.id
1186
1323
  LEFT JOIN message_embeddings me ON me.message_uuid = m.uuid
@@ -1188,7 +1325,7 @@ show full content: recall paste --show <id>
1188
1325
  AND m.content_text IS NOT NULL
1189
1326
  AND length(m.content_text) > ?
1190
1327
  AND me.message_uuid IS NULL
1191
- ${l}`).get(rt,...u);p.n>0&&console.log(` ${p.n} session(s) still have unembedded eligible messages. Re-run to continue.`)}w();import{createHash as tE}from"node:crypto";Be();w();P();import{writeFileSync as qh,readFileSync as kA,existsSync as Kh,mkdirSync as Vh,readdirSync as CA}from"node:fs";import{join as xc}from"node:path";var Fr=xc(x,"output-index");function Zh(){F(),Kh(Fr)||Vh(Fr,{recursive:!0})}function rs(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function Qh(e){if(!e)return null;try{return JSON.parse(e)}catch{return e}}function kc(e){return{session_id:e.session_id,files_written:rs(e.files_written),brands_mentioned:rs(e.brands_mentioned),terms_introduced:rs(e.terms_introduced),plan_ids_referenced:rs(e.plan_ids_referenced),bug_signatures:rs(e.bug_signatures),raw_extraction:Qh(e.raw_extraction),extracted_at:e.extracted_at,extractor_version:e.extractor_version}}function Cc(e){if(!e.session_id)throw new Error("session_id is required");let t=_(),s=new Date().toISOString(),n=JSON.stringify(e.files_written??[]),r=JSON.stringify(e.brands_mentioned??[]),o=JSON.stringify(e.terms_introduced??[]),i=JSON.stringify(e.plan_ids_referenced??[]),a=JSON.stringify(e.bug_signatures??[]),d=e.raw_extraction===void 0?null:JSON.stringify(e.raw_extraction),l=Math.max(1,Math.floor(e.extractor_version??1));t.prepare(`INSERT INTO session_output_index
1328
+ ${l}`).get(mt,...u);m.n>0&&console.log(` ${m.n} session(s) still have unembedded eligible messages. Re-run to continue.`)}R();import{createHash as Rw}from"node:crypto";Ye();R();D();import{writeFileSync as Ew,readFileSync as OD,existsSync as bw,mkdirSync as Sw,readdirSync as vD}from"node:fs";import{join as fd}from"node:path";var zo=fd(x,"output-index");function ww(){j(),bw(zo)||Sw(zo,{recursive:!0})}function Rn(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function yw(e){if(!e)return null;try{return JSON.parse(e)}catch{return e}}function _d(e){return{session_id:e.session_id,files_written:Rn(e.files_written),brands_mentioned:Rn(e.brands_mentioned),terms_introduced:Rn(e.terms_introduced),plan_ids_referenced:Rn(e.plan_ids_referenced),bug_signatures:Rn(e.bug_signatures),raw_extraction:yw(e.raw_extraction),extracted_at:e.extracted_at,extractor_version:e.extractor_version}}function hd(e){if(!e.session_id)throw new Error("session_id is required");let t=E(),n=new Date().toISOString(),s=JSON.stringify(e.files_written??[]),r=JSON.stringify(e.brands_mentioned??[]),o=JSON.stringify(e.terms_introduced??[]),i=JSON.stringify(e.plan_ids_referenced??[]),a=JSON.stringify(e.bug_signatures??[]),d=e.raw_extraction===void 0?null:JSON.stringify(e.raw_extraction),l=Math.max(1,Math.floor(e.extractor_version??1));t.prepare(`INSERT INTO session_output_index
1192
1329
  (session_id, files_written, brands_mentioned, terms_introduced,
1193
1330
  plan_ids_referenced, bug_signatures, raw_extraction,
1194
1331
  extracted_at, extractor_version)
@@ -1201,7 +1338,7 @@ show full content: recall paste --show <id>
1201
1338
  bug_signatures = excluded.bug_signatures,
1202
1339
  raw_extraction = excluded.raw_extraction,
1203
1340
  extracted_at = excluded.extracted_at,
1204
- extractor_version = excluded.extractor_version`).run(e.session_id,n,r,o,i,a,d,s,l);let u=t.prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e.session_id);if(!u)throw new Error("setOutputIndex succeeded but read-back failed");let p=kc(u);return eE(e.session_id),p}function os(e){let s=_().prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e);return s?kc(s):null}function eE(e){try{Zh();let t=os(e);if(!t)return;let s=xc(Fr,`${e}.json`),n={schema:"claude-recall.session-output-index.v1",backed_up_at:new Date().toISOString(),...t};qh(s,JSON.stringify(n,null,2))}catch(t){console.error("[output-index] backup failed:",t)}}var is=1,Br="claude-haiku-4-5-20251001",sE=3,nE=32e3,Lc=2e3,rE=30,oE=30,iE=30,aE=30;function cE(e){let s=_().prepare(`SELECT s.id,
1341
+ extractor_version = excluded.extractor_version`).run(e.session_id,s,r,o,i,a,d,n,l);let u=t.prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e.session_id);if(!u)throw new Error("setOutputIndex succeeded but read-back failed");let m=_d(u);return Tw(e.session_id),m}function kn(e){let n=E().prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e);return n?_d(n):null}function Tw(e){try{ww();let t=kn(e);if(!t)return;let n=fd(zo,`${e}.json`),s={schema:"claude-recall.session-output-index.v1",backed_up_at:new Date().toISOString(),...t};Ew(n,JSON.stringify(s,null,2))}catch(t){console.error("[output-index] backup failed:",t)}}var xn=1,Vo="claude-haiku-4-5-20251001",kw=3,xw=32e3,Ed=2e3,Cw=30,Aw=30,Lw=30,Nw=30;function Ow(e){let n=E().prepare(`SELECT s.id,
1205
1342
  NULLIF(sa.alias, '') AS alias,
1206
1343
  s.auto_title,
1207
1344
  s.auto_title_source,
@@ -1212,15 +1349,15 @@ show full content: recall paste --show <id>
1212
1349
  FROM sessions s
1213
1350
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1214
1351
  LEFT JOIN projects p ON p.id = s.project_id
1215
- WHERE s.id = ?`).get(e);return s?{...s,alias_source:s.alias?"manual":null}:null}function Nc(e,t={}){if(e.message_count<sE)return{eligible:!1,reason:"too-short"};let s=e.title_quality;if(s==="programmatic"||s==="recursive_meta")return{eligible:!1,reason:"low-signal-title"};if(e.auto_title_source==="agent"&&!e.alias)return{eligible:!1,reason:"agent-titled-no-override"};if(!t.force){let n=os(e.id);if(n&&n.extractor_version>=is)return{eligible:!1,reason:"already-extracted"}}return{eligible:!0}}function lE(e){let t=cE(e);if(!t)return null;let n=_().prepare(`SELECT role, content_text
1352
+ WHERE s.id = ?`).get(e);return n?{...n,alias_source:n.alias?"manual":null}:null}function bd(e,t={}){if(e.message_count<kw)return{eligible:!1,reason:"too-short"};let n=e.title_quality;if(n==="programmatic"||n==="recursive_meta")return{eligible:!1,reason:"low-signal-title"};if(e.auto_title_source==="agent"&&!e.alias)return{eligible:!1,reason:"agent-titled-no-override"};if(!t.force){let s=kn(e.id);if(s&&s.extractor_version>=xn)return{eligible:!1,reason:"already-extracted"}}return{eligible:!0}}function vw(e){let t=Ow(e);if(!t)return null;let s=E().prepare(`SELECT role, content_text
1216
1353
  FROM messages
1217
1354
  WHERE session_id = ?
1218
1355
  AND is_sidechain = 0
1219
1356
  AND content_text IS NOT NULL
1220
- ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let i of n){let a=i.role??"system",d=i.content_text.trim();if(!d)continue;let l=d.length>Lc?d.slice(0,Lc)+"\u2026":d,u=`${a}: ${l}`;if(o+u.length>nE)break;r.push(u),o+=u.length}return{meta:t,excerpt:r.join(`
1357
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(e),r=[],o=0;for(let i of s){let a=i.role??"system",d=i.content_text.trim();if(!d)continue;let l=d.length>Ed?d.slice(0,Ed)+"\u2026":d,u=`${a}: ${l}`;if(o+u.length>xw)break;r.push(u),o+=u.length}return{meta:t,excerpt:r.join(`
1221
1358
 
1222
- `)}}function dE(e){let{meta:t}=e;return["You are extracting a structured Output Index from a Claude Code session for a local knowledge graph.","","Read the transcript and produce a single JSON object with EXACTLY these fields. Output JSON only \u2014 no Markdown fences, no commentary, no explanation.","","Fields (every field MUST appear; use [] for empty):","- files_written: array of strings. Relative or absolute file paths the assistant created, edited, or extensively discussed (Write/Edit tool calls or paths quoted verbatim).",'- brands_mentioned: array of strings. Distinct proper-noun company / product / brand names mentioned. Examples of valid: "Glaser Group", "Apollo", "TikTok", "Cloudflare R2". NOT generic terms like "the company" or "users".','- terms_introduced: array of objects { "term": "<lowercase phrase>", "freq": <int> }. Distinctive multi-word noun phrases the session introduced or extensively discussed. Skip generic words. Cap at 30 entries.','- plan_ids_referenced: array of strings. Planning identifiers like "v0.18.A", "Phase D", "L3", "MASTER-PLAN-cognitive-graph". Empty array if none.','- bug_signatures: array of objects { "error_type": "<class or null>", "snippet": "<first line of the error>", "file": "<path or null>" }. Errors actually encountered in the session (not hypothetical). The error_type is the exception class (e.g. "TypeError", "ReferenceError") or null if unspecified.',"","Hard constraints:","- Do NOT fabricate. If a field has no concrete content from the transcript, output an empty array.","- Output JSON ONLY. No backticks, no markdown, no preamble.","- terms_introduced \u2264 30 entries; brands_mentioned \u2264 30 distinct entries; plan_ids_referenced \u2264 30; bug_signatures \u2264 30.","",`Session: ${t.alias??t.id.slice(0,8)}`,`Project: ${t.project??"unknown"}`,t.first_user_message?`Opening prompt: ${t.first_user_message.slice(0,500)}`:"","","Transcript excerpt (may be truncated):","---",e.excerpt,"---"].filter(Boolean).join(`
1223
- `)}function uE(e){return Array.isArray(e)&&e.every(t=>typeof t=="string")}function jr(e,t,s=!1){if(!Array.isArray(e))return[];let n=new Set,r=[];for(let o of e){if(typeof o!="string")continue;let i=s?o.trim().toLowerCase():o.trim();if(!(!i||i.length>256)&&!n.has(i)&&(n.add(i),r.push(i),r.length>=t))break}return r}function pE(e,t){if(!Array.isArray(e))return[];let s=new Set,n=[];for(let r of e){if(!r||typeof r!="object")continue;let o=r.term,i=r.freq;if(typeof o!="string")continue;let a=o.trim().toLowerCase();if(!a||a.length>128||s.has(a))continue;s.add(a);let d=typeof i=="number"&&Number.isFinite(i)&&i>0?Math.floor(i):1;if(n.push({term:a,frequency:d}),n.length>=t)break}return n}function mE(e,t){if(!Array.isArray(e))return[];let s=[];for(let n of e){if(!n||typeof n!="object")continue;let r=n.error_type,o=n.snippet,i=n.file,a=typeof r=="string"&&r.trim().length>0?r.trim().slice(0,64):"unknown",d=typeof o=="string"?o.trim().replace(/\s+/g," ").slice(0,256):"";if(!d)continue;let l=typeof i=="string"&&i.trim().length>0?i.trim().slice(0,256):null,u=tE("sha256").update(`${a}::${d}`).digest("hex").slice(0,12);if(s.push({error_type:a,message_hash:u,snippet:d,file:l}),s.length>=t)break}return s}function gE(e){let t=e.trim();try{let m=JSON.parse(t);typeof m.result=="string"&&(t=m.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let s=t.indexOf("{"),n=t.lastIndexOf("}");if(s===-1||n===-1||n<=s)return null;let r=t.slice(s,n+1),o;try{o=JSON.parse(r)}catch{return null}let i=jr(o.files_written,200),a=jr(o.brands_mentioned,oE),d=pE(o.terms_introduced,rE),l=jr(o.plan_ids_referenced,iE),u=mE(o.bug_signatures,aE);return i.length===0&&a.length===0&&d.length===0&&l.length===0&&u.length===0&&!uE(o.files_written)?null:{files_written:i,brands_mentioned:a,terms_introduced:d,plan_ids_referenced:l,bug_signatures:u}}var Ur=null;async function fE(e,t){return Ur?Ur(e,t):bt(e,[],{model:t})}async function _E(e,t={}){if(t.signal?.aborted)return{session_id:e,ok:!1,failed:"aborted"};let s=lE(e);if(!s)return{session_id:e,ok:!1,skipped:"session-not-found"};let n=Nc(s.meta,{force:t.force});if(!n.eligible)return{session_id:e,ok:!1,skipped:n.reason};if(!Ur&&!ne())return{session_id:e,ok:!1,failed:"claude-cli-missing"};let r=dE(s),o=t.model??Br,i=await fE(r,o);if(!i.success){let u=(i.stderr||i.stdout||"").slice(0,400),p=u?we(u).redacted:void 0;return{session_id:e,ok:!1,failed:"claude-cli-error",exit_code:i.exitCode,stderr_excerpt:p}}let a=gE(i.stdout);if(!a){let u=i.stdout.slice(0,400),p=u?we(u).redacted:void 0;return{session_id:e,ok:!1,failed:"parse-failed",exit_code:i.exitCode,stderr_excerpt:p}}let d=hE(i.stdout),l=Cc({session_id:e,files_written:a.files_written,brands_mentioned:a.brands_mentioned,terms_introduced:a.terms_introduced,plan_ids_referenced:a.plan_ids_referenced,bug_signatures:a.bug_signatures,raw_extraction:{model:o,usage:d,raw_response_excerpt:i.stdout.slice(0,4e3)},extractor_version:is});return{session_id:e,ok:!0,index:l,usage:d}}function hE(e){try{let t=JSON.parse(e.trim());if(t&&typeof t=="object"&&t.usage){let s=t.usage,n={};return typeof s.input_tokens=="number"&&(n.input_tokens=s.input_tokens),typeof s.output_tokens=="number"&&(n.output_tokens=s.output_tokens),n}}catch{}return null}function EE(e={}){let t=_(),s=[],n=[];typeof e.projectId=="number"&&(s.push("s.project_id = ?"),n.push(e.projectId));let r=Math.max(1,Math.min(1e4,e.limit??1e3)),o=s.length?`WHERE ${s.join(" AND ")}`:"",i=t.prepare(`SELECT s.id,
1359
+ `)}}function Iw(e){let{meta:t}=e;return["You are extracting a structured Output Index from a Claude Code session for a local knowledge graph.","","Read the transcript and produce a single JSON object with EXACTLY these fields. Output JSON only \u2014 no Markdown fences, no commentary, no explanation.","","Fields (every field MUST appear; use [] for empty):","- files_written: array of strings. Relative or absolute file paths the assistant created, edited, or extensively discussed (Write/Edit tool calls or paths quoted verbatim).",'- brands_mentioned: array of strings. Distinct proper-noun company / product / brand names mentioned. Examples of valid: "Glaser Group", "Apollo", "TikTok", "Cloudflare R2". NOT generic terms like "the company" or "users".','- terms_introduced: array of objects { "term": "<lowercase phrase>", "freq": <int> }. Distinctive multi-word noun phrases the session introduced or extensively discussed. Skip generic words. Cap at 30 entries.','- plan_ids_referenced: array of strings. Planning identifiers like "v0.18.A", "Phase D", "L3", "MASTER-PLAN-cognitive-graph". Empty array if none.','- bug_signatures: array of objects { "error_type": "<class or null>", "snippet": "<first line of the error>", "file": "<path or null>" }. Errors actually encountered in the session (not hypothetical). The error_type is the exception class (e.g. "TypeError", "ReferenceError") or null if unspecified.',"","Hard constraints:","- Do NOT fabricate. If a field has no concrete content from the transcript, output an empty array.","- Output JSON ONLY. No backticks, no markdown, no preamble.","- terms_introduced \u2264 30 entries; brands_mentioned \u2264 30 distinct entries; plan_ids_referenced \u2264 30; bug_signatures \u2264 30.","",`Session: ${t.alias??t.id.slice(0,8)}`,`Project: ${t.project??"unknown"}`,t.first_user_message?`Opening prompt: ${t.first_user_message.slice(0,500)}`:"","","Transcript excerpt (may be truncated):","---",e.excerpt,"---"].filter(Boolean).join(`
1360
+ `)}function Mw(e){return Array.isArray(e)&&e.every(t=>typeof t=="string")}function Yo(e,t,n=!1){if(!Array.isArray(e))return[];let s=new Set,r=[];for(let o of e){if(typeof o!="string")continue;let i=n?o.trim().toLowerCase():o.trim();if(!(!i||i.length>256)&&!s.has(i)&&(s.add(i),r.push(i),r.length>=t))break}return r}function Dw(e,t){if(!Array.isArray(e))return[];let n=new Set,s=[];for(let r of e){if(!r||typeof r!="object")continue;let o=r.term,i=r.freq;if(typeof o!="string")continue;let a=o.trim().toLowerCase();if(!a||a.length>128||n.has(a))continue;n.add(a);let d=typeof i=="number"&&Number.isFinite(i)&&i>0?Math.floor(i):1;if(s.push({term:a,frequency:d}),s.length>=t)break}return s}function $w(e,t){if(!Array.isArray(e))return[];let n=[];for(let s of e){if(!s||typeof s!="object")continue;let r=s.error_type,o=s.snippet,i=s.file,a=typeof r=="string"&&r.trim().length>0?r.trim().slice(0,64):"unknown",d=typeof o=="string"?o.trim().replace(/\s+/g," ").slice(0,256):"";if(!d)continue;let l=typeof i=="string"&&i.trim().length>0?i.trim().slice(0,256):null,u=Rw("sha256").update(`${a}::${d}`).digest("hex").slice(0,12);if(n.push({error_type:a,message_hash:u,snippet:d,file:l}),n.length>=t)break}return n}function Pw(e){let t=e.trim();try{let p=JSON.parse(t);typeof p.result=="string"&&(t=p.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let n=t.indexOf("{"),s=t.lastIndexOf("}");if(n===-1||s===-1||s<=n)return null;let r=t.slice(n,s+1),o;try{o=JSON.parse(r)}catch{return null}let i=Yo(o.files_written,200),a=Yo(o.brands_mentioned,Aw),d=Dw(o.terms_introduced,Cw),l=Yo(o.plan_ids_referenced,Lw),u=$w(o.bug_signatures,Nw);return i.length===0&&a.length===0&&d.length===0&&l.length===0&&u.length===0&&!Mw(o.files_written)?null:{files_written:i,brands_mentioned:a,terms_introduced:d,plan_ids_referenced:l,bug_signatures:u}}var qo=null;async function Fw(e,t){return qo?qo(e,t):At(e,[],{model:t})}async function jw(e,t={}){if(t.signal?.aborted)return{session_id:e,ok:!1,failed:"aborted"};let n=vw(e);if(!n)return{session_id:e,ok:!1,skipped:"session-not-found"};let s=bd(n.meta,{force:t.force});if(!s.eligible)return{session_id:e,ok:!1,skipped:s.reason};if(!qo&&!ie())return{session_id:e,ok:!1,failed:"claude-cli-missing"};let r=Iw(n),o=t.model??Vo,i=await Fw(r,o);if(!i.success){let u=(i.stderr||i.stdout||"").slice(0,400),m=u?Ce(u).redacted:void 0;return{session_id:e,ok:!1,failed:"claude-cli-error",exit_code:i.exitCode,stderr_excerpt:m}}let a=Pw(i.stdout);if(!a){let u=i.stdout.slice(0,400),m=u?Ce(u).redacted:void 0;return{session_id:e,ok:!1,failed:"parse-failed",exit_code:i.exitCode,stderr_excerpt:m}}let d=Uw(i.stdout),l=hd({session_id:e,files_written:a.files_written,brands_mentioned:a.brands_mentioned,terms_introduced:a.terms_introduced,plan_ids_referenced:a.plan_ids_referenced,bug_signatures:a.bug_signatures,raw_extraction:{model:o,usage:d,raw_response_excerpt:i.stdout.slice(0,4e3)},extractor_version:xn});return{session_id:e,ok:!0,index:l,usage:d}}function Uw(e){try{let t=JSON.parse(e.trim());if(t&&typeof t=="object"&&t.usage){let n=t.usage,s={};return typeof n.input_tokens=="number"&&(s.input_tokens=n.input_tokens),typeof n.output_tokens=="number"&&(s.output_tokens=n.output_tokens),s}}catch{}return null}function Bw(e={}){let t=E(),n=[],s=[];typeof e.projectId=="number"&&(n.push("s.project_id = ?"),s.push(e.projectId));let r=Math.max(1,Math.min(1e4,e.limit??1e3)),o=n.length?`WHERE ${n.join(" AND ")}`:"",i=t.prepare(`SELECT s.id,
1224
1361
  NULLIF(sa.alias, '') AS alias,
1225
1362
  s.auto_title,
1226
1363
  s.auto_title_source,
@@ -1232,13 +1369,13 @@ show full content: recall paste --show <id>
1232
1369
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1233
1370
  LEFT JOIN projects p ON p.id = s.project_id
1234
1371
  ${o}
1235
- ORDER BY COALESCE(s.started_at, ''), s.id`).all(...n),a=[],d=new Map;for(let l of i){let u={...l,alias_source:l.alias?"manual":null},p=Nc(u,{force:e.force});if(!p.eligible){let m=p.reason??"session-not-found";d.set(m,(d.get(m)??0)+1);continue}if(a.push(u),a.length>=r)break}return{eligible:a,skipped:d}}async function Ac(e={}){let t=EE({projectId:e.projectId,limit:e.limit,force:e.force}),s={total:t.eligible.length,processed:0,ok:0,failed:0,skipped:0,current_session_id:null,total_input_tokens:0,total_output_tokens:0};for(let r of t.skipped.values())s.skipped+=r;e.onProgress?.({...s});let n=[];for(let r of t.eligible){if(e.signal?.aborted)break;s.current_session_id=r.id,e.onProgress?.({...s});let o=await _E(r.id,{model:e.model,force:e.force,signal:e.signal});n.push(o),e.onResult?.(o),o.ok?s.ok+=1:o.skipped?s.skipped+=1:s.failed+=1,o.usage?.input_tokens&&(s.total_input_tokens+=o.usage.input_tokens),o.usage?.output_tokens&&(s.total_output_tokens+=o.usage.output_tokens),s.processed+=1,e.onProgress?.({...s})}return s.current_session_id=null,e.onProgress?.({...s}),{progress:s,results:n}}Be();async function Oc(e){if(!e.project){console.error("--project <name> is required."),process.exitCode=1;return}let t=Ee(e.project);if(!t){console.error(`Project "${e.project}" not found.`),process.exitCode=1;return}if(!ne()){console.error("claude CLI not found on PATH. Install Claude Code first, then re-run."),process.exitCode=1;return}let s=e.limit?Math.max(1,Number(e.limit)):200,n=e.model??Br;e.json||console.log(`Extracting Output Index \u2014 project "${t.name}" \u2014 extractor v${is} \u2014 model ${n} \u2014 up to ${s} sessions\u2026`);let r=Date.now(),o=-1,{progress:i,results:a}=await Ac({projectId:t.id,limit:s,force:e.force,model:n,onProgress:u=>{if(e.json||u.processed===o)return;o=u.processed;let p=u.total>0?` (${Math.round(u.processed/u.total*100)}%)`:"";process.stdout.write(`\r ${u.processed}/${u.total}${p} ok=${u.ok} failed=${u.failed} skipped=${u.skipped} in=${u.total_input_tokens} out=${u.total_output_tokens} `)}});e.json||process.stdout.write(`
1236
- `);let d=Number(((Date.now()-r)/1e3).toFixed(1)),l=i.total>0?Math.round(i.ok/i.total*1e3)/10:0;if(e.json){let u=a.filter(p=>!p.ok&&p.failed).map(p=>({session_id:p.session_id,failed:p.failed,exit_code:p.exit_code??null}));console.log(JSON.stringify({project:t.name,extractor_version:is,model:n,progress:i,acceptance_pct:l,failures:u.slice(0,20),elapsed_seconds:d},null,2))}else console.log(`Done in ${d}s \u2014 ok=${i.ok}/${i.total} (${l}%) failed=${i.failed} skipped=${i.skipped}`),console.log(`Token spend: input=${i.total_input_tokens} output=${i.total_output_tokens}`),i.total>0&&l<90?(console.log(` \u26A0 Phase D acceptance bar is 90% non-empty Output Index; actual ${l}%. Inspect failures and re-run.`),process.exitCode=1):i.total===0&&console.log(" \u24D8 No eligible sessions (all sessions were skipped or already extracted).")}w();as();var Qs={citation:"same-project",similar:"same-project",skill_track:"same-project",bug_pattern:"cross-project",wiki_link:"cross-project",temporal_proximity:"same-project"},AE=2,OE=.25,vE=5,IE=60,ME=25;function Zs(e){return e.trim().toLowerCase()}function DE(e){let t=new Set;for(let s of e.files_written)t.add(`file:${Zs(s)}`);for(let s of e.brands_mentioned)t.add(`brand:${Zs(s)}`);for(let s of e.terms_introduced)t.add(`term:${Zs(s)}`);for(let s of e.plan_ids_referenced)t.add(`plan:${Zs(s)}`);return t}function $E(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/IE);return Math.max(.2,t)}function PE(e,t){if(!e||!t)return 0;let s=Date.parse(e),n=Date.parse(t);return!Number.isFinite(s)||!Number.isFinite(n)?0:Math.abs(n-s)/(1e3*60*60*24)}function FE(e){let t=os(e);return t?{session_id:t.session_id,files_written:t.files_written,brands_mentioned:t.brands_mentioned,terms_introduced:t.terms_introduced.map(s=>s.term),plan_ids_referenced:t.plan_ids_referenced}:null}function jE(e){return _().prepare(`SELECT s.id AS id, s.project_id AS project_id, s.started_at AS started_at
1372
+ ORDER BY COALESCE(s.started_at, ''), s.id`).all(...s),a=[],d=new Map;for(let l of i){let u={...l,alias_source:l.alias?"manual":null},m=bd(u,{force:e.force});if(!m.eligible){let p=m.reason??"session-not-found";d.set(p,(d.get(p)??0)+1);continue}if(a.push(u),a.length>=r)break}return{eligible:a,skipped:d}}async function Sd(e={}){let t=Bw({projectId:e.projectId,limit:e.limit,force:e.force}),n={total:t.eligible.length,processed:0,ok:0,failed:0,skipped:0,current_session_id:null,total_input_tokens:0,total_output_tokens:0};for(let r of t.skipped.values())n.skipped+=r;e.onProgress?.({...n});let s=[];for(let r of t.eligible){if(e.signal?.aborted)break;n.current_session_id=r.id,e.onProgress?.({...n});let o=await jw(r.id,{model:e.model,force:e.force,signal:e.signal});s.push(o),e.onResult?.(o),o.ok?n.ok+=1:o.skipped?n.skipped+=1:n.failed+=1,o.usage?.input_tokens&&(n.total_input_tokens+=o.usage.input_tokens),o.usage?.output_tokens&&(n.total_output_tokens+=o.usage.output_tokens),n.processed+=1,e.onProgress?.({...n})}return n.current_session_id=null,e.onProgress?.({...n}),{progress:n,results:s}}Ye();async function wd(e){if(!e.project){console.error("--project <name> is required."),process.exitCode=1;return}let t=Te(e.project);if(!t){console.error(`Project "${e.project}" not found.`),process.exitCode=1;return}if(!ie()){console.error("claude CLI not found on PATH. Install Claude Code first, then re-run."),process.exitCode=1;return}let n=e.limit?Math.max(1,Number(e.limit)):200,s=e.model??Vo;e.json||console.log(`Extracting Output Index \u2014 project "${t.name}" \u2014 extractor v${xn} \u2014 model ${s} \u2014 up to ${n} sessions\u2026`);let r=Date.now(),o=-1,{progress:i,results:a}=await Sd({projectId:t.id,limit:n,force:e.force,model:s,onProgress:u=>{if(e.json||u.processed===o)return;o=u.processed;let m=u.total>0?` (${Math.round(u.processed/u.total*100)}%)`:"";process.stdout.write(`\r ${u.processed}/${u.total}${m} ok=${u.ok} failed=${u.failed} skipped=${u.skipped} in=${u.total_input_tokens} out=${u.total_output_tokens} `)}});e.json||process.stdout.write(`
1373
+ `);let d=Number(((Date.now()-r)/1e3).toFixed(1)),l=i.total>0?Math.round(i.ok/i.total*1e3)/10:0;if(e.json){let u=a.filter(m=>!m.ok&&m.failed).map(m=>({session_id:m.session_id,failed:m.failed,exit_code:m.exit_code??null}));console.log(JSON.stringify({project:t.name,extractor_version:xn,model:s,progress:i,acceptance_pct:l,failures:u.slice(0,20),elapsed_seconds:d},null,2))}else console.log(`Done in ${d}s \u2014 ok=${i.ok}/${i.total} (${l}%) failed=${i.failed} skipped=${i.skipped}`),console.log(`Token spend: input=${i.total_input_tokens} output=${i.total_output_tokens}`),i.total>0&&l<90?(console.log(` \u26A0 Phase D acceptance bar is 90% non-empty Output Index; actual ${l}%. Inspect failures and re-run.`),process.exitCode=1):i.total===0&&console.log(" \u24D8 No eligible sessions (all sessions were skipped or already extracted).")}R();Cn();var Us={citation:"same-project",similar:"same-project",skill_track:"same-project",bug_pattern:"cross-project",wiki_link:"cross-project",temporal_proximity:"same-project"},Zw=2,ey=.25,ty=5,ny=60,sy=25;function js(e){return e.trim().toLowerCase()}function ry(e){let t=new Set;for(let n of e.files_written)t.add(`file:${js(n)}`);for(let n of e.brands_mentioned)t.add(`brand:${js(n)}`);for(let n of e.terms_introduced)t.add(`term:${js(n)}`);for(let n of e.plan_ids_referenced)t.add(`plan:${js(n)}`);return t}function oy(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/ny);return Math.max(.2,t)}function iy(e,t){if(!e||!t)return 0;let n=Date.parse(e),s=Date.parse(t);return!Number.isFinite(n)||!Number.isFinite(s)?0:Math.abs(s-n)/(1e3*60*60*24)}function ay(e){let t=kn(e);return t?{session_id:t.session_id,files_written:t.files_written,brands_mentioned:t.brands_mentioned,terms_introduced:t.terms_introduced.map(n=>n.term),plan_ids_referenced:t.plan_ids_referenced}:null}function cy(e){return E().prepare(`SELECT s.id AS id, s.project_id AS project_id, s.started_at AS started_at
1237
1374
  FROM sessions s
1238
1375
  JOIN session_output_index oi ON oi.session_id = s.id
1239
1376
  WHERE s.project_id = ?
1240
- ORDER BY COALESCE(s.started_at, ''), s.id`).all(e)}function UE(e,t){let s=new Map,n=new Map,r=new Map;for(let o of e){r.set(o.id,o.started_at);let i=t.get(o.id);if(!i)continue;let a=DE(i);if(a.size!==0){n.set(o.id,a);for(let d of a){let l=s.get(d);l?l.push(o.id):s.set(d,[o.id])}}}return{posting:s,vocab:n,startedAt:r}}function BE(e,t){let s=t.vocab.get(e),n=t.startedAt.get(e)??null;if(!s||!n)return[];let r=new Map;for(let i of s){let a=t.posting.get(i);if(a)for(let d of a){if(d===e)continue;let l=t.startedAt.get(d);if(!l||l>=n)continue;let u=r.get(d);u?u.push(i):r.set(d,[i])}}let o=[];for(let[i,a]of r){let d=a.length;if(d<AE)continue;let l=t.startedAt.get(i)??null,u=PE(n,l),p=$E(u),m=Math.min(1,d/vE*p);if(m<OE)continue;let g=a.slice(0,12);o.push({target_session_id:i,matched_terms:g,overlap:d,days_apart:Math.round(u*10)/10,recency:Math.round(p*1e3)/1e3,confidence:Math.round(m*1e3)/1e3})}return o.sort((i,a)=>a.confidence-i.confidence),o.slice(0,ME)}async function Uc(e){if(Qs.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project \u2014 refusing to run inference");let t=jE(e.projectId),s=new Map;for(let i of t){let a=FE(i.id);a&&s.set(i.id,a)}let n=UE(t,s),r={total_sessions:t.length,processed_sessions:0,suggestions_created:0,suggestions_skipped_existing:0,current_session_id:null};e.onProgress?.({...r});let o=[];for(let i of t){if(e.signal?.aborted)break;r.current_session_id=i.id,e.onProgress?.({...r});let a=BE(i.id,n);for(let d of a)try{let l=ot({source_session_id:i.id,target_session_id:d.target_session_id,link_type:"citation",confidence:d.confidence,evidence:{matched_terms:d.matched_terms,overlap_count:d.overlap,recency:d.recency,days_apart:d.days_apart},inferred_by:"L2"});o.push(l.id),r.suggestions_created+=1}catch(l){console.error("[citation-inference] createSuggestion failed:",l)}r.processed_sessions+=1,e.onProgress?.({...r})}return r.current_session_id=null,e.onProgress?.({...r}),{progress:r,suggestion_ids:o}}async function Bc(e){if(!e.project){console.error("--project <name> is required."),process.exitCode=1;return}let t=Ee(e.project);if(!t){console.error(`Project "${e.project}" not found.`),process.exitCode=1;return}e.json||console.log(`Inferring citation suggestions \u2014 project "${t.name}"\u2026`);let s=Date.now(),n=-1,r=await Uc({projectId:t.id,onProgress:i=>{if(e.json||i.processed_sessions===n)return;n=i.processed_sessions;let a=i.total_sessions>0?` (${Math.round(i.processed_sessions/i.total_sessions*100)}%)`:"";process.stdout.write(`\r ${i.processed_sessions}/${i.total_sessions} sessions${a} suggestions=${i.suggestions_created} `)}});e.json||process.stdout.write(`
1241
- `);let o=Number(((Date.now()-s)/1e3).toFixed(1));e.json?console.log(JSON.stringify({project:t.name,progress:r.progress,suggestion_count:r.suggestion_ids.length,sample_suggestion_ids:r.suggestion_ids.slice(0,25),elapsed_seconds:o},null,2)):(console.log(`Done in ${o}s \u2014 sessions=${r.progress.processed_sessions}/${r.progress.total_sessions} suggestions=${r.progress.suggestions_created}`),r.progress.total_sessions===0?console.log(` \u24D8 No sessions had a populated Output Index. Run \`recall extract-outputs --project ${t.name}\` first.`):r.progress.suggestions_created===0?console.log(" \u24D8 No suggestions met the confidence threshold. This is expected on small corpora; the Output Index needs more shared vocabulary across sessions for citations to surface."):console.log(" Review the queue at GET /api/links/suggestions?status=pending (Phase F UI ships the queue review)."))}import{existsSync as tb,readFileSync as sb}from"node:fs";import{join as nb}from"node:path";w();as();var HE=/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/gi,WE=[/\bv\d+\.\d+(?:\.[A-Za-z]+)?\b/g,/\bPhase\s+[A-Ha-h]\b/g,/\bL\d+\b/g,/MASTER-PLAN-[A-Za-z-]+/g],XE=.95,JE=.85,GE=.7,Yr=50,YE=50;function zE(e){if(!e)return[];let t=new Set,s=[],n=e.match(HE);if(!n)return s;for(let r of n){let o=r.toLowerCase();if(!t.has(o)&&(t.add(o),s.push(o),s.length>=Yr))break}return s}function qE(e){if(!e)return[];let t=new Set,s=[];for(let n of WE){n.lastIndex=0;let r=e.match(n);if(r){for(let o of r){let i=o.trim().toLowerCase().replace(/\s+/g," ");if(!(!i||i.length>64)&&!t.has(i)&&(t.add(i),s.push(i),s.length>=Yr))break}if(s.length>=Yr)break}}return s}function zr(e){let t=_();return typeof e=="number"?t.prepare("SELECT id, project_id FROM sessions WHERE project_id = ?").all(e):t.prepare("SELECT id, project_id FROM sessions").all()}function Hc(e){let t=_();return typeof e=="number"?t.prepare(`SELECT m.uuid AS message_uuid, m.session_id, m.content_text,
1377
+ ORDER BY COALESCE(s.started_at, ''), s.id`).all(e)}function ly(e,t){let n=new Map,s=new Map,r=new Map;for(let o of e){r.set(o.id,o.started_at);let i=t.get(o.id);if(!i)continue;let a=ry(i);if(a.size!==0){s.set(o.id,a);for(let d of a){let l=n.get(d);l?l.push(o.id):n.set(d,[o.id])}}}return{posting:n,vocab:s,startedAt:r}}function dy(e,t){let n=t.vocab.get(e),s=t.startedAt.get(e)??null;if(!n||!s)return[];let r=new Map;for(let i of n){let a=t.posting.get(i);if(a)for(let d of a){if(d===e)continue;let l=t.startedAt.get(d);if(!l||l>=s)continue;let u=r.get(d);u?u.push(i):r.set(d,[i])}}let o=[];for(let[i,a]of r){let d=a.length;if(d<Zw)continue;let l=t.startedAt.get(i)??null,u=iy(s,l),m=oy(u),p=Math.min(1,d/ty*m);if(p<ey)continue;let g=a.slice(0,12);o.push({target_session_id:i,matched_terms:g,overlap:d,days_apart:Math.round(u*10)/10,recency:Math.round(m*1e3)/1e3,confidence:Math.round(p*1e3)/1e3})}return o.sort((i,a)=>a.confidence-i.confidence),o.slice(0,sy)}async function Nd(e){if(Us.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project \u2014 refusing to run inference");let t=cy(e.projectId),n=new Map;for(let i of t){let a=ay(i.id);a&&n.set(i.id,a)}let s=ly(t,n),r={total_sessions:t.length,processed_sessions:0,suggestions_created:0,suggestions_skipped_existing:0,current_session_id:null};e.onProgress?.({...r});let o=[];for(let i of t){if(e.signal?.aborted)break;r.current_session_id=i.id,e.onProgress?.({...r});let a=dy(i.id,s);for(let d of a)try{let l=pt({source_session_id:i.id,target_session_id:d.target_session_id,link_type:"citation",confidence:d.confidence,evidence:{matched_terms:d.matched_terms,overlap_count:d.overlap,recency:d.recency,days_apart:d.days_apart},inferred_by:"L2"});o.push(l.id),r.suggestions_created+=1}catch(l){console.error("[citation-inference] createSuggestion failed:",l)}r.processed_sessions+=1,e.onProgress?.({...r})}return r.current_session_id=null,e.onProgress?.({...r}),{progress:r,suggestion_ids:o}}async function Od(e){if(!e.project){console.error("--project <name> is required."),process.exitCode=1;return}let t=Te(e.project);if(!t){console.error(`Project "${e.project}" not found.`),process.exitCode=1;return}e.json||console.log(`Inferring citation suggestions \u2014 project "${t.name}"\u2026`);let n=Date.now(),s=-1,r=await Nd({projectId:t.id,onProgress:i=>{if(e.json||i.processed_sessions===s)return;s=i.processed_sessions;let a=i.total_sessions>0?` (${Math.round(i.processed_sessions/i.total_sessions*100)}%)`:"";process.stdout.write(`\r ${i.processed_sessions}/${i.total_sessions} sessions${a} suggestions=${i.suggestions_created} `)}});e.json||process.stdout.write(`
1378
+ `);let o=Number(((Date.now()-n)/1e3).toFixed(1));e.json?console.log(JSON.stringify({project:t.name,progress:r.progress,suggestion_count:r.suggestion_ids.length,sample_suggestion_ids:r.suggestion_ids.slice(0,25),elapsed_seconds:o},null,2)):(console.log(`Done in ${o}s \u2014 sessions=${r.progress.processed_sessions}/${r.progress.total_sessions} suggestions=${r.progress.suggestions_created}`),r.progress.total_sessions===0?console.log(` \u24D8 No sessions had a populated Output Index. Run \`recall extract-outputs --project ${t.name}\` first.`):r.progress.suggestions_created===0?console.log(" \u24D8 No suggestions met the confidence threshold. This is expected on small corpora; the Output Index needs more shared vocabulary across sessions for citations to surface."):console.log(" Review the queue at GET /api/links/suggestions?status=pending (Phase F UI ships the queue review)."))}import{existsSync as Ry,readFileSync as ky}from"node:fs";import{join as xy}from"node:path";R();Cn();var uy=/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/gi,my=[/\bv\d+\.\d+(?:\.[A-Za-z]+)?\b/g,/\bPhase\s+[A-Ha-h]\b/g,/\bL\d+\b/g,/MASTER-PLAN-[A-Za-z-]+/g],py=.95,gy=.85,fy=.7,ni=50,_y=50;function hy(e){if(!e)return[];let t=new Set,n=[],s=e.match(uy);if(!s)return n;for(let r of s){let o=r.toLowerCase();if(!t.has(o)&&(t.add(o),n.push(o),n.length>=ni))break}return n}function Ey(e){if(!e)return[];let t=new Set,n=[];for(let s of my){s.lastIndex=0;let r=e.match(s);if(r){for(let o of r){let i=o.trim().toLowerCase().replace(/\s+/g," ");if(!(!i||i.length>64)&&!t.has(i)&&(t.add(i),n.push(i),n.length>=ni))break}if(n.length>=ni)break}}return n}function si(e){let t=E();return typeof e=="number"?t.prepare("SELECT id, project_id FROM sessions WHERE project_id = ?").all(e):t.prepare("SELECT id, project_id FROM sessions").all()}function vd(e){let t=E();return typeof e=="number"?t.prepare(`SELECT m.uuid AS message_uuid, m.session_id, m.content_text,
1242
1379
  s.project_id
1243
1380
  FROM messages m
1244
1381
  JOIN sessions s ON s.id = m.session_id
@@ -1251,12 +1388,12 @@ show full content: recall paste --show <id>
1251
1388
  JOIN sessions s ON s.id = m.session_id
1252
1389
  WHERE m.is_sidechain = 0
1253
1390
  AND m.content_text IS NOT NULL
1254
- AND length(m.content_text) > 0`).all()}function KE(e){let t=_(),s=typeof e=="number"?"WHERE s.project_id = ?":"",n=typeof e=="number"?[e]:[];return t.prepare(`SELECT oi.session_id AS session_id,
1391
+ AND length(m.content_text) > 0`).all()}function by(e){let t=E(),n=typeof e=="number"?"WHERE s.project_id = ?":"",s=typeof e=="number"?[e]:[];return t.prepare(`SELECT oi.session_id AS session_id,
1255
1392
  s.project_id AS project_id,
1256
1393
  oi.plan_ids_referenced AS plan_ids_json
1257
1394
  FROM session_output_index oi
1258
1395
  JOIN sessions s ON s.id = oi.session_id
1259
- ${s}`).all(...n)}function VE(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(s=>typeof s=="string")}catch{}return[]}function ZE(e={}){if(Qs.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project");let t=zr(e.projectId),s=new Set(t.map(o=>o.id)),n=new Map;for(let o of t)n.set(o.id,o.project_id);let r=0;for(let o of Hc(e.projectId)){if(e.signal?.aborted)break;let i=zE(o.content_text);if(i.length===0)continue;let a=n.get(o.session_id);if(a!==void 0)for(let d of i){if(d===o.session_id)continue;let l=n.get(d);if(!(l===void 0&&!s.has(d))&&!(l!==void 0&&a!==void 0&&l!==a))try{ot({source_session_id:o.session_id,target_session_id:d,link_type:"citation",confidence:XE,evidence:{matched_uuid:d,source_message_uuid:o.message_uuid,scanner:"uuid-ref"},inferred_by:"L1"}),r+=1}catch{}}}return{created:r}}function QE(e={}){let t=KE(e.projectId);if(t.length===0)return{created:0};let s=new Map;for(let a of t){let d=VE(a.plan_ids_json);for(let l of d){let u=l.trim().toLowerCase();if(!u)continue;let p=s.get(u);p?p.push({id:a.session_id,project_id:a.project_id}):s.set(u,[{id:a.session_id,project_id:a.project_id}])}}if(s.size===0)return{created:0};let n=zr(e.projectId),r=new Map;for(let a of n)r.set(a.id,a.project_id);let o=0,i=new Map;for(let a of Hc(e.projectId)){if(e.signal?.aborted)break;let d=qE(a.content_text);if(d.length===0)continue;let l=r.get(a.session_id);if(l!==void 0)for(let u of d){let p=s.get(u);if(p)for(let m of p){if(m.id===a.session_id||m.project_id!==l)continue;let g=i.get(a.session_id);g||(g=new Map,i.set(a.session_id,g));let f=g.get(m.id);f||(f=new Set,g.set(m.id,f)),f.add(u)}}}for(let[a,d]of i)for(let[l,u]of d)try{ot({source_session_id:a,target_session_id:l,link_type:"citation",confidence:JE,evidence:{matched_plan_ids:Array.from(u).slice(0,12),scanner:"plan-ref"},inferred_by:"L1"}),o+=1}catch{}return{created:o}}function eb(e){if(Qs.skill_track!=="same-project")throw new Error("skill_track policy unexpectedly not same-project");if(!e.registry||!Array.isArray(e.registry.terminals))return{created:0};let t=zr(e.projectId),s=new Map,n=new Map;if(t.length>0){let i=_(),a=t.map(u=>u.id),d=a.map(()=>"?").join(","),l=i.prepare(`SELECT id, started_at FROM sessions WHERE id IN (${d})`).all(...a);for(let u of l)n.set(u.id,u.started_at)}for(let i of t)s.set(i.id,i.project_id);let r=new Map;for(let i of e.registry.terminals){let a=e.registry.sessions_by_pid[String(i.shell_pid)]??[];if(a.length===0)continue;let d=`${i.cwd??"<no-cwd>"}::${i.tab_name}`,l=r.get(d);l||(l=new Set,r.set(d,l));for(let u of a)l.add(u)}let o=0;for(let[,i]of r){if(i.size<2)continue;let a=new Map;for(let d of i){let l=s.get(d);if(l===void 0||typeof e.projectId=="number"&&l!==e.projectId)continue;let u=a.get(l);u||(u=[],a.set(l,u)),u.push(d)}for(let[,d]of a){if(d.length<2)continue;d.sort((u,p)=>{let m=n.get(u)??"",g=n.get(p)??"";return m<g?-1:m>g?1:u<p?-1:1});let l=0;for(let u=1;u<d.length&&!e.signal?.aborted;u++)for(let p=0;p<u&&!(l>=YE);p++)try{ot({source_session_id:d[u],target_session_id:d[p],link_type:"skill_track",confidence:GE,evidence:{shared_tab_signature:!0,bucket_size:d.length,scanner:"tab-carry"},inferred_by:"L1"}),o+=1,l+=1}catch{}}}return{created:o}}function Wc(e={}){let t=ZE(e);if(e.signal?.aborted)return{uuid_refs_created:t.created,plan_refs_created:0,tab_carry_created:0,total:t.created};let s=QE(e);if(e.signal?.aborted)return{uuid_refs_created:t.created,plan_refs_created:s.created,tab_carry_created:0,total:t.created+s.created};let n=e.registry?eb({projectId:e.projectId,signal:e.signal,registry:e.registry}):{created:0};return{uuid_refs_created:t.created,plan_refs_created:s.created,tab_carry_created:n.created,total:t.created+s.created+n.created}}P();function rb(){let e=nb(x,"terminals.json");if(tb(e))try{let t=sb(e,"utf8"),s=JSON.parse(t);if(!Array.isArray(s.terminals))return;let n=s.terminals.filter(o=>typeof o.shell_pid=="number"&&typeof o.tab_name=="string").map(o=>({shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd??null})),r=s.sessions_by_pid??{};return{terminals:n,sessions_by_pid:r}}catch{return}}async function Xc(e){let t=null;if(e.project&&(t=Ee(e.project),!t)){console.error(`Project "${e.project}" not found.`),process.exitCode=1;return}let s=rb(),n=t?`project "${t.name}"`:"all projects";e.json||console.log(`L1 deterministic inference \u2014 ${n} \u2014 `+(s?`(tab registry has ${s.terminals.length} entries)`:"(no tab registry; tab-carry scanner skipped)"));let r=Date.now(),o=Wc({projectId:t?.id,registry:s}),i=Number(((Date.now()-r)/1e3).toFixed(2));e.json?console.log(JSON.stringify({project:t?.name??null,...o,elapsed_seconds:i},null,2)):(console.log(`Done in ${i}s \u2014 uuid_refs=${o.uuid_refs_created} plan_refs=${o.plan_refs_created} tab_carry=${o.tab_carry_created} (total=${o.total})`),o.total===0?console.log(" \u24D8 Zero new suggestions. This is expected when L1 has already run against this corpus or when the project has no inter-session references. (Tombstones from prior reject decisions also keep re-runs idempotent.)"):console.log(" Review the queue at GET /api/links/suggestions?status=pending (or the web UI at #view=suggestions)."))}w();Be();as();function qr(e){if(!e||e.length===0)return 0;let t=1,s=0;for(let n of e){if(!Number.isFinite(n))continue;let r=Math.max(0,Math.min(1,n));if(t*=1-r,s+=1,t<=1e-9)return 1}return s===0?0:Math.round((1-t)*1e4)/1e4}var Vr="claude-haiku-4-5-20251001",en=.4,Zr=.95,Jc=30,Gc=240,ob=new Set(["CITATION","SIMILAR","SKILL_TRACK","UNRELATED"]),ib={CITATION:"citation",SIMILAR:"similar",SKILL_TRACK:"skill_track"};function qc(e){return _().prepare(`SELECT s.id,
1396
+ ${n}`).all(...s)}function Sy(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(n=>typeof n=="string")}catch{}return[]}function wy(e={}){if(Us.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project");let t=si(e.projectId),n=new Set(t.map(o=>o.id)),s=new Map;for(let o of t)s.set(o.id,o.project_id);let r=0;for(let o of vd(e.projectId)){if(e.signal?.aborted)break;let i=hy(o.content_text);if(i.length===0)continue;let a=s.get(o.session_id);if(a!==void 0)for(let d of i){if(d===o.session_id)continue;let l=s.get(d);if(!(l===void 0&&!n.has(d))&&!(l!==void 0&&a!==void 0&&l!==a))try{pt({source_session_id:o.session_id,target_session_id:d,link_type:"citation",confidence:py,evidence:{matched_uuid:d,source_message_uuid:o.message_uuid,scanner:"uuid-ref"},inferred_by:"L1"}),r+=1}catch{}}}return{created:r}}function yy(e={}){let t=by(e.projectId);if(t.length===0)return{created:0};let n=new Map;for(let a of t){let d=Sy(a.plan_ids_json);for(let l of d){let u=l.trim().toLowerCase();if(!u)continue;let m=n.get(u);m?m.push({id:a.session_id,project_id:a.project_id}):n.set(u,[{id:a.session_id,project_id:a.project_id}])}}if(n.size===0)return{created:0};let s=si(e.projectId),r=new Map;for(let a of s)r.set(a.id,a.project_id);let o=0,i=new Map;for(let a of vd(e.projectId)){if(e.signal?.aborted)break;let d=Ey(a.content_text);if(d.length===0)continue;let l=r.get(a.session_id);if(l!==void 0)for(let u of d){let m=n.get(u);if(m)for(let p of m){if(p.id===a.session_id||p.project_id!==l)continue;let g=i.get(a.session_id);g||(g=new Map,i.set(a.session_id,g));let f=g.get(p.id);f||(f=new Set,g.set(p.id,f)),f.add(u)}}}for(let[a,d]of i)for(let[l,u]of d)try{pt({source_session_id:a,target_session_id:l,link_type:"citation",confidence:gy,evidence:{matched_plan_ids:Array.from(u).slice(0,12),scanner:"plan-ref"},inferred_by:"L1"}),o+=1}catch{}return{created:o}}function Ty(e){if(Us.skill_track!=="same-project")throw new Error("skill_track policy unexpectedly not same-project");if(!e.registry||!Array.isArray(e.registry.terminals))return{created:0};let t=si(e.projectId),n=new Map,s=new Map;if(t.length>0){let i=E(),a=t.map(u=>u.id),d=a.map(()=>"?").join(","),l=i.prepare(`SELECT id, started_at FROM sessions WHERE id IN (${d})`).all(...a);for(let u of l)s.set(u.id,u.started_at)}for(let i of t)n.set(i.id,i.project_id);let r=new Map;for(let i of e.registry.terminals){let a=e.registry.sessions_by_pid[String(i.shell_pid)]??[];if(a.length===0)continue;let d=`${i.cwd??"<no-cwd>"}::${i.tab_name}`,l=r.get(d);l||(l=new Set,r.set(d,l));for(let u of a)l.add(u)}let o=0;for(let[,i]of r){if(i.size<2)continue;let a=new Map;for(let d of i){let l=n.get(d);if(l===void 0||typeof e.projectId=="number"&&l!==e.projectId)continue;let u=a.get(l);u||(u=[],a.set(l,u)),u.push(d)}for(let[,d]of a){if(d.length<2)continue;d.sort((u,m)=>{let p=s.get(u)??"",g=s.get(m)??"";return p<g?-1:p>g?1:u<m?-1:1});let l=0;for(let u=1;u<d.length&&!e.signal?.aborted;u++)for(let m=0;m<u&&!(l>=_y);m++)try{pt({source_session_id:d[u],target_session_id:d[m],link_type:"skill_track",confidence:fy,evidence:{shared_tab_signature:!0,bucket_size:d.length,scanner:"tab-carry"},inferred_by:"L1"}),o+=1,l+=1}catch{}}}return{created:o}}function Id(e={}){let t=wy(e);if(e.signal?.aborted)return{uuid_refs_created:t.created,plan_refs_created:0,tab_carry_created:0,total:t.created};let n=yy(e);if(e.signal?.aborted)return{uuid_refs_created:t.created,plan_refs_created:n.created,tab_carry_created:0,total:t.created+n.created};let s=e.registry?Ty({projectId:e.projectId,signal:e.signal,registry:e.registry}):{created:0};return{uuid_refs_created:t.created,plan_refs_created:n.created,tab_carry_created:s.created,total:t.created+n.created+s.created}}D();function Cy(){let e=xy(x,"terminals.json");if(Ry(e))try{let t=ky(e,"utf8"),n=JSON.parse(t);if(!Array.isArray(n.terminals))return;let s=n.terminals.filter(o=>typeof o.shell_pid=="number"&&typeof o.tab_name=="string").map(o=>({shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd??null})),r=n.sessions_by_pid??{};return{terminals:s,sessions_by_pid:r}}catch{return}}async function Md(e){let t=null;if(e.project&&(t=Te(e.project),!t)){console.error(`Project "${e.project}" not found.`),process.exitCode=1;return}let n=Cy(),s=t?`project "${t.name}"`:"all projects";e.json||console.log(`L1 deterministic inference \u2014 ${s} \u2014 `+(n?`(tab registry has ${n.terminals.length} entries)`:"(no tab registry; tab-carry scanner skipped)"));let r=Date.now(),o=Id({projectId:t?.id,registry:n}),i=Number(((Date.now()-r)/1e3).toFixed(2));e.json?console.log(JSON.stringify({project:t?.name??null,...o,elapsed_seconds:i},null,2)):(console.log(`Done in ${i}s \u2014 uuid_refs=${o.uuid_refs_created} plan_refs=${o.plan_refs_created} tab_carry=${o.tab_carry_created} (total=${o.total})`),o.total===0?console.log(" \u24D8 Zero new suggestions. This is expected when L1 has already run against this corpus or when the project has no inter-session references. (Tombstones from prior reject decisions also keep re-runs idempotent.)"):console.log(" Review the queue at GET /api/links/suggestions?status=pending (or the web UI at #view=suggestions)."))}R();Ye();Cn();function ri(e){if(!e||e.length===0)return 0;let t=1,n=0;for(let s of e){if(!Number.isFinite(s))continue;let r=Math.max(0,Math.min(1,s));if(t*=1-r,n+=1,t<=1e-9)return 1}return n===0?0:Math.round((1-t)*1e4)/1e4}var ii="claude-haiku-4-5-20251001",Bs=.4,ai=.95,Dd=30,$d=240,Ay=new Set(["CITATION","SIMILAR","SKILL_TRACK","UNRELATED"]),Ly={CITATION:"citation",SIMILAR:"similar",SKILL_TRACK:"skill_track"};function jd(e){return E().prepare(`SELECT s.id,
1260
1397
  s.source_session_id,
1261
1398
  s.target_session_id,
1262
1399
  s.link_type,
@@ -1266,7 +1403,7 @@ show full content: recall paste --show <id>
1266
1403
  FROM session_link_suggestions s
1267
1404
  JOIN sessions src ON src.id = s.source_session_id
1268
1405
  WHERE s.status = 'pending'
1269
- AND src.project_id = ?`).all(e)}function ab(e){if(e.length===0)return new Map;let t=_(),s=e.map(()=>"?").join(","),n=t.prepare(`SELECT s.id,
1406
+ AND src.project_id = ?`).all(e)}function Ny(e){if(e.length===0)return new Map;let t=E(),n=e.map(()=>"?").join(","),s=t.prepare(`SELECT s.id,
1270
1407
  NULLIF(sa.alias, '') AS alias,
1271
1408
  s.auto_title,
1272
1409
  s.first_user_message,
@@ -1275,18 +1412,18 @@ show full content: recall paste --show <id>
1275
1412
  FROM sessions s
1276
1413
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1277
1414
  LEFT JOIN projects p ON p.id = s.project_id
1278
- WHERE s.id IN (${s})`).all(...e),r=new Map;for(let o of n)r.set(o.id,o);return r}function Yc(e,t){if(!e)return t.slice(0,8);let s=e.first_user_message?e.first_user_message.slice(0,80):null;return e.alias??e.auto_title??s??t.slice(0,8)}function zc(e){try{return JSON.parse(e)}catch{return e}}function cb(e){let t=e.minConfidence??en,s=Math.max(1,Math.min(500,e.limit??100)),n=qc(e.projectId);if(n.length===0)return[];let r=new Map,o=new Map;for(let l of n){let u=`${l.source_session_id}|${l.target_session_id}|${l.link_type}`,p=o.get(u);p||(p={source_session_id:l.source_session_id,target_session_id:l.target_session_id,link_type:l.link_type,layers:[]},o.set(u,p));let m=p.layers.find(g=>g.inferred_by===l.inferred_by);m?(m.suggestion_ids.push(l.id),l.confidence>m.confidence&&(m.confidence=l.confidence,m.evidence=zc(l.evidence))):p.layers.push({inferred_by:l.inferred_by,link_type:l.link_type,confidence:l.confidence,evidence:zc(l.evidence),suggestion_ids:[l.id]})}let i=new Set;for(let l of o.values())i.add(l.source_session_id),i.add(l.target_session_id);let a=ab(Array.from(i)),d=[];for(let l of o.values()){let u=qr(l.layers.map(g=>g.confidence));if(u<t)continue;let p=a.get(l.source_session_id),m=a.get(l.target_session_id);d.push({source_session_id:l.source_session_id,target_session_id:l.target_session_id,primary_link_type:l.link_type,combined_confidence:u,layers:l.layers,source_title:Yc(p,l.source_session_id),target_title:Yc(m,l.target_session_id),source_project:p?.project??null})}return d.sort((l,u)=>u.combined_confidence-l.combined_confidence),d.slice(0,s)}function lb(e){let t;try{t=JSON.stringify(e)}catch{t=String(e)}return t.length>Gc&&(t=t.slice(0,Gc)+"\u2026"),t}function db(e){let t=["You are classifying candidate session-pair relationships for a developer knowledge graph.","","Categories:","- CITATION: the source session explicitly references content/decisions from the target (a previous session).","- SIMILAR: same subject matter, but neither cites the other directly.","- SKILL_TRACK: same person doing the same kind of work \u2014 same skill, different tasks.","- UNRELATED: shared evidence is coincidental; no real relationship.","","Use UNRELATED with low confidence when evidence is weak or contradictory.","Output a SINGLE JSON object on one line, no markdown fences, no commentary:",'{"classifications":[{"i":0,"label":"CITATION","conf":0.85,"why":"<<=25 words>"},...]}',"",`Pairs (${e.length}):`];return e.forEach((s,n)=>{t.push(""),t.push(`[${n}] B="${(s.source_title??"").slice(0,80)}" [${s.source_session_id.slice(0,8)}] -> A="${(s.target_title??"").slice(0,80)}" [${s.target_session_id.slice(0,8)}]`),t.push(` primary_type: ${s.primary_link_type}`),t.push(` combined_conf: ${s.combined_confidence.toFixed(3)}`);for(let r of s.layers)t.push(` ${r.inferred_by} ${r.link_type} conf=${r.confidence.toFixed(2)} ev=${lb(r.evidence)}`)}),t.join(`
1279
- `)}var Kr=null;async function ub(e,t){return Kr?Kr(e,t):bt(e,[],{model:t})}function pb(e){try{let t=JSON.parse(e.trim());return{input_tokens:t?.usage?.input_tokens??0,output_tokens:t?.usage?.output_tokens??0}}catch{return{input_tokens:0,output_tokens:0}}}function mb(e){let t=e.trim();try{let i=JSON.parse(t);typeof i.result=="string"&&(t=i.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let s=t.indexOf("{"),n=t.lastIndexOf("}");if(s===-1||n===-1||n<=s)return null;let r;try{r=JSON.parse(t.slice(s,n+1))}catch{return null}if(!Array.isArray(r.classifications))return null;let o=[];for(let i of r.classifications){if(!i||typeof i!="object")continue;let a=i,d=typeof a.i=="number"?a.i:typeof a.pair_index=="number"?a.pair_index:NaN;if(!Number.isInteger(d)||d<0)continue;let l=typeof a.label=="string"?a.label.toUpperCase():"";if(!ob.has(l))continue;let u=typeof a.conf=="number"?a.conf:typeof a.confidence=="number"?a.confidence:NaN;if(!Number.isFinite(u))continue;let p=Math.max(0,Math.min(1,u)),m=typeof a.why=="string"?a.why.trim().slice(0,200):typeof a.reason=="string"?a.reason.trim().slice(0,200):"";o.push({pair_index:d,label:l,confidence:p,reason:m})}return o}async function Kc(e){let t={candidates_total:0,candidates_after_filter:0,classified:0,suggestions_created:0,links_promoted:0,total_input_tokens:0,total_output_tokens:0,failures:[]};if(!Kr&&!ne())return t.failures.push({batch_index:-1,error:"claude CLI not available on PATH"}),t;let s=cb({projectId:e.projectId,minConfidence:e.minConfidence??en,limit:e.limit??100});t.candidates_after_filter=s.length;let n=qc(e.projectId),r=new Set;for(let a of n)r.add(`${a.source_session_id}|${a.target_session_id}|${a.link_type}`);if(t.candidates_total=r.size,s.length===0)return t;let o=e.model??Vr,i=e.autoPromoteThreshold??Zr;for(let a=0,d=0;a<s.length&&!e.signal?.aborted;a+=Jc,d+=1){let l=s.slice(a,a+Jc);e.onBatchStart?.({batch:d,pairs:l.length});let u=db(l),p=await ub(u,o);if(!p.success){t.failures.push({batch_index:d,error:`claude CLI exited ${p.exitCode}: ${p.stderr.slice(0,200)}`});continue}let m=mb(p.stdout);if(!m){t.failures.push({batch_index:d,error:"parse-failed"});continue}let g=pb(p.stdout);t.total_input_tokens+=g.input_tokens,t.total_output_tokens+=g.output_tokens;for(let f of m){if(f.pair_index>=l.length)continue;let h=l[f.pair_index];if(t.classified+=1,f.label==="UNRELATED")continue;let E=ib[f.label];if(E)try{let b=ot({source_session_id:h.source_session_id,target_session_id:h.target_session_id,link_type:E,confidence:f.confidence,evidence:{l4_label:f.label,l4_reason:f.reason,primary_type_l1_l2_l3:h.primary_link_type,combined_pre_l4:h.combined_confidence,scanner:"l4-llm",model:o},inferred_by:"L4"});if(t.suggestions_created+=1,e.autoPromote){let S=h.layers.map(T=>T.confidence);if(S.push(f.confidence),qr(S)>=i)try{Fc(b.id,"approved",{source:"auto"}),t.links_promoted+=1}catch{}}}catch{}}}return t}Be();var gb=1e3;function fb(e){let t=e.minConf?Math.max(0,Math.min(1,Number(e.minConf))):en,s=e.autoPromoteThreshold!==void 0?Math.max(0,Math.min(1,Number(e.autoPromoteThreshold))):Zr,n=e.limit?Math.max(1,Number(e.limit)):gb,r=e.model??Vr,o=!!e.autoPromote||e.autoPromoteThreshold!==void 0;return{minConfidence:t,autoPromoteThreshold:s,limit:n,model:r,autoPromote:o}}async function Vc(e){if(!e.project){console.error("--project <name> is required."),process.exitCode=1;return}let t=Ee(e.project);if(!t){console.error(`Project "${e.project}" not found.`),process.exitCode=1;return}if(!ne()){console.error("claude CLI not found on PATH. Install Claude Code first, then re-run."),process.exitCode=1;return}let{minConfidence:s,autoPromoteThreshold:n,limit:r,model:o,autoPromote:i}=fb(e);e.json||console.log(`L4 inference \u2014 project "${t.name}" \u2014 model ${o} \u2014 pre-filter conf \u2265 ${s} \u2014 limit ${r} \u2014 auto-promote ${i?`ON (\u2265 ${n})`:"OFF"}`);let a=Date.now(),d=await Kc({projectId:t.id,minConfidence:s,limit:r,autoPromote:i,autoPromoteThreshold:n,model:o,onBatchStart:u=>{e.json||process.stdout.write(`\r batch ${u.batch} (${u.pairs} pairs)\u2026 `)}});e.json||process.stdout.write(`
1280
- `);let l=Number(((Date.now()-a)/1e3).toFixed(1));if(e.json)console.log(JSON.stringify({project:t.name,model:o,pre_filter_threshold:s,auto_promote:i,auto_promote_threshold:i?n:null,...d,elapsed_seconds:l},null,2));else{if(console.log(`Done in ${l}s \u2014 pairs=${d.candidates_after_filter}/${d.candidates_total} classified=${d.classified} suggestions=${d.suggestions_created} promoted=${d.links_promoted} failures=${d.failures.length}`),console.log(`Token spend: input=${d.total_input_tokens} output=${d.total_output_tokens}`),d.failures.length>0)for(let u of d.failures.slice(0,3))console.log(` \u26A0 batch ${u.batch_index}: ${u.error}`);d.suggestions_created>0?console.log(" Review the new L4 suggestions at #view=suggestions (filter by inferred_by=L4)."):d.candidates_after_filter===0&&console.log(` \u24D8 No pairs cleared the pre-filter (combined conf \u2265 ${s}). Lower --min-conf to widen the batch, or run more L1/L2/L3 inference first.`)}}w();import{createHash as Tb}from"node:crypto";w();P();import{writeFileSync as _b,readFileSync as TO,existsSync as hb,mkdirSync as Eb,readdirSync as RO,unlinkSync as xO}from"node:fs";import{join as Zc}from"node:path";import{randomUUID as bb}from"node:crypto";var Qr=Zc(x,"bug-patterns");function Sb(){F(),hb(Qr)||Eb(Qr,{recursive:!0})}function tn(e){return{id:e.id,signature_hash:e.signature_hash,example_message:e.example_message,occurrence_count:e.occurrence_count,first_seen_at:e.first_seen_at,last_seen_at:e.last_seen_at,resolved_in_session_id:e.resolved_in_session_id,fix_summary:e.fix_summary}}function yb(e){return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at}}function Qc(e){if(!e.signature_hash)throw new Error("signature_hash is required");if(!e.example_message)throw new Error("example_message is required");if(!Array.isArray(e.member_session_ids)||e.member_session_ids.length===0)throw new Error("at least one member_session_id is required");let t=_(),s=new Date().toISOString(),n=e.id??bb(),r=e.first_seen_at??s,o=e.last_seen_at??s,i=Array.from(new Set(e.member_session_ids));t.transaction(()=>{t.prepare(`INSERT INTO bug_pattern_clusters
1415
+ WHERE s.id IN (${n})`).all(...e),r=new Map;for(let o of s)r.set(o.id,o);return r}function Pd(e,t){if(!e)return t.slice(0,8);let n=e.first_user_message?e.first_user_message.slice(0,80):null;return e.alias??e.auto_title??n??t.slice(0,8)}function Fd(e){try{return JSON.parse(e)}catch{return e}}function Oy(e){let t=e.minConfidence??Bs,n=Math.max(1,Math.min(500,e.limit??100)),s=jd(e.projectId);if(s.length===0)return[];let r=new Map,o=new Map;for(let l of s){let u=`${l.source_session_id}|${l.target_session_id}|${l.link_type}`,m=o.get(u);m||(m={source_session_id:l.source_session_id,target_session_id:l.target_session_id,link_type:l.link_type,layers:[]},o.set(u,m));let p=m.layers.find(g=>g.inferred_by===l.inferred_by);p?(p.suggestion_ids.push(l.id),l.confidence>p.confidence&&(p.confidence=l.confidence,p.evidence=Fd(l.evidence))):m.layers.push({inferred_by:l.inferred_by,link_type:l.link_type,confidence:l.confidence,evidence:Fd(l.evidence),suggestion_ids:[l.id]})}let i=new Set;for(let l of o.values())i.add(l.source_session_id),i.add(l.target_session_id);let a=Ny(Array.from(i)),d=[];for(let l of o.values()){let u=ri(l.layers.map(g=>g.confidence));if(u<t)continue;let m=a.get(l.source_session_id),p=a.get(l.target_session_id);d.push({source_session_id:l.source_session_id,target_session_id:l.target_session_id,primary_link_type:l.link_type,combined_confidence:u,layers:l.layers,source_title:Pd(m,l.source_session_id),target_title:Pd(p,l.target_session_id),source_project:m?.project??null})}return d.sort((l,u)=>u.combined_confidence-l.combined_confidence),d.slice(0,n)}function vy(e){let t;try{t=JSON.stringify(e)}catch{t=String(e)}return t.length>$d&&(t=t.slice(0,$d)+"\u2026"),t}function Iy(e){let t=["You are classifying candidate session-pair relationships for a developer knowledge graph.","","Categories:","- CITATION: the source session explicitly references content/decisions from the target (a previous session).","- SIMILAR: same subject matter, but neither cites the other directly.","- SKILL_TRACK: same person doing the same kind of work \u2014 same skill, different tasks.","- UNRELATED: shared evidence is coincidental; no real relationship.","","Use UNRELATED with low confidence when evidence is weak or contradictory.","Output a SINGLE JSON object on one line, no markdown fences, no commentary:",'{"classifications":[{"i":0,"label":"CITATION","conf":0.85,"why":"<<=25 words>"},...]}',"",`Pairs (${e.length}):`];return e.forEach((n,s)=>{t.push(""),t.push(`[${s}] B="${(n.source_title??"").slice(0,80)}" [${n.source_session_id.slice(0,8)}] -> A="${(n.target_title??"").slice(0,80)}" [${n.target_session_id.slice(0,8)}]`),t.push(` primary_type: ${n.primary_link_type}`),t.push(` combined_conf: ${n.combined_confidence.toFixed(3)}`);for(let r of n.layers)t.push(` ${r.inferred_by} ${r.link_type} conf=${r.confidence.toFixed(2)} ev=${vy(r.evidence)}`)}),t.join(`
1416
+ `)}var oi=null;async function My(e,t){return oi?oi(e,t):At(e,[],{model:t})}function Dy(e){try{let t=JSON.parse(e.trim());return{input_tokens:t?.usage?.input_tokens??0,output_tokens:t?.usage?.output_tokens??0}}catch{return{input_tokens:0,output_tokens:0}}}function $y(e){let t=e.trim();try{let i=JSON.parse(t);typeof i.result=="string"&&(t=i.result.trim())}catch{}t=t.replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").trim();let n=t.indexOf("{"),s=t.lastIndexOf("}");if(n===-1||s===-1||s<=n)return null;let r;try{r=JSON.parse(t.slice(n,s+1))}catch{return null}if(!Array.isArray(r.classifications))return null;let o=[];for(let i of r.classifications){if(!i||typeof i!="object")continue;let a=i,d=typeof a.i=="number"?a.i:typeof a.pair_index=="number"?a.pair_index:NaN;if(!Number.isInteger(d)||d<0)continue;let l=typeof a.label=="string"?a.label.toUpperCase():"";if(!Ay.has(l))continue;let u=typeof a.conf=="number"?a.conf:typeof a.confidence=="number"?a.confidence:NaN;if(!Number.isFinite(u))continue;let m=Math.max(0,Math.min(1,u)),p=typeof a.why=="string"?a.why.trim().slice(0,200):typeof a.reason=="string"?a.reason.trim().slice(0,200):"";o.push({pair_index:d,label:l,confidence:m,reason:p})}return o}async function Ud(e){let t={candidates_total:0,candidates_after_filter:0,classified:0,suggestions_created:0,links_promoted:0,total_input_tokens:0,total_output_tokens:0,failures:[]};if(!oi&&!ie())return t.failures.push({batch_index:-1,error:"claude CLI not available on PATH"}),t;let n=Oy({projectId:e.projectId,minConfidence:e.minConfidence??Bs,limit:e.limit??100});t.candidates_after_filter=n.length;let s=jd(e.projectId),r=new Set;for(let a of s)r.add(`${a.source_session_id}|${a.target_session_id}|${a.link_type}`);if(t.candidates_total=r.size,n.length===0)return t;let o=e.model??ii,i=e.autoPromoteThreshold??ai;for(let a=0,d=0;a<n.length&&!e.signal?.aborted;a+=Dd,d+=1){let l=n.slice(a,a+Dd);e.onBatchStart?.({batch:d,pairs:l.length});let u=Iy(l),m=await My(u,o);if(!m.success){t.failures.push({batch_index:d,error:`claude CLI exited ${m.exitCode}: ${m.stderr.slice(0,200)}`});continue}let p=$y(m.stdout);if(!p){t.failures.push({batch_index:d,error:"parse-failed"});continue}let g=Dy(m.stdout);t.total_input_tokens+=g.input_tokens,t.total_output_tokens+=g.output_tokens;for(let f of p){if(f.pair_index>=l.length)continue;let h=l[f.pair_index];if(t.classified+=1,f.label==="UNRELATED")continue;let _=Ly[f.label];if(_)try{let b=pt({source_session_id:h.source_session_id,target_session_id:h.target_session_id,link_type:_,confidence:f.confidence,evidence:{l4_label:f.label,l4_reason:f.reason,primary_type_l1_l2_l3:h.primary_link_type,combined_pre_l4:h.combined_confidence,scanner:"l4-llm",model:o},inferred_by:"L4"});if(t.suggestions_created+=1,e.autoPromote){let S=h.layers.map(y=>y.confidence);if(S.push(f.confidence),ri(S)>=i)try{Ad(b.id,"approved",{source:"auto"}),t.links_promoted+=1}catch{}}}catch{}}}return t}Ye();var Py=1e3;function Fy(e){let t=e.minConf?Math.max(0,Math.min(1,Number(e.minConf))):Bs,n=e.autoPromoteThreshold!==void 0?Math.max(0,Math.min(1,Number(e.autoPromoteThreshold))):ai,s=e.limit?Math.max(1,Number(e.limit)):Py,r=e.model??ii,o=!!e.autoPromote||e.autoPromoteThreshold!==void 0;return{minConfidence:t,autoPromoteThreshold:n,limit:s,model:r,autoPromote:o}}async function Bd(e){if(!e.project){console.error("--project <name> is required."),process.exitCode=1;return}let t=Te(e.project);if(!t){console.error(`Project "${e.project}" not found.`),process.exitCode=1;return}if(!ie()){console.error("claude CLI not found on PATH. Install Claude Code first, then re-run."),process.exitCode=1;return}let{minConfidence:n,autoPromoteThreshold:s,limit:r,model:o,autoPromote:i}=Fy(e);e.json||console.log(`L4 inference \u2014 project "${t.name}" \u2014 model ${o} \u2014 pre-filter conf \u2265 ${n} \u2014 limit ${r} \u2014 auto-promote ${i?`ON (\u2265 ${s})`:"OFF"}`);let a=Date.now(),d=await Ud({projectId:t.id,minConfidence:n,limit:r,autoPromote:i,autoPromoteThreshold:s,model:o,onBatchStart:u=>{e.json||process.stdout.write(`\r batch ${u.batch} (${u.pairs} pairs)\u2026 `)}});e.json||process.stdout.write(`
1417
+ `);let l=Number(((Date.now()-a)/1e3).toFixed(1));if(e.json)console.log(JSON.stringify({project:t.name,model:o,pre_filter_threshold:n,auto_promote:i,auto_promote_threshold:i?s:null,...d,elapsed_seconds:l},null,2));else{if(console.log(`Done in ${l}s \u2014 pairs=${d.candidates_after_filter}/${d.candidates_total} classified=${d.classified} suggestions=${d.suggestions_created} promoted=${d.links_promoted} failures=${d.failures.length}`),console.log(`Token spend: input=${d.total_input_tokens} output=${d.total_output_tokens}`),d.failures.length>0)for(let u of d.failures.slice(0,3))console.log(` \u26A0 batch ${u.batch_index}: ${u.error}`);d.suggestions_created>0?console.log(" Review the new L4 suggestions at #view=suggestions (filter by inferred_by=L4)."):d.candidates_after_filter===0&&console.log(` \u24D8 No pairs cleared the pre-filter (combined conf \u2265 ${n}). Lower --min-conf to widen the batch, or run more L1/L2/L3 inference first.`)}}R();import{createHash as Jy}from"node:crypto";R();D();import{writeFileSync as jy,readFileSync as A$,existsSync as Uy,mkdirSync as By,readdirSync as L$,unlinkSync as N$}from"node:fs";import{join as Hd}from"node:path";import{randomUUID as Hy}from"node:crypto";var ci=Hd(x,"bug-patterns");function Wy(){j(),Uy(ci)||By(ci,{recursive:!0})}function Hs(e){return{id:e.id,signature_hash:e.signature_hash,example_message:e.example_message,occurrence_count:e.occurrence_count,first_seen_at:e.first_seen_at,last_seen_at:e.last_seen_at,resolved_in_session_id:e.resolved_in_session_id,fix_summary:e.fix_summary}}function Xy(e){return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at}}function Wd(e){if(!e.signature_hash)throw new Error("signature_hash is required");if(!e.example_message)throw new Error("example_message is required");if(!Array.isArray(e.member_session_ids)||e.member_session_ids.length===0)throw new Error("at least one member_session_id is required");let t=E(),n=new Date().toISOString(),s=e.id??Hy(),r=e.first_seen_at??n,o=e.last_seen_at??n,i=Array.from(new Set(e.member_session_ids));t.transaction(()=>{t.prepare(`INSERT INTO bug_pattern_clusters
1281
1418
  (id, signature_hash, example_message, occurrence_count,
1282
1419
  first_seen_at, last_seen_at, resolved_in_session_id, fix_summary)
1283
- VALUES (?, ?, ?, ?, ?, ?, NULL, NULL)`).run(n,e.signature_hash,e.example_message,i.length,r,o);let d=t.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
1420
+ VALUES (?, ?, ?, ?, ?, ?, NULL, NULL)`).run(s,e.signature_hash,e.example_message,i.length,r,o);let d=t.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
1284
1421
  VALUES (?, ?, ?)
1285
- ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let l of i)d.run(n,l,s)})();let a=el(n);if(!a)throw new Error("createCluster succeeded but read-back failed");return nl(n),a}function el(e){let t=_(),s=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!s)return null;let n=t.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ? ORDER BY matched_at ASC, session_id ASC").all(e);return{cluster:tn(s),members:n.map(yb)}}function tl(e,t){if(!e)throw new Error("clusterId is required");if(!Array.isArray(t)||t.length===0)throw new Error("sessionIds must be a non-empty array");let s=_();if(!s.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);let r=new Date().toISOString(),o=0;s.transaction(()=>{let a=s.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
1422
+ ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let l of i)d.run(s,l,n)})();let a=Xd(s);if(!a)throw new Error("createCluster succeeded but read-back failed");return zd(s),a}function Xd(e){let t=E(),n=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!n)return null;let s=t.prepare("SELECT * FROM bug_pattern_members WHERE cluster_id = ? ORDER BY matched_at ASC, session_id ASC").all(e);return{cluster:Hs(n),members:s.map(Xy)}}function Gd(e,t){if(!e)throw new Error("clusterId is required");if(!Array.isArray(t)||t.length===0)throw new Error("sessionIds must be a non-empty array");let n=E();if(!n.prepare("SELECT 1 FROM bug_pattern_clusters WHERE id = ?").get(e))throw new Error(`cluster ${e} not found`);let r=new Date().toISOString(),o=0;n.transaction(()=>{let a=n.prepare(`INSERT INTO bug_pattern_members (cluster_id, session_id, matched_at)
1286
1423
  VALUES (?, ?, ?)
1287
- ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let d of Array.from(new Set(t)))a.run(e,d,r).changes>0&&(o+=1);if(o>0){let d=s.prepare("SELECT COUNT(*) AS n FROM bug_pattern_members WHERE cluster_id = ?").get(e).n;s.prepare(`UPDATE bug_pattern_clusters
1424
+ ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let d of Array.from(new Set(t)))a.run(e,d,r).changes>0&&(o+=1);if(o>0){let d=n.prepare("SELECT COUNT(*) AS n FROM bug_pattern_members WHERE cluster_id = ?").get(e).n;n.prepare(`UPDATE bug_pattern_clusters
1288
1425
  SET occurrence_count = ?, last_seen_at = ?
1289
- WHERE id = ?`).run(d,r,e)}})(),o>0&&nl(e);let i=s.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:tn(i),added:o}}function wb(e){let t=e.first_user_message?e.first_user_message.slice(0,80):null,s=e.alias??e.auto_title??t??e.session_id.slice(0,8);return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at,title:s,alias:e.alias,auto_title:e.auto_title,project:e.project,started_at:e.started_at}}function sl(e){let t=_(),s=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT m.cluster_id, m.session_id, m.matched_at,
1426
+ WHERE id = ?`).run(d,r,e)}})(),o>0&&zd(e);let i=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:Hs(i),added:o}}function Gy(e){let t=e.first_user_message?e.first_user_message.slice(0,80):null,n=e.alias??e.auto_title??t??e.session_id.slice(0,8);return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at,title:n,alias:e.alias,auto_title:e.auto_title,project:e.project,started_at:e.started_at}}function Jd(e){let t=E(),n=t.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT m.cluster_id, m.session_id, m.matched_at,
1290
1427
  NULLIF(sa.alias, '') AS alias,
1291
1428
  s.auto_title,
1292
1429
  s.first_user_message,
@@ -1297,37 +1434,37 @@ show full content: recall paste --show <id>
1297
1434
  LEFT JOIN session_aliases sa ON sa.session_id = m.session_id
1298
1435
  LEFT JOIN projects p ON p.id = s.project_id
1299
1436
  WHERE m.cluster_id = ?
1300
- ORDER BY COALESCE(s.started_at, ''), m.matched_at, m.session_id`).all(e);return{cluster:tn(s),members:n.map(wb)}}function nl(e){try{Sb();let t=el(e);if(!t)return;let s=Zc(Qr,`${e}.json`),n={schema:"claude-recall.bug-pattern-cluster.v1",cluster:t.cluster,members:t.members,backed_up_at:new Date().toISOString()};_b(s,JSON.stringify(n,null,2))}catch(t){console.error("[bug-patterns] backup failed:",t)}}function rl(e){return e?_().prepare(`SELECT * FROM bug_pattern_clusters
1437
+ ORDER BY COALESCE(s.started_at, ''), m.matched_at, m.session_id`).all(e);return{cluster:Hs(n),members:s.map(Gy)}}function zd(e){try{Wy();let t=Xd(e);if(!t)return;let n=Hd(ci,`${e}.json`),s={schema:"claude-recall.bug-pattern-cluster.v1",cluster:t.cluster,members:t.members,backed_up_at:new Date().toISOString()};jy(n,JSON.stringify(s,null,2))}catch(t){console.error("[bug-patterns] backup failed:",t)}}function Yd(e){return e?E().prepare(`SELECT * FROM bug_pattern_clusters
1301
1438
  WHERE signature_hash = ?
1302
- ORDER BY last_seen_at DESC, id ASC`).all(e).map(tn):[]}function ol(e){if(!e)return new Set;let s=_().prepare(`SELECT DISTINCT m.session_id
1439
+ ORDER BY last_seen_at DESC, id ASC`).all(e).map(Hs):[]}function qd(e){if(!e)return new Set;let n=E().prepare(`SELECT DISTINCT m.session_id
1303
1440
  FROM bug_pattern_members m
1304
1441
  JOIN bug_pattern_clusters c ON c.id = m.cluster_id
1305
- WHERE c.signature_hash = ?`).all(e);return new Set(s.map(n=>n.session_id))}var Rb=/\b0x[0-9a-fA-F]+\b/g,xb=/\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b/g,kb=/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?\b/g,Cb=/:\d+:\d+/g,Lb=/\bline\s+\d+\b/gi,Nb=/\bcolumn\s+\d+\b/gi,Ab=/\b(?:pid|PID|process(?:\s+id)?)\s*[:=]?\s*\d+\b/gi,Ob=/\b(?:port|:)\s*[:=]?\s*\d{2,5}\b/gi,vb=/\b\d{4,}\b/g,Ib=/(['"`])[^'"`\n]{1,128}\1/g;function Mb(e){if(!e)return"";let t=String(e);return t=t.replace(Rb,"<hex>"),t=t.replace(xb,"<uuid>"),t=t.replace(kb,"<ts>"),t=t.replace(Cb,":<line>:<col>"),t=t.replace(Lb,"line <n>"),t=t.replace(Nb,"column <n>"),t=t.replace(Ab,"pid <n>"),t=t.replace(Ob,"port <n>"),t=t.replace(vb,"<num>"),t=t.replace(Ib,"<arg>"),t=t.replace(/\s+/g," ").trim(),t.toLowerCase()}function Db(e){let t=(e.error_type??"unknown").toLowerCase().trim(),s=Mb(e.snippet??e.message_hash??""),n=`${t}|${s}`;return Tb("sha256").update(n).digest("hex").slice(0,16)}function $b(e){let t=_(),s=["oi.bug_signatures IS NOT NULL"],n=[];e&&(s.push("p.name = ?"),n.push(e));let r=`WHERE ${s.join(" AND ")}`,o=t.prepare(`SELECT oi.session_id AS session_id,
1442
+ WHERE c.signature_hash = ?`).all(e);return new Set(n.map(s=>s.session_id))}var zy=/\b0x[0-9a-fA-F]+\b/g,Yy=/\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b/g,qy=/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?\b/g,Vy=/:\d+:\d+/g,Ky=/\bline\s+\d+\b/gi,Qy=/\bcolumn\s+\d+\b/gi,Zy=/\b(?:pid|PID|process(?:\s+id)?)\s*[:=]?\s*\d+\b/gi,eT=/\b(?:port|:)\s*[:=]?\s*\d{2,5}\b/gi,tT=/\b\d{4,}\b/g,nT=/(['"`])[^'"`\n]{1,128}\1/g;function sT(e){if(!e)return"";let t=String(e);return t=t.replace(zy,"<hex>"),t=t.replace(Yy,"<uuid>"),t=t.replace(qy,"<ts>"),t=t.replace(Vy,":<line>:<col>"),t=t.replace(Ky,"line <n>"),t=t.replace(Qy,"column <n>"),t=t.replace(Zy,"pid <n>"),t=t.replace(eT,"port <n>"),t=t.replace(tT,"<num>"),t=t.replace(nT,"<arg>"),t=t.replace(/\s+/g," ").trim(),t.toLowerCase()}function rT(e){let t=(e.error_type??"unknown").toLowerCase().trim(),n=sT(e.snippet??e.message_hash??""),s=`${t}|${n}`;return Jy("sha256").update(s).digest("hex").slice(0,16)}function oT(e){let t=E(),n=["oi.bug_signatures IS NOT NULL"],s=[];e&&(n.push("p.name = ?"),s.push(e));let r=`WHERE ${n.join(" AND ")}`,o=t.prepare(`SELECT oi.session_id AS session_id,
1306
1443
  p.name AS project,
1307
1444
  s.started_at AS started_at,
1308
1445
  oi.bug_signatures AS bug_signatures
1309
1446
  FROM session_output_index oi
1310
1447
  LEFT JOIN sessions s ON s.id = oi.session_id
1311
1448
  LEFT JOIN projects p ON p.id = s.project_id
1312
- ${r}`).all(...n),i=[];for(let a of o){if(!a.bug_signatures)continue;let d=[];try{let l=JSON.parse(a.bug_signatures);Array.isArray(l)&&(d=l)}catch{continue}for(let l of d){if(!l||typeof l!="object"||!(l.snippet??"").trim())continue;let p=Db(l);i.push({session_id:a.session_id,project:a.project,started_at:a.started_at,signature:l,fingerprint:p})}}return i}function Pb(e){let t=new Map;for(let n of e){let r=t.get(n.fingerprint);r||(r=[],t.set(n.fingerprint,r)),r.push(n)}let s=[];for(let[n,r]of t){let o=new Set,i=[];for(let u of r)o.has(u.session_id)||(o.add(u.session_id),i.push(u));let a=[...i].sort((u,p)=>{let m=u.started_at??"",g=p.started_at??"";return m&&g?m<g?-1:m>g?1:0:m?-1:g?1:0}),d=a.find(u=>u.started_at),l=[...a].reverse().find(u=>u.started_at);s.push({fingerprint:n,example_message:i[0].signature.snippet??i[0].signature.message_hash??"",members:i,first_seen_at:d?.started_at??new Date().toISOString(),last_seen_at:l?.started_at??new Date().toISOString()})}return s}function Fb(e,t){if(e.length!==t.length)return 0;let s=0,n=0,r=0;for(let i=0;i<e.length;i++)s+=e[i]*t[i],n+=e[i]*e[i],r+=t[i]*t[i];let o=Math.sqrt(n)*Math.sqrt(r);return o>0?s/o:0}function jb(e){let{records:t,vectors:s,epsilon:n,minPts:r}=e,o=t.length;if(o===0)return[];let i=[];for(let u=0;u<o;u++){let p=[];for(let m=0;m<o;m++){if(u===m)continue;1-Fb(s[u],s[m])<=n&&p.push(m)}i.push(p)}let a=new Array(o).fill(!1),d=new Array(o).fill(-1),l=[];for(let u=0;u<o;u++){if(a[u])continue;a[u]=!0;let p=i[u];if(p.length<r)continue;let m=l.length;l.push({members:[t[u]]}),d[u]=m;let g=[...p];for(;g.length>0;){let f=g.shift();if(!a[f]&&(a[f]=!0,i[f].length>=r))for(let h of i[f])(!a[h]||d[h]===-1)&&g.push(h);d[f]===-1&&(d[f]=m,l[m].members.push(t[f]))}}return l}async function Ub(e,t,s,n){if(e.length===0)return[];let r=e.map(d=>{let l=d.signature.snippet??d.signature.message_hash??"";return`${d.signature.error_type??""}: ${l}`.trim()}),o=await t(r);if(o.length!==e.length)throw new Error(`embedder returned ${o.length} vectors for ${e.length} inputs`);let i=jb({records:e,vectors:o,epsilon:s,minPts:n}),a=[];for(let d of i){if(d.members.length===0)continue;let l=new Set,u=[];for(let E of d.members)l.has(E.session_id)||(l.add(E.session_id),u.push(E));if(u.length===0)continue;let m=`sem:${[...u.map(E=>E.fingerprint)].sort()[0]}`,g=[...u].sort((E,b)=>{let S=E.started_at??"",R=b.started_at??"";return S<R?-1:S>R?1:0}),f=g.find(E=>E.started_at),h=[...g].reverse().find(E=>E.started_at);a.push({fingerprint:m,example_message:u[0].signature.snippet??u[0].signature.message_hash??"",members:u,first_seen_at:f?.started_at??new Date().toISOString(),last_seen_at:h?.started_at??new Date().toISOString()})}return a}function il(e,t){let s={clusters_created:0,clusters_merged:0,members_added:0,cluster_ids:[]};for(let n of e){if(n.members.length<t)continue;let r=rl(n.fingerprint),o=ol(n.fingerprint),i=n.members.map(l=>l.session_id).filter(l=>!o.has(l));if(r.length===0){let l=Qc({signature_hash:n.fingerprint,example_message:n.example_message.slice(0,256),member_session_ids:n.members.map(u=>u.session_id),first_seen_at:n.first_seen_at,last_seen_at:n.last_seen_at});s.clusters_created+=1,s.members_added+=l.members.length,s.cluster_ids.push(l.cluster.id);continue}if(i.length===0){s.cluster_ids.push(r[0].id);continue}let a=r[0],d=tl(a.id,i);d.added>0&&(s.clusters_merged+=1,s.members_added+=d.added),s.cluster_ids.push(a.id)}return s}async function al(e={}){let t=Math.max(2,Math.floor(e.minClusterSize??3)),s=Math.max(1,Math.min(5e3,Math.floor(e.limit??1e3))),n=e.semanticEpsilon??.15,r=Math.max(1,Math.floor(e.semanticMinPts??1)),o=$b(e.project),i=new Set(o.map(S=>S.session_id)),a=Pb(o),d=[],l=!1;if(e.semantic){let S=e.embedder??null;if(!S)try{S=await Wb()}catch(R){let L=(R instanceof Error?R.message:String(R)).split(`
1313
- `)[0];console.warn(`[bug-pattern] --semantic requested but the embedder is unavailable: ${L}
1314
- Falling back to exact-match-only clustering. Run \`recall semantic install\` to enable the semantic pass.`),l=!0}if(S){let R=[];for(let T of a)T.members.length===1&&R.push(T.members[0]);R.length>=2&&(d=await Ub(R,S,n,r))}}let u=a.filter(S=>S.members.length>=t),p=d.filter(S=>S.members.length>=t),m=il(u,t),g=il(p,t),f=[...m.cluster_ids,...g.cluster_ids],h=Array.from(new Set(f)),E=[];if(h.length>0){let S=_(),R=h.map(()=>"?").join(","),T=S.prepare(`SELECT * FROM bug_pattern_clusters
1315
- WHERE id IN (${R})
1449
+ ${r}`).all(...s),i=[];for(let a of o){if(!a.bug_signatures)continue;let d=[];try{let l=JSON.parse(a.bug_signatures);Array.isArray(l)&&(d=l)}catch{continue}for(let l of d){if(!l||typeof l!="object"||!(l.snippet??"").trim())continue;let m=rT(l);i.push({session_id:a.session_id,project:a.project,started_at:a.started_at,signature:l,fingerprint:m})}}return i}function iT(e){let t=new Map;for(let s of e){let r=t.get(s.fingerprint);r||(r=[],t.set(s.fingerprint,r)),r.push(s)}let n=[];for(let[s,r]of t){let o=new Set,i=[];for(let u of r)o.has(u.session_id)||(o.add(u.session_id),i.push(u));let a=[...i].sort((u,m)=>{let p=u.started_at??"",g=m.started_at??"";return p&&g?p<g?-1:p>g?1:0:p?-1:g?1:0}),d=a.find(u=>u.started_at),l=[...a].reverse().find(u=>u.started_at);n.push({fingerprint:s,example_message:i[0].signature.snippet??i[0].signature.message_hash??"",members:i,first_seen_at:d?.started_at??new Date().toISOString(),last_seen_at:l?.started_at??new Date().toISOString()})}return n}function aT(e,t){if(e.length!==t.length)return 0;let n=0,s=0,r=0;for(let i=0;i<e.length;i++)n+=e[i]*t[i],s+=e[i]*e[i],r+=t[i]*t[i];let o=Math.sqrt(s)*Math.sqrt(r);return o>0?n/o:0}function cT(e){let{records:t,vectors:n,epsilon:s,minPts:r}=e,o=t.length;if(o===0)return[];let i=[];for(let u=0;u<o;u++){let m=[];for(let p=0;p<o;p++){if(u===p)continue;1-aT(n[u],n[p])<=s&&m.push(p)}i.push(m)}let a=new Array(o).fill(!1),d=new Array(o).fill(-1),l=[];for(let u=0;u<o;u++){if(a[u])continue;a[u]=!0;let m=i[u];if(m.length<r)continue;let p=l.length;l.push({members:[t[u]]}),d[u]=p;let g=[...m];for(;g.length>0;){let f=g.shift();if(!a[f]&&(a[f]=!0,i[f].length>=r))for(let h of i[f])(!a[h]||d[h]===-1)&&g.push(h);d[f]===-1&&(d[f]=p,l[p].members.push(t[f]))}}return l}async function lT(e,t,n,s){if(e.length===0)return[];let r=e.map(d=>{let l=d.signature.snippet??d.signature.message_hash??"";return`${d.signature.error_type??""}: ${l}`.trim()}),o=await t(r);if(o.length!==e.length)throw new Error(`embedder returned ${o.length} vectors for ${e.length} inputs`);let i=cT({records:e,vectors:o,epsilon:n,minPts:s}),a=[];for(let d of i){if(d.members.length===0)continue;let l=new Set,u=[];for(let _ of d.members)l.has(_.session_id)||(l.add(_.session_id),u.push(_));if(u.length===0)continue;let p=`sem:${[...u.map(_=>_.fingerprint)].sort()[0]}`,g=[...u].sort((_,b)=>{let S=_.started_at??"",w=b.started_at??"";return S<w?-1:S>w?1:0}),f=g.find(_=>_.started_at),h=[...g].reverse().find(_=>_.started_at);a.push({fingerprint:p,example_message:u[0].signature.snippet??u[0].signature.message_hash??"",members:u,first_seen_at:f?.started_at??new Date().toISOString(),last_seen_at:h?.started_at??new Date().toISOString()})}return a}function Vd(e,t){let n={clusters_created:0,clusters_merged:0,members_added:0,cluster_ids:[]};for(let s of e){if(s.members.length<t)continue;let r=Yd(s.fingerprint),o=qd(s.fingerprint),i=s.members.map(l=>l.session_id).filter(l=>!o.has(l));if(r.length===0){let l=Wd({signature_hash:s.fingerprint,example_message:s.example_message.slice(0,256),member_session_ids:s.members.map(u=>u.session_id),first_seen_at:s.first_seen_at,last_seen_at:s.last_seen_at});n.clusters_created+=1,n.members_added+=l.members.length,n.cluster_ids.push(l.cluster.id);continue}if(i.length===0){n.cluster_ids.push(r[0].id);continue}let a=r[0],d=Gd(a.id,i);d.added>0&&(n.clusters_merged+=1,n.members_added+=d.added),n.cluster_ids.push(a.id)}return n}async function Kd(e={}){let t=Math.max(2,Math.floor(e.minClusterSize??3)),n=Math.max(1,Math.min(5e3,Math.floor(e.limit??1e3))),s=e.semanticEpsilon??.15,r=Math.max(1,Math.floor(e.semanticMinPts??1)),o=oT(e.project),i=new Set(o.map(S=>S.session_id)),a=iT(o),d=[],l=!1;if(e.semantic){let S=e.embedder??null;if(!S)try{S=await mT()}catch(w){let T=(w instanceof Error?w.message:String(w)).split(`
1450
+ `)[0];console.warn(`[bug-pattern] --semantic requested but the embedder is unavailable: ${T}
1451
+ Falling back to exact-match-only clustering. Run \`recall semantic install\` to enable the semantic pass.`),l=!0}if(S){let w=[];for(let y of a)y.members.length===1&&w.push(y.members[0]);w.length>=2&&(d=await lT(w,S,s,r))}}let u=a.filter(S=>S.members.length>=t),m=d.filter(S=>S.members.length>=t),p=Vd(u,t),g=Vd(m,t),f=[...p.cluster_ids,...g.cluster_ids],h=Array.from(new Set(f)),_=[];if(h.length>0){let S=E(),w=h.map(()=>"?").join(","),y=S.prepare(`SELECT * FROM bug_pattern_clusters
1452
+ WHERE id IN (${w})
1316
1453
  ORDER BY occurrence_count DESC, last_seen_at DESC
1317
- LIMIT ?`).all(...h,s);for(let L of T)E.push({id:L.id,signature_hash:L.signature_hash,example_message:L.example_message,occurrence_count:L.occurrence_count,first_seen_at:L.first_seen_at,last_seen_at:L.last_seen_at,resolved_in_session_id:L.resolved_in_session_id,fix_summary:L.fix_summary})}return{progress:{total_sessions:i.size,total_signatures:o.length,exact_match_groups:u.length,semantic_groups:p.length,clusters_created:m.clusters_created+g.clusters_created,clusters_merged:m.clusters_merged+g.clusters_merged,members_added:m.members_added+g.members_added,semantic_skipped:l},clusters:E}}var Bb=async()=>{let{embed:e,loadEmbedder:t,getEmbedderStatus:s}=await Promise.resolve().then(()=>(yt(),Dr));return s().loaded||await t(),e},Hb=Bb;async function Wb(){return Hb()}function Xb(e,t){let s=sl(e);if(!s)return null;let n=s.cluster,r=s.members.slice(0,t).map(o=>({session_id:o.session_id,title:o.title,project:o.project,started_at:o.started_at}));return{id:n.id,signature_hash:n.signature_hash,example_message:n.example_message,occurrence_count:n.occurrence_count,first_seen_at:n.first_seen_at,last_seen_at:n.last_seen_at,status:n.resolved_in_session_id?"resolved":"open",resolved_in_session_id:n.resolved_in_session_id,sample_members:r}}async function cl(e){let t=e.minClusterSize?Math.max(2,Math.floor(Number(e.minClusterSize))):3;if(!Number.isFinite(t)){console.error("--min-cluster-size must be a positive number"),process.exitCode=1;return}let s=e.limit?Math.max(1,Math.floor(Number(e.limit))):100;if(!Number.isFinite(s)){console.error("--limit must be a positive number"),process.exitCode=1;return}let n;if(e.project){let l=Ee(e.project);if(!l){console.error(`Project "${e.project}" not found.`),process.exitCode=1;return}n=l.name}if(!e.json){let l=n?`project "${n}"`:"all projects",u=e.semantic?"exact + semantic":"exact";console.log(`Inferring bug patterns \u2014 ${l}, min-cluster-size=${t}, ${u} passes\u2026`)}let r={project:n,minClusterSize:t,semantic:!!e.semantic,limit:s},o=Date.now(),i;try{i=await al(r)}catch(l){console.error(l instanceof Error?l.message:String(l)),process.exitCode=1;return}let a=Number(((Date.now()-o)/1e3).toFixed(1)),d=[];for(let l of i.clusters.slice(0,s)){let u=Xb(l.id,3);u&&d.push(u)}if(e.json){console.log(JSON.stringify({project:n??null,progress:i.progress,elapsed_seconds:a,clusters:d},null,2));return}if(console.log(`Done in ${a}s \u2014 sessions=${i.progress.total_sessions} signatures=${i.progress.total_signatures} clusters: created=${i.progress.clusters_created} merged=${i.progress.clusters_merged} members_added=${i.progress.members_added}`),i.progress.semantic_skipped&&console.log(" \u24D8 semantic pass skipped \u2014 embedder unavailable. Run `recall semantic install` to enable."),d.length===0){i.progress.total_signatures===0?console.log(" \u24D8 No bug signatures in the corpus yet. Run `recall extract-outputs --project <name>` first to populate session_output_index."):console.log(` \u24D8 No clusters at min-cluster-size=${t}. Try \`--semantic\` to merge near-duplicate snippets, or lower \`--min-cluster-size 2\` to surface pairs.`);return}console.log(`
1318
- Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u2713 resolved":"open",p=l.example_message.slice(0,80);console.log(`
1454
+ LIMIT ?`).all(...h,n);for(let T of y)_.push({id:T.id,signature_hash:T.signature_hash,example_message:T.example_message,occurrence_count:T.occurrence_count,first_seen_at:T.first_seen_at,last_seen_at:T.last_seen_at,resolved_in_session_id:T.resolved_in_session_id,fix_summary:T.fix_summary})}return{progress:{total_sessions:i.size,total_signatures:o.length,exact_match_groups:u.length,semantic_groups:m.length,clusters_created:p.clusters_created+g.clusters_created,clusters_merged:p.clusters_merged+g.clusters_merged,members_added:p.members_added+g.members_added,semantic_skipped:l},clusters:_}}var dT=async()=>{let{embed:e,loadEmbedder:t,getEmbedderStatus:n}=await Promise.resolve().then(()=>(Pe(),bs));return n().loaded||await t(),e},uT=dT;async function mT(){return uT()}function pT(e,t){let n=Jd(e);if(!n)return null;let s=n.cluster,r=n.members.slice(0,t).map(o=>({session_id:o.session_id,title:o.title,project:o.project,started_at:o.started_at}));return{id:s.id,signature_hash:s.signature_hash,example_message:s.example_message,occurrence_count:s.occurrence_count,first_seen_at:s.first_seen_at,last_seen_at:s.last_seen_at,status:s.resolved_in_session_id?"resolved":"open",resolved_in_session_id:s.resolved_in_session_id,sample_members:r}}async function Qd(e){let t=e.minClusterSize?Math.max(2,Math.floor(Number(e.minClusterSize))):3;if(!Number.isFinite(t)){console.error("--min-cluster-size must be a positive number"),process.exitCode=1;return}let n=e.limit?Math.max(1,Math.floor(Number(e.limit))):100;if(!Number.isFinite(n)){console.error("--limit must be a positive number"),process.exitCode=1;return}let s;if(e.project){let l=Te(e.project);if(!l){console.error(`Project "${e.project}" not found.`),process.exitCode=1;return}s=l.name}if(!e.json){let l=s?`project "${s}"`:"all projects",u=e.semantic?"exact + semantic":"exact";console.log(`Inferring bug patterns \u2014 ${l}, min-cluster-size=${t}, ${u} passes\u2026`)}let r={project:s,minClusterSize:t,semantic:!!e.semantic,limit:n},o=Date.now(),i;try{i=await Kd(r)}catch(l){console.error(l instanceof Error?l.message:String(l)),process.exitCode=1;return}let a=Number(((Date.now()-o)/1e3).toFixed(1)),d=[];for(let l of i.clusters.slice(0,n)){let u=pT(l.id,3);u&&d.push(u)}if(e.json){console.log(JSON.stringify({project:s??null,progress:i.progress,elapsed_seconds:a,clusters:d},null,2));return}if(console.log(`Done in ${a}s \u2014 sessions=${i.progress.total_sessions} signatures=${i.progress.total_signatures} clusters: created=${i.progress.clusters_created} merged=${i.progress.clusters_merged} members_added=${i.progress.members_added}`),i.progress.semantic_skipped&&console.log(" \u24D8 semantic pass skipped \u2014 embedder unavailable. Run `recall semantic install` to enable."),d.length===0){i.progress.total_signatures===0?console.log(" \u24D8 No bug signatures in the corpus yet. Run `recall extract-outputs --project <name>` first to populate session_output_index."):console.log(` \u24D8 No clusters at min-cluster-size=${t}. Try \`--semantic\` to merge near-duplicate snippets, or lower \`--min-cluster-size 2\` to surface pairs.`);return}console.log(`
1455
+ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u2713 resolved":"open",m=l.example_message.slice(0,80);console.log(`
1319
1456
  [${u}] occurs=${l.occurrence_count} hash=${l.signature_hash.slice(0,8)}
1320
- ${p}
1321
- first=${l.first_seen_at} last=${l.last_seen_at}`);for(let m of l.sample_members){let g=m.project?`[${m.project}] `:"";console.log(` \u2022 ${g}${m.title} (${m.session_id.slice(0,8)})`)}}console.log("\n Review at /graph/patterns in the web UI, or `recall neighborhood <session-id>` for an agent-facing bundle.")}w();import{z as WO}from"zod";w();function ll(e){let t=e.trim();if(t.length<4)return null;let s=_();if(t.length>=32)return s.prepare("SELECT id FROM sessions WHERE id = ?").get(t)?.id??null;let n=s.prepare("SELECT id FROM sessions WHERE id LIKE ? LIMIT 2").all(`${t}%`);return n.length===1?n[0].id:null}eo();var El=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),bl=new Set(["pagerank","embedding-rerank","hybrid"]);function oS(e){if(!e)return;let t=e.split(",").map(s=>s.trim()).filter(Boolean);for(let s of t)if(!El.has(s))throw new Error(`invalid --edge-types value: '${s}'. Valid: ${Array.from(El).join(", ")}`);return t}function iS(e){if(!e)return"hybrid";if(!bl.has(e))throw new Error(`invalid --scoring value: '${e}'. Valid: ${Array.from(bl).join(", ")}`);return e}async function Sl(e,t){let s=ll(e);if(!s){console.error(`session not found or prefix ambiguous: ${e}`),process.exitCode=1;return}let n,r;try{n=iS(t.scoring),r=oS(t.edgeTypes)}catch(d){console.error(d instanceof Error?d.message:String(d)),process.exitCode=1;return}let o=t.budget?Math.max(100,Number(t.budget)):4e3;if(!Number.isFinite(o)){console.error("--budget must be a positive number"),process.exitCode=1;return}let i=t.maxDepth?Math.max(1,Number(t.maxDepth)):2;if(!Number.isFinite(i)){console.error("--max-depth must be a positive number"),process.exitCode=1;return}let a;try{a=rn(s,{budget:o,maxDepth:i,scoring:n,edgeTypes:r,includeWikiLinks:t.wikiLinks!==!1,includeSuggestions:!!t.includeSuggestions})}catch(d){console.error(d instanceof Error?d.message:String(d)),process.exitCode=1;return}if(t.json){process.stdout.write(JSON.stringify(a,null,2)+`
1457
+ ${m}
1458
+ first=${l.first_seen_at} last=${l.last_seen_at}`);for(let p of l.sample_members){let g=p.project?`[${p.project}] `:"";console.log(` \u2022 ${g}${p.title} (${p.session_id.slice(0,8)})`)}}console.log("\n Review at /graph/patterns in the web UI, or `recall neighborhood <session-id>` for an agent-facing bundle.")}R();import{z as Y$}from"zod";R();function Zd(e){let t=e.trim();if(t.length<4)return null;let n=E();if(t.length>=32)return n.prepare("SELECT id FROM sessions WHERE id = ?").get(t)?.id??null;let s=n.prepare("SELECT id FROM sessions WHERE id LIKE ? LIMIT 2").all(`${t}%`);return s.length===1?s[0].id:null}li();var cu=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),lu=new Set(["pagerank","embedding-rerank","hybrid"]);function AT(e){if(!e)return;let t=e.split(",").map(n=>n.trim()).filter(Boolean);for(let n of t)if(!cu.has(n))throw new Error(`invalid --edge-types value: '${n}'. Valid: ${Array.from(cu).join(", ")}`);return t}function LT(e){if(!e)return"hybrid";if(!lu.has(e))throw new Error(`invalid --scoring value: '${e}'. Valid: ${Array.from(lu).join(", ")}`);return e}async function du(e,t){let n=Zd(e);if(!n){console.error(`session not found or prefix ambiguous: ${e}`),process.exitCode=1;return}let s,r;try{s=LT(t.scoring),r=AT(t.edgeTypes)}catch(d){console.error(d instanceof Error?d.message:String(d)),process.exitCode=1;return}let o=t.budget?Math.max(100,Number(t.budget)):4e3;if(!Number.isFinite(o)){console.error("--budget must be a positive number"),process.exitCode=1;return}let i=t.maxDepth?Math.max(1,Number(t.maxDepth)):2;if(!Number.isFinite(i)){console.error("--max-depth must be a positive number"),process.exitCode=1;return}let a;try{a=Gs(n,{budget:o,maxDepth:i,scoring:s,edgeTypes:r,includeWikiLinks:t.wikiLinks!==!1,includeSuggestions:!!t.includeSuggestions})}catch(d){console.error(d instanceof Error?d.message:String(d)),process.exitCode=1;return}if(t.json){process.stdout.write(JSON.stringify(a,null,2)+`
1322
1459
  `);return}process.stdout.write(a.bundle),a.truncated.length>0&&console.error(`
1323
- [truncated ${a.truncated.length} refs to fit ${o}-token budget; ${a.budgetUsed} used / ${a.budgetRemaining} remaining]`)}w();v();var aS=[[/opus[-_ ]?4[-_. ]?7/i,{label:"Opus 4.7",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/opus[-_ ]?4[-_. ]?6/i,{label:"Opus 4.6",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet[-_ ]?4[-_. ]?6/i,{label:"Sonnet 4.6",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/sonnet[-_ ]?4[-_. ]?5/i,{label:"Sonnet 4.5",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/haiku[-_ ]?4[-_. ]?5/i,{label:"Haiku 4.5",inputCentsPerMtok:100,outputCentsPerMtok:500,cacheCreateCentsPerMtok:125,cacheReadCentsPerMtok:10}],[/opus[-_ ]?4(?!.*[5-9])/i,{label:"Opus 4",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet[-_ ]?4(?!.*[5-9])/i,{label:"Sonnet 4",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?7[-_ ]?sonnet|sonnet[-_ ]?3[-_. ]?7/i,{label:"Sonnet 3.7",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?5[-_ ]?sonnet|sonnet[-_ ]?3[-_. ]?5/i,{label:"Sonnet 3.5",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?5[-_ ]?haiku|haiku[-_ ]?3[-_. ]?5/i,{label:"Haiku 3.5",inputCentsPerMtok:80,outputCentsPerMtok:400,cacheCreateCentsPerMtok:100,cacheReadCentsPerMtok:8}],[/3[-_ ]?opus|opus[-_ ]?3/i,{label:"Opus 3",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/3[-_ ]?haiku|haiku(?!.*3[-_. ]?5)/i,{label:"Haiku 3",inputCentsPerMtok:25,outputCentsPerMtok:125,cacheCreateCentsPerMtok:30,cacheReadCentsPerMtok:3}],[/opus/i,{label:"Opus",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet/i,{label:"Sonnet",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/haiku/i,{label:"Haiku",inputCentsPerMtok:100,outputCentsPerMtok:500,cacheCreateCentsPerMtok:125,cacheReadCentsPerMtok:10}]],yl={label:"unknown",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30};function it(e){if(!e)return yl;for(let[t,s]of aS)if(t.test(e))return s;return yl}function Ie(e,t){if(e.byModel&&Object.keys(e.byModel).length>0){let i={input:0,output:0,cacheCreate:0,cacheRead:0},a=0;for(let[l,u]of Object.entries(e.byModel)){let p=it(l);i.input+=u.inputTokens/1e6*p.inputCentsPerMtok,i.output+=u.outputTokens/1e6*p.outputCentsPerMtok,i.cacheCreate+=u.cacheCreateTokens/1e6*p.cacheCreateCentsPerMtok,i.cacheRead+=u.cacheReadTokens/1e6*p.cacheReadCentsPerMtok,a+=u.inputTokens+u.outputTokens+u.cacheCreateTokens+u.cacheReadTokens}let d=i.input+i.output+i.cacheCreate+i.cacheRead;return{cents:d,dollars:d/100,totalTokens:a,parts:i}}let s=it(t),n={input:e.inputTokens/1e6*s.inputCentsPerMtok,output:e.outputTokens/1e6*s.outputCentsPerMtok,cacheCreate:e.cacheCreateTokens/1e6*s.cacheCreateCentsPerMtok,cacheRead:e.cacheReadTokens/1e6*s.cacheReadCentsPerMtok},r=n.input+n.output+n.cacheCreate+n.cacheRead,o=e.inputTokens+e.outputTokens+e.cacheCreateTokens+e.cacheReadTokens;return{cents:r,dollars:r/100,totalTokens:o,parts:n}}function re(e){let t=e/100;return t===0?"$0.00":t<.01?"<$0.01":t<1?`$${t.toFixed(2)}`:t<100?`$${t.toFixed(2)}`:t<1e4?`$${t.toFixed(0)}`:`$${(t/1e3).toFixed(1)}k`}function ue(e){return!Number.isFinite(e)||e<0?"0":e<1e3?String(Math.round(e)):e<1e6?`${(e/1e3).toFixed(1)}k`:e<1e9?`${(e/1e6).toFixed(2)}M`:e<1e12?`${(e/1e9).toFixed(2)}B`:`${(e/1e12).toFixed(2)}T`}w();w();var cS=500,on=new Set;function wl(){return on.size}function lS(){return`
1460
+ [truncated ${a.truncated.length} refs to fit ${o}-token budget; ${a.budgetUsed} used / ${a.budgetRemaining} remaining]`)}R();$();var NT=[[/opus[-_ ]?4[-_. ]?7/i,{label:"Opus 4.7",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/opus[-_ ]?4[-_. ]?6/i,{label:"Opus 4.6",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet[-_ ]?4[-_. ]?6/i,{label:"Sonnet 4.6",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/sonnet[-_ ]?4[-_. ]?5/i,{label:"Sonnet 4.5",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/haiku[-_ ]?4[-_. ]?5/i,{label:"Haiku 4.5",inputCentsPerMtok:100,outputCentsPerMtok:500,cacheCreateCentsPerMtok:125,cacheReadCentsPerMtok:10}],[/opus[-_ ]?4(?!.*[5-9])/i,{label:"Opus 4",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet[-_ ]?4(?!.*[5-9])/i,{label:"Sonnet 4",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?7[-_ ]?sonnet|sonnet[-_ ]?3[-_. ]?7/i,{label:"Sonnet 3.7",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?5[-_ ]?sonnet|sonnet[-_ ]?3[-_. ]?5/i,{label:"Sonnet 3.5",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/3[-_. ]?5[-_ ]?haiku|haiku[-_ ]?3[-_. ]?5/i,{label:"Haiku 3.5",inputCentsPerMtok:80,outputCentsPerMtok:400,cacheCreateCentsPerMtok:100,cacheReadCentsPerMtok:8}],[/3[-_ ]?opus|opus[-_ ]?3/i,{label:"Opus 3",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/3[-_ ]?haiku|haiku(?!.*3[-_. ]?5)/i,{label:"Haiku 3",inputCentsPerMtok:25,outputCentsPerMtok:125,cacheCreateCentsPerMtok:30,cacheReadCentsPerMtok:3}],[/opus/i,{label:"Opus",inputCentsPerMtok:1500,outputCentsPerMtok:7500,cacheCreateCentsPerMtok:1875,cacheReadCentsPerMtok:150}],[/sonnet/i,{label:"Sonnet",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30}],[/haiku/i,{label:"Haiku",inputCentsPerMtok:100,outputCentsPerMtok:500,cacheCreateCentsPerMtok:125,cacheReadCentsPerMtok:10}]],uu={label:"unknown",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30};function gt(e){if(!e)return uu;for(let[t,n]of NT)if(t.test(e))return n;return uu}function Fe(e,t){if(e.byModel&&Object.keys(e.byModel).length>0){let i={input:0,output:0,cacheCreate:0,cacheRead:0},a=0;for(let[l,u]of Object.entries(e.byModel)){let m=gt(l);i.input+=u.inputTokens/1e6*m.inputCentsPerMtok,i.output+=u.outputTokens/1e6*m.outputCentsPerMtok,i.cacheCreate+=u.cacheCreateTokens/1e6*m.cacheCreateCentsPerMtok,i.cacheRead+=u.cacheReadTokens/1e6*m.cacheReadCentsPerMtok,a+=u.inputTokens+u.outputTokens+u.cacheCreateTokens+u.cacheReadTokens}let d=i.input+i.output+i.cacheCreate+i.cacheRead;return{cents:d,dollars:d/100,totalTokens:a,parts:i}}let n=gt(t),s={input:e.inputTokens/1e6*n.inputCentsPerMtok,output:e.outputTokens/1e6*n.outputCentsPerMtok,cacheCreate:e.cacheCreateTokens/1e6*n.cacheCreateCentsPerMtok,cacheRead:e.cacheReadTokens/1e6*n.cacheReadCentsPerMtok},r=s.input+s.output+s.cacheCreate+s.cacheRead,o=e.inputTokens+e.outputTokens+e.cacheCreateTokens+e.cacheReadTokens;return{cents:r,dollars:r/100,totalTokens:o,parts:s}}function ae(e){let t=e/100;return t===0?"$0.00":t<.01?"<$0.01":t<1?`$${t.toFixed(2)}`:t<100?`$${t.toFixed(2)}`:t<1e4?`$${t.toFixed(0)}`:`$${(t/1e3).toFixed(1)}k`}function _e(e){return!Number.isFinite(e)||e<0?"0":e<1e3?String(Math.round(e)):e<1e6?`${(e/1e3).toFixed(1)}k`:e<1e9?`${(e/1e6).toFixed(2)}M`:e<1e12?`${(e/1e9).toFixed(2)}B`:`${(e/1e12).toFixed(2)}T`}R();R();var OT=500,Js=new Set;function mu(){return Js.size}function vT(){return`
1324
1461
  SELECT m.uuid, m.session_id, m.timestamp, m.raw_json
1325
1462
  FROM messages m
1326
1463
  LEFT JOIN message_usage mu ON mu.message_uuid = m.uuid
1327
1464
  WHERE m.role = 'assistant' AND mu.message_uuid IS NULL
1328
1465
  AND m.uuid NOT IN (SELECT value FROM json_each(?))
1329
1466
  LIMIT ?
1330
- `}function dS(e,t){let s=t.limit??Number.MAX_SAFE_INTEGER,n=Math.max(1,t.chunkSize??cS),r=e.prepare(lS()),o=e.prepare(`
1467
+ `}function IT(e,t){let n=t.limit??Number.MAX_SAFE_INTEGER,s=Math.max(1,t.chunkSize??OT),r=e.prepare(vT()),o=e.prepare(`
1331
1468
  INSERT INTO message_usage (
1332
1469
  message_uuid, session_id, model,
1333
1470
  input_tokens, output_tokens, cache_create_tokens, cache_read_tokens,
@@ -1337,14 +1474,14 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
1337
1474
  @input, @output, @cc, @cr, @ts
1338
1475
  )
1339
1476
  ON CONFLICT(message_uuid) DO NOTHING
1340
- `),i=0,a=0,d=new Set;for(;i<s;){let l=Math.min(n,s-i),u=JSON.stringify([...on]),p=r.all(u,l);if(p.length===0)break;let m=new Set;if(e.transaction(()=>{for(let f of p){let h;try{h=JSON.parse(f.raw_json)}catch{on.add(f.uuid);continue}let E=Jn(h.message);if(!E){on.add(f.uuid);continue}o.run({uuid:f.uuid,session_id:f.session_id,model:h.message?.model??null,input:E.inputTokens,output:E.outputTokens,cc:E.cacheCreateTokens,cr:E.cacheReadTokens,ts:f.timestamp}),a+=1,m.add(f.session_id)}for(let f of m)zt(e,f),d.add(f)})(),i+=p.length,t.onProgress?.({scanned:i,inserted:a,sessionsTouched:d.size,done:p.length<l}),p.length<l)break}return{scanned:i,inserted:a,sessionsTouched:d.size,done:!0}}function Tl(e={}){return dS(_(),e)}function to(e){let t=new Map;for(let n of e){let r=n.model??null,o=t.get(r)??{inputTokens:0,outputTokens:0,cacheCreateTokens:0,cacheReadTokens:0,messageCount:0};o.inputTokens+=n.input_tokens,o.outputTokens+=n.output_tokens,o.cacheCreateTokens+=n.cache_create_tokens,o.cacheReadTokens+=n.cache_read_tokens,o.messageCount+=n.n,t.set(r,o)}let s=[];for(let[n,r]of t.entries()){let o=Ie({inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens},n);s.push({model:n,modelLabel:it(n).label,inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens,messageCount:r.messageCount,cost:o})}return s.sort((n,r)=>r.cost.cents-n.cost.cents)}function so(e){let t={};for(let s of e)t[s.model??"__unknown__"]={inputTokens:s.inputTokens,outputTokens:s.outputTokens,cacheCreateTokens:s.cacheCreateTokens,cacheReadTokens:s.cacheReadTokens};return{byModel:t}}function Rl(e){let t=_(),s=t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1477
+ `),i=0,a=0,d=new Set;for(;i<n;){let l=Math.min(s,n-i),u=JSON.stringify([...Js]),m=r.all(u,l);if(m.length===0)break;let p=new Set;if(e.transaction(()=>{for(let f of m){let h;try{h=JSON.parse(f.raw_json)}catch{Js.add(f.uuid);continue}let _=Mr(h.message);if(!_){Js.add(f.uuid);continue}o.run({uuid:f.uuid,session_id:f.session_id,model:h.message?.model??null,input:_.inputTokens,output:_.outputTokens,cc:_.cacheCreateTokens,cr:_.cacheReadTokens,ts:f.timestamp}),a+=1,p.add(f.session_id)}for(let f of p)on(e,f),d.add(f)})(),i+=m.length,t.onProgress?.({scanned:i,inserted:a,sessionsTouched:d.size,done:m.length<l}),m.length<l)break}return{scanned:i,inserted:a,sessionsTouched:d.size,done:!0}}function pu(e={}){return IT(E(),e)}function di(e){let t=new Map;for(let s of e){let r=s.model??null,o=t.get(r)??{inputTokens:0,outputTokens:0,cacheCreateTokens:0,cacheReadTokens:0,messageCount:0};o.inputTokens+=s.input_tokens,o.outputTokens+=s.output_tokens,o.cacheCreateTokens+=s.cache_create_tokens,o.cacheReadTokens+=s.cache_read_tokens,o.messageCount+=s.n,t.set(r,o)}let n=[];for(let[s,r]of t.entries()){let o=Fe({inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens},s);n.push({model:s,modelLabel:gt(s).label,inputTokens:r.inputTokens,outputTokens:r.outputTokens,cacheCreateTokens:r.cacheCreateTokens,cacheReadTokens:r.cacheReadTokens,messageCount:r.messageCount,cost:o})}return n.sort((s,r)=>r.cost.cents-s.cost.cents)}function ui(e){let t={};for(let n of e)t[n.model??"__unknown__"]={inputTokens:n.inputTokens,outputTokens:n.outputTokens,cacheCreateTokens:n.cacheCreateTokens,cacheReadTokens:n.cacheReadTokens};return{byModel:t}}function gu(e){let t=E(),n=t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1341
1478
  s.message_count,
1342
1479
  s.total_input_tokens, s.total_output_tokens,
1343
1480
  s.total_cache_create_tokens, s.total_cache_read_tokens,
1344
1481
  s.primary_model
1345
1482
  FROM sessions s
1346
1483
  LEFT JOIN projects p ON p.id = s.project_id
1347
- WHERE s.id = ?`).get(e);if(!s)return null;let n=t.prepare(`SELECT model,
1484
+ WHERE s.id = ?`).get(e);if(!n)return null;let s=t.prepare(`SELECT model,
1348
1485
  COALESCE(SUM(input_tokens), 0) AS input_tokens,
1349
1486
  COALESCE(SUM(output_tokens), 0) AS output_tokens,
1350
1487
  COALESCE(SUM(cache_create_tokens), 0) AS cache_create_tokens,
@@ -1352,7 +1489,7 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
1352
1489
  COUNT(*) AS n
1353
1490
  FROM message_usage
1354
1491
  WHERE session_id = ?
1355
- GROUP BY model`).all(e),r=to(n),o=s.total_input_tokens??0,i=s.total_output_tokens??0,a=s.total_cache_create_tokens??0,d=s.total_cache_read_tokens??0,l=Ie({inputTokens:o,outputTokens:i,cacheCreateTokens:a,cacheReadTokens:d,...so(r)},s.primary_model);return{sessionId:s.id,project:s.project,startedAt:s.started_at,endedAt:s.ended_at,messageCount:s.message_count,primaryModel:s.primary_model,primaryModelLabel:it(s.primary_model).label,inputTokens:o,outputTokens:i,cacheCreateTokens:a,cacheReadTokens:d,totalTokens:l.totalTokens,cost:l,byModel:r,display:{dollars:re(l.cents),tokens:ue(l.totalTokens),model:it(s.primary_model).label}}}function xl(e){let t=_(),s=t.prepare("SELECT id, name FROM projects WHERE name = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT mu.model,
1492
+ GROUP BY model`).all(e),r=di(s),o=n.total_input_tokens??0,i=n.total_output_tokens??0,a=n.total_cache_create_tokens??0,d=n.total_cache_read_tokens??0,l=Fe({inputTokens:o,outputTokens:i,cacheCreateTokens:a,cacheReadTokens:d,...ui(r)},n.primary_model);return{sessionId:n.id,project:n.project,startedAt:n.started_at,endedAt:n.ended_at,messageCount:n.message_count,primaryModel:n.primary_model,primaryModelLabel:gt(n.primary_model).label,inputTokens:o,outputTokens:i,cacheCreateTokens:a,cacheReadTokens:d,totalTokens:l.totalTokens,cost:l,byModel:r,display:{dollars:ae(l.cents),tokens:_e(l.totalTokens),model:gt(n.primary_model).label}}}function fu(e){let t=E(),n=t.prepare("SELECT id, name FROM projects WHERE name = ?").get(e);if(!n)return null;let s=t.prepare(`SELECT mu.model,
1356
1493
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1357
1494
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
1358
1495
  COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
@@ -1361,12 +1498,12 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
1361
1498
  FROM message_usage mu
1362
1499
  JOIN sessions s ON s.id = mu.session_id
1363
1500
  WHERE s.project_id = ?
1364
- GROUP BY mu.model`).all(s.id),r=to(n),o=t.prepare(`SELECT COALESCE(SUM(total_input_tokens), 0) AS input_tokens,
1501
+ GROUP BY mu.model`).all(n.id),r=di(s),o=t.prepare(`SELECT COALESCE(SUM(total_input_tokens), 0) AS input_tokens,
1365
1502
  COALESCE(SUM(total_output_tokens), 0) AS output_tokens,
1366
1503
  COALESCE(SUM(total_cache_create_tokens), 0) AS cache_create_tokens,
1367
1504
  COALESCE(SUM(total_cache_read_tokens), 0) AS cache_read_tokens,
1368
1505
  COUNT(*) AS session_count
1369
- FROM sessions WHERE project_id = ?`).get(s.id),i=Ie({inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,...so(r)},null),d=t.prepare(`SELECT s.id, sa.alias, s.started_at,
1506
+ FROM sessions WHERE project_id = ?`).get(n.id),i=Fe({inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,...ui(r)},null),d=t.prepare(`SELECT s.id, sa.alias, s.started_at,
1370
1507
  s.total_input_tokens, s.total_output_tokens,
1371
1508
  s.total_cache_create_tokens, s.total_cache_read_tokens,
1372
1509
  s.primary_model
@@ -1377,7 +1514,7 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
1377
1514
  + COALESCE(s.total_output_tokens,0)
1378
1515
  + COALESCE(s.total_cache_create_tokens,0)
1379
1516
  + COALESCE(s.total_cache_read_tokens,0)) DESC
1380
- LIMIT 10`).all(s.id).map(l=>{let u=Ie({inputTokens:l.total_input_tokens??0,outputTokens:l.total_output_tokens??0,cacheCreateTokens:l.total_cache_create_tokens??0,cacheReadTokens:l.total_cache_read_tokens??0},l.primary_model);return{sessionId:l.id,alias:l.alias,startedAt:l.started_at,totalTokens:u.totalTokens,cost:u}});return{project:s.name,sessionCount:o.session_count,inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,totalTokens:i.totalTokens,cost:i,byModel:r,topSessions:d,display:{dollars:re(i.cents),tokens:ue(i.totalTokens)}}}function kl(e="all"){let t=_(),s=e==="7d"?new Date(Date.now()-7*864e5).toISOString():e==="30d"?new Date(Date.now()-30*864e5).toISOString():null,n=s?"WHERE mu.timestamp >= @since OR (mu.timestamp IS NULL AND s.started_at >= @since)":"",r=s?"WHERE m.timestamp >= @since OR (m.timestamp IS NULL AND s2.started_at >= @since)":"",o=s?{since:s}:{},i=y=>s?t.prepare(y).get(o):t.prepare(y).get(),a=y=>s?t.prepare(y).all(o):t.prepare(y).all(),d=a(`SELECT mu.model,
1517
+ LIMIT 10`).all(n.id).map(l=>{let u=Fe({inputTokens:l.total_input_tokens??0,outputTokens:l.total_output_tokens??0,cacheCreateTokens:l.total_cache_create_tokens??0,cacheReadTokens:l.total_cache_read_tokens??0},l.primary_model);return{sessionId:l.id,alias:l.alias,startedAt:l.started_at,totalTokens:u.totalTokens,cost:u}});return{project:n.name,sessionCount:o.session_count,inputTokens:o.input_tokens,outputTokens:o.output_tokens,cacheCreateTokens:o.cache_create_tokens,cacheReadTokens:o.cache_read_tokens,totalTokens:i.totalTokens,cost:i,byModel:r,topSessions:d,display:{dollars:ae(i.cents),tokens:_e(i.totalTokens)}}}function _u(e="all"){let t=E(),n=e==="7d"?new Date(Date.now()-7*864e5).toISOString():e==="30d"?new Date(Date.now()-30*864e5).toISOString():null,s=n?"WHERE mu.timestamp >= @since OR (mu.timestamp IS NULL AND s.started_at >= @since)":"",r=n?"WHERE m.timestamp >= @since OR (m.timestamp IS NULL AND s2.started_at >= @since)":"",o=n?{since:n}:{},i=k=>n?t.prepare(k).get(o):t.prepare(k).get(),a=k=>n?t.prepare(k).all(o):t.prepare(k).all(),d=a(`SELECT mu.model,
1381
1518
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1382
1519
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
1383
1520
  COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
@@ -1385,8 +1522,8 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
1385
1522
  COUNT(*) AS n
1386
1523
  FROM message_usage mu
1387
1524
  JOIN sessions s ON s.id = mu.session_id
1388
- ${n}
1389
- GROUP BY mu.model`),l=to(d),u=0,p=0,m=0,g=0;for(let y of l)u+=y.inputTokens,p+=y.outputTokens,m+=y.cacheCreateTokens,g+=y.cacheReadTokens;let f=Ie({inputTokens:u,outputTokens:p,cacheCreateTokens:m,cacheReadTokens:g,...so(l)},null),h=s?i(`SELECT
1525
+ ${s}
1526
+ GROUP BY mu.model`),l=di(d),u=0,m=0,p=0,g=0;for(let k of l)u+=k.inputTokens,m+=k.outputTokens,p+=k.cacheCreateTokens,g+=k.cacheReadTokens;let f=Fe({inputTokens:u,outputTokens:m,cacheCreateTokens:p,cacheReadTokens:g,...ui(l)},null),h=n?i(`SELECT
1390
1527
  (SELECT COUNT(DISTINCT m.session_id)
1391
1528
  FROM messages m
1392
1529
  JOIN sessions s2 ON s2.id = m.session_id
@@ -1394,13 +1531,13 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
1394
1531
  (SELECT COUNT(DISTINCT mu.session_id)
1395
1532
  FROM message_usage mu
1396
1533
  JOIN sessions s ON s.id = mu.session_id
1397
- ${n}) AS sessions_with_usage`):t.prepare(`SELECT
1534
+ ${s}) AS sessions_with_usage`):t.prepare(`SELECT
1398
1535
  (SELECT COUNT(*) FROM sessions) AS total_sessions,
1399
1536
  (SELECT COUNT(*) FROM sessions
1400
1537
  WHERE (COALESCE(total_input_tokens,0)
1401
1538
  +COALESCE(total_output_tokens,0)
1402
1539
  +COALESCE(total_cache_create_tokens,0)
1403
- +COALESCE(total_cache_read_tokens,0)) > 0) AS sessions_with_usage`).get(),E=a(`SELECT substr(datetime(COALESCE(mu.timestamp, s.started_at, ''), 'localtime'), 1, 10) AS day,
1540
+ +COALESCE(total_cache_read_tokens,0)) > 0) AS sessions_with_usage`).get(),_=a(`SELECT substr(datetime(COALESCE(mu.timestamp, s.started_at, ''), 'localtime'), 1, 10) AS day,
1404
1541
  mu.model,
1405
1542
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1406
1543
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
@@ -1408,9 +1545,9 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
1408
1545
  COALESCE(SUM(mu.cache_read_tokens), 0) AS cache_read_tokens
1409
1546
  FROM message_usage mu
1410
1547
  JOIN sessions s ON s.id = mu.session_id
1411
- ${n}
1548
+ ${s}
1412
1549
  GROUP BY day, mu.model
1413
- ORDER BY day ASC`),b=new Map;for(let y of E){if(!y.day)continue;let B=Ie({inputTokens:y.input_tokens,outputTokens:y.output_tokens,cacheCreateTokens:y.cache_create_tokens,cacheReadTokens:y.cache_read_tokens},y.model),M=b.get(y.day)??{tokens:0,cents:0};M.tokens+=B.totalTokens,M.cents+=B.cents,b.set(y.day,M)}let S=[...b.entries()].map(([y,B])=>({day:y,tokens:B.tokens,cents:B.cents})).sort((y,B)=>y.day.localeCompare(B.day)),T=a(`SELECT s.id, p.name AS project, sa.alias, s.started_at,
1550
+ ORDER BY day ASC`),b=new Map;for(let k of _){if(!k.day)continue;let I=Fe({inputTokens:k.input_tokens,outputTokens:k.output_tokens,cacheCreateTokens:k.cache_create_tokens,cacheReadTokens:k.cache_read_tokens},k.model),F=b.get(k.day)??{tokens:0,cents:0};F.tokens+=I.totalTokens,F.cents+=I.cents,b.set(k.day,F)}let S=[...b.entries()].map(([k,I])=>({day:k,tokens:I.tokens,cents:I.cents})).sort((k,I)=>k.day.localeCompare(I.day)),y=a(`SELECT s.id, p.name AS project, sa.alias, s.started_at,
1414
1551
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
1415
1552
  COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
1416
1553
  COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
@@ -1420,13 +1557,13 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
1420
1557
  JOIN sessions s ON s.id = mu.session_id
1421
1558
  LEFT JOIN projects p ON p.id = s.project_id
1422
1559
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1423
- ${n}
1560
+ ${s}
1424
1561
  GROUP BY s.id
1425
1562
  ORDER BY (COALESCE(SUM(mu.input_tokens),0)
1426
1563
  + COALESCE(SUM(mu.output_tokens),0)
1427
1564
  + COALESCE(SUM(mu.cache_create_tokens),0)
1428
1565
  + COALESCE(SUM(mu.cache_read_tokens),0)) DESC
1429
- LIMIT 10`).map(y=>{let B=Ie({inputTokens:y.input_tokens,outputTokens:y.output_tokens,cacheCreateTokens:y.cache_create_tokens,cacheReadTokens:y.cache_read_tokens},y.primary_model);return{sessionId:y.id,project:y.project,alias:y.alias,startedAt:y.started_at,totalTokens:B.totalTokens,cost:B}}),L=a(`SELECT p.id AS project_id,
1566
+ LIMIT 10`).map(k=>{let I=Fe({inputTokens:k.input_tokens,outputTokens:k.output_tokens,cacheCreateTokens:k.cache_create_tokens,cacheReadTokens:k.cache_read_tokens},k.primary_model);return{sessionId:k.id,project:k.project,alias:k.alias,startedAt:k.started_at,totalTokens:I.totalTokens,cost:I}}),T=a(`SELECT p.id AS project_id,
1430
1567
  p.name AS project,
1431
1568
  mu.model,
1432
1569
  COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
@@ -1437,17 +1574,17 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
1437
1574
  FROM message_usage mu
1438
1575
  JOIN sessions s ON s.id = mu.session_id
1439
1576
  LEFT JOIN projects p ON p.id = s.project_id
1440
- ${n}
1441
- GROUP BY p.id, mu.model`),D=new Map;for(let y of L){let B=y.project_id??"__none__",M=D.get(B);M||(M={project:y.project??"(no project)",sessionIds:new Set,sessionsApprox:0,byModel:{}},D.set(B,M)),y.sessions>M.sessionsApprox&&(M.sessionsApprox=y.sessions),M.byModel[y.model??"__unknown__"]={inputTokens:y.input_tokens,outputTokens:y.output_tokens,cacheCreateTokens:y.cache_create_tokens,cacheReadTokens:y.cache_read_tokens}}let A=[...D.values()].map(y=>{let B=0,M=0,V=0,Ge=0;for(let _t of Object.values(y.byModel))B+=_t.inputTokens,M+=_t.outputTokens,V+=_t.cacheCreateTokens,Ge+=_t.cacheReadTokens;let Fe=Ie({inputTokens:B,outputTokens:M,cacheCreateTokens:V,cacheReadTokens:Ge,byModel:y.byModel},null);return{project:y.project,sessions:y.sessionsApprox,totalTokens:Fe.totalTokens,cost:Fe}});A.sort((y,B)=>B.totalTokens-y.totalTokens);let $=A.slice(0,20),U=t.prepare(`SELECT
1577
+ ${s}
1578
+ GROUP BY p.id, mu.model`),B=new Map;for(let k of T){let I=k.project_id??"__none__",F=B.get(I);F||(F={project:k.project??"(no project)",sessionIds:new Set,sessionsApprox:0,byModel:{}},B.set(I,F)),k.sessions>F.sessionsApprox&&(F.sessionsApprox=k.sessions),F.byModel[k.model??"__unknown__"]={inputTokens:k.input_tokens,outputTokens:k.output_tokens,cacheCreateTokens:k.cache_create_tokens,cacheReadTokens:k.cache_read_tokens}}let A=[...B.values()].map(k=>{let I=0,F=0,ne=0,et=0;for(let Rt of Object.values(k.byModel))I+=Rt.inputTokens,F+=Rt.outputTokens,ne+=Rt.cacheCreateTokens,et+=Rt.cacheReadTokens;let We=Fe({inputTokens:I,outputTokens:F,cacheCreateTokens:ne,cacheReadTokens:et,byModel:k.byModel},null);return{project:k.project,sessions:k.sessionsApprox,totalTokens:We.totalTokens,cost:We}});A.sort((k,I)=>I.totalTokens-k.totalTokens);let v=A.slice(0,20),O=t.prepare(`SELECT
1442
1579
  (SELECT COUNT(*) FROM messages WHERE role='assistant') AS assistant_messages,
1443
- (SELECT COUNT(*) FROM message_usage) AS messages_with_usage`).get();return{range:e,totalSessions:h.total_sessions,sessionsWithUsage:h.sessions_with_usage,inputTokens:u,outputTokens:p,cacheCreateTokens:m,cacheReadTokens:g,totalTokens:f.totalTokens,cost:f,daily:S,byModel:l,topSessions:T,topRepos:$,backfill:{assistantMessages:U.assistant_messages,messagesWithUsage:U.messages_with_usage,pending:Math.max(0,U.assistant_messages-U.messages_with_usage),unrecoverable:Math.min(wl(),Math.max(0,U.assistant_messages-U.messages_with_usage))},display:{dollars:re(f.cents),tokens:ue(f.totalTokens)}}}var Qe=e=>e.toLocaleString();function uS(e){return _().prepare("SELECT id FROM sessions WHERE id = ? OR id LIKE ? LIMIT 1").get(e,`${e}%`)?.id??null}async function Cl(e,t){if(t.backfill){let r=t.limit?Math.max(1,Number(t.limit)):void 0;console.log(c.dim("backfilling per-message usage from raw_json\u2026"));let o=Tl({limit:r});if(console.log(`${c.ok("backfill done")}: scanned ${c.bold(String(o.scanned))}, inserted ${c.bold(String(o.inserted))}, rolled-up ${c.bold(String(o.sessionsTouched))} sessions`),!e&&!t.project)return}if(e){let r=uS(e);if(!r){console.error(c.err(`no session matches '${e}'`)),process.exit(1);return}let o=Rl(r);if(!o){console.error(c.err("session has no usage data yet \u2014 try --backfill")),process.exit(1);return}if(t.json){console.log(JSON.stringify(o,null,2));return}if(console.log(""),console.log(c.bold("session cost")),console.log(c.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(` id ${c.dim(o.sessionId)}`),console.log(` project ${c.accent(o.project??"\u2014")}`),console.log(` started ${c.dim(o.startedAt??"n/a")}`),console.log(` messages ${c.accent(Qe(o.messageCount))}`),console.log(` model ${c.accent(o.primaryModelLabel)} ${c.dim(o.primaryModel??"")}`),console.log(""),console.log(` input ${Qe(o.inputTokens).padStart(12)}`),console.log(` output ${Qe(o.outputTokens).padStart(12)}`),console.log(` cache write ${Qe(o.cacheCreateTokens).padStart(12)}`),console.log(` cache read ${Qe(o.cacheReadTokens).padStart(12)}`),console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),console.log(` total tokens ${c.bold(Qe(o.totalTokens).padStart(12))}`),console.log(` estimated cost ${c.accent(re(o.cost.cents).padStart(12))}`),o.byModel.length>1){console.log(""),console.log(c.dim(" by model:"));for(let i of o.byModel)console.log(` ${i.modelLabel.padEnd(14)} ${ue(i.inputTokens+i.outputTokens+i.cacheCreateTokens+i.cacheReadTokens).padStart(10)} ${c.accent(re(i.cost.cents).padStart(10))}`)}console.log("");return}if(t.project){let r=xl(t.project);if(!r){console.error(c.err(`project '${t.project}' not found`)),process.exit(1);return}if(t.json){console.log(JSON.stringify(r,null,2));return}if(console.log(""),console.log(c.bold(`project \xB7 ${r.project}`)),console.log(c.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(` sessions ${c.accent(Qe(r.sessionCount))}`),console.log(` total tokens ${c.accent(ue(r.totalTokens).padStart(12))}`),console.log(` estimated cost ${c.accent(re(r.cost.cents).padStart(12))}`),r.byModel.length>0){console.log(""),console.log(c.dim(" by model:"));for(let o of r.byModel)console.log(` ${o.modelLabel.padEnd(14)} ${ue(o.inputTokens+o.outputTokens+o.cacheCreateTokens+o.cacheReadTokens).padStart(10)} ${c.accent(re(o.cost.cents).padStart(10))}`)}if(r.topSessions.length>0){console.log(""),console.log(c.dim(" top sessions:"));for(let o of r.topSessions){let i=o.alias??o.sessionId.slice(0,8);console.log(` ${i.padEnd(22)} ${ue(o.totalTokens).padStart(10)} ${c.accent(re(o.cost.cents).padStart(10))}`)}}console.log("");return}let s=t.days==="7"?"7d":t.days==="30"?"30d":"all",n=kl(s);if(t.json){console.log(JSON.stringify(n,null,2));return}if(console.log(""),console.log(c.bold(`overview \xB7 ${s}`)),console.log(c.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(` sessions ${c.accent(Qe(n.totalSessions))} ${c.dim(`(${n.sessionsWithUsage} w/ usage)`)}`),console.log(` total tokens ${c.accent(ue(n.totalTokens).padStart(12))}`),console.log(` estimated cost ${c.accent(re(n.cost.cents).padStart(12))}`),n.backfill.pending>0&&console.log(c.dim(` (${Qe(n.backfill.pending)} assistant messages pending \u2014 run \`recall stats --backfill\`)`)),n.byModel.length>0){console.log(""),console.log(c.dim(" by model:"));for(let r of n.byModel)console.log(` ${r.modelLabel.padEnd(14)} ${ue(r.inputTokens+r.outputTokens+r.cacheCreateTokens+r.cacheReadTokens).padStart(10)} ${c.accent(re(r.cost.cents).padStart(10))}`)}if(n.topSessions.length>0){console.log(""),console.log(c.dim(" top sessions:"));for(let r of n.topSessions){let o=r.alias??r.sessionId.slice(0,8);console.log(` ${o.padEnd(22)} ${(r.project??"").slice(0,20).padEnd(22)} ${ue(r.totalTokens).padStart(10)} ${c.accent(re(r.cost.cents).padStart(10))}`)}}console.log("")}v();w();w();import{execFile as pS}from"node:child_process";import{promisify as mS}from"node:util";import{stat as gS}from"node:fs/promises";var Ll=mS(pS),Nl=1e4,fS="%H%x09%aI%x09%s";async function _S(e){try{let{stdout:t}=await Ll("git",["rev-parse","--is-inside-work-tree"],{cwd:e,timeout:Nl});return t.trim()==="true"}catch{return!1}}async function hS(e,t,s){let n=["--no-pager","log","--all","--no-color","--since",t,"--until",s,`--pretty=format:${fS}`],{stdout:r}=await Ll("git",n,{cwd:e,timeout:Nl,maxBuffer:8*1024*1024}),o=[],i=new Set;for(let a of r.split(`
1444
- `)){if(!a)continue;let[d,l,...u]=a.split(" ");!d||i.has(d)||(i.add(d),o.push({commit_sha:d,committed_at:l??null,subject:u.join(" ")||null}))}return o}function ES(e){return _().prepare(`SELECT id, cwd, started_at, ended_at
1445
- FROM sessions WHERE id = ?`).get(e)??null}function bS(e,t,s){if(s.length===0)return 0;let n=_(),r=new Date().toISOString(),o=n.prepare(`INSERT OR IGNORE INTO session_commits
1580
+ (SELECT COUNT(*) FROM message_usage) AS messages_with_usage`).get();return{range:e,totalSessions:h.total_sessions,sessionsWithUsage:h.sessions_with_usage,inputTokens:u,outputTokens:m,cacheCreateTokens:p,cacheReadTokens:g,totalTokens:f.totalTokens,cost:f,daily:S,byModel:l,topSessions:y,topRepos:v,backfill:{assistantMessages:O.assistant_messages,messagesWithUsage:O.messages_with_usage,pending:Math.max(0,O.assistant_messages-O.messages_with_usage),unrecoverable:Math.min(mu(),Math.max(0,O.assistant_messages-O.messages_with_usage))},display:{dollars:ae(f.cents),tokens:_e(f.totalTokens)}}}var at=e=>e.toLocaleString();function MT(e){return E().prepare("SELECT id FROM sessions WHERE id = ? OR id LIKE ? LIMIT 1").get(e,`${e}%`)?.id??null}async function hu(e,t){if(t.backfill){let r=t.limit?Math.max(1,Number(t.limit)):void 0;console.log(c.dim("backfilling per-message usage from raw_json\u2026"));let o=pu({limit:r});if(console.log(`${c.ok("backfill done")}: scanned ${c.bold(String(o.scanned))}, inserted ${c.bold(String(o.inserted))}, rolled-up ${c.bold(String(o.sessionsTouched))} sessions`),!e&&!t.project)return}if(e){let r=MT(e);if(!r){console.error(c.err(`no session matches '${e}'`)),process.exit(1);return}let o=gu(r);if(!o){console.error(c.err("session has no usage data yet \u2014 try --backfill")),process.exit(1);return}if(t.json){console.log(JSON.stringify(o,null,2));return}if(console.log(""),console.log(c.bold("session cost")),console.log(c.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(` id ${c.dim(o.sessionId)}`),console.log(` project ${c.accent(o.project??"\u2014")}`),console.log(` started ${c.dim(o.startedAt??"n/a")}`),console.log(` messages ${c.accent(at(o.messageCount))}`),console.log(` model ${c.accent(o.primaryModelLabel)} ${c.dim(o.primaryModel??"")}`),console.log(""),console.log(` input ${at(o.inputTokens).padStart(12)}`),console.log(` output ${at(o.outputTokens).padStart(12)}`),console.log(` cache write ${at(o.cacheCreateTokens).padStart(12)}`),console.log(` cache read ${at(o.cacheReadTokens).padStart(12)}`),console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),console.log(` total tokens ${c.bold(at(o.totalTokens).padStart(12))}`),console.log(` estimated cost ${c.accent(ae(o.cost.cents).padStart(12))}`),o.byModel.length>1){console.log(""),console.log(c.dim(" by model:"));for(let i of o.byModel)console.log(` ${i.modelLabel.padEnd(14)} ${_e(i.inputTokens+i.outputTokens+i.cacheCreateTokens+i.cacheReadTokens).padStart(10)} ${c.accent(ae(i.cost.cents).padStart(10))}`)}console.log("");return}if(t.project){let r=fu(t.project);if(!r){console.error(c.err(`project '${t.project}' not found`)),process.exit(1);return}if(t.json){console.log(JSON.stringify(r,null,2));return}if(console.log(""),console.log(c.bold(`project \xB7 ${r.project}`)),console.log(c.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(` sessions ${c.accent(at(r.sessionCount))}`),console.log(` total tokens ${c.accent(_e(r.totalTokens).padStart(12))}`),console.log(` estimated cost ${c.accent(ae(r.cost.cents).padStart(12))}`),r.byModel.length>0){console.log(""),console.log(c.dim(" by model:"));for(let o of r.byModel)console.log(` ${o.modelLabel.padEnd(14)} ${_e(o.inputTokens+o.outputTokens+o.cacheCreateTokens+o.cacheReadTokens).padStart(10)} ${c.accent(ae(o.cost.cents).padStart(10))}`)}if(r.topSessions.length>0){console.log(""),console.log(c.dim(" top sessions:"));for(let o of r.topSessions){let i=o.alias??o.sessionId.slice(0,8);console.log(` ${i.padEnd(22)} ${_e(o.totalTokens).padStart(10)} ${c.accent(ae(o.cost.cents).padStart(10))}`)}}console.log("");return}let n=t.days==="7"?"7d":t.days==="30"?"30d":"all",s=_u(n);if(t.json){console.log(JSON.stringify(s,null,2));return}if(console.log(""),console.log(c.bold(`overview \xB7 ${n}`)),console.log(c.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),console.log(` sessions ${c.accent(at(s.totalSessions))} ${c.dim(`(${s.sessionsWithUsage} w/ usage)`)}`),console.log(` total tokens ${c.accent(_e(s.totalTokens).padStart(12))}`),console.log(` estimated cost ${c.accent(ae(s.cost.cents).padStart(12))}`),s.backfill.pending>0&&console.log(c.dim(` (${at(s.backfill.pending)} assistant messages pending \u2014 run \`recall stats --backfill\`)`)),s.byModel.length>0){console.log(""),console.log(c.dim(" by model:"));for(let r of s.byModel)console.log(` ${r.modelLabel.padEnd(14)} ${_e(r.inputTokens+r.outputTokens+r.cacheCreateTokens+r.cacheReadTokens).padStart(10)} ${c.accent(ae(r.cost.cents).padStart(10))}`)}if(s.topSessions.length>0){console.log(""),console.log(c.dim(" top sessions:"));for(let r of s.topSessions){let o=r.alias??r.sessionId.slice(0,8);console.log(` ${o.padEnd(22)} ${(r.project??"").slice(0,20).padEnd(22)} ${_e(r.totalTokens).padStart(10)} ${c.accent(ae(r.cost.cents).padStart(10))}`)}}console.log("")}$();R();R();import{execFile as DT}from"node:child_process";import{promisify as $T}from"node:util";import{stat as PT}from"node:fs/promises";var Eu=$T(DT),bu=1e4,FT="%H%x09%aI%x09%s";async function jT(e){try{let{stdout:t}=await Eu("git",["rev-parse","--is-inside-work-tree"],{cwd:e,timeout:bu});return t.trim()==="true"}catch{return!1}}async function UT(e,t,n){let s=["--no-pager","log","--all","--no-color","--since",t,"--until",n,`--pretty=format:${FT}`],{stdout:r}=await Eu("git",s,{cwd:e,timeout:bu,maxBuffer:8*1024*1024}),o=[],i=new Set;for(let a of r.split(`
1581
+ `)){if(!a)continue;let[d,l,...u]=a.split(" ");!d||i.has(d)||(i.add(d),o.push({commit_sha:d,committed_at:l??null,subject:u.join(" ")||null}))}return o}function BT(e){return E().prepare(`SELECT id, cwd, started_at, ended_at
1582
+ FROM sessions WHERE id = ?`).get(e)??null}function HT(e,t,n){if(n.length===0)return 0;let s=E(),r=new Date().toISOString(),o=s.prepare(`INSERT OR IGNORE INTO session_commits
1446
1583
  (session_id, commit_sha, committed_at, subject, cwd_snapshot, correlated_at)
1447
- VALUES (?, ?, ?, ?, ?, ?)`),i=0;return n.transaction(d=>{for(let l of d)o.run(e,l.commit_sha,l.committed_at,l.subject,t,r).changes>0&&(i+=1)})(s),i}async function no(e){let t=ES(e);if(!t)return{sessionId:e,status:"error",commitsFound:0,commitsInserted:0,error:"session not found"};if(!t.cwd)return{sessionId:e,status:"no-cwd",commitsFound:0,commitsInserted:0};if(!t.started_at||!t.ended_at)return{sessionId:e,status:"no-window",commitsFound:0,commitsInserted:0};let s=t.started_at,n=t.ended_at===t.started_at?new Date(Date.parse(t.ended_at)+1e3).toISOString():t.ended_at;try{if(!(await gS(t.cwd)).isDirectory())return{sessionId:e,status:"cwd-missing",commitsFound:0,commitsInserted:0}}catch{return{sessionId:e,status:"cwd-missing",commitsFound:0,commitsInserted:0}}if(!await _S(t.cwd))return{sessionId:e,status:"not-a-repo",commitsFound:0,commitsInserted:0};try{let o=await hS(t.cwd,s,n),i=bS(e,t.cwd,o);return{sessionId:e,status:"ok",commitsFound:o.length,commitsInserted:i}}catch(o){return{sessionId:e,status:"error",commitsFound:0,commitsInserted:0,error:o.message}}}async function Al(e={}){let t=_(),s=Math.max(1,Math.min(1e5,e.limit??1e4)),n=t.prepare(`SELECT id FROM sessions
1584
+ VALUES (?, ?, ?, ?, ?, ?)`),i=0;return s.transaction(d=>{for(let l of d)o.run(e,l.commit_sha,l.committed_at,l.subject,t,r).changes>0&&(i+=1)})(n),i}async function mi(e){let t=BT(e);if(!t)return{sessionId:e,status:"error",commitsFound:0,commitsInserted:0,error:"session not found"};if(!t.cwd)return{sessionId:e,status:"no-cwd",commitsFound:0,commitsInserted:0};if(!t.started_at||!t.ended_at)return{sessionId:e,status:"no-window",commitsFound:0,commitsInserted:0};let n=t.started_at,s=t.ended_at===t.started_at?new Date(Date.parse(t.ended_at)+1e3).toISOString():t.ended_at;try{if(!(await PT(t.cwd)).isDirectory())return{sessionId:e,status:"cwd-missing",commitsFound:0,commitsInserted:0}}catch{return{sessionId:e,status:"cwd-missing",commitsFound:0,commitsInserted:0}}if(!await jT(t.cwd))return{sessionId:e,status:"not-a-repo",commitsFound:0,commitsInserted:0};try{let o=await UT(t.cwd,n,s),i=HT(e,t.cwd,o);return{sessionId:e,status:"ok",commitsFound:o.length,commitsInserted:i}}catch(o){return{sessionId:e,status:"error",commitsFound:0,commitsInserted:0,error:o.message}}}async function Su(e={}){let t=E(),n=Math.max(1,Math.min(1e5,e.limit??1e4)),s=t.prepare(`SELECT id FROM sessions
1448
1585
  WHERE cwd IS NOT NULL AND started_at IS NOT NULL AND ended_at IS NOT NULL
1449
1586
  ORDER BY COALESCE(ended_at, started_at) DESC
1450
- LIMIT ?`).all(s),r={total:n.length,ok:0,skipped:0,errors:0,commitsInserted:0,results:[]},o=0;for(let i of n){let a=await no(i.id);r.results.push(a),a.status==="ok"?(r.ok+=1,r.commitsInserted+=a.commitsInserted):a.status==="error"?r.errors+=1:r.skipped+=1,o+=1,e.onProgress?.(o,n.length)}return r}function an(e){let t=_(),s=e.trim();if(!/^[0-9a-fA-F]{4,40}$/.test(s))return[];let n=`${s.toLowerCase()}%`;return t.prepare(`SELECT sc.session_id AS sessionId,
1587
+ LIMIT ?`).all(n),r={total:s.length,ok:0,skipped:0,errors:0,commitsInserted:0,results:[]},o=0;for(let i of s){let a=await mi(i.id);r.results.push(a),a.status==="ok"?(r.ok+=1,r.commitsInserted+=a.commitsInserted):a.status==="error"?r.errors+=1:r.skipped+=1,o+=1,e.onProgress?.(o,s.length)}return r}function zs(e){let t=E(),n=e.trim();if(!/^[0-9a-fA-F]{4,40}$/.test(n))return[];let s=`${n.toLowerCase()}%`;return t.prepare(`SELECT sc.session_id AS sessionId,
1451
1588
  NULLIF(sa.alias, '') AS alias,
1452
1589
  p.name AS project,
1453
1590
  s.started_at AS startedAt,
@@ -1461,33 +1598,34 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
1461
1598
  LEFT JOIN session_aliases sa ON sa.session_id = sc.session_id
1462
1599
  WHERE lower(sc.commit_sha) = lower(?)
1463
1600
  OR lower(sc.commit_sha) LIKE ?
1464
- ORDER BY COALESCE(sc.committed_at, s.started_at, '') DESC`).all(s,n)}function SS(e){return _().prepare("SELECT id FROM sessions WHERE id = ? OR id LIKE ? LIMIT 1").get(e,`${e}%`)?.id??null}function yS(e){let t=e.sessionId.slice(0,8);switch(e.status){case"ok":console.log(`${c.ok("\u2713")} ${c.dim(t)} ${c.bold(String(e.commitsFound))} commits (${e.commitsInserted} new)`);break;case"no-cwd":console.log(`${c.dim("\xB7")} ${c.dim(t)} no cwd recorded \u2014 skipped`);break;case"not-a-repo":console.log(`${c.dim("\xB7")} ${c.dim(t)} cwd is not a git worktree \u2014 skipped`);break;case"cwd-missing":console.log(`${c.dim("\xB7")} ${c.dim(t)} cwd no longer exists on disk \u2014 skipped`);break;case"no-window":console.log(`${c.dim("\xB7")} ${c.dim(t)} session has no time window yet \u2014 skipped`);break;case"error":console.log(`${c.err("\u2717")} ${c.dim(t)} ${e.error??"unknown error"}`);break}}async function Ol(e,t){if(e){let r=SS(e);if(!r){console.error(c.err(`no session matches '${e}'`)),process.exit(1);return}let o=await no(r);if(t.json){console.log(JSON.stringify(o,null,2));return}yS(o);return}let s=t.limit?Math.max(1,Number(t.limit)):void 0;console.log(c.dim(`correlating sessions to git commits (limit ${s??"default"})\u2026`));let n=await Al({limit:s,onProgress:(r,o)=>{(r%25===0||r===o)&&process.stderr.write(`\r${c.dim(` ${r}/${o}`)}`)}});if(process.stderr.write(`
1465
- `),t.json){console.log(JSON.stringify(n,null,2));return}console.log(""),console.log(`${c.ok("correlated")}: ${c.bold(String(n.ok))} sessions \xB7 ${c.bold(String(n.commitsInserted))} new commits`),console.log(c.dim(` skipped ${n.skipped} (no cwd / non-repo / cwd missing), errors ${n.errors}`)),console.log("")}v();w();Et();import{existsSync as lo,readFileSync as uo}from"node:fs";P();import{existsSync as wS,readFileSync as TS,writeFileSync as RS}from"node:fs";import{join as xS}from"node:path";import{z as pe}from"zod";var ro=xS(x,"terminals.json"),vl=1440*60*1e3,kS=3e4,CS=6e4,LS=pe.object({shell_pid:pe.number(),tab_name:pe.string(),cwd:pe.string().nullable().optional(),opened_at:pe.string(),last_seen_at:pe.string()}),NS=pe.object({schema:pe.string().optional(),saved_at:pe.string().optional(),terminals:pe.array(LS).max(500).default([]),sessions_by_pid:pe.record(pe.string(),pe.array(pe.string()).max(50)).optional().default({})}),Il=/^[⠀-⣿✳\s]+/,Ml=/^\d+(\.\d+){1,3}$/;function io(e){let t=e.trim();return!!(!t||Il.test(t)||Ml.test(t))}function Dl(e){let t=e.trim();if(!t||Ml.test(t))return null;let s=t.replace(Il,"").trim();return s.length>0?s:null}function oo(e,t){if(!io(e))return e;let s=Dl(e);return s||(t&&!io(t)?t:e)}function AS(e){let t=e.now-e.withinMs,s=e.pending.filter(n=>{let r=Date.parse(n.started_at);return Number.isFinite(r)&&r>=t});if(s.length===0)return{kind:"none"};if(e.shellPid!=null){let n=s.find(r=>r.shell_pid===e.shellPid);if(n)return{kind:"pid-match",entry:n}}if(e.cwd){let n=e.cwd.replace(/\/+$/,""),r=s.filter(o=>o.cwd&&o.cwd.replace(/\/+$/,"")===n);if(r.length===1)return{kind:"singleton-cwd",entry:r[0]};if(r.length>=2)return{kind:"ambiguous",candidates:r}}return{kind:"none"}}var ao=class{entries=new Map;origins=new Map;sessionsByPid=new Map;pendingClaudeStarts=[];deferredLinks=new Map;pidOwnership=new Map;loaded=!1;ensureLoaded(){if(!this.loaded&&(this.loaded=!0,!!wS(ro)))try{let t=TS(ro,"utf8"),s=JSON.parse(t),n=NS.safeParse(s);if(!n.success){console.warn("[terminal-registry] terminals.json failed validation, starting with empty registry:",n.error.issues);return}let r=n.data;for(let o of r.terminals)this.entries.set(o.shell_pid,{shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd??null,opened_at:o.opened_at,last_seen_at:o.last_seen_at});for(let[o,i]of Object.entries(r.sessions_by_pid??{})){let a=Number(o);if(!Number.isFinite(a))continue;let d=new Set;for(let l of i)l.length>0&&d.add(l);d.size>0&&this.sessionsByPid.set(a,d)}this.gc()}catch{}}save(){try{F();let t={schema:"claude-recall.terminals.v1",saved_at:new Date().toISOString(),terminals:Array.from(this.entries.values()),sessions_by_pid:Object.fromEntries(Array.from(this.sessionsByPid.entries()).map(([s,n])=>[String(s),Array.from(n)]))};RS(ro,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let s=new Date().toISOString(),n=this.entries.get(t.shell_pid),r=oo(t.tab_name,n?.tab_name),o=n?.opened_at??t.opened_at,i={...t,tab_name:r,opened_at:o,last_seen_at:s};return this.entries.set(t.shell_pid,i),this.gc(),this.save(),i}rename(t,s){this.ensureLoaded();let n=this.entries.get(t);if(!n)return null;let r=oo(s,n.tab_name),o={...n,tab_name:r,last_seen_at:new Date().toISOString()};return this.entries.set(t,o),this.save(),o}remove(t){this.ensureLoaded();let s=this.entries.delete(t),n=this.sessionsByPid.delete(t);return this.outputTails.delete(t),this.pidOwnership.delete(t),(s||n)&&this.save(),s}claimPidOwnership(t,s,n=Date.now()){if(this.ensureLoaded(),!s)return"anonymous";let r=this.pidOwnership.get(t);return r?r.instance_id===s?(r.last_claim_at=n,"refreshed"):n-r.last_claim_at>CS?(this.pidOwnership.set(t,{instance_id:s,last_claim_at:n}),"claimed"):"rejected":(this.pidOwnership.set(t,{instance_id:s,last_claim_at:n}),"claimed")}pidOwnershipSnapshot(){return this.ensureLoaded(),Array.from(this.pidOwnership.entries()).map(([t,s])=>({shell_pid:t,instance_id:s.instance_id,last_claim_at:s.last_claim_at}))}sync(t){this.ensureLoaded();let s=new Date().toISOString(),n=0,r=0;for(let o of t){let i=this.entries.get(o.shell_pid),a=oo(o.tab_name,i?.tab_name),d=i?.opened_at??o.opened_at;this.entries.set(o.shell_pid,{...o,tab_name:a,opened_at:d,last_seen_at:s}),i?(i.tab_name!==a||i.cwd!==o.cwd)&&r++:n++}return this.gc(),this.save(),{added:n,updated:r,removed:0}}get(t){return this.ensureLoaded(),this.entries.get(t)??null}all(){return this.ensureLoaded(),this.gc(),Array.from(this.entries.values())}size(){return this.ensureLoaded(),this.entries.size}linkSession(t,s){this.ensureLoaded();let n=this.sessionsByPid.get(s);n||(n=new Set,this.sessionsByPid.set(s,n)),n.has(t)||(n.add(t),this.save())}sessionsFor(t){return this.ensureLoaded(),Array.from(this.sessionsByPid.get(t)??[])}isSessionAutoLinked(t){this.ensureLoaded();for(let s of this.sessionsByPid.values())if(s.has(t))return!0;return!1}unlinkSession(t){this.ensureLoaded();let s=!1;for(let[n,r]of this.sessionsByPid)r.delete(t)&&(s=!0,r.size===0&&this.sessionsByPid.delete(n));return s&&this.save(),s}pushPending(t){this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.push({...t})}takePendingMatched(t){this.ensureLoaded(),this.gcPending();let s=AS({pending:this.pendingClaudeStarts,shellPid:t.shellPid,cwd:t.cwd,withinMs:t.withinMs,now:Date.now()});if(s.kind==="pid-match"||s.kind==="singleton-cwd"){let n=this.pendingClaudeStarts.indexOf(s.entry);n>=0&&this.pendingClaudeStarts.splice(n,1)}return s}pendingSize(){return this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.length}deferSessionLink(t,s,n,r){this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.set(t,{parent_shell_pid:s,queued_at:Date.now(),cwd:n,git_branch:r})}allDeferredLinks(){return this.ensureLoaded(),this.gcDeferredLinks(),Array.from(this.deferredLinks.entries()).map(([t,s])=>({session_id:t,...s}))}resolveDeferredLink(t){return this.deferredLinks.delete(t)}deferredLinkSize(){return this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.size}gcDeferredLinks(){let t=Date.now()-9e4;for(let[s,n]of this.deferredLinks)n.queued_at<t&&this.deferredLinks.delete(s)}gcPending(){let t=Date.now()-kS;this.pendingClaudeStarts.length!==0&&(this.pendingClaudeStarts=this.pendingClaudeStarts.filter(s=>{let n=Date.parse(s.started_at);return Number.isFinite(n)&&n>=t}))}outputTails=new Map;setOutputTail(t,s,n){this.ensureLoaded(),this.outputTails.set(t,{text:s,captured_at:n})}getOutputTail(t){return this.ensureLoaded(),this.outputTails.get(t)??null}allOutputTails(){return this.ensureLoaded(),new Map(this.outputTails)}removeOutputTail(t){return this.outputTails.delete(t)}setOrigin(t,s){this.ensureLoaded(),this.origins.set(t,s),this.gcOrigins()}getOrigin(t){return this.ensureLoaded(),this.origins.get(t)??null}removeOrigin(t){return this.ensureLoaded(),this.origins.delete(t)}allOrigins(){return this.ensureLoaded(),this.gcOrigins(),new Map(this.origins)}originSize(){return this.ensureLoaded(),this.origins.size}gc(){let t=Date.now()-vl;for(let[s,n]of this.entries){let r=Date.parse(n.last_seen_at);!Number.isNaN(r)&&r<t&&this.entries.delete(s)}}gcOrigins(){let t=Date.now()-vl;for(let[s,n]of this.origins)n.detectedAt<t&&this.origins.delete(s)}reapStaleLinks(t=10080*60*1e3){this.ensureLoaded();let n=Date.now()-t,r=0,o=0;for(let[i,a]of this.sessionsByPid){let d=this.entries.get(i);if(d){let l=Date.parse(d.last_seen_at);if(Number.isFinite(l)&&l>=n)continue}o+=a.size,this.sessionsByPid.delete(i),d&&(this.entries.delete(i),r++)}return(r||o)&&this.save(),{pruned_pids:r,pruned_sessions:o}}gcDeadPids(){this.ensureLoaded();let t=0,s=0;for(let n of[...this.entries.keys()]){let r=!0;try{process.kill(n,0)}catch(i){i.code==="ESRCH"&&(r=!1)}if(r)continue;this.entries.delete(n),t++;let o=this.sessionsByPid.get(n);o&&(s+=o.size,this.sessionsByPid.delete(n)),this.outputTails.delete(n),this.pidOwnership.delete(n)}return(t||s)&&this.save(),{pruned_pids:t,pruned_sessions:s}}},co=new ao;P();function vS(){let e=`${x}/daemon.port`;if(!lo(e))return null;try{let t=uo(e,"utf8").trim();return/^\d+$/.test(t)?t:null}catch{return null}}async function IS(e,t){try{return(await st("POST",`http://127.0.0.1:${e}/api/sessions/${encodeURIComponent(t)}/relink`,{clear:!0})).ok}catch{return!1}}function MS(e,t){let s=_(),n=t?" AND p.name = ?":"",r=t?[t]:[],o=s.prepare(`SELECT s.id, p.name AS project_name, s.cwd, s.started_at, sa.alias
1601
+ ORDER BY COALESCE(sc.committed_at, s.started_at, '') DESC`).all(n,s)}function WT(e){return E().prepare("SELECT id FROM sessions WHERE id = ? OR id LIKE ? LIMIT 1").get(e,`${e}%`)?.id??null}function XT(e){let t=e.sessionId.slice(0,8);switch(e.status){case"ok":console.log(`${c.ok("\u2713")} ${c.dim(t)} ${c.bold(String(e.commitsFound))} commits (${e.commitsInserted} new)`);break;case"no-cwd":console.log(`${c.dim("\xB7")} ${c.dim(t)} no cwd recorded \u2014 skipped`);break;case"not-a-repo":console.log(`${c.dim("\xB7")} ${c.dim(t)} cwd is not a git worktree \u2014 skipped`);break;case"cwd-missing":console.log(`${c.dim("\xB7")} ${c.dim(t)} cwd no longer exists on disk \u2014 skipped`);break;case"no-window":console.log(`${c.dim("\xB7")} ${c.dim(t)} session has no time window yet \u2014 skipped`);break;case"error":console.log(`${c.err("\u2717")} ${c.dim(t)} ${e.error??"unknown error"}`);break}}async function wu(e,t){if(e){let r=WT(e);if(!r){console.error(c.err(`no session matches '${e}'`)),process.exit(1);return}let o=await mi(r);if(t.json){console.log(JSON.stringify(o,null,2));return}XT(o);return}let n=t.limit?Math.max(1,Number(t.limit)):void 0;console.log(c.dim(`correlating sessions to git commits (limit ${n??"default"})\u2026`));let s=await Su({limit:n,onProgress:(r,o)=>{(r%25===0||r===o)&&process.stderr.write(`\r${c.dim(` ${r}/${o}`)}`)}});if(process.stderr.write(`
1602
+ `),t.json){console.log(JSON.stringify(s,null,2));return}console.log(""),console.log(`${c.ok("correlated")}: ${c.bold(String(s.ok))} sessions \xB7 ${c.bold(String(s.commitsInserted))} new commits`),console.log(c.dim(` skipped ${s.skipped} (no cwd / non-repo / cwd missing), errors ${s.errors}`)),console.log("")}$();R();Ct();import{existsSync as Ei,readFileSync as bi}from"node:fs";D();import{existsSync as GT,readFileSync as JT,writeFileSync as zT}from"node:fs";import{join as YT}from"node:path";import{z as he}from"zod";var pi=YT(x,"terminals.json"),yu=1440*60*1e3,qT=3e4,VT=6e4,KT=he.object({shell_pid:he.number(),tab_name:he.string(),cwd:he.string().nullable().optional(),opened_at:he.string(),last_seen_at:he.string()}),QT=he.object({schema:he.string().optional(),saved_at:he.string().optional(),terminals:he.array(KT).max(500).default([]),sessions_by_pid:he.record(he.string(),he.array(he.string()).max(50)).optional().default({})}),Tu=/^[⠀-⣿✳\s]+/,Ru=/^\d+(\.\d+){1,3}$/;function fi(e){let t=e.trim();return!!(!t||Tu.test(t)||Ru.test(t))}function ku(e){let t=e.trim();if(!t||Ru.test(t))return null;let n=t.replace(Tu,"").trim();return n.length>0?n:null}function gi(e,t){if(!fi(e))return e;let n=ku(e);return n||(t&&!fi(t)?t:e)}function ZT(e){let t=e.now-e.withinMs,n=e.pending.filter(s=>{let r=Date.parse(s.started_at);return Number.isFinite(r)&&r>=t});if(n.length===0)return{kind:"none"};if(e.shellPid!=null){let s=n.find(r=>r.shell_pid===e.shellPid);if(s)return{kind:"pid-match",entry:s}}if(e.cwd){let s=e.cwd.replace(/\/+$/,""),r=n.filter(o=>o.cwd&&o.cwd.replace(/\/+$/,"")===s);if(r.length===1)return{kind:"singleton-cwd",entry:r[0]};if(r.length>=2)return{kind:"ambiguous",candidates:r}}return{kind:"none"}}var _i=class{entries=new Map;origins=new Map;sessionsByPid=new Map;pendingClaudeStarts=[];deferredLinks=new Map;pidOwnership=new Map;loaded=!1;ensureLoaded(){if(!this.loaded&&(this.loaded=!0,!!GT(pi)))try{let t=JT(pi,"utf8"),n=JSON.parse(t),s=QT.safeParse(n);if(!s.success){console.warn("[terminal-registry] terminals.json failed validation, starting with empty registry:",s.error.issues);return}let r=s.data;for(let o of r.terminals)this.entries.set(o.shell_pid,{shell_pid:o.shell_pid,tab_name:o.tab_name,cwd:o.cwd??null,opened_at:o.opened_at,last_seen_at:o.last_seen_at});for(let[o,i]of Object.entries(r.sessions_by_pid??{})){let a=Number(o);if(!Number.isFinite(a))continue;let d=new Set;for(let l of i)l.length>0&&d.add(l);d.size>0&&this.sessionsByPid.set(a,d)}this.gc()}catch{}}save(){try{j();let t={schema:"claude-recall.terminals.v1",saved_at:new Date().toISOString(),terminals:Array.from(this.entries.values()),sessions_by_pid:Object.fromEntries(Array.from(this.sessionsByPid.entries()).map(([n,s])=>[String(n),Array.from(s)]))};zT(pi,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let n=new Date().toISOString(),s=this.entries.get(t.shell_pid),r=gi(t.tab_name,s?.tab_name),o=s?.opened_at??t.opened_at,i={...t,tab_name:r,opened_at:o,last_seen_at:n};return this.entries.set(t.shell_pid,i),this.gc(),this.save(),i}rename(t,n){this.ensureLoaded();let s=this.entries.get(t);if(!s)return null;let r=gi(n,s.tab_name),o={...s,tab_name:r,last_seen_at:new Date().toISOString()};return this.entries.set(t,o),this.save(),o}remove(t){this.ensureLoaded();let n=this.entries.delete(t),s=this.sessionsByPid.delete(t);return this.outputTails.delete(t),this.pidOwnership.delete(t),(n||s)&&this.save(),n}claimPidOwnership(t,n,s=Date.now()){if(this.ensureLoaded(),!n)return"anonymous";let r=this.pidOwnership.get(t);return r?r.instance_id===n?(r.last_claim_at=s,"refreshed"):s-r.last_claim_at>VT?(this.pidOwnership.set(t,{instance_id:n,last_claim_at:s}),"claimed"):"rejected":(this.pidOwnership.set(t,{instance_id:n,last_claim_at:s}),"claimed")}pidOwnershipSnapshot(){return this.ensureLoaded(),Array.from(this.pidOwnership.entries()).map(([t,n])=>({shell_pid:t,instance_id:n.instance_id,last_claim_at:n.last_claim_at}))}sync(t){this.ensureLoaded();let n=new Date().toISOString(),s=0,r=0;for(let o of t){let i=this.entries.get(o.shell_pid),a=gi(o.tab_name,i?.tab_name),d=i?.opened_at??o.opened_at;this.entries.set(o.shell_pid,{...o,tab_name:a,opened_at:d,last_seen_at:n}),i?(i.tab_name!==a||i.cwd!==o.cwd)&&r++:s++}return this.gc(),this.save(),{added:s,updated:r,removed:0}}get(t){return this.ensureLoaded(),this.entries.get(t)??null}all(){return this.ensureLoaded(),this.gc(),Array.from(this.entries.values())}size(){return this.ensureLoaded(),this.entries.size}linkSession(t,n){this.ensureLoaded();let s=this.sessionsByPid.get(n);s||(s=new Set,this.sessionsByPid.set(n,s)),s.has(t)||(s.add(t),this.save())}sessionsFor(t){return this.ensureLoaded(),Array.from(this.sessionsByPid.get(t)??[])}isSessionAutoLinked(t){this.ensureLoaded();for(let n of this.sessionsByPid.values())if(n.has(t))return!0;return!1}unlinkSession(t){this.ensureLoaded();let n=!1;for(let[s,r]of this.sessionsByPid)r.delete(t)&&(n=!0,r.size===0&&this.sessionsByPid.delete(s));return n&&this.save(),n}pushPending(t){this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.push({...t})}takePendingMatched(t){this.ensureLoaded(),this.gcPending();let n=ZT({pending:this.pendingClaudeStarts,shellPid:t.shellPid,cwd:t.cwd,withinMs:t.withinMs,now:Date.now()});if(n.kind==="pid-match"||n.kind==="singleton-cwd"){let s=this.pendingClaudeStarts.indexOf(n.entry);s>=0&&this.pendingClaudeStarts.splice(s,1)}return n}pendingSize(){return this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.length}deferSessionLink(t,n,s,r){this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.set(t,{parent_shell_pid:n,queued_at:Date.now(),cwd:s,git_branch:r})}allDeferredLinks(){return this.ensureLoaded(),this.gcDeferredLinks(),Array.from(this.deferredLinks.entries()).map(([t,n])=>({session_id:t,...n}))}resolveDeferredLink(t){return this.deferredLinks.delete(t)}deferredLinkSize(){return this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.size}gcDeferredLinks(){let t=Date.now()-9e4;for(let[n,s]of this.deferredLinks)s.queued_at<t&&this.deferredLinks.delete(n)}gcPending(){let t=Date.now()-qT;this.pendingClaudeStarts.length!==0&&(this.pendingClaudeStarts=this.pendingClaudeStarts.filter(n=>{let s=Date.parse(n.started_at);return Number.isFinite(s)&&s>=t}))}outputTails=new Map;setOutputTail(t,n,s){this.ensureLoaded(),this.outputTails.set(t,{text:n,captured_at:s})}getOutputTail(t){return this.ensureLoaded(),this.outputTails.get(t)??null}allOutputTails(){return this.ensureLoaded(),new Map(this.outputTails)}removeOutputTail(t){return this.outputTails.delete(t)}setOrigin(t,n){this.ensureLoaded(),this.origins.set(t,n),this.gcOrigins()}getOrigin(t){return this.ensureLoaded(),this.origins.get(t)??null}removeOrigin(t){return this.ensureLoaded(),this.origins.delete(t)}allOrigins(){return this.ensureLoaded(),this.gcOrigins(),new Map(this.origins)}originSize(){return this.ensureLoaded(),this.origins.size}gc(){let t=Date.now()-yu;for(let[n,s]of this.entries){let r=Date.parse(s.last_seen_at);!Number.isNaN(r)&&r<t&&this.entries.delete(n)}}gcOrigins(){let t=Date.now()-yu;for(let[n,s]of this.origins)s.detectedAt<t&&this.origins.delete(n)}reapStaleLinks(t=10080*60*1e3){this.ensureLoaded();let s=Date.now()-t,r=0,o=0;for(let[i,a]of this.sessionsByPid){let d=this.entries.get(i);if(d){let l=Date.parse(d.last_seen_at);if(Number.isFinite(l)&&l>=s)continue}o+=a.size,this.sessionsByPid.delete(i),d&&(this.entries.delete(i),r++)}return(r||o)&&this.save(),{pruned_pids:r,pruned_sessions:o}}gcDeadPids(){this.ensureLoaded();let t=0,n=0;for(let s of[...this.entries.keys()]){let r=!0;try{process.kill(s,0)}catch(i){i.code==="ESRCH"&&(r=!1)}if(r)continue;this.entries.delete(s),t++;let o=this.sessionsByPid.get(s);o&&(n+=o.size,this.sessionsByPid.delete(s)),this.outputTails.delete(s),this.pidOwnership.delete(s)}return(t||n)&&this.save(),{pruned_pids:t,pruned_sessions:n}}},hi=new _i;D();function tR(){let e=`${x}/daemon.port`;if(!Ei(e))return null;try{let t=bi(e,"utf8").trim();return/^\d+$/.test(t)?t:null}catch{return null}}async function nR(e,t){try{return(await dt("POST",`http://127.0.0.1:${e}/api/sessions/${encodeURIComponent(t)}/relink`,{clear:!0})).ok}catch{return!1}}function sR(e,t){let n=E(),s=t?" AND p.name = ?":"",r=t?[t]:[],o=n.prepare(`SELECT s.id, p.name AS project_name, s.cwd, s.started_at, sa.alias
1466
1603
  FROM sessions s
1467
1604
  JOIN projects p ON p.id = s.project_id
1468
1605
  JOIN session_aliases sa ON sa.session_id = s.id
1469
1606
  WHERE s.cwd IS NOT NULL
1470
1607
  AND s.started_at IS NOT NULL
1471
1608
  AND sa.alias IS NOT NULL
1472
- AND sa.alias <> ''${n}`).all(...r),i=new Map;for(let l of o){let u=l.cwd.replace(/\/+$/,""),p=i.get(u);p||(p=[],i.set(u,p)),p.push(l)}let a=e*1e3,d=new Map;for(let[l,u]of i){let p=new Map;for(let m of u){let g=p.get(m.alias);g||(g=[],p.set(m.alias,g)),g.push(m)}for(let m of p.values())if(!(m.length<2))for(let g of m){let f=new Set;for(let h of m)h.id!==g.id&&f.add(h.id);d.set(g.id,{row:g,cwdKey:l,reason:"duplicate-alias",siblingIds:f})}}for(let[l,u]of i){if(u.length<2)continue;u.sort((g,f)=>Date.parse(g.started_at)-Date.parse(f.started_at));let p=[],m=()=>{if(p.length>=2)for(let g of p){if(d.has(g.id))continue;let f=new Set;for(let h of p)h.id!==g.id&&f.add(h.id);d.set(g.id,{row:g,cwdKey:l,reason:"cwd-collision",siblingIds:f})}p=[]};for(let g of u){if(p.length===0){p.push(g);continue}let f=p[p.length-1];Date.parse(g.started_at)-Date.parse(f.started_at)<=a||m(),p.push(g)}m()}return Array.from(d.values()).map(l=>({session_id:l.row.id,project_name:l.row.project_name,cwd:l.cwdKey,started_at:l.row.started_at,alias:l.row.alias,sibling_ids:Array.from(l.siblingIds),reason:l.reason}))}function DS(e){let t=[];try{let s=`${x}/terminals.json`;if(!lo(s))return t;let n=JSON.parse(uo(s,"utf8")),r=n.sessions_by_pid??{},o=new Map;for(let a of n.terminals??[])o.set(a.shell_pid,a);let i=_();for(let[a,d]of Object.entries(r)){let l=Number(a);if(!Number.isFinite(l))continue;let u=o.get(l)??null;for(let p of d){let m=i.prepare(`SELECT s.id, p.name AS project_name, s.cwd, s.started_at,
1609
+ AND sa.alias <> ''${s}`).all(...r),i=new Map;for(let l of o){let u=l.cwd.replace(/\/+$/,""),m=i.get(u);m||(m=[],i.set(u,m)),m.push(l)}let a=e*1e3,d=new Map;for(let[l,u]of i){let m=new Map;for(let p of u){let g=m.get(p.alias);g||(g=[],m.set(p.alias,g)),g.push(p)}for(let p of m.values())if(!(p.length<2))for(let g of p){let f=new Set;for(let h of p)h.id!==g.id&&f.add(h.id);d.set(g.id,{row:g,cwdKey:l,reason:"duplicate-alias",siblingIds:f})}}for(let[l,u]of i){if(u.length<2)continue;u.sort((g,f)=>Date.parse(g.started_at)-Date.parse(f.started_at));let m=[],p=()=>{if(m.length>=2)for(let g of m){if(d.has(g.id))continue;let f=new Set;for(let h of m)h.id!==g.id&&f.add(h.id);d.set(g.id,{row:g,cwdKey:l,reason:"cwd-collision",siblingIds:f})}m=[]};for(let g of u){if(m.length===0){m.push(g);continue}let f=m[m.length-1];Date.parse(g.started_at)-Date.parse(f.started_at)<=a||p(),m.push(g)}p()}return Array.from(d.values()).map(l=>({session_id:l.row.id,project_name:l.row.project_name,cwd:l.cwdKey,started_at:l.row.started_at,alias:l.row.alias,sibling_ids:Array.from(l.siblingIds),reason:l.reason}))}function rR(e){let t=[];try{let n=`${x}/terminals.json`;if(!Ei(n))return t;let s=JSON.parse(bi(n,"utf8")),r=s.sessions_by_pid??{},o=new Map;for(let a of s.terminals??[])o.set(a.shell_pid,a);let i=E();for(let[a,d]of Object.entries(r)){let l=Number(a);if(!Number.isFinite(l))continue;let u=o.get(l)??null;for(let m of d){let p=i.prepare(`SELECT s.id, p.name AS project_name, s.cwd, s.started_at,
1473
1610
  NULLIF(sa.alias, '') AS alias
1474
1611
  FROM sessions s
1475
1612
  JOIN projects p ON p.id = s.project_id
1476
1613
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1477
- WHERE s.id = ?`).get(p);m&&(m.alias||e&&m.project_name!==e||t.push({session_id:m.id,project_name:m.project_name,cwd:(m.cwd??"").replace(/\/+$/,""),started_at:m.started_at??"",alias:"",sibling_ids:[],reason:"orphan-link",stale_terminal:u}))}}}catch{}return t}function $S(e){let t=[],s=`${x}/terminals.json`;if(!lo(s))return t;let n;try{n=JSON.parse(uo(s,"utf8"))}catch{return t}let r=new Map;for(let l of n.terminals??[]){if(!l.tab_name)continue;let u=r.get(l.tab_name);u||(u=[],r.set(l.tab_name,u)),u.push(l)}if(r.size===0)return t;let o=_(),i=e?" AND p.name = ?":"",a=e?[e]:[],d=o.prepare(`SELECT s.id, p.name AS project_name, s.cwd, s.started_at, sa.alias
1614
+ WHERE s.id = ?`).get(m);p&&(p.alias||e&&p.project_name!==e||t.push({session_id:p.id,project_name:p.project_name,cwd:(p.cwd??"").replace(/\/+$/,""),started_at:p.started_at??"",alias:"",sibling_ids:[],reason:"orphan-link",stale_terminal:u}))}}}catch{}return t}function oR(e){let t=[],n=`${x}/terminals.json`;if(!Ei(n))return t;let s;try{s=JSON.parse(bi(n,"utf8"))}catch{return t}let r=new Map;for(let l of s.terminals??[]){if(!l.tab_name)continue;let u=r.get(l.tab_name);u||(u=[],r.set(l.tab_name,u)),u.push(l)}if(r.size===0)return t;let o=E(),i=e?" AND p.name = ?":"",a=e?[e]:[],d=o.prepare(`SELECT s.id, p.name AS project_name, s.cwd, s.started_at, sa.alias
1478
1615
  FROM sessions s
1479
1616
  JOIN projects p ON p.id = s.project_id
1480
1617
  JOIN session_aliases sa ON sa.session_id = s.id
1481
1618
  WHERE s.started_at IS NOT NULL
1482
1619
  AND sa.alias IS NOT NULL
1483
- AND sa.alias <> ''${i}`).all(...a);for(let l of d){let u=r.get(l.alias);if(!u||u.length===0)continue;let p=Date.parse(l.started_at);if(!Number.isFinite(p))continue;let m=!1,g=null,f=1/0;for(let h of u){let E=Date.parse(h.opened_at);if(Number.isFinite(E)){if(E-p<=6e4){m=!0;break}E<f&&(f=E,g=h)}}m||!g||t.push({session_id:l.id,project_name:l.project_name,cwd:l.cwd.replace(/\/+$/,""),started_at:l.started_at,alias:l.alias,sibling_ids:[],reason:"temporal-anomaly",postdating_terminal:g})}return t}function PS(e){if(e.length===0){console.log(c.ok("No suspicious correlations found."));return}console.log(c.warn(`Found ${e.length} session${e.length===1?"":"s"} with likely-bad correlator aliases:`)),console.log();let t=new Map;for(let s of e){let n=t.get(s.cwd);n||(n=[],t.set(s.cwd,n)),n.push(s)}for(let[s,n]of t){console.log(c.dim(` cwd: ${s}`)),n.sort((r,o)=>Date.parse(r.started_at)-Date.parse(o.started_at));for(let r of n){let o=r.reason==="duplicate-alias"?c.err("[dup-alias]"):r.reason==="orphan-link"?c.warn("[orphan-link]"):r.reason==="temporal-anomaly"?c.err("[temporal]"):c.warn("[cwd-collision]"),i=r.reason==="orphan-link"&&r.stale_terminal?`${c.dim("still pointing at pid")} ${r.stale_terminal.shell_pid} ${c.dim("=")} ${c.bold(`"${r.stale_terminal.tab_name}"`)}`:r.reason==="temporal-anomaly"&&r.postdating_terminal?`${c.bold(`"${r.alias}"`)} ${c.dim("\u2190 terminal opened")} ${r.postdating_terminal.opened_at.replace("T"," ").slice(0,19)} ${c.dim("(after session)")}`:c.bold(`"${r.alias}"`);console.log(` ${o} ${c.dim(r.session_id.slice(0,8))} ${c.dim(r.started_at.replace("T"," ").slice(0,19))} \u2192 ${i}`)}console.log()}}async function $l(e){let t=e.window?Math.max(5,Math.min(600,Number(e.window)||60)):60,s=e.project?.trim()||void 0,n=MS(t,s),r=DS(s),o=$S(s),i=new Set,a=[];for(let p of[...n,...r,...o])i.has(p.session_id)||(i.add(p.session_id),a.push(p));if(e.json){console.log(JSON.stringify({window_sec:t,suspects:a},null,2));return}if(PS(a),!e.fix){a.length>0&&console.log(c.dim("Re-run with --fix to clear these aliases. Heuristic titles will display instead, and you can manually relink via the UI's \u{1F517} picker."));return}let d=vS(),l=0,u=0;for(let p of a){if(d){await IS(d,p.session_id)?l++:(u++,console.error(c.err(`failed to clear ${p.session_id.slice(0,8)} via daemon`)));continue}try{Ma(p.session_id),co.unlinkSession(p.session_id),l++}catch(m){u++,console.error(c.err(`failed to clear ${p.session_id.slice(0,8)}: ${m.message}`))}}console.log(c.ok(`Cleared ${l} suspect alias${l===1?"":"es"}.`)),u>0&&console.log(c.warn(`${u} clears failed; see errors above.`)),d||console.log(c.dim("Daemon was not running, so changes were applied directly to state files.")),console.log(c.dim("Open an affected session in the UI and use the \u{1F517} picker to pin the correct terminal."))}v();w();P();import{existsSync as Hl,readFileSync as JS,statSync as go,statfsSync as GS}from"node:fs";import{join as Wl}from"node:path";import*as Xl from"node:http";P();w();import{watch as RI}from"chokidar";import{readdirSync as WS,statSync as kI}from"node:fs";import{basename as OI,join as XS}from"node:path";import{execFile as US}from"node:child_process";import{promisify as BS}from"node:util";Et();w();import{execFile as FS}from"node:child_process";import{promisify as jS}from"node:util";var Uv=jS(FS);var qv=BS(US);var Kv=3600*1e3;w();P();import{basename as aI,join as po}from"node:path";w();P();import{join as HS}from"node:path";var tI=HS(x,"collections.json");var Pl=po(x,"auto-rules"),uI=po(Pl,"rules.json"),pI=po(Pl,"suggestions.json");Et();function Fl(e){let t=e.split(/[/\\]/),s=t.findIndex(n=>n==="projects");return s===-1||s+1>=t.length?null:t[s+1]??null}function jl(e){return e.replace(/\\/g,"/").includes("/subagents/")}function*mo(e){let t;try{t=WS(e,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let s of t){if(s.isSymbolicLink())continue;let n=XS(e,s.name);s.isDirectory()?yield*mo(n):s.isFile()&&s.name.endsWith(".jsonl")&&(yield n)}}var YS=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],zS=new RegExp(`^(${YS.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);function qS(e){let t=[];for(let s of e)if(s.alias){if(zS.test(s.alias)){t.push({session_id:s.session_id,alias:s.alias,violation:"fabricated-origin-label",cwd:s.cwd});continue}if(s.cwd){let n=s.cwd.replace(/\/+$/,"").split("/").pop();n&&s.alias.startsWith(`${n} \xB7 `)&&t.push({session_id:s.session_id,alias:s.alias,violation:"fabricated-cwd-branch",cwd:s.cwd})}}return t}var Jl=5*6e4,fo=24,Gl=.5;function KS(){let e=Wl(x,"daemon.port");if(!Hl(e))return null;try{let t=JS(e,"utf8").trim();if(t.startsWith("{")){let n=JSON.parse(t);return typeof n.port=="number"?n.port:null}let s=Number.parseInt(t,10);return Number.isFinite(s)&&s>0&&s<65536?s:null}catch{return null}}function VS(e,t,s=1500){return new Promise(n=>{let r=Xl.request({host:"127.0.0.1",port:e,path:t,method:"GET",timeout:s,headers:{host:"127.0.0.1","user-agent":"recall-doctor"}},o=>{let i=[];o.on("data",a=>i.push(Buffer.isBuffer(a)?a:Buffer.from(a))),o.on("end",()=>{if(!o.statusCode||o.statusCode<200||o.statusCode>=300){n(null);return}try{n(JSON.parse(Buffer.concat(i).toString("utf8")))}catch{n(null)}})});r.on("error",()=>n(null)),r.on("timeout",()=>{r.destroy(),n(null)}),r.end()})}function ZS(){let e=Wl(x,"terminals.json");if(!Hl(e))return{exists:!1,mtimeMs:null,ageSeconds:null};try{let t=go(e),s=Math.floor((Date.now()-t.mtimeMs)/1e3);return{exists:!0,mtimeMs:t.mtimeMs,ageSeconds:s}}catch{return{exists:!1,mtimeMs:null,ageSeconds:null}}}function QS(){let e=new Date(Date.now()-fo*36e5).toISOString();try{let t=_().prepare(`SELECT
1620
+ AND sa.alias <> ''${i}`).all(...a);for(let l of d){let u=r.get(l.alias);if(!u||u.length===0)continue;let m=Date.parse(l.started_at);if(!Number.isFinite(m))continue;let p=!1,g=null,f=1/0;for(let h of u){let _=Date.parse(h.opened_at);if(Number.isFinite(_)){if(_-m<=6e4){p=!0;break}_<f&&(f=_,g=h)}}p||!g||t.push({session_id:l.id,project_name:l.project_name,cwd:l.cwd.replace(/\/+$/,""),started_at:l.started_at,alias:l.alias,sibling_ids:[],reason:"temporal-anomaly",postdating_terminal:g})}return t}function iR(e){if(e.length===0){console.log(c.ok("No suspicious correlations found."));return}console.log(c.warn(`Found ${e.length} session${e.length===1?"":"s"} with likely-bad correlator aliases:`)),console.log();let t=new Map;for(let n of e){let s=t.get(n.cwd);s||(s=[],t.set(n.cwd,s)),s.push(n)}for(let[n,s]of t){console.log(c.dim(` cwd: ${n}`)),s.sort((r,o)=>Date.parse(r.started_at)-Date.parse(o.started_at));for(let r of s){let o=r.reason==="duplicate-alias"?c.err("[dup-alias]"):r.reason==="orphan-link"?c.warn("[orphan-link]"):r.reason==="temporal-anomaly"?c.err("[temporal]"):c.warn("[cwd-collision]"),i=r.reason==="orphan-link"&&r.stale_terminal?`${c.dim("still pointing at pid")} ${r.stale_terminal.shell_pid} ${c.dim("=")} ${c.bold(`"${r.stale_terminal.tab_name}"`)}`:r.reason==="temporal-anomaly"&&r.postdating_terminal?`${c.bold(`"${r.alias}"`)} ${c.dim("\u2190 terminal opened")} ${r.postdating_terminal.opened_at.replace("T"," ").slice(0,19)} ${c.dim("(after session)")}`:c.bold(`"${r.alias}"`);console.log(` ${o} ${c.dim(r.session_id.slice(0,8))} ${c.dim(r.started_at.replace("T"," ").slice(0,19))} \u2192 ${i}`)}console.log()}}async function xu(e){let t=e.window?Math.max(5,Math.min(600,Number(e.window)||60)):60,n=e.project?.trim()||void 0,s=sR(t,n),r=rR(n),o=oR(n),i=new Set,a=[];for(let m of[...s,...r,...o])i.has(m.session_id)||(i.add(m.session_id),a.push(m));if(e.json){console.log(JSON.stringify({window_sec:t,suspects:a},null,2));return}if(iR(a),!e.fix){a.length>0&&console.log(c.dim("Re-run with --fix to clear these aliases. Heuristic titles will display instead, and you can manually relink via the UI's \u{1F517} picker."));return}let d=tR(),l=0,u=0;for(let m of a){if(d){await nR(d,m.session_id)?l++:(u++,console.error(c.err(`failed to clear ${m.session_id.slice(0,8)} via daemon`)));continue}try{Vc(m.session_id),hi.unlinkSession(m.session_id),l++}catch(p){u++,console.error(c.err(`failed to clear ${m.session_id.slice(0,8)}: ${p.message}`))}}console.log(c.ok(`Cleared ${l} suspect alias${l===1?"":"es"}.`)),u>0&&console.log(c.warn(`${u} clears failed; see errors above.`)),d||console.log(c.dim("Daemon was not running, so changes were applied directly to state files.")),console.log(c.dim("Open an affected session in the UI and use the \u{1F517} picker to pin the correct terminal."))}$();R();D();import{existsSync as An,readdirSync as OR,readFileSync as yi,statSync as qs,statfsSync as vR}from"node:fs";import{homedir as IR}from"node:os";import{join as Vs}from"node:path";import*as Ju from"node:http";D();R();import{watch as $F}from"chokidar";import{readdirSync as pR,statSync as FF}from"node:fs";import{basename as WF,join as gR}from"node:path";import{execFile as lR}from"node:child_process";import{promisify as dR}from"node:util";Ct();R();import{execFile as aR}from"node:child_process";import{promisify as cR}from"node:util";var GP=cR(aR);var eF=dR(lR);var tF=3600*1e3;R();D();import{basename as mF,join as Si}from"node:path";R();D();import{join as uR}from"node:path";var iF=uR(x,"collections.json");var Cu=Si(x,"auto-rules"),_F=Si(Cu,"rules.json"),hF=Si(Cu,"suggestions.json");Ct();Ss();function Au(e){let t=e.split(/[/\\]/),n=t.findIndex(s=>s==="projects");return n===-1||n+1>=t.length?null:t[n+1]??null}function Lu(e){return e.replace(/\\/g,"/").includes("/subagents/")}function*wi(e){let t;try{t=pR(e,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let n of t){if(n.isSymbolicLink())continue;let s=gR(e,n.name);n.isDirectory()?yield*wi(s):n.isFile()&&n.name.endsWith(".jsonl")&&(yield s)}}mn();Xe();D();R();import{existsSync as fR,readFileSync as _R,renameSync as Nu,writeFileSync as hR}from"node:fs";import{join as ER}from"node:path";var bR=24,SR=3600*1e3,wR=1e3,yR=1e3,TR=1e4,RR=5e3,kR=1440*60*1e3;function Ou(){return ER(x,"doctor-state.json")}function vu(){let e=Ou();if(!fR(e))return{chunkQueue:{samples:[]}};let t;try{t=_R(e,"utf8")}catch{return{chunkQueue:{samples:[]}}}try{let n=JSON.parse(t),s=n.chunkQueue?.samples,r=Array.isArray(s)?s.filter(d=>typeof d=="object"&&d!==null&&typeof d.ts=="string"&&typeof d.size=="number"&&typeof d.semanticEnabled=="boolean"):[],o=n.autoPruneCounters?.events,i=Array.isArray(o)?o.filter(d=>{if(!d||typeof d!="object")return!1;let l=d;return!(typeof l.ts!="string"||typeof l.pid!="number"||!Number.isFinite(l.pid)||l.action!=="would_kill"&&l.action!=="killed"&&l.action!=="failed"||l.reason!=="orphan_10min"&&l.reason!=="runaway_cpu_5min")}):[],a={chunkQueue:{samples:r}};return(i.length>0||o!==void 0)&&(a.autoPruneCounters={events:i}),a}catch{try{Nu(e,`${e}.corrupt.${Date.now()}`)}catch{}return{chunkQueue:{samples:[]}}}}function Iu(e){let t=Ou(),n=`${t}.tmp`;try{hR(n,JSON.stringify(e,null,2),{mode:384}),Nu(n,t)}catch{}}function Mu(){let e=E(),t=0;try{t=e.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}catch{}let n=!1;try{n=e.prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value==="1"}catch{}let s=vu(),r=Date.now(),o=s.chunkQueue.samples,i=o.map(f=>({s:f,ms:Date.parse(f.ts)})).filter(f=>Number.isFinite(f.ms)&&r-f.ms<=SR).sort((f,h)=>f.ms-h.ms),a=null;i.length>0&&(a=t-i[0].s.size);let d=i.length,u={chunkQueue:{samples:[...o,{ts:new Date(r).toISOString(),size:t,semanticEnabled:n}].slice(-bR)}};s.autoPruneCounters&&(u.autoPruneCounters=s.autoPruneCounters),Iu(u);let m="ok",p=`chunk_queue growth: ok (current ${t.toLocaleString()} row${t===1?"":"s"}, semantic ${n?"on":"off"}, ${d} prior sample${d===1?"":"s"} in last hour).`,g=null;return t>wR&&!n&&a!==null&&a>yR?(m="critical",p=`chunk_queue growth: CRITICAL \u2014 ${t.toLocaleString()} rows with semantic disabled, grew by ${a.toLocaleString()} in the last hour. Schema-sync race shape.`,g="Schema-sync race likely \u2014 Phase 1.1 fix shipped on `feat/daemon-state-integrity`; if rows persist after a clean restart, re-investigate. Inspect with `sqlite3 ~/.recall/db.sqlite 'SELECT action, COUNT(*) FROM chunk_queue GROUP BY action;'`."):t>TR?(m="high",p=`chunk_queue growth: HIGH \u2014 ${t.toLocaleString()} rows pending (semantic ${n?"on":"off"}`+(a!==null?`, last-hour \u0394 ${a>=0?"+":""}${a.toLocaleString()}`:"")+").",g="Queue is large but stable \u2014 operator-authorized `recall semantic backfill` (or the daemon's embed worker, if intentionally on) would drain."):a!==null&&a>RR&&(m="medium",p=`chunk_queue growth: MEDIUM \u2014 grew by ${a.toLocaleString()} in the last hour (current ${t.toLocaleString()}, semantic ${n?"on":"off"}).`,g="Growth is fast even though the absolute size is small. Re-run `recall doctor` in 10\u201330 min; if growth continues without semantic on, investigate the schema-sync gate."),{status:m,currentSize:t,semanticEnabled:n,lastHourGrowth:a,priorSampleCount:d,message:p,remediation:g}}function Du(e={}){let t=e.now??Date.now(),n=vu(),s=n.autoPruneCounters?.events??[],r=s.filter(l=>{let u=Date.parse(l.ts);return Number.isFinite(u)&&t-u<=kR});if(r.length!==s.length)try{let l={chunkQueue:n.chunkQueue,autoPruneCounters:{events:r}};Iu(l)}catch{}let o={orphan_10min:0,runaway_cpu_5min:0},i=0,a=0,d=0;for(let l of r)o[l.reason]+=1,l.action==="would_kill"?i+=1:l.action==="killed"?a+=1:d+=1;return{wouldHaveKilled:i,killed:a,failed:d,byReason:o}}var $u="dry-run";function Pu(e=process.env){let t=e.RECALL_AUTO_PRUNE;if(typeof t!="string")return $u;let n=t.trim().toLowerCase();return n==="off"||n==="dry-run"||n==="enabled"?n:$u}import{existsSync as xR,readFileSync as CR,renameSync as ju,writeFileSync as AR}from"node:fs";D();import{join as LR}from"node:path";function Uu(){return LR(x,"doctor-alerts.json")}function Ys(){let e=Uu();if(!xR(e))return{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]};let t;try{t=CR(e,"utf8")}catch{return{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]}}let n;try{n=JSON.parse(t)}catch{return Fu(e),{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]}}if(!n||typeof n!="object"||Array.isArray(n))return Fu(e),{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]};let s=n,r=Array.isArray(s.alerts)?s.alerts.filter(NR):[];return{version:1,lastTickAt:typeof s.lastTickAt=="string"?s.lastTickAt:new Date(0).toISOString(),alerts:r}}function Fu(e){try{ju(e,`${e}.corrupt.${Date.now()}`)}catch{}}function NR(e){if(!e||typeof e!="object")return!1;let t=e;return typeof t.id=="string"&&typeof t.check=="string"&&(t.severity==="critical"||t.severity==="high"||t.severity==="medium")&&typeof t.message=="string"&&typeof t.remediation=="string"&&typeof t.firstSeenAt=="string"&&typeof t.lastSeenAt=="string"&&typeof t.seenCount=="number"&&typeof t.acknowledged=="boolean"}function Bu(e){let t=Uu(),n=`${t}.tmp`;try{AR(n,JSON.stringify(e,null,2),{mode:384}),ju(n,t)}catch{}}function Hu(e,t){let n=t.trim().toLowerCase();if(n.length<4)return{file:e,result:{outcome:"not-found"}};let s=e.alerts.filter(a=>a.id.startsWith(n));if(s.length===0)return{file:e,result:{outcome:"not-found"}};if(s.length>1)return{file:e,result:{outcome:"ambiguous",candidates:s.map(a=>a.id)}};let r=s[0];if(r.acknowledged)return{file:e,result:{outcome:"already-acknowledged",matched:r}};let o={...r,acknowledged:!0},i=e.alerts.map(a=>a.id===r.id?o:a);return{file:{...e,alerts:i},result:{outcome:"acknowledged",matched:o}}}function Wu(e,t="critical"){let n={medium:0,high:1,critical:2},s=n[t];return e.alerts.filter(r=>!r.acknowledged&&n[r.severity]>=s)}var MR=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],DR=new RegExp(`^(${MR.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);function $R(e){let t=[];for(let n of e)if(n.alias){if(DR.test(n.alias)){t.push({session_id:n.session_id,alias:n.alias,violation:"fabricated-origin-label",cwd:n.cwd});continue}if(n.cwd){let s=n.cwd.replace(/\/+$/,"").split("/").pop();s&&n.alias.startsWith(`${s} \xB7 `)&&t.push({session_id:n.session_id,alias:n.alias,violation:"fabricated-cwd-branch",cwd:n.cwd})}}return t}var PR=50;function FR(e,t=PR){let n=[];for(let s of e){if(s.count<=t)continue;let r=s.path.replace(/'/g,"''");n.push(`Watcher reindexed ${s.path} ${s.count.toLocaleString()} times in the last hour \u2014 reflag loop. Mark it skipped with a SQL one-liner:
1621
+ sqlite3 ~/.recall/db.sqlite "UPDATE sessions SET skipped_reason='reflag_loop_breaker' WHERE file_path = '${r}';"`)}return n}var zu=5*6e4,Ti=24,Yu=.5;function jR(){let e=Vs(x,"daemon.port");if(!An(e))return null;try{let t=yi(e,"utf8").trim();if(t.startsWith("{")){let s=JSON.parse(t);return typeof s.port=="number"?s.port:null}let n=Number.parseInt(t,10);return Number.isFinite(n)&&n>0&&n<65536?n:null}catch{return null}}function UR(e,t,n=1500){return new Promise(s=>{let r=Ju.request({host:"127.0.0.1",port:e,path:t,method:"GET",timeout:n,headers:{host:"127.0.0.1","user-agent":"recall-doctor"}},o=>{let i=[];o.on("data",a=>i.push(Buffer.isBuffer(a)?a:Buffer.from(a))),o.on("end",()=>{if(!o.statusCode||o.statusCode<200||o.statusCode>=300){s(null);return}try{s(JSON.parse(Buffer.concat(i).toString("utf8")))}catch{s(null)}})});r.on("error",()=>s(null)),r.on("timeout",()=>{r.destroy(),s(null)}),r.end()})}function BR(){let e=Vs(x,"terminals.json");if(!An(e))return{exists:!1,mtimeMs:null,ageSeconds:null};try{let t=qs(e),n=Math.floor((Date.now()-t.mtimeMs)/1e3);return{exists:!0,mtimeMs:t.mtimeMs,ageSeconds:n}}catch{return{exists:!1,mtimeMs:null,ageSeconds:null}}}function HR(){let e=new Date(Date.now()-Ti*36e5).toISOString();try{let t=E().prepare(`SELECT
1484
1622
  COUNT(*) AS total,
1485
1623
  SUM(CASE WHEN sa.alias IS NULL OR sa.alias = '' THEN 1 ELSE 0 END) AS without_alias,
1486
1624
  SUM(CASE WHEN (sa.alias IS NULL OR sa.alias = '')
1487
1625
  AND s.auto_title_source = 'heuristic' THEN 1 ELSE 0 END) AS heuristic_only
1488
1626
  FROM sessions s
1489
1627
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1490
- WHERE s.started_at >= ?`).get(e),s=t.total??0,n=t.without_alias??0,r=t.heuristic_only??0,o=s>0?r/s:0;return{total:s,withoutAlias:n,heuristicOnly:r,fractionHeuristic:o}}catch{return{total:0,withoutAlias:0,heuristicOnly:0,fractionHeuristic:0}}}async function ey(){let e=KS(),t=ZS(),s=QS(),n=!1,r=null,o=null,i=null,a=null,d=null;if(e){let p=await VS(e,"/api/health");if(p){n=!0,r=typeof p.uptimeSeconds=="number"?p.uptimeSeconds:null,o=typeof p.version=="string"?p.version:null,i=typeof p.pipeline?.silentTerminalRejections=="number"?p.pipeline.silentTerminalRejections:null,a=p.pipeline?.lastTerminalSyncAt??null;let m=p.pipeline?.autoExtract;m&&(d={circuitBroken:m.circuitBroken===!0,reason:m.reason??null,brokenAt:typeof m.brokenAt=="number"?m.brokenAt:null,consecutiveZeroTokenRuns:typeof m.consecutiveZeroTokenRuns=="number"?m.consecutiveZeroTokenRuns:0})}}let l=[];if(n||l.push("Daemon not reachable on 127.0.0.1 \u2014 start it with `recall start` before further diagnosis."),i!==null&&i>0&&l.push(`Daemon rejected ${i.toLocaleString()} /api/terminal/* request(s) without a valid X-Recall-Token. The editor extension is outdated relative to this daemon \u2014 tab names are not flowing through. Reinstall: \`code --install-extension extensions/vscode/clauderecall-vscode-*.vsix\`, then reload the extension host (Cmd/Ctrl+Shift+P \u2192 "Developer: Restart Extension Host").`),n&&r!==null&&r>30)if(!a)l.push(`Daemon has been running ${Math.round(r/60)} min but has not yet seen a single successful /api/terminal/sync. Either no editor extension is installed/active, or every attempt is being rejected (see counter above).`);else{let p=Date.now()-Date.parse(a);Number.isFinite(p)&&p>Jl&&l.push(`Last successful /api/terminal/sync was ${Math.round(p/6e4)} min ago \u2014 extension may have crashed, been disabled, or is failing auth. Run \`recall doctor\` again after restarting the extension host.`)}return!n&&t.exists&&t.ageSeconds!==null&&t.ageSeconds>24*3600&&l.push(`~/.recall/terminals.json is ${Math.round(t.ageSeconds/3600)}h old \u2014 pipeline has not produced fresh data recently. Likely root cause is the same as the rejection counter would show; start the daemon and re-run.`),d?.circuitBroken&&l.push(`Auto-extract circuit breaker tripped \u2014 ${d.reason??"reason unknown"}. Run \`recall semantic auto-extract off\` then \`... on\` to reset, or restart the daemon.`),s.total>=3&&s.fractionHeuristic>=Gl&&l.push(`${s.heuristicOnly}/${s.total} sessions in the last ${fo}h (${Math.round(s.fractionHeuristic*100)}%) fell back to the heuristic first-message title. A healthy pipeline rate is < 20%. Either the extension is not syncing tab names, or the correlator cannot disambiguate. Reinstall the extension and verify the rejection counter drops to 0.`),{state:n?l.length>0?"degraded":"ok":"down",flags:l,daemon:{running:n,port:e,uptimeSeconds:r,version:o},runtime:{silentTerminalRejections:i,lastTerminalSyncAt:a,autoExtract:d},terminalsJson:t,recentSessions:s}}function We(e){return e<1024?`${e} B`:e<1024**2?`${(e/1024).toFixed(1)} KB`:e<1024**3?`${(e/1024**2).toFixed(1)} MB`:`${(e/1024**3).toFixed(2)} GB`}function Ul(e){try{return go(e).size}catch{return 0}}function Bl(e){try{return _().prepare(`SELECT COUNT(*) AS n FROM ${e}_data WHERE block = 1`).get().n}catch{return 0}}function ty(){try{return _().prepare(`SELECT p.name AS project,
1628
+ WHERE s.started_at >= ?`).get(e),n=t.total??0,s=t.without_alias??0,r=t.heuristic_only??0,o=n>0?r/n:0;return{total:n,withoutAlias:s,heuristicOnly:r,fractionHeuristic:o}}catch{return{total:0,withoutAlias:0,heuristicOnly:0,fractionHeuristic:0}}}async function WR(){let e=jR(),t=BR(),n=HR(),s=!1,r=null,o=null,i=null,a=null,d=null,l=[];if(e){let p=await UR(e,"/api/health");if(p){s=!0,r=typeof p.uptimeSeconds=="number"?p.uptimeSeconds:null,o=typeof p.version=="string"?p.version:null,i=typeof p.pipeline?.silentTerminalRejections=="number"?p.pipeline.silentTerminalRejections:null,a=p.pipeline?.lastTerminalSyncAt??null;let g=p.pipeline?.autoExtract;g&&(d={circuitBroken:g.circuitBroken===!0,reason:g.reason??null,brokenAt:typeof g.brokenAt=="number"?g.brokenAt:null,consecutiveZeroTokenRuns:typeof g.consecutiveZeroTokenRuns=="number"?g.consecutiveZeroTokenRuns:0});let f=p.pipeline?.watcherReindexHotFiles;Array.isArray(f)&&(l=f.filter(h=>typeof h?.path=="string"&&typeof h?.count=="number"&&typeof h?.firstSeenAt=="number").map(h=>({path:h.path,count:h.count,firstSeenAt:h.firstSeenAt})))}}let u=[];if(s||u.push("Daemon not reachable on 127.0.0.1 \u2014 start it with `recall start` before further diagnosis."),i!==null&&i>0&&u.push(`Daemon rejected ${i.toLocaleString()} /api/terminal/* request(s) without a valid X-Recall-Token. The editor extension is outdated relative to this daemon \u2014 tab names are not flowing through. Reinstall: \`code --install-extension extensions/vscode/clauderecall-vscode-*.vsix\`, then reload the extension host (Cmd/Ctrl+Shift+P \u2192 "Developer: Restart Extension Host").`),s&&r!==null&&r>30)if(!a)u.push(`Daemon has been running ${Math.round(r/60)} min but has not yet seen a single successful /api/terminal/sync. Either no editor extension is installed/active, or every attempt is being rejected (see counter above).`);else{let p=Date.now()-Date.parse(a);Number.isFinite(p)&&p>zu&&u.push(`Last successful /api/terminal/sync was ${Math.round(p/6e4)} min ago \u2014 extension may have crashed, been disabled, or is failing auth. Run \`recall doctor\` again after restarting the extension host.`)}!s&&t.exists&&t.ageSeconds!==null&&t.ageSeconds>24*3600&&u.push(`~/.recall/terminals.json is ${Math.round(t.ageSeconds/3600)}h old \u2014 pipeline has not produced fresh data recently. Likely root cause is the same as the rejection counter would show; start the daemon and re-run.`),d?.circuitBroken&&u.push(`Auto-extract circuit breaker tripped \u2014 ${d.reason??"reason unknown"}. Run \`recall semantic auto-extract off\` then \`... on\` to reset, or restart the daemon.`),n.total>=3&&n.fractionHeuristic>=Yu&&u.push(`${n.heuristicOnly}/${n.total} sessions in the last ${Ti}h (${Math.round(n.fractionHeuristic*100)}%) fell back to the heuristic first-message title. A healthy pipeline rate is < 20%. Either the extension is not syncing tab names, or the correlator cannot disambiguate. Reinstall the extension and verify the rejection counter drops to 0.`);for(let p of FR(l))u.push(p);return{state:s?u.length>0?"degraded":"ok":"down",flags:u,daemon:{running:s,port:e,uptimeSeconds:r,version:o},runtime:{silentTerminalRejections:i,lastTerminalSyncAt:a,autoExtract:d},terminalsJson:t,recentSessions:n,watcherReindexHotFiles:l}}function ce(e){return e<1024?`${e} B`:e<1024**2?`${(e/1024).toFixed(1)} KB`:e<1024**3?`${(e/1024**2).toFixed(1)} MB`:`${(e/1024**3).toFixed(2)} GB`}function Xu(e){try{return qs(e).size}catch{return 0}}function XR(e){return e==="db.sqlite"||e==="db.sqlite-wal"||e==="db.sqlite-shm"?!1:!!(e.startsWith("db.sqlite.")||e.endsWith(".bak")||e.includes(".bak."))}function GR(e=x){let t=0,n=0;try{let g=vR(e);t=Number(g.bavail)*Number(g.bsize),n=Number(g.blocks)*Number(g.bsize)}catch{}let s=n>0?t/n*100:100,r=[];try{r=OR(e)}catch{r=[]}let o=0,i=0,a=[],d=Date.now(),l=720*60*60*1e3;for(let g of r){if(!XR(g))continue;let f;try{f=qs(Vs(e,g))}catch{continue}if(!f.isFile())continue;i+=1,o+=f.size;let h=Math.max(0,d-f.mtimeMs),_=Math.floor(h/(1440*60*1e3));h>=l&&a.push({name:g,sizeBytes:f.size,ageDays:_})}a.sort((g,f)=>f.sizeBytes!==g.sizeBytes?f.sizeBytes-g.sizeBytes:f.ageDays-g.ageDays);let u=2*1024**3,m=5*1024**3,p="ok";return n>0&&s<10||o>m?p="high":n>0&&s<20||o>u?p="medium":i>0&&(p="low"),{freeBytes:t,totalBytes:n,freePercent:s,backupTotalBytes:o,backupFileCount:i,oldFiles:a,severity:p}}function Gu(e){try{return E().prepare(`SELECT COUNT(*) AS n FROM ${e}_data WHERE block = 1`).get().n}catch{return 0}}function JR(){try{return E().prepare(`SELECT p.name AS project,
1491
1629
  COALESCE(NULLIF(sa.alias, ''), s.auto_title, substr(s.first_user_message, 1, 60)) AS label,
1492
1630
  COUNT(*) AS count,
1493
1631
  MAX(CASE WHEN sa.alias IS NOT NULL AND sa.alias != '' THEN 1 ELSE 0 END) AS any_aliased
@@ -1499,27 +1637,34 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
1499
1637
  GROUP BY p.name, label
1500
1638
  HAVING count >= 2
1501
1639
  ORDER BY count DESC, label ASC
1502
- LIMIT 10`).all().map(e=>{let t=e;return{project:t.project,label:t.label,count:t.count,anyAliased:t.any_aliased===1}})}catch{return[]}}function sy(e){let t=_(),s=t.pragma("page_size",{simple:!0})||4096,n=t.pragma("page_count",{simple:!0})||0,r=t.pragma("freelist_count",{simple:!0})||0;e?.("Checking database integrity");let o="ok";try{let A=t.pragma("quick_check").map($=>$.quick_check);o=A.length===1&&A[0]==="ok"?"ok":A.join("; ")}catch(D){o=`check failed: ${D.message}`}let i=Ul(se),a=Ul(`${se}-wal`),d=0,l=0;try{let D=GS(x);d=Number(D.bavail)*Number(D.bsize),l=Number(D.blocks)*Number(D.bsize)}catch{}e?.("Counting rows");let u=t.prepare(`SELECT
1640
+ LIMIT 10`).all().map(e=>{let t=e;return{project:t.project,label:t.label,count:t.count,anyAliased:t.any_aliased===1}})}catch{return[]}}function zR(e){let t=E(),n=t.pragma("page_size",{simple:!0})||4096,s=t.pragma("page_count",{simple:!0})||0,r=t.pragma("freelist_count",{simple:!0})||0;e?.("Checking database integrity");let o="ok";try{let v=t.pragma("quick_check").map(O=>O.quick_check);o=v.length===1&&v[0]==="ok"?"ok":v.join("; ")}catch(A){o=`check failed: ${A.message}`}let i=Xu(ee),a=Xu(`${ee}-wal`),d=GR(),l=d.freeBytes,u=d.totalBytes;e?.("Counting rows");let m=t.prepare(`SELECT
1503
1641
  (SELECT COUNT(*) FROM projects) AS projects,
1504
1642
  (SELECT COUNT(*) FROM sessions) AS sessions,
1505
1643
  (SELECT COUNT(*) FROM messages) AS messages,
1506
- (SELECT COUNT(*) FROM message_usage) AS message_usage`).get(),p=0;try{p=t.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get().n}catch{}e?.("Measuring FTS fragmentation");let m=[];d>0&&d<1*1024**3&&m.push(`Disk free is ${We(d)} \u2014 heavy operations (synthesis, extract-outputs, vector ingest) may fail.`);let g=100*1024**2,f=a>=sa?"error":a>=g?"warn":"ok";f==="error"?m.push(`WAL is ${We(a)} \u2014 readers are pinning the checkpoint frontier. Run \`recall mcp-prune\` to release stuck MCP children, then \`recall optimize\` to truncate.`):f==="warn"&&m.push(`WAL is ${We(a)} \u2014 run \`recall optimize\` to truncate it.`);let h=Qt(),E=h.filter(D=>D.orphan);E.length>0&&m.push(`${E.length} orphaned MCP child${E.length===1?"":"ren"} (pid${E.length===1?"":"s"}: ${E.map(D=>D.pid).join(", ")}). Each holds a SQLite read connection. Run \`recall mcp-prune\` to clean up.`);let b=h.filter(vs);b.length>0&&m.push(`${b.length} MCP child${b.length===1?"":"ren"} burning CPU (pid${b.length===1?"":"s"}: ${b.map(D=>D.pid).join(", ")}). Likely runaway vec0 kNN. Run \`recall stop --all\` or \`recall mcp-prune --all\`.`),r>n*.2&&n>1e3&&m.push(`${r.toLocaleString()} free pages (${(r/n*100).toFixed(0)}% of file) \u2014 \`recall optimize --vacuum\` will reclaim them.`);let S=Bl("messages_fts"),R=Bl("sessions_fts");S>16&&m.push(`messages_fts has ${S} segments \u2014 \`recall optimize\` will merge them.`);let T=0;try{T=t.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}catch{}let L=!1;try{L=t.prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value==="1"}catch{}return!L&&T>0?m.push(`${T.toLocaleString()} rows in chunk_queue with semantic disabled \u2014 schema gate is stale; restart daemon (v0.67+) to migrate.`):T>1e5&&m.push(`chunk_queue has ${T.toLocaleString()} pending rows \u2014 embedder is behind. \`recall semantic backfill\` to drain manually.`),{db:{sizeBytes:i,walSizeBytes:a,pageCount:n,pageSize:s,freelistCount:r,freelistBytes:r*s,integrity:o},disk:{freeBytes:d,totalBytes:l},fts:{messages:{fragments:S},sessions:{fragments:R}},vectors:{rows:p},rows:{projects:u.projects,sessions:u.sessions,messages:u.messages,messageUsage:u.message_usage},chunkQueue:{size:T,semanticEnabled:L},wal:{sizeBytes:a,level:f},mcpProcesses:h,warnings:m}}function ny(e){if(!e)return{stage:()=>{},done:()=>{}};let t=!!process.stderr.isTTY,s="",n=0,r=()=>{if(!s)return;let o=Date.now()-n,i=o<1e3?`${o}ms`:`${(o/1e3).toFixed(1)}s`;t?process.stderr.write(`\r\x1B[2K ${c.ok("\u2713")} ${s} ${c.dim(`(${i})`)}
1507
- `):process.stderr.write(` \u2713 ${s} (${i})
1508
- `),s=""};return{stage(o){r(),s=o,n=Date.now(),t?process.stderr.write(` ${c.dim("\u2026")} ${s}`):process.stderr.write(` \u2026 ${s}
1509
- `)},done:r}}function ry(e){if(console.log(c.dim("\u2014 MCP processes \u2014")),e.length===0){console.log(c.ok(" \u2713 no MCP children running"));return}let t=e.filter(o=>o.orphan),s=e.reduce((o,i)=>Math.max(o,i.etimeSeconds),0),n=e.length-t.length;if(console.log(` ${e.length} total, ${n} with live parent, ${t.length===0?c.ok("0 orphaned"):c.err(`${t.length} orphaned`)} (oldest ${It(s)})`),t.length>0){console.log(c.dim(" Orphans hold a SQLite read connection and can stall WAL checkpoints. Run `recall mcp-prune` to kill them."));for(let o of t.slice(0,5))console.log(` ${c.dim("\u2022")} pid ${o.pid} age ${It(o.etimeSeconds)} ppid=${o.ppid} (gone)`)}let r=e.filter(vs);if(r.length>0){console.log(c.warn(` ! ${r.length} MCP child${r.length===1?"":"ren"} burning CPU (likely runaway vec0 kNN -- see 2026-05-15 incident).`));for(let o of r){let i=o.parentCommand?`parent ${o.parentCommand}`:`ppid ${o.ppid}`;console.log(c.dim(` pid ${o.pid} ${o.pcpu.toFixed(0)}%cpu ${It(o.etimeSeconds)} elapsed (${i})`))}console.log(c.dim(" Run `recall stop --all` for one-command recovery, or `recall mcp-prune --all` to keep the daemon alive."))}}function oy(){let e=_(),t=e.prepare("SELECT encoded_path FROM projects").all(),s=new Set(t.map(l=>l.encoded_path));if(s.size===0)return{status:"ok",staleCount:0,scanned:0,sampleFiles:[],message:"Ingest freshness: ok (no projects indexed yet \u2014 nothing to monitor)."};let n=e.prepare("SELECT file_mtime FROM sessions WHERE file_path = ? LIMIT 1"),r=0,o=0,i=[];for(let l of mo(Lt)){if(jl(l))continue;let u=Fl(l);if(!u||!s.has(u))continue;r+=1;let p;try{p=go(l).mtimeMs}catch{continue}let m=n.get(l);(!m||m.file_mtime<p)&&(o+=1,i.length<5&&i.push(l))}if(o===0)return{status:"ok",staleCount:0,scanned:r,sampleFiles:[],message:`Ingest freshness: ok (scanned ${r} JSONL${r===1?"":"s"} in ${s.size} known project${s.size===1?"":"s"}, none newer than the index).`};let a=o>10?"fail":"warn",d=i.slice(0,3).map(l=>l.split(/[/\\]/).pop()??l).join(", ");return{status:a,staleCount:o,scanned:r,sampleFiles:i,message:`Ingest freshness: ${a} \u2014 ${o} JSONL${o===1?"":" files"} newer than the index (sample: ${d}). Run \`recall index\` to force a reindex, or restart the daemon if this is persistent.`}}async function Yl(e={}){let t=ny(!e.json);t.stage("Scanning session aliases");let s=_().prepare(`SELECT sa.session_id AS session_id, sa.alias AS alias, s.cwd AS cwd
1644
+ (SELECT COUNT(*) FROM message_usage) AS message_usage`).get(),p=0;try{p=t.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get().n}catch{}e?.("Measuring FTS fragmentation");let g=[];if(l>0&&l<1*1024**3&&g.push(`Disk free is ${ce(l)} \u2014 heavy operations (synthesis, extract-outputs, vector ingest) may fail.`),d.severity==="high"||d.severity==="medium"){let A=d.severity==="high"?"HIGH":"MEDIUM",v=[];u>0&&v.push(`disk: ${ce(l)} free of ${ce(u)} (${d.freePercent.toFixed(1)}%)`),d.backupFileCount>0&&v.push(`backups: ${ce(d.backupTotalBytes)} in ${d.backupFileCount} file${d.backupFileCount===1?"":"s"}`);let O=`[${A}] disk pressure \u2014 ${v.join(" \xB7 ")}`;if(d.oldFiles.length>0){let k=d.oldFiles.map(I=>` \u2022 ${I.name} ${ce(I.sizeBytes)} (${I.ageDays} days old)`);O+=`
1645
+ Snapshots older than 30 days:
1646
+ `+k.join(`
1647
+ `)+"\n review then delete with: `ls -la ~/.recall/*.bak* | sort -k6,7` \u2014 items older than 30 days are typically safe per memory `partial_corpus_swap_bug_20260519` (keep most recent .pre-swap for rollback)."}else d.backupFileCount>0&&(O+=`
1648
+ No snapshots older than 30 days \u2014 recent ones may still be rollback-critical; leave them alone unless you know what you're doing.`);g.push(O)}let f=100*1024**2,h=a>=Ec?"error":a>=f?"warn":"ok";h==="error"?g.push(`WAL is ${ce(a)} \u2014 readers are pinning the checkpoint frontier. Run \`recall mcp-prune\` to release stuck MCP children, then \`recall optimize\` to truncate.`):h==="warn"&&g.push(`WAL is ${ce(a)} \u2014 run \`recall optimize\` to truncate it.`);let _=xt(),b=_.filter(A=>A.orphan);b.length>0&&g.push(`${b.length} orphaned MCP child${b.length===1?"":"ren"} (pid${b.length===1?"":"s"}: ${b.map(A=>A.pid).join(", ")}). Each holds a SQLite read connection. Run \`recall mcp-prune\` to clean up.`);let S=_.filter(es);S.length>0&&g.push(`${S.length} MCP child${S.length===1?"":"ren"} burning CPU (pid${S.length===1?"":"s"}: ${S.map(A=>A.pid).join(", ")}). Likely runaway vec0 kNN. Run \`recall stop --all\` or \`recall mcp-prune --all\`.`),r>s*.2&&s>1e3&&g.push(`${r.toLocaleString()} free pages (${(r/s*100).toFixed(0)}% of file) \u2014 \`recall optimize --vacuum\` will reclaim them.`);let w=Gu("messages_fts"),y=Gu("sessions_fts");w>16&&g.push(`messages_fts has ${w} segments \u2014 \`recall optimize\` will merge them.`);let T=0;try{T=t.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}catch{}let B=!1;try{B=t.prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value==="1"}catch{}return!B&&T>0?g.push(`${T.toLocaleString()} rows in chunk_queue with semantic disabled \u2014 schema gate is stale; restart daemon (v0.67+) to migrate.`):T>1e5&&g.push(`chunk_queue has ${T.toLocaleString()} pending rows \u2014 embedder is behind. \`recall semantic backfill\` to drain manually.`),{db:{sizeBytes:i,walSizeBytes:a,pageCount:s,pageSize:n,freelistCount:r,freelistBytes:r*n,integrity:o},disk:{freeBytes:l,totalBytes:u,backups:{totalBytes:d.backupTotalBytes,fileCount:d.backupFileCount,oldFiles:d.oldFiles,severity:d.severity}},fts:{messages:{fragments:w},sessions:{fragments:y}},vectors:{rows:p},rows:{projects:m.projects,sessions:m.sessions,messages:m.messages,messageUsage:m.message_usage},chunkQueue:{size:T,semanticEnabled:B},wal:{sizeBytes:a,level:h},mcpProcesses:_,warnings:g}}function YR(e){if(!e)return{stage:()=>{},done:()=>{}};let t=!!process.stderr.isTTY,n="",s=0,r=()=>{if(!n)return;let o=Date.now()-s,i=o<1e3?`${o}ms`:`${(o/1e3).toFixed(1)}s`;t?process.stderr.write(`\r\x1B[2K ${c.ok("\u2713")} ${n} ${c.dim(`(${i})`)}
1649
+ `):process.stderr.write(` \u2713 ${n} (${i})
1650
+ `),n=""};return{stage(o){r(),n=o,s=Date.now(),t?process.stderr.write(` ${c.dim("\u2026")} ${n}`):process.stderr.write(` \u2026 ${n}
1651
+ `)},done:r}}function qR(e){let t=e??st(),n=t.length;if(n<=1)return{flagged:!1,severity:"ok",count:n,processes:t,message:null};let s=t.map(o=>{let i=un(o.etimeSeconds)??o.etime;return`pid=${o.pid} started=${i} age=${ut(o.etimeSeconds)}`}),r=`${n} Recall daemons running simultaneously: ${s.join("; ")}. Two daemons write the same db.sqlite and corrupt state (chunk_queue fills, schema-sync race re-enables feature flags). Run \`recall stop\` to kill all (now nukes orphans too as of Phase 2.6), then \`recall start\`.`;return{flagged:!0,severity:"critical",count:n,processes:t,message:r}}function VR(e={}){if((e.liveDaemons??st({excludePids:[process.pid]})).length===0)return{flagged:!1,severity:"ok",daemonAlive:!1,missing:[],message:null,remediation:null};let n=e.paths??Kn(),s=e.existsSync??An,r=e.isProcessAlive??pn,o=!1;if(s(n.pid))try{let d=JSON.parse(yi(n.pid,"utf8"));typeof d.pid=="number"&&r(d.pid)&&(o=!0)}catch{}if(!o)return{flagged:!1,severity:"ok",daemonAlive:!0,missing:[],message:null,remediation:null};let i=[];return s(n.port)||i.push("daemon.port"),s(n.token)||i.push("daemon.token"),i.length===0?{flagged:!1,severity:"ok",daemonAlive:!0,missing:[],message:null,remediation:null}:{flagged:!0,severity:"critical",daemonAlive:!0,missing:i,message:`Daemon process is alive (per pidfile) but companion state file${i.length===1?"":"s"} ${i.join(", ")} missing. Web UI and authenticated CLI calls will return 401 until the 30s heal tick restores them.`,remediation:"The 30-second heal tick re-creates these files automatically. To force immediate recovery: recall stop && recall start. Inspect ~/.recall/daemon.log for [state-files-audit] entries to identify the deletion source."}}function KR(e){e.flagged&&(console.log(""),console.log(c.dim("\u2014 Daemon state files \u2014")),console.log(c.err(` \u2717 CRITICAL: daemon process alive but missing ${e.missing.join(", ")}`)),e.remediation&&console.log(c.dim(` Remediation: ${e.remediation}`)))}function QR(e){if(e.flagged){console.log(""),console.log(c.dim("\u2014 Daemon processes \u2014")),console.log(c.err(` \u2717 CRITICAL: ${e.count} daemons running (operator rule: never two)`));for(let t of e.processes){let n=un(t.etimeSeconds)??t.etime;console.log(` ${c.dim("\u2022")} pid ${t.pid} started ${n} age ${ut(t.etimeSeconds)} ppid=${t.ppid}`)}e.message&&console.log(c.dim(" Remediation: `recall stop` (now nukes orphans too as of Phase 2.6), then `recall start`."))}}function ZR(e){if(console.log(c.dim("\u2014 MCP processes \u2014")),e.length===0){console.log(c.ok(" \u2713 no MCP children running"));return}let t=e.filter(i=>i.orphan),n=e.reduce((i,a)=>Math.max(i,a.etimeSeconds),0),s=e.length-t.length;if(console.log(` ${e.length} total, ${s} with live parent, ${t.length===0?c.ok("0 orphaned"):c.err(`${t.length} orphaned`)} (oldest ${ut(n)})`),t.length>0){console.log(c.dim(" Orphans hold a SQLite read connection and can stall WAL checkpoints. Run `recall mcp-prune` to kill them."));for(let i of t.slice(0,5))console.log(` ${c.dim("\u2022")} pid ${i.pid} age ${ut(i.etimeSeconds)} ppid=${i.ppid} (gone)`)}let r=e.filter(es);if(r.length>0){console.log(c.warn(` ! ${r.length} MCP child${r.length===1?"":"ren"} burning CPU (likely runaway vec0 kNN -- see 2026-05-15 incident).`));for(let i of r){let a=i.parentCommand?`parent ${i.parentCommand}`:`ppid ${i.ppid}`;console.log(c.dim(` pid ${i.pid} ${i.pcpu.toFixed(0)}%cpu ${ut(i.etimeSeconds)} elapsed (${a})`))}console.log(c.dim(" Run `recall stop --all` for one-command recovery, or `recall mcp-prune --all` to keep the daemon alive."))}let o=hc(e);o.flagged&&o.message&&console.log(c.warn(` ! ${o.message}`))}function ek(){let e=Pu(),t=Du();if(console.log(c.dim("\u2014 Auto-prune (last 24h) \u2014")),console.log(` Mode: ${e}`),e==="off"){console.log(c.dim(" (auto-prune disabled \u2014 orphans + runaway MCPs will accumulate until manually reaped)"));return}e==="dry-run"?console.log(` Would-have-killed: ${t.wouldHaveKilled} ${c.dim("(dry-run only)")}`):(console.log(` Killed: ${t.killed} ${c.dim("(enabled)")}`),t.failed>0&&console.log(` Failed: ${c.warn(String(t.failed))} ${c.dim("(kill returned EPERM or similar)")}`));let n=t.byReason;console.log(` By reason: orphan_10min=${n.orphan_10min}, runaway_cpu_5min=${n.runaway_cpu_5min}`)}function tk(){let e=E(),t,n,s;try{t=e.prepare("SELECT * FROM migration_state WHERE status IN ('in_progress', 'paused') LIMIT 1").get(),n=e.prepare("SELECT * FROM migration_state WHERE status = 'completed' ORDER BY id DESC LIMIT 1").get(),s=e.prepare("SELECT * FROM migration_state WHERE status IN ('failed', 'rolled_back') ORDER BY id DESC LIMIT 1").get()}catch(i){let a=i instanceof Error?i.message:String(i);return a.includes("no such table: migration_state")||process.stderr.write(`[doctor] renderMigrationDoctorSection: ${a}
1652
+ `),null}if(!t&&!n&&!s)return null;let r=["--- Migration status ---"];if(t&&(r.push(` Status: ${t.status}`),r.push(` Cursor: chunk_id=${t.cursor_chunk_id??"(none yet)"}`),r.push(` Lock pid: ${t.lock_taken_by_pid??"(released)"}`),r.push(` Old model: ${t.model_id_old}`),r.push(` New model: ${t.model_id_new}`)),n&&!t&&e.prepare("SELECT name FROM sqlite_master WHERE name='vec_chunks_v1_backup'").get()){let a=e.prepare("SELECT CAST((julianday('now') - julianday(?)) AS INTEGER) AS days").get(n.completed_at),d=Math.max(0,30-a.days);r.push(` Backup retained: ${d} day(s) until auto-prune.`),r.push(` Last completed: ${n.completed_at}`),r.push(` Old model: ${n.model_id_old}`),r.push(` New model: ${n.model_id_new}`)}s&&!t&&!n&&(r.push(` ! Last migration: ${s.status}`),r.push(` Completed at: ${s.completed_at}`),r.push(` Old model: ${s.model_id_old}`),r.push(` New model: ${s.model_id_new}`),r.push(" Remediation: inspect daemon log; re-run `recall semantic migrate` to retry"));let o=nk();return o>0&&r.push(` ! ${o} orphan row(s) in vec_chunks_v1_backup (deletion path missed the backup; report to maintainer)`),r.join(`
1653
+ `)}function nk(){let e=E();return e.prepare("SELECT name FROM sqlite_master WHERE name='vec_chunks_v1_backup'").get()?e.prepare("SELECT COUNT(*) AS n FROM vec_chunks_v1_backup b LEFT JOIN chunk_meta cm ON cm.rowid = b.rowid WHERE cm.rowid IS NULL").get().n:0}function sk(){let e=E(),t=e.prepare("SELECT encoded_path FROM projects").all(),n=new Set(t.map(l=>l.encoded_path));if(n.size===0)return{status:"ok",staleCount:0,scanned:0,sampleFiles:[],message:"Ingest freshness: ok (no projects indexed yet \u2014 nothing to monitor)."};let s=e.prepare("SELECT file_mtime, skipped_reason FROM sessions WHERE file_path = ? LIMIT 1"),r=0,o=0,i=[];for(let l of wi($t)){if(Lu(l))continue;let u=Au(l);if(!u||!n.has(u))continue;r+=1;let m;try{m=qs(l).mtimeMs}catch{continue}let p=s.get(l);p&&p.skipped_reason!==null||(!p||p.file_mtime<m)&&(o+=1,i.length<5&&i.push(l))}if(o===0)return{status:"ok",staleCount:0,scanned:r,sampleFiles:[],message:`Ingest freshness: ok (scanned ${r} JSONL${r===1?"":"s"} in ${n.size} known project${n.size===1?"":"s"}, none newer than the index).`};let a=o>10?"fail":"warn",d=i.slice(0,3).map(l=>l.split(/[/\\]/).pop()??l).join(", ");return{status:a,staleCount:o,scanned:r,sampleFiles:i,message:`Ingest freshness: ${a} \u2014 ${o} JSONL${o===1?"":" files"} newer than the index (sample: ${d}). Run \`recall index\` to force a reindex, or restart the daemon if this is persistent.`}}function rk(e=Vs(IR(),".claude.json")){let t={status:"ok",configPath:e,configExists:An(e),findings:[]};if(!t.configExists)return t;let n;try{n=yi(e,"utf8")}catch{return t}let s;try{s=JSON.parse(n)}catch{return t}if(!s||typeof s!="object"||Array.isArray(s))return t;let r=s.mcpServers;if(!r||typeof r!="object"||Array.isArray(r))return t;for(let[o,i]of Object.entries(r)){if(!i||typeof i!="object")continue;let a=i.args;if(!Array.isArray(a)||a.length===0)continue;let d=a[0];if(typeof d!="string"||d.length===0||!d.startsWith("/")&&!d.startsWith("~")||An(d))continue;let l=o==="recall";t.findings.push({name:o,stalePath:d,severity:l?"HIGH":"MEDIUM",remediation:l?"restart daemon (`recall stop && recall start`) \u2014 auto-repoint runs on boot now":"this is a third-party MCP \u2014 the vendor's package may have moved; reinstall their package"})}return t.findings.some(o=>o.severity==="HIGH")?t.status="fail":t.findings.length>0&&(t.status="warn"),t}function ok(){let e=null;try{e=ye().enabled}catch{return{status:"ok",configEnabled:null,dbValue:null,message:null}}let t=null;try{t=E().prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value??null}catch{return{status:"ok",configEnabled:e,dbValue:null,message:null}}if(t===null)return{status:"ok",configEnabled:e,dbValue:null,message:null};if(t==="1"===e)return{status:"ok",configEnabled:e,dbValue:t,message:null};let s=`config.json says ${String(e)} but DB gate says ${t}. Recover with \`recall stop && recall start\` (boot sync re-reads config). If this recurs, grep daemon.log for \`[semantic-config] gate flip\` to identify the runtime flipper.`;return{status:"critical",configEnabled:e,dbValue:t,message:s}}function ik(e,t){let n=Ys(),{file:s,result:r}=Hu(n,e);if((r.outcome==="acknowledged"||r.outcome==="already-acknowledged")&&s!==n&&Bu(s),t.json){let o=r.outcome==="acknowledged"||r.outcome==="already-acknowledged"?{outcome:r.outcome,id:r.matched?.id,check:r.matched?.check,message:r.matched?.message}:{outcome:r.outcome,candidates:r.candidates??[]};process.stdout.write(`${JSON.stringify(o)}
1654
+ `)}else switch(r.outcome){case"acknowledged":console.log(`acknowledged: ${r.matched?.message??"(no message)"}`);break;case"already-acknowledged":console.log(`already acknowledged: ${r.matched?.message??"(no message)"}`);break;case"not-found":console.log(`no alert matched "${e}". Run \`recall doctor\` to list current alerts.`);break;case"ambiguous":console.log(`"${e}" matched ${r.candidates?.length??0} alerts \u2014 use a longer prefix:`);for(let o of r.candidates??[])console.log(` ${o}`);break}return r.outcome==="not-found"||r.outcome==="ambiguous"?1:0}async function qu(e={}){if(typeof e.ack=="string"&&e.ack.length>0)return ik(e.ack,{json:!!e.json});let t=YR(!e.json);t.stage("Scanning session aliases");let n=E().prepare(`SELECT sa.session_id AS session_id, sa.alias AS alias, s.cwd AS cwd
1510
1655
  FROM session_aliases sa
1511
1656
  LEFT JOIN sessions s ON s.id = sa.session_id
1512
- WHERE sa.alias IS NOT NULL AND sa.alias != ''`).all(),n=qS(s),r=sy(t.stage);t.stage("Probing daemon");let o=await ey();t.stage("Detecting label collisions");let i=ty();t.stage("Checking ingest freshness");let a=oy();if(t.done(),e.json){process.stdout.write(JSON.stringify({scanned:s.length,violations:n.length,items:n,health:r,pipeline:o,labelCollisions:i,ingestFreshness:a},null,2)),process.stdout.write(`
1513
- `);let u=o.state==="degraded",p=a.status==="fail";return n.length===0&&r.db.integrity==="ok"&&!u&&!p?0:1}console.log(c.dim("\u2014 System health \u2014")),console.log(` Database ${We(r.db.sizeBytes)} (${r.rows.messages.toLocaleString()} messages across ${r.rows.sessions.toLocaleString()} sessions, ${r.rows.projects.toLocaleString()} projects)`);{let u=r.wal,p=u.level==="error"?c.err(We(u.sizeBytes)):u.level==="warn"?c.warn(We(u.sizeBytes)):c.ok(We(u.sizeBytes)),m=u.level==="error"?" (readers are pinning the checkpoint frontier \u2014 see warnings below)":u.level==="warn"?" (above 100 MB \u2014 daemon will WARN until it drops)":" (daemon checkpoints PASSIVE every 60s, RESTART above 5 GB)";console.log(` WAL ${p}${m}`)}console.log(` Free pages ${r.db.freelistCount.toLocaleString()} (${We(r.db.freelistBytes)} reclaimable via VACUUM)`);{let u=r.fts.messages.fragments,p=r.fts.sessions.fragments,m=u===0&&p===0;console.log(` FTS segments messages=${u}, sessions=${p}`+(m?" (merged \u2014 search uses the index)":" (lower is faster \u2014 `recall optimize` merges them)"))}if(r.chunkQueue.size>0){let p=r.chunkQueue.size>1e5?c.warn(r.chunkQueue.size.toLocaleString()):r.chunkQueue.size.toLocaleString();console.log(` Embed queue ${p}`+(r.chunkQueue.semanticEnabled?" (worker is enabled; should drain over time)":" (semantic disabled \u2014 should be 0; if not, schema migration may be stale)"))}if(console.log(` Vector rows ${r.vectors.rows.toLocaleString()}`),r.disk.totalBytes>0){let u=r.disk.freeBytes/r.disk.totalBytes*100;console.log(` Disk free ${We(r.disk.freeBytes)} of ${We(r.disk.totalBytes)} (${u.toFixed(1)}%)`)}if(console.log(` Integrity ${r.db.integrity==="ok"?c.ok("ok"):c.err(r.db.integrity)}`),r.warnings.length>0){console.log("");for(let u of r.warnings)console.log(` ${c.warn("!")} ${u}`)}if(console.log(""),ry(r.mcpProcesses),console.log(""),console.log(c.dim("\u2014 Ingest freshness \u2014")),a.status==="ok")console.log(c.ok(` \u2713 ${a.scanned.toLocaleString()} JSONL${a.scanned===1?"":"s"} scanned, none newer than the index`));else if(a.status==="warn"){console.log(` ${c.warn(`${a.staleCount} stale file${a.staleCount===1?"":"s"}`)} newer than the index \u2014 live watcher may be missing events`);for(let u of a.sampleFiles.slice(0,5))console.log(` ${c.dim("\u2022")} ${u}`);console.log(c.dim(" Run `recall index` to force a reindex, or restart the daemon if persistent."))}else{console.log(` ${c.err(`${a.staleCount} stale file${a.staleCount===1?"":"s"}`)} newer than the index \u2014 significant ingest gap`);for(let u of a.sampleFiles.slice(0,5))console.log(` ${c.dim("\u2022")} ${u}`);console.log(c.err(" Restart the daemon (recall stop && recall start) \u2014 the live watcher has fallen behind."))}if(console.log(""),console.log(c.dim("\u2014 Pipeline health (tab-name \u2192 session alias) \u2014")),o.daemon.running?console.log(` Daemon ${c.ok("running")} (port ${o.daemon.port}, version ${o.daemon.version??"?"}, up ${o.daemon.uptimeSeconds!==null?Math.round(o.daemon.uptimeSeconds/60)+" min":"?"})`):console.log(` Daemon ${c.warn("not reachable")}`),o.runtime.silentTerminalRejections!==null){let u=o.runtime.silentTerminalRejections;console.log(` Auth rejections ${u===0?c.ok("0"):c.err(u.toLocaleString())} (extension /api/terminal/* requests denied without a valid X-Recall-Token)`)}if(o.runtime.autoExtract){let u=o.runtime.autoExtract;u.circuitBroken?console.log(` Auto-extract ${c.err("circuit broken")} (${u.reason??"unknown"} \u2014 toggle off/on to reset)`):u.consecutiveZeroTokenRuns>0&&console.log(` Auto-extract ${c.warn(`${u.consecutiveZeroTokenRuns} zero-token run(s)`)} (breaker trips at 3)`)}if(o.runtime.lastTerminalSyncAt!==null){let u=Date.now()-Date.parse(o.runtime.lastTerminalSyncAt),p=Number.isFinite(u)?Math.round(u/6e4):null,m=p!==null&&u>Jl;console.log(` Last ext sync ${m?c.warn(`${p} min ago`):c.ok(p===0?"just now":`${p} min ago`)} (most recent successful POST /api/terminal/sync)`)}else o.daemon.running&&console.log(` Last ext sync ${c.warn("never")} (no extension has called /api/terminal/sync since the daemon started)`);if(o.terminalsJson.exists&&o.terminalsJson.ageSeconds!==null){let u=Math.round(o.terminalsJson.ageSeconds/3600),p=o.terminalsJson.ageSeconds>24*3600;console.log(` terminals.json ${p?c.warn(`${u}h old`):c.ok(`${u}h old`)} (persisted registry mtime \u2014 fresh means extensions are connecting)`)}let d=o.recentSessions;if(d.total>0){let u=Math.round(d.fractionHeuristic*100),p=d.fractionHeuristic>=Gl&&d.total>=3;console.log(` Recent titles ${p?c.err(`${u}% heuristic`):c.ok(`${u}% heuristic`)} (${d.heuristicOnly}/${d.total} sessions in last ${fo}h fell back to first-message title)`)}if(o.flags.length>0){console.log("");for(let u of o.flags)console.log(` ${c.warn("!")} ${u}`)}if(console.log(""),console.log(c.dim("\u2014 Label collisions (last 7d) \u2014")),i.length===0)console.log(c.ok(" \u2713 no session-list label collisions in the last week"));else{let u=i.filter(p=>!p.anyAliased);console.log(` ${c.warn(`${i.length} group(s)`)} of 2+ sessions in the same project share an identical display label \u2014 N parallel runs ended up with the same heuristic title.`);for(let p of i.slice(0,5)){let m=p.anyAliased?c.dim("partial alias"):c.warn("NO alias"),g=p.label.length>60?`${p.label.slice(0,57)}\u2026`:p.label;console.log(` ${c.dim(`${p.count}\xD7`)} ${g} ${m} ${c.dim(`(${p.project})`)}`)}i.length>5&&console.log(c.dim(` \u2026 and ${i.length-5} more (--json for full list)`)),console.log(""),console.log(c.dim(" The display layer auto-disambiguates these (HH:MM / msgs / UUID suffix), so customers\n never see two identical rows. To fix at the source \u2014 when spawning `claude -p` from\n a script or Captain-Code subordinate, prepend each prompt with:")),console.log(c.dim(" <!-- claude-recall-alias: T2.1 Run-Prospects -->")),console.log(c.dim(` The watcher will read the header, alias the session, and strip the marker from the
1514
- displayed first user message. See docs/HANDOFF.md \u2192 "Alias header convention".`)),u.length>0&&(console.log(""),console.log(c.warn(` ${u.length} of these groups have NO alias on any row \u2014 strongest candidates
1515
- for the header-alias fix above.`)))}if(console.log(""),console.log(c.dim("\u2014 Tab-name invariant \u2014")),n.length===0)return console.log(c.ok(` \u2713 holds across ${s.length.toLocaleString()} aliased session${s.length===1?"":"s"}`)),console.log(c.dim(" No fabricated origin labels (`VS Code \xB7 cwd \xB7 branch`) found. No deprecated cwd-branch synthesis found.")),r.db.integrity==="ok"&&a.status!=="fail"?0:1;console.log(c.err(`\u2717 ${n.length} invariant violation${n.length===1?"":"s"} found across ${s.length.toLocaleString()} aliased sessions`)),console.log("");let l=new Map;for(let u of n){let p=l.get(u.violation)??[];p.push(u),l.set(u.violation,p)}for(let[u,p]of l){console.log(c.warn(` ${u} (${p.length})`));for(let m of p.slice(0,10))console.log(` ${m.session_id.slice(0,8)} ${c.dim("\u2192")} ${JSON.stringify(m.alias)}`);p.length>10&&console.log(c.dim(` \u2026 and ${p.length-10} more (rerun with --json for the full list)`)),console.log("")}return console.log(c.dim('Remediation: `recall name <id-prefix> ""` clears a bad alias so the heuristic title takes over,\nor `recall name <id-prefix> "<actual tab name>"` sets it to the real value.')),1}v();w();P();import{existsSync as iy,readFileSync as ay}from"node:fs";import{join as cy}from"node:path";function ly(){let e=cy(x,"daemon.pid");if(!iy(e))return!1;try{let t=parseInt(ay(e,"utf-8").trim(),10);return!Number.isFinite(t)||t<=0?!1:(process.kill(t,0),!0)}catch{return!1}}async function cs(e,t){let s=Date.now();try{return t(),{step:e,ok:!0,durationMs:Date.now()-s}}catch(n){return{step:e,ok:!1,durationMs:Date.now()-s,error:n.message}}}async function zl(e={}){let t=_(),s=[];if(e.vacuum&&ly())return e.json?(process.stdout.write(JSON.stringify({ok:!1,error:"daemon-running",message:"VACUUM requires the daemon to be stopped. Run `recall stop`, then re-run with --vacuum."},null,2)+`
1516
- `),2):(console.error(c.err("\u2717 VACUUM requires the daemon to be stopped. Run `recall stop` first, then re-run with --vacuum.")),2);s.push(await cs("wal_checkpoint(TRUNCATE)",()=>{t.pragma("wal_checkpoint(TRUNCATE)")})),s.push(await cs("messages_fts optimize",()=>{t.exec("INSERT INTO messages_fts(messages_fts) VALUES('optimize');")})),s.push(await cs("sessions_fts optimize",()=>{t.exec("INSERT INTO sessions_fts(sessions_fts) VALUES('optimize');")})),s.push(await cs("PRAGMA optimize",()=>{t.exec("PRAGMA optimize")})),e.vacuum&&s.push(await cs("VACUUM",()=>{t.exec("VACUUM")}));let n=s.filter(r=>!r.ok);if(e.json)return process.stdout.write(JSON.stringify({ok:n.length===0,steps:s,vacuum:!!e.vacuum},null,2)+`
1517
- `),n.length===0?0:1;for(let r of s){let o=r.ok?c.ok("\u2713"):c.err("\u2717"),i=`${r.durationMs} ms`;console.log(` ${o} ${r.step.padEnd(28)} ${c.dim(i)}`),r.error&&console.log(` ${c.err(r.error)}`)}return n.length===0?(console.log(""),console.log(c.ok("All maintenance passes completed.")),e.vacuum||console.log(c.dim(" Run `recall optimize --vacuum` (with the daemon stopped) to reclaim disk pages from deleted rows.")),0):(console.log(""),console.log(c.warn(`${n.length} step(s) failed \u2014 review the errors above.`)),1)}v();w();P();import{copyFileSync as dy,existsSync as uy,readFileSync as py}from"node:fs";import{join as ql}from"node:path";function my(){let e=ql(x,"daemon.pid");if(!uy(e))return!1;try{let t=parseInt(py(e,"utf-8").trim(),10);return!Number.isFinite(t)||t<=0?!1:(process.kill(t,0),!0)}catch{return!1}}function gy(){let e=[];for(let t of Nt){let r=t.source.replace(/^\^/,"").replace(/\\d\+/g,"").match(/^([^.*+?{[(\\]+)/)?.[1]??"";r.length>=8&&e.push(r.replace(/'/g,"''"))}return e.length===0?"0":e.map(t=>`first_user_message LIKE '${t}%'`).join(" OR ")}async function Kl(e={}){if(my()&&!e.dryRun){let m="Daemon is running. Run `recall stop`, then `recall purge-phantoms`, then `recall start`. The purge needs an exclusive write lock and the daemon would race it.";return e.json?(process.stdout.write(JSON.stringify({ok:!1,reason:"daemon-running",message:m},null,2)+`
1518
- `),2):(console.error(c.err(`\u2717 ${m}`)),2)}let t=gy();if(t==="0"){let m="No phantom patterns are registered \u2014 nothing to purge.";return e.json?(process.stdout.write(JSON.stringify({ok:!0,reason:"no-patterns",message:m})+`
1519
- `),0):(console.log(c.warn(m)),0)}let s=_(),n=s.prepare(`SELECT COUNT(*) AS n FROM sessions WHERE ${t}`).get().n;if(n===0){let m="No phantom sessions found. Your DB is clean.";return e.json?(process.stdout.write(JSON.stringify({ok:!0,sessionsBefore:0,sessionsAfter:0,messagesDeleted:0,aliasesDeleted:0,patternsApplied:Nt.length,vacuumHint:!1,dryRun:!!e.dryRun},null,2)+`
1520
- `),0):(console.log(c.ok(`\u2713 ${m}`)),0)}let r=s.prepare(`SELECT COUNT(*) AS n FROM messages WHERE session_id IN (SELECT id FROM sessions WHERE ${t})`).get().n,o=s.prepare(`SELECT COUNT(*) AS n FROM session_aliases WHERE session_id IN (SELECT id FROM sessions WHERE ${t})`).get().n;if(e.dryRun){let m={ok:!0,sessionsBefore:n,sessionsAfter:n,messagesDeleted:r,aliasesDeleted:o,patternsApplied:Nt.length,vacuumHint:!1,dryRun:!0};return e.json?(process.stdout.write(JSON.stringify(m,null,2)+`
1521
- `),0):(console.log(c.warn("\u2014 dry run, no changes \u2014")),console.log(` Phantoms found: ${c.dim(String(n))} sessions`),console.log(` Messages cascaded: ${c.dim(String(r))}`),console.log(` Aliases cascaded: ${c.dim(String(o))}`),console.log(c.dim(" Re-run without --dry-run to actually delete.")),0)}let i=ql(x,"db.sqlite"),a=new Date().toISOString().replace(/[-:T]/g,"").replace(/\..+$/,"").slice(0,15),d=`${i}.pre-phantom-purge-${a}`;dy(i,d),s.pragma("foreign_keys = ON"),s.transaction(()=>{s.exec(`DELETE FROM sessions WHERE ${t}`)})();let u=s.prepare(`SELECT COUNT(*) AS n FROM sessions WHERE ${t}`).get().n,p={ok:u===0,backupPath:d,sessionsBefore:n,sessionsAfter:u,messagesDeleted:r,aliasesDeleted:o,patternsApplied:Nt.length,vacuumHint:n>100,dryRun:!1};return e.json?(process.stdout.write(JSON.stringify(p,null,2)+`
1522
- `),p.ok?0:1):(console.log(c.ok(`\u2713 Purged ${n} phantom session(s).`)),console.log(` Cascaded ${r} message(s) and ${o} alias row(s).`),console.log(` Backup: ${c.dim(d)}`),console.log(` Patterns applied: ${c.dim(String(Nt.length))}`),console.log(""),console.log(u===0?c.ok("All known phantom patterns are clear."):c.warn(`${u} phantom row(s) remain after purge \u2014 investigate.`)),p.vacuumHint&&(console.log(""),console.log(c.dim(" Reclaim the disk pages with `recall optimize --vacuum` (daemon must stay stopped)."))),console.log(c.dim(" Restart with `recall start` when ready.")),p.ok?0:1)}v();w();P();w();import{existsSync as fy}from"node:fs";import{join as _y}from"node:path";var cn=_y(x,"archive.sqlite");var Vl=!1;function Zl(){if(Vl&&fy(cn))return;F();let e=_(),t=cn.replace(/'/g,"''");e.exec(`ATTACH DATABASE '${t}' AS archive`);try{e.exec(`
1657
+ WHERE sa.alias IS NOT NULL AND sa.alias != ''`).all(),s=$R(n),r=zR(t.stage);t.stage("Probing daemon");let o=await WR();t.stage("Detecting label collisions");let i=JR();t.stage("Checking ingest freshness");let a=sk();t.stage("Checking ~/.claude.json MCP paths");let d=rk();t.stage("Checking semantic gate drift");let l=ok();t.stage("Sampling chunk_queue growth");let u=Mu();t.stage("Scanning for sibling daemons");let m=qR();t.stage("Checking daemon state files");let p=VR({liveDaemons:m.processes});if(t.done(),e.json){process.stdout.write(JSON.stringify({scanned:n.length,violations:s.length,items:s,health:r,pipeline:o,labelCollisions:i,ingestFreshness:a,staleMcpEntries:d,semanticGateDrift:l,chunkQueueGrowth:u,siblingDaemons:m,daemonStateFiles:p},null,2)),process.stdout.write(`
1658
+ `);let _=o.state==="degraded",b=a.status==="fail",S=d.status==="fail",w=u.status==="critical",y=m.flagged,T=p.flagged,B=l.status==="critical";return s.length===0&&r.db.integrity==="ok"&&!_&&!b&&!S&&!w&&!y&&!B&&!T?0:1}console.log(c.dim("\u2014 System health \u2014")),console.log(` Database ${ce(r.db.sizeBytes)} (${r.rows.messages.toLocaleString()} messages across ${r.rows.sessions.toLocaleString()} sessions, ${r.rows.projects.toLocaleString()} projects)`);{let _=r.wal,b=_.level==="error"?c.err(ce(_.sizeBytes)):_.level==="warn"?c.warn(ce(_.sizeBytes)):c.ok(ce(_.sizeBytes)),S=_.level==="error"?" (readers are pinning the checkpoint frontier \u2014 see warnings below)":_.level==="warn"?" (above 100 MB \u2014 daemon will WARN until it drops)":" (daemon checkpoints PASSIVE every 60s, RESTART above 5 GB)";console.log(` WAL ${b}${S}`)}console.log(` Free pages ${r.db.freelistCount.toLocaleString()} (${ce(r.db.freelistBytes)} reclaimable via VACUUM)`);{let _=r.fts.messages.fragments,b=r.fts.sessions.fragments,S=_===0&&b===0;console.log(` FTS segments messages=${_}, sessions=${b}`+(S?" (merged \u2014 search uses the index)":" (lower is faster \u2014 `recall optimize` merges them)"))}if(r.chunkQueue.size>0){let b=r.chunkQueue.size>1e5?c.warn(r.chunkQueue.size.toLocaleString()):r.chunkQueue.size.toLocaleString();console.log(` Embed queue ${b}`+(r.chunkQueue.semanticEnabled?" (worker is enabled; should drain over time)":" (semantic disabled \u2014 should be 0; if not, schema migration may be stale)"))}if(console.log(` Vector rows ${r.vectors.rows.toLocaleString()}`),r.disk.totalBytes>0){let _=r.disk.freeBytes/r.disk.totalBytes*100,b=r.disk.backups.severity,S=`${_.toFixed(1)}%`,w=b==="high"?c.err(S):b==="medium"?c.warn(S):c.ok(S);console.log(` Disk free ${ce(r.disk.freeBytes)} of ${ce(r.disk.totalBytes)} (${w})`)}if(r.disk.backups.fileCount>0){let _=r.disk.backups,b=ce(_.totalBytes),S=_.severity==="high"?c.err(b):_.severity==="medium"?c.warn(b):b;console.log(` Snapshot files ${S} across ${_.fileCount} file${_.fileCount===1?"":"s"} in ~/.recall/`+(_.oldFiles.length>0?` (${_.oldFiles.length} older than 30 days)`:" (all recent \u2014 likely rollback-critical)"))}if(console.log(` Integrity ${r.db.integrity==="ok"?c.ok("ok"):c.err(r.db.integrity)}`),r.warnings.length>0){console.log("");for(let _ of r.warnings)console.log(` ${c.warn("!")} ${_}`)}console.log(""),QR(m),KR(p),ZR(r.mcpProcesses),console.log(""),ek();let g=tk();if(g!==null&&(console.log(""),console.log(g)),console.log(""),console.log(c.dim("\u2014 Ingest freshness \u2014")),a.status==="ok")console.log(c.ok(` \u2713 ${a.scanned.toLocaleString()} JSONL${a.scanned===1?"":"s"} scanned, none newer than the index`));else if(a.status==="warn"){console.log(` ${c.warn(`${a.staleCount} stale file${a.staleCount===1?"":"s"}`)} newer than the index \u2014 live watcher may be missing events`);for(let _ of a.sampleFiles.slice(0,5))console.log(` ${c.dim("\u2022")} ${_}`);console.log(c.dim(" Run `recall index` to force a reindex, or restart the daemon if persistent."))}else{console.log(` ${c.err(`${a.staleCount} stale file${a.staleCount===1?"":"s"}`)} newer than the index \u2014 significant ingest gap`);for(let _ of a.sampleFiles.slice(0,5))console.log(` ${c.dim("\u2022")} ${_}`);console.log(c.err(" Restart the daemon (recall stop && recall start) \u2014 the live watcher has fallen behind."))}console.log(""),console.log(c.dim("\u2014 chunk_queue growth \u2014"));{let _=u,b=_.status,S=_.currentSize.toLocaleString(),w=_.lastHourGrowth!==null?`\u0394 ${_.lastHourGrowth>=0?"+":""}${_.lastHourGrowth.toLocaleString()}/h`:"no prior sample in last hour",y=_.semanticEnabled?c.dim("semantic=on"):c.dim("semantic=off");b==="ok"?console.log(c.ok(` \u2713 ${S} rows, ${w}`)+` ${y}`+c.dim(` (${_.priorSampleCount} prior sample${_.priorSampleCount===1?"":"s"} in last hour)`)):b==="critical"?(console.log(` ${c.err("CRITICAL")} ${S} rows, ${w} ${y}`),_.remediation&&console.log(c.dim(` ${_.remediation}`))):b==="high"?(console.log(` ${c.warn("HIGH")} ${S} rows, ${w} ${y}`),_.remediation&&console.log(c.dim(` ${_.remediation}`))):(console.log(` ${c.warn("MEDIUM")} ${S} rows, ${w} ${y}`),_.remediation&&console.log(c.dim(` ${_.remediation}`)))}if(console.log(""),console.log(c.dim("\u2014 ~/.claude.json MCP paths \u2014")),!d.configExists)console.log(c.dim(" (no ~/.claude.json on disk \u2014 skipped)"));else if(d.findings.length===0)console.log(c.ok(" \u2713 all MCP server script paths exist on disk"));else{let _=d.findings.filter(S=>S.severity==="HIGH"),b=d.findings.filter(S=>S.severity==="MEDIUM");if(_.length>0){console.log(` ${c.err(`${_.length} stale entry`)}${_.length===1?"":" (HIGH)"} that we own:`);for(let S of _)console.log(` ${c.err("\u2717")} ${S.name} ${c.dim("\u2192")} ${S.stalePath}`),console.log(` ${c.dim("Remediation:")} ${S.remediation}`)}if(b.length>0){console.log(` ${c.warn(`${b.length} third-party stale entr${b.length===1?"y":"ies"}`)} (MEDIUM):`);for(let S of b)console.log(` ${c.warn("!")} ${S.name} ${c.dim("\u2192")} ${S.stalePath}`),console.log(` ${c.dim("Remediation:")} ${S.remediation}`)}}if(console.log(""),console.log(c.dim("\u2014 Semantic gate drift \u2014")),l.status==="ok"?l.configEnabled!==null&&l.dbValue!==null?console.log(c.ok(` \u2713 config.json + DB gate agree (semantic.enabled=${String(l.configEnabled)}, gate=${l.dbValue})`)):console.log(c.dim(" (no gate row to compare \u2014 skipped)")):(console.log(` ${c.err("CRITICAL")} config.json semantic.enabled=${String(l.configEnabled)} but DB gate=${l.dbValue}`),l.message&&console.log(c.dim(` ${l.message}`))),console.log(""),console.log(c.dim("\u2014 Pipeline health (tab-name \u2192 session alias) \u2014")),o.daemon.running?console.log(` Daemon ${c.ok("running")} (port ${o.daemon.port}, version ${o.daemon.version??"?"}, up ${o.daemon.uptimeSeconds!==null?Math.round(o.daemon.uptimeSeconds/60)+" min":"?"})`):console.log(` Daemon ${c.warn("not reachable")}`),o.runtime.silentTerminalRejections!==null){let _=o.runtime.silentTerminalRejections;console.log(` Auth rejections ${_===0?c.ok("0"):c.err(_.toLocaleString())} (extension /api/terminal/* requests denied without a valid X-Recall-Token)`)}if(o.runtime.autoExtract){let _=o.runtime.autoExtract;_.circuitBroken?console.log(` Auto-extract ${c.err("circuit broken")} (${_.reason??"unknown"} \u2014 toggle off/on to reset)`):_.consecutiveZeroTokenRuns>0&&console.log(` Auto-extract ${c.warn(`${_.consecutiveZeroTokenRuns} zero-token run(s)`)} (breaker trips at 3)`)}if(o.runtime.lastTerminalSyncAt!==null){let _=Date.now()-Date.parse(o.runtime.lastTerminalSyncAt),b=Number.isFinite(_)?Math.round(_/6e4):null,S=b!==null&&_>zu;console.log(` Last ext sync ${S?c.warn(`${b} min ago`):c.ok(b===0?"just now":`${b} min ago`)} (most recent successful POST /api/terminal/sync)`)}else o.daemon.running&&console.log(` Last ext sync ${c.warn("never")} (no extension has called /api/terminal/sync since the daemon started)`);if(o.terminalsJson.exists&&o.terminalsJson.ageSeconds!==null){let _=Math.round(o.terminalsJson.ageSeconds/3600),b=o.terminalsJson.ageSeconds>24*3600;console.log(` terminals.json ${b?c.warn(`${_}h old`):c.ok(`${_}h old`)} (persisted registry mtime \u2014 fresh means extensions are connecting)`)}let f=o.recentSessions;if(f.total>0){let _=Math.round(f.fractionHeuristic*100),b=f.fractionHeuristic>=Yu&&f.total>=3;console.log(` Recent titles ${b?c.err(`${_}% heuristic`):c.ok(`${_}% heuristic`)} (${f.heuristicOnly}/${f.total} sessions in last ${Ti}h fell back to first-message title)`)}if(o.flags.length>0){console.log("");for(let _ of o.flags)console.log(` ${c.warn("!")} ${_}`)}if(console.log(""),console.log(c.dim("\u2014 Label collisions (last 7d) \u2014")),i.length===0)console.log(c.ok(" \u2713 no session-list label collisions in the last week"));else{let _=i.filter(b=>!b.anyAliased);console.log(` ${c.warn(`${i.length} group(s)`)} of 2+ sessions in the same project share an identical display label \u2014 N parallel runs ended up with the same heuristic title.`);for(let b of i.slice(0,5)){let S=b.anyAliased?c.dim("partial alias"):c.warn("NO alias"),w=b.label.length>60?`${b.label.slice(0,57)}\u2026`:b.label;console.log(` ${c.dim(`${b.count}\xD7`)} ${w} ${S} ${c.dim(`(${b.project})`)}`)}i.length>5&&console.log(c.dim(` \u2026 and ${i.length-5} more (--json for full list)`)),console.log(""),console.log(c.dim(" The display layer auto-disambiguates these (HH:MM / msgs / UUID suffix), so customers\n never see two identical rows. To fix at the source \u2014 when spawning `claude -p` from\n a script or Captain-Code subordinate, prepend each prompt with:")),console.log(c.dim(" <!-- claude-recall-alias: T2.1 Run-Prospects -->")),console.log(c.dim(` The watcher will read the header, alias the session, and strip the marker from the
1659
+ displayed first user message. See docs/HANDOFF.md \u2192 "Alias header convention".`)),_.length>0&&(console.log(""),console.log(c.warn(` ${_.length} of these groups have NO alias on any row \u2014 strongest candidates
1660
+ for the header-alias fix above.`)))}if(console.log(""),console.log(c.dim("\u2014 Tab-name invariant \u2014")),s.length===0)return console.log(c.ok(` \u2713 holds across ${n.length.toLocaleString()} aliased session${n.length===1?"":"s"}`)),console.log(c.dim(" No fabricated origin labels (`VS Code \xB7 cwd \xB7 branch`) found. No deprecated cwd-branch synthesis found.")),r.db.integrity==="ok"&&a.status!=="fail"&&d.status!=="fail"&&u.status!=="critical"&&!m.flagged&&l.status!=="critical"&&!p.flagged?0:1;console.log(c.err(`\u2717 ${s.length} invariant violation${s.length===1?"":"s"} found across ${n.length.toLocaleString()} aliased sessions`)),console.log("");let h=new Map;for(let _ of s){let b=h.get(_.violation)??[];b.push(_),h.set(_.violation,b)}for(let[_,b]of h){console.log(c.warn(` ${_} (${b.length})`));for(let S of b.slice(0,10))console.log(` ${S.session_id.slice(0,8)} ${c.dim("\u2192")} ${JSON.stringify(S.alias)}`);b.length>10&&console.log(c.dim(` \u2026 and ${b.length-10} more (rerun with --json for the full list)`)),console.log("")}return console.log(c.dim('Remediation: `recall name <id-prefix> ""` clears a bad alias so the heuristic title takes over,\nor `recall name <id-prefix> "<actual tab name>"` sets it to the real value.')),1}$();R();D();import{existsSync as ak,readFileSync as ck}from"node:fs";import{join as lk}from"node:path";function dk(){let e=lk(x,"daemon.pid");if(!ak(e))return!1;try{let t=parseInt(ck(e,"utf-8").trim(),10);return!Number.isFinite(t)||t<=0?!1:(process.kill(t,0),!0)}catch{return!1}}async function Ln(e,t){let n=Date.now();try{return t(),{step:e,ok:!0,durationMs:Date.now()-n}}catch(s){return{step:e,ok:!1,durationMs:Date.now()-n,error:s.message}}}async function Vu(e={}){let t=E(),n=[];if(e.vacuum&&dk())return e.json?(process.stdout.write(JSON.stringify({ok:!1,error:"daemon-running",message:"VACUUM requires the daemon to be stopped. Run `recall stop`, then re-run with --vacuum."},null,2)+`
1661
+ `),2):(console.error(c.err("\u2717 VACUUM requires the daemon to be stopped. Run `recall stop` first, then re-run with --vacuum.")),2);n.push(await Ln("wal_checkpoint(TRUNCATE)",()=>{t.pragma("wal_checkpoint(TRUNCATE)")})),n.push(await Ln("messages_fts optimize",()=>{t.exec("INSERT INTO messages_fts(messages_fts) VALUES('optimize');")})),n.push(await Ln("sessions_fts optimize",()=>{t.exec("INSERT INTO sessions_fts(sessions_fts) VALUES('optimize');")})),n.push(await Ln("PRAGMA optimize",()=>{t.exec("PRAGMA optimize")})),e.vacuum&&n.push(await Ln("VACUUM",()=>{t.exec("VACUUM")}));let s=n.filter(r=>!r.ok);if(e.json)return process.stdout.write(JSON.stringify({ok:s.length===0,steps:n,vacuum:!!e.vacuum},null,2)+`
1662
+ `),s.length===0?0:1;for(let r of n){let o=r.ok?c.ok("\u2713"):c.err("\u2717"),i=`${r.durationMs} ms`;console.log(` ${o} ${r.step.padEnd(28)} ${c.dim(i)}`),r.error&&console.log(` ${c.err(r.error)}`)}return s.length===0?(console.log(""),console.log(c.ok("All maintenance passes completed.")),e.vacuum||console.log(c.dim(" Run `recall optimize --vacuum` (with the daemon stopped) to reclaim disk pages from deleted rows.")),0):(console.log(""),console.log(c.warn(`${s.length} step(s) failed \u2014 review the errors above.`)),1)}$();R();D();import{copyFileSync as uk,existsSync as mk,readFileSync as pk}from"node:fs";import{join as Ku}from"node:path";function gk(){let e=Ku(x,"daemon.pid");if(!mk(e))return!1;try{let t=parseInt(pk(e,"utf-8").trim(),10);return!Number.isFinite(t)||t<=0?!1:(process.kill(t,0),!0)}catch{return!1}}function fk(){let e=[];for(let t of Pt){let r=t.source.replace(/^\^/,"").replace(/\\d\+/g,"").match(/^([^.*+?{[(\\]+)/)?.[1]??"";r.length>=8&&e.push(r.replace(/'/g,"''"))}return e.length===0?"0":e.map(t=>`first_user_message LIKE '${t}%'`).join(" OR ")}async function Qu(e={}){if(gk()&&!e.dryRun){let p="Daemon is running. Run `recall stop`, then `recall purge-phantoms`, then `recall start`. The purge needs an exclusive write lock and the daemon would race it.";return e.json?(process.stdout.write(JSON.stringify({ok:!1,reason:"daemon-running",message:p},null,2)+`
1663
+ `),2):(console.error(c.err(`\u2717 ${p}`)),2)}let t=fk();if(t==="0"){let p="No phantom patterns are registered \u2014 nothing to purge.";return e.json?(process.stdout.write(JSON.stringify({ok:!0,reason:"no-patterns",message:p})+`
1664
+ `),0):(console.log(c.warn(p)),0)}let n=E(),s=n.prepare(`SELECT COUNT(*) AS n FROM sessions WHERE ${t}`).get().n;if(s===0){let p="No phantom sessions found. Your DB is clean.";return e.json?(process.stdout.write(JSON.stringify({ok:!0,sessionsBefore:0,sessionsAfter:0,messagesDeleted:0,aliasesDeleted:0,patternsApplied:Pt.length,vacuumHint:!1,dryRun:!!e.dryRun},null,2)+`
1665
+ `),0):(console.log(c.ok(`\u2713 ${p}`)),0)}let r=n.prepare(`SELECT COUNT(*) AS n FROM messages WHERE session_id IN (SELECT id FROM sessions WHERE ${t})`).get().n,o=n.prepare(`SELECT COUNT(*) AS n FROM session_aliases WHERE session_id IN (SELECT id FROM sessions WHERE ${t})`).get().n;if(e.dryRun){let p={ok:!0,sessionsBefore:s,sessionsAfter:s,messagesDeleted:r,aliasesDeleted:o,patternsApplied:Pt.length,vacuumHint:!1,dryRun:!0};return e.json?(process.stdout.write(JSON.stringify(p,null,2)+`
1666
+ `),0):(console.log(c.warn("\u2014 dry run, no changes \u2014")),console.log(` Phantoms found: ${c.dim(String(s))} sessions`),console.log(` Messages cascaded: ${c.dim(String(r))}`),console.log(` Aliases cascaded: ${c.dim(String(o))}`),console.log(c.dim(" Re-run without --dry-run to actually delete.")),0)}let i=Ku(x,"db.sqlite"),a=new Date().toISOString().replace(/[-:T]/g,"").replace(/\..+$/,"").slice(0,15),d=`${i}.pre-phantom-purge-${a}`;uk(i,d),n.pragma("foreign_keys = ON"),n.transaction(()=>{n.exec(`DELETE FROM sessions WHERE ${t}`)})();let u=n.prepare(`SELECT COUNT(*) AS n FROM sessions WHERE ${t}`).get().n,m={ok:u===0,backupPath:d,sessionsBefore:s,sessionsAfter:u,messagesDeleted:r,aliasesDeleted:o,patternsApplied:Pt.length,vacuumHint:s>100,dryRun:!1};return e.json?(process.stdout.write(JSON.stringify(m,null,2)+`
1667
+ `),m.ok?0:1):(console.log(c.ok(`\u2713 Purged ${s} phantom session(s).`)),console.log(` Cascaded ${r} message(s) and ${o} alias row(s).`),console.log(` Backup: ${c.dim(d)}`),console.log(` Patterns applied: ${c.dim(String(Pt.length))}`),console.log(""),console.log(u===0?c.ok("All known phantom patterns are clear."):c.warn(`${u} phantom row(s) remain after purge \u2014 investigate.`)),m.vacuumHint&&(console.log(""),console.log(c.dim(" Reclaim the disk pages with `recall optimize --vacuum` (daemon must stay stopped)."))),console.log(c.dim(" Restart with `recall start` when ready.")),m.ok?0:1)}$();R();D();R();import{existsSync as _k}from"node:fs";import{join as hk}from"node:path";var Ks=hk(x,"archive.sqlite");var Zu=!1;function em(){if(Zu&&_k(Ks))return;j();let e=E(),t=Ks.replace(/'/g,"''");e.exec(`ATTACH DATABASE '${t}' AS archive`);try{e.exec(`
1523
1668
  CREATE TABLE IF NOT EXISTS archive.messages_archive (
1524
1669
  uuid TEXT PRIMARY KEY,
1525
1670
  session_id TEXT NOT NULL,
@@ -1534,24 +1679,24 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
1534
1679
  archived_at TEXT NOT NULL DEFAULT (datetime('now'))
1535
1680
  );
1536
1681
  CREATE INDEX IF NOT EXISTS archive.idx_messages_archive_session ON messages_archive(session_id);
1537
- `)}finally{e.exec("DETACH DATABASE archive")}Vl=!0}import{existsSync as Ql,mkdirSync as hy,readFileSync as Ey,writeFileSync as by}from"node:fs";import{homedir as Sy}from"node:os";import{join as ed}from"node:path";import{z as ln}from"zod";function td(){return process.env.RECALL_HOME??ed(Sy(),".recall")}function yy(){let e=td();Ql(e)||hy(e,{recursive:!0})}function sd(){return ed(td(),"config.json")}var nd=ln.object({autoArchiveEnabled:ln.boolean().default(!1),autoArchiveAfterDays:ln.number().int().min(7).max(3650).default(90),lastRunAt:ln.string().nullable().default(null)}),dn={autoArchiveEnabled:!1,autoArchiveAfterDays:90,lastRunAt:null};function rd(){let e=sd();if(!Ql(e))return{};try{return JSON.parse(Ey(e,"utf8"))}catch(t){let s=t instanceof Error?t.message:String(t);return console.error(`[retention-config] failed to parse config.json: ${s}`),{}}}function od(){let e=rd().retention;if(!e)return{...dn};let t=nd.safeParse({...dn,...e});return t.success?t.data:{...dn}}function _o(e){yy();let t=rd(),s=nd.parse({...dn,...t.retention??{},...e}),n={...t,retention:s};return by(sd(),JSON.stringify(n,null,2)),s}function ho(e){Zl();let t=_(),s=cn.replace(/'/g,"''");t.exec(`ATTACH DATABASE '${s}' AS archive`);try{return e(t)}finally{t.exec("DETACH DATABASE archive")}}function wy(){return ho(e=>{let t=e.prepare(`SELECT
1682
+ `)}finally{e.exec("DETACH DATABASE archive")}Zu=!0}import{existsSync as tm,mkdirSync as Ek,readFileSync as bk,writeFileSync as Sk}from"node:fs";import{homedir as wk}from"node:os";import{join as nm}from"node:path";import{z as Qs}from"zod";function sm(){return process.env.RECALL_HOME??nm(wk(),".recall")}function yk(){let e=sm();tm(e)||Ek(e,{recursive:!0})}function rm(){return nm(sm(),"config.json")}var om=Qs.object({autoArchiveEnabled:Qs.boolean().default(!1),autoArchiveAfterDays:Qs.number().int().min(7).max(3650).default(90),lastRunAt:Qs.string().nullable().default(null)}),Zs={autoArchiveEnabled:!1,autoArchiveAfterDays:90,lastRunAt:null};function im(){let e=rm();if(!tm(e))return{};try{return JSON.parse(bk(e,"utf8"))}catch(t){let n=t instanceof Error?t.message:String(t);return console.error(`[retention-config] failed to parse config.json: ${n}`),{}}}function am(){let e=im().retention;if(!e)return{...Zs};let t=om.safeParse({...Zs,...e});return t.success?t.data:{...Zs}}function Ri(e){yk();let t=im(),n=om.parse({...Zs,...t.retention??{},...e}),s={...t,retention:n};return Sk(rm(),JSON.stringify(s,null,2)),n}function ki(e){em();let t=E(),n=Ks.replace(/'/g,"''");t.exec(`ATTACH DATABASE '${n}' AS archive`);try{return e(t)}finally{t.exec("DETACH DATABASE archive")}}function Tk(){return ki(e=>{let t=e.prepare(`SELECT
1538
1683
  SUM(CASE WHEN archive_status = 'archived' THEN 1 ELSE 0 END) AS archived,
1539
1684
  SUM(CASE WHEN archive_status != 'archived' THEN 1 ELSE 0 END) AS live
1540
- FROM sessions`).get(),s=e.prepare("SELECT COUNT(*) AS n FROM messages").get().n,n=e.prepare(`SELECT
1685
+ FROM sessions`).get(),n=e.prepare("SELECT COUNT(*) AS n FROM messages").get().n,s=e.prepare(`SELECT
1541
1686
  (SELECT COUNT(*) FROM main.messages_archive) +
1542
1687
  (SELECT COUNT(*) FROM archive.messages_archive) AS n`).get().n,r=e.prepare("SELECT MIN(timestamp) AS t FROM messages WHERE timestamp IS NOT NULL").get(),o=e.prepare(`SELECT MAX(t) AS t FROM (
1543
1688
  SELECT MAX(timestamp) AS t FROM main.messages_archive WHERE timestamp IS NOT NULL
1544
1689
  UNION ALL
1545
1690
  SELECT MAX(timestamp) AS t FROM archive.messages_archive WHERE timestamp IS NOT NULL
1546
- )`).get();return{liveSessions:t.live??0,archivedSessions:t.archived??0,liveMessages:s,archivedMessages:n,oldestLiveTimestamp:r.t,newestArchivedTimestamp:o.t}})}function id(e){return e?e.slice(0,10):"\u2014"}function Ty(){let e=wy();return console.log(c.dim("\u2014 Archive state \u2014")),console.log(` Live sessions ${e.liveSessions.toLocaleString()}`),console.log(` Archived sessions ${e.archivedSessions.toLocaleString()}`),console.log(` Live messages ${e.liveMessages.toLocaleString()}`),console.log(` Archived messages ${e.archivedMessages.toLocaleString()}`),console.log(` Oldest live ${id(e.oldestLiveTimestamp)}`),console.log(` Newest archived ${id(e.newestArchivedTimestamp)}`),console.log(""),console.log(c.dim(" recall archive run --before YYYY-MM-DD")),console.log(c.dim(" recall archive restore <session-id>")),0}function Ry(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e.before))return console.error("--before must be YYYY-MM-DD"),1;let s=_().prepare(`SELECT s.id, s.ended_at, s.message_count
1691
+ )`).get();return{liveSessions:t.live??0,archivedSessions:t.archived??0,liveMessages:n,archivedMessages:s,oldestLiveTimestamp:r.t,newestArchivedTimestamp:o.t}})}function cm(e){return e?e.slice(0,10):"\u2014"}function Rk(){let e=Tk();return console.log(c.dim("\u2014 Archive state \u2014")),console.log(` Live sessions ${e.liveSessions.toLocaleString()}`),console.log(` Archived sessions ${e.archivedSessions.toLocaleString()}`),console.log(` Live messages ${e.liveMessages.toLocaleString()}`),console.log(` Archived messages ${e.archivedMessages.toLocaleString()}`),console.log(` Oldest live ${cm(e.oldestLiveTimestamp)}`),console.log(` Newest archived ${cm(e.newestArchivedTimestamp)}`),console.log(""),console.log(c.dim(" recall archive run --before YYYY-MM-DD")),console.log(c.dim(" recall archive restore <session-id>")),0}function kk(e){if(!/^\d{4}-\d{2}-\d{2}$/.test(e.before))return console.error("--before must be YYYY-MM-DD"),1;let n=E().prepare(`SELECT s.id, s.ended_at, s.message_count
1547
1692
  FROM sessions s
1548
1693
  WHERE s.archive_status != 'archived'
1549
- AND COALESCE(s.ended_at, s.started_at) < ?`).all(`${e.before}T00:00:00.000Z`);if(s.length===0)return console.log(`No sessions to archive (none older than ${e.before}).`),0;let n=s.reduce((a,d)=>a+(d.message_count??0),0);if(console.log(`${s.length.toLocaleString()} session(s), ${n.toLocaleString()} message(s) eligible.`),e.dryRun)return console.log(c.dim("Dry run \u2014 no rows moved. Re-run without --dry-run to apply.")),0;let r=s.map(a=>a.id),o=Date.now(),i=ho(a=>a.transaction(l=>{let u=0,p=a.prepare(`INSERT OR IGNORE INTO archive.messages_archive
1694
+ AND COALESCE(s.ended_at, s.started_at) < ?`).all(`${e.before}T00:00:00.000Z`);if(n.length===0)return console.log(`No sessions to archive (none older than ${e.before}).`),0;let s=n.reduce((a,d)=>a+(d.message_count??0),0);if(console.log(`${n.length.toLocaleString()} session(s), ${s.toLocaleString()} message(s) eligible.`),e.dryRun)return console.log(c.dim("Dry run \u2014 no rows moved. Re-run without --dry-run to apply.")),0;let r=n.map(a=>a.id),o=Date.now(),i=ki(a=>a.transaction(l=>{let u=0,m=a.prepare(`INSERT OR IGNORE INTO archive.messages_archive
1550
1695
  (uuid, session_id, parent_uuid, type, role, timestamp,
1551
1696
  is_sidechain, content_text, tool_names, raw_json, archived_at)
1552
1697
  SELECT uuid, session_id, parent_uuid, type, role, timestamp,
1553
1698
  is_sidechain, content_text, tool_names, raw_json, datetime('now')
1554
- FROM messages WHERE session_id = ?`),m=a.prepare("DELETE FROM messages WHERE session_id = ?"),g=a.prepare("UPDATE sessions SET archive_status = 'archived', archived_at = datetime('now') WHERE id = ?");for(let f of l){p.run(f);let h=m.run(f);u+=Number(h.changes??0),g.run(f)}return u})(r));return console.log(`Archived ${s.length.toLocaleString()} session(s), moved ${i.toLocaleString()} message(s) in ${Date.now()-o}ms.`),console.log(c.dim(" Run `recall optimize --vacuum` (with the daemon stopped) to reclaim disk pages from the moved rows.")),0}function xy(e){if(!e)return console.error("Usage: recall archive restore <session-id>"),1;let s=_().prepare("SELECT id, archive_status FROM sessions WHERE id = ?").get(e);if(!s)return console.error(`Session ${e} not found.`),1;if(s.archive_status!=="archived")return console.error(`Session ${e} is not archived (status=${s.archive_status}).`),1;let n=ho(r=>r.transaction(()=>{let i=r.prepare(`INSERT OR IGNORE INTO messages
1699
+ FROM messages WHERE session_id = ?`),p=a.prepare("DELETE FROM messages WHERE session_id = ?"),g=a.prepare("UPDATE sessions SET archive_status = 'archived', archived_at = datetime('now') WHERE id = ?");for(let f of l){m.run(f);let h=p.run(f);u+=Number(h.changes??0),g.run(f)}return u})(r));return console.log(`Archived ${n.length.toLocaleString()} session(s), moved ${i.toLocaleString()} message(s) in ${Date.now()-o}ms.`),console.log(c.dim(" Run `recall optimize --vacuum` (with the daemon stopped) to reclaim disk pages from the moved rows.")),0}function xk(e){if(!e)return console.error("Usage: recall archive restore <session-id>"),1;let n=E().prepare("SELECT id, archive_status FROM sessions WHERE id = ?").get(e);if(!n)return console.error(`Session ${e} not found.`),1;if(n.archive_status!=="archived")return console.error(`Session ${e} is not archived (status=${n.archive_status}).`),1;let s=ki(r=>r.transaction(()=>{let i=r.prepare(`INSERT OR IGNORE INTO messages
1555
1700
  (uuid, session_id, parent_uuid, type, role, timestamp,
1556
1701
  is_sidechain, content_text, tool_names, raw_json)
1557
1702
  SELECT uuid, session_id, parent_uuid, type, role, timestamp,
@@ -1561,33 +1706,33 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
1561
1706
  is_sidechain, content_text, tool_names, raw_json)
1562
1707
  SELECT uuid, session_id, parent_uuid, type, role, timestamp,
1563
1708
  is_sidechain, content_text, tool_names, raw_json
1564
- FROM archive.messages_archive WHERE session_id = ?`),d=r.prepare("DELETE FROM main.messages_archive WHERE session_id = ?"),l=r.prepare("DELETE FROM archive.messages_archive WHERE session_id = ?"),u=Number(i.run(e).changes??0),p=Number(a.run(e).changes??0);return d.run(e),l.run(e),r.prepare("UPDATE sessions SET archive_status = 'live', archived_at = NULL WHERE id = ?").run(e),u+p})());return console.log(`Restored ${n.toLocaleString()} message(s) for session ${e}.`),0}function ky(){let e=od();return console.log(c.dim("\u2014 Auto-archive \u2014")),console.log(` Enabled ${e.autoArchiveEnabled?c.ok("YES"):"no"}`),console.log(` After ${e.autoArchiveAfterDays} days`),console.log(` Last run ${e.lastRunAt??"\u2014"}`),0}function Cy(e){if(e===void 0||!Number.isFinite(e))return console.error("Usage: recall archive auto on --after <days>"),1;let t=Math.floor(e);if(t<7||t>3650)return console.error("--after must be between 7 and 3650 days"),1;let s=_o({autoArchiveEnabled:!0,autoArchiveAfterDays:t});return console.log(`Auto-archive: ENABLED (after ${s.autoArchiveAfterDays} days).`),console.log(c.dim(" The daemon will run a daily archive pass on the next tick.")),0}function Ly(){return _o({autoArchiveEnabled:!1}),console.log("Auto-archive: DISABLED. Existing archived sessions stay archived."),0}async function ad(e){let t=e._action??"list";if(t==="list"||t==="stats")return Ty();if(t==="run")return e.before?Ry({before:e.before,dryRun:e.dryRun===!0}):(console.error("Usage: recall archive run --before YYYY-MM-DD [--dry-run]"),1);if(t==="restore")return xy(e._sessionId??"");if(t==="auto"){let s=e._subAction??"status";return s==="status"?ky():s==="on"||s==="enable"?Cy(e.after):s==="off"||s==="disable"?Ly():(console.error("Usage: recall archive auto <status|on|off> [--after <days>]"),1)}return console.error(`Usage: recall archive <list|run|restore|auto> [args]
1709
+ FROM archive.messages_archive WHERE session_id = ?`),d=r.prepare("DELETE FROM main.messages_archive WHERE session_id = ?"),l=r.prepare("DELETE FROM archive.messages_archive WHERE session_id = ?"),u=Number(i.run(e).changes??0),m=Number(a.run(e).changes??0);return d.run(e),l.run(e),r.prepare("UPDATE sessions SET archive_status = 'live', archived_at = NULL WHERE id = ?").run(e),u+m})());return console.log(`Restored ${s.toLocaleString()} message(s) for session ${e}.`),0}function Ck(){let e=am();return console.log(c.dim("\u2014 Auto-archive \u2014")),console.log(` Enabled ${e.autoArchiveEnabled?c.ok("YES"):"no"}`),console.log(` After ${e.autoArchiveAfterDays} days`),console.log(` Last run ${e.lastRunAt??"\u2014"}`),0}function Ak(e){if(e===void 0||!Number.isFinite(e))return console.error("Usage: recall archive auto on --after <days>"),1;let t=Math.floor(e);if(t<7||t>3650)return console.error("--after must be between 7 and 3650 days"),1;let n=Ri({autoArchiveEnabled:!0,autoArchiveAfterDays:t});return console.log(`Auto-archive: ENABLED (after ${n.autoArchiveAfterDays} days).`),console.log(c.dim(" The daemon will run a daily archive pass on the next tick.")),0}function Lk(){return Ri({autoArchiveEnabled:!1}),console.log("Auto-archive: DISABLED. Existing archived sessions stay archived."),0}async function lm(e){let t=e._action??"list";if(t==="list"||t==="stats")return Rk();if(t==="run")return e.before?kk({before:e.before,dryRun:e.dryRun===!0}):(console.error("Usage: recall archive run --before YYYY-MM-DD [--dry-run]"),1);if(t==="restore")return xk(e._sessionId??"");if(t==="auto"){let n=e._subAction??"status";return n==="status"?Ck():n==="on"||n==="enable"?Ak(e.after):n==="off"||n==="disable"?Lk():(console.error("Usage: recall archive auto <status|on|off> [--after <days>]"),1)}return console.error(`Usage: recall archive <list|run|restore|auto> [args]
1565
1710
  list \u2014 show archive counts
1566
1711
  run --before YYYY-MM-DD [--dry-run] \u2014 move sessions older than DATE
1567
1712
  restore <session-id> \u2014 pull a session back from archive
1568
- auto <status|on|off> [--after N] \u2014 daemon auto-archives sessions older than N days`),1}v();w();P();import{existsSync as Ny,readFileSync as Ay}from"node:fs";function Oy(){let e=`${x}/daemon.port`;if(!Ny(e))return null;try{let t=Ay(e,"utf8").trim();return/^\d+$/.test(t)?t:null}catch{return null}}function vy(e){let t=_();if(e.length>=32){let n=t.prepare("SELECT id FROM sessions WHERE id = ?").get(e);return n?n.id:(process.stderr.write(`session not found: ${e}
1569
- `),null)}let s=t.prepare("SELECT id FROM sessions WHERE id LIKE ? LIMIT 2").all(e+"%");return s.length===1?s[0].id:s.length===0?(process.stderr.write(`no session matches prefix "${e}"
1713
+ auto <status|on|off> [--after N] \u2014 daemon auto-archives sessions older than N days`),1}$();R();D();import{existsSync as Nk,readFileSync as Ok}from"node:fs";function vk(){let e=`${x}/daemon.port`;if(!Nk(e))return null;try{let t=Ok(e,"utf8").trim();return/^\d+$/.test(t)?t:null}catch{return null}}function Ik(e){let t=E();if(e.length>=32){let s=t.prepare("SELECT id FROM sessions WHERE id = ?").get(e);return s?s.id:(process.stderr.write(`session not found: ${e}
1714
+ `),null)}let n=t.prepare("SELECT id FROM sessions WHERE id LIKE ? LIMIT 2").all(e+"%");return n.length===1?n[0].id:n.length===0?(process.stderr.write(`no session matches prefix "${e}"
1570
1715
  `),null):(process.stderr.write(`ambiguous session prefix "${e}". be more specific.
1571
- `),null)}async function cd(e,t,s){let n=vy(e);if(!n){process.exitCode=1;return}let r=t.trim();if(!r){process.stderr.write(`name cannot be empty
1572
- `),process.exitCode=1;return}let o=Oy();if(!o){process.stderr.write("daemon is not running. start it with `recall start` so the alias write is durable.\n"),process.exitCode=1;return}let i;try{i=await st("PUT",`http://127.0.0.1:${o}/api/sessions/${encodeURIComponent(n)}/alias`,{alias:r,pin:s.pin===!0})}catch(d){process.stderr.write(`failed to reach daemon at 127.0.0.1:${o}: ${d.message}
1716
+ `),null)}async function dm(e,t,n){let s=Ik(e);if(!s){process.exitCode=1;return}let r=t.trim();if(!r){process.stderr.write(`name cannot be empty
1717
+ `),process.exitCode=1;return}let o=vk();if(!o){process.stderr.write("daemon is not running. start it with `recall start` so the alias write is durable.\n"),process.exitCode=1;return}let i;try{i=await dt("PUT",`http://127.0.0.1:${o}/api/sessions/${encodeURIComponent(s)}/alias`,{alias:r,pin:n.pin===!0})}catch(d){process.stderr.write(`failed to reach daemon at 127.0.0.1:${o}: ${d.message}
1573
1718
  `),process.exitCode=1;return}if(!i.ok){let d="";try{d=await i.text()}catch{}process.stderr.write(`daemon rejected alias write (HTTP ${i.status}): ${d}
1574
- `),process.exitCode=1;return}let a={session_id:n,alias:r};if(s.json){console.log(JSON.stringify(a));return}console.log(`${c.ok("renamed")} ${c.dim(n.slice(0,8))} \u2192 ${c.bold(`"${r}"`)}`)}v();w();var ud=90;async function Iy(){try{let e=`${process.env.HOME}/.recall/daemon.port`,t=await import("node:fs");if(!t.existsSync(e))return null;let s=t.readFileSync(e,"utf8").trim(),n=await ze(`http://127.0.0.1:${s}/api/terminal/registry`);return n.ok?await n.json():null}catch{return null}}function My(){let e=Date.now()/1e3-ud;return _().prepare(`SELECT s.id, s.cwd, s.file_mtime, NULLIF(sa.alias, '') AS alias
1719
+ `),process.exitCode=1;return}let a={session_id:s,alias:r};if(n.json){console.log(JSON.stringify(a));return}console.log(`${c.ok("renamed")} ${c.dim(s.slice(0,8))} \u2192 ${c.bold(`"${r}"`)}`)}$();R();var pm=90;async function Mk(){try{let e=`${process.env.HOME}/.recall/daemon.port`,t=await import("node:fs");if(!t.existsSync(e))return null;let n=t.readFileSync(e,"utf8").trim(),s=await nt(`http://127.0.0.1:${n}/api/terminal/registry`);return s.ok?await s.json():null}catch{return null}}function Dk(){let e=Date.now()/1e3-pm;return E().prepare(`SELECT s.id, s.cwd, s.file_mtime, NULLIF(sa.alias, '') AS alias
1575
1720
  FROM sessions s
1576
1721
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1577
1722
  WHERE s.cwd IS NOT NULL AND s.file_mtime > ?
1578
- ORDER BY s.cwd, s.file_mtime DESC`).all(e)}var Dy=new Set(["zsh","bash","fish","sh","dash","ksh","tcsh","csh","pwsh","powershell","cmd","nu"]);function ld(e){let t=e.trim().toLowerCase().replace(/^[-/]+/,"").replace(/^.*\//,"").replace(/\s*\(\d+\)\s*$/,"").trim();return!t||Dy.has(t)}var $y=/^[⠀-⣿✳\s]+/,Py=/^\d+(\.\d+){1,3}$/;function dd(e){let t=e.trim();return!!(!t||$y.test(t)||Py.test(t))}async function pd(e){let t=await Iy(),s=My();if(e.json){console.log(JSON.stringify({registry:t,active:s},null,2));return}if(console.log(),console.log(c.bold("TERMINAL REGISTRY")),console.log(c.dim("(what the daemon thinks each VS Code terminal is named)")),console.log(),!t){console.log(c.err(" Could not reach daemon. Is recall start running?"));return}let n=new Map;for(let o of t.terminals){let i=o.cwd??"(no cwd)",a=n.get(i);a||(a=[],n.set(i,a)),a.push(o)}for(let[o,i]of n){console.log(c.dim(` cwd: ${o}`));for(let a of i){let d=ld(a.tab_name),l=dd(a.tab_name),p=!d&&!l?c.ok("[usable]"):d?c.warn("[generic-shell]"):c.warn("[claude-auto]");console.log(` ${p} pid ${a.shell_pid} ${c.bold(`"${a.tab_name}"`)}`)}console.log()}if(console.log(c.bold(`ACTIVE SESSIONS (mtime within last ${ud}s)`)),console.log(c.dim("(sessions whose JSONL is being actively written)")),console.log(),s.length===0){console.log(c.dim(" (none)")),console.log();return}let r=new Map;for(let o of s){let i=r.get(o.cwd);i||(i=[],r.set(o.cwd,i)),i.push(o)}for(let[o,i]of r){console.log(c.dim(` cwd: ${o}`));for(let a of i)console.log(` ${c.dim(a.id.slice(0,8))} ${c.bold(a.alias??"(no alias)")} ${c.dim(`mtime ${new Date(a.file_mtime*1e3).toISOString().slice(11,19)}`)}`);console.log()}console.log(c.bold("LAYER 5 SWEEP DECISIONS")),console.log(c.dim("(what the live correlator would do for each cwd right now)")),console.log();for(let[o,i]of r){let d=(n.get(o)??[]).filter(l=>!ld(l.tab_name)&&!dd(l.tab_name));d.length===0?console.log(` ${c.warn("skip")} ${c.dim(o)} ${c.dim("\u2014 no usable terminal name (all generic or claude-auto)")}`):d.length>1?console.log(` ${c.warn("refuse")} ${c.dim(o)} ${c.dim(`\u2014 ${d.length} usable terminals, ambiguous`)}`):i.length>1?console.log(` ${c.warn("refuse")} ${c.dim(o)} ${c.dim(`\u2014 ${i.length} active sessions, ambiguous`)}`):console.log(` ${c.ok("link")} ${c.dim(o)} ${c.dim("\u2192")} ${c.dim(i[0].id.slice(0,8))} ${c.dim("=")} ${c.bold(`"${d[0].tab_name}"`)}`)}console.log()}v();w();P();import{existsSync as Fy,readFileSync as jy}from"node:fs";function Uy(){let e=`${x}/daemon.port`;if(!Fy(e))return null;try{let t=jy(e,"utf8").trim();return/^\d+$/.test(t)?t:null}catch{return null}}async function By(e){try{let t=await ze(`http://127.0.0.1:${e}/api/terminal/registry`);return t.ok?(await t.json()).terminals??[]:[]}catch{return[]}}async function Hy(e,t,s){try{return(await st("POST",`http://127.0.0.1:${e}/api/sessions/${encodeURIComponent(t)}/relink`,{shell_pid:s})).ok}catch{return!1}}function Wy(e){try{let t=JSON.parse(e);if(!Array.isArray(t))return null;for(let s=t.length-1;s>=0;s--){let n=t[s];if(n&&typeof n.alias=="string"&&n.alias!=="")return n.alias}return null}catch{return null}}function Xy(e){let t=new Map;for(let i of e){if(!i.cwd)continue;let a=i.cwd.replace(/\/+$/,""),d=t.get(a);d||(d=new Map,t.set(a,d)),d.set(i.tab_name,i.shell_pid)}let s=new Map;for(let i of e){if(!i.cwd)continue;let d=`${i.cwd.replace(/\/+$/,"")}::${i.tab_name}`,l=s.get(d);l||(l=new Set,s.set(d,l)),l.add(String(i.shell_pid))}let r=_().prepare(`SELECT sa.session_id, s.cwd, sa.previous_aliases
1723
+ ORDER BY s.cwd, s.file_mtime DESC`).all(e)}var $k=new Set(["zsh","bash","fish","sh","dash","ksh","tcsh","csh","pwsh","powershell","cmd","nu"]);function um(e){let t=e.trim().toLowerCase().replace(/^[-/]+/,"").replace(/^.*\//,"").replace(/\s*\(\d+\)\s*$/,"").trim();return!t||$k.has(t)}var Pk=/^[⠀-⣿✳\s]+/,Fk=/^\d+(\.\d+){1,3}$/;function mm(e){let t=e.trim();return!!(!t||Pk.test(t)||Fk.test(t))}async function gm(e){let t=await Mk(),n=Dk();if(e.json){console.log(JSON.stringify({registry:t,active:n},null,2));return}if(console.log(),console.log(c.bold("TERMINAL REGISTRY")),console.log(c.dim("(what the daemon thinks each VS Code terminal is named)")),console.log(),!t){console.log(c.err(" Could not reach daemon. Is recall start running?"));return}let s=new Map;for(let o of t.terminals){let i=o.cwd??"(no cwd)",a=s.get(i);a||(a=[],s.set(i,a)),a.push(o)}for(let[o,i]of s){console.log(c.dim(` cwd: ${o}`));for(let a of i){let d=um(a.tab_name),l=mm(a.tab_name),m=!d&&!l?c.ok("[usable]"):d?c.warn("[generic-shell]"):c.warn("[claude-auto]");console.log(` ${m} pid ${a.shell_pid} ${c.bold(`"${a.tab_name}"`)}`)}console.log()}if(console.log(c.bold(`ACTIVE SESSIONS (mtime within last ${pm}s)`)),console.log(c.dim("(sessions whose JSONL is being actively written)")),console.log(),n.length===0){console.log(c.dim(" (none)")),console.log();return}let r=new Map;for(let o of n){let i=r.get(o.cwd);i||(i=[],r.set(o.cwd,i)),i.push(o)}for(let[o,i]of r){console.log(c.dim(` cwd: ${o}`));for(let a of i)console.log(` ${c.dim(a.id.slice(0,8))} ${c.bold(a.alias??"(no alias)")} ${c.dim(`mtime ${new Date(a.file_mtime*1e3).toISOString().slice(11,19)}`)}`);console.log()}console.log(c.bold("LAYER 5 SWEEP DECISIONS")),console.log(c.dim("(what the live correlator would do for each cwd right now)")),console.log();for(let[o,i]of r){let d=(s.get(o)??[]).filter(l=>!um(l.tab_name)&&!mm(l.tab_name));d.length===0?console.log(` ${c.warn("skip")} ${c.dim(o)} ${c.dim("\u2014 no usable terminal name (all generic or claude-auto)")}`):d.length>1?console.log(` ${c.warn("refuse")} ${c.dim(o)} ${c.dim(`\u2014 ${d.length} usable terminals, ambiguous`)}`):i.length>1?console.log(` ${c.warn("refuse")} ${c.dim(o)} ${c.dim(`\u2014 ${i.length} active sessions, ambiguous`)}`):console.log(` ${c.ok("link")} ${c.dim(o)} ${c.dim("\u2192")} ${c.dim(i[0].id.slice(0,8))} ${c.dim("=")} ${c.bold(`"${d[0].tab_name}"`)}`)}console.log()}$();R();D();import{existsSync as jk,readFileSync as Uk}from"node:fs";function Bk(){let e=`${x}/daemon.port`;if(!jk(e))return null;try{let t=Uk(e,"utf8").trim();return/^\d+$/.test(t)?t:null}catch{return null}}async function Hk(e){try{let t=await nt(`http://127.0.0.1:${e}/api/terminal/registry`);return t.ok?(await t.json()).terminals??[]:[]}catch{return[]}}async function Wk(e,t,n){try{return(await dt("POST",`http://127.0.0.1:${e}/api/sessions/${encodeURIComponent(t)}/relink`,{shell_pid:n})).ok}catch{return!1}}function Xk(e){try{let t=JSON.parse(e);if(!Array.isArray(t))return null;for(let n=t.length-1;n>=0;n--){let s=t[n];if(s&&typeof s.alias=="string"&&s.alias!=="")return s.alias}return null}catch{return null}}function Gk(e){let t=new Map;for(let i of e){if(!i.cwd)continue;let a=i.cwd.replace(/\/+$/,""),d=t.get(a);d||(d=new Map,t.set(a,d)),d.set(i.tab_name,i.shell_pid)}let n=new Map;for(let i of e){if(!i.cwd)continue;let d=`${i.cwd.replace(/\/+$/,"")}::${i.tab_name}`,l=n.get(d);l||(l=new Set,n.set(d,l)),l.add(String(i.shell_pid))}let r=E().prepare(`SELECT sa.session_id, s.cwd, sa.previous_aliases
1579
1724
  FROM session_aliases sa
1580
1725
  JOIN sessions s ON s.id = sa.session_id
1581
- WHERE sa.alias = '' AND sa.previous_aliases != '[]' AND s.cwd IS NOT NULL`).all(),o=[];for(let i of r){let a=Wy(i.previous_aliases);if(!a)continue;let d=i.cwd.replace(/\/+$/,""),u=t.get(d)?.get(a);if(!u)continue;let p=s.get(`${d}::${a}`);p&&p.size>1||o.push({session_id:i.session_id,cwd:d,alias_to_restore:a,matching_pid:u})}return o}async function md(e){let t=Uy();t||(console.error(c.err("Daemon is not running. Run `recall start` first.")),process.exit(1));let s=await By(t);s.length===0&&(console.error(c.err("Registry is empty. Open VS Code with the extension to populate it.")),process.exit(1));let n=Xy(s);if(e.json){console.log(JSON.stringify({candidates:n},null,2));return}if(n.length===0){console.log(c.dim("No restorable sessions found.")),console.log(c.dim("A session is restorable when its previous alias still matches a currently-open terminal in the same cwd, with no name collisions."));return}console.log(c.bold(`Found ${n.length} session${n.length===1?"":"s"} that can be cleanly restored:`)),console.log();let r=new Map;for(let a of n){let d=r.get(a.cwd);d||(d=[],r.set(a.cwd,d)),d.push(a)}for(let[a,d]of r){console.log(c.dim(` cwd: ${a}`));for(let l of d)console.log(` ${c.dim(l.session_id.slice(0,8))} ${c.dim("\u2192 pid")} ${l.matching_pid} ${c.dim("=")} ${c.bold(`"${l.alias_to_restore}"`)}`);console.log()}if(!e.apply){console.log(c.dim("Re-run with --apply to restore these aliases."));return}let o=0,i=0;for(let a of n)await Hy(t,a.session_id,a.matching_pid)?o++:(i++,console.error(c.err(`failed to restore ${a.session_id.slice(0,8)}`)));console.log(c.ok(`Restored ${o} alias${o===1?"":"es"}.${i>0?` ${i} failed.`:""}`)),console.log(c.dim("Sessions whose previous alias did not cleanly match a current terminal still need manual relink via the \u{1F517} picker."))}v();async function gd(e,t){let s=e.trim();if(!/^[0-9a-fA-F]{4,40}$/.test(s)){console.error(c.err(`not a valid commit SHA: '${e}'`)),process.exit(1);return}let n=an(s);if(t.json){console.log(JSON.stringify(n,null,2));return}if(n.length===0){console.log(""),console.log(c.dim(`no correlated sessions for ${s}`)),console.log(c.dim(" (if you haven't yet, run `recall correlate` to backfill)")),console.log("");return}console.log(""),console.log(`${c.bold("commit")} ${c.accent(n[0].commitSha.slice(0,12))} ${c.dim(n[0].committedAt??"")}`),n[0].subject&&console.log(` ${n[0].subject}`),console.log(""),console.log(c.dim(`authored during ${n.length} session${n.length===1?"":"s"}:`));for(let r of n){let o=r.alias??r.sessionId.slice(0,8);console.log(` ${c.accent(o.padEnd(24))} ${c.dim((r.project??"").slice(0,24).padEnd(26))} ${c.dim(r.startedAt??"")}`),console.log(` ${c.dim(`recall show ${r.sessionId}`)}`)}console.log("")}v();w();import{execFile as Jy}from"node:child_process";import{promisify as Gy}from"node:util";import{stat as Yy}from"node:fs/promises";var zy=Gy(Jy),qy=60,Ky=7,Vy=7,Zy=5e3;function Qy(){let e=_(),t=s=>{try{return!!e.prepare(s).get()}catch{return!1}};return{semantic:t("SELECT 1 FROM session_semantic LIMIT 1"),cost:t(`SELECT 1 FROM sessions
1726
+ WHERE sa.alias = '' AND sa.previous_aliases != '[]' AND s.cwd IS NOT NULL`).all(),o=[];for(let i of r){let a=Xk(i.previous_aliases);if(!a)continue;let d=i.cwd.replace(/\/+$/,""),u=t.get(d)?.get(a);if(!u)continue;let m=n.get(`${d}::${a}`);m&&m.size>1||o.push({session_id:i.session_id,cwd:d,alias_to_restore:a,matching_pid:u})}return o}async function fm(e){let t=Bk();t||(console.error(c.err("Daemon is not running. Run `recall start` first.")),process.exit(1));let n=await Hk(t);n.length===0&&(console.error(c.err("Registry is empty. Open VS Code with the extension to populate it.")),process.exit(1));let s=Gk(n);if(e.json){console.log(JSON.stringify({candidates:s},null,2));return}if(s.length===0){console.log(c.dim("No restorable sessions found.")),console.log(c.dim("A session is restorable when its previous alias still matches a currently-open terminal in the same cwd, with no name collisions."));return}console.log(c.bold(`Found ${s.length} session${s.length===1?"":"s"} that can be cleanly restored:`)),console.log();let r=new Map;for(let a of s){let d=r.get(a.cwd);d||(d=[],r.set(a.cwd,d)),d.push(a)}for(let[a,d]of r){console.log(c.dim(` cwd: ${a}`));for(let l of d)console.log(` ${c.dim(l.session_id.slice(0,8))} ${c.dim("\u2192 pid")} ${l.matching_pid} ${c.dim("=")} ${c.bold(`"${l.alias_to_restore}"`)}`);console.log()}if(!e.apply){console.log(c.dim("Re-run with --apply to restore these aliases."));return}let o=0,i=0;for(let a of s)await Wk(t,a.session_id,a.matching_pid)?o++:(i++,console.error(c.err(`failed to restore ${a.session_id.slice(0,8)}`)));console.log(c.ok(`Restored ${o} alias${o===1?"":"es"}.${i>0?` ${i} failed.`:""}`)),console.log(c.dim("Sessions whose previous alias did not cleanly match a current terminal still need manual relink via the \u{1F517} picker."))}$();async function _m(e,t){let n=e.trim();if(!/^[0-9a-fA-F]{4,40}$/.test(n)){console.error(c.err(`not a valid commit SHA: '${e}'`)),process.exit(1);return}let s=zs(n);if(t.json){console.log(JSON.stringify(s,null,2));return}if(s.length===0){console.log(""),console.log(c.dim(`no correlated sessions for ${n}`)),console.log(c.dim(" (if you haven't yet, run `recall correlate` to backfill)")),console.log("");return}console.log(""),console.log(`${c.bold("commit")} ${c.accent(s[0].commitSha.slice(0,12))} ${c.dim(s[0].committedAt??"")}`),s[0].subject&&console.log(` ${s[0].subject}`),console.log(""),console.log(c.dim(`authored during ${s.length} session${s.length===1?"":"s"}:`));for(let r of s){let o=r.alias??r.sessionId.slice(0,8);console.log(` ${c.accent(o.padEnd(24))} ${c.dim((r.project??"").slice(0,24).padEnd(26))} ${c.dim(r.startedAt??"")}`),console.log(` ${c.dim(`recall show ${r.sessionId}`)}`)}console.log("")}$();R();import{execFile as Jk}from"node:child_process";import{promisify as zk}from"node:util";import{stat as Yk}from"node:fs/promises";var qk=zk(Jk),Vk=60,Kk=7,Qk=7,Zk=5e3;function e0(){let e=E(),t=n=>{try{return!!e.prepare(n).get()}catch{return!1}};return{semantic:t("SELECT 1 FROM session_semantic LIMIT 1"),cost:t(`SELECT 1 FROM sessions
1582
1727
  WHERE (COALESCE(total_input_tokens,0)
1583
1728
  + COALESCE(total_output_tokens,0)
1584
1729
  + COALESCE(total_cache_create_tokens,0)
1585
1730
  + COALESCE(total_cache_read_tokens,0)) > 0
1586
- LIMIT 1`),git:t("SELECT 1 FROM session_commits LIMIT 1")}}function Eo(e){if(!e)return[];let t=new Set,s=[];for(let n of e.split(",")){let r=n.trim().toLowerCase();!r||t.has(r)||(t.add(r),s.push(r))}return s}function fd(e){return{sessionId:e.session_id,project:e.project,alias:e.alias,startedAt:e.started_at,endedAt:e.ended_at,firstUserMessage:e.first_user_message}}function ew(){let e=_(),t=e.prepare(`SELECT ss.keywords
1731
+ LIMIT 1`),git:t("SELECT 1 FROM session_commits LIMIT 1")}}function xi(e){if(!e)return[];let t=new Set,n=[];for(let s of e.split(",")){let r=s.trim().toLowerCase();!r||t.has(r)||(t.add(r),n.push(r))}return n}function hm(e){return{sessionId:e.session_id,project:e.project,alias:e.alias,startedAt:e.started_at,endedAt:e.ended_at,firstUserMessage:e.first_user_message}}function t0(){let e=E(),t=e.prepare(`SELECT ss.keywords
1587
1732
  FROM session_semantic ss
1588
1733
  JOIN sessions s ON s.id = ss.session_id
1589
1734
  WHERE s.started_at IS NOT NULL
1590
- AND julianday('now') - julianday(s.started_at) <= @windowDays`).all({windowDays:Ky});if(t.length===0)return null;let s=new Set;for(let o of t)for(let i of Eo(o.keywords))s.add(i);if(s.size===0)return null;let n=e.prepare(`SELECT ss.session_id AS session_id,
1735
+ AND julianday('now') - julianday(s.started_at) <= @windowDays`).all({windowDays:Kk});if(t.length===0)return null;let n=new Set;for(let o of t)for(let i of xi(o.keywords))n.add(i);if(n.size===0)return null;let s=e.prepare(`SELECT ss.session_id AS session_id,
1591
1736
  ss.summary AS summary,
1592
1737
  ss.keywords AS keywords,
1593
1738
  p.name AS project,
@@ -1603,7 +1748,7 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
1603
1748
  WHERE s.started_at IS NOT NULL
1604
1749
  AND s.message_count > 2
1605
1750
  AND julianday('now') - julianday(s.started_at) >= @ageDays
1606
- ORDER BY s.started_at ASC`).all({ageDays:qy});if(n.length===0)return null;let r=null;for(let o of n){let a=Eo(o.keywords).filter(d=>s.has(d));a.length!==0&&(!r||a.length>r.overlap.length)&&(r={row:o,overlap:a})}return r?{...fd(r.row),summary:r.row.summary,keywords:Eo(r.row.keywords),matchedKeywords:r.overlap,daysAgo:Math.max(0,Math.round(r.row.days_old))}:null}function tw(){let t=_().prepare(`SELECT s.id AS session_id,
1751
+ ORDER BY s.started_at ASC`).all({ageDays:Vk});if(s.length===0)return null;let r=null;for(let o of s){let a=xi(o.keywords).filter(d=>n.has(d));a.length!==0&&(!r||a.length>r.overlap.length)&&(r={row:o,overlap:a})}return r?{...hm(r.row),summary:r.row.summary,keywords:xi(r.row.keywords),matchedKeywords:r.overlap,daysAgo:Math.max(0,Math.round(r.row.days_old))}:null}function n0(){let t=E().prepare(`SELECT s.id AS session_id,
1607
1752
  p.name AS project,
1608
1753
  NULLIF(sa.alias, '') AS alias,
1609
1754
  s.started_at AS started_at,
@@ -1622,117 +1767,118 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
1622
1767
  AND (COALESCE(s.total_input_tokens, 0)
1623
1768
  + COALESCE(s.total_output_tokens, 0)
1624
1769
  + COALESCE(s.total_cache_create_tokens, 0)
1625
- + COALESCE(s.total_cache_read_tokens, 0)) > 0`).all({windowDays:Vy});if(t.length===0)return null;let s=null;for(let n of t){let r=Ie({inputTokens:n.input_tokens,outputTokens:n.output_tokens,cacheCreateTokens:n.cache_create_tokens,cacheReadTokens:n.cache_read_tokens},n.primary_model);r.cents<=0||(!s||r.cents>s.cents)&&(s={row:n,cents:r.cents,totalTokens:r.totalTokens})}return s?{...fd(s.row),totalTokens:s.totalTokens,costCents:s.cents,costDisplay:re(s.cents),tokensDisplay:ue(s.totalTokens),primaryModel:s.row.primary_model,primaryModelLabel:it(s.row.primary_model).label}:null}async function sw(e){try{if(!(await Yy(e)).isDirectory())return null}catch{return null}try{let{stdout:t}=await zy("git",["rev-parse","HEAD"],{cwd:e,timeout:Zy}),s=t.trim();return/^[0-9a-f]{40}$/.test(s)?s:null}catch{return null}}async function nw(){let e=_(),t=e.prepare(`SELECT s.id AS id, s.cwd AS cwd
1770
+ + COALESCE(s.total_cache_read_tokens, 0)) > 0`).all({windowDays:Qk});if(t.length===0)return null;let n=null;for(let s of t){let r=Fe({inputTokens:s.input_tokens,outputTokens:s.output_tokens,cacheCreateTokens:s.cache_create_tokens,cacheReadTokens:s.cache_read_tokens},s.primary_model);r.cents<=0||(!n||r.cents>n.cents)&&(n={row:s,cents:r.cents,totalTokens:r.totalTokens})}return n?{...hm(n.row),totalTokens:n.totalTokens,costCents:n.cents,costDisplay:ae(n.cents),tokensDisplay:_e(n.totalTokens),primaryModel:n.row.primary_model,primaryModelLabel:gt(n.row.primary_model).label}:null}async function s0(e){try{if(!(await Yk(e)).isDirectory())return null}catch{return null}try{let{stdout:t}=await qk("git",["rev-parse","HEAD"],{cwd:e,timeout:Zk}),n=t.trim();return/^[0-9a-f]{40}$/.test(n)?n:null}catch{return null}}async function r0(){let e=E(),t=e.prepare(`SELECT s.id AS id, s.cwd AS cwd
1626
1771
  FROM sessions s
1627
1772
  WHERE s.cwd IS NOT NULL AND s.started_at IS NOT NULL
1628
1773
  ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC
1629
- LIMIT 1`).get();if(!t?.cwd)return null;let s=await sw(t.cwd);if(!s)return null;let n=an(s);if(n.length===0)return null;let r=n[0],o=e.prepare(`SELECT s.first_user_message AS first_user_message, s.ended_at AS ended_at
1774
+ LIMIT 1`).get();if(!t?.cwd)return null;let n=await s0(t.cwd);if(!n)return null;let s=zs(n);if(s.length===0)return null;let r=s[0],o=e.prepare(`SELECT s.first_user_message AS first_user_message, s.ended_at AS ended_at
1630
1775
  FROM sessions s
1631
- WHERE s.id = ?`).get(r.sessionId);return{sessionId:r.sessionId,project:r.project,alias:r.alias,startedAt:r.startedAt,endedAt:o?.ended_at??r.endedAt,firstUserMessage:o?.first_user_message??null,commitSha:r.commitSha,shortSha:r.commitSha.slice(0,7),subject:r.subject,committedAt:r.committedAt,cwd:t.cwd}}async function _d(){let e=Qy(),t=e.semantic?Promise.resolve().then(()=>{try{return ew()}catch(a){return console.error("[discover.rediscovered]",a),null}}):Promise.resolve(null),s=e.cost?Promise.resolve().then(()=>{try{return tw()}catch(a){return console.error("[discover.expensive]",a),null}}):Promise.resolve(null),n=e.git?nw().catch(a=>(console.error("[discover.authored]",a),null)):Promise.resolve(null),[r,o,i]=await Promise.all([t,s,n]);return{rediscovered:r,expensive:o,authored:i,availability:e,generatedAt:new Date().toISOString()}}function So(e,t=60){if(!e)return"";let s=e.replace(/\s+/g," ").trim();return s.length<=t?s:s.slice(0,t-1)+"\u2026"}function bo(e){return e.alias??(e.firstUserMessage?So(e.firstUserMessage,60):null)??e.sessionId.slice(0,8)}function rw(e){let t=e.rediscovered||e.expensive||e.authored;if(console.log(""),console.log(c.bold("today \xB7 for you")),console.log(c.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),!t){let s=[];e.availability.semantic||s.push("v0.11 semantic (`recall semantic on`)"),e.availability.cost||s.push("v0.10a cost (`recall stats --backfill`)"),e.availability.git||s.push("v0.10b git (`recall correlate`)"),console.log(c.dim(" no picks today.")),s.length>0?console.log(c.dim(" enable: "+s.join(", "))):console.log(c.dim(" data sources are live but nothing matched \u2014 try again tomorrow.")),console.log("");return}if(e.rediscovered){let s=e.rediscovered;console.log(""),console.log(` ${c.tool("\u2726 rediscovered")} ${c.dim(`(${s.daysAgo}d ago)`)}`),console.log(` ${c.bold(bo(s))} ${c.dim(s.project?`\xB7 ${s.project}`:"")}`),console.log(` ${c.dim(s.sessionId.slice(0,8))}`),s.matchedKeywords.length>0&&console.log(" "+c.dim("matched: ")+s.matchedKeywords.slice(0,6).map(n=>c.tool(`#${n}`)).join(" ")),s.summary&&console.log(" "+c.dim(So(s.summary,100)))}if(e.expensive){let s=e.expensive;console.log(""),console.log(` ${c.accent("$ most expensive \xB7 7d")} ${c.bold(s.costDisplay)} ${c.dim(`(${s.tokensDisplay} \xB7 ${s.primaryModelLabel})`)}`),console.log(` ${c.bold(bo(s))} ${c.dim(s.project?`\xB7 ${s.project}`:"")}`),console.log(` ${c.dim(s.sessionId.slice(0,8))} ${c.dim(s.startedAt?J(s.startedAt):"")}`)}if(e.authored){let s=e.authored;console.log(""),console.log(` ${c.ok("\u2387 authored current HEAD")} ${c.bold(s.shortSha)} ${c.dim(s.committedAt?`(${J(s.committedAt)})`:"")}`),console.log(` ${c.bold(bo(s))} ${c.dim(s.project?`\xB7 ${s.project}`:"")}`),s.subject&&console.log(` ${c.dim(So(s.subject,80))}`),console.log(` ${c.dim(s.sessionId.slice(0,8))} ${c.dim("cwd: "+s.cwd)}`)}console.log("")}async function hd(e){let t=await _d();if(e.json){console.log(JSON.stringify(t,null,2));return}rw(t)}v();Ke();import{spawnSync as yd}from"node:child_process";import{readdirSync as ow}from"node:fs";import{join as iw,resolve as aw}from"node:path";var yo=["code","cursor","code-insiders","windsurf"],Ed=[{id:"code",label:"VS Code"},{id:"cursor",label:"Cursor"},{id:"code-insiders",label:"VS Code Insiders"},{id:"windsurf",label:"Windsurf"}],cw="https://marketplace.visualstudio.com/items?itemName=clauderecallhq.clauderecall-vscode";function lw(){return iw(ee(),"extensions","vscode")}function dw(){let e=lw(),t;try{t=ow(e)}catch{return null}let s=t.filter(n=>/^(clauderecall|claude-recall)-vscode-.*\.vsix$/i.test(n));return s.length===0?null:(s.sort((n,r)=>r.localeCompare(n,void 0,{numeric:!0})),aw(e,s[0]))}function bd(e){let t=yd(e,["--version"],{stdio:"ignore",shell:process.platform==="win32"});return t.error?!1:t.status===0}function uw(e,t){let s=yd(e,["--install-extension",t],{stdio:"pipe",encoding:"utf8",shell:process.platform==="win32"});return s.error?{ok:!1,message:s.error.message}:s.status!==0?{ok:!1,message:(s.stderr??"").trim()||`exit code ${s.status??"null"}`}:{ok:!0,message:""}}function Sd(){process.stderr.write(c.err(`no bundled .vsix found at extensions/vscode/(clauderecall|claude-recall)-vscode-*.vsix
1776
+ WHERE s.id = ?`).get(r.sessionId);return{sessionId:r.sessionId,project:r.project,alias:r.alias,startedAt:r.startedAt,endedAt:o?.ended_at??r.endedAt,firstUserMessage:o?.first_user_message??null,commitSha:r.commitSha,shortSha:r.commitSha.slice(0,7),subject:r.subject,committedAt:r.committedAt,cwd:t.cwd}}async function Em(){let e=e0(),t=e.semantic?Promise.resolve().then(()=>{try{return t0()}catch(a){return console.error("[discover.rediscovered]",a),null}}):Promise.resolve(null),n=e.cost?Promise.resolve().then(()=>{try{return n0()}catch(a){return console.error("[discover.expensive]",a),null}}):Promise.resolve(null),s=e.git?r0().catch(a=>(console.error("[discover.authored]",a),null)):Promise.resolve(null),[r,o,i]=await Promise.all([t,n,s]);return{rediscovered:r,expensive:o,authored:i,availability:e,generatedAt:new Date().toISOString()}}function Ai(e,t=60){if(!e)return"";let n=e.replace(/\s+/g," ").trim();return n.length<=t?n:n.slice(0,t-1)+"\u2026"}function Ci(e){return e.alias??(e.firstUserMessage?Ai(e.firstUserMessage,60):null)??e.sessionId.slice(0,8)}function o0(e){let t=e.rediscovered||e.expensive||e.authored;if(console.log(""),console.log(c.bold("today \xB7 for you")),console.log(c.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),!t){let n=[];e.availability.semantic||n.push("v0.11 semantic (`recall semantic on`)"),e.availability.cost||n.push("v0.10a cost (`recall stats --backfill`)"),e.availability.git||n.push("v0.10b git (`recall correlate`)"),console.log(c.dim(" no picks today.")),n.length>0?console.log(c.dim(" enable: "+n.join(", "))):console.log(c.dim(" data sources are live but nothing matched \u2014 try again tomorrow.")),console.log("");return}if(e.rediscovered){let n=e.rediscovered;console.log(""),console.log(` ${c.tool("\u2726 rediscovered")} ${c.dim(`(${n.daysAgo}d ago)`)}`),console.log(` ${c.bold(Ci(n))} ${c.dim(n.project?`\xB7 ${n.project}`:"")}`),console.log(` ${c.dim(n.sessionId.slice(0,8))}`),n.matchedKeywords.length>0&&console.log(" "+c.dim("matched: ")+n.matchedKeywords.slice(0,6).map(s=>c.tool(`#${s}`)).join(" ")),n.summary&&console.log(" "+c.dim(Ai(n.summary,100)))}if(e.expensive){let n=e.expensive;console.log(""),console.log(` ${c.accent("$ most expensive \xB7 7d")} ${c.bold(n.costDisplay)} ${c.dim(`(${n.tokensDisplay} \xB7 ${n.primaryModelLabel})`)}`),console.log(` ${c.bold(Ci(n))} ${c.dim(n.project?`\xB7 ${n.project}`:"")}`),console.log(` ${c.dim(n.sessionId.slice(0,8))} ${c.dim(n.startedAt?G(n.startedAt):"")}`)}if(e.authored){let n=e.authored;console.log(""),console.log(` ${c.ok("\u2387 authored current HEAD")} ${c.bold(n.shortSha)} ${c.dim(n.committedAt?`(${G(n.committedAt)})`:"")}`),console.log(` ${c.bold(Ci(n))} ${c.dim(n.project?`\xB7 ${n.project}`:"")}`),n.subject&&console.log(` ${c.dim(Ai(n.subject,80))}`),console.log(` ${c.dim(n.sessionId.slice(0,8))} ${c.dim("cwd: "+n.cwd)}`)}console.log("")}async function bm(e){let t=await Em();if(e.json){console.log(JSON.stringify(t,null,2));return}o0(t)}$();Ge();import{spawnSync as Tm}from"node:child_process";import{readdirSync as i0}from"node:fs";import{join as a0,resolve as c0}from"node:path";var Li=["code","cursor","code-insiders","windsurf"],Sm=[{id:"code",label:"VS Code"},{id:"cursor",label:"Cursor"},{id:"code-insiders",label:"VS Code Insiders"},{id:"windsurf",label:"Windsurf"}],l0="https://marketplace.visualstudio.com/items?itemName=clauderecallhq.clauderecall-vscode";function d0(){return a0(te(),"extensions","vscode")}function u0(){let e=d0(),t;try{t=i0(e)}catch{return null}let n=t.filter(s=>/^(clauderecall|claude-recall)-vscode-.*\.vsix$/i.test(s));return n.length===0?null:(n.sort((s,r)=>r.localeCompare(s,void 0,{numeric:!0})),c0(e,n[0]))}function wm(e){let t=Tm(e,["--version"],{stdio:"ignore",shell:process.platform==="win32"});return t.error?!1:t.status===0}function m0(e,t){let n=Tm(e,["--install-extension",t],{stdio:"pipe",encoding:"utf8",shell:process.platform==="win32"});return n.error?{ok:!1,message:n.error.message}:n.status!==0?{ok:!1,message:(n.stderr??"").trim()||`exit code ${n.status??"null"}`}:{ok:!0,message:""}}function ym(){process.stderr.write(c.err(`no bundled .vsix found at extensions/vscode/(clauderecall|claude-recall)-vscode-*.vsix
1632
1777
  `)),process.stderr.write(c.dim(`see PUBLISHING.md for how to build the extension, or run:
1633
1778
  `)),process.stderr.write(c.dim(` (cd extensions/vscode && npm install && npm run build && npm run package)
1634
- `))}function pw(e){return yo.includes(e)}async function wd(e){if(e.editor!==void 0&&!pw(e.editor)){process.stderr.write(c.err(`unknown --editor target: ${e.editor}
1635
- `)),process.stderr.write(c.dim(`valid values: ${yo.join(", ")}
1636
- `)),process.exitCode=1;return}let t=dw();if(e.printPath){if(!t){Sd(),process.exitCode=1;return}process.stdout.write(t+`
1637
- `);return}if(!t){Sd(),process.exitCode=1;return}let s;if(e.editor){let r=e.editor;if(!bd(r)){process.stderr.write(c.err(`editor CLI not found on PATH: ${r}
1779
+ `))}function p0(e){return Li.includes(e)}async function Rm(e){if(e.editor!==void 0&&!p0(e.editor)){process.stderr.write(c.err(`unknown --editor target: ${e.editor}
1780
+ `)),process.stderr.write(c.dim(`valid values: ${Li.join(", ")}
1781
+ `)),process.exitCode=1;return}let t=u0();if(e.printPath){if(!t){ym(),process.exitCode=1;return}process.stdout.write(t+`
1782
+ `);return}if(!t){ym(),process.exitCode=1;return}let n;if(e.editor){let r=e.editor;if(!wm(r)){process.stderr.write(c.err(`editor CLI not found on PATH: ${r}
1638
1783
  `)),process.stderr.write(c.dim(`install the editor shell integration, or omit --editor to install into every detected editor.
1639
1784
  `)),process.stderr.write(c.dim(`see PUBLISHING.md for manual install instructions.
1640
- `)),process.exitCode=1;return}s=[Ed.find(i=>i.id===r)??{id:r,label:r}]}else s=Ed.filter(r=>bd(r.id));if(s.length===0){process.stderr.write(c.err(`no supported editor CLI detected on PATH.
1641
- `)),process.stderr.write(c.dim(`looked for: ${yo.join(", ")}.
1785
+ `)),process.exitCode=1;return}n=[Sm.find(i=>i.id===r)??{id:r,label:r}]}else n=Sm.filter(r=>wm(r.id));if(n.length===0){process.stderr.write(c.err(`no supported editor CLI detected on PATH.
1786
+ `)),process.stderr.write(c.dim(`looked for: ${Li.join(", ")}.
1642
1787
  `)),process.stderr.write(c.dim(`install the editor shell integration and try again.
1643
1788
  `)),process.stderr.write(c.dim(`see PUBLISHING.md for manual install instructions.
1644
1789
  `)),process.exitCode=1;return}process.stderr.write(c.dim(`bundled .vsix: ${t}
1645
1790
 
1646
- `));let n=!1;for(let r of s){let{ok:o,message:i}=uw(r.id,t);o?process.stderr.write(c.ok(`\u2713 installed into ${r.label} (${r.id})
1647
- `)):(n=!0,process.stderr.write(c.err(`\u2717 ${r.label} (${r.id}): ${i}
1791
+ `));let s=!1;for(let r of n){let{ok:o,message:i}=m0(r.id,t);o?process.stderr.write(c.ok(`\u2713 installed into ${r.label} (${r.id})
1792
+ `)):(s=!0,process.stderr.write(c.err(`\u2717 ${r.label} (${r.id}): ${i}
1648
1793
  `)))}process.stderr.write(`
1649
1794
  `),process.stderr.write(c.bold(`next steps:
1650
1795
  `)),process.stderr.write(` 1. reload the editor window: Cmd/Ctrl+Shift+P then pick "Developer: Reload Window"
1651
1796
  `),process.stderr.write(` 2. enable the extension: open settings and set "claude-recall.autoAlias" to true
1652
1797
  `),process.stderr.write(`
1653
- `),process.stderr.write(c.dim(`marketplace install: ${cw}
1654
- `)),n&&(process.exitCode=1)}xo();v();function ko(e){return e>=70?c.ok:e>=40?c.warn:c.err}function Rd(e,t=20){let s=Math.round(e/100*t);return"\u2588".repeat(s)+"\u2591".repeat(t-s)}function mw(e){let t=ko(e.score);process.stdout.write(`
1798
+ `),process.stderr.write(c.dim(`marketplace install: ${l0}
1799
+ `)),s&&(process.exitCode=1)}Ii();$();function Mi(e){return e>=70?c.ok:e>=40?c.warn:c.err}function xm(e,t=20){let n=Math.round(e/100*t);return"\u2588".repeat(n)+"\u2591".repeat(t-n)}function g0(e){let t=Mi(e.score);process.stdout.write(`
1655
1800
  ${c.bold(e.projectName)} ${t(String(e.score)+"/100")}
1656
- `);let s=e.breakdown,n=[["Sessions",s.sessionCount.score*100,`${s.sessionCount.raw} sessions`],["Recency",s.recency.score*100,`${s.recency.daysSinceLastSession}d ago`],["Depth",s.fragmentation.score*100,`avg ${s.fragmentation.avgMessages} msgs`],["Search",s.searchCoverage.score*100,`${Math.round(s.searchCoverage.ratio*100)}% covered`],["Tags",s.tagCoverage.score*100,`${Math.round(s.tagCoverage.ratio*100)}% tagged`]];for(let[r,o,i]of n){let a=ko(o);process.stdout.write(` ${r.padEnd(10)} ${a(Rd(o,16))} ${String(Math.round(o)).padStart(3)}% ${c.dim(i)}
1657
- `)}}function xd(e,t){if(e){let r=To(e);if(!r){process.stderr.write(c.err(`project "${e}" not found
1801
+ `);let n=e.breakdown,s=[["Sessions",n.sessionCount.score*100,`${n.sessionCount.raw} sessions`],["Recency",n.recency.score*100,`${n.recency.daysSinceLastSession}d ago`],["Depth",n.fragmentation.score*100,`avg ${n.fragmentation.avgMessages} msgs`],["Search",n.searchCoverage.score*100,`${Math.round(n.searchCoverage.ratio*100)}% covered`],["Tags",n.tagCoverage.score*100,`${Math.round(n.tagCoverage.ratio*100)}% tagged`]];for(let[r,o,i]of s){let a=Mi(o);process.stdout.write(` ${r.padEnd(10)} ${a(xm(o,16))} ${String(Math.round(o)).padStart(3)}% ${c.dim(i)}
1802
+ `)}}function Cm(e,t){if(e){let r=Oi(e);if(!r){process.stderr.write(c.err(`project "${e}" not found
1658
1803
  `)),process.exitCode=1;return}if(t.json){process.stdout.write(JSON.stringify(r,null,2)+`
1659
- `);return}mw(r);return}let s=Ro();if(s.length===0){process.stdout.write("No projects found. Run `recall index` first.\n");return}if(t.json){process.stdout.write(JSON.stringify(s,null,2)+`
1660
- `);return}let n=[...s].sort((r,o)=>r.score-o.score);process.stdout.write(c.bold("Memory Health Scores")+` (worst first)
1804
+ `);return}g0(r);return}let n=vi();if(n.length===0){process.stdout.write("No projects found. Run `recall index` first.\n");return}if(t.json){process.stdout.write(JSON.stringify(n,null,2)+`
1805
+ `);return}let s=[...n].sort((r,o)=>r.score-o.score);process.stdout.write(c.bold("Memory Health Scores")+` (worst first)
1661
1806
 
1662
- `);for(let r of n){let o=ko(r.score);process.stdout.write(` ${o(Rd(r.score,12))} ${String(r.score).padStart(3)}/100 ${r.projectName}
1807
+ `);for(let r of s){let o=Mi(r.score);process.stdout.write(` ${o(xm(r.score,12))} ${String(r.score).padStart(3)}/100 ${r.projectName}
1663
1808
  `)}process.stdout.write(`
1664
- `)}v();function kd(e){if(e==="on"){rr(!0),process.stdout.write(c.ok("Verification badges enabled.")+`
1665
- `);return}if(e==="off"){rr(!1),process.stdout.write(`Verification badges disabled.
1666
- `);return}let t=ws();process.stdout.write(`Verification badges: ${t?c.ok("ON"):"OFF"}
1809
+ `)}$();function Am(e){if(e==="on"){zr(!0),process.stdout.write(c.ok("Verification badges enabled.")+`
1810
+ `);return}if(e==="off"){zr(!1),process.stdout.write(`Verification badges disabled.
1811
+ `);return}let t=Wn();process.stdout.write(`Verification badges: ${t?c.ok("ON"):"OFF"}
1667
1812
  `),process.stdout.write(`
1668
1813
  Toggle with: recall verify on | off
1669
- `)}v();Vt();xs();At();ks();import{hostname as Ww}from"node:os";import{randomBytes as Xw}from"node:crypto";qe();de();P();w();Ke();import{existsSync as gw}from"node:fs";import fw from"node:readline";import{createRequire as _w}from"node:module";import{Chalk as hw}from"chalk";var Ew=_w(import.meta.url),bw=Ew(`${ee()}/package.json`).version,Ad="#f97316",Sw="#8b9098",yw="#10b981",ww="#f59e0b",Ft=new hw({level:process.env.NO_COLOR?0:3}),be=Ft.hex(Ad),oe=Ft.hex(Ad).bold,I=Ft.hex(Sw),wt=Ft.hex(yw),un=Ft.hex(ww),Od=Ft.bold,vd=[" \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557","\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D","\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 ","\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D ","\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557"," \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D","\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 ","\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 ","\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 ","\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 ","\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557","\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"],Tw=vd[0]?.length??49,Cd="Never lose a Claude Code session again.",Rw="CLAUDE RECALL";function xw(){if(!gw(se))return{sessions:0,projects:0};try{let t=_().prepare(`SELECT
1814
+ `)}$();ln();Jn();Ft();zn();import{hostname as X0}from"node:os";import{randomBytes as G0}from"node:crypto";Xe();ge();D();R();Ge();import{existsSync as f0}from"node:fs";import _0 from"node:readline";import{createRequire as h0}from"node:module";import{Chalk as E0}from"chalk";var b0=h0(import.meta.url),S0=b0(`${te()}/package.json`).version,vm="#f97316",w0="#8b9098",y0="#10b981",T0="#f59e0b",qt=new E0({level:process.env.NO_COLOR?0:3}),Re=qt.hex(vm),le=qt.hex(vm).bold,P=qt.hex(w0),Lt=qt.hex(y0),er=qt.hex(T0),Im=qt.bold,Mm=[" \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557","\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D","\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 ","\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D ","\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557"," \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D","\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 ","\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 ","\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 ","\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 ","\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557","\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"],R0=Mm[0]?.length??49,Lm="Never lose a Claude Code session again.",k0="CLAUDE RECALL";function x0(){if(!f0(ee))return{sessions:0,projects:0};try{let t=E().prepare(`SELECT
1670
1815
  (SELECT COUNT(*) FROM sessions) AS sessions,
1671
- (SELECT COUNT(*) FROM projects) AS projects`).get();return{sessions:t.sessions,projects:t.projects}}catch{return{sessions:0,projects:0}}}function kw(){let e=process.stdout.columns;return typeof e!="number"||e<=0?!1:e<Tw+2}function Id(){if(kw()){console.log(`${oe(Rw)} ${I("\xB7")} ${I(Cd)}`);return}for(let e of vd)console.log(oe(e));console.log(I(Cd))}async function Cw(){return{daemon:Q(),counts:xw(),license:await Te()}}function Lw(e){let t=[];if(t.push(` ${I("Version:")} ${be(`v${bw}`)} ${I("\xB7 CLI \xB7 @clauderecallhq/cli")}`),e.daemon){let s=`http://127.0.0.1:${e.daemon.port}`;t.push(` ${I("Daemon:")} ${wt("running")} on ${s} ${I(`(pid ${e.daemon.pid})`)}`)}else t.push(` ${I("Daemon:")} ${un("stopped")}`);if(e.counts.sessions===0)t.push(` ${I("Sessions:")} ${un("none indexed yet")}`);else{let s=e.counts.projects===1?"project":"projects";t.push(` ${I("Sessions:")} ${be(String(e.counts.sessions))} indexed across ${be(String(e.counts.projects))} ${s}`)}return e.license.tier==="pro"?t.push(` ${I("License:")} ${wt("Pro")}`):t.push(` ${I("License:")} Free`),t}function Nw(e){let t=be(">");if(e.counts.sessions===0)return`${t} Run ${oe("recall index")} to scan ~/.claude/projects/`;if(!e.daemon)return`${t} Run ${oe("recall start")} to launch the local daemon`;if(e.license.tier==="free")return`${t} Run ${oe("recall upgrade")} to unlock Pro features`;let s=`http://127.0.0.1:${e.daemon.port}`;return`${t} Open ${be(s)} in your browser ${I("\xB7")} or run ${oe("recall --help")} for all commands`}var pn=[{title:"Setup",commands:[{name:"index",description:"Scan your Claude sessions and build the searchable database"},{name:"status",description:"Database size, session counts, and daemon state"},{name:"projects",description:"List every project with how many sessions are in each"},{name:"install-extension",description:"Install the VS Code / Cursor / Windsurf extension"},{name:"doctor",description:"Health check: DB size, WAL, FTS fragmentation, integrity, disk space, alias invariant"},{name:"optimize",description:"Maintenance: WAL checkpoint, FTS5 merge, planner stats. Add --vacuum to reclaim free pages"}]},{title:"Browse",commands:[{name:"tui",description:"Browse and search sessions in an interactive terminal UI"},{name:"list",description:"List your sessions, newest first"},{name:"show <id>",description:"Print a full session transcript"},{name:"search <query>",description:"Full-text search across every message"},{name:"similar <id>",description:"Find sessions about the same topic (Pro)"},{name:"semantic [action]",description:'Manage semantic search (defaults to "status")'},{name:"name <id> <name>",description:"Rename a session (or its terminal tab)"}]},{title:"Pipe to Claude",commands:[{name:"context <id>",description:"Pipe a past session as markdown into a fresh `claude` chat"},{name:"neighborhood <id>",description:"Bundle a session with its parents, children, and citations for richer context"}]},{title:"Threads",commands:[{name:"threads sync",description:"Capture sessions running in this repo right now into a thread"},{name:"threads sync --preflight",description:"Preview what `threads sync` would do without writing"},{name:"threads scan",description:"Auto-detect parent-child links across past sessions"},{name:"threads list",description:"List threads, newest first"},{name:"threads show <id>",description:"Show a thread's header and session tree"},{name:"threads new <name>",description:"Create a new thread"},{name:"threads link <id>",description:"Link a session into a thread"},{name:"threads unlink <id>",description:"Remove a session from a thread"},{name:"threads set-parent <id>",description:"Change a session's parent within a thread"},{name:"threads rename <id>",description:"Rename a thread"},{name:"threads close <id>",description:"Mark a thread as closed"},{name:"threads reopen <id>",description:"Reopen a closed thread"},{name:"threads archive <id>",description:"Archive a thread"},{name:"threads merge <id>",description:"Merge one thread into another"},{name:"threads split <id>",description:"Split sessions out into a new thread"}]},{title:"Inference",commands:[{name:"infer outputs",description:"Extract code, file, and error references from sessions in one project"},{name:"infer citations",description:"Find sessions that cite the same files or errors"},{name:"infer l1",description:"Fast deterministic link inference. No LLM cost"},{name:"infer links",description:"LLM-powered link classification (Pro). Optional --auto-promote"},{name:"infer bug-patterns",description:"Cluster sessions that hit the same bug"},{name:"embeddings [action]",description:'Audit embedding coverage (defaults to "audit")'}]},{title:"Analytics",commands:[{name:"stats [id]",description:"Token + dollar usage. No args = overview, pass a session for details"},{name:"health [project]",description:"Score how well-organized each project's sessions are"},{name:"digest",description:"Today's rediscovery picks and standouts"},{name:"correlate [id]",description:"Link sessions to the git commits they wrote"},{name:"blame <sha>",description:"Find which session(s) wrote the code in a commit"}]},{title:"Daemon",commands:[{name:"start",description:"Start the background daemon (live tab tracking + web UI)"},{name:"stop",description:"Stop the background daemon"},{name:"open",description:"Open the local web UI in your browser"}]},{title:"Sharing",commands:[{name:"share [id]",description:"Save a session as a shareable PNG card"},{name:"wrapped [month]",description:"Save a monthly recap as a PNG card"}]},{title:"Diagnostics",commands:[{name:"correlator audit",description:"Find sessions with bad terminal-name aliases (--fix to clear)"},{name:"correlator debug",description:"Show how the daemon is matching open terminals to sessions"},{name:"correlator restore",description:"Restore aliases that `correlator audit --fix` cleared"},{name:"titles [action]",description:'Audit session titles in the current project (defaults to "audit")'},{name:"import-vscode-state",description:"Backfill missing tab names from your editor's workspace state"},{name:"audit-secrets",description:"Scan the database for leaked secrets"}]},{title:"Integrations",commands:[{name:"mcp",description:"Expose Recall as an MCP server (for Claude Desktop / Claude Code)"},{name:"paste",description:"Save clipboard content (or stdin) into Recall"}]},{title:"Pro & License",commands:[{name:"upgrade",description:"Open the Pro pricing page"},{name:"activate <key>",description:"Activate your Pro license (run once after purchase)"},{name:"license [action]",description:"Show the current license. Use `license deactivate` to remove it"},{name:"verify [action]",description:'Toggle verification badges in the UI (defaults to "status")'}]},{title:"Feedback",commands:[{name:"feedback",description:"Send a 1-5 rating to the team. Pass --score for non-interactive use"}]}],Aw=pn.reduce((e,t)=>e+t.commands.length,0),Ow=new Set([...pn.flatMap(e=>e.commands.map(t=>t.name.split(/\s+/)[0])),"thread","correlator-audit","correlator-debug","correlator-restore","extract-outputs","infer-citations","infer-l1","infer-links","infer-bug-patterns"]);function Ld(){let e=pn.flatMap(s=>s.commands.map(n=>`recall ${n.name}`)),t=Math.max(...e.map(s=>s.length));console.log(),console.log(`${Od("COMMANDS")} ${I(`\xB7 ${Aw} total \xB7 q to quit \xB7 recall --help for full details`)}`),console.log(` ${I("All commands run as subcommands of")} ${oe("recall")}${I(". There is no separate")} ${be("threads")}${I(",")} ${be("thread")}${I(", or")} ${be("titles")} ${I("binary.")}`);for(let s of pn){console.log(),console.log(` ${oe(s.title.toUpperCase())}`);for(let n of s.commands){let r=`recall ${n.name}`.padEnd(t," ");console.log(` ${be(r)} ${I(n.description)}`)}}console.log()}function vw(){return!!process.stdin.isTTY&&!!process.stdout.isTTY}async function Iw(){if(!vw())return;let e=fw.createInterface({input:process.stdin,output:process.stdout}),t=`${I("Type")} ${oe("/")} ${I("for commands, or press")} ${oe("Enter")} ${I("to exit")} ${be("\u203A")} `;return new Promise(s=>{let n=()=>{e.question(t,r=>{let o=r.trim();if(o==="/"||o==="/help"||o==="help"||o==="?"){Ld(),n();return}if(o===""||o==="q"||o==="quit"||o==="exit"){e.close();return}let i=o.startsWith("/")?o.slice(1).trimStart():o,a=i.toLowerCase();if((a==="recall"||a.startsWith("recall "))&&(i=i.slice(6).trimStart()),i===""){Ld(),n();return}let d=(i.split(/\s+/)[0]??"").toLowerCase();Ow.has(d)?console.log(` ${I("Run")} ${oe(`recall ${i}`)} ${I("in your shell to invoke that command.")}`):console.log(` ${I(`No command matches "${o}". Type`)} ${oe("/")} ${I("to browse, or")} ${oe("recall --help")} ${I("for the full list.")}`),n()})};e.on("close",()=>{console.log(),s()}),n()})}async function Md(){let e=await Cw();console.log(),Id(),console.log();for(let t of Lw(e))console.log(t);console.log(),console.log(Nw(e)),console.log(),await Iw()}var Mw=[{name:"Unlimited search",detail:"full history, no caps"},{name:"Context for Claude",detail:"recall past sessions on demand"},{name:"MCP integration",detail:"native tool access from any client"},{name:"Semantic search",detail:"find by meaning, not just keywords"},{name:"Advanced UI",detail:"tags, aliases, notes, exports"}];function Dw(){return process.env.NO_COLOR||process.env.CI||process.env.RECALL_NO_ANIMATION?!1:!!process.stdout.isTTY}function Co(e){return new Promise(t=>setTimeout(t,e))}function $w(e){let s=(e.split("@")[0]??"").split(/[._+\-]/)[0]??"";return s?s.charAt(0).toUpperCase()+s.slice(1).toLowerCase():""}function Nd(){process.stdout.write("\r\x1B[2K")}async function mn(e){let t=Dw();console.log(),Id(),console.log(),t&&(process.stdout.write(` ${I("Verifying license...")}`),await Co(260),Nd()),console.log(` ${wt("\u2713")} ${I("License verified")}`),console.log(` ${wt("\u2713")} ${I("Tier:")} ${oe("Pro")} ${I("\xB7 Lifetime")}`),console.log(` ${wt("\u2713")} ${I("Key:")} ${be(e.key_short)}`),console.log(` ${wt("\u2713")} ${I("Email:")} ${e.email}`),e.test_mode&&console.log(` ${un("!")} ${un("Test mode:")} this license was issued in test mode.`),console.log(),console.log(` ${Od("Unlocking Pro features")}`),console.log();for(let r of Mw)t&&(process.stdout.write(` ${I("\xB7")} ${I(r.name)}`),await Co(110),Nd()),console.log(` ${wt("\u2713")} ${r.name} ${I(r.detail)}`);t&&await Co(160),console.log();let s=$w(e.email),n=s?`Welcome aboard, ${s}.`:"Welcome aboard.";console.log(` ${oe(n)}`),console.log(` ${I("Run")} ${be("recall")} ${I("to open the dashboard, or")} ${be("recall help")} ${I("for the command list.")}`),console.log()}v();Vt();xs();At();ks();import{hostname as Pw}from"node:os";import{randomBytes as Fw}from"node:crypto";import{createInterface as jw}from"node:readline/promises";async function Uw(e){let t=e.fetchFn??fetch,s=`${e.apiBaseUrl}/api/trial/cli-redeem`,n=await t(s,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({promo_code:e.promoCode,email:e.email,machine_fingerprint:e.fingerprint,instance_name:e.instanceName,referral_source:"cli"})}),r;try{r=await n.json()}catch{throw new Error(`server returned non-JSON (status ${n.status})`)}return{status:n.status,body:r}}async function Bw(e={}){let t=jw({input:e.stdin??process.stdin,output:e.stdout??process.stdout});try{return(await t.question(c.bold("Email for your 7-day trial: "))).trim().toLowerCase()}finally{t.close()}}function Hw(e){if(e.length<5||e.length>256||!e.includes("@"))return!1;let[t,s]=e.split("@");return!(!t||!s||!s.includes(".")||/\s/.test(e))}async function gn(e,t={}){let s=e.trim().toUpperCase();if(s.length<3&&(console.error(c.warn(`Promo code "${e}" is too short.`)),process.exit(1)),!t.skipExistingProCheck){let{getLicenseStatus:u}=await Promise.resolve().then(()=>(de(),Zt)),p=await u();if(p.tier==="pro"){console.log(),console.log(c.bold("You are already on Pro \u2014 no need to redeem a trial.")),console.log(` ${c.dim("Key:")} ${p.key_short}`),console.log(` ${c.dim("Email:")} ${p.customer_email}`),console.log(),console.log(c.dim("If you wanted to switch accounts, run `recall license deactivate` first.")),console.log();return}}console.log(),console.log(`${c.ok("Redeeming")} promo code ${c.bold(s)} for a 7-day Pro trial.`),console.log(c.dim("You will get an email with your license key for backup.")),console.log(),!t.promptFn&&!process.stdin.isTTY&&(console.error(c.warn("Trial activation needs an interactive terminal so you can type your email.")),console.error(c.dim("Re-run `recall activate "+s+"` in a terminal (not piped, not CI).")),process.exit(1));let r=await(t.promptFn??Bw)();Hw(r)||(console.error(c.warn(`"${r}" doesn't look like an email address.`)),process.exit(1));let o=t.instanceName??`${Pw()}-${Fw(4).toString("hex")}`,i=t.fingerprint??vt(),a=t.apiBaseUrl??tt(),d;try{d=await Uw({promoCode:s,email:r,fingerprint:i,instanceName:o,apiBaseUrl:a,...t.fetchFn?{fetchFn:t.fetchFn}:{}})}catch(u){let p=u instanceof Error?u.message:"unknown error";console.error(c.warn(`Could not reach the trial server at ${a}: ${p}`)),console.error(c.dim("If you are offline, try again when you have a connection.")),process.exit(1)}if(d.status!==200||!d.body.license_jwt||!d.body.license_key){let u=d.body.error??`trial activation failed (status ${d.status})`;console.error(c.warn(`Trial refused: ${u}`)),process.exit(1)}let l=await Ot(d.body.license_jwt);(!l.valid||!l.claims)&&(console.error(c.warn(`Server returned a JWT that fails local verification: ${l.reason??"unknown"}`)),console.error(c.dim("This usually means the CLI is older than the server. Try `npm i -g @clauderecallhq/cli`.")),process.exit(1)),Ts({license_jwt:d.body.license_jwt,license_key:d.body.license_key,key_short:d.body.key_short??l.claims.key_short,customer_email:d.body.customer_email??l.claims.email,activated_at:new Date().toISOString(),tier:"pro",test_mode:!!l.claims.test_mode}),t.skipSuccessDashboard||await mn({...l.claims,key_short:d.body.key_short??l.claims.key_short,email:d.body.customer_email??l.claims.email})}function Jw(e){return/^recall-pro-/i.test(e)}async function Dd(e){let t=e.trim();if(t.length<3&&(console.error(c.warn("License key or promo code looks too short.")),console.error(c.dim("Paste the full key from your purchase email, or a promo code like RECALL7DAY.")),process.exit(1)),!Jw(t)){await gn(t);return}t.length<8&&(console.error(c.warn("License key looks too short. Paste the full key from your purchase email.")),process.exit(1));let s=`${Ww()}-${Xw(4).toString("hex")}`,n=`${tt()}/api/license/activate`,r;try{r=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({license_key:t,instance_name:s,machine_fingerprint:vt()})})}catch(a){let d=a instanceof Error?a.message:"unknown error";console.error(c.warn(`Could not reach activation server at ${n}: ${d}`)),console.error(c.dim("If you are offline, try again when you have a connection.")),process.exit(1)}let o;try{o=await r.json()}catch{console.error(c.warn(`Activation server returned invalid JSON (status ${r.status}).`)),process.exit(1)}if(r.status!==200||!o.license_jwt){let a=o.error??`activation failed (status ${r.status})`;console.error(c.warn(`Activation refused: ${a}`)),process.exit(1)}let i=await Ot(o.license_jwt);(!i.valid||!i.claims)&&(console.error(c.warn(`Server returned a JWT that fails local verification: ${i.reason??"unknown"}`)),console.error(c.dim("This usually means the CLI is older than the server, or the public key was rotated.")),process.exit(1)),Ts({license_jwt:o.license_jwt,license_key:t,key_short:o.key_short??i.claims.key_short,customer_email:o.customer_email??i.claims.email,activated_at:new Date().toISOString(),tier:"pro",test_mode:!!i.claims.test_mode}),await mn({...i.claims,key_short:o.key_short??i.claims.key_short,email:o.customer_email??i.claims.email})}v();import{spawn as Gw}from"node:child_process";import{platform as fn}from"node:os";var $d="https://clauderecall.com/pricing";function Yw(e){let t=fn()==="darwin"?"open":fn()==="win32"?"start":"xdg-open",s=fn()==="win32"?["",e]:[e];Gw(t,s,{detached:!0,stdio:"ignore",shell:fn()==="win32"}).unref()}async function Pd(){let{getLicenseStatus:e}=await Promise.resolve().then(()=>(de(),Zt)),t=await e();if(t.tier==="pro"){console.log(),console.log(c.bold("Already on Pro.")),console.log(` ${c.dim("Key:")} ${t.key_short}`),console.log(` ${c.dim("Email:")} ${t.customer_email}`),console.log();return}console.log(),console.log(`${c.ok("Opening")} ${$d}`),console.log(c.dim("After purchase, run: recall activate <license-key>")),console.log(),Yw($d)}v();import{spawn as zw}from"node:child_process";import{platform as _n}from"node:os";var Fd="https://clauderecall.com/pricing?trial=1&utm_source=cli&utm_medium=recall_trial";function qw(e){let t=_n()==="darwin"?"open":_n()==="win32"?"start":"xdg-open",s=_n()==="win32"?["",e]:[e];zw(t,s,{detached:!0,stdio:"ignore",shell:_n()==="win32"}).unref()}async function jd(e){if(e&&e.trim().length>0){await gn(e);return}let{getLicenseStatus:t}=await Promise.resolve().then(()=>(de(),Zt)),s=await t();if(s.tier==="pro"){console.log(),console.log(c.bold("You are already on Pro.")),console.log(` ${c.dim("Key:")} ${s.key_short}`),console.log(` ${c.dim("Email:")} ${s.customer_email}`),console.log();return}console.log(),console.log(`${c.ok("Opening")} ${Fd}`),console.log(c.dim("Enter your email to get a 7-day Pro trial link. No card.")),console.log(c.dim("After you click the verification email, run: recall activate <key>")),console.log(),console.log(c.dim("Tip: if you have a promo code, run `recall trial <CODE>` to skip the browser.")),console.log(),qw(Fd)}v();Vt();import{existsSync as Ud,mkdirSync as Kw,readFileSync as Vw,writeFileSync as Zw}from"node:fs";import{homedir as Qw}from"node:os";import{join as Bd}from"node:path";import{randomBytes as eT}from"node:crypto";var No=Bd(Qw(),".recall"),En=Bd(No,"telemetry.json"),Lo={decided_at:null,decision:null,last_ping_month:null,nonce_month:null,nonce:null,last_error:null};function Tt(){if(!Ud(En))return{...Lo};try{let e=Vw(En,"utf8"),t=JSON.parse(e);return{...Lo,...t}}catch{return{...Lo}}}function hn(e){Ud(No)||Kw(No,{recursive:!0}),Zw(En,JSON.stringify(e,null,2)+`
1672
- `,{mode:384})}function Hd(){return En}function Ao(e=new Date){let t=e.getUTCFullYear(),s=String(e.getUTCMonth()+1).padStart(2,"0");return`${t}-${s}`}function Wd(e,t){return e.nonce&&e.nonce_month===t?e:{...e,nonce:eT(16).toString("hex"),nonce_month:t}}function tT(e,t){return!t.nonce||!t.nonce_month?null:{event:"install",version:e,platform:process.platform,arch:process.arch,month:t.nonce_month,nonce:t.nonce}}async function Xd(e){let t=Tt();if(t.decision!=="on")return{status:t.decision==="off"?"opted-out":"no-decision"};let s=Ao();if(t.last_ping_month===s)return{status:"already-this-month"};t=Wd(t,s);let n=tT(e,t);if(!n)return{status:"error",detail:"no nonce"};try{let r=await fetch(`${tt()}/api/install-ping`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n),signal:AbortSignal.timeout(5e3)});if(!r.ok){let i=`http ${r.status}`;return hn({...t,last_error:`${new Date().toISOString()} ${i}`}),{status:"error",detail:i}}let o=await r.json().catch(()=>({}));return hn({...t,last_ping_month:s,last_error:null}),{status:o.deduped?"deduped":"sent"}}catch(r){let o=r instanceof Error?r.message:"unknown";return hn({...t,last_error:`${new Date().toISOString()} ${o}`}),{status:"error",detail:o}}}function jt(e){let s={...Tt(),decision:e,decided_at:new Date().toISOString(),last_error:null,...e==="off"?{nonce:null,nonce_month:null,last_ping_month:null}:{}};return hn(s),s}function bn(e){let t=Wd(Tt(),Ao());return{event:"install",version:e,platform:process.platform,arch:process.arch,month:t.nonce_month??Ao(),nonce:t.nonce??"0".repeat(32)}}async function Oo(e){let t=Tt();console.log(),console.log(c.bold("Telemetry \u2014 anonymous install ping")),console.log(` ${c.dim("decision:")} ${t.decision??"not yet decided"}`),console.log(` ${c.dim("decided at:")} ${t.decided_at??"\u2014"}`),console.log(` ${c.dim("last ping:")} ${t.last_ping_month??"\u2014"}`),console.log(` ${c.dim("state file:")} ${Hd()}`),t.last_error&&console.log(` ${c.warn("last error:")} ${t.last_error}`),console.log(),console.log(c.dim("Next-ping payload preview (only sent if decision = on):")),console.log(c.dim(JSON.stringify(bn(e),null,2))),console.log(),console.log(c.dim("Toggle with `recall telemetry on` / `recall telemetry off`.")),console.log(c.dim("Full disclosure: https://clauderecall.com/telemetry")),console.log()}async function Jd(){jt("on"),console.log(),console.log(c.ok("Telemetry: opted in.")),console.log(c.dim("One anonymous ping per machine per month. No PII. Source: docs/specs/v0.16-install-ping.md")),console.log(c.dim("Reverse with `recall telemetry off`.")),console.log()}async function Gd(){jt("off"),console.log(),console.log(c.ok("Telemetry: opted out.")),console.log(c.dim("No further pings will be sent. Existing nonce deleted.")),console.log()}async function Yd(e){await Oo(e)}import{createInterface as sT}from"node:readline/promises";import{stdin as zd,stdout as me}from"node:process";var nT=new Set(["telemetry","trial","upgrade","activate","license","help","version"]);function rT(e){return!(process.env.CI==="true"||process.env.RECALL_QUIET_INSTALL==="1"||process.env.RECALL_DISABLE_TELEMETRY_PROMPT==="1"||!zd.isTTY||!me.isTTY||e&&nT.has(e)||Tt().decision!==null)}async function qd(e,t){if(!rT(e))return;let s=bn(t);me.write(`
1673
- `),me.write(` Claude Recall \u2014 anonymous install ping?
1816
+ (SELECT COUNT(*) FROM projects) AS projects`).get();return{sessions:t.sessions,projects:t.projects}}catch{return{sessions:0,projects:0}}}function C0(){let e=process.stdout.columns;return typeof e!="number"||e<=0?!1:e<R0+2}function Dm(){if(C0()){console.log(`${le(k0)} ${P("\xB7")} ${P(Lm)}`);return}for(let e of Mm)console.log(le(e));console.log(P(Lm))}async function A0(){return{daemon:oe(),counts:x0(),license:await Ae()}}function L0(e){let t=[];if(t.push(` ${P("Version:")} ${Re(`v${S0}`)} ${P("\xB7 CLI \xB7 @clauderecallhq/cli")}`),e.daemon){let n=`http://127.0.0.1:${e.daemon.port}`;t.push(` ${P("Daemon:")} ${Lt("running")} on ${n} ${P(`(pid ${e.daemon.pid})`)}`)}else t.push(` ${P("Daemon:")} ${er("stopped")}`);if(e.counts.sessions===0)t.push(` ${P("Sessions:")} ${er("none indexed yet")}`);else{let n=e.counts.projects===1?"project":"projects";t.push(` ${P("Sessions:")} ${Re(String(e.counts.sessions))} indexed across ${Re(String(e.counts.projects))} ${n}`)}return e.license.tier==="pro"?t.push(` ${P("License:")} ${Lt("Pro")}`):t.push(` ${P("License:")} Free`),t}function N0(e){let t=Re(">");if(e.counts.sessions===0)return`${t} Run ${le("recall index")} to scan ~/.claude/projects/`;if(!e.daemon)return`${t} Run ${le("recall start")} to launch the local daemon`;if(e.license.tier==="free")return`${t} Run ${le("recall upgrade")} to unlock Pro features`;let n=`http://127.0.0.1:${e.daemon.port}`;return`${t} Open ${Re(n)} in your browser ${P("\xB7")} or run ${le("recall --help")} for all commands`}var tr=[{title:"Setup",commands:[{name:"index",description:"Scan your Claude sessions and build the searchable database"},{name:"status",description:"Database size, session counts, and daemon state"},{name:"projects",description:"List every project with how many sessions are in each"},{name:"install-extension",description:"Install the VS Code / Cursor / Windsurf extension"},{name:"doctor",description:"Health check: DB size, WAL, FTS fragmentation, integrity, disk space, alias invariant"},{name:"optimize",description:"Maintenance: WAL checkpoint, FTS5 merge, planner stats. Add --vacuum to reclaim free pages"}]},{title:"Browse",commands:[{name:"tui",description:"Browse and search sessions in an interactive terminal UI"},{name:"list",description:"List your sessions, newest first"},{name:"show <id>",description:"Print a full session transcript"},{name:"search <query>",description:"Full-text search across every message"},{name:"similar <id>",description:"Find sessions about the same topic (Pro)"},{name:"semantic [action]",description:'Manage semantic search (defaults to "status")'},{name:"name <id> <name>",description:"Rename a session (or its terminal tab)"}]},{title:"Pipe to Claude",commands:[{name:"context <id>",description:"Pipe a past session as markdown into a fresh `claude` chat"},{name:"neighborhood <id>",description:"Bundle a session with its parents, children, and citations for richer context"}]},{title:"Threads",commands:[{name:"threads sync",description:"Capture sessions running in this repo right now into a thread"},{name:"threads sync --preflight",description:"Preview what `threads sync` would do without writing"},{name:"threads scan",description:"Auto-detect parent-child links across past sessions"},{name:"threads list",description:"List threads, newest first"},{name:"threads show <id>",description:"Show a thread's header and session tree"},{name:"threads new <name>",description:"Create a new thread"},{name:"threads link <id>",description:"Link a session into a thread"},{name:"threads unlink <id>",description:"Remove a session from a thread"},{name:"threads set-parent <id>",description:"Change a session's parent within a thread"},{name:"threads rename <id>",description:"Rename a thread"},{name:"threads close <id>",description:"Mark a thread as closed"},{name:"threads reopen <id>",description:"Reopen a closed thread"},{name:"threads archive <id>",description:"Archive a thread"},{name:"threads merge <id>",description:"Merge one thread into another"},{name:"threads split <id>",description:"Split sessions out into a new thread"}]},{title:"Inference",commands:[{name:"infer outputs",description:"Extract code, file, and error references from sessions in one project"},{name:"infer citations",description:"Find sessions that cite the same files or errors"},{name:"infer l1",description:"Fast deterministic link inference. No LLM cost"},{name:"infer links",description:"LLM-powered link classification (Pro). Optional --auto-promote"},{name:"infer bug-patterns",description:"Cluster sessions that hit the same bug"},{name:"embeddings [action]",description:'Audit embedding coverage (defaults to "audit")'}]},{title:"Analytics",commands:[{name:"stats [id]",description:"Token + dollar usage. No args = overview, pass a session for details"},{name:"health [project]",description:"Score how well-organized each project's sessions are"},{name:"digest",description:"Today's rediscovery picks and standouts"},{name:"correlate [id]",description:"Link sessions to the git commits they wrote"},{name:"blame <sha>",description:"Find which session(s) wrote the code in a commit"}]},{title:"Daemon",commands:[{name:"start",description:"Start the background daemon (live tab tracking + web UI)"},{name:"stop",description:"Stop the background daemon"},{name:"open",description:"Open the local web UI in your browser"}]},{title:"Sharing",commands:[{name:"share [id]",description:"Save a session as a shareable PNG card"},{name:"wrapped [month]",description:"Save a monthly recap as a PNG card"}]},{title:"Diagnostics",commands:[{name:"correlator audit",description:"Find sessions with bad terminal-name aliases (--fix to clear)"},{name:"correlator debug",description:"Show how the daemon is matching open terminals to sessions"},{name:"correlator restore",description:"Restore aliases that `correlator audit --fix` cleared"},{name:"titles [action]",description:'Audit session titles in the current project (defaults to "audit")'},{name:"import-vscode-state",description:"Backfill missing tab names from your editor's workspace state"},{name:"audit-secrets",description:"Scan the database for leaked secrets"}]},{title:"Integrations",commands:[{name:"mcp",description:"Expose Recall as an MCP server (for Claude Desktop / Claude Code)"},{name:"paste",description:"Save clipboard content (or stdin) into Recall"}]},{title:"Pro & License",commands:[{name:"upgrade",description:"Open the Pro pricing page"},{name:"activate <key>",description:"Activate your Pro license (run once after purchase)"},{name:"license [action]",description:"Show the current license. Use `license deactivate` to remove it"},{name:"verify [action]",description:'Toggle verification badges in the UI (defaults to "status")'}]},{title:"Feedback",commands:[{name:"feedback",description:"Send a 1-5 rating to the team. Pass --score for non-interactive use"}]}],O0=tr.reduce((e,t)=>e+t.commands.length,0),v0=new Set([...tr.flatMap(e=>e.commands.map(t=>t.name.split(/\s+/)[0])),"thread","correlator-audit","correlator-debug","correlator-restore","extract-outputs","infer-citations","infer-l1","infer-links","infer-bug-patterns"]);function Nm(){let e=tr.flatMap(n=>n.commands.map(s=>`recall ${s.name}`)),t=Math.max(...e.map(n=>n.length));console.log(),console.log(`${Im("COMMANDS")} ${P(`\xB7 ${O0} total \xB7 q to quit \xB7 recall --help for full details`)}`),console.log(` ${P("All commands run as subcommands of")} ${le("recall")}${P(". There is no separate")} ${Re("threads")}${P(",")} ${Re("thread")}${P(", or")} ${Re("titles")} ${P("binary.")}`);for(let n of tr){console.log(),console.log(` ${le(n.title.toUpperCase())}`);for(let s of n.commands){let r=`recall ${s.name}`.padEnd(t," ");console.log(` ${Re(r)} ${P(s.description)}`)}}console.log()}function I0(){return!!process.stdin.isTTY&&!!process.stdout.isTTY}async function M0(){if(!I0())return;let e=_0.createInterface({input:process.stdin,output:process.stdout}),t=`${P("Type")} ${le("/")} ${P("for commands, or press")} ${le("Enter")} ${P("to exit")} ${Re("\u203A")} `;return new Promise(n=>{let s=()=>{e.question(t,r=>{let o=r.trim();if(o==="/"||o==="/help"||o==="help"||o==="?"){Nm(),s();return}if(o===""||o==="q"||o==="quit"||o==="exit"){e.close();return}let i=o.startsWith("/")?o.slice(1).trimStart():o,a=i.toLowerCase();if((a==="recall"||a.startsWith("recall "))&&(i=i.slice(6).trimStart()),i===""){Nm(),s();return}let d=(i.split(/\s+/)[0]??"").toLowerCase();v0.has(d)?console.log(` ${P("Run")} ${le(`recall ${i}`)} ${P("in your shell to invoke that command.")}`):console.log(` ${P(`No command matches "${o}". Type`)} ${le("/")} ${P("to browse, or")} ${le("recall --help")} ${P("for the full list.")}`),s()})};e.on("close",()=>{console.log(),n()}),s()})}async function $m(){let e=await A0();console.log(),Dm(),console.log();for(let t of L0(e))console.log(t);console.log(),console.log(N0(e)),console.log(),await M0()}var D0=[{name:"Unlimited search",detail:"full history, no caps"},{name:"Context for Claude",detail:"recall past sessions on demand"},{name:"MCP integration",detail:"native tool access from any client"},{name:"Semantic search",detail:"find by meaning, not just keywords"},{name:"Advanced UI",detail:"tags, aliases, notes, exports"}];function $0(){return process.env.NO_COLOR||process.env.CI||process.env.RECALL_NO_ANIMATION?!1:!!process.stdout.isTTY}function Di(e){return new Promise(t=>setTimeout(t,e))}function P0(e){let n=(e.split("@")[0]??"").split(/[._+\-]/)[0]??"";return n?n.charAt(0).toUpperCase()+n.slice(1).toLowerCase():""}function Om(){process.stdout.write("\r\x1B[2K")}async function nr(e){let t=$0();console.log(),Dm(),console.log(),t&&(process.stdout.write(` ${P("Verifying license...")}`),await Di(260),Om()),console.log(` ${Lt("\u2713")} ${P("License verified")}`),console.log(` ${Lt("\u2713")} ${P("Tier:")} ${le("Pro")} ${P("\xB7 Lifetime")}`),console.log(` ${Lt("\u2713")} ${P("Key:")} ${Re(e.key_short)}`),console.log(` ${Lt("\u2713")} ${P("Email:")} ${e.email}`),e.test_mode&&console.log(` ${er("!")} ${er("Test mode:")} this license was issued in test mode.`),console.log(),console.log(` ${Im("Unlocking Pro features")}`),console.log();for(let r of D0)t&&(process.stdout.write(` ${P("\xB7")} ${P(r.name)}`),await Di(110),Om()),console.log(` ${Lt("\u2713")} ${r.name} ${P(r.detail)}`);t&&await Di(160),console.log();let n=P0(e.email),s=n?`Welcome aboard, ${n}.`:"Welcome aboard.";console.log(` ${le(s)}`),console.log(` ${P("Run")} ${Re("recall")} ${P("to open the dashboard, or")} ${Re("recall help")} ${P("for the command list.")}`),console.log()}$();ln();Jn();Ft();zn();import{hostname as F0}from"node:os";import{randomBytes as j0}from"node:crypto";import{createInterface as U0}from"node:readline/promises";async function B0(e){let t=e.fetchFn??fetch,n=`${e.apiBaseUrl}/api/trial/cli-redeem`,s=await t(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({promo_code:e.promoCode,email:e.email,machine_fingerprint:e.fingerprint,instance_name:e.instanceName,referral_source:"cli"})}),r;try{r=await s.json()}catch{throw new Error(`server returned non-JSON (status ${s.status})`)}return{status:s.status,body:r}}async function H0(e={}){let t=U0({input:e.stdin??process.stdin,output:e.stdout??process.stdout});try{return(await t.question(c.bold("Email for your 7-day trial: "))).trim().toLowerCase()}finally{t.close()}}function W0(e){if(e.length<5||e.length>256||!e.includes("@"))return!1;let[t,n]=e.split("@");return!(!t||!n||!n.includes(".")||/\s/.test(e))}async function sr(e,t={}){let n=e.trim().toUpperCase();if(n.length<3&&(console.error(c.warn(`Promo code "${e}" is too short.`)),process.exit(1)),!t.skipExistingProCheck){let{getLicenseStatus:u}=await Promise.resolve().then(()=>(ge(),dn)),m=await u();if(m.tier==="pro"){console.log(),console.log(c.bold("You are already on Pro \u2014 no need to redeem a trial.")),console.log(` ${c.dim("Key:")} ${m.key_short}`),console.log(` ${c.dim("Email:")} ${m.customer_email}`),console.log(),console.log(c.dim("If you wanted to switch accounts, run `recall license deactivate` first.")),console.log();return}}console.log(),console.log(`${c.ok("Redeeming")} promo code ${c.bold(n)} for a 7-day Pro trial.`),console.log(c.dim("You will get an email with your license key for backup.")),console.log(),!t.promptFn&&!process.stdin.isTTY&&(console.error(c.warn("Trial activation needs an interactive terminal so you can type your email.")),console.error(c.dim("Re-run `recall activate "+n+"` in a terminal (not piped, not CI).")),process.exit(1));let r=await(t.promptFn??H0)();W0(r)||(console.error(c.warn(`"${r}" doesn't look like an email address.`)),process.exit(1));let o=t.instanceName??`${F0()}-${j0(4).toString("hex")}`,i=t.fingerprint??Ut(),a=t.apiBaseUrl??lt(),d;try{d=await B0({promoCode:n,email:r,fingerprint:i,instanceName:o,apiBaseUrl:a,...t.fetchFn?{fetchFn:t.fetchFn}:{}})}catch(u){let m=u instanceof Error?u.message:"unknown error";console.error(c.warn(`Could not reach the trial server at ${a}: ${m}`)),console.error(c.dim("If you are offline, try again when you have a connection.")),process.exit(1)}if(d.status!==200||!d.body.license_jwt||!d.body.license_key){let u=d.body.error??`trial activation failed (status ${d.status})`;console.error(c.warn(`Trial refused: ${u}`)),process.exit(1)}let l=await jt(d.body.license_jwt);(!l.valid||!l.claims)&&(console.error(c.warn(`Server returned a JWT that fails local verification: ${l.reason??"unknown"}`)),console.error(c.dim("This usually means the CLI is older than the server. Try `npm i -g @clauderecallhq/cli`.")),process.exit(1)),Xn({license_jwt:d.body.license_jwt,license_key:d.body.license_key,key_short:d.body.key_short??l.claims.key_short,customer_email:d.body.customer_email??l.claims.email,activated_at:new Date().toISOString(),tier:"pro",test_mode:!!l.claims.test_mode}),t.skipSuccessDashboard||await nr({...l.claims,key_short:d.body.key_short??l.claims.key_short,email:d.body.customer_email??l.claims.email})}function J0(e){return/^recall-pro-/i.test(e)}async function Pm(e){let t=e.trim();if(t.length<3&&(console.error(c.warn("License key or promo code looks too short.")),console.error(c.dim("Paste the full key from your purchase email, or a promo code like RECALL7DAY.")),process.exit(1)),!J0(t)){await sr(t);return}t.length<8&&(console.error(c.warn("License key looks too short. Paste the full key from your purchase email.")),process.exit(1));let n=`${X0()}-${G0(4).toString("hex")}`,s=`${lt()}/api/license/activate`,r;try{r=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({license_key:t,instance_name:n,machine_fingerprint:Ut()})})}catch(a){let d=a instanceof Error?a.message:"unknown error";console.error(c.warn(`Could not reach activation server at ${s}: ${d}`)),console.error(c.dim("If you are offline, try again when you have a connection.")),process.exit(1)}let o;try{o=await r.json()}catch{console.error(c.warn(`Activation server returned invalid JSON (status ${r.status}).`)),process.exit(1)}if(r.status!==200||!o.license_jwt){let a=o.error??`activation failed (status ${r.status})`;console.error(c.warn(`Activation refused: ${a}`)),process.exit(1)}let i=await jt(o.license_jwt);(!i.valid||!i.claims)&&(console.error(c.warn(`Server returned a JWT that fails local verification: ${i.reason??"unknown"}`)),console.error(c.dim("This usually means the CLI is older than the server, or the public key was rotated.")),process.exit(1)),Xn({license_jwt:o.license_jwt,license_key:t,key_short:o.key_short??i.claims.key_short,customer_email:o.customer_email??i.claims.email,activated_at:new Date().toISOString(),tier:"pro",test_mode:!!i.claims.test_mode}),await nr({...i.claims,key_short:o.key_short??i.claims.key_short,email:o.customer_email??i.claims.email})}$();import{spawn as z0}from"node:child_process";import{platform as rr}from"node:os";var Fm="https://clauderecall.com/pricing";function Y0(e){let t=rr()==="darwin"?"open":rr()==="win32"?"start":"xdg-open",n=rr()==="win32"?["",e]:[e];z0(t,n,{detached:!0,stdio:"ignore",shell:rr()==="win32"}).unref()}async function jm(){let{getLicenseStatus:e}=await Promise.resolve().then(()=>(ge(),dn)),t=await e();if(t.tier==="pro"){console.log(),console.log(c.bold("Already on Pro.")),console.log(` ${c.dim("Key:")} ${t.key_short}`),console.log(` ${c.dim("Email:")} ${t.customer_email}`),console.log();return}console.log(),console.log(`${c.ok("Opening")} ${Fm}`),console.log(c.dim("After purchase, run: recall activate <license-key>")),console.log(),Y0(Fm)}$();import{spawn as q0}from"node:child_process";import{platform as or}from"node:os";var Um="https://clauderecall.com/pricing?trial=1&utm_source=cli&utm_medium=recall_trial";function V0(e){let t=or()==="darwin"?"open":or()==="win32"?"start":"xdg-open",n=or()==="win32"?["",e]:[e];q0(t,n,{detached:!0,stdio:"ignore",shell:or()==="win32"}).unref()}async function Bm(e){if(e&&e.trim().length>0){await sr(e);return}let{getLicenseStatus:t}=await Promise.resolve().then(()=>(ge(),dn)),n=await t();if(n.tier==="pro"){console.log(),console.log(c.bold("You are already on Pro.")),console.log(` ${c.dim("Key:")} ${n.key_short}`),console.log(` ${c.dim("Email:")} ${n.customer_email}`),console.log();return}console.log(),console.log(`${c.ok("Opening")} ${Um}`),console.log(c.dim("Enter your email to get a 7-day Pro trial link. No card.")),console.log(c.dim("After you click the verification email, run: recall activate <key>")),console.log(),console.log(c.dim("Tip: if you have a promo code, run `recall trial <CODE>` to skip the browser.")),console.log(),V0(Um)}$();ln();import{existsSync as Hm,mkdirSync as K0,readFileSync as Q0,writeFileSync as Z0}from"node:fs";import{homedir as ex}from"node:os";import{join as Wm}from"node:path";import{randomBytes as tx}from"node:crypto";var Pi=Wm(ex(),".recall"),ar=Wm(Pi,"telemetry.json"),$i={decided_at:null,decision:null,last_ping_month:null,nonce_month:null,nonce:null,last_error:null};function Nt(){if(!Hm(ar))return{...$i};try{let e=Q0(ar,"utf8"),t=JSON.parse(e);return{...$i,...t}}catch{return{...$i}}}function ir(e){Hm(Pi)||K0(Pi,{recursive:!0}),Z0(ar,JSON.stringify(e,null,2)+`
1817
+ `,{mode:384})}function Xm(){return ar}function Fi(e=new Date){let t=e.getUTCFullYear(),n=String(e.getUTCMonth()+1).padStart(2,"0");return`${t}-${n}`}function Gm(e,t){return e.nonce&&e.nonce_month===t?e:{...e,nonce:tx(16).toString("hex"),nonce_month:t}}function nx(e,t){return!t.nonce||!t.nonce_month?null:{event:"install",version:e,platform:process.platform,arch:process.arch,month:t.nonce_month,nonce:t.nonce}}async function Jm(e){let t=Nt();if(t.decision!=="on")return{status:t.decision==="off"?"opted-out":"no-decision"};let n=Fi();if(t.last_ping_month===n)return{status:"already-this-month"};t=Gm(t,n);let s=nx(e,t);if(!s)return{status:"error",detail:"no nonce"};try{let r=await fetch(`${lt()}/api/install-ping`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s),signal:AbortSignal.timeout(5e3)});if(!r.ok){let i=`http ${r.status}`;return ir({...t,last_error:`${new Date().toISOString()} ${i}`}),{status:"error",detail:i}}let o=await r.json().catch(()=>({}));return ir({...t,last_ping_month:n,last_error:null}),{status:o.deduped?"deduped":"sent"}}catch(r){let o=r instanceof Error?r.message:"unknown";return ir({...t,last_error:`${new Date().toISOString()} ${o}`}),{status:"error",detail:o}}}function Vt(e){let n={...Nt(),decision:e,decided_at:new Date().toISOString(),last_error:null,...e==="off"?{nonce:null,nonce_month:null,last_ping_month:null}:{}};return ir(n),n}function cr(e){let t=Gm(Nt(),Fi());return{event:"install",version:e,platform:process.platform,arch:process.arch,month:t.nonce_month??Fi(),nonce:t.nonce??"0".repeat(32)}}async function ji(e){let t=Nt();console.log(),console.log(c.bold("Telemetry \u2014 anonymous install ping")),console.log(` ${c.dim("decision:")} ${t.decision??"not yet decided"}`),console.log(` ${c.dim("decided at:")} ${t.decided_at??"\u2014"}`),console.log(` ${c.dim("last ping:")} ${t.last_ping_month??"\u2014"}`),console.log(` ${c.dim("state file:")} ${Xm()}`),t.last_error&&console.log(` ${c.warn("last error:")} ${t.last_error}`),console.log(),console.log(c.dim("Next-ping payload preview (only sent if decision = on):")),console.log(c.dim(JSON.stringify(cr(e),null,2))),console.log(),console.log(c.dim("Toggle with `recall telemetry on` / `recall telemetry off`.")),console.log(c.dim("Full disclosure: https://clauderecall.com/telemetry")),console.log()}async function zm(){Vt("on"),console.log(),console.log(c.ok("Telemetry: opted in.")),console.log(c.dim("One anonymous ping per machine per month. No PII. Source: docs/specs/v0.16-install-ping.md")),console.log(c.dim("Reverse with `recall telemetry off`.")),console.log()}async function Ym(){Vt("off"),console.log(),console.log(c.ok("Telemetry: opted out.")),console.log(c.dim("No further pings will be sent. Existing nonce deleted.")),console.log()}async function qm(e){await ji(e)}import{createInterface as sx}from"node:readline/promises";import{stdin as Vm,stdout as Ee}from"node:process";var rx=new Set(["telemetry","trial","upgrade","activate","license","help","version"]);function ox(e){return!(process.env.CI==="true"||process.env.RECALL_QUIET_INSTALL==="1"||process.env.RECALL_DISABLE_TELEMETRY_PROMPT==="1"||!Vm.isTTY||!Ee.isTTY||e&&rx.has(e)||Nt().decision!==null)}async function Km(e,t){if(!ox(e))return;let n=cr(t);Ee.write(`
1818
+ `),Ee.write(` Claude Recall \u2014 anonymous install ping?
1674
1819
 
1675
- `),me.write(` npm download counts mix real installs with bots and scanners.
1676
- `),me.write(` We have no way to tell them apart without your help.
1820
+ `),Ee.write(` npm download counts mix real installs with bots and scanners.
1821
+ `),Ee.write(` We have no way to tell them apart without your help.
1677
1822
 
1678
- `),me.write(` If you say yes, we send ONE message, ONE time per month:
1823
+ `),Ee.write(` If you say yes, we send ONE message, ONE time per month:
1679
1824
 
1680
- `),me.write(` ${JSON.stringify(s)}
1825
+ `),Ee.write(` ${JSON.stringify(n)}
1681
1826
 
1682
- `),me.write(` Never sent: email, IP, fingerprint, file paths, file content,
1683
- `),me.write(` session content, license key. Reverse any time with:
1684
- `),me.write(` recall telemetry off
1827
+ `),Ee.write(` Never sent: email, IP, fingerprint, file paths, file content,
1828
+ `),Ee.write(` session content, license key. Reverse any time with:
1829
+ `),Ee.write(` recall telemetry off
1685
1830
 
1686
- `),me.write(` Full disclosure: https://clauderecall.com/telemetry
1831
+ `),Ee.write(` Full disclosure: https://clauderecall.com/telemetry
1687
1832
 
1688
- `);let n=sT({input:zd,output:me}),r;try{r=(await n.question(" Send the install ping? [y/N]: ")).trim().toLowerCase()}finally{n.close()}r==="y"||r==="yes"?(jt("on"),me.write(`
1833
+ `);let s=sx({input:Vm,output:Ee}),r;try{r=(await s.question(" Send the install ping? [y/N]: ")).trim().toLowerCase()}finally{s.close()}r==="y"||r==="yes"?(Vt("on"),Ee.write(`
1689
1834
  Opted in. Reverse any time with \`recall telemetry off\`.
1690
1835
 
1691
- `)):(jt("off"),me.write(`
1836
+ `)):(Vt("off"),Ee.write(`
1692
1837
  Opted out. No pings will be sent. Reverse with \`recall telemetry on\`.
1693
1838
 
1694
- `))}v();de();At();ar();async function Kd(){let e=await Te();if(console.log(),e.tier==="free"){console.log(c.bold("Tier: Free")),console.log(c.dim("Free includes: index, list, show, projects, status, the daemon, and the basic web UI.")),e.invalid_reason&&(console.log(),console.log(c.warn(`Stored license is invalid: ${e.invalid_reason}`))),console.log(),console.log("Buy Pro at https://clauderecall.com/pricing"),console.log("Then run: recall activate <license-key>"),console.log();return}console.log(c.bold("Tier: Pro")),console.log(` ${c.dim("Key:")} ${e.key_short}`),console.log(` ${c.dim("Email:")} ${e.customer_email}`),console.log(` ${c.dim("Activated:")} ${e.activated_at}`),e.expires_at&&console.log(` ${c.dim("Expires:")} ${e.expires_at}`),e.test_mode&&console.log(` ${c.warn("Test mode:")} this license was issued in test mode.`);let t=Cs();t&&console.log(` ${c.dim("Last server check:")} ${t.last_checked_at}`),console.log()}async function Vd(){let e=await mr({force:!0});if(console.log(),!e.ran){console.log(c.warn("No license stored on this machine.")),console.log(c.dim("Run `recall activate <license-key>` first.")),console.log();return}if(!e.last_checked_at){console.log(c.warn("Could not reach the license server.")),console.log(c.dim("Network error. Try again when you have a connection.")),console.log();return}if(e.revoked){console.log(c.warn("License is revoked.")),e.reason&&console.log(` ${c.dim("Reason:")} ${e.reason}`),console.log(` ${c.dim("Checked:")} ${e.last_checked_at}`),console.log();return}console.log(c.ok("License is valid.")),console.log(` ${c.dim("Checked:")} ${e.last_checked_at}`),console.log()}function Zd(){$i(),console.log(),console.log("License removed from this machine."),console.log(c.dim(`(${ht})`)),console.log()}w();v();import oT from"cli-table3";function Me(e){let t=_();if(e.length>=32){let n=t.prepare("SELECT id FROM threads WHERE id = ?").get(e);return n?n.id:(process.stderr.write(`thread not found: ${e}
1695
- `),null)}let s=t.prepare("SELECT id, name FROM threads WHERE id LIKE ? ORDER BY created_at DESC").all(e+"%");if(s.length===1)return s[0].id;if(s.length===0)return process.stderr.write(`no thread matches prefix "${e}"
1839
+ `))}var ix="RECALL_NO_BANNER",ax=new Set(["doctor"]),Qm=!1;function Zm(e){if(Qm||ax.has(e.commandName)||(e.optOut!==void 0?e.optOut:process.env[ix]==="1"))return 0;let n=e.write??(r=>process.stderr.write(r)),s=0;try{let r=Ys(),o=Wu(r,"critical");if(o.length===0)return 0;s=o.length,n(cx(o)),Qm=!0}catch{return 0}return s}function cx(e){let t=[];t.push(""),t.push(`! ${e.length} unacknowledged critical alert(s) from background doctor:`);for(let n of e.slice(0,5))t.push(` - [${n.id.slice(0,8)}] ${n.message}`);return e.length>5&&t.push(` \u2026 and ${e.length-5} more (run \`recall doctor\` for the full list).`),t.push("Run `recall doctor` for full details. Acknowledge with `recall doctor --ack <id>`."),t.push(""),t.join(`
1840
+ `)}$();ge();Ft();Vr();async function ep(){let e=await Ae();if(console.log(),e.tier==="free"){console.log(c.bold("Tier: Free")),console.log(c.dim("Free includes: index, list, show, projects, status, the daemon, and the basic web UI.")),e.invalid_reason&&(console.log(),console.log(c.warn(`Stored license is invalid: ${e.invalid_reason}`))),console.log(),console.log("Buy Pro at https://clauderecall.com/pricing"),console.log("Then run: recall activate <license-key>"),console.log();return}console.log(c.bold("Tier: Pro")),console.log(` ${c.dim("Key:")} ${e.key_short}`),console.log(` ${c.dim("Email:")} ${e.customer_email}`),console.log(` ${c.dim("Activated:")} ${e.activated_at}`),e.expires_at&&console.log(` ${c.dim("Expires:")} ${e.expires_at}`),e.test_mode&&console.log(` ${c.warn("Test mode:")} this license was issued in test mode.`);let t=Yn();t&&console.log(` ${c.dim("Last server check:")} ${t.last_checked_at}`),console.log()}async function tp(){let e=await no({force:!0});if(console.log(),!e.ran){console.log(c.warn("No license stored on this machine.")),console.log(c.dim("Run `recall activate <license-key>` first.")),console.log();return}if(!e.last_checked_at){console.log(c.warn("Could not reach the license server.")),console.log(c.dim("Network error. Try again when you have a connection.")),console.log();return}if(e.revoked){console.log(c.warn("License is revoked.")),e.reason&&console.log(` ${c.dim("Reason:")} ${e.reason}`),console.log(` ${c.dim("Checked:")} ${e.last_checked_at}`),console.log();return}console.log(c.ok("License is valid.")),console.log(` ${c.dim("Checked:")} ${e.last_checked_at}`),console.log()}function np(){Xa(),console.log(),console.log("License removed from this machine."),console.log(c.dim(`(${kt})`)),console.log()}R();$();import lx from"cli-table3";function je(e){let t=E();if(e.length>=32){let s=t.prepare("SELECT id FROM threads WHERE id = ?").get(e);return s?s.id:(process.stderr.write(`thread not found: ${e}
1841
+ `),null)}let n=t.prepare("SELECT id, name FROM threads WHERE id LIKE ? ORDER BY created_at DESC").all(e+"%");if(n.length===1)return n[0].id;if(n.length===0)return process.stderr.write(`no thread matches prefix "${e}"
1696
1842
  `),null;process.stderr.write(`ambiguous thread prefix "${e}" \u2014 candidates:
1697
- `);for(let n of s)process.stderr.write(` ${X(n.id)} ${n.name}
1698
- `);return null}function Rt(e){let t=_();if(e.length>=32){let n=t.prepare("SELECT id FROM sessions WHERE id = ?").get(e);return n?n.id:(process.stderr.write(`session not found: ${e}
1699
- `),null)}let s=t.prepare("SELECT id FROM sessions WHERE id LIKE ? LIMIT 2").all(e+"%");return s.length===1?s[0].id:s.length===0?(process.stderr.write(`no session matches prefix "${e}"
1843
+ `);for(let s of n)process.stderr.write(` ${X(s.id)} ${s.name}
1844
+ `);return null}function Ot(e){let t=E();if(e.length>=32){let s=t.prepare("SELECT id FROM sessions WHERE id = ?").get(e);return s?s.id:(process.stderr.write(`session not found: ${e}
1845
+ `),null)}let n=t.prepare("SELECT id FROM sessions WHERE id LIKE ? LIMIT 2").all(e+"%");return n.length===1?n[0].id:n.length===0?(process.stderr.write(`no session matches prefix "${e}"
1700
1846
  `),null):(process.stderr.write(`ambiguous session prefix "${e}". be more specific.
1701
- `),null)}function iT(e){let t=new Map;if(e.length===0)return t;let s=e.map(()=>"?").join(","),r=_().prepare(`SELECT s.id,
1847
+ `),null)}function dx(e){let t=new Map;if(e.length===0)return t;let n=e.map(()=>"?").join(","),r=E().prepare(`SELECT s.id,
1702
1848
  s.first_user_message,
1703
1849
  p.name AS project_name,
1704
1850
  a.alias AS alias
1705
1851
  FROM sessions s
1706
1852
  LEFT JOIN projects p ON p.id = s.project_id
1707
1853
  LEFT JOIN session_aliases a ON a.session_id = s.id
1708
- WHERE s.id IN (${s})`).all(...e);for(let o of r)t.set(o.id,o);return t}function Qd(e,t){let s=t.get(e),n=c.accent(X(e));if(!s)return`${n} ${c.dim("(unindexed)")}`;let r=s.alias??s.first_user_message??"",o=s.project_name?c.project(`[${s.project_name}] `):"";return`${n} ${o}${K(r,80)}`}function vo(e){return e.archived?"archived":e.closed_at?"closed":"open"}function Ut(e){let t=vo(e);process.stderr.write(c.ok(`\u2713 ${e.name} ${c.dim(`(${X(e.id)})`)} [${t}]
1709
- `))}function xe(e,t,s){if(e){process.stdout.write(JSON.stringify(t,null,2)+`
1710
- `);return}s()}function eu(e){let t=wr({includeArchived:e.archived===!0});xe(e.json===!0,t,()=>{if(t.length===0){console.log(c.dim("no threads. create one with `recall thread new <name>`."));return}let s=new oT({head:[c.bold("id"),c.bold("name"),c.bold("sessions"),c.bold("origins"),c.bold("status"),c.bold("created")],colWidths:[10,40,10,9,10,16],wordWrap:!0,style:{head:[],border:["grey"]}});for(let n of t)s.push([c.accent(X(n.id)),K(n.name,38),String(n.session_count),String(n.origin_count),vo(n),c.dim(J(n.created_at))]);console.log(s.toString()),console.log(c.dim(`${t.length} thread${t.length===1?"":"s"}.`+(e.archived?"":" add --archived to include archived.")))})}function aT(e,t){let s=new Set(e.edges.map(a=>a.session_id)),n=new Map,r=[];for(let a of e.edges){let d=a.parent_session_id;if(a.role==="origin"||!d||!s.has(d)){r.push(a);continue}let l=n.get(d)??[];l.push(a),n.set(d,l)}let o=new Set,i=(a,d,l,u)=>{if(o.has(a.session_id))return;o.add(a.session_id);let p=u?"":l?"\u2514\u2500 ":"\u251C\u2500 ",m=a.role==="origin"?c.warn("[origin] "):"";process.stdout.write(`${d}${p}${m}${Qd(a.session_id,t)}
1711
- `);let g=n.get(a.session_id)??[],f=u?"":d+(l?" ":"\u2502 ");g.forEach((h,E)=>{i(h,f,E===g.length-1,!1)})};r.forEach((a,d)=>i(a,"",d===r.length-1,!0));for(let a of e.edges)o.has(a.session_id)||process.stdout.write(`${c.warn("? ")}${Qd(a.session_id,t)}
1712
- `)}function tu(e,t){let s=Me(e);if(!s){process.exitCode=1;return}let n=Oe(s);if(!n){process.stderr.write(`thread not found: ${e}
1713
- `),process.exitCode=1;return}let r=iT(n.edges.map(o=>o.session_id));xe(t.json===!0,n,()=>{let o=vo(n);if(console.log(c.bold(n.name)),console.log(c.dim(`id ${X(n.id)} \xB7 ${o} \xB7 ${n.session_count} session${n.session_count===1?"":"s"} \xB7 ${n.origin_count} origin${n.origin_count===1?"":"s"} \xB7 created ${J(n.created_at)}`)),n.summary&&console.log(n.summary),console.log(),n.edges.length===0){console.log(c.dim("(no sessions linked \u2014 use `recall thread link ...`)"));return}aT(n,r)})}function su(e,t){let s;if(t.origin){let r=Rt(t.origin);if(!r){process.exitCode=1;return}s=r}let n=_a({name:e,summary:t.summary??null,originSessionId:s});xe(t.json===!0,n,()=>{Ut(n),process.stderr.write(c.dim(`id ${n.id}
1714
- `)),s&&process.stderr.write(c.dim(`origin: ${X(s)}
1715
- `))})}function nu(e,t){let s=Me(t.thread);if(!s){process.exitCode=1;return}let n=Rt(e);if(!n){process.exitCode=1;return}let r=null;if(t.parent){let a=Rt(t.parent);if(!a){process.exitCode=1;return}r=a}let o=t.role??(r?"child":"origin"),i=ha({threadId:s,sessionId:n,parentSessionId:r,role:o});xe(t.json===!0,i,()=>{process.stderr.write(c.ok(`\u2713 linked ${X(n)} \u2192 ${X(s)} [${o}]
1716
- `))})}function ru(e,t){let s=Me(t.thread);if(!s){process.exitCode=1;return}let n=Rt(e);if(!n){process.exitCode=1;return}let r=Ea(s,n);xe(t.json===!0,r,()=>{if(r.removed===0){process.stderr.write(c.warn(`(no edge existed for ${X(n)} in ${X(s)})
1717
- `));return}process.stderr.write(c.ok(`\u2713 unlinked ${X(n)} from ${X(s)}
1718
- `))})}function ou(e,t){let s=Me(t.thread);if(!s){process.exitCode=1;return}let n=Rt(e);if(!n){process.exitCode=1;return}let r=null;if(t.parent&&t.parent.toLowerCase()!=="none"){let i=Rt(t.parent);if(!i){process.exitCode=1;return}r=i}let o=ba(s,n,r);xe(t.json===!0,o,()=>{process.stderr.write(c.ok(`\u2713 ${X(n)} in ${X(s)} \u2192 `+(r?`child of ${X(r)}`:"origin")+`
1719
- `))})}function iu(e,t,s){let n=Me(e);if(!n){process.exitCode=1;return}let r=Sa(n,t);xe(s.json===!0,r,()=>Ut(r))}function au(e,t){let s=Me(e);if(!s){process.exitCode=1;return}let n=ya(s);xe(t.json===!0,n,()=>Ut(n))}function cu(e,t){let s=Me(e);if(!s){process.exitCode=1;return}let n=wa(s);xe(t.json===!0,n,()=>Ut(n))}function lu(e,t){let s=Me(e);if(!s){process.exitCode=1;return}let n=Ta(s);xe(t.json===!0,n,()=>Ut(n))}function du(e,t){let s=Me(e);if(!s){process.exitCode=1;return}let n=Me(t.into);if(!n){process.exitCode=1;return}if(s===n){process.stderr.write(c.err(`cannot merge a thread into itself
1720
- `)),process.exitCode=1;return}let r=Ra(s,n);xe(t.json===!0,r,()=>{process.stderr.write(c.ok(`\u2713 merged ${X(s)} \u2192 ${X(n)} (${r.session_count} sessions)
1721
- `))})}function uu(e,t){let s=Me(e);if(!s){process.exitCode=1;return}let n=t.sessions.split(",").map(i=>i.trim()).filter(Boolean);if(n.length===0){process.stderr.write(c.err(`--sessions must be a non-empty comma-separated list
1722
- `)),process.exitCode=1;return}let r=[];for(let i of n){let a=Rt(i);if(!a){process.exitCode=1;return}r.push(a)}let o=xa({threadId:s,sessionIds:r,newThreadName:t.name});xe(t.json===!0,o,()=>{Ut(o),process.stderr.write(c.dim(`split off ${r.length} session${r.length===1?"":"s"} from ${X(s)}
1723
- `))})}w();v();import{randomUUID as AT}from"node:crypto";import{readFileSync as cT,statSync as lT}from"node:fs";var dT=200*1024*1024,Mo=.7,Do=.5,gu=Do,uT=[{maxGapMs:3600*1e3,weight:.7,label:"<1h gap"},{maxGapMs:14400*1e3,weight:.4,label:"<4h gap"},{maxGapMs:1440*60*1e3,weight:.2,label:"<24h gap"}],pT=["let's commit","lets commit","now commit","inspect the diff","inspect this diff","review the diff","review this diff","diff of all changes","diff of changes","based on what we just did","based on the things done","based on all the things","continue from","continuing from","pick up where","next step","now fix","now lets","now let's","from the previous session","from our last session","from the last session"];function pu(e){if(!e)return null;if(e.startsWith("/")){let s=e.split(" \xB7 ");if(s.length>1)return s[1].trim().toLowerCase()}let t=e.split(" \xB7 ");return t.length>1?t[t.length-1].trim().toLowerCase():null}function mT(e,t){let s=t-e;if(s<0)return{weight:0,label:null};for(let n of uT)if(s<=n.maxGapMs)return{weight:n.weight,label:n.label};return{weight:0,label:null}}function gT(e){if(e.length===0)return{weight:0,matched:null,matchedIndex:-1};let t=[.4,.35,.3,.25,.2,.15,.1],s=Math.min(e.length,t.length);for(let n=0;n<s;n++){let r=e[n];if(!r)continue;let o=r.toLowerCase();for(let i of pT)if(o.includes(i))return{weight:t[n],matched:i,matchedIndex:n}}return{weight:0,matched:null,matchedIndex:-1}}function Io(e,t){if(!e||!t||e.length!==t.length)return 0;let s=0;for(let n=0;n<e.length;n++)s+=e[n]*t[n];return s<-1?-1:s>1?1:s}function fT(e,t){if(e.length===0||t.length===0)return 0;let s=0;for(let n of e)for(let r of t){let o=Io(n,r);o>s&&(s=o)}return s}function _T(e,t){let s=Io(e.mean_embedding,t.mean_embedding),n=Io(e.tail_pool,t.head_pool),r=fT(e.sample_chunks,t.sample_chunks),o=0,i=null;if(s>o&&(o=s,i="mean"),n>o&&(o=n,i="asymmetric"),r>o&&(o=r,i="max_pool"),o<.65)return{weight:0,cosine:o,mode:null};if(o>=.85)return{weight:.3,cosine:o,mode:i};let a=(o-.65)/.2*.3;return{weight:Math.round(a*100)/100,cosine:o,mode:i}}function hT(e,t){return e.cluster_id===null||t.cluster_id===null?{weight:0,same:!1}:e.cluster_id!==t.cluster_id?{weight:0,same:!1}:{weight:.05,same:!0}}function ET(e,t){if(e.size===0||t.size===0)return{weight:0,count:0};let s=0;for(let r of t)e.has(r)&&s++;return s===0?{weight:0,count:0}:{weight:Math.min(.4,s*.1),count:s}}function bT(e,t){let s=pu(e),n=pu(t);return s&&n&&s===n?{weight:.1,brand:s}:{weight:0,brand:null}}function mu(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function ST(e,t){let s=0,n=null,r=!1;if(e.authored_paths.size>0){let o=t.recent_user_messages.slice(0,3).join(`
1724
- `).toLowerCase();for(let i of e.authored_paths){let a=i.toLowerCase();if(t.touched_files.has(i)||o.includes(a)){s+=.5,n=i;break}}}if(e.authored_content.length>0&&t.recent_user_messages[0]){let o=mu(t.recent_user_messages[0]);if(o.length>=200)for(let i of e.authored_content){let a=mu(i);if(a.length<200)continue;let d=Math.min(a.length,240),l=a.slice(0,d);if(o.includes(l)){s+=.4,r=!0;break}}}return{weight:Math.min(.6,s),pathMatch:n,contentMatch:r}}function yT(e,t,s=gu){if(t.started_at_ms<=e.started_at_ms)return null;let n=e.ended_at_ms??e.started_at_ms;if(t.started_at_ms<n)return null;let r=mT(n,t.started_at_ms),o=t.recent_user_messages.length>0?t.recent_user_messages:t.first_user_message?[t.first_user_message]:[],i=gT(o),a=ET(e.touched_files,t.touched_files),d=bT(e.auto_title,t.auto_title),l=_T(e,t),u=hT(e,t),p=ST(e,t),m=r.weight+i.weight+a.weight+d.weight+l.weight+u.weight+p.weight;if(m<s)return null;let g=[];if(r.label&&g.push(`temporal ${r.label} (+${r.weight})`),i.matched){let f=i.matchedIndex===0?"opening message":`message #${i.matchedIndex+1}`;g.push(`continuation phrase "${i.matched}" in ${f} (+${i.weight})`)}if(a.count>0&&g.push(`${a.count} file${a.count===1?"":"s"} overlap (+${a.weight.toFixed(1)})`),d.brand&&g.push(`shared brand "${d.brand}" (+${d.weight})`),l.weight>0&&l.mode&&g.push(`semantic ${l.mode==="asymmetric"?"tail\u2192head":l.mode==="max_pool"?"best-chunk":"mean"} ${l.cosine.toFixed(2)} (+${l.weight.toFixed(2)})`),u.same&&g.push(`same cluster (+${u.weight})`),p.weight>0){let f=[];p.pathMatch&&f.push(`opened authored path "${p.pathMatch.split("/").pop()}"`),p.contentMatch&&f.push("verbatim-paste of authored content"),g.push(`doc-authorship: ${f.join(" + ")} (+${p.weight.toFixed(2)})`)}return{parent_id:e.id,child_id:t.id,confidence:Math.min(1,m),signals:{temporal:r.weight,continuation:i.weight,file_overlap:a.weight,same_brand:d.weight,semantic:l.weight,cluster:u.weight,doc_authorship:p.weight},reasons:g}}function fu(e,t=gu){let s=[];for(let n=0;n<e.length;n++){let r=e[n],o=null;for(let i=0;i<n;i++){let a=e[i],d=yT(a,r,t);d&&(!o||d.confidence>o.confidence)&&(o=d)}o&&s.push(o)}return s}function _u(e,t){let s=new Map,n=a=>{let d=a;for(;s.get(d)!==d;)d=s.get(d);let l=a;for(;s.get(l)!==d;){let u=s.get(l);s.set(l,d),l=u}return d},r=(a,d)=>{let l=n(a),u=n(d);l!==u&&s.set(l,u)};for(let a of e)s.has(a.parent_id)||s.set(a.parent_id,a.parent_id),s.has(a.child_id)||s.set(a.child_id,a.child_id),r(a.parent_id,a.child_id);let o=new Map;for(let a of s.keys()){let d=n(a),l=o.get(d);l||(l=[],o.set(d,l)),l.push(a)}let i=new Map;for(let a of t)i.set(a.id,a.started_at_ms);return Array.from(o.values()).map(a=>(a.sort((d,l)=>(i.get(d)??0)-(i.get(l)??0)),{rootId:a[0],sessionIds:a}))}function hu(e,t={}){let s=t.maxUserMessages??5,n=t.userMessageMaxLen??2e3,r=new Set,o=[],i=new Set,a=[],d;try{if(lT(e).size>dT)return{touched_files:r,recent_user_messages:o,authored_paths:i,authored_content:a};d=cT(e,"utf8")}catch{return{touched_files:r,recent_user_messages:o,authored_paths:i,authored_content:a}}let l=0;for(;l<d.length;){let u=d.indexOf(`
1725
- `,l),p=u===-1?d.length:u,m=d.slice(l,p);if(l=u===-1?d.length:u+1,!m.trim())continue;let g;try{g=JSON.parse(m)}catch{continue}let f=g;if(f.type==="user"&&f.message?.role==="user"&&typeof f.message.content=="string"&&o.length<s){let E=f.message.content.trim();E&&o.push(E.length>n?E.slice(0,n):E)}let h=f.message?.content;if(Array.isArray(h))for(let E of h){if(!E||typeof E!="object")continue;let b=E;if(b.type!=="tool_use")continue;let S=b.input??{},R=typeof S.file_path=="string"?S.file_path:null;if(R){let T=Sn(R);T&&r.add(T)}if((b.name==="Write"||b.name==="Edit"||b.name==="MultiEdit")&&R){let T=Sn(R);T&&i.add(T);let L=typeof S.content=="string"?S.content:typeof S.new_string=="string"?S.new_string:null;L&&L.length>=200&&a.push(L.length>4096?L.slice(0,4096):L)}if(b.name==="Bash"&&typeof S.command=="string")for(let T of S.command.matchAll(/(?:^|[\s'"`(=])((?:\.\.?\/|\/|src\/|test\/|docs\/|site\/)[A-Za-z0-9_./-]+)/g)){let L=Sn(T[1]);L&&r.add(L)}if((b.name==="Glob"||b.name==="Grep")&&typeof S.pattern=="string"){let T=Sn(S.pattern);T&&!T.includes("*")&&r.add(T)}}}return{touched_files:r,recent_user_messages:o,authored_paths:i,authored_content:a}}function Sn(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}w();var Eu=10,bu=20;function wT(e){if(!e)return!1;let t=e.split(`
1726
- `);if(t.length<4)return!1;let s=0,n=0;for(let r of t)/^\s*\d{1,5}\t/.test(r)?s++:/^\s*\/[A-Za-z0-9_./-]+/.test(r.trim())&&n++;return s/t.length>.7||n/t.length>.5}function TT(e){let t=e.byteLength/4;if(!Number.isInteger(t)||t<=0)return null;let s=new Float32Array(t),n=new Float32Array(e.buffer,e.byteOffset,t);return s.set(n),s}function Su(e){let t=0;for(let n=0;n<e.length;n++)t+=e[n]*e[n];if(t<=0)return!1;let s=1/Math.sqrt(t);for(let n=0;n<e.length;n++)e[n]*=s;return!0}function $o(e){if(e.length===0)return null;let t=e[0].length,s=new Float32Array(t);for(let n of e)if(n.length===t)for(let r=0;r<t;r++)s[r]+=n[r];for(let n=0;n<t;n++)s[n]/=e.length;return Su(s)?s:null}function yu(e){let t=new Map;if(e.length===0)return t;let s=_(),n=e.map(()=>"?").join(","),r=[];try{r=s.prepare(`SELECT cm.rowid AS rowid, cm.session_id AS session_id,
1854
+ WHERE s.id IN (${n})`).all(...e);for(let o of r)t.set(o.id,o);return t}function sp(e,t){let n=t.get(e),s=c.accent(X(e));if(!n)return`${s} ${c.dim("(unindexed)")}`;let r=n.alias??n.first_user_message??"",o=n.project_name?c.project(`[${n.project_name}] `):"";return`${s} ${o}${K(r,80)}`}function Ui(e){return e.archived?"archived":e.closed_at?"closed":"open"}function Kt(e){let t=Ui(e);process.stderr.write(c.ok(`\u2713 ${e.name} ${c.dim(`(${X(e.id)})`)} [${t}]
1855
+ `))}function Oe(e,t,n){if(e){process.stdout.write(JSON.stringify(t,null,2)+`
1856
+ `);return}n()}function rp(e){let t=lo({includeArchived:e.archived===!0});Oe(e.json===!0,t,()=>{if(t.length===0){console.log(c.dim("no threads. create one with `recall thread new <name>`."));return}let n=new lx({head:[c.bold("id"),c.bold("name"),c.bold("sessions"),c.bold("origins"),c.bold("status"),c.bold("created")],colWidths:[10,40,10,9,10,16],wordWrap:!0,style:{head:[],border:["grey"]}});for(let s of t)n.push([c.accent(X(s.id)),K(s.name,38),String(s.session_count),String(s.origin_count),Ui(s),c.dim(G(s.created_at))]);console.log(n.toString()),console.log(c.dim(`${t.length} thread${t.length===1?"":"s"}.`+(e.archived?"":" add --archived to include archived.")))})}function ux(e,t){let n=new Set(e.edges.map(a=>a.session_id)),s=new Map,r=[];for(let a of e.edges){let d=a.parent_session_id;if(a.role==="origin"||!d||!n.has(d)){r.push(a);continue}let l=s.get(d)??[];l.push(a),s.set(d,l)}let o=new Set,i=(a,d,l,u)=>{if(o.has(a.session_id))return;o.add(a.session_id);let m=u?"":l?"\u2514\u2500 ":"\u251C\u2500 ",p=a.role==="origin"?c.warn("[origin] "):"";process.stdout.write(`${d}${m}${p}${sp(a.session_id,t)}
1857
+ `);let g=s.get(a.session_id)??[],f=u?"":d+(l?" ":"\u2502 ");g.forEach((h,_)=>{i(h,f,_===g.length-1,!1)})};r.forEach((a,d)=>i(a,"",d===r.length-1,!0));for(let a of e.edges)o.has(a.session_id)||process.stdout.write(`${c.warn("? ")}${sp(a.session_id,t)}
1858
+ `)}function op(e,t){let n=je(e);if(!n){process.exitCode=1;return}let s=$e(n);if(!s){process.stderr.write(`thread not found: ${e}
1859
+ `),process.exitCode=1;return}let r=dx(s.edges.map(o=>o.session_id));Oe(t.json===!0,s,()=>{let o=Ui(s);if(console.log(c.bold(s.name)),console.log(c.dim(`id ${X(s.id)} \xB7 ${o} \xB7 ${s.session_count} session${s.session_count===1?"":"s"} \xB7 ${s.origin_count} origin${s.origin_count===1?"":"s"} \xB7 created ${G(s.created_at)}`)),s.summary&&console.log(s.summary),console.log(),s.edges.length===0){console.log(c.dim("(no sessions linked \u2014 use `recall thread link ...`)"));return}ux(s,r)})}function ip(e,t){let n;if(t.origin){let r=Ot(t.origin);if(!r){process.exitCode=1;return}n=r}let s=vc({name:e,summary:t.summary??null,originSessionId:n});Oe(t.json===!0,s,()=>{Kt(s),process.stderr.write(c.dim(`id ${s.id}
1860
+ `)),n&&process.stderr.write(c.dim(`origin: ${X(n)}
1861
+ `))})}function ap(e,t){let n=je(t.thread);if(!n){process.exitCode=1;return}let s=Ot(e);if(!s){process.exitCode=1;return}let r=null;if(t.parent){let a=Ot(t.parent);if(!a){process.exitCode=1;return}r=a}let o=t.role??(r?"child":"origin"),i=Ic({threadId:n,sessionId:s,parentSessionId:r,role:o});Oe(t.json===!0,i,()=>{process.stderr.write(c.ok(`\u2713 linked ${X(s)} \u2192 ${X(n)} [${o}]
1862
+ `))})}function cp(e,t){let n=je(t.thread);if(!n){process.exitCode=1;return}let s=Ot(e);if(!s){process.exitCode=1;return}let r=Mc(n,s);Oe(t.json===!0,r,()=>{if(r.removed===0){process.stderr.write(c.warn(`(no edge existed for ${X(s)} in ${X(n)})
1863
+ `));return}process.stderr.write(c.ok(`\u2713 unlinked ${X(s)} from ${X(n)}
1864
+ `))})}function lp(e,t){let n=je(t.thread);if(!n){process.exitCode=1;return}let s=Ot(e);if(!s){process.exitCode=1;return}let r=null;if(t.parent&&t.parent.toLowerCase()!=="none"){let i=Ot(t.parent);if(!i){process.exitCode=1;return}r=i}let o=Dc(n,s,r);Oe(t.json===!0,o,()=>{process.stderr.write(c.ok(`\u2713 ${X(s)} in ${X(n)} \u2192 `+(r?`child of ${X(r)}`:"origin")+`
1865
+ `))})}function dp(e,t,n){let s=je(e);if(!s){process.exitCode=1;return}let r=$c(s,t);Oe(n.json===!0,r,()=>Kt(r))}function up(e,t){let n=je(e);if(!n){process.exitCode=1;return}let s=Pc(n);Oe(t.json===!0,s,()=>Kt(s))}function mp(e,t){let n=je(e);if(!n){process.exitCode=1;return}let s=Fc(n);Oe(t.json===!0,s,()=>Kt(s))}function pp(e,t){let n=je(e);if(!n){process.exitCode=1;return}let s=jc(n);Oe(t.json===!0,s,()=>Kt(s))}function gp(e,t){let n=je(e);if(!n){process.exitCode=1;return}let s=je(t.into);if(!s){process.exitCode=1;return}if(n===s){process.stderr.write(c.err(`cannot merge a thread into itself
1866
+ `)),process.exitCode=1;return}let r=Uc(n,s);Oe(t.json===!0,r,()=>{process.stderr.write(c.ok(`\u2713 merged ${X(n)} \u2192 ${X(s)} (${r.session_count} sessions)
1867
+ `))})}function fp(e,t){let n=je(e);if(!n){process.exitCode=1;return}let s=t.sessions.split(",").map(i=>i.trim()).filter(Boolean);if(s.length===0){process.stderr.write(c.err(`--sessions must be a non-empty comma-separated list
1868
+ `)),process.exitCode=1;return}let r=[];for(let i of s){let a=Ot(i);if(!a){process.exitCode=1;return}r.push(a)}let o=Bc({threadId:n,sessionIds:r,newThreadName:t.name});Oe(t.json===!0,o,()=>{Kt(o),process.stderr.write(c.dim(`split off ${r.length} session${r.length===1?"":"s"} from ${X(n)}
1869
+ `))})}R();$();import{randomUUID as Mx}from"node:crypto";import{readFileSync as mx,statSync as px}from"node:fs";var gx=200*1024*1024,Hi=.7,Wi=.5,Ep=Wi,fx=[{maxGapMs:3600*1e3,weight:.7,label:"<1h gap"},{maxGapMs:14400*1e3,weight:.4,label:"<4h gap"},{maxGapMs:1440*60*1e3,weight:.2,label:"<24h gap"}],_x=["let's commit","lets commit","now commit","inspect the diff","inspect this diff","review the diff","review this diff","diff of all changes","diff of changes","based on what we just did","based on the things done","based on all the things","continue from","continuing from","pick up where","next step","now fix","now lets","now let's","from the previous session","from our last session","from the last session"];function _p(e){if(!e)return null;if(e.startsWith("/")){let n=e.split(" \xB7 ");if(n.length>1)return n[1].trim().toLowerCase()}let t=e.split(" \xB7 ");return t.length>1?t[t.length-1].trim().toLowerCase():null}function hx(e,t){let n=t-e;if(n<0)return{weight:0,label:null};for(let s of fx)if(n<=s.maxGapMs)return{weight:s.weight,label:s.label};return{weight:0,label:null}}function Ex(e){if(e.length===0)return{weight:0,matched:null,matchedIndex:-1};let t=[.4,.35,.3,.25,.2,.15,.1],n=Math.min(e.length,t.length);for(let s=0;s<n;s++){let r=e[s];if(!r)continue;let o=r.toLowerCase();for(let i of _x)if(o.includes(i))return{weight:t[s],matched:i,matchedIndex:s}}return{weight:0,matched:null,matchedIndex:-1}}function Bi(e,t){if(!e||!t||e.length!==t.length)return 0;let n=0;for(let s=0;s<e.length;s++)n+=e[s]*t[s];return n<-1?-1:n>1?1:n}function bx(e,t){if(e.length===0||t.length===0)return 0;let n=0;for(let s of e)for(let r of t){let o=Bi(s,r);o>n&&(n=o)}return n}function Sx(e,t){let n=Bi(e.mean_embedding,t.mean_embedding),s=Bi(e.tail_pool,t.head_pool),r=bx(e.sample_chunks,t.sample_chunks),o=0,i=null;if(n>o&&(o=n,i="mean"),s>o&&(o=s,i="asymmetric"),r>o&&(o=r,i="max_pool"),o<.65)return{weight:0,cosine:o,mode:null};if(o>=.85)return{weight:.3,cosine:o,mode:i};let a=(o-.65)/.2*.3;return{weight:Math.round(a*100)/100,cosine:o,mode:i}}function wx(e,t){return e.cluster_id===null||t.cluster_id===null?{weight:0,same:!1}:e.cluster_id!==t.cluster_id?{weight:0,same:!1}:{weight:.05,same:!0}}function yx(e,t){if(e.size===0||t.size===0)return{weight:0,count:0};let n=0;for(let r of t)e.has(r)&&n++;return n===0?{weight:0,count:0}:{weight:Math.min(.4,n*.1),count:n}}function Tx(e,t){let n=_p(e),s=_p(t);return n&&s&&n===s?{weight:.1,brand:n}:{weight:0,brand:null}}function hp(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function Rx(e,t){let n=0,s=null,r=!1;if(e.authored_paths.size>0){let o=t.recent_user_messages.slice(0,3).join(`
1870
+ `).toLowerCase();for(let i of e.authored_paths){let a=i.toLowerCase();if(t.touched_files.has(i)||o.includes(a)){n+=.5,s=i;break}}}if(e.authored_content.length>0&&t.recent_user_messages[0]){let o=hp(t.recent_user_messages[0]);if(o.length>=200)for(let i of e.authored_content){let a=hp(i);if(a.length<200)continue;let d=Math.min(a.length,240),l=a.slice(0,d);if(o.includes(l)){n+=.4,r=!0;break}}}return{weight:Math.min(.6,n),pathMatch:s,contentMatch:r}}function kx(e,t,n=Ep){if(t.started_at_ms<=e.started_at_ms)return null;let s=e.ended_at_ms??e.started_at_ms;if(t.started_at_ms<s)return null;let r=hx(s,t.started_at_ms),o=t.recent_user_messages.length>0?t.recent_user_messages:t.first_user_message?[t.first_user_message]:[],i=Ex(o),a=yx(e.touched_files,t.touched_files),d=Tx(e.auto_title,t.auto_title),l=Sx(e,t),u=wx(e,t),m=Rx(e,t),p=r.weight+i.weight+a.weight+d.weight+l.weight+u.weight+m.weight;if(p<n)return null;let g=[];if(r.label&&g.push(`temporal ${r.label} (+${r.weight})`),i.matched){let f=i.matchedIndex===0?"opening message":`message #${i.matchedIndex+1}`;g.push(`continuation phrase "${i.matched}" in ${f} (+${i.weight})`)}if(a.count>0&&g.push(`${a.count} file${a.count===1?"":"s"} overlap (+${a.weight.toFixed(1)})`),d.brand&&g.push(`shared brand "${d.brand}" (+${d.weight})`),l.weight>0&&l.mode&&g.push(`semantic ${l.mode==="asymmetric"?"tail\u2192head":l.mode==="max_pool"?"best-chunk":"mean"} ${l.cosine.toFixed(2)} (+${l.weight.toFixed(2)})`),u.same&&g.push(`same cluster (+${u.weight})`),m.weight>0){let f=[];m.pathMatch&&f.push(`opened authored path "${m.pathMatch.split("/").pop()}"`),m.contentMatch&&f.push("verbatim-paste of authored content"),g.push(`doc-authorship: ${f.join(" + ")} (+${m.weight.toFixed(2)})`)}return{parent_id:e.id,child_id:t.id,confidence:Math.min(1,p),signals:{temporal:r.weight,continuation:i.weight,file_overlap:a.weight,same_brand:d.weight,semantic:l.weight,cluster:u.weight,doc_authorship:m.weight},reasons:g}}function bp(e,t=Ep){let n=[];for(let s=0;s<e.length;s++){let r=e[s],o=null;for(let i=0;i<s;i++){let a=e[i],d=kx(a,r,t);d&&(!o||d.confidence>o.confidence)&&(o=d)}o&&n.push(o)}return n}function Sp(e,t){let n=new Map,s=a=>{let d=a;for(;n.get(d)!==d;)d=n.get(d);let l=a;for(;n.get(l)!==d;){let u=n.get(l);n.set(l,d),l=u}return d},r=(a,d)=>{let l=s(a),u=s(d);l!==u&&n.set(l,u)};for(let a of e)n.has(a.parent_id)||n.set(a.parent_id,a.parent_id),n.has(a.child_id)||n.set(a.child_id,a.child_id),r(a.parent_id,a.child_id);let o=new Map;for(let a of n.keys()){let d=s(a),l=o.get(d);l||(l=[],o.set(d,l)),l.push(a)}let i=new Map;for(let a of t)i.set(a.id,a.started_at_ms);return Array.from(o.values()).map(a=>(a.sort((d,l)=>(i.get(d)??0)-(i.get(l)??0)),{rootId:a[0],sessionIds:a}))}function wp(e,t={}){let n=t.maxUserMessages??5,s=t.userMessageMaxLen??2e3,r=new Set,o=[],i=new Set,a=[],d;try{if(px(e).size>gx)return{touched_files:r,recent_user_messages:o,authored_paths:i,authored_content:a};d=mx(e,"utf8")}catch{return{touched_files:r,recent_user_messages:o,authored_paths:i,authored_content:a}}let l=0;for(;l<d.length;){let u=d.indexOf(`
1871
+ `,l),m=u===-1?d.length:u,p=d.slice(l,m);if(l=u===-1?d.length:u+1,!p.trim())continue;let g;try{g=JSON.parse(p)}catch{continue}let f=g;if(f.type==="user"&&f.message?.role==="user"&&typeof f.message.content=="string"&&o.length<n){let _=f.message.content.trim();_&&o.push(_.length>s?_.slice(0,s):_)}let h=f.message?.content;if(Array.isArray(h))for(let _ of h){if(!_||typeof _!="object")continue;let b=_;if(b.type!=="tool_use")continue;let S=b.input??{},w=typeof S.file_path=="string"?S.file_path:null;if(w){let y=lr(w);y&&r.add(y)}if((b.name==="Write"||b.name==="Edit"||b.name==="MultiEdit")&&w){let y=lr(w);y&&i.add(y);let T=typeof S.content=="string"?S.content:typeof S.new_string=="string"?S.new_string:null;T&&T.length>=200&&a.push(T.length>4096?T.slice(0,4096):T)}if(b.name==="Bash"&&typeof S.command=="string")for(let y of S.command.matchAll(/(?:^|[\s'"`(=])((?:\.\.?\/|\/|src\/|test\/|docs\/|site\/)[A-Za-z0-9_./-]+)/g)){let T=lr(y[1]);T&&r.add(T)}if((b.name==="Glob"||b.name==="Grep")&&typeof S.pattern=="string"){let y=lr(S.pattern);y&&!y.includes("*")&&r.add(y)}}}return{touched_files:r,recent_user_messages:o,authored_paths:i,authored_content:a}}function lr(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}R();var yp=10,Tp=20;function xx(e){if(!e)return!1;let t=e.split(`
1872
+ `);if(t.length<4)return!1;let n=0,s=0;for(let r of t)/^\s*\d{1,5}\t/.test(r)?n++:/^\s*\/[A-Za-z0-9_./-]+/.test(r.trim())&&s++;return n/t.length>.7||s/t.length>.5}function Cx(e){let t=e.byteLength/4;if(!Number.isInteger(t)||t<=0)return null;let n=new Float32Array(t),s=new Float32Array(e.buffer,e.byteOffset,t);return n.set(s),n}function Rp(e){let t=0;for(let s=0;s<e.length;s++)t+=e[s]*e[s];if(t<=0)return!1;let n=1/Math.sqrt(t);for(let s=0;s<e.length;s++)e[s]*=n;return!0}function Xi(e){if(e.length===0)return null;let t=e[0].length,n=new Float32Array(t);for(let s of e)if(s.length===t)for(let r=0;r<t;r++)n[r]+=s[r];for(let s=0;s<t;s++)n[s]/=e.length;return Rp(n)?n:null}function kp(e){let t=new Map;if(e.length===0)return t;let n=E(),s=e.map(()=>"?").join(","),r=[];try{r=n.prepare(`SELECT cm.rowid AS rowid, cm.session_id AS session_id,
1727
1873
  cm.text AS text, v.embedding AS embedding
1728
1874
  FROM chunk_meta cm
1729
1875
  JOIN vec_chunks v ON v.rowid = cm.rowid
1730
1876
  WHERE cm.stale = 0
1731
- AND cm.session_id IN (${n})
1732
- ORDER BY cm.session_id, cm.rowid ASC`).all(...e)}catch{return t}if(r.length===0)return t;let o=new Map;for(let i of r){let a=o.get(i.session_id);a||(a=[],o.set(i.session_id,a)),a.push(i)}for(let[i,a]of o){let d=a.filter(T=>!wT(T.text)),l=d.length>0?d:a,u=[];for(let T of l){let L=TT(T.embedding);L&&Su(L)&&u.push(L)}if(u.length===0)continue;let p=Math.min(Eu,u.length),m=Math.max(0,u.length-Eu),g=u.slice(0,p),f=u.slice(m),h=$o(g),E=$o(f),b=$o(u),S=new Map;for(let T=0;T<g.length&&S.size<bu;T++)S.set(T,g[T]);for(let T=0;T<f.length&&S.size<bu;T++)S.set(m+T,f[T]);let R=Array.from(S.values());t.set(i,{session_id:i,full_mean:b,head_pool:h,tail_pool:E,sample_chunks:R})}return t}function RT(e,t){if(e.length!==t.length)return 0;let s=0;for(let n=0;n<e.length;n++)s+=e[n]*t[n];return s<-1?-1:s>1?1:s}function wu(e,t=.8){let s=new Map,n=[],r=[];for(let[l,u]of e)u.full_mean&&(n.push(l),r.push(u.full_mean));if(n.length===0)return s;let o=new Int32Array(n.length);for(let l=0;l<o.length;l++)o[l]=l;let i=l=>{let u=l;for(;o[u]!==u;)u=o[u];let p=l;for(;o[p]!==u;){let m=o[p];o[p]=u,p=m}return u},a=(l,u)=>{let p=i(l),m=i(u);p!==m&&(o[p]=m)};for(let l=0;l<n.length;l++)for(let u=l+1;u<n.length;u++)RT(r[l],r[u])>=t&&a(l,u);let d=new Map;for(let l=0;l<n.length;l++){let u=i(l),p=d.get(u);p===void 0&&(p=d.size,d.set(u,p)),s.set(n[l],p)}return s}var ds={lo:.4,hi:.7},yn="claude-haiku-4-5-20251001",Tu=50;var xT={same_workflow:.15,unrelated:-.2,unsure:0};function Ru(e,t,s=ds){if(s.lo>s.hi)throw new Error(`borderline band invalid: lo=${s.lo} > hi=${s.hi}`);let n=[];for(let r of e){if(r.confidence<s.lo||r.confidence>s.hi)continue;let o=t.get(r.parent_id),i=t.get(r.child_id);!o||!i||n.push({parent:o,child:i,step1:r})}return n.sort((r,o)=>r.child.started_at_ms-o.child.started_at_ms),n}function kT(e,t){let s=e.replace(/\s+/g," ").trim();return s.length>t?s.slice(0,t-1)+"\u2026":s}function CT(e,t){if(e===null)return"unknown";let s=t-e;if(s<0)return"overlap";let n=Math.round(s/6e4);if(n<60)return`${n}m`;let r=Math.round(s/36e5);return r<24?`${r}h`:`${Math.round(s/864e5)}d`}function LT(e){let s=e.parent.recent_user_messages,n=e.child.recent_user_messages,r=s.slice(0,3),o=s.length>3?s.slice(-3):[],i=n.slice(0,3),a=l=>l.length===0?" (none captured)":l.map(u=>` - ${kT(u,500)}`).join(`
1733
- `),d=CT(e.parent.ended_at_ms??e.parent.started_at_ms,e.child.started_at_ms);return["You are evaluating whether two Claude Code sessions belong to the same continuous workflow.","","A deterministic scorer rated this pair in the borderline band. Its signals:",e.step1.reasons.length===0?" (no signals fired strongly)":e.step1.reasons.map(l=>` - ${l}`).join(`
1877
+ AND cm.session_id IN (${s})
1878
+ ORDER BY cm.session_id, cm.rowid ASC`).all(...e)}catch{return t}if(r.length===0)return t;let o=new Map;for(let i of r){let a=o.get(i.session_id);a||(a=[],o.set(i.session_id,a)),a.push(i)}for(let[i,a]of o){let d=a.filter(y=>!xx(y.text)),l=d.length>0?d:a,u=[];for(let y of l){let T=Cx(y.embedding);T&&Rp(T)&&u.push(T)}if(u.length===0)continue;let m=Math.min(yp,u.length),p=Math.max(0,u.length-yp),g=u.slice(0,m),f=u.slice(p),h=Xi(g),_=Xi(f),b=Xi(u),S=new Map;for(let y=0;y<g.length&&S.size<Tp;y++)S.set(y,g[y]);for(let y=0;y<f.length&&S.size<Tp;y++)S.set(p+y,f[y]);let w=Array.from(S.values());t.set(i,{session_id:i,full_mean:b,head_pool:h,tail_pool:_,sample_chunks:w})}return t}function Ax(e,t){if(e.length!==t.length)return 0;let n=0;for(let s=0;s<e.length;s++)n+=e[s]*t[s];return n<-1?-1:n>1?1:n}function xp(e,t=.8){let n=new Map,s=[],r=[];for(let[l,u]of e)u.full_mean&&(s.push(l),r.push(u.full_mean));if(s.length===0)return n;let o=new Int32Array(s.length);for(let l=0;l<o.length;l++)o[l]=l;let i=l=>{let u=l;for(;o[u]!==u;)u=o[u];let m=l;for(;o[m]!==u;){let p=o[m];o[m]=u,m=p}return u},a=(l,u)=>{let m=i(l),p=i(u);m!==p&&(o[m]=p)};for(let l=0;l<s.length;l++)for(let u=l+1;u<s.length;u++)Ax(r[l],r[u])>=t&&a(l,u);let d=new Map;for(let l=0;l<s.length;l++){let u=i(l),m=d.get(u);m===void 0&&(m=d.size,d.set(u,m)),n.set(s[l],m)}return n}var On={lo:.4,hi:.7},dr="claude-haiku-4-5-20251001",Cp=50;var Lx={same_workflow:.15,unrelated:-.2,unsure:0};function Ap(e,t,n=On){if(n.lo>n.hi)throw new Error(`borderline band invalid: lo=${n.lo} > hi=${n.hi}`);let s=[];for(let r of e){if(r.confidence<n.lo||r.confidence>n.hi)continue;let o=t.get(r.parent_id),i=t.get(r.child_id);!o||!i||s.push({parent:o,child:i,step1:r})}return s.sort((r,o)=>r.child.started_at_ms-o.child.started_at_ms),s}function Nx(e,t){let n=e.replace(/\s+/g," ").trim();return n.length>t?n.slice(0,t-1)+"\u2026":n}function Ox(e,t){if(e===null)return"unknown";let n=t-e;if(n<0)return"overlap";let s=Math.round(n/6e4);if(s<60)return`${s}m`;let r=Math.round(n/36e5);return r<24?`${r}h`:`${Math.round(n/864e5)}d`}function vx(e){let n=e.parent.recent_user_messages,s=e.child.recent_user_messages,r=n.slice(0,3),o=n.length>3?n.slice(-3):[],i=s.slice(0,3),a=l=>l.length===0?" (none captured)":l.map(u=>` - ${Nx(u,500)}`).join(`
1879
+ `),d=Ox(e.parent.ended_at_ms??e.parent.started_at_ms,e.child.started_at_ms);return["You are evaluating whether two Claude Code sessions belong to the same continuous workflow.","","A deterministic scorer rated this pair in the borderline band. Its signals:",e.step1.reasons.length===0?" (no signals fired strongly)":e.step1.reasons.map(l=>` - ${l}`).join(`
1734
1880
  `),` step1_confidence: ${e.step1.confidence.toFixed(2)}`,"","PARENT SESSION","First user messages:",a(r),"Last user messages:",a(o),"",`CHILD SESSION (gap from parent: ${d})`,"First user messages:",a(i),"","Reply with EXACTLY one JSON object on a single line, no prose, no markdown:",'{"verdict":"same_workflow"|"unrelated"|"unsure","reason":"<one short sentence>"}',"","Guidance:",'- "same_workflow" = the child is clearly continuing what the parent was doing.','- "unrelated" = different problem, different files, different intent.','- "unsure" = could go either way; do not force a verdict.'].join(`
1735
- `)}function NT(e){if(!e)return null;let t=e.trim();if(!t)return null;let s=t;try{let d=JSON.parse(t);typeof d.result=="string"&&(s=d.result.trim())}catch{}let n=s.match(/\{[\s\S]*\}/);if(!n)return null;let r;try{r=JSON.parse(n[0])}catch{return null}if(!r||typeof r!="object")return null;let o=r,i=typeof o.verdict=="string"?o.verdict.toLowerCase():"";if(i!=="same_workflow"&&i!=="unrelated"&&i!=="unsure")return null;let a=typeof o.reason=="string"&&o.reason.trim()?o.reason.trim():"";return{verdict:i,reason:a,delta:xT[i]}}async function xu(e,t={}){if(t.signal?.aborted)return null;let s=t.model??yn,n=LT(e),r=t.spawn;if(!r)try{let i=await Promise.resolve().then(()=>(Be(),Us));if(!i.isClaudeCliAvailable())return null;r=(a,d)=>i.spawnClaudePrompt(a,[],d)}catch{return null}let o;try{o=await Promise.race([r(n,{model:s}),new Promise(i=>{t.signal&&t.signal.addEventListener("abort",()=>i({success:!1,stdout:""}),{once:!0})})])}catch{return null}return!o.success||!o.stdout?null:NT(o.stdout)}function ku(e){let t=[];for(let s of e.proposals){let n=`${s.parent_id}::${s.child_id}`,r=e.rescored.get(n);if(!r){t.push(s);continue}let o=Math.max(0,Math.min(1,s.confidence+r.delta));if(o<e.applyThreshold)continue;let i=`LLM: ${r.verdict}${r.reason?` \u2014 ${r.reason}`:""} (${r.delta>=0?"+":""}${r.delta.toFixed(2)})`;t.push({...s,confidence:o,reasons:[...s.reasons,i]})}return t}function Cu(e){return`${e.parent_id}::${e.child_id}`}async function OT(e){if(e.signal?.aborted)return null;let t,s;try{({spawnClaudePrompt:t,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(Be(),Us)))}catch{return null}if(!s())return null;let n=[];for(let o of e.sessionIds){let i=e.rowById.get(o);if(!i)continue;let a=i.alias||i.auto_title||(i.first_user_message?i.first_user_message.slice(0,120).replace(/\n/g," "):"(no title)");n.push(`- ${a}`)}let r=`Below is a chronological list of related Claude Code sessions that form one continuous workflow. Generate a single short descriptive name for the workflow as a whole. Requirements:
1881
+ `)}function Ix(e){if(!e)return null;let t=e.trim();if(!t)return null;let n=t;try{let d=JSON.parse(t);typeof d.result=="string"&&(n=d.result.trim())}catch{}let s=n.match(/\{[\s\S]*\}/);if(!s)return null;let r;try{r=JSON.parse(s[0])}catch{return null}if(!r||typeof r!="object")return null;let o=r,i=typeof o.verdict=="string"?o.verdict.toLowerCase():"";if(i!=="same_workflow"&&i!=="unrelated"&&i!=="unsure")return null;let a=typeof o.reason=="string"&&o.reason.trim()?o.reason.trim():"";return{verdict:i,reason:a,delta:Lx[i]}}async function Lp(e,t={}){if(t.signal?.aborted)return null;let n=t.model??dr,s=vx(e),r=t.spawn;if(!r)try{let i=await Promise.resolve().then(()=>(Ye(),cs));if(!i.isClaudeCliAvailable())return null;r=(a,d)=>i.spawnClaudePrompt(a,[],d)}catch{return null}let o;try{o=await Promise.race([r(s,{model:n}),new Promise(i=>{t.signal&&t.signal.addEventListener("abort",()=>i({success:!1,stdout:""}),{once:!0})})])}catch{return null}return!o.success||!o.stdout?null:Ix(o.stdout)}function Np(e){let t=[];for(let n of e.proposals){let s=`${n.parent_id}::${n.child_id}`,r=e.rescored.get(s);if(!r){t.push(n);continue}let o=Math.max(0,Math.min(1,n.confidence+r.delta));if(o<e.applyThreshold)continue;let i=`LLM: ${r.verdict}${r.reason?` \u2014 ${r.reason}`:""} (${r.delta>=0?"+":""}${r.delta.toFixed(2)})`;t.push({...n,confidence:o,reasons:[...n.reasons,i]})}return t}function Op(e){return`${e.parent_id}::${e.child_id}`}async function Dx(e){if(e.signal?.aborted)return null;let t,n;try{({spawnClaudePrompt:t,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(Ye(),cs)))}catch{return null}if(!n())return null;let s=[];for(let o of e.sessionIds){let i=e.rowById.get(o);if(!i)continue;let a=i.alias||i.auto_title||(i.first_user_message?i.first_user_message.slice(0,120).replace(/\n/g," "):"(no title)");s.push(`- ${a}`)}let r=`Below is a chronological list of related Claude Code sessions that form one continuous workflow. Generate a single short descriptive name for the workflow as a whole. Requirements:
1736
1882
  - 4 to 8 words
1737
1883
  - Title-case, no trailing punctuation
1738
1884
  - Capture the WORKFLOW (e.g., "Update Remotion deps + lint cleanup"), not any one session
@@ -1740,51 +1886,51 @@ Toggle with: recall verify on | off
1740
1886
  - No markdown
1741
1887
 
1742
1888
  Sessions:
1743
- `+n.join(`
1889
+ `+s.join(`
1744
1890
  `)+`
1745
1891
 
1746
- Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model:e.model}:{}),i=null,a=new Promise(p=>{e.signal&&(i=()=>p(null),e.signal.addEventListener("abort",i,{once:!0}))}),d=await Promise.race([o,a]);if(i&&e.signal&&e.signal.removeEventListener("abort",i),!d||!d.success)return null;let l=d.stdout.trim();if(!l)return null;let u;try{let p=JSON.parse(l);u=typeof p.result=="string"?p.result:l}catch{u=l}return u=u.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/[.!?]+$/g,"").trim(),u?u.length>80?u.slice(0,77)+"...":u:null}catch{return null}}function vT(e){let t=new Map;for(let o of e){let i=t.get(o.project);i||(i=[],t.set(o.project,i)),i.push(o)}let s=e.map(o=>o.id),n=new Map;try{n=yu(s)}catch{n=new Map}let r=new Map;for(let[o,i]of t){let a=new Map;for(let u of i){let p=n.get(u.id);p&&a.set(u.id,p)}let d=wu(a,.8),l=[];for(let u of i){let p=MT(u,n,d);p&&l.push(p)}r.set(o,l)}return{byProject:t,scannablesByProject:r}}function IT(e){let t=_(),s={},n="1=1 AND s.message_count > 2";return n+=" AND COALESCE(s.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(s.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(s.auto_title, '') NOT LIKE '[skill]%'",e.project&&(n+=" AND p.name = @project",s.project=e.project),t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1892
+ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model:e.model}:{}),i=null,a=new Promise(m=>{e.signal&&(i=()=>m(null),e.signal.addEventListener("abort",i,{once:!0}))}),d=await Promise.race([o,a]);if(i&&e.signal&&e.signal.removeEventListener("abort",i),!d||!d.success)return null;let l=d.stdout.trim();if(!l)return null;let u;try{let m=JSON.parse(l);u=typeof m.result=="string"?m.result:l}catch{u=l}return u=u.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/[.!?]+$/g,"").trim(),u?u.length>80?u.slice(0,77)+"...":u:null}catch{return null}}function $x(e){let t=new Map;for(let o of e){let i=t.get(o.project);i||(i=[],t.set(o.project,i)),i.push(o)}let n=e.map(o=>o.id),s=new Map;try{s=kp(n)}catch{s=new Map}let r=new Map;for(let[o,i]of t){let a=new Map;for(let u of i){let m=s.get(u.id);m&&a.set(u.id,m)}let d=xp(a,.8),l=[];for(let u of i){let m=Fx(u,s,d);m&&l.push(m)}r.set(o,l)}return{byProject:t,scannablesByProject:r}}function Px(e){let t=E(),n={},s="1=1 AND s.message_count > 2";return s+=" AND COALESCE(s.auto_title, '') NOT LIKE '[meta]%' AND COALESCE(s.auto_title, '') NOT LIKE '[output-index]%' AND COALESCE(s.auto_title, '') NOT LIKE '[skill]%'",e.project&&(s+=" AND p.name = @project",n.project=e.project),t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
1747
1893
  s.first_user_message, s.auto_title,
1748
1894
  NULLIF(sa.alias, '') AS alias,
1749
1895
  s.file_path
1750
1896
  FROM sessions s
1751
1897
  JOIN projects p ON p.id = s.project_id
1752
1898
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1753
- WHERE ${n}
1754
- ORDER BY p.name ASC, s.started_at ASC`).all(s)}function MT(e,t,s){if(!e.started_at)return null;let n=Date.parse(e.started_at);if(!Number.isFinite(n))return null;let r=e.ended_at?Date.parse(e.ended_at):null,o=hu(e.file_path,{maxUserMessages:7}),i=t?.get(e.id);return{id:e.id,started_at_ms:n,ended_at_ms:Number.isFinite(r)?r:null,first_user_message:e.first_user_message,recent_user_messages:o.recent_user_messages,auto_title:e.auto_title,touched_files:o.touched_files,mean_embedding:i?.full_mean??null,head_pool:i?.head_pool??null,tail_pool:i?.tail_pool??null,sample_chunks:i?.sample_chunks??[],cluster_id:s?.get(e.id)??null,authored_paths:o.authored_paths,authored_content:o.authored_content}}function wn(e){let t=e.id.slice(0,8),s=e.alias||e.auto_title||(e.first_user_message?e.first_user_message.slice(0,50):"(no title)");return`${t} ${s}`}function Lu(e){if(!e)return"?";let t=new Date(e);if(Number.isNaN(t.getTime()))return"?";let s=n=>String(n).padStart(2,"0");return`${t.getFullYear()}-${s(t.getMonth()+1)}-${s(t.getDate())} ${s(t.getHours())}:${s(t.getMinutes())}`}function Nu(e){let t=e.alias||e.auto_title||(e.first_user_message?e.first_user_message.slice(0,60).replace(/\n/g," ").trim():`thread from ${e.id.slice(0,8)}`);return t.length>80?t.slice(0,77)+"...":t}async function DT(e){if(!e.rescore.enabled)return{proposals:e.proposals,ran:!1,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1};let t=e.rescore.band??ds,s=e.rescore.cap??Tu,n=e.rescore.model??yn,r=new Map(e.scannables.map(m=>[m.id,m])),o=Ru(e.proposals,r,t);if(o.length===0)return{proposals:e.proposals,ran:!0,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1};if(o.length>s)return{proposals:e.proposals,ran:!1,considered:o.length,promoted:0,demoted:0,unsure:0,failed:0,capped:!0};let i=new Map,a=0,d=0,l=0,u=0;for(let m=0;m<o.length&&!e.signal?.aborted;m++){let g=o[m],f=await xu(g,{model:n,signal:e.signal});f?(i.set(Cu(g.step1),f),f.verdict==="same_workflow"?a++:f.verdict==="unrelated"?d++:l++):u++,e.onProgress?.({phase:"rescoring",current:m+1,total:o.length,verdict:f?.verdict??"failed"})}return{proposals:ku({proposals:e.proposals,rescored:i,applyThreshold:e.applyThreshold}),ran:!0,considered:o.length,promoted:a,demoted:d,unsure:l,failed:u,capped:!1}}async function $T(e){let t=_(),s=new Map(e.rows.map(l=>[l.id,l])),n=_u(e.edges,e.scannables),r=new Date().toISOString(),o=[],i=new Map,a=0;for(let l of n){if(a++,e.signal?.aborted)break;let u=s.get(l.rootId),p=Nu(u);if(!e.llmNames){i.set(l.rootId,p),e.onProgress?.({phase:"naming",current:a,total:n.length,thread_name:p});continue}let g=await OT({rootRow:u,sessionIds:l.sessionIds,rowById:s,signal:e.signal,model:e.model})??p;i.set(l.rootId,g),e.onProgress?.({phase:"naming",current:a,total:n.length,thread_name:g})}return t.transaction(()=>{for(let l of n){if(!i.has(l.rootId))continue;let u=s.get(l.rootId),p=`auto-scan-${AT()}`,m=i.get(l.rootId)??Nu(u),g=`Auto-detected workflow chain (${l.sessionIds.length} sessions). Source: auto-scan-v1.`;t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(p,m,g,r),t.prepare(`INSERT INTO thread_edges
1899
+ WHERE ${s}
1900
+ ORDER BY p.name ASC, s.started_at ASC`).all(n)}function Fx(e,t,n){if(!e.started_at)return null;let s=Date.parse(e.started_at);if(!Number.isFinite(s))return null;let r=e.ended_at?Date.parse(e.ended_at):null,o=wp(e.file_path,{maxUserMessages:7}),i=t?.get(e.id);return{id:e.id,started_at_ms:s,ended_at_ms:Number.isFinite(r)?r:null,first_user_message:e.first_user_message,recent_user_messages:o.recent_user_messages,auto_title:e.auto_title,touched_files:o.touched_files,mean_embedding:i?.full_mean??null,head_pool:i?.head_pool??null,tail_pool:i?.tail_pool??null,sample_chunks:i?.sample_chunks??[],cluster_id:n?.get(e.id)??null,authored_paths:o.authored_paths,authored_content:o.authored_content}}function ur(e){let t=e.id.slice(0,8),n=e.alias||e.auto_title||(e.first_user_message?e.first_user_message.slice(0,50):"(no title)");return`${t} ${n}`}function vp(e){if(!e)return"?";let t=new Date(e);if(Number.isNaN(t.getTime()))return"?";let n=s=>String(s).padStart(2,"0");return`${t.getFullYear()}-${n(t.getMonth()+1)}-${n(t.getDate())} ${n(t.getHours())}:${n(t.getMinutes())}`}function Ip(e){let t=e.alias||e.auto_title||(e.first_user_message?e.first_user_message.slice(0,60).replace(/\n/g," ").trim():`thread from ${e.id.slice(0,8)}`);return t.length>80?t.slice(0,77)+"...":t}async function jx(e){if(!e.rescore.enabled)return{proposals:e.proposals,ran:!1,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1};let t=e.rescore.band??On,n=e.rescore.cap??Cp,s=e.rescore.model??dr,r=new Map(e.scannables.map(p=>[p.id,p])),o=Ap(e.proposals,r,t);if(o.length===0)return{proposals:e.proposals,ran:!0,considered:0,promoted:0,demoted:0,unsure:0,failed:0,capped:!1};if(o.length>n)return{proposals:e.proposals,ran:!1,considered:o.length,promoted:0,demoted:0,unsure:0,failed:0,capped:!0};let i=new Map,a=0,d=0,l=0,u=0;for(let p=0;p<o.length&&!e.signal?.aborted;p++){let g=o[p],f=await Lp(g,{model:s,signal:e.signal});f?(i.set(Op(g.step1),f),f.verdict==="same_workflow"?a++:f.verdict==="unrelated"?d++:l++):u++,e.onProgress?.({phase:"rescoring",current:p+1,total:o.length,verdict:f?.verdict??"failed"})}return{proposals:Np({proposals:e.proposals,rescored:i,applyThreshold:e.applyThreshold}),ran:!0,considered:o.length,promoted:a,demoted:d,unsure:l,failed:u,capped:!1}}async function Ux(e){let t=E(),n=new Map(e.rows.map(l=>[l.id,l])),s=Sp(e.edges,e.scannables),r=new Date().toISOString(),o=[],i=new Map,a=0;for(let l of s){if(a++,e.signal?.aborted)break;let u=n.get(l.rootId),m=Ip(u);if(!e.llmNames){i.set(l.rootId,m),e.onProgress?.({phase:"naming",current:a,total:s.length,thread_name:m});continue}let g=await Dx({rootRow:u,sessionIds:l.sessionIds,rowById:n,signal:e.signal,model:e.model})??m;i.set(l.rootId,g),e.onProgress?.({phase:"naming",current:a,total:s.length,thread_name:g})}return t.transaction(()=>{for(let l of s){if(!i.has(l.rootId))continue;let u=n.get(l.rootId),m=`auto-scan-${Mx()}`,p=i.get(l.rootId)??Ip(u),g=`Auto-detected workflow chain (${l.sessionIds.length} sessions). Source: auto-scan-v1.`;t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(m,p,g,r),t.prepare(`INSERT INTO thread_edges
1755
1901
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1756
- VALUES (?, ?, NULL, 'origin', 1.0, 'auto-scan-v1', ?)`).run(p,l.rootId,r);let f=new Map;for(let h of e.edges)l.sessionIds.includes(h.child_id)&&f.set(h.child_id,h);for(let h of l.sessionIds){if(h===l.rootId)continue;let E=f.get(h);E&&t.prepare(`INSERT INTO thread_edges
1902
+ VALUES (?, ?, NULL, 'origin', 1.0, 'auto-scan-v1', ?)`).run(m,l.rootId,r);let f=new Map;for(let h of e.edges)l.sessionIds.includes(h.child_id)&&f.set(h.child_id,h);for(let h of l.sessionIds){if(h===l.rootId)continue;let _=f.get(h);_&&t.prepare(`INSERT INTO thread_edges
1757
1903
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
1758
- VALUES (?, ?, ?, 'child', ?, 'auto-scan-v1', ?)`).run(p,h,E.parent_id,E.confidence,r)}o.push({thread_id:p,name:m,session_count:l.sessionIds.length})}})(),{project:e.project,threads:o}}async function Au(e){let t=!!e.apply,s=e.confidenceMin?Number(e.confidenceMin):t?Mo:Do;if(!Number.isFinite(s)||s<0||s>1){console.error(c.err("--confidence-min must be a number in [0, 1]")),process.exitCode=1;return}let n=!!e.llmRescore,r={lo:e.rescoreBandLo?Number(e.rescoreBandLo):ds.lo,hi:e.rescoreBandHi?Number(e.rescoreBandHi):ds.hi};if(n&&(!Number.isFinite(r.lo)||!Number.isFinite(r.hi)||r.lo<0||r.hi>1||r.lo>r.hi)){console.error(c.err("--rescore-band-lo/hi must satisfy 0 \u2264 lo \u2264 hi \u2264 1")),process.exitCode=1;return}let o=e.rescoreModel??yn,i=IT({project:e.project});if(i.length===0){console.log(c.dim("no sessions matched"));return}let{byProject:a,scannablesByProject:d}=vT(i),l=n?Math.min(s,r.lo):s,u=new Map,p=0;for(let[g]of a){let f=d.get(g)??[],h=fu(f,l);if(n&&h.length>0){let E=await DT({proposals:h,scannables:f,applyThreshold:s,rescore:{enabled:!0,band:r,model:o}});h=E.proposals,E.capped?(console.error(c.err(`[${g}] borderline pairs exceeded cap (${E.considered}); skipped LLM rescore. Tighten the band or scan a smaller project.`)),h=h.filter(b=>b.confidence>=s)):n?E.failed===E.considered&&E.considered>0&&(h=h.filter(b=>b.confidence>=s)):h=h.filter(b=>b.confidence>=s)}else n&&(h=h.filter(E=>E.confidence>=s));u.set(g,{proposals:h,scannables:f}),p+=h.length}if(t){if(p===0){console.log(c.dim("no edges above threshold; nothing to apply"));return}let g={threads_created:0,edges_written:0,per_project:[]},f=!e.noLlmNames;for(let[h,{proposals:E,scannables:b}]of u){if(E.length===0)continue;let S=await $T({project:h,rows:a.get(h),edges:E,scannables:b,llmNames:f});g.per_project.push(S),g.threads_created+=S.threads.length,g.edges_written+=E.length+S.threads.length}if(e.json){console.log(JSON.stringify({mode:"apply",threshold:s,...g},null,2));return}console.log(c.ok(`Applied ${g.threads_created} thread${g.threads_created===1?"":"s"} with ${g.edges_written} edges total (threshold ${s}).`));for(let h of g.per_project){console.log(` ${c.bold(h.project)}`);for(let E of h.threads)console.log(` ${c.dim(E.thread_id.slice(0,16)+"\u2026")} ${E.name} ${c.dim(`(${E.session_count} sessions)`)}`)}console.log(),console.log(c.dim("Rollback: sqlite3 ~/.recall/db.sqlite \\")),console.log(c.dim(` "DELETE FROM thread_edges WHERE source='auto-scan-v1';`)),console.log(c.dim(` DELETE FROM threads WHERE id LIKE 'auto-scan-%';"`));return}if(e.json){let g=[];for(let[f,{proposals:h}]of u){let E=new Map(a.get(f).map(b=>[b.id,b]));for(let b of h)g.push({project:f,parent_id:b.parent_id,parent_label:wn(E.get(b.parent_id)),child_id:b.child_id,child_label:wn(E.get(b.child_id)),confidence:b.confidence,signals:b.signals,reasons:b.reasons})}console.log(JSON.stringify({mode:"dry-run",threshold:s,total:p,edges:g},null,2));return}console.log(c.dim(`recall thread scan \xB7 ${a.size} project${a.size===1?"":"s"} \xB7 DRY RUN \xB7 threshold ${s}`)),console.log(c.dim(" Algorithm: temporal + continuation phrase + file overlap + same brand.")),console.log();for(let[g,f]of a){let{proposals:h}=u.get(g),E=new Map(f.map(S=>[S.id,S]));if(console.log(c.bold(g)),console.log(c.dim(` ${f.length} eligible session${f.length===1?"":"s"}; ${h.length} parent-child edge${h.length===1?"":"s"} proposed.`)),h.length===0){console.log(c.dim(" (no edges above threshold \u2014 all sessions are origins)")),console.log();continue}let b=[...h].sort((S,R)=>{let T=E.get(S.child_id),L=E.get(R.child_id);return(T.started_at??"").localeCompare(L.started_at??"")});for(let S of b){let R=E.get(S.parent_id),T=E.get(S.child_id),L=S.confidence.toFixed(2);console.log(` ${c.dim(Lu(R.started_at).padEnd(16))} ${c.bold("PARENT")} ${wn(R)}`),console.log(` ${c.dim(Lu(T.started_at).padEnd(16))} ${c.bold(" \u2514\u2500 child")} ${wn(T)} ${c.dim(`[conf ${L}]`)}`),console.log(` ${" ".repeat(28)}${c.dim(S.reasons.join(" \xB7 "))}`),console.log()}}let m=Array.from(a.entries()).reduce((g,[f,h])=>g+(h.length-(u.get(f)?.proposals.length??0)),0);console.log(`${p} edge${p===1?"":"s"} proposed across ${a.size} project${a.size===1?"":"s"}; ${m} origin session${m===1?"":"s"} (no proposed parent).`),console.log(c.dim(` This is a DRY RUN. To write: rerun with --apply (default threshold ${Mo}).`))}v();P();import{existsSync as PT,readFileSync as FT}from"node:fs";function jT(){let e=`${x}/daemon.port`;if(!PT(e))return null;try{let t=FT(e,"utf8").trim();return/^\d+$/.test(t)?t:null}catch{return null}}async function UT(e){try{return(await ze(`http://127.0.0.1:${e}/api/health`,{signal:AbortSignal.timeout(1e4)})).ok}catch{return!1}}async function BT(e){let t=await ze(`http://127.0.0.1:${e}/api/projects`);if(!t.ok)throw new Error(`failed to list projects: HTTP ${t.status}`);return await t.json()}function HT(e,t){let s=t.replace(/\/+$/,""),n=null,r=-1;for(let o of e){if(!o.decoded_path)continue;let i=o.decoded_path.replace(/\/+$/,"");(s===i||s.startsWith(i+"/"))&&i.length>r&&(n=o,r=i.length)}return n}function WT(e,t){let s=e.find(r=>r.name===t);if(s)return s;let n=e.filter(r=>r.name.toLowerCase().includes(t.toLowerCase()));return n.length===1?n[0]:null}function Ou(e){return e.alias||e.auto_title||(e.first_user_message?K(e.first_user_message,60):"(no title)")}function XT(e){if(!e)return"?";let t=new Date(e);if(Number.isNaN(t.getTime()))return"?";let s=n=>String(n).padStart(2,"0");return`${t.getFullYear()}-${s(t.getMonth()+1)}-${s(t.getDate())} ${s(t.getHours())}:${s(t.getMinutes())}`}function JT(e,t){let s=t==="preflight"?c.dim(`recall threads sync \xB7 ${e.project.name} \xB7 PREFLIGHT (no writes)`):c.dim(`recall threads sync \xB7 ${e.project.name}`);if(console.log(s),console.log(),e.thread.exists?console.log(` ${c.bold("Thread")} ${e.thread.name} ${c.dim(`(reusing, ${e.thread.existing_session_count} existing session${e.thread.existing_session_count===1?"":"s"})`)}`):console.log(` ${c.bold("Thread")} ${e.thread.name} ${c.ok("(new)")}`),console.log(),e.candidates.length===0){if(console.log(c.dim(" No active sessions in the rolling window.")),e.warnings.length>0)for(let l of e.warnings)console.log(c.warn(` \u26A0 ${l}`));return}let n=new Map;for(let l of e.proposed_edges)n.set(l.child_id,l);let r=new Map;for(let l of e.preserved_manual_edges)r.set(l.session_id,l.parent_session_id);let o=new Set(e.candidates.map(l=>l.session_id)),i=e.proposed_additions.length,a=e.candidates.length-i;console.log(` ${c.bold("Candidates")} ${e.candidates.length} active session${e.candidates.length===1?"":"s"} ${c.dim(`(${i} new \xB7 ${a} keep)`)}`);let d=String(e.candidates.length).length;for(let l=0;l<e.candidates.length;l++){let u=e.candidates[l],m=e.proposed_additions.some(S=>S.session_id===u.session_id)?c.ok("NEW "):c.dim("keep"),g=n.get(u.session_id)??(r.has(u.session_id)&&r.get(u.session_id)!==null,null),f=g&&o.has(g.parent_id)?" \u2514\u2500 ":" ",h=XT(u.started_at),E=c.dim(`${String(l+1).padStart(d," ")}.`);console.log(` ${E} ${m} ${c.dim(h.padEnd(16))} ${f}${c.dim(u.session_id.slice(0,8))} ${Ou(u)}`);let b=" ".repeat(d+2);if(g&&o.has(g.parent_id)){let S=g.confidence.toFixed(2),R=Ou(e.candidates.find(T=>T.session_id===g.parent_id));console.log(` ${b}${" ".repeat(28)} parent ${c.dim(g.parent_id.slice(0,8))} (${R}) ${c.dim(`[conf ${S}]`)}`),console.log(` ${b}${" ".repeat(28)} ${c.dim(g.reasons.join(" \xB7 "))}`)}if(r.has(u.session_id)){let S=r.get(u.session_id);S&&console.log(` ${b}${" ".repeat(28)} ${c.bold("manual parent preserved")} ${c.dim(S.slice(0,8))}`)}}if(e.preserved_manual_edges.length>0&&(console.log(),console.log(c.dim(` ${e.preserved_manual_edges.length} manual edge${e.preserved_manual_edges.length===1?"":"s"} will be preserved.`))),e.warnings.length>0){console.log();for(let l of e.warnings)console.log(c.warn(` \u26A0 ${l}`))}}function GT(e){console.log(),console.log(c.ok(`Added ${e.added} session${e.added===1?"":"s"}, set ${e.edges_set} parent edge${e.edges_set===1?"":"s"}, preserved ${e.preserved_manual} manual edge${e.preserved_manual===1?"":"s"}.`)),console.log(c.dim(` thread_id: ${e.thread_id}`))}async function vu(e){let t=jT();if(!t){console.error(c.err("Daemon is not running. Start it with `recall start`, then re-run this command.")),process.exitCode=1;return}if(!await UT(t)){console.error(c.err(`Daemon on port ${t} is not responding. Try \`recall stop && recall start\`.`)),process.exitCode=1;return}let n;try{n=await BT(t)}catch(l){console.error(c.err(l.message)),process.exitCode=1;return}let r;if(e.project){if(r=WT(n,e.project),!r){console.error(c.err(`No project matching "${e.project}". Run \`recall projects\` to list known projects.`)),process.exitCode=1;return}}else{let l=process.cwd();if(r=HT(n,l),!r){console.error(c.err(`Current directory (${l}) does not match any known project. Pass --project <name> or cd into a project root.`)),process.exitCode=1;return}}let o=e.windowHours?Number(e.windowHours):void 0;if(o!==void 0&&(!Number.isFinite(o)||o<=0)){console.error(c.err("--window-hours must be a positive number")),process.exitCode=1;return}let i=e.preflight?"preflight":"apply",a={project_id:r.id,mode:i};o!==void 0&&(a.window_hours=o);let d;try{let l=await st("POST",`http://127.0.0.1:${t}/api/threads/sync-active`,a);if(!l.ok){let u=await l.text().catch(()=>"");throw new Error(`HTTP ${l.status}: ${u.slice(0,200)}`)}d=await l.json()}catch(l){console.error(c.err(`Sync failed: ${l.message}`)),process.exitCode=1;return}if(e.json){console.log(JSON.stringify({mode:i,...d},null,2));return}JT(d.plan,i),i==="apply"&&d.result?GT(d.result):i==="preflight"&&(console.log(),console.log(c.dim(" This is a PREFLIGHT. To write: re-run without --preflight.")))}w();import{writeFileSync as fR,existsSync as _R,mkdirSync as hR}from"node:fs";import{join as Vu}from"node:path";import{homedir as ER}from"node:os";import{execSync as Xt}from"node:child_process";Ke();import uR from"satori";import pR from"sharp";import{readFileSync as Bo}from"node:fs";import{join as On}from"node:path";var Ho=On(ee(),"dist","share","fonts"),zu=!1,qu=[];function mR(){return zu||(qu=[{name:"Inter",data:Bo(On(Ho,"Inter-Regular.woff")),weight:400,style:"normal"},{name:"Inter",data:Bo(On(Ho,"Inter-Bold.woff")),weight:700,style:"normal"},{name:"JetBrains Mono",data:Bo(On(Ho,"JetBrainsMono-Regular.woff")),weight:400,style:"normal"}],zu=!0),qu}async function gR(e){switch(e){case"A":return await Promise.resolve().then(()=>($u(),Du));case"B":return await Promise.resolve().then(()=>(ju(),Fu));case"C":return await Promise.resolve().then(()=>(Hu(),Bu));case"D":return await Promise.resolve().then(()=>(Ju(),Xu));case"E":return await Promise.resolve().then(()=>(Yu(),Gu))}}async function vn(e,t){let n=(await gR(e)).render(t),i=await uR(n,{width:1080,height:e==="E"?1920:1350,fonts:mR()});return await pR(Buffer.from(i)).png({quality:90}).toBuffer()}function Ku(e,t){let s=new URLSearchParams({v:"1",s:t,t:e.sessionTitle,d:e.sessionDate.slice(0,10),tk:String(e.tokenCount),m:String(e.messageCount)});return typeof e.inputTokens=="number"&&s.set("i",String(e.inputTokens)),typeof e.outputTokens=="number"&&s.set("o",String(e.outputTokens)),e.verdict&&s.set("q",e.verdict),`https://clauderecall.com/card?${s.toString()}`}function bR(e,t){if(t.length>=32)return e.prepare("SELECT id FROM sessions WHERE id = ?").get(t)?.id??null;let s=e.prepare("SELECT session_id FROM session_aliases WHERE alias = ?").get(t);if(s)return s.session_id;let n=e.prepare("SELECT id FROM sessions WHERE id LIKE ? LIMIT 2").all(t+"%");return n.length===1?n[0].id:(n.length===0||process.stderr.write(`ambiguous id prefix "${t}". be more specific.
1759
- `),null)}function SR(e){return e.prepare("SELECT session_id FROM recall_events ORDER BY recalled_at DESC LIMIT 1").get()?.session_id??null}function yR(e,t){let s=e.prepare(`
1904
+ VALUES (?, ?, ?, 'child', ?, 'auto-scan-v1', ?)`).run(m,h,_.parent_id,_.confidence,r)}o.push({thread_id:m,name:p,session_count:l.sessionIds.length})}})(),{project:e.project,threads:o}}async function Mp(e){let t=!!e.apply,n=e.confidenceMin?Number(e.confidenceMin):t?Hi:Wi;if(!Number.isFinite(n)||n<0||n>1){console.error(c.err("--confidence-min must be a number in [0, 1]")),process.exitCode=1;return}let s=!!e.llmRescore,r={lo:e.rescoreBandLo?Number(e.rescoreBandLo):On.lo,hi:e.rescoreBandHi?Number(e.rescoreBandHi):On.hi};if(s&&(!Number.isFinite(r.lo)||!Number.isFinite(r.hi)||r.lo<0||r.hi>1||r.lo>r.hi)){console.error(c.err("--rescore-band-lo/hi must satisfy 0 \u2264 lo \u2264 hi \u2264 1")),process.exitCode=1;return}let o=e.rescoreModel??dr,i=Px({project:e.project});if(i.length===0){console.log(c.dim("no sessions matched"));return}let{byProject:a,scannablesByProject:d}=$x(i),l=s?Math.min(n,r.lo):n,u=new Map,m=0;for(let[g]of a){let f=d.get(g)??[],h=bp(f,l);if(s&&h.length>0){let _=await jx({proposals:h,scannables:f,applyThreshold:n,rescore:{enabled:!0,band:r,model:o}});h=_.proposals,_.capped?(console.error(c.err(`[${g}] borderline pairs exceeded cap (${_.considered}); skipped LLM rescore. Tighten the band or scan a smaller project.`)),h=h.filter(b=>b.confidence>=n)):s?_.failed===_.considered&&_.considered>0&&(h=h.filter(b=>b.confidence>=n)):h=h.filter(b=>b.confidence>=n)}else s&&(h=h.filter(_=>_.confidence>=n));u.set(g,{proposals:h,scannables:f}),m+=h.length}if(t){if(m===0){console.log(c.dim("no edges above threshold; nothing to apply"));return}let g={threads_created:0,edges_written:0,per_project:[]},f=!e.noLlmNames;for(let[h,{proposals:_,scannables:b}]of u){if(_.length===0)continue;let S=await Ux({project:h,rows:a.get(h),edges:_,scannables:b,llmNames:f});g.per_project.push(S),g.threads_created+=S.threads.length,g.edges_written+=_.length+S.threads.length}if(e.json){console.log(JSON.stringify({mode:"apply",threshold:n,...g},null,2));return}console.log(c.ok(`Applied ${g.threads_created} thread${g.threads_created===1?"":"s"} with ${g.edges_written} edges total (threshold ${n}).`));for(let h of g.per_project){console.log(` ${c.bold(h.project)}`);for(let _ of h.threads)console.log(` ${c.dim(_.thread_id.slice(0,16)+"\u2026")} ${_.name} ${c.dim(`(${_.session_count} sessions)`)}`)}console.log(),console.log(c.dim("Rollback: sqlite3 ~/.recall/db.sqlite \\")),console.log(c.dim(` "DELETE FROM thread_edges WHERE source='auto-scan-v1';`)),console.log(c.dim(` DELETE FROM threads WHERE id LIKE 'auto-scan-%';"`));return}if(e.json){let g=[];for(let[f,{proposals:h}]of u){let _=new Map(a.get(f).map(b=>[b.id,b]));for(let b of h)g.push({project:f,parent_id:b.parent_id,parent_label:ur(_.get(b.parent_id)),child_id:b.child_id,child_label:ur(_.get(b.child_id)),confidence:b.confidence,signals:b.signals,reasons:b.reasons})}console.log(JSON.stringify({mode:"dry-run",threshold:n,total:m,edges:g},null,2));return}console.log(c.dim(`recall thread scan \xB7 ${a.size} project${a.size===1?"":"s"} \xB7 DRY RUN \xB7 threshold ${n}`)),console.log(c.dim(" Algorithm: temporal + continuation phrase + file overlap + same brand.")),console.log();for(let[g,f]of a){let{proposals:h}=u.get(g),_=new Map(f.map(S=>[S.id,S]));if(console.log(c.bold(g)),console.log(c.dim(` ${f.length} eligible session${f.length===1?"":"s"}; ${h.length} parent-child edge${h.length===1?"":"s"} proposed.`)),h.length===0){console.log(c.dim(" (no edges above threshold \u2014 all sessions are origins)")),console.log();continue}let b=[...h].sort((S,w)=>{let y=_.get(S.child_id),T=_.get(w.child_id);return(y.started_at??"").localeCompare(T.started_at??"")});for(let S of b){let w=_.get(S.parent_id),y=_.get(S.child_id),T=S.confidence.toFixed(2);console.log(` ${c.dim(vp(w.started_at).padEnd(16))} ${c.bold("PARENT")} ${ur(w)}`),console.log(` ${c.dim(vp(y.started_at).padEnd(16))} ${c.bold(" \u2514\u2500 child")} ${ur(y)} ${c.dim(`[conf ${T}]`)}`),console.log(` ${" ".repeat(28)}${c.dim(S.reasons.join(" \xB7 "))}`),console.log()}}let p=Array.from(a.entries()).reduce((g,[f,h])=>g+(h.length-(u.get(f)?.proposals.length??0)),0);console.log(`${m} edge${m===1?"":"s"} proposed across ${a.size} project${a.size===1?"":"s"}; ${p} origin session${p===1?"":"s"} (no proposed parent).`),console.log(c.dim(` This is a DRY RUN. To write: rerun with --apply (default threshold ${Hi}).`))}$();D();import{existsSync as Bx,readFileSync as Hx}from"node:fs";function Wx(){let e=`${x}/daemon.port`;if(!Bx(e))return null;try{let t=Hx(e,"utf8").trim();return/^\d+$/.test(t)?t:null}catch{return null}}async function Xx(e){try{return(await nt(`http://127.0.0.1:${e}/api/health`,{signal:AbortSignal.timeout(1e4)})).ok}catch{return!1}}async function Gx(e){let t=await nt(`http://127.0.0.1:${e}/api/projects`);if(!t.ok)throw new Error(`failed to list projects: HTTP ${t.status}`);return await t.json()}function Jx(e,t){let n=t.replace(/\/+$/,""),s=null,r=-1;for(let o of e){if(!o.decoded_path)continue;let i=o.decoded_path.replace(/\/+$/,"");(n===i||n.startsWith(i+"/"))&&i.length>r&&(s=o,r=i.length)}return s}function zx(e,t){let n=e.find(r=>r.name===t);if(n)return n;let s=e.filter(r=>r.name.toLowerCase().includes(t.toLowerCase()));return s.length===1?s[0]:null}function Dp(e){return e.alias||e.auto_title||(e.first_user_message?K(e.first_user_message,60):"(no title)")}function Yx(e){if(!e)return"?";let t=new Date(e);if(Number.isNaN(t.getTime()))return"?";let n=s=>String(s).padStart(2,"0");return`${t.getFullYear()}-${n(t.getMonth()+1)}-${n(t.getDate())} ${n(t.getHours())}:${n(t.getMinutes())}`}function qx(e,t){let n=t==="preflight"?c.dim(`recall threads sync \xB7 ${e.project.name} \xB7 PREFLIGHT (no writes)`):c.dim(`recall threads sync \xB7 ${e.project.name}`);if(console.log(n),console.log(),e.thread.exists?console.log(` ${c.bold("Thread")} ${e.thread.name} ${c.dim(`(reusing, ${e.thread.existing_session_count} existing session${e.thread.existing_session_count===1?"":"s"})`)}`):console.log(` ${c.bold("Thread")} ${e.thread.name} ${c.ok("(new)")}`),console.log(),e.candidates.length===0){if(console.log(c.dim(" No active sessions in the rolling window.")),e.warnings.length>0)for(let l of e.warnings)console.log(c.warn(` \u26A0 ${l}`));return}let s=new Map;for(let l of e.proposed_edges)s.set(l.child_id,l);let r=new Map;for(let l of e.preserved_manual_edges)r.set(l.session_id,l.parent_session_id);let o=new Set(e.candidates.map(l=>l.session_id)),i=e.proposed_additions.length,a=e.candidates.length-i;console.log(` ${c.bold("Candidates")} ${e.candidates.length} active session${e.candidates.length===1?"":"s"} ${c.dim(`(${i} new \xB7 ${a} keep)`)}`);let d=String(e.candidates.length).length;for(let l=0;l<e.candidates.length;l++){let u=e.candidates[l],p=e.proposed_additions.some(S=>S.session_id===u.session_id)?c.ok("NEW "):c.dim("keep"),g=s.get(u.session_id)??(r.has(u.session_id)&&r.get(u.session_id)!==null,null),f=g&&o.has(g.parent_id)?" \u2514\u2500 ":" ",h=Yx(u.started_at),_=c.dim(`${String(l+1).padStart(d," ")}.`);console.log(` ${_} ${p} ${c.dim(h.padEnd(16))} ${f}${c.dim(u.session_id.slice(0,8))} ${Dp(u)}`);let b=" ".repeat(d+2);if(g&&o.has(g.parent_id)){let S=g.confidence.toFixed(2),w=Dp(e.candidates.find(y=>y.session_id===g.parent_id));console.log(` ${b}${" ".repeat(28)} parent ${c.dim(g.parent_id.slice(0,8))} (${w}) ${c.dim(`[conf ${S}]`)}`),console.log(` ${b}${" ".repeat(28)} ${c.dim(g.reasons.join(" \xB7 "))}`)}if(r.has(u.session_id)){let S=r.get(u.session_id);S&&console.log(` ${b}${" ".repeat(28)} ${c.bold("manual parent preserved")} ${c.dim(S.slice(0,8))}`)}}if(e.preserved_manual_edges.length>0&&(console.log(),console.log(c.dim(` ${e.preserved_manual_edges.length} manual edge${e.preserved_manual_edges.length===1?"":"s"} will be preserved.`))),e.warnings.length>0){console.log();for(let l of e.warnings)console.log(c.warn(` \u26A0 ${l}`))}}function Vx(e){console.log(),console.log(c.ok(`Added ${e.added} session${e.added===1?"":"s"}, set ${e.edges_set} parent edge${e.edges_set===1?"":"s"}, preserved ${e.preserved_manual} manual edge${e.preserved_manual===1?"":"s"}.`)),console.log(c.dim(` thread_id: ${e.thread_id}`))}async function $p(e){let t=Wx();if(!t){console.error(c.err("Daemon is not running. Start it with `recall start`, then re-run this command.")),process.exitCode=1;return}if(!await Xx(t)){console.error(c.err(`Daemon on port ${t} is not responding. Try \`recall stop && recall start\`.`)),process.exitCode=1;return}let s;try{s=await Gx(t)}catch(l){console.error(c.err(l.message)),process.exitCode=1;return}let r;if(e.project){if(r=zx(s,e.project),!r){console.error(c.err(`No project matching "${e.project}". Run \`recall projects\` to list known projects.`)),process.exitCode=1;return}}else{let l=process.cwd();if(r=Jx(s,l),!r){console.error(c.err(`Current directory (${l}) does not match any known project. Pass --project <name> or cd into a project root.`)),process.exitCode=1;return}}let o=e.windowHours?Number(e.windowHours):void 0;if(o!==void 0&&(!Number.isFinite(o)||o<=0)){console.error(c.err("--window-hours must be a positive number")),process.exitCode=1;return}let i=e.preflight?"preflight":"apply",a={project_id:r.id,mode:i};o!==void 0&&(a.window_hours=o);let d;try{let l=await dt("POST",`http://127.0.0.1:${t}/api/threads/sync-active`,a);if(!l.ok){let u=await l.text().catch(()=>"");throw new Error(`HTTP ${l.status}: ${u.slice(0,200)}`)}d=await l.json()}catch(l){console.error(c.err(`Sync failed: ${l.message}`)),process.exitCode=1;return}if(e.json){console.log(JSON.stringify({mode:i,...d},null,2));return}qx(d.plan,i),i==="apply"&&d.result?Vx(d.result):i==="preflight"&&(console.log(),console.log(c.dim(" This is a PREFLIGHT. To write: re-run without --preflight.")))}R();import{writeFileSync as bC,existsSync as SC,mkdirSync as wC}from"node:fs";import{join as tg}from"node:path";import{homedir as yC}from"node:os";import{execSync as tn}from"node:child_process";Ge();import fC from"satori";import _C from"sharp";import{readFileSync as qi}from"node:fs";import{join as Sr}from"node:path";var Vi=Sr(te(),"dist","share","fonts"),Qp=!1,Zp=[];function hC(){return Qp||(Zp=[{name:"Inter",data:qi(Sr(Vi,"Inter-Regular.woff")),weight:400,style:"normal"},{name:"Inter",data:qi(Sr(Vi,"Inter-Bold.woff")),weight:700,style:"normal"},{name:"JetBrains Mono",data:qi(Sr(Vi,"JetBrainsMono-Regular.woff")),weight:400,style:"normal"}],Qp=!0),Zp}async function EC(e){switch(e){case"A":return await Promise.resolve().then(()=>(Up(),jp));case"B":return await Promise.resolve().then(()=>(Wp(),Hp));case"C":return await Promise.resolve().then(()=>(Jp(),Gp));case"D":return await Promise.resolve().then(()=>(qp(),Yp));case"E":return await Promise.resolve().then(()=>(Kp(),Vp))}}async function wr(e,t){let s=(await EC(e)).render(t),i=await fC(s,{width:1080,height:e==="E"?1920:1350,fonts:hC()});return await _C(Buffer.from(i)).png({quality:90}).toBuffer()}function eg(e,t){let n=new URLSearchParams({v:"1",s:t,t:e.sessionTitle,d:e.sessionDate.slice(0,10),tk:String(e.tokenCount),m:String(e.messageCount)});return typeof e.inputTokens=="number"&&n.set("i",String(e.inputTokens)),typeof e.outputTokens=="number"&&n.set("o",String(e.outputTokens)),e.verdict&&n.set("q",e.verdict),`https://clauderecall.com/card?${n.toString()}`}function TC(e,t){if(t.length>=32)return e.prepare("SELECT id FROM sessions WHERE id = ?").get(t)?.id??null;let n=e.prepare("SELECT session_id FROM session_aliases WHERE alias = ?").get(t);if(n)return n.session_id;let s=e.prepare("SELECT id FROM sessions WHERE id LIKE ? LIMIT 2").all(t+"%");return s.length===1?s[0].id:(s.length===0||process.stderr.write(`ambiguous id prefix "${t}". be more specific.
1905
+ `),null)}function RC(e){return e.prepare("SELECT session_id FROM recall_events ORDER BY recalled_at DESC LIMIT 1").get()?.session_id??null}function kC(e,t){let n=e.prepare(`
1760
1906
  SELECT s.id, s.started_at, s.message_count, s.first_user_message, s.auto_title,
1761
1907
  s.total_input_tokens, s.total_output_tokens
1762
1908
  FROM sessions s WHERE s.id = ?
1763
- `).get(t);if(!s)return null;let r=e.prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(t)?.alias||s.auto_title||s.first_user_message?.slice(0,80)||s.id.slice(0,8),o=e.prepare(`
1909
+ `).get(t);if(!n)return null;let r=e.prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(t)?.alias||n.auto_title||n.first_user_message?.slice(0,80)||n.id.slice(0,8),o=e.prepare(`
1764
1910
  SELECT tool_names, raw_json FROM messages
1765
1911
  WHERE session_id = ? AND tool_names IS NOT NULL AND tool_names != ''
1766
- `).all(t),i=o.length,a=new Set;for(let u of o){if(!/Read|Write|Edit/.test(u.tool_names))continue;let p=u.raw_json.match(/"(?:file_path|path)":\s*"([^"]+)"/g);if(p)for(let m of p){let g=m.match(/":\s*"([^"]+)"/);g&&a.add(g[1])}}let d=s.total_input_tokens??0,l=s.total_output_tokens??0;return{sessionTitle:r,sessionDate:s.started_at??new Date().toISOString(),recallDate:new Date().toISOString(),tokenCount:d+l,inputTokens:d,outputTokens:l,messageCount:s.message_count,filesReferenced:a.size,toolCallCount:i,verdict:""}}function wR(e){process.platform==="darwin"?Xt(`osascript -e 'set the clipboard to (read (POSIX file "${e}") as \xABclass PNGf\xBB)'`):process.platform==="linux"&&Xt(`xclip -selection clipboard -t image/png -i "${e}"`)}function TR(e){try{process.platform==="darwin"?Xt(`open "${e}"`):process.platform==="linux"?Xt(`xdg-open "${e}"`):process.platform==="win32"&&Xt(`start "" "${e}"`)}catch{}}async function Zu(e,t){let s=_(),n;if(e){if(n=bR(s,e),!n){process.stderr.write(`session not found: ${e}
1767
- `),process.exitCode=1;return}}else if(n=SR(s),!n){process.stderr.write("No recalled sessions yet. Run `recall context <id>` first, then `recall share`.\n"),process.exitCode=1;return}let r=yR(s,n);if(!r){process.stderr.write(`failed to load metadata for session ${n}
1768
- `),process.exitCode=1;return}let o=t.style&&["A","B","C","D"].includes(t.style.toUpperCase())?t.style.toUpperCase():"A";t.verdict&&(r.verdict=t.verdict);let i=await vn(o,r),a=n.slice(0,8),d=t.out??Vu(ER(),"Downloads");_R(d)||hR(d,{recursive:!0});let l=Vu(d,`recall-card-${a}.png`);if(fR(l,i),process.stderr.write(`Card saved to ${l}
1769
- `),t.clipboard&&(wR(l),process.stderr.write(`Copied PNG to clipboard.
1770
- `)),t.link){let u=Ku(r,o);process.stdout.write(u+`
1771
- `),process.platform==="darwin"&&(Xt("pbcopy",{input:u}),process.stderr.write(`Link copied to clipboard.
1772
- `))}t.open!==!1&&TR(l)}w();import{createInterface as RR}from"node:readline";import{writeFileSync as xR,existsSync as kR,mkdirSync as CR}from"node:fs";import{join as ep}from"node:path";import{homedir as LR}from"node:os";function Qu(e){return e.recallCount>=15&&e.uniqueSessionsRecalled>=10?"Context Architect":e.nightSessionRatio>.5?"Night Owl":e.sessionCount>30&&e.avgMessageCount<50?"Rapid Shipper":e.sessionCount<15&&e.avgMessageCount>200?"Deep Diver":e.debugTagRatio>.4?"Debugger":"Power User"}var Ct=["january","february","march","april","may","june","july","august","september","october","november","december"],NR=["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"];function AR(e){let t=new Date;if(!e){let i=t.getFullYear(),a=t.getMonth();return{start:`${i}-${String(a+1).padStart(2,"0")}-01`,end:a===11?`${i+1}-01-01`:`${i}-${String(a+2).padStart(2,"0")}-01`,label:`${Ct[a][0].toUpperCase()}${Ct[a].slice(1)} ${i}`}}let s=e.match(/^(\d{4})-(\d{2})$/);if(s){let i=parseInt(s[1],10),a=parseInt(s[2],10)-1;return a<0||a>11?null:{start:`${i}-${String(a+1).padStart(2,"0")}-01`,end:a===11?`${i+1}-01-01`:`${i}-${String(a+2).padStart(2,"0")}-01`,label:`${Ct[a][0].toUpperCase()}${Ct[a].slice(1)} ${i}`}}let n=Ct.indexOf(e.toLowerCase()),r=n===-1?NR.indexOf(e.toLowerCase()):-1,o=n!==-1?n:r;if(o!==-1){let i=t.getFullYear();return{start:`${i}-${String(o+1).padStart(2,"0")}-01`,end:o===11?`${i+1}-01-01`:`${i}-${String(o+2).padStart(2,"0")}-01`,label:`${Ct[o][0].toUpperCase()}${Ct[o].slice(1)} ${i}`}}return null}function OR(e){if(e.length===0)return"N/A";let t=r=>r===0?"12am":r<12?`${r}am`:r===12?"12pm":`${r-12}pm`,s=Math.min(...e),n=Math.max(...e);return`${t(s)}-${t(n+1>23?0:n+1)}`}async function tp(e,t){let s=AR(e);if(!s){process.stderr.write(`invalid month format: ${e}. Use YYYY-MM, month name, or abbreviation.
1773
- `),process.exitCode=1;return}let n=_(),r=n.prepare("SELECT COUNT(*) AS c FROM recall_events WHERE recalled_at >= ? AND recalled_at < ?").get(s.start,s.end).c,o=n.prepare("SELECT COALESCE(SUM(token_count), 0) AS s FROM recall_events WHERE recalled_at >= ? AND recalled_at < ?").get(s.start,s.end).s,i=n.prepare("SELECT COUNT(*) AS c FROM sessions WHERE indexed_at >= ? AND indexed_at < ?").get(s.start,s.end).c,a=n.prepare(`SELECT session_id, COUNT(*) AS c FROM recall_events
1912
+ `).all(t),i=o.length,a=new Set;for(let u of o){if(!/Read|Write|Edit/.test(u.tool_names))continue;let m=u.raw_json.match(/"(?:file_path|path)":\s*"([^"]+)"/g);if(m)for(let p of m){let g=p.match(/":\s*"([^"]+)"/);g&&a.add(g[1])}}let d=n.total_input_tokens??0,l=n.total_output_tokens??0;return{sessionTitle:r,sessionDate:n.started_at??new Date().toISOString(),recallDate:new Date().toISOString(),tokenCount:d+l,inputTokens:d,outputTokens:l,messageCount:n.message_count,filesReferenced:a.size,toolCallCount:i,verdict:""}}function xC(e){process.platform==="darwin"?tn(`osascript -e 'set the clipboard to (read (POSIX file "${e}") as \xABclass PNGf\xBB)'`):process.platform==="linux"&&tn(`xclip -selection clipboard -t image/png -i "${e}"`)}function CC(e){try{process.platform==="darwin"?tn(`open "${e}"`):process.platform==="linux"?tn(`xdg-open "${e}"`):process.platform==="win32"&&tn(`start "" "${e}"`)}catch{}}async function ng(e,t){let n=E(),s;if(e){if(s=TC(n,e),!s){process.stderr.write(`session not found: ${e}
1913
+ `),process.exitCode=1;return}}else if(s=RC(n),!s){process.stderr.write("No recalled sessions yet. Run `recall context <id>` first, then `recall share`.\n"),process.exitCode=1;return}let r=kC(n,s);if(!r){process.stderr.write(`failed to load metadata for session ${s}
1914
+ `),process.exitCode=1;return}let o=t.style&&["A","B","C","D"].includes(t.style.toUpperCase())?t.style.toUpperCase():"A";t.verdict&&(r.verdict=t.verdict);let i=await wr(o,r),a=s.slice(0,8),d=t.out??tg(yC(),"Downloads");SC(d)||wC(d,{recursive:!0});let l=tg(d,`recall-card-${a}.png`);if(bC(l,i),process.stderr.write(`Card saved to ${l}
1915
+ `),t.clipboard&&(xC(l),process.stderr.write(`Copied PNG to clipboard.
1916
+ `)),t.link){let u=eg(r,o);process.stdout.write(u+`
1917
+ `),process.platform==="darwin"&&(tn("pbcopy",{input:u}),process.stderr.write(`Link copied to clipboard.
1918
+ `))}t.open!==!1&&CC(l)}R();import{createInterface as AC}from"node:readline";import{writeFileSync as LC,existsSync as NC,mkdirSync as OC}from"node:fs";import{join as rg}from"node:path";import{homedir as vC}from"node:os";function sg(e){return e.recallCount>=15&&e.uniqueSessionsRecalled>=10?"Context Architect":e.nightSessionRatio>.5?"Night Owl":e.sessionCount>30&&e.avgMessageCount<50?"Rapid Shipper":e.sessionCount<15&&e.avgMessageCount>200?"Deep Diver":e.debugTagRatio>.4?"Debugger":"Power User"}var Mt=["january","february","march","april","may","june","july","august","september","october","november","december"],IC=["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"];function MC(e){let t=new Date;if(!e){let i=t.getFullYear(),a=t.getMonth();return{start:`${i}-${String(a+1).padStart(2,"0")}-01`,end:a===11?`${i+1}-01-01`:`${i}-${String(a+2).padStart(2,"0")}-01`,label:`${Mt[a][0].toUpperCase()}${Mt[a].slice(1)} ${i}`}}let n=e.match(/^(\d{4})-(\d{2})$/);if(n){let i=parseInt(n[1],10),a=parseInt(n[2],10)-1;return a<0||a>11?null:{start:`${i}-${String(a+1).padStart(2,"0")}-01`,end:a===11?`${i+1}-01-01`:`${i}-${String(a+2).padStart(2,"0")}-01`,label:`${Mt[a][0].toUpperCase()}${Mt[a].slice(1)} ${i}`}}let s=Mt.indexOf(e.toLowerCase()),r=s===-1?IC.indexOf(e.toLowerCase()):-1,o=s!==-1?s:r;if(o!==-1){let i=t.getFullYear();return{start:`${i}-${String(o+1).padStart(2,"0")}-01`,end:o===11?`${i+1}-01-01`:`${i}-${String(o+2).padStart(2,"0")}-01`,label:`${Mt[o][0].toUpperCase()}${Mt[o].slice(1)} ${i}`}}return null}function DC(e){if(e.length===0)return"N/A";let t=r=>r===0?"12am":r<12?`${r}am`:r===12?"12pm":`${r-12}pm`,n=Math.min(...e),s=Math.max(...e);return`${t(n)}-${t(s+1>23?0:s+1)}`}async function og(e,t){let n=MC(e);if(!n){process.stderr.write(`invalid month format: ${e}. Use YYYY-MM, month name, or abbreviation.
1919
+ `),process.exitCode=1;return}let s=E(),r=s.prepare("SELECT COUNT(*) AS c FROM recall_events WHERE recalled_at >= ? AND recalled_at < ?").get(n.start,n.end).c,o=s.prepare("SELECT COALESCE(SUM(token_count), 0) AS s FROM recall_events WHERE recalled_at >= ? AND recalled_at < ?").get(n.start,n.end).s,i=s.prepare("SELECT COUNT(*) AS c FROM sessions WHERE indexed_at >= ? AND indexed_at < ?").get(n.start,n.end).c,a=s.prepare(`SELECT session_id, COUNT(*) AS c FROM recall_events
1774
1920
  WHERE recalled_at >= ? AND recalled_at < ?
1775
- GROUP BY session_id ORDER BY c DESC LIMIT 1`).get(s.start,s.end),d="None";if(a){let U=n.prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(a.session_id),y=n.prepare("SELECT auto_title, first_user_message, id FROM sessions WHERE id = ?").get(a.session_id);d=U?.alias||y?.auto_title||y?.first_user_message?.slice(0,40)||a.session_id.slice(0,8)}let l=n.prepare("SELECT COALESCE(MAX(token_count), 0) AS m FROM recall_events WHERE recalled_at >= ? AND recalled_at < ?").get(s.start,s.end).m,u=n.prepare(`SELECT CAST(strftime('%H', started_at) AS INTEGER) AS h, COUNT(*) AS c
1921
+ GROUP BY session_id ORDER BY c DESC LIMIT 1`).get(n.start,n.end),d="None";if(a){let O=s.prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(a.session_id),k=s.prepare("SELECT auto_title, first_user_message, id FROM sessions WHERE id = ?").get(a.session_id);d=O?.alias||k?.auto_title||k?.first_user_message?.slice(0,40)||a.session_id.slice(0,8)}let l=s.prepare("SELECT COALESCE(MAX(token_count), 0) AS m FROM recall_events WHERE recalled_at >= ? AND recalled_at < ?").get(n.start,n.end).m,u=s.prepare(`SELECT CAST(strftime('%H', started_at) AS INTEGER) AS h, COUNT(*) AS c
1776
1922
  FROM sessions WHERE started_at >= ? AND started_at < ?
1777
- GROUP BY h ORDER BY c DESC LIMIT 3`).all(s.start,s.end),p=OR(u.map(U=>U.h)),m=n.prepare("SELECT COUNT(DISTINCT session_id) AS c FROM recall_events WHERE recalled_at >= ? AND recalled_at < ?").get(s.start,s.end).c,g=n.prepare(`SELECT COUNT(*) AS cnt, COALESCE(AVG(message_count), 0) AS avg_msgs
1778
- FROM sessions WHERE started_at >= ? AND started_at < ?`).get(s.start,s.end),f=n.prepare(`SELECT COUNT(*) AS c FROM sessions
1923
+ GROUP BY h ORDER BY c DESC LIMIT 3`).all(n.start,n.end),m=DC(u.map(O=>O.h)),p=s.prepare("SELECT COUNT(DISTINCT session_id) AS c FROM recall_events WHERE recalled_at >= ? AND recalled_at < ?").get(n.start,n.end).c,g=s.prepare(`SELECT COUNT(*) AS cnt, COALESCE(AVG(message_count), 0) AS avg_msgs
1924
+ FROM sessions WHERE started_at >= ? AND started_at < ?`).get(n.start,n.end),f=s.prepare(`SELECT COUNT(*) AS c FROM sessions
1779
1925
  WHERE started_at >= ? AND started_at < ?
1780
1926
  AND (CAST(strftime('%H', started_at) AS INTEGER) >= 22
1781
- OR CAST(strftime('%H', started_at) AS INTEGER) < 4)`).get(s.start,s.end).c,h=n.prepare(`SELECT COUNT(DISTINCT st.session_id) AS c
1927
+ OR CAST(strftime('%H', started_at) AS INTEGER) < 4)`).get(n.start,n.end).c,h=s.prepare(`SELECT COUNT(DISTINCT st.session_id) AS c
1782
1928
  FROM session_tags st JOIN sessions s ON s.id = st.session_id
1783
1929
  WHERE s.started_at >= ? AND s.started_at < ?
1784
- AND (st.tag LIKE '%debug%' OR st.tag LIKE '%fix%' OR st.tag LIKE '%bug%' OR st.tag LIKE '%error%')`).get(s.start,s.end).c,E={recallCount:r,uniqueSessionsRecalled:m,sessionCount:g.cnt,avgMessageCount:g.avg_msgs,nightSessionRatio:g.cnt>0?f/g.cnt:0,debugTagRatio:g.cnt>0?h/g.cnt:0},b=Qu(E),S=50;try{let{computeAllHealthScores:U}=await Promise.resolve().then(()=>(xo(),Td)),y=U();y.length>0&&(S=Math.round(y.reduce((B,M)=>B+M.score,0)/y.length))}catch{}let R=t.verdict??"";if(!t.verdict){let U=RR({input:process.stdin,output:process.stderr});R=await new Promise(y=>{U.question("Add your take (one line, or press Enter to skip): ",B=>{U.close(),y(B.trim())})})}let T={month:s.label,totalRecalls:r,totalTokensPiped:o,sessionsIndexed:i,mostRecalledSession:d,biggestRecallTokens:l,healthScore:S,mostActiveHours:p,archetype:b,verdict:R},L=await vn("E",T),D=s.start.slice(0,7),A=t.out??ep(LR(),"Downloads");kR(A)||CR(A,{recursive:!0});let $=ep(A,`recall-wrapped-${D}.png`);xR($,L),process.stderr.write(`Wrapped card saved to ${$}
1785
- `)}de();At();Ke();import{createRequire as vR}from"node:module";import{createInterface as IR}from"node:readline";import{stdin as MR,stdout as DR}from"node:process";var $R=vR(import.meta.url),PR=$R(`${ee()}/package.json`).version,rp="https://clauderecall.com/api/feedback",Wo=process.env.RECALL_FEEDBACK_API??rp,sp=Wo===rp,Xo=2e3;function op(e){let t=IR({input:MR,output:DR});return new Promise(s=>t.question(e,n=>{t.close(),s(n.trim())}))}function np(e){if(!/^[+-]?\d+$/.test(e))return null;let t=Number.parseInt(e,10);return!Number.isFinite(t)||t<1||t>5?null:t}async function FR(e){if(e.score!==void 0){let n=np(e.score);return n===null?(console.error(`--score must be an integer from 1 to 5 (got "${e.score}").`),null):n}if(!process.stdin.isTTY)return console.error("feedback needs a TTY to prompt. Pass --score 1..5 (and optionally --message) to send non-interactively."),null;console.log("How is Claude Recall going for you?"),console.log(" 1 = bad 2 = meh 3 = ok 4 = good 5 = great"),console.log("");let t=await op("Score (1-5, q to quit): ");if(t.toLowerCase()==="q"||t==="")return console.log("No worries, skipped."),null;let s=np(t);return s===null?(console.error("Please enter an integer 1 through 5."),null):s}async function jR(e,t){if(e.message!==void 0)return e.message.trim();if(!process.stdin.isTTY)return"";let s=t<=2?"What is the biggest pain point? (Enter to skip): ":"Anything you would add? (Enter to skip): ";return await op(s)}function UR(e){return e.length<=Xo?{value:e,truncated:!1}:{value:e.slice(0,Xo),truncated:!0}}async function ip(e={}){sp||process.stderr.write(`[recall] RECALL_FEEDBACK_API override active (${Wo}); license token will NOT be sent.
1786
- `);let t=await FR(e);if(t===null)return e.score!==void 0||!process.stdin.isTTY?1:0;let s=await jR(e,t),{value:n,truncated:r}=UR(s);r&&process.stderr.write(`[recall] --message truncated to ${Xo} characters before send.
1787
- `);let o=await Te(),i=Kt(),a=sp&&o.tier==="pro"&&i?i.license_jwt:null,d={score:t,comment:n.length>0?n:null,surface:"cli",version:PR,os:process.platform,trigger_kind:"manual",license_jwt:a};try{let l=await fetch(Wo,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(d)});if(l.ok)return console.log(""),console.log("Thanks. Your feedback landed."),0;let u=await l.json().catch(()=>({}));return l.status===429?(console.log("You submitted feedback recently. Try again later."),0):(console.error(`Feedback failed: ${l.status} ${u.error??"unknown"}`),1)}catch(l){let u=l instanceof Error?l.message:"network error";return console.error(`Feedback could not be sent: ${u}`),1}}var j0=P0(import.meta.url),hs=j0("../package.json").version,C=new F0;C.name("recall").description("Searchable memory for every Claude Code session you have ever run. Local, fast, offline. Run `recall` (no args) for the dashboard, or `recall --help` for the full command list.").version(hs);C.command("index").description("Scan your Claude sessions and build the searchable database. Run this once after install.").option("-f, --force","reindex all files even if unchanged").option("-v, --verbose","show each file as it is processed").action(async e=>{await Ci(e)});C.command("list").description("List your sessions, newest first.").option("-p, --project <name>","filter by project name").option("-n, --limit <n>","max rows to show","30").option("-a, --all","include short sessions (2 messages or fewer)").action(e=>{Li(e)});C.command("show <id>").description("Print a full session transcript. Accepts the full id or an 8-char prefix.").option("-r, --raw","print raw JSONL lines instead of formatted output").option("-n, --limit <n>","max messages to show").option("--no-pager","do not auto-pipe long output through less").action((e,t)=>{Mi(e,t)});C.command("search <query...>").description("Search every message in every session. Pass --semantic for fused keyword + summary + vector hits (delegates to the running daemon).").option("-p, --project <name>","restrict results to one project").option("-n, --limit <n>","max results","20").option("--semantic","fuse keyword + summary + vector hits via the daemon (Pro; requires `recall start`)").action(async(e,t)=>{await qi(e.join(" "),t)});C.command("projects").description("List every project with how many sessions are in each.").action(()=>{Zi()});C.command("status").description("Show database size, session counts, and whether the daemon is running.").action(()=>{Vi()});C.command("start").description("Start the background daemon. Required for live tab-name tracking and the web UI.").action(async()=>{await Os()});C.command("stop").description("Stop the background daemon.").option("--all","also kill every MCP child and truncate the WAL (one-command WAL-pin recovery)").action(async e=>{process.exitCode=await ra({all:e.all})});C.command("open").description("Open the local web UI in your browser. Starts the daemon if it is not already running.").action(async()=>{await oa()});C.command("tui").description("Browse and search sessions in an interactive terminal UI. No browser needed.").action(async()=>{let{runTui:e}=await Promise.resolve().then(()=>(jp(),Fp));await e()});C.command("context <id>").description("Pipe a past session into a fresh Claude chat. Prints condensed markdown to stdout.").option("-f, --full","include full tool call and result bodies (much longer)").option("--since <when>","only messages at or after this time (2h, 30m, 1d, YYYY-MM-DD, or ISO)").option("--subagents","also include subagent / sidechain messages").option("--prelude <text>",'prepend a custom header (e.g. "Continue this conversation")').action(async(e,t)=>{await La(e,t)});C.command("mcp").description("Expose Recall as an MCP server over stdio. Add this to your Claude Desktop / Claude Code MCP config to let Claude search your sessions.").option("--allow-writes","enable write tools (add_tag, set_alias, append_note, \u2026). Off by default; rate-limited and audited when on.").action(async e=>{await Na({allowWrites:!!e.allowWrites})});C.command("mcp-prune").description("Kill stuck MCP children that are holding SQLite connections. Default scope is orphans (parent claude is gone); pass --all to nuke every MCP child. Runs a WAL TRUNCATE on success so disk space comes back immediately.").option("--all","prune every MCP child, not just orphans").option("--yes","skip the interactive confirmation prompt").option("--json","emit a single JSON line instead of human-readable output (implies --yes)").action(async e=>{let t=await Ms({all:!!e.all,yes:!!e.yes,json:!!e.json});process.exit(t)});C.command("paste").description("Save clipboard content (or stdin) into Recall. Opt-in. Secrets are blocked by default; pass --force to override.").option("-l, --list","list archived pastes").option("--purge <id>","permanently delete one paste by id or 8-char prefix").option("-f, --force","skip the secret-scan confirmation prompt").option("--pipe","echo the content to stdout after archiving (for shell pipelines)").option("--dry-run","preview what would be archived without writing").option("--label <text>","short description shown in `recall paste --list`").action(async e=>{await Aa(e)});C.command("audit-secrets").description("Scan the database for secrets that slipped through. Read-only by default; pass --redact to scrub in place.").option("--redact","rewrite offending rows in place (your source files are never touched)").option("-v, --verbose","print every session and message containing a hit").action(async e=>{await Oa(e)});C.command("titles [action]").description('Audit session titles in the current project. Defaults to "audit".').option("-p, --project <name>","project to audit (defaults to current cwd)").option("--json","machine-readable JSON output").option("--dry-run","classify but do not write the title_quality column").action(async(e,t)=>{if((e??"audit").toLowerCase()==="audit"){await Ia(t);return}console.error(`Unknown titles action: ${e}. Supported: audit`),process.exitCode=1});C.command("import-vscode-state").description("Backfill missing tab names by reading VS Code, Cursor, and Windsurf workspace state. Dry-run by default; pass --apply to write.").option("-p, --project <name>","scope to a single project").option("--apply","write the proposed names (default: dry-run)").option("--min-score <n>","minimum match score from 0 to 1 (default 0.7)","0.7").option("--limit <n>","cap the number of proposals returned").option("--source <list>","which editors to scan: vscode | cursor | windsurf | all (default all)","all").option("--json","machine-readable JSON output").action(async e=>{await ja(e)});C.command("semantic [action] [subAction]").description('Local on-device semantic search. Default action is "status". Free, local, zero tokens (Tier-2): install, status, reindex, uninstall. Advanced \u2014 sends summaries via your Claude plan and costs plan tokens (Tier-1): on, off, backfill, pause, resume, auto-extract <on|off>. Diagnostic: verify-spawn (confirms claude CLI honors --no-session-persistence before re-enabling Tier-1).').option("-n, --limit <n>","max sessions to process during backfill","1000").option("-f, --force","regenerate summaries even when one already exists").option("--rate <perMin>","sessions per minute when enabling").option("--model <name>","claude model id (e.g. claude-haiku-4-5-20251001)").addHelpText("after",`
1930
+ AND (st.tag LIKE '%debug%' OR st.tag LIKE '%fix%' OR st.tag LIKE '%bug%' OR st.tag LIKE '%error%')`).get(n.start,n.end).c,_={recallCount:r,uniqueSessionsRecalled:p,sessionCount:g.cnt,avgMessageCount:g.avg_msgs,nightSessionRatio:g.cnt>0?f/g.cnt:0,debugTagRatio:g.cnt>0?h/g.cnt:0},b=sg(_),S=50;try{let{computeAllHealthScores:O}=await Promise.resolve().then(()=>(Ii(),km)),k=O();k.length>0&&(S=Math.round(k.reduce((I,F)=>I+F.score,0)/k.length))}catch{}let w=t.verdict??"";if(!t.verdict){let O=AC({input:process.stdin,output:process.stderr});w=await new Promise(k=>{O.question("Add your take (one line, or press Enter to skip): ",I=>{O.close(),k(I.trim())})})}let y={month:n.label,totalRecalls:r,totalTokensPiped:o,sessionsIndexed:i,mostRecalledSession:d,biggestRecallTokens:l,healthScore:S,mostActiveHours:m,archetype:b,verdict:w},T=await wr("E",y),B=n.start.slice(0,7),A=t.out??rg(vC(),"Downloads");NC(A)||OC(A,{recursive:!0});let v=rg(A,`recall-wrapped-${B}.png`);LC(v,T),process.stderr.write(`Wrapped card saved to ${v}
1931
+ `)}ge();Ft();Ge();import{createRequire as $C}from"node:module";import{createInterface as PC}from"node:readline";import{stdin as FC,stdout as jC}from"node:process";var UC=$C(import.meta.url),BC=UC(`${te()}/package.json`).version,cg="https://clauderecall.com/api/feedback",Ki=process.env.RECALL_FEEDBACK_API??cg,ig=Ki===cg,Qi=2e3;function lg(e){let t=PC({input:FC,output:jC});return new Promise(n=>t.question(e,s=>{t.close(),n(s.trim())}))}function ag(e){if(!/^[+-]?\d+$/.test(e))return null;let t=Number.parseInt(e,10);return!Number.isFinite(t)||t<1||t>5?null:t}async function HC(e){if(e.score!==void 0){let s=ag(e.score);return s===null?(console.error(`--score must be an integer from 1 to 5 (got "${e.score}").`),null):s}if(!process.stdin.isTTY)return console.error("feedback needs a TTY to prompt. Pass --score 1..5 (and optionally --message) to send non-interactively."),null;console.log("How is Claude Recall going for you?"),console.log(" 1 = bad 2 = meh 3 = ok 4 = good 5 = great"),console.log("");let t=await lg("Score (1-5, q to quit): ");if(t.toLowerCase()==="q"||t==="")return console.log("No worries, skipped."),null;let n=ag(t);return n===null?(console.error("Please enter an integer 1 through 5."),null):n}async function WC(e,t){if(e.message!==void 0)return e.message.trim();if(!process.stdin.isTTY)return"";let n=t<=2?"What is the biggest pain point? (Enter to skip): ":"Anything you would add? (Enter to skip): ";return await lg(n)}function XC(e){return e.length<=Qi?{value:e,truncated:!1}:{value:e.slice(0,Qi),truncated:!0}}async function dg(e={}){ig||process.stderr.write(`[recall] RECALL_FEEDBACK_API override active (${Ki}); license token will NOT be sent.
1932
+ `);let t=await HC(e);if(t===null)return e.score!==void 0||!process.stdin.isTTY?1:0;let n=await WC(e,t),{value:s,truncated:r}=XC(n);r&&process.stderr.write(`[recall] --message truncated to ${Qi} characters before send.
1933
+ `);let o=await Ae(),i=cn(),a=ig&&o.tier==="pro"&&i?i.license_jwt:null,d={score:t,comment:s.length>0?s:null,surface:"cli",version:BC,os:process.platform,trigger_kind:"manual",license_jwt:a};try{let l=await fetch(Ki,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(d)});if(l.ok)return console.log(""),console.log("Thanks. Your feedback landed."),0;let u=await l.json().catch(()=>({}));return l.status===429?(console.log("You submitted feedback recently. Try again later."),0):(console.error(`Feedback failed: ${l.status} ${u.error??"unknown"}`),1)}catch(l){let u=l instanceof Error?l.message:"network error";return console.error(`Feedback could not be sent: ${u}`),1}}var WA=BA(import.meta.url),Fn=WA("../package.json").version,N=new HA;N.name("recall").description("Searchable memory for every Claude Code session you have ever run. Local, fast, offline. Run `recall` (no args) for the dashboard, or `recall --help` for the full command list.").version(Fn);N.command("index").description("Scan your Claude sessions and build the searchable database. Run this once after install.").option("-f, --force","reindex all files even if unchanged").option("-v, --verbose","show each file as it is processed").action(async e=>{await Da(e)});N.command("list").description("List your sessions, newest first.").option("-p, --project <name>","filter by project name").option("-n, --limit <n>","max rows to show","30").option("-a, --all","include short sessions (2 messages or fewer)").action(e=>{$a(e)});N.command("show <id>").description("Print a full session transcript. Accepts the full id or an 8-char prefix.").option("-r, --raw","print raw JSONL lines instead of formatted output").option("-n, --limit <n>","max messages to show").option("--no-pager","do not auto-pipe long output through less").action((e,t)=>{Ha(e,t)});N.command("search <query...>").description("Search every message in every session. Pass --semantic for fused keyword + summary + vector hits (delegates to the running daemon).").option("-p, --project <name>","restrict results to one project").option("-n, --limit <n>","max results","20").option("--semantic","fuse keyword + summary + vector hits via the daemon (Pro; requires `recall start`)").action(async(e,t)=>{await sc(e.join(" "),t)});N.command("projects").description("List every project with how many sessions are in each.").action(()=>{mc()});N.command("status").description("Show database size, session counts, and whether the daemon is running.").action(()=>{uc()});N.command("start").description("Start the background daemon. Required for live tab-name tracking and the web UI.").action(async()=>{await Zn()});N.command("stop").description("Stop the background daemon.").option("--all","also kill every MCP child and truncate the WAL (one-command WAL-pin recovery)").action(async e=>{process.exitCode=await Sc({all:e.all})});N.command("open").description("Open the local web UI in your browser. Starts the daemon if it is not already running.").action(async()=>{await wc()});N.command("tui").description("Browse and search sessions in an interactive terminal UI. No browser needed.").action(async()=>{let{runTui:e}=await Promise.resolve().then(()=>(Wg(),Hg));await e()});N.command("context <id>").description("Pipe a past session into a fresh Claude chat. Prints condensed markdown to stdout.").option("-f, --full","include full tool call and result bodies (much longer)").option("--since <when>","only messages at or after this time (2h, 30m, 1d, YYYY-MM-DD, or ISO)").option("--subagents","also include subagent / sidechain messages").option("--prelude <text>",'prepend a custom header (e.g. "Continue this conversation")').action(async(e,t)=>{await Xc(e,t)});N.command("mcp").description("Expose Recall as an MCP server over stdio. Add this to your Claude Desktop / Claude Code MCP config to let Claude search your sessions.").option("--allow-writes","enable write tools (add_tag, set_alias, append_note, \u2026). Off by default; rate-limited and audited when on.").action(async e=>{await Gc({allowWrites:!!e.allowWrites})});N.command("mcp-prune").description("Kill stuck MCP children that are holding SQLite connections. Default scope is orphans (parent claude is gone); pass --all to nuke every MCP child. Runs a WAL TRUNCATE on success so disk space comes back immediately.").option("--all","prune every MCP child, not just orphans").option("--yes","skip the interactive confirmation prompt").option("--json","emit a single JSON line instead of human-readable output (implies --yes)").action(async e=>{let t=await ns({all:!!e.all,yes:!!e.yes,json:!!e.json});process.exit(t)});N.command("paste").description("Save clipboard content (or stdin) into Recall. Opt-in. Secrets are blocked by default; pass --force to override.").option("-l, --list","list archived pastes").option("--purge <id>","permanently delete one paste by id or 8-char prefix").option("-f, --force","skip the secret-scan confirmation prompt").option("--pipe","echo the content to stdout after archiving (for shell pipelines)").option("--dry-run","preview what would be archived without writing").option("--label <text>","short description shown in `recall paste --list`").action(async e=>{await Jc(e)});N.command("audit-secrets").description("Scan the database for secrets that slipped through. Read-only by default; pass --redact to scrub in place.").option("--redact","rewrite offending rows in place (your source files are never touched)").option("-v, --verbose","print every session and message containing a hit").action(async e=>{await zc(e)});N.command("titles [action]").description('Audit session titles in the current project. Defaults to "audit".').option("-p, --project <name>","project to audit (defaults to current cwd)").option("--json","machine-readable JSON output").option("--dry-run","classify but do not write the title_quality column").action(async(e,t)=>{if((e??"audit").toLowerCase()==="audit"){await qc(t);return}console.error(`Unknown titles action: ${e}. Supported: audit`),process.exitCode=1});N.command("import-vscode-state").description("Backfill missing tab names by reading VS Code, Cursor, and Windsurf workspace state. Dry-run by default; pass --apply to write.").option("-p, --project <name>","scope to a single project").option("--apply","write the proposed names (default: dry-run)").option("--min-score <n>","minimum match score from 0 to 1 (default 0.7)","0.7").option("--limit <n>","cap the number of proposals returned").option("--source <list>","which editors to scan: vscode | cursor | windsurf | all (default all)","all").option("--json","machine-readable JSON output").action(async e=>{await tl(e)});N.command("semantic [action] [subAction]").description('Local on-device semantic search. Default action is "status". Free, local, zero tokens (Tier-2): install, status, reindex, uninstall. Advanced \u2014 sends summaries via your Claude plan and costs plan tokens (Tier-1): on, off, backfill, pause, resume, auto-extract <on|off>. Diagnostic: verify-spawn (confirms claude CLI honors --no-session-persistence before re-enabling Tier-1).').option("-n, --limit <n>","max sessions to process during backfill","1000").option("-f, --force","regenerate summaries even when one already exists").option("--rate <perMin>","sessions per minute when enabling").option("--model <name>","claude model id (e.g. claude-haiku-4-5-20251001)").option("--yes","skip interactive confirmation for `recall semantic migrate`").option("--no-backup","after `recall semantic migrate`, do NOT retain the old vectors in vec_chunks_v1_backup").option("--dry-run","for `recall semantic migrate`: print the plan + measured throughput and exit without writing anything").option("-p, --project <name>","for `recall semantic migrate`: scope to one project. Runs the embed loop only (no swap, no chunk_meta retag). Use repeated runs to drip-migrate per project; finalize with the unscoped `recall semantic migrate`.").addHelpText("after",`
1788
1934
  Two lanes:
1789
1935
  Tier-2 (default, recommended): on-device 768-d embeddings via bge-base-en-v1.5
1790
1936
  + sqlite-vec. No network, no token spend. Powered by:
@@ -1800,7 +1946,7 @@ Two lanes:
1800
1946
  Reindex chunks-per-session cap (advanced):
1801
1947
  Set RECALL_REINDEX_MAX_CHUNKS=200 to cap each session at 200 chunks for a
1802
1948
  faster first pass. Default is 0 (no cap, full accuracy).
1803
- `).action(async(e,t,s)=>{await bc(e,{...s,_autoExtractAction:t})});C.command("embeddings [action]").description('Audit embedding coverage and backfill missing ones. Defaults to "audit". Actions: audit, backfill-summaries, backfill-messages.').option("-p, --project <name>","scope to a single project").option("-n, --limit <n>","max sessions to process during backfill").option("--json","emit JSON instead of formatted output").action(async(e,t)=>{await Rc(e,t)});var Es=C.command("infer").description("Discover links between sessions. Subcommands: outputs | citations | l1 | links | bug-patterns.");function Xp(e){return e.description("Extract code, file, and error references from sessions in one project. Uses the local claude CLI.").requiredOption("-p, --project <name>","project name (required)").option("-n, --limit <n>","max sessions to process","200").option("-f, --force","re-extract even if already done").option("--model <name>","override the default Haiku model id").option("-y, --yes","skip the >50-session cost confirmation (for scripts)").option("--json","emit JSON summary instead of formatted output").action(async t=>{await Oc(t)})}function Jp(e){return e.description("Find sessions that cite the same files or errors. Lands proposals for review.").requiredOption("-p, --project <name>","project name (required)").option("--json","emit JSON summary instead of formatted output").action(async t=>{await Bc(t)})}function Gp(e){return e.description("Fast deterministic link inference across sessions. No LLM cost.").option("-p, --project <name>","restrict to one project (default: all)").option("--json","emit JSON summary instead of formatted output").action(async t=>{await Xc(t)})}function Yp(e){return e.description("LLM-powered link classification. Pre-filters by confidence, batches the survivors through your local claude CLI (Haiku).").requiredOption("-p, --project <name>","project name (required)").option("--min-conf <n>","pre-filter threshold (default 0.4)","0.4").option("-n, --limit <n>","max candidate pairs to classify (default 1000)","1000").option("--auto-promote","promote high-confidence pairs straight into session_links").option("--auto-promote-threshold <n>","promotion threshold (default 0.95)").option("--model <name>","override the default Haiku model id").option("--json","emit JSON summary instead of formatted output").action(async t=>{await Vc(t)})}function zp(e){return e.description("Cluster sessions that hit the same bug. Idempotent.").option("-p, --project <name>","restrict to one project (default: all)").option("--min-cluster-size <n>","skip clusters smaller than this (default 3)","3").option("--semantic","add an embedding-distance pass to catch near-duplicate snippets").option("-n, --limit <n>","cap clusters in the output (default 100)","100").option("--json","emit JSON summary instead of formatted output").action(async t=>{await cl(t)})}Xp(Es.command("outputs"));Jp(Es.command("citations"));Gp(Es.command("l1"));Yp(Es.command("links"));zp(Es.command("bug-patterns"));Xp(C.command("extract-outputs"));Jp(C.command("infer-citations"));Gp(C.command("infer-l1"));Yp(C.command("infer-links"));zp(C.command("infer-bug-patterns"));C.command("neighborhood <id>").description("Bundle a session with its parents, children, citations, and similar sessions. Pipe into `claude` to seed a fresh chat with rich context.").option("-b, --budget <n>","token budget for the bundle (default 4000)","4000").option("--scoring <mode>","pagerank | embedding-rerank | hybrid (default hybrid)","hybrid").option("-e, --edge-types <list>","comma-separated link types to include: citation,similar,skill_track,bug_pattern,wiki_link,temporal_proximity (default all)").option("--max-depth <n>","how far to walk the link graph (default 2)","2").option("--no-wiki-links","exclude manual wiki links").option("--include-suggestions","include pending suggestions (debug only)").option("--json","emit the full result as JSON instead of markdown").action(async(e,t)=>{await Sl(e,t)});C.command("similar <session-id>").description("Find sessions about the same topic as this one. Pro feature.").option("-n, --limit <n>","max results","10").action(async(e,t)=>{let s=process.env.RECALL_DEBUG_TIMING==="1",n=(f,h)=>{if(s){let E=Math.round(performance.now()-h);process.stderr.write(`[similar:timing] ${f}: ${E}ms
1804
- `)}},r=performance.now(),{requireProOrExit:o}=await Promise.resolve().then(()=>(de(),Zt));await o("Similar sessions"),n("requireProOrExit",r);let i=performance.now(),{isModelInstalled:a}=await Promise.resolve().then(()=>(Js(),dc)),{loadEmbedder:d,getEmbedderStatus:l}=await Promise.resolve().then(()=>(yt(),Dr)),{findSimilarSessions:u}=await Promise.resolve().then(()=>(Wp(),Hp));if(n("dynamic imports",i),!a()){console.error("Model not installed. Run `recall semantic install` first."),process.exitCode=1;return}if(!l().loaded){process.stderr.write(`[recall similar] loading embedder model (first run is slow, ~5-30s)\u2026
1805
- `);let f=performance.now();await d(),n("loadEmbedder (cold)",f)}let p=Math.max(1,Math.min(50,Number(t.limit??10))),m=performance.now(),g=await u(e,p);if(n("findSimilarSessions",m),g.length===0){console.log("No similar sessions found.");return}for(let f of g)console.log(` ${f.sessionId} similarity=${f.similarity.toFixed(3)}`)});C.command("stats [id]").description("How much you have spent and how many tokens you have used. No args = overview. Pass a session id for one session, or --project for one project.").option("-p, --project <name>","show stats for a single project").option("-d, --days <n>","restrict the overview to the last N days (7 or 30)").option("--backfill","compute usage for already-indexed messages (safe to rerun)").option("-n, --limit <n>","max messages to scan during backfill").option("--json","emit JSON instead of a formatted table").action(async(e,t)=>{await Cl(e,t)});C.command("correlate [id]").description("Link sessions to the git commits they wrote. Read-only. No args = batch mode; pass an id to correlate one.").option("-n, --limit <n>","max sessions to process in batch mode").option("--json","emit JSON instead of formatted output").action(async(e,t)=>{await Ol(e,t)});var oi=C.command("correlator").description("Diagnose and fix terminal-to-session matching. Subcommands: audit | debug | restore.");function qp(e){return e.description("Show how the daemon is matching open terminals to sessions right now.").option("--json","emit JSON instead of formatted output").action(async t=>{await pd(t)})}function Kp(e){return e.description("Restore aliases that `correlator audit --fix` cleared, when they still cleanly match an open terminal. Dry-run by default.").option("--apply","apply the restoration (otherwise dry-run only)").option("--json","emit JSON instead of formatted output").action(async t=>{await md(t)})}function Vp(e){return e.description("Find sessions with bad terminal-name aliases from the legacy race-window bug. Use --fix to clear suspects.").option("--fix","clear suspect aliases (otherwise dry-run only)").option("-w, --window <seconds>","collision window in seconds (default 60, clamped 5..600)").option("-p, --project <name>","limit detection and --fix to a single project").option("--json","emit JSON instead of formatted output").action(async t=>{await $l(t)})}qp(oi.command("debug"));Kp(oi.command("restore"));Vp(oi.command("audit"));qp(C.command("correlator-debug"));Kp(C.command("correlator-restore"));C.command("name <id-prefix> <name>").description("Rename a session. By default it stays linked to its terminal tab, so renaming the tab later still updates the name. Use --pin to lock the name permanently. Daemon must be running.").option("--json","emit JSON").option("--pin","lock the name; later tab renames will NOT overwrite it").action(async(e,t,s)=>{await cd(e,t,s)});C.command("doctor").description("Diagnose database health: size, WAL, FTS fragmentation, integrity, alias invariant. Exits non-zero on issues.").option("--json","emit JSON instead of formatted output").action(async e=>{let t=await Yl(e);process.exit(t)});C.command("optimize").description("Run maintenance: WAL checkpoint, FTS5 merge, planner stats. Use --vacuum to also reclaim free pages (requires daemon stopped).").option("--vacuum","also run VACUUM to reclaim free pages (rewrites the whole DB; daemon must be stopped)").option("--json","emit JSON instead of formatted output").action(async e=>{let t=await zl(e);process.exit(t)});C.command("purge-phantoms").description("Remove session rows produced by historical daemon-autonomous `claude -p` spawns (auto-titler, summariser, output-index, \u2026). Source JSONLs at ~/.claude/projects/ are NEVER touched. Requires daemon stopped.").option("--dry-run","count phantom rows without deleting anything").option("--json","emit JSON instead of formatted output").action(async e=>{let t=await Kl(e);process.exit(t)});C.command("archive").description("Power-user retention. Subcommands: list, run --before YYYY-MM-DD [--dry-run], restore <session-id>, auto <status|on|off> [--after N].").argument("[action]","list | run | restore | auto","list").argument("[arg]","session id (for restore) or sub-action (for auto)").option("--before <date>","YYYY-MM-DD cutoff for `run`").option("--dry-run","show what would be archived without moving rows").option("--after <days>","days threshold for `auto on`",e=>Number.parseInt(e,10)).action(async(e,t,s)=>{let n=e==="auto",r=await ad({_action:e,_subAction:n?t:void 0,_sessionId:n?void 0:t,before:typeof s.before=="string"?s.before:void 0,dryRun:s.dryRun===!0,after:typeof s.after=="number"?s.after:void 0});process.exit(r)});Vp(C.command("correlator-audit"));C.command("blame <sha>").description("Look up which session(s) wrote the code in a commit. Reverse of `correlate`.").option("--json","emit JSON instead of formatted output").action(async(e,t)=>{await gd(e,t)});C.command("digest").description("Today's rediscovery picks: a session worth revisiting, the costliest of the week, and the one behind your latest commit.").option("--json","emit JSON instead of formatted output").action(async e=>{await hd(e)});C.command("install-extension").description("Install the bundled VS Code / Cursor / Windsurf extension into your editor. Auto-detects which CLIs are installed.").option("--editor <name>","install only into one editor: code | cursor | code-insiders | windsurf").option("--print-path","print the path to the bundled .vsix and exit").action(async e=>{await wd(e)});C.command("health [project]").description("Score how well-organized each project's sessions are. Worst first, or pass a project name for a single breakdown.").option("--json","emit JSON instead of formatted output").action((e,t)=>{xd(e,t)});C.command("verify [action]").description('Toggle verification badges in the UI. Defaults to "status". Actions: on, off, status.').action(e=>{kd(e)});C.command("activate <license-key>").description("Activate your Pro license. Run once after purchase.").action(async e=>{await Dd(e)});C.command("upgrade").description("Open the Pro pricing page in your browser.").action(async()=>{await Pd()});C.command("trial [promo-code]").description("Start a 7-day Pro trial. With a promo code (e.g. RECALL7DAY), redeems it directly from the CLI. With no code, opens the signup page.").action(async e=>{await jd(e)});var Pn=C.command("telemetry").description('Manage the opt-in anonymous install ping. Defaults to "view".');Pn.command("view",{isDefault:!0}).description("Show current decision, state file path, and next-ping payload.").action(async()=>{await Yd(hs)});Pn.command("on").description("Opt in. One anonymous ping per machine per month. See https://clauderecall.com/telemetry").action(async()=>{await Jd()});Pn.command("off").description("Opt out. Deletes the nonce. No further pings.").action(async()=>{await Gd()});Pn.command("status").description("Alias for view.").action(async()=>{await Oo(hs)});var ii=C.command("license").description('Show or remove the Pro license on this machine. Defaults to "status".');ii.command("status",{isDefault:!0}).description("Show the current license tier.").action(async()=>{await Kd()});ii.command("deactivate").description("Remove the stored license from this machine. Reverses `recall activate`.").action(()=>{Zd()});ii.command("check").description("Force a revocation check against the license server. The daemon also runs this on a 24-hour timer in the background.").action(async()=>{await Vd()});var U0=C.command("thread").description("Group related sessions into threads, then list, show, link, merge, or scan them."),B0=C.command("threads").description("Alias for `thread`. Both forms accept the same subcommands.");function Zp(e){e.command("sync").description("Capture sessions running in this repo right now into a thread.").option("--preflight","preview the plan without writing").option("-p, --project <name>","override the cwd-based project").option("--window-hours <n>","rolling activity window (default 6)").option("--json","machine-readable JSON output").action(async t=>{await vu(t)}),e.command("scan").description("Auto-detect parent-child links across past sessions. Dry-run by default; pass --apply to write.").option("-p, --project <name>","scope to a single project").option("--json","emit JSON instead of formatted output").option("--apply","write edges (default: dry-run preview)").option("--confidence-min <n>","override threshold (dry-run 0.5, apply 0.7)").option("--no-llm-names","skip LLM naming (free, offline)").option("--llm-rescore","LLM re-score borderline pairs (small Haiku call each)").option("--rescore-band-lo <n>","low end of the borderline band (default 0.4)").option("--rescore-band-hi <n>","high end of the borderline band (default 0.7)").option("--rescore-model <id>","model id for the rescore call (default Haiku 4.5)").action(t=>{Au(t)}),e.command("list").description("List threads, newest first.").option("--archived","include archived threads").option("--json","emit JSON instead of a table").action(t=>{eu(t)}),e.command("show <id-prefix>").description("Show a thread's header and session tree.").option("--json","emit JSON instead of formatted output").action((t,s)=>{tu(t,s)}),e.command("new <name>").description("Create a new thread.").option("--origin <session-id>","seed with an origin session").option("--summary <text>","optional short summary").option("--json","emit JSON").action((t,s)=>{su(t,s)}),e.command("link <session-id>").description("Link a session into a thread.").requiredOption("--thread <id-prefix>","thread id or prefix").option("--parent <session-id>","parent session within the thread").option("--role <role>","origin | child (default inferred from --parent)").option("--json","emit JSON").action((t,s)=>{nu(t,s)}),e.command("unlink <session-id>").description("Remove a session from a thread.").requiredOption("--thread <id-prefix>","thread id or prefix").option("--json","emit JSON").action((t,s)=>{ru(t,s)}),e.command("set-parent <session-id>").description("Change a session's parent within a thread. Use --parent none to promote to origin.").requiredOption("--thread <id-prefix>","thread id or prefix").option("--parent <session-id|none>",'new parent, or "none" to promote to origin').option("--json","emit JSON").action((t,s)=>{ou(t,s)}),e.command("rename <id-prefix> <new-name>").description("Rename a thread.").option("--json","emit JSON").action((t,s,n)=>{iu(t,s,n)}),e.command("close <id-prefix>").description("Mark a thread as closed (work complete).").option("--json","emit JSON").action((t,s)=>{au(t,s)}),e.command("reopen <id-prefix>").description("Reopen a closed thread.").option("--json","emit JSON").action((t,s)=>{cu(t,s)}),e.command("archive <id-prefix>").description("Archive a thread (hidden from the default list).").option("--json","emit JSON").action((t,s)=>{lu(t,s)}),e.command("merge <source-id-prefix>").description("Merge one thread into another.").requiredOption("--into <id-prefix>","destination thread").option("--json","emit JSON").action((t,s)=>{du(t,s)}),e.command("split <id-prefix>").description("Split sessions out of a thread into a new thread.").requiredOption("--sessions <s1,s2,...>","comma-separated session ids or prefixes").requiredOption("--name <new-name>","name for the new thread").option("--json","emit JSON").action((t,s)=>{uu(t,s)})}Zp(U0);Zp(B0);C.command("share [id]").description("Save a session as a shareable PNG card. No args = your last recalled session. Saved to ~/Downloads and opened.").option("--style <A|B|C|D>","card style (default A: Minimal)").option("--verdict <quote>","your one-line take on the session").option("--link","also generate a shareable URL and copy it to the clipboard").option("--clipboard","also copy the PNG to the system clipboard").option("--out <path>","override the save directory").option("--no-open","do not auto-open the saved PNG").action(async(e,t)=>{await Zu(e,t)});C.command("feedback").description("Send a 1-5 rating and optional comment to the team. Interactive by default; pass --score (and optionally --message) to skip prompts.").option("--score <1-5>","rating from 1 (bad) to 5 (great)").option("--message <text>","optional comment to send with the rating").action(async e=>{let t=await ip(e);t!==0&&(process.exitCode=t)});C.command("wrapped [month]").description("Save your monthly recap as a shareable PNG card. Defaults to the current month. Accepts YYYY-MM, full name, or abbreviation.").option("--verdict <quote>","your one-line take on the month").option("--out <path>","override the save directory").action(async(e,t)=>{await tp(e,t)});process.env.CLAUDECODE&&!process.stdout.isTTY&&process.stderr.write(`<claude-code-hint v="1" type="plugin" value="claude-recall@claude-plugins-official" />
1806
- `);C.action(async()=>{await Md()});C.hook("preAction",async(e,t)=>{let s=t.name();try{await qd(s,hs)}catch{}Xd(hs).catch(()=>{})});C.parseAsync(process.argv).catch(e=>{console.error(e),process.exit(1)});
1949
+ `).action(async(e,t,n)=>{await ld(e,{...n,_autoExtractAction:t})});N.command("embeddings [action]").description('Audit embedding coverage and backfill missing ones. Defaults to "audit". Actions: audit, backfill-summaries, backfill-messages.').option("-p, --project <name>","scope to a single project").option("-n, --limit <n>","max sessions to process during backfill").option("--json","emit JSON instead of formatted output").action(async(e,t)=>{await gd(e,t)});var jn=N.command("infer").description("Discover links between sessions. Subcommands: outputs | citations | l1 | links | bug-patterns.");function Yg(e){return e.description("Extract code, file, and error references from sessions in one project. Uses the local claude CLI.").requiredOption("-p, --project <name>","project name (required)").option("-n, --limit <n>","max sessions to process","200").option("-f, --force","re-extract even if already done").option("--model <name>","override the default Haiku model id").option("-y, --yes","skip the >50-session cost confirmation (for scripts)").option("--json","emit JSON summary instead of formatted output").action(async t=>{await wd(t)})}function qg(e){return e.description("Find sessions that cite the same files or errors. Lands proposals for review.").requiredOption("-p, --project <name>","project name (required)").option("--json","emit JSON summary instead of formatted output").action(async t=>{await Od(t)})}function Vg(e){return e.description("Fast deterministic link inference across sessions. No LLM cost.").option("-p, --project <name>","restrict to one project (default: all)").option("--json","emit JSON summary instead of formatted output").action(async t=>{await Md(t)})}function Kg(e){return e.description("LLM-powered link classification. Pre-filters by confidence, batches the survivors through your local claude CLI (Haiku).").requiredOption("-p, --project <name>","project name (required)").option("--min-conf <n>","pre-filter threshold (default 0.4)","0.4").option("-n, --limit <n>","max candidate pairs to classify (default 1000)","1000").option("--auto-promote","promote high-confidence pairs straight into session_links").option("--auto-promote-threshold <n>","promotion threshold (default 0.95)").option("--model <name>","override the default Haiku model id").option("--json","emit JSON summary instead of formatted output").action(async t=>{await Bd(t)})}function Qg(e){return e.description("Cluster sessions that hit the same bug. Idempotent.").option("-p, --project <name>","restrict to one project (default: all)").option("--min-cluster-size <n>","skip clusters smaller than this (default 3)","3").option("--semantic","add an embedding-distance pass to catch near-duplicate snippets").option("-n, --limit <n>","cap clusters in the output (default 100)","100").option("--json","emit JSON summary instead of formatted output").action(async t=>{await Qd(t)})}Yg(jn.command("outputs"));qg(jn.command("citations"));Vg(jn.command("l1"));Kg(jn.command("links"));Qg(jn.command("bug-patterns"));Yg(N.command("extract-outputs"));qg(N.command("infer-citations"));Vg(N.command("infer-l1"));Kg(N.command("infer-links"));Qg(N.command("infer-bug-patterns"));N.command("neighborhood <id>").description("Bundle a session with its parents, children, citations, and similar sessions. Pipe into `claude` to seed a fresh chat with rich context.").option("-b, --budget <n>","token budget for the bundle (default 4000)","4000").option("--scoring <mode>","pagerank | embedding-rerank | hybrid (default hybrid)","hybrid").option("-e, --edge-types <list>","comma-separated link types to include: citation,similar,skill_track,bug_pattern,wiki_link,temporal_proximity (default all)").option("--max-depth <n>","how far to walk the link graph (default 2)","2").option("--no-wiki-links","exclude manual wiki links").option("--include-suggestions","include pending suggestions (debug only)").option("--json","emit the full result as JSON instead of markdown").action(async(e,t)=>{await du(e,t)});N.command("similar <session-id>").description("Find sessions about the same topic as this one. Pro feature.").option("-n, --limit <n>","max results","10").action(async(e,t)=>{let n=process.env.RECALL_DEBUG_TIMING==="1",s=(f,h)=>{if(n){let _=Math.round(performance.now()-h);process.stderr.write(`[similar:timing] ${f}: ${_}ms
1950
+ `)}},r=performance.now(),{requireProOrExit:o}=await Promise.resolve().then(()=>(ge(),dn));await o("Similar sessions"),s("requireProOrExit",r);let i=performance.now(),{isModelInstalled:a}=await Promise.resolve().then(()=>(_n(),xl)),{loadEmbedder:d,getEmbedderStatus:l}=await Promise.resolve().then(()=>(Pe(),bs)),{findSimilarSessions:u}=await Promise.resolve().then(()=>(zg(),Jg));if(s("dynamic imports",i),!a()){console.error("Model not installed. Run `recall semantic install` first."),process.exitCode=1;return}if(!l().loaded){process.stderr.write(`[recall similar] loading embedder model (first run is slow, ~5-30s)\u2026
1951
+ `);let f=performance.now();await d(),s("loadEmbedder (cold)",f)}let m=Math.max(1,Math.min(50,Number(t.limit??10))),p=performance.now(),g=await u(e,m);if(s("findSimilarSessions",p),g.length===0){console.log("No similar sessions found.");return}for(let f of g)console.log(` ${f.sessionId} similarity=${f.similarity.toFixed(3)}`)});N.command("stats [id]").description("How much you have spent and how many tokens you have used. No args = overview. Pass a session id for one session, or --project for one project.").option("-p, --project <name>","show stats for a single project").option("-d, --days <n>","restrict the overview to the last N days (7 or 30)").option("--backfill","compute usage for already-indexed messages (safe to rerun)").option("-n, --limit <n>","max messages to scan during backfill").option("--json","emit JSON instead of a formatted table").action(async(e,t)=>{await hu(e,t)});N.command("correlate [id]").description("Link sessions to the git commits they wrote. Read-only. No args = batch mode; pass an id to correlate one.").option("-n, --limit <n>","max sessions to process in batch mode").option("--json","emit JSON instead of formatted output").action(async(e,t)=>{await wu(e,t)});var pa=N.command("correlator").description("Diagnose and fix terminal-to-session matching. Subcommands: audit | debug | restore.");function Zg(e){return e.description("Show how the daemon is matching open terminals to sessions right now.").option("--json","emit JSON instead of formatted output").action(async t=>{await gm(t)})}function ef(e){return e.description("Restore aliases that `correlator audit --fix` cleared, when they still cleanly match an open terminal. Dry-run by default.").option("--apply","apply the restoration (otherwise dry-run only)").option("--json","emit JSON instead of formatted output").action(async t=>{await fm(t)})}function tf(e){return e.description("Find sessions with bad terminal-name aliases from the legacy race-window bug. Use --fix to clear suspects.").option("--fix","clear suspect aliases (otherwise dry-run only)").option("-w, --window <seconds>","collision window in seconds (default 60, clamped 5..600)").option("-p, --project <name>","limit detection and --fix to a single project").option("--json","emit JSON instead of formatted output").action(async t=>{await xu(t)})}Zg(pa.command("debug"));ef(pa.command("restore"));tf(pa.command("audit"));Zg(N.command("correlator-debug"));ef(N.command("correlator-restore"));N.command("name <id-prefix> <name>").description("Rename a session. By default it stays linked to its terminal tab, so renaming the tab later still updates the name. Use --pin to lock the name permanently. Daemon must be running.").option("--json","emit JSON").option("--pin","lock the name; later tab renames will NOT overwrite it").action(async(e,t,n)=>{await dm(e,t,n)});N.command("doctor").description("Diagnose database health: size, WAL, FTS fragmentation, integrity, alias invariant. Exits non-zero on issues. Pass --ack <id> to acknowledge a background alert.").option("--json","emit JSON instead of formatted output").option("--ack <id>","acknowledge a background doctor alert by id (8-char prefix accepted)").action(async e=>{let t=await qu(e);process.exit(t)});N.command("optimize").description("Run maintenance: WAL checkpoint, FTS5 merge, planner stats. Use --vacuum to also reclaim free pages (requires daemon stopped).").option("--vacuum","also run VACUUM to reclaim free pages (rewrites the whole DB; daemon must be stopped)").option("--json","emit JSON instead of formatted output").action(async e=>{let t=await Vu(e);process.exit(t)});N.command("purge-phantoms").description("Remove session rows produced by historical daemon-autonomous `claude -p` spawns (auto-titler, summariser, output-index, \u2026). Source JSONLs at ~/.claude/projects/ are NEVER touched. Requires daemon stopped.").option("--dry-run","count phantom rows without deleting anything").option("--json","emit JSON instead of formatted output").action(async e=>{let t=await Qu(e);process.exit(t)});N.command("archive").description("Power-user retention. Subcommands: list, run --before YYYY-MM-DD [--dry-run], restore <session-id>, auto <status|on|off> [--after N].").argument("[action]","list | run | restore | auto","list").argument("[arg]","session id (for restore) or sub-action (for auto)").option("--before <date>","YYYY-MM-DD cutoff for `run`").option("--dry-run","show what would be archived without moving rows").option("--after <days>","days threshold for `auto on`",e=>Number.parseInt(e,10)).action(async(e,t,n)=>{let s=e==="auto",r=await lm({_action:e,_subAction:s?t:void 0,_sessionId:s?void 0:t,before:typeof n.before=="string"?n.before:void 0,dryRun:n.dryRun===!0,after:typeof n.after=="number"?n.after:void 0});process.exit(r)});tf(N.command("correlator-audit"));N.command("blame <sha>").description("Look up which session(s) wrote the code in a commit. Reverse of `correlate`.").option("--json","emit JSON instead of formatted output").action(async(e,t)=>{await _m(e,t)});N.command("digest").description("Today's rediscovery picks: a session worth revisiting, the costliest of the week, and the one behind your latest commit.").option("--json","emit JSON instead of formatted output").action(async e=>{await bm(e)});N.command("install-extension").description("Install the bundled VS Code / Cursor / Windsurf extension into your editor. Auto-detects which CLIs are installed.").option("--editor <name>","install only into one editor: code | cursor | code-insiders | windsurf").option("--print-path","print the path to the bundled .vsix and exit").action(async e=>{await Rm(e)});N.command("health [project]").description("Score how well-organized each project's sessions are. Worst first, or pass a project name for a single breakdown.").option("--json","emit JSON instead of formatted output").action((e,t)=>{Cm(e,t)});N.command("verify [action]").description('Toggle verification badges in the UI. Defaults to "status". Actions: on, off, status.').action(e=>{Am(e)});N.command("activate <license-key>").description("Activate your Pro license. Run once after purchase.").action(async e=>{await Pm(e)});N.command("upgrade").description("Open the Pro pricing page in your browser.").action(async()=>{await jm()});N.command("trial [promo-code]").description("Start a 7-day Pro trial. With a promo code (e.g. RECALL7DAY), redeems it directly from the CLI. With no code, opens the signup page.").action(async e=>{await Bm(e)});var xr=N.command("telemetry").description('Manage the opt-in anonymous install ping. Defaults to "view".');xr.command("view",{isDefault:!0}).description("Show current decision, state file path, and next-ping payload.").action(async()=>{await qm(Fn)});xr.command("on").description("Opt in. One anonymous ping per machine per month. See https://clauderecall.com/telemetry").action(async()=>{await zm()});xr.command("off").description("Opt out. Deletes the nonce. No further pings.").action(async()=>{await Ym()});xr.command("status").description("Alias for view.").action(async()=>{await ji(Fn)});var ga=N.command("license").description('Show or remove the Pro license on this machine. Defaults to "status".');ga.command("status",{isDefault:!0}).description("Show the current license tier.").action(async()=>{await ep()});ga.command("deactivate").description("Remove the stored license from this machine. Reverses `recall activate`.").action(()=>{np()});ga.command("check").description("Force a revocation check against the license server. The daemon also runs this on a 24-hour timer in the background.").action(async()=>{await tp()});var XA=N.command("thread").description("Group related sessions into threads, then list, show, link, merge, or scan them."),GA=N.command("threads").description("Alias for `thread`. Both forms accept the same subcommands.");function nf(e){e.command("sync").description("Capture sessions running in this repo right now into a thread.").option("--preflight","preview the plan without writing").option("-p, --project <name>","override the cwd-based project").option("--window-hours <n>","rolling activity window (default 6)").option("--json","machine-readable JSON output").action(async t=>{await $p(t)}),e.command("scan").description("Auto-detect parent-child links across past sessions. Dry-run by default; pass --apply to write.").option("-p, --project <name>","scope to a single project").option("--json","emit JSON instead of formatted output").option("--apply","write edges (default: dry-run preview)").option("--confidence-min <n>","override threshold (dry-run 0.5, apply 0.7)").option("--no-llm-names","skip LLM naming (free, offline)").option("--llm-rescore","LLM re-score borderline pairs (small Haiku call each)").option("--rescore-band-lo <n>","low end of the borderline band (default 0.4)").option("--rescore-band-hi <n>","high end of the borderline band (default 0.7)").option("--rescore-model <id>","model id for the rescore call (default Haiku 4.5)").action(t=>{Mp(t)}),e.command("list").description("List threads, newest first.").option("--archived","include archived threads").option("--json","emit JSON instead of a table").action(t=>{rp(t)}),e.command("show <id-prefix>").description("Show a thread's header and session tree.").option("--json","emit JSON instead of formatted output").action((t,n)=>{op(t,n)}),e.command("new <name>").description("Create a new thread.").option("--origin <session-id>","seed with an origin session").option("--summary <text>","optional short summary").option("--json","emit JSON").action((t,n)=>{ip(t,n)}),e.command("link <session-id>").description("Link a session into a thread.").requiredOption("--thread <id-prefix>","thread id or prefix").option("--parent <session-id>","parent session within the thread").option("--role <role>","origin | child (default inferred from --parent)").option("--json","emit JSON").action((t,n)=>{ap(t,n)}),e.command("unlink <session-id>").description("Remove a session from a thread.").requiredOption("--thread <id-prefix>","thread id or prefix").option("--json","emit JSON").action((t,n)=>{cp(t,n)}),e.command("set-parent <session-id>").description("Change a session's parent within a thread. Use --parent none to promote to origin.").requiredOption("--thread <id-prefix>","thread id or prefix").option("--parent <session-id|none>",'new parent, or "none" to promote to origin').option("--json","emit JSON").action((t,n)=>{lp(t,n)}),e.command("rename <id-prefix> <new-name>").description("Rename a thread.").option("--json","emit JSON").action((t,n,s)=>{dp(t,n,s)}),e.command("close <id-prefix>").description("Mark a thread as closed (work complete).").option("--json","emit JSON").action((t,n)=>{up(t,n)}),e.command("reopen <id-prefix>").description("Reopen a closed thread.").option("--json","emit JSON").action((t,n)=>{mp(t,n)}),e.command("archive <id-prefix>").description("Archive a thread (hidden from the default list).").option("--json","emit JSON").action((t,n)=>{pp(t,n)}),e.command("merge <source-id-prefix>").description("Merge one thread into another.").requiredOption("--into <id-prefix>","destination thread").option("--json","emit JSON").action((t,n)=>{gp(t,n)}),e.command("split <id-prefix>").description("Split sessions out of a thread into a new thread.").requiredOption("--sessions <s1,s2,...>","comma-separated session ids or prefixes").requiredOption("--name <new-name>","name for the new thread").option("--json","emit JSON").action((t,n)=>{fp(t,n)})}nf(XA);nf(GA);N.command("share [id]").description("Save a session as a shareable PNG card. No args = your last recalled session. Saved to ~/Downloads and opened.").option("--style <A|B|C|D>","card style (default A: Minimal)").option("--verdict <quote>","your one-line take on the session").option("--link","also generate a shareable URL and copy it to the clipboard").option("--clipboard","also copy the PNG to the system clipboard").option("--out <path>","override the save directory").option("--no-open","do not auto-open the saved PNG").action(async(e,t)=>{await ng(e,t)});N.command("feedback").description("Send a 1-5 rating and optional comment to the team. Interactive by default; pass --score (and optionally --message) to skip prompts.").option("--score <1-5>","rating from 1 (bad) to 5 (great)").option("--message <text>","optional comment to send with the rating").action(async e=>{let t=await dg(e);t!==0&&(process.exitCode=t)});N.command("wrapped [month]").description("Save your monthly recap as a shareable PNG card. Defaults to the current month. Accepts YYYY-MM, full name, or abbreviation.").option("--verdict <quote>","your one-line take on the month").option("--out <path>","override the save directory").action(async(e,t)=>{await og(e,t)});process.env.CLAUDECODE&&!process.stdout.isTTY&&process.stderr.write(`<claude-code-hint v="1" type="plugin" value="claude-recall@claude-plugins-official" />
1952
+ `);N.action(async()=>{await $m()});N.hook("preAction",async(e,t)=>{let n=t.name();try{await Km(n,Fn)}catch{}Jm(Fn).catch(()=>{});try{Zm({commandName:n})}catch{}});N.parseAsync(process.argv).catch(e=>{console.error(e),process.exit(1)});