@clauderecallhq/cli 0.92.3 → 0.94.0
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 +435 -408
- package/dist/daemon/entrypoint.js +339 -290
- package/dist/mcp-server.js +130 -111
- package/dist/web/assets/{card-a-minimal-ujNERzX7.js → card-a-minimal-Beg8-vFs.js} +1 -1
- package/dist/web/assets/{card-b-terminal-DpJ_tVpg.js → card-b-terminal-BxIeub6M.js} +1 -1
- package/dist/web/assets/{card-c-gradient-CZXVGuNd.js → card-c-gradient-Dp20rmwu.js} +1 -1
- package/dist/web/assets/{card-d-dashboard-CHKD-PnB.js → card-d-dashboard-Dix1TgTv.js} +1 -1
- package/dist/web/assets/{dist-Dr4OUd4W.js → dist-Zvhj1IwK.js} +6 -6
- package/dist/web/assets/index-Bk28z8Va.css +1 -0
- package/dist/web/assets/{index-CSk4z4xG.js → index-Cc61yjtR.js} +53 -56
- package/dist/web/assets/{patterns-BPeZb9N0.js → patterns-Dsv_eCME.js} +1 -1
- package/dist/web/assets/{stats-BSwqSiFU.js → stats-Db9NRYik.js} +1 -1
- package/dist/web/assets/{thread-D2AXyhOx.js → thread-4KmPh02n.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +2 -2
- package/dist/web/assets/index-D8_ySbyW.css +0 -1
- /package/dist/web/assets/{_brand-Bw9uSB4O.js → _brand-C2V3ysl0.js} +0 -0
- /package/dist/web/assets/{captureNode-9CVj9vYC.js → captureNode-Da1ofkVZ.js} +0 -0
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
|
|
3
|
+
var p_=Object.defineProperty;var w=(e,t)=>()=>(e&&(t=e(e=0)),t);var V=(e,t)=>{for(var n in t)p_(e,n,{get:t[n],enumerable:!0})};import{createRequire as m_}from"node:module";var g_,f_,__,Wr,Xr,Pt,zn=w(()=>{"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)}}g_=m_(import.meta.url),f_=["node","sqlite"].join(":"),__=g_(f_),Wr=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)}},Xr=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,n={}){this.inner=new __.DatabaseSync(t,{readOnly:n.readonly??!1,allowExtension:!0})}prepare(t){return new Wr(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)}},Pt=Xr});import{homedir as Ba}from"node:os";import{join as an,basename as h_}from"node:path";import{existsSync as Ha,mkdirSync as E_,chmodSync as b_,readdirSync as Ua,statSync as S_}from"node:fs";function j(){Ha(C)||E_(C,{recursive:!0,mode:448}),process.platform!=="win32"&&b_(C,448)}function Gr(e){return e.replace(/^-/,"/").replace(/-/g,"/")}function Wa(e){let t=Gr(e);return h_(t)||t}function Xa(){if(!Ha(Ft))return[];let e=[],t=Ua(Ft,{withFileTypes:!0}).filter(n=>n.isDirectory()).map(n=>n.name);for(let n of t){let s=an(Ft,n),r=Ua(s,{withFileTypes:!0});for(let o of r){if(!o.isFile()||!o.name.endsWith(".jsonl"))continue;let i=an(s,o.name),a=S_(i);e.push({sessionFile:i,encodedProject:n,mtime:a.mtimeMs,size:a.size})}}return e}var Ft,C,ee,D=w(()=>{"use strict";Ft=process.env.CLAUDE_PROJECTS_DIR?process.env.CLAUDE_PROJECTS_DIR:an(Ba(),".claude","projects"),C=process.env.RECALL_HOME?process.env.RECALL_HOME:an(Ba(),".recall"),ee=an(C,"db.sqlite")});function Ja(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 sf=Object.defineProperty;var C=(e,t)=>()=>(e&&(t=e(e=0)),t);var Z=(e,t)=>{fo
|
|
|
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
|
-
`);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
|
|
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 Ga,za=w(()=>{"use strict";Ga=`
|
|
16
16
|
PRAGMA journal_mode = WAL;
|
|
17
17
|
PRAGMA synchronous = NORMAL;
|
|
18
18
|
PRAGMA foreign_keys = ON;
|
|
@@ -228,10 +228,11 @@ END;
|
|
|
228
228
|
-- segments of messages, embedded via local bge-base-en-v1.5 (768d). The vector
|
|
229
229
|
-- data is a derived cache; if lost, it is recomputable from messages.
|
|
230
230
|
-- Tables stay empty on Free tier (no model downloaded, no worker started).
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
231
|
+
--
|
|
232
|
+
-- NOTE: the vec_chunks vec0 virtual table is intentionally NOT created here.
|
|
233
|
+
-- It lives in src/db/vecLoader.ts (ensureVecChunksTable), loaded lazily so a
|
|
234
|
+
-- lean MCP child that only does FTS/metadata never instantiates the native
|
|
235
|
+
-- vec0 module. The daemon/worker/CLI call ensureVecChunksTable at open.
|
|
235
236
|
|
|
236
237
|
CREATE TABLE IF NOT EXISTS chunk_meta (
|
|
237
238
|
rowid INTEGER PRIMARY KEY,
|
|
@@ -730,37 +731,120 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_target
|
|
|
730
731
|
ON bug_synthesis_results(scope, target_id, created_at DESC);
|
|
731
732
|
CREATE INDEX IF NOT EXISTS idx_synth_results_created
|
|
732
733
|
ON bug_synthesis_results(created_at DESC);
|
|
733
|
-
|
|
734
|
-
|
|
734
|
+
|
|
735
|
+
-- Incremental-indexing parse cursor. One row per JSONL file (a file may
|
|
736
|
+
-- hold multiple sessions, so the cursor is per-FILE, not per-session).
|
|
737
|
+
-- Lets the watcher stream only newly-appended bytes instead of reparsing
|
|
738
|
+
-- from byte 0. Derived/recomputable state \u2014 NOT subject to the three-layer
|
|
739
|
+
-- durability rule (a wrong/missing cursor just forces a full reparse).
|
|
740
|
+
-- size_bytes and line_count are diagnostic only: decideReadStrategy compares
|
|
741
|
+
-- the LIVE stat size against byte_offset, never the stored size_bytes.
|
|
742
|
+
CREATE TABLE IF NOT EXISTS file_cursor (
|
|
743
|
+
file_path TEXT PRIMARY KEY,
|
|
744
|
+
byte_offset INTEGER NOT NULL,
|
|
745
|
+
size_bytes INTEGER NOT NULL,
|
|
746
|
+
line_count INTEGER NOT NULL DEFAULT 0,
|
|
747
|
+
inode INTEGER,
|
|
748
|
+
prefix_hash TEXT,
|
|
749
|
+
mtime REAL,
|
|
750
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
751
|
+
);
|
|
752
|
+
`});function Ya(e,t){let n=t.RECALL_DB_PROFILE;if(n&&y_.has(n))return n;let s=(e??"").split(/[\\/]/).pop()??"";return s==="mcp-server.js"||s==="claude-recall-mcp"?"light":s==="query-worker.js"?"worker":"full"}function qa(e){let t=[["busy_timeout",15e3],["journal_size_limit",67108864],["wal_autocheckpoint",1e3]];return e==="full"?[["cache_size",-64e3],["mmap_size",268435456],...t]:e==="worker"?[["cache_size",-16e3],["mmap_size",0],...t]:[["cache_size",-4e3],["mmap_size",0],...t]}var y_,Va=w(()=>{"use strict";y_=new Set(["light","full","worker"])});import*as Qa from"sqlite-vec";function lt(e){let t=e;Ka.has(t)||(Qa.load(e),Ka.add(t))}function Za(e){e.prepare("SELECT 1 FROM sqlite_master WHERE name = 'vec_chunks'").get()||(lt(e),e.exec(w_))}var Ka,w_,Yn=w(()=>{"use strict";Ka=new WeakSet;w_=`
|
|
753
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS vec_chunks USING vec0(
|
|
754
|
+
embedding float[768] distance_metric=cosine
|
|
755
|
+
);`});var qn={};V(qn,{closeDb:()=>T_,getDb:()=>E});function E(){if(K)return K;j();let e=Ya(process.argv[1],process.env);ec=e,K=new Pt(ee);for(let[t,n]of qa(e))K.pragma(`${t} = ${n}`);K.pragma("temp_store = MEMORY"),e!=="light"&<(K),K.exec(Ga),Ja(K),Za(K);try{K.exec("PRAGMA optimize")}catch{}return K}function T_(){if(K){if(ec==="light"){try{K.pragma("wal_checkpoint(PASSIVE)")}catch{}K.close(),K=null;return}try{K.exec("PRAGMA optimize")}catch{}try{K.exec("INSERT INTO messages_fts(messages_fts, rank) VALUES('merge', 4);")}catch{}try{K.exec("INSERT INTO sessions_fts(sessions_fts, rank) VALUES('merge', 4);")}catch{}try{K.pragma("wal_checkpoint(TRUNCATE)")}catch{}K.close(),K=null}}var K,ec,k=w(()=>{"use strict";zn();D();za();Va();Yn();K=null,ec="full"});import{createReadStream as R_}from"node:fs";import{createInterface as k_}from"node:readline";function Vn(e){return typeof e=="number"&&Number.isFinite(e)?e:0}function Yr(e){let t=e?.usage;if(!t||typeof t!="object")return null;let n={inputTokens:Vn(t.input_tokens),outputTokens:Vn(t.output_tokens),cacheCreateTokens:Vn(t.cache_creation_input_tokens),cacheReadTokens:Vn(t.cache_read_input_tokens)};return n.inputTokens===0&&n.outputTokens===0&&n.cacheCreateTokens===0&&n.cacheReadTokens===0?null:n}function Jr(e){return e.replace(x_,"")}function tc(e,t){if(e.length<=zr)return e;let n=e.slice(0,zr),s=e.length-zr;return`${n}
|
|
756
|
+
|
|
757
|
+
\u27E8\u2026 ${s.toLocaleString()} more chars in ${t}; see raw JSONL for full content \u27E9`}function C_(e){try{return JSON.stringify(e,null,2)}catch{return String(e)}}function L_(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(`
|
|
758
|
+
`)}return""}function A_(e){if(!e)return{text:"",toolNames:[]};if(typeof e.content=="string")return{text:Jr(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(Jr(s.text));continue}if(s.type==="tool_use"&&typeof s.name=="string"){n.push(s.name);let r=s.input!=null?C_(s.input):"",o=tc(r,"tool input");t.push(`\u26A1 **Tool call \xB7 \`${s.name}\`**
|
|
759
|
+
|
|
760
|
+
\`\`\`json
|
|
761
|
+
${o}
|
|
762
|
+
\`\`\``);continue}if(s.type==="tool_result"){let r=Jr(L_(s));if(r){let o=tc(r,"tool result");t.push(`**Tool result**
|
|
763
|
+
|
|
764
|
+
\`\`\`
|
|
765
|
+
${o}
|
|
766
|
+
\`\`\``)}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(`
|
|
767
|
+
|
|
768
|
+
`),toolNames:n}}function N_(e){if(e.endsWith("\r")&&(e=e.slice(0,-1)),!e.trim())return null;let t;try{t=JSON.parse(e)}catch{return null}if(!t.uuid||!t.sessionId)return null;let{text:n,toolNames:s}=A_(t.message);return{uuid:t.uuid,parentUuid:t.parentUuid??null,sessionId:t.sessionId,type:t.type??"unknown",role:t.message?.role??null,timestamp:t.timestamp??null,isSidechain:t.isSidechain===!0,cwd:t.cwd??null,gitBranch:t.gitBranch??null,version:t.version??null,contentText:n,toolNames:s,raw:e,usage:Yr(t.message),model:t.message?.model??null}}async function*nc(e){let t=R_(e,{encoding:"utf8"}),n=k_({input:t,crlfDelay:1/0});for await(let s of n){let r=N_(s);r!==null&&(yield r)}}var x_,zr,Kn=w(()=>{"use strict";x_=/\x1B\[[0-9;]*[a-zA-Z]/g;zr=12e3});import Me from"chalk";import{formatDistanceToNowStrict as O_,parseISO as v_}from"date-fns";function Q(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 O_(v_(e),{addSuffix:!0})}catch{return""}}function X(e){return e.slice(0,8)}function qr(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,$=w(()=>{"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")}});function rc(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 cn(e){if(!e)return[];let t=new Set,n=[];for(let s of sc){s.regex.lastIndex=0;for(let r of e.matchAll(s.regex)){let o=r[0],i=`${s.name}::${oc(o)}`;t.has(i)||(t.add(i),n.push({pattern:s.name,maskedPreview:rc(o),offset:r.index??0,severity:s.severity}))}}return n}function oc(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 sc)r.regex.lastIndex=0,t=t.replace(r.regex,o=>{let i=`${r.name}::${oc(o)}`;return s.has(i)||(s.add(i),n+=1),`[REDACTED ${r.name}: ${rc(o)}]`});return{redacted:t,count:n}}var sc,jt=w(()=>{"use strict";sc=[{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 Vr(e,t,n,s={}){s.insertOnly||e.prepare("DELETE FROM message_usage WHERE session_id = ?").run(t);let r=e.prepare(`
|
|
769
|
+
INSERT INTO message_usage (
|
|
770
|
+
message_uuid, session_id, model,
|
|
771
|
+
input_tokens, output_tokens, cache_create_tokens, cache_read_tokens,
|
|
772
|
+
timestamp
|
|
773
|
+
) VALUES (
|
|
774
|
+
@uuid, @session_id, @model,
|
|
775
|
+
@input, @output, @cc, @cr,
|
|
776
|
+
@ts
|
|
777
|
+
)
|
|
778
|
+
ON CONFLICT(message_uuid) DO UPDATE SET
|
|
779
|
+
model = excluded.model,
|
|
780
|
+
input_tokens = excluded.input_tokens,
|
|
781
|
+
output_tokens = excluded.output_tokens,
|
|
782
|
+
cache_create_tokens = excluded.cache_create_tokens,
|
|
783
|
+
cache_read_tokens = excluded.cache_read_tokens,
|
|
784
|
+
timestamp = excluded.timestamp
|
|
785
|
+
`);for(let o of n)o.usage&&o.role==="assistant"&&r.run({uuid:o.uuid,session_id:t,model:o.model,input:o.usage.inputTokens,output:o.usage.outputTokens,cc:o.usage.cacheCreateTokens,cr:o.usage.cacheReadTokens,ts:o.timestamp})}function ln(e,t){let n=e.prepare(`SELECT
|
|
786
|
+
COALESCE(SUM(input_tokens), 0) AS input_tokens,
|
|
787
|
+
COALESCE(SUM(output_tokens), 0) AS output_tokens,
|
|
788
|
+
COALESCE(SUM(cache_create_tokens), 0) AS cache_create_tokens,
|
|
789
|
+
COALESCE(SUM(cache_read_tokens), 0) AS cache_read_tokens
|
|
790
|
+
FROM message_usage WHERE session_id = ?`).get(t),s=e.prepare(`SELECT model, SUM(output_tokens) AS out
|
|
791
|
+
FROM message_usage
|
|
792
|
+
WHERE session_id = ? AND model IS NOT NULL
|
|
793
|
+
GROUP BY model
|
|
794
|
+
ORDER BY out DESC LIMIT 1`).get(t);e.prepare(`UPDATE sessions SET
|
|
795
|
+
total_input_tokens = @input,
|
|
796
|
+
total_output_tokens = @output,
|
|
797
|
+
total_cache_create_tokens = @cc,
|
|
798
|
+
total_cache_read_tokens = @cr,
|
|
799
|
+
primary_model = @model
|
|
800
|
+
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})}var Qn=w(()=>{"use strict"});var ic=w(()=>{"use strict";k()});function ac(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<$_)return"low_signal";for(let t of I_)if(t.test(e.auto_title))return"recursive_meta";for(let t of M_)if(t.test(e.auto_title))return"programmatic";for(let t of D_)if(t.test(e.auto_title))return"template_pending";return"clean"}var I_,Ut,M_,D_,$_,dn=w(()=>{"use strict";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,/^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],Ut=[/^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],M_=[/^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],D_=[/^Draft (a|an|marketing) [a-z]/i,/^Read the file at /i,/^Read this and then begin/i,/^read this and then begin/i],$_=20});function Kr(e){if(!e)return"";let t=e.split("|")[0];return t=t.replace(/\s*\([^)]*\)\s*$/,""),t=t.replace(/\s+/g," ").trim(),t}var cc=w(()=>{"use strict"});import{writeFileSync as P_,mkdirSync as F_,existsSync as j_}from"node:fs";import{join as dc}from"node:path";function W_(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 to(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=X_(t,e);if(n)return n;let s=t.match(/^[^.!?\n]{8,}?[.!?]/)?.[0]?.trim();return(s&&s.length<=U_?s:t.slice(0,B_)).trim()||null}function X_(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=eo(r);return o?un(`${s} \xB7 ${o}`):s}for(let s of G_){if(!e.match(s.match))continue;let o=s.prefix,i=s.extract?s.extract(e,t):eo(t);return i?s.completeFromExtract?un(i):un(`${o} \xB7 ${i}`):o}for(let s of Y_){if(!e.match(s.match))continue;let o=s.extract?s.extract(e,t):Zn(t);return o?s.completeFromExtract?un(o):un(`${s.prefix} \xB7 ${o}`):s.prefix}return null}function J_(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 lc(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 z_(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}function Zn(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 Kr(r)||r}return null}function un(e){return e.slice(0,H_).trim()}function Qr(e){let t=e.trim();return t.length<3?!0:q_.some(n=>n.test(t))}function K_(e){let t=e.trim().replace(/\s*\([^)]*\)\s*$/,"").trim();return V_.some(n=>n.test(t))}function eo(e){let t=Q_(e);return t===null?null:Kr(t)||t}function Q_(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(!Qr(l)){if(K_(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]&&!Qr(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&&!Qr(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(`
|
|
801
|
+
`).map(l=>l.trim()).find(l=>l.length>=4);if(d)return d.slice(0,60)}let a=e.split(`
|
|
802
|
+
`).map(d=>d.trim()).find(d=>d.length>=4);return a?a.slice(0,60):null}function no(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
|
|
803
|
+
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=W_(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
|
|
804
|
+
SET auto_title = ?,
|
|
805
|
+
auto_title_source = ?,
|
|
806
|
+
auto_title_generated_at = ?,
|
|
807
|
+
auto_title_history = ?
|
|
808
|
+
WHERE id = ?`).run(s,n,Date.now(),JSON.stringify(i),e),eh(e,s,n,a)}function Z_(){j(),j_(Zr)||F_(Zr,{recursive:!0})}function eh(e,t,n,s){try{Z_();let r=dc(Zr,`${e}.txt`),o=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${n} \xB7 updated ${s}
|
|
809
|
+
`;P_(r,o+t+`
|
|
810
|
+
`)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}var Zr,U_,B_,H_,G_,Y_,q_,V_,so=w(()=>{"use strict";k();D();ic();dn();cc();Zr=dc(C,"titles"),U_=80,B_=60,H_=100;G_=[{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=eo(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)=>z_(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)=>J_(t)},{match:/^You will receive a sample of user messages from a Claude Code session:/i,prefix:"[meta]",completeFromExtract:!0,extract:(e,t)=>lc(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)=>lc(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}`}}];Y_=[{match:/^Score this person'?s relevance/i,prefix:"Score relevance",extract:(e,t)=>Zn(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=Zn(t);return o?`${r} \xB7 ${o}`:r}},{match:/^Return ONLY valid JSON/i,prefix:"JSON-only",extract:(e,t)=>Zn(t)}];q_=[/^deep research$/i,/^reference(?: spec)?$/i,/^canonical spec/i,/^product context/i,/^services?(?: overview)?$/i,/^overview$/i,/^(?:introduction|template|instructions|context)$/i],V_=[/^(?:brand|brand brief|brand summary)$/i,/^(?:known facts?|facts)$/i,/^(?:client|prospect|customer|account)$/i,/^(?:topic|subject|brief|task)$/i,/^(?:about|for|re)$/i]});import{existsSync as th,mkdirSync as IN,readFileSync as nh,writeFileSync as MN}from"node:fs";import{homedir as sh}from"node:os";import{join as uc}from"node:path";import{z as ro}from"zod";function rh(){return process.env.RECALL_HOME??uc(sh(),".recall")}function oh(){return uc(rh(),"config.json")}function ah(){let e=oh();if(!th(e))return{};try{return JSON.parse(nh(e,"utf8"))}catch(t){return console.error("[auto-title-config] failed to parse config.json, using defaults:",t),{}}}function io(){let e=ah().autoTitle;if(!e)return{...oo};let t=ih.safeParse({...oo,...e});return t.success?t.data:{...oo}}var ih,oo,ao=w(()=>{"use strict";ih=ro.object({heuristicEnabled:ro.boolean().default(!0),agentEnabled:ro.boolean().default(!1)}),oo={heuristicEnabled:!0,agentEnabled:!1}});import{existsSync as Sc,readFileSync as Ah,writeFileSync as Nh,unlinkSync as Oh}from"node:fs";import{join as vh}from"node:path";function pn(){if(!Sc(Ct))return null;try{let e=Ah(Ct,"utf8"),t=JSON.parse(e);return typeof t.license_jwt!="string"||t.license_jwt.length===0?null:t}catch{return null}}function ts(e){j(),Nh(Ct,JSON.stringify(e,null,2)+`
|
|
811
|
+
`,{mode:384})}function yc(){Sc(Ct)&&Oh(Ct)}var Ct,Bt=w(()=>{"use strict";D();Ct=vh(C,"license.json")});var wc,lo,Tc,Rc,kc=w(()=>{"use strict";wc=`-----BEGIN PUBLIC KEY-----
|
|
735
812
|
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZysO2FffTLdyxQnTmnt78/ayvqz9
|
|
736
813
|
kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
|
|
737
814
|
-----END PUBLIC KEY-----
|
|
738
|
-
`,
|
|
739
|
-
`,{mode:384})}async function
|
|
815
|
+
`,lo="ES256",Tc="clauderecall.com",Rc="clauderecall-cli"});import{jwtVerify as Ih,importSPKI as Mh}from"jose";async function Dh(){return ns||(ns=await Mh(wc,lo),ns)}async function Ht(e){try{let t=await Dh(),{payload:n}=await Ih(e,t,{issuer:Tc,audience:Rc,algorithms:[lo]});return{valid:!0,claims:n}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}var ns,ss=w(()=>{"use strict";kc();ns=null});import{createHash as $h}from"node:crypto";import{hostname as Ph,userInfo as Fh,platform as jh,arch as Uh}from"node:os";function Wt(){let e="unknown";try{e=Fh().username}catch{}let t=[Ph(),e,jh(),Uh()];return $h("sha256").update(t.join("\0")).digest("hex")}var rs=w(()=>{"use strict"});function dt(){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 mn=w(()=>{"use strict"});import{existsSync as Bh,readFileSync as Hh,writeFileSync as Wh}from"node:fs";import{join as Xh}from"node:path";function os(){if(!Bh(uo))return null;try{let e=JSON.parse(Hh(uo,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function Yh(e){j(),Wh(uo,JSON.stringify(e,null,2)+`
|
|
816
|
+
`,{mode:384})}async function qh(e,t){let n=null,s=null;try{n=new AbortController,s=setTimeout(()=>n?.abort(),zh);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 xc(e,t={}){let n=os(),s=t.apiUrl??`${dt()}/api/license/check`,r=n?.license_key===e,o=!n||!r||Date.now()-new Date(n.last_checked_at).getTime()>=Gh;if(!t.force&&!o)return n;let i=await qh(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 Yh(a),a}function Cc(e){let t=os();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()>Jh?{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 uo,Gh,Jh,zh,po=w(()=>{"use strict";D();mn();uo=Xh(C,"license-check.json"),Gh=1440*60*1e3,Jh=720*60*60*1e3,zh=1e4});function Kh(e=Date.now()){return e<mo}function Ac(e=Date.now()){return Kh(e)?`(one-time purchase, $${Vh} through May 2026 Founder pricing, $${Lc} from June, lifetime updates either way)`:`(one-time purchase, $${Lc}, lifetime updates)`}var mo,Vh,Lc,go=w(()=>{"use strict";mo=Date.UTC(2026,5,1,7,0,0),Vh="29.69",Lc="49.69"});function ho(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<Qh;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): ${fo}`}}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): ${fo}`};if(e.status.tier==="free"){let o=mo-t;if(o>0&&o<=3*_o){let i=Math.max(1,Math.ceil(o/_o));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: ${fo}`}}}return null}var fo,_o,Qh,Nc=w(()=>{"use strict";go();fo="https://clauderecall.com/pricing",_o=1440*60*1e3,Qh=60*_o});var gn={};V(gn,{getLicenseStatus:()=>Le,isPro:()=>eE,performRevocationCheck:()=>Eo,printTrialBannerIfAny:()=>tE,requireProOrExit:()=>oe});async function Le(){let e=pn();if(!e)return{tier:"free"};let t=await Ht(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==Wt())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let n=Cc(e.license_key);return n?.revoked?{tier:"free",invalid_reason:n.reason}:Zh(e,t.claims)}async function Eo(e){let t=pn();if(!t)return{ran:!1,revoked:!1,reason:null,last_checked_at:null};let n=await xc(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 Zh(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 eE(){return(await Le()).tier==="pro"}async function oe(e){let t=await Le();if(t.tier==="pro")return;let n=ho({status:t});n&&process.stderr.write(`
|
|
740
817
|
${n.message}
|
|
741
818
|
`),process.stderr.write(`
|
|
742
819
|
${e} is a Pro feature.
|
|
743
820
|
|
|
744
821
|
Buy a license at https://clauderecall.com/pricing
|
|
745
|
-
${
|
|
822
|
+
${Ac()}.
|
|
746
823
|
Then run: recall activate <license-key>
|
|
747
824
|
|
|
748
825
|
`),t.invalid_reason&&process.stderr.write(`(stored license is invalid: ${t.invalid_reason})
|
|
749
826
|
|
|
750
|
-
`),process.exit(1)}async function
|
|
827
|
+
`),process.exit(1)}async function tE(){let e=await Le(),t=ho({status:e});return t?(process.stderr.write(`
|
|
751
828
|
${t.message}
|
|
752
829
|
|
|
753
|
-
`),!0):!1}var ge=
|
|
754
|
-
`).slice(2,4).map(i=>i.trim().replace(/file:\/\/.*?(\bdist\b|\bsrc\b)/g,"$1")).join(" << "),o=`${new Date().toISOString()} ${
|
|
755
|
-
`;try{
|
|
756
|
-
`)){let o=r.trim();if(!o||!
|
|
757
|
-
`),""}}function
|
|
830
|
+
`),!0):!1}var ge=w(()=>{"use strict";Bt();ss();rs();po();go();Nc()});import{appendFileSync as nE}from"node:fs";import{join as sE}from"node:path";function Xt(e,t){let s=(new Error().stack??"").split(`
|
|
831
|
+
`).slice(2,4).map(i=>i.trim().replace(/file:\/\/.*?(\bdist\b|\bsrc\b)/g,"$1")).join(" << "),o=`${new Date().toISOString()} ${oE} ${e}: ${t} | caller: ${s}
|
|
832
|
+
`;try{nE(rE,o)}catch{}}var rE,oE,bo=w(()=>{"use strict";D();rE=sE(C,"daemon.log"),oE="[state-files-audit]"});import{writeFileSync as HO,readFileSync as iE,existsSync as aE}from"node:fs";import{join as cE}from"node:path";function Oc(){if(!aE(is))return null;try{return iE(is,"utf8").trim()}catch{return null}}var is,So=w(()=>{"use strict";D();bo();is=cE(C,"daemon.token")});import{execFileSync as mE}from"node:child_process";function fE(e){for(let t of gE)if(e.includes(t))return!0;return!1}function _E(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??hE(),n=new Set(e.excludePids??[]),s=[];for(let r of t.split(`
|
|
833
|
+
`)){let o=r.trim();if(!o||!fE(o))continue;let i=o.split(/\s+/);if(_E(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:EE(l),etime:l,command:o})}return s.sort((r,o)=>r.etimeSeconds-o.etimeSeconds),s}function hE(){try{return mE("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}
|
|
834
|
+
`),""}}function EE(e){if(!e)return 0;let t=0,n=e,s=e.indexOf("-");s>=0&&(t=Dc(e.slice(0,s)),n=e.slice(s+1));let r=n.split(":").map(Dc),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 Dc(e){let t=Number(e);return Number.isFinite(t)?t:0}function fn(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 gE,_n=w(()=>{"use strict";gE=["dist/daemon/entrypoint.js"]});import{existsSync as $c,readFileSync as bE,writeFileSync as iv,unlinkSync as SE}from"node:fs";import{basename as yE,join as yo}from"node:path";function cs(){return{pid:Pc,port:wE,token:is}}function TE(){return Fc(Pc)}function Fc(e){if(!$c(e))return null;try{let t=JSON.parse(bE(e,"utf8"));return typeof t.pid!="number"||typeof t.port!="number"?null:t}catch{return null}}function jc(e={}){let t=e.paths??cs(),n=e.listDaemons??(()=>st({excludePids:[process.pid]})),s=e.isProcessAlive??Bc,r=n();if(r.length>0){let i=r.map(a=>a.pid).join(",");return Xt("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=Fc(t.pid);return o&&s(o.pid)?(Xt("clear-refused",`pidfile owner pid=${o.pid} alive but not surfaced by ps`),{deleted:!1,reason:`refused: pidfile owner pid=${o.pid} alive`,cleared:[]}):RE({paths:t})}function RE(e={}){let t=e.paths??cs(),n=[];for(let s of[t.pid,t.port,t.token])if($c(s))try{SE(s),n.push(yE(s))}catch{}return Xt("clear-force",`cleared: ${n.join(",")||"(none -- files already absent)"}`),{deleted:!0,reason:`cleared ${n.length} file(s)`,cleared:n}}function Uc(){let e=jc();e.deleted||Xt("clear-refused-shim",e.reason)}function hn(e){return Bc(e)}function Bc(e){try{return process.kill(e,0),!0}catch{return!1}}function ie(){let e=TE();return e?hn(e.pid)?e:(jc(),null):null}var Pc,wE,as,Xe=w(()=>{"use strict";D();So();_n();bo();Pc=yo(C,"daemon.pid"),wE=yo(C,"daemon.port"),as=yo(C,"daemon.log")});import{existsSync as LE}from"node:fs";import{dirname as Xc}from"node:path";import{fileURLToPath as AE}from"node:url";function te(){if(ls)return ls;let e=Xc(AE(import.meta.url));for(;!LE(`${e}/package.json`);){let t=Xc(e);if(t===e)throw new Error(`package.json not found walking up from ${import.meta.url}`);e=t}return ls=e,ls}var ls,Ge=w(()=>{"use strict";ls=null});import{execFileSync as Yc}from"node:child_process";function PE(e){for(let t of $E)if(e.includes(t))return!0;return!1}function Lt(e={}){let t=e.psOutput??FE(),n=e.isProcessAlive??jE,s=e.getParentCommand??UE,r=[];for(let o of t.split(`
|
|
835
|
+
`)){let i=o.trim();if(!i||!PE(i)||BE(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:HE(u),pcpu:Number.isFinite(f)?f:0,rssKb:Number.isFinite(g)?g:0,orphan:!_,parentCommand:_?s(l):null})}return r}function FE(){try{return Yc("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}
|
|
836
|
+
`),""}}function jE(e){if(!Number.isFinite(e)||e<=1)return!1;try{return process.kill(e,0),!0}catch{return!1}}function UE(e){if(!Number.isFinite(e)||e<=1)return null;try{let n=Yc("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 BE(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 HE(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}function us(e){return e.pcpu>=WE&&e.etimeSeconds>=XE}function zc(e){return e.reduce((t,n)=>t+(Number.isFinite(n.rssKb)&&n.rssKb>0?n.rssKb:0),0)}function qc(e){let t=e??Lt(),n=t.length,s=zc(t),r=t.filter(p=>p.orphan),o=r.length,i=zc(r),a=o>Jc,d=i>GE;if(!(a||d))return{flagged:!1,severity:"ok",count:n,aggregateRssKb:s,orphanCount:o,orphanRssKb:i,message:null};let u=[];a&&u.push(`${o} orphaned MCP children (threshold ${Jc})`),d&&u.push(`${JE(i)} aggregate RSS across orphaned children (threshold 1 GiB)`);let m=`Zombie MCP threshold breached: ${u.join(" + ")}. Each orphaned MCP child (its parent claude/VS Code tab has exited) still 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,orphanCount:o,orphanRssKb:i,message:m}}function JE(e){return e<1024?`${e} KB`:e<1024*1024?`${(e/1024).toFixed(1)} MB`:`${(e/(1024*1024)).toFixed(2)} GB`}var $E,WE,XE,Jc,GE,En=w(()=>{"use strict";$E=["dist/mcp-server.js","dist/mcp/server.js"];WE=50,XE=60;Jc=4,GE=1024*1024});function pt(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 ps=w(()=>{"use strict"});function ms(e,t="PASSIVE",n){let s=Date.now(),r=e.pragma(`wal_checkpoint(${t})`),o=YE(r),i={busy:o.busy,log:o.log,moved:o.checkpointed,mode:t,durationMs:Date.now()-s};return n&&(t==="PASSIVE"&&i.log>=zE&&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 YE(e){let t=Array.isArray(e)?e[0]??{}:e??{};return{busy:wo(t.busy),log:wo(t.log),checkpointed:wo(t.checkpointed)}}function wo(e){return typeof e=="number"&&Number.isFinite(e)?e:typeof e=="bigint"?Number(e):0}var Wv,Vc,zE,gs=w(()=>{"use strict";ps();En();Wv=5*6e4,Vc=1073741824,zE=100});import{writeFileSync as vb}from"node:fs";import{join as Ib}from"node:path";function Lo(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Es(e){return E().prepare("SELECT alias FROM session_aliases WHERE session_id = ?").get(e)?.alias??null}function Db(){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:Lo(t.previous_aliases)}))}function Gt(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=Lo(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)
|
|
758
837
|
VALUES (?, ?, ?, ?)
|
|
759
838
|
ON CONFLICT(session_id) DO UPDATE SET
|
|
760
839
|
alias = excluded.alias,
|
|
761
840
|
updated_at = excluded.updated_at,
|
|
762
|
-
previous_aliases = excluded.previous_aliases`).run(e,n,r,JSON.stringify(i)),
|
|
763
|
-
WHERE session_id = ?`).run(n,JSON.stringify(r),e),
|
|
841
|
+
previous_aliases = excluded.previous_aliases`).run(e,n,r,JSON.stringify(i)),Al(),{session_id:e,alias:n,updated_at:r,previous_aliases:i}}function Ll(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=Lo(s.previous_aliases);r.push({alias:s.alias,replaced_at:n}),t.prepare(`UPDATE session_aliases SET alias = '', updated_at = ?, previous_aliases = ?
|
|
842
|
+
WHERE session_id = ?`).run(n,JSON.stringify(r),e),Al()}function Al(){try{j();let e=Db(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};vb(Mb,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}var Mb,At=w(()=>{"use strict";k();D();Mb=Ib(C,"aliases.json")});import{existsSync as Ml,mkdirSync as eS,readFileSync as tS,writeFileSync as nS}from"node:fs";import{homedir as sS}from"node:os";import{join as Dl}from"node:path";import{z as Je}from"zod";function $l(){return process.env.RECALL_HOME??Dl(sS(),".recall")}function rS(){let e=$l();Ml(e)||eS(e,{recursive:!0})}function Pl(){return Dl($l(),"config.json")}function jl(){let e=Pl();if(!Ml(e))return{};try{return JSON.parse(tS(e,"utf8"))}catch(t){return console.error("[semantic-config] failed to parse config.json, using defaults:",t),{}}}function we(){let e=jl().semantic;if(!e)return{...Ss};let t=Fl.safeParse({...Ss,...e});return t.success?t.data:{...Ss}}function ze(e,t="unknown"){rS();let n=jl(),s=Fl.parse({...Ss,...n.semantic??{},...e}),r={...n,semantic:s};return nS(Pl(),JSON.stringify(r,null,2)),oS(s.enabled,t),s}function oS(e,t="unknown"){let n=e?"1":"0",s=null;try{E().prepare(`INSERT INTO app_settings(key, value) VALUES ('semantic_enabled', ?)
|
|
843
|
+
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 Pt(ee);try{r.exec(`CREATE TABLE IF NOT EXISTS app_settings (
|
|
844
|
+
key TEXT PRIMARY KEY,
|
|
845
|
+
value TEXT NOT NULL
|
|
846
|
+
);`),r.prepare(`INSERT INTO app_settings(key, value) VALUES ('semantic_enabled', ?)
|
|
847
|
+
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)})`)}}var Fl,Ss,Sn=w(()=>{"use strict";zn();k();D();Fl=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)}),Ss={enabled:!1,ratePerMinute:30,lastProcessedSessionId:null,backfillPaused:!1,autoExtractEnabled:!1,autoExtractIntervalMinutes:60,autoExtractBatchSize:1,autoResumeWorker:!1}});import{writeFileSync as iS}from"node:fs";import{join as aS}from"node:path";function lS(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function Ul(e,t){let n=lS(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)})(),dS(),{tag:n,added:!0})}function Bl(e){return E().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function dS(){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};iS(cS,JSON.stringify(s,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var cS,No=w(()=>{"use strict";k();D();cS=aS(C,"tags.json")});function uS(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 Hl(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,
|
|
764
848
|
NULLIF(sa.alias, '') AS alias,
|
|
765
849
|
COALESCE(s.first_user_message, '') AS first_user_message
|
|
766
850
|
FROM sessions s
|
|
@@ -770,23 +854,48 @@ ${t.message}
|
|
|
770
854
|
ORDER BY COALESCE(s.started_at, '') DESC
|
|
771
855
|
LIMIT @limit`).all(n).map(i=>{let a=t.prepare(`SELECT role, COALESCE(content_text, '') AS content_text
|
|
772
856
|
FROM messages WHERE session_id = ?
|
|
773
|
-
ORDER BY COALESCE(timestamp, ''), rowid`).all(i.id),l=
|
|
857
|
+
ORDER BY COALESCE(timestamp, ''), rowid`).all(i.id),l=uS(a,5).map(u=>`${u.role}: ${u.content_text.slice(0,400)}`).join(`
|
|
774
858
|
---
|
|
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:
|
|
776
|
-
`)}var
|
|
859
|
+
`);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:Bl(i.id)}})}var Wl=w(()=>{"use strict";k();No()});import{z as Ae}from"zod";function Xl(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(`
|
|
860
|
+
`)}var MM,DM,$M,PM,Gl=w(()=>{"use strict";MM={project:Ae.string().optional().describe("Exact project name match (optional)."),collectionId:Ae.string().optional().describe("Restrict to sessions in this collection (optional)."),sessionId:Ae.string().optional().describe("Full session UUID to tag just one session (optional)."),untaggedOnly:Ae.boolean().optional().describe("Skip sessions that already have any tag (default: true)."),limit:Ae.number().int().min(1).max(500).optional().describe("Max sessions to process (default: 100)."),minTags:Ae.number().int().min(1).max(10).optional().describe("Minimum tags per session (default: 2)."),maxTags:Ae.number().int().min(1).max(10).optional().describe("Maximum tags per session (default: 4).")};DM={sessionId:Ae.string().describe("Session UUID (or 8+-char prefix) to summarize."),mode:Ae.enum(["brief","detailed"]).optional().describe("brief = 3-5 bullets; detailed = paragraph + bullets. Default: brief.")},$M={sessionId:Ae.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")},PM={sessionId:Ae.string().describe("Session UUID (or 8+-char prefix) to find similar sessions to."),limit:Ae.number().int().min(1).max(20).optional().describe("How many similar sessions to surface (default: 5).")}});function Jl(e,t){let n=pS.get(e);if(!(!n||n.size===0))for(let s of n)try{s(t)}catch{}}var pS,zl=w(()=>{"use strict";pS=new Map});import{existsSync as mS,statSync as gS}from"node:fs";import{delimiter as fS,join as _S}from"node:path";function Yl(e){if(e.includes("/")||e.includes("\\")||e.includes(".."))return null;let t=(process.env.PATH??"").split(fS).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=_S(s,r);try{if(mS(o)&&gS(o).isFile())return o}catch{}}return null}function ql(e){if(process.platform!=="win32"||!e)return!1;let t=e.toLowerCase();return t.endsWith(".cmd")||t.endsWith(".bat")}var Vl=w(()=>{"use strict"});var ys={};V(ys,{_resetClaudePathCacheForTests:()=>SS,buildScanPrompt:()=>Ql,isClaudeCliAvailable:()=>ae,runClaudeCliScan:()=>kS,spawnClaudePrompt:()=>Nt});import{spawn as hS}from"node:child_process";function Kl(){if(Jt!==void 0&&yn!==void 0)return{path:Jt,available:yn};let e=Yl("claude");return Jt=e??"claude",yn=e!==null,{path:Jt,available:yn}}function bS(){return Kl().path}function ae(){return Kl().available}function SS(){Jt=void 0,yn=void 0}function Ql(e){return Xl({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 yS(e,t){let n=t.get(e);return n||e.slice(0,8)}function wS(e){try{return Hl(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 TS(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),Jl(t,{type:"progress",current:r.size,total:n,sessionId:m,sessionLabel:yS(m,s)}))}}}function RS(e){let t="";return n=>{t+=n.toString("utf8");let s=t.indexOf(`
|
|
777
861
|
`);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
|
|
779
|
-
|
|
780
|
-
|
|
862
|
+
`)}}}async function kS(e,t={},n){let s=!!t.scanId,r=s?wS(e):[],o=new Map(r.map(d=>[d.id,d.label])),i=r.length,a;return s&&t.scanId&&(a=TS({scanId:t.scanId,total:i,labelTable:o})),Zl({prompt:Ql(e),allowedTools:ES.split(","),opts:t,onProgress:n,onStdoutLine:a,outputFormat:s?"stream-json":"json"})}async function Nt(e,t,n={},s){return Zl({prompt:e,allowedTools:t,opts:n,onProgress:s,outputFormat:"json"})}function Zl(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=bS(),u=hS(l,a,{stdio:["ignore","pipe","pipe"],shell:ql(l)||process.platform==="win32"&&Jt==="claude"}),m=[],p=[],g=o?RS(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 ES,Jt,yn,Ye=w(()=>{"use strict";Wl();Gl();zl();Vl();ES=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});import{existsSync as xS,mkdirSync as CS,writeFileSync as LS}from"node:fs";import{homedir as AS}from"node:os";import{join as Oo}from"node:path";function MS(){return process.env.RECALL_HOME??Oo(AS(),".recall")}function ed(){return Oo(MS(),"semantic")}function DS(){let e=ed();xS(e)||CS(e,{recursive:!0})}function $S(e){let t=E(),n=t.prepare(`SELECT s.id, s.message_count, s.first_user_message,
|
|
863
|
+
p.name AS project,
|
|
864
|
+
NULLIF(sa.alias, '') AS alias
|
|
865
|
+
FROM sessions s
|
|
866
|
+
JOIN projects p ON p.id = s.project_id
|
|
867
|
+
LEFT JOIN session_aliases sa ON sa.session_id = s.id
|
|
868
|
+
WHERE s.id = ?`).get(e);if(!n)return null;let s=t.prepare(`SELECT role, content_text
|
|
869
|
+
FROM messages
|
|
870
|
+
WHERE session_id = ? AND is_sidechain = 0
|
|
871
|
+
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>OS)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(`
|
|
872
|
+
|
|
873
|
+
`),messageCount:n.message_count}}function PS(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(`
|
|
874
|
+
`)}function FS(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 jS(e){let t=E(),n=e.keywords.join(",");t.prepare(`INSERT INTO session_semantic
|
|
875
|
+
(session_id, summary, keywords, model, source_message_count, generated_at)
|
|
876
|
+
VALUES (@session_id, @summary, @keywords, @model, @source_message_count, @generated_at)
|
|
877
|
+
ON CONFLICT(session_id) DO UPDATE SET
|
|
878
|
+
summary = excluded.summary,
|
|
879
|
+
keywords = excluded.keywords,
|
|
880
|
+
model = excluded.model,
|
|
881
|
+
source_message_count = excluded.source_message_count,
|
|
882
|
+
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}),DS();let s=Oo(ed(),`${e.sessionId}.json`);LS(s,JSON.stringify({version:NS,session_id:e.sessionId,summary:e.summary,keywords:e.keywords,model:e.model,source_message_count:e.sourceMessageCount,generated_at:e.generatedAt},null,2))}function US(){let t=we().ratePerMinute,n=t/6e4;return(!ws||ws.capacity!==t)&&(ws={tokens:t,capacity:t,refillPerMs:n,lastRefill:Date.now()}),ws}function BS(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 HS(e){for(;;){if(e?.aborted)throw new Error("aborted");let t=US();if(BS(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 WS(e,t={}){let n=we();if(!n.enabled)return{sessionId:e,ok:!1,reason:"disabled"};if(!ae())return{sessionId:e,ok:!1,reason:"claude-cli-missing"};let s=$S(e);if(!s)return{sessionId:e,ok:!1,reason:"session-not-found"};if(s.messageCount<vS)return{sessionId:e,ok:!1,reason:"too-short"};if(!s.excerpt.trim())return{sessionId:e,ok:!1,reason:"empty-excerpt"};await HS(t.signal);let r=PS(s),o=await Nt(r,IS,{model:n.model});if(!o.success)return{sessionId:e,ok:!1,reason:`claude-cli-exit-${o.exitCode??"null"}`,model:n.model??null};let i=FS(o.stdout);return i?(jS({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 Ts(e={}){let t=we();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
|
|
883
|
+
FROM sessions s
|
|
884
|
+
LEFT JOIN session_semantic ss ON ss.session_id = s.id
|
|
885
|
+
WHERE ${o}
|
|
886
|
+
ORDER BY COALESCE(s.started_at, '') ASC, s.id ASC
|
|
887
|
+
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||we().backfillPaused)break;a.currentSessionId=d,e.onProgress?.({...a});try{(await WS(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 td(){let e=we(),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:ae(),ratePerMinute:e.ratePerMinute,model:e.model??null,totalSessions:n,processedSessions:s,pendingSessions:Math.max(0,n-s),lastProcessedSessionId:e.lastProcessedSessionId,backfillPaused:e.backfillPaused}}var NS,OS,vS,IS,ws,Rs=w(()=>{"use strict";k();Ye();Sn();NS=1,OS=12e3,vS=3,IS=[];ws=null});var rd={};V(rd,{downloadModel:()=>Mo,getModelDir:()=>xs,isModelInstalled:()=>rt,uninstallModel:()=>Do});import{existsSync as Io,mkdirSync as nd,rmSync as vo,createWriteStream as XS,statSync as GS}from"node:fs";import{join as ks}from"node:path";import{createHash as JS}from"node:crypto";import{readFile as zS}from"node:fs/promises";function xs(){return ks(C,"models","BAAI","bge-base-en-v1.5")}function rt(){let e=xs();return sd.every(t=>Io(ks(e,t.path)))}async function Mo(e){let t=xs();nd(t,{recursive:!0}),nd(ks(t,"onnx"),{recursive:!0});for(let n of sd){let s=ks(t,n.path),r=YS+n.path,o=0;Io(s)&&(o=GS(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=XS(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 vo(s),new Error(`Refusing to install: SHA-256 not pinned for ${n.path}. Update model-download.ts.`);let f=await zS(s);if(JS("sha256").update(f).digest("hex")!==n.sha256)throw vo(s),new Error(`SHA-256 mismatch for ${n.path}`)}}function Do(){let e=xs();Io(e)&&vo(e,{recursive:!0,force:!0})}var YS,sd,wn=w(()=>{"use strict";D();YS="https://huggingface.co/BAAI/bge-base-en-v1.5/resolve/main/",sd=[{path:"config.json",sha256:"bc00af31a4a31b74040d73370aa83b62da34c90b75eb77bfa7db039d90abd591"},{path:"tokenizer.json",sha256:"d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"},{path:"tokenizer_config.json",sha256:"9261e7d79b44c8195c1cada2b453e55b00aeb81e907a6664974b4d7776172ab3"},{path:"onnx/model.onnx",sha256:"9bc579acdba21c253c62a9bf866891355a63ffa3442b52c8a37d75b2ccb91848"}]});function Cs(e){let t=null;return()=>t||(t=(async()=>{try{return await e()}finally{t=null}})(),t)}var $o=w(()=>{"use strict"});function qS(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(`
|
|
888
|
+
`)}var Ls,fe,zt=w(()=>{"use strict";Ls="BAAI/bge-base-en-v1.5",fe=class extends Error{kind;path;underlying;cause;constructor(t){super(qS(t)),this.name="EmbedderUnavailableError",this.kind=t.kind,this.path=t.path,this.underlying=t.underlying,this.cause=t}}});var Os={};V(Os,{embed:()=>ry,embedQuery:()=>oy,getEmbedderStatus:()=>sy,loadEmbedder:()=>ny,unloadEmbedder:()=>iy});import{Worker as VS}from"node:worker_threads";import{join as KS}from"node:path";import{existsSync as QS}from"node:fs";function ZS(){return KS(te(),"dist","daemon","embedder-worker.js")}function od(e){for(let t of Tn.values())t.reject(e);Tn.clear()}function ey(){if(ot)return ot;let e=ZS();if(!QS(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 VS(e);return t.on("message",n=>{let s=Tn.get(n.id);s&&(Tn.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));od(s),ot=null,qe=!1}),t.on("exit",n=>{n!==0&&(console.error(`[embedder-worker] exited with code ${n}`),od(new Error(`embedder worker exited with code ${n}`))),ot=null,qe=!1}),ot=t,t}function As(e){return new Promise((t,n)=>{let s;try{s=ey()}catch(r){n(r instanceof Error?r:new Error(String(r)));return}Tn.set(e.id,{resolve:t,reject:n}),s.postMessage(e)})}function Ns(){return Po=Po+1>>>0,String(Po)}function jo(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 ny(){if(!(qe&&ot))try{await ty(),qe=!0}catch(e){throw qe=!1,e}}function sy(){return{loaded:qe,modelId:Ls,dim:768}}async function ry(e){if(!qe)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return jo(await As({id:Ns(),type:"embed",texts:e})).embeddings.map(n=>new Float32Array(n))}async function oy(e){if(!qe)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=jo(await As({id:Ns(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function iy(){if(!ot){qe=!1;return}try{await As({id:Ns(),type:"unload"})}catch{}qe=!1;let e=ot;ot=null;try{await e.terminate()}catch{}}var ot,Tn,Po,qe,ty,id=w(()=>{"use strict";Ge();$o();zt();ot=null,Tn=new Map,Po=0,qe=!1;ty=Cs(async()=>{jo(await As({id:Ns(),type:"load"}))})});var Ho={};V(Ho,{LLAMACPP_MODEL_ID:()=>cd,MODEL_ID:()=>Ls,embed:()=>fy,embedQuery:()=>_y,getEmbedderStatus:()=>gy,loadEmbedder:()=>my,unloadEmbedder:()=>hy});import{Worker as ay}from"node:worker_threads";import{join as cy}from"node:path";import{existsSync as ly}from"node:fs";function dy(){return cy(te(),"dist","daemon","embedder-worker-llamacpp.js")}function ad(e){for(let t of Rn.values())t.reject(e);Rn.clear()}function uy(){if(it)return it;let e=dy();if(!ly(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 ay(e);return t.on("message",n=>{let s=Rn.get(n.id);s&&(Rn.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));ad(s),it=null,Ve=!1}),t.on("exit",n=>{n!==0&&(console.error(`[embedder-worker-llamacpp] exited with code ${n}`),ad(new Error(`embedder worker exited with code ${n}`))),it=null,Ve=!1}),it=t,t}function vs(e){return new Promise((t,n)=>{let s;try{s=uy()}catch(r){n(r instanceof Error?r:new Error(String(r)));return}Rn.set(e.id,{resolve:t,reject:n}),s.postMessage(e)})}function Is(){return Uo=Uo+1>>>0,String(Uo)}function Bo(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 my(){if(!(Ve&&it))try{await py(),Ve=!0}catch(e){throw Ve=!1,e}}function gy(){return{loaded:Ve,modelId:cd,dim:768}}async function fy(e){if(!Ve)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");return Bo(await vs({id:Is(),type:"embed",texts:e})).embeddings.map(n=>new Float32Array(n))}async function _y(e){if(!Ve)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=Bo(await vs({id:Is(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}async function hy(){if(!it){Ve=!1;return}try{await vs({id:Is(),type:"unload"})}catch{}Ve=!1;let e=it;it=null;try{await e.terminate()}catch{}}var cd,it,Rn,Uo,Ve,py,ld=w(()=>{"use strict";Ge();$o();zt();zt();cd="BAAI/bge-base-en-v1.5-gguf-q8_0";it=null,Rn=new Map,Uo=0,Ve=!1;py=Cs(async()=>{Bo(await vs({id:Is(),type:"load"}))})});var Ms={};V(Ms,{EmbedderUnavailableError:()=>fe,embed:()=>Ne,embedQuery:()=>by,getEmbedderStatus:()=>Z,loadEmbedder:()=>Ke,unloadEmbedder:()=>Sy});function Ey(){let e=(process.env.RECALL_EMBEDDER_BACKEND??"").trim().toLowerCase();return e==="llama"||e==="llamacpp"?Ho:(e===""||e==="onnx"||process.stderr.write(`[embedder] RECALL_EMBEDDER_BACKEND="${e}" is not recognized; falling back to onnx. Valid values: onnx, llama.
|
|
889
|
+
`),Os)}var kn,Ke,Z,Ne,by,Sy,Pe=w(()=>{"use strict";id();ld();zt();kn=Ey(),Ke=kn.loadEmbedder,Z=kn.getEmbedderStatus,Ne=kn.embed,by=kn.embedQuery,Sy=kn.unloadEmbedder});function dd(e){return e.replace(/```json[\s\S]*?```/g,"[tool-call]").replace(/\{[\s\S]{200,}?\}/g,"[json-object]")}function yy(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=dd(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=dd(l.content_text??"");return`[${u}] ${m}`}).join(`
|
|
781
890
|
|
|
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
|
|
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(`
|
|
891
|
+
`);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 Wo(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 yy(n)}var Xo=w(()=>{"use strict";k()});function Go(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 Jo=w(()=>{"use strict";k()});function ud(){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 zo(){return E().prepare("UPDATE chunk_queue SET action = 'embed' WHERE action = 'pending_post_migration'").run().changes}function ky(){return E().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}function pd(){return{running:wy,queueDepth:ky(),lastProcessedAt:Ty,blacklistedCount:Ry.size,pausedForMigration:ud()}}var wy,Ty,Ry,Ds=w(()=>{"use strict";k();Pe();Xo();Jo();wy=!1,Ty=null,Ry=new Set});var md={};V(md,{ensureLlamaCppInstalled:()=>Dy});import{spawn as xy}from"node:child_process";import{existsSync as Cy}from"node:fs";import{dirname as Ly,resolve as Ay}from"node:path";import{fileURLToPath as Ny}from"node:url";function vy(){let e=Ly(Ny(import.meta.url));return Ay(e,"..","..")}async function Iy(){try{return await import("node-llama-cpp"),!0}catch{return!1}}function My(e){return new Promise(t=>{let n=["install","--no-save","--no-audit","--no-fund","--prefix",e,`node-llama-cpp@${Oy}`],s=xy("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 Dy(){if(await Iy())return{ok:!0,action:"already-installed"};let e=vy();return Cy(e)?(console.log(`Installing node-llama-cpp into ${e} ...`),await My(e)):{ok:!1,error:`package root not found at ${e}`}}var Oy,gd=w(()=>{"use strict";Oy="3.18.1"});var js={};V(js,{BGE_GGUF_Q8_EXPECTED_BYTES:()=>Ed,BGE_GGUF_Q8_SHA256:()=>Yt,BGE_GGUF_Q8_URL:()=>hd,downloadGgufModel:()=>Gy,getGgufModelDir:()=>xn,getGgufModelPath:()=>Fs,isGgufModelInstalled:()=>Xy,uninstallGgufModel:()=>Jy,verifyGgufModelHash:()=>yd});import{createHash as fd}from"node:crypto";import{createWriteStream as $y,existsSync as Ps,mkdirSync as Py,readFileSync as Fy,rmSync as $s,statSync as _d,writeFileSync as jy}from"node:fs";import{createReadStream as Uy}from"node:fs";import{join as Yo}from"node:path";function xn(){return Yo(C,"models","gguf")}function Fs(){return Yo(xn(),By)}function bd(){return Yo(xn(),Hy)}function Wy(){try{let e=Fy(bd(),"utf8"),t=JSON.parse(e);return typeof t.size=="number"&&typeof t.mtimeMs=="number"&&typeof t.sha256=="string"?t:null}catch{return null}}function Sd(e){try{jy(bd(),JSON.stringify(e)+`
|
|
892
|
+
`,"utf8")}catch(t){console.warn("[recall] gguf verify-cache write failed (will rehash on next start):",t instanceof Error?t.message:String(t))}}function Xy(){return Ps(Fs())}async function yd(){let e=Fs();if(!Ps(e))return{ok:!1,reason:"model file missing"};let t=_d(e),n=Wy();if(n!==null&&n.size===t.size&&n.mtimeMs===t.mtimeMs&&n.sha256===Yt)return{ok:!0};let s=fd("sha256"),r=Uy(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!==Yt?(console.error(`[recall] GGUF integrity check failed at ${e}: computed ${o}, expected ${Yt}`),{ok:!1,reason:"model integrity check failed"}):(Sd({size:t.size,mtimeMs:t.mtimeMs,sha256:o}),{ok:!0})}async function Gy(e){let t=xn();Py(t,{recursive:!0});let n=Fs();if(Ps(n)){if((await yd()).ok)return;$s(n)}let s=await fetch(hd);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):Ed,a=fd("sha256"),d=$y(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{$s(n,{force:!0})}catch{}}}let p=a.digest("hex");if(p!==Yt)throw $s(n,{force:!0}),console.error(`[recall] GGUF integrity check failed during download: computed ${p}, expected ${Yt}`),new fe({kind:"platform-unsupported",detail:"GGUF model integrity check failed during download"});let g=_d(n);Sd({size:g.size,mtimeMs:g.mtimeMs,sha256:p})}function Jy(){let e=xn();Ps(e)&&$s(e,{recursive:!0,force:!0})}var Yt,hd,Ed,By,Hy,Us=w(()=>{"use strict";D();zt();Yt="ad1afe72cd6654a558667a3db10878b049a75bfd72912e1dabb91310d671173c",hd="https://huggingface.co/CompendiumLabs/bge-base-en-v1.5-gguf/resolve/main/bge-base-en-v1.5-q8_0.gguf",Ed=118438752,By="bge-base-en-v1.5-q8_0.gguf",Hy=".gguf-verify-cache.json"});var Ld={};V(Ld,{applyIntelMacOnnxFallback:()=>Ko,detectRunningClaudeOnWindows:()=>Cd,ensureTransformersInstalled:()=>sw});import{execFileSync as Hs,spawn as zy}from"node:child_process";import{existsSync as qo,mkdtempSync as Yy,mkdirSync as qy,readdirSync as xd,rmSync as wd}from"node:fs";import{createRequire as Vy}from"node:module";import{tmpdir as Ky}from"node:os";import{dirname as Vo,join as Ws,resolve as Qy}from"node:path";import{fileURLToPath as Zy}from"node:url";function Rd(e){let t=Ws(e,"bin");if(!qo(t))return!1;try{for(let n of xd(t)){if(!n.startsWith("napi-"))continue;let s=Ws(t,n,"darwin","x64","onnxruntime_binding.node");if(qo(s))return!0}}catch{return!1}return!1}function kd(){let e=Vo(Zy(import.meta.url));return Qy(e,"..","..")}async function tw(){try{return await import("@huggingface/transformers"),!0}catch{return!1}}function nw(e){return new Promise(t=>{let n=["install","--no-save","--no-audit","--no-fund","--prefix",e,`@huggingface/transformers@${ew}`],s=zy("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 Cd(){if(process.platform!=="win32")return!1;try{return Hs("tasklist",["/FI","IMAGENAME eq claude.exe"],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).toLowerCase().includes("claude.exe")}catch{return!1}}async function Ko(e){if(process.platform!=="darwin"||process.arch!=="x64")return;let t;try{let s=Td.resolve("@huggingface/transformers/package.json",{paths:[e]}),r=Td.resolve("onnxruntime-node/package.json",{paths:[Vo(s)]});t=Vo(r)}catch{return}if(Rd(t))return;console.log(`[recall] Intel macOS detected: onnxruntime-node at ${t} is missing the darwin/x64 binding. Applying ${Bs} fallback (npm overrides did not propagate through the scoped-package hoisting).`);let n=Yy(Ws(Ky(),"recall-ort-pin-"));try{Hs("npm",["pack","--pack-destination",n,`onnxruntime-node@${Bs}`],{cwd:n,stdio:["ignore","inherit","inherit"]});let s=xd(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(Rd(t)){console.log("[recall] onnxruntime-node@"+Bs+" already in place (concurrent install raced us). Nothing to do.");return}wd(t,{recursive:!0}),qy(t,{recursive:!0}),Hs("tar",["-xzf",Ws(n,s),"-C",t,"--strip-components=1"],{stdio:["ignore","inherit","inherit"]});try{Hs("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@${Bs} 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{wd(n,{recursive:!0,force:!0})}catch{}}}async function sw(){if(await tw())return await Ko(kd()),{ok:!0,action:"already-installed"};if(Cd())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=kd();if(!qo(e))return{ok:!1,error:`package root not found at ${e}`};console.log(`Installing @huggingface/transformers into ${e} ...`);let t=await nw(e);return t.ok&&await Ko(e),t}var Td,ew,Bs,Ad=w(()=>{"use strict";Td=Vy(import.meta.url),ew="^4.2.0",Bs="1.23.2"});var zs={};V(zs,{CURRENT_MIGRATION_SCHEMA_VERSION:()=>Gs,UnsupportedMigrationSchemaError:()=>Xs,completeMigration:()=>Zo,detectStaleLock:()=>ni,failMigration:()=>ei,getActiveMigration:()=>Cn,pauseMigration:()=>rw,releaseStaleLock:()=>si,rollbackMigration:()=>ti,rowToState:()=>qt,startMigration:()=>Js,updateCursor:()=>Qo});function qt(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!==Gs)throw new Xs(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 Cn(){let t=E().prepare("SELECT * FROM migration_state WHERE status IN ('in_progress', 'paused') ORDER BY id DESC LIMIT 1").get();return t?qt(t):null}function Js(e){let t=E(),n=Cn();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(Gs,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 qt(o)}function Ln(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 Qo(e,t){let s=E().prepare("UPDATE migration_state SET cursor_session_id = ?, cursor_chunk_id = ? WHERE id = ?").run(t.sessionId,t.chunkId,e);Ln(s,"updateCursor",e)}function rw(e){let n=E().prepare("UPDATE migration_state SET status = 'paused' WHERE id = ? AND status = 'in_progress'").run(e);Ln(n,"pauseMigration",e)}function Zo(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);Ln(n,"completeMigration",e)}function ei(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);Ln(s,"failMigration",e)}function ti(e){let n=E().prepare("UPDATE migration_state SET status = 'rolled_back', completed_at = datetime('now') WHERE id = ?").run(e);Ln(n,"rollbackMigration",e)}function ow(e){try{return process.kill(e,0),!0}catch(t){return t.code==="EPERM"}}function ni(){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=qt(t);return n.lockTakenByPid===null||n.lockTakenByPid===process.pid||ow(n.lockTakenByPid)?null:n}function si(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 Gs,Xs,Vt=w(()=>{"use strict";k();Gs=1,Xs=class extends Error{constructor(t){super(`unsupported migration schema_version: found ${t}, this CLI understands ${Gs}. Upgrade or run 'recall semantic migrate --reset' to discard the cursor and start fresh.`),this.name="UnsupportedMigrationSchemaError"}}});async function oi(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
893
|
CREATE TABLE IF NOT EXISTS vec_chunks_v1_backup (
|
|
785
894
|
rowid INTEGER PRIMARY KEY,
|
|
786
895
|
embedding BLOB NOT NULL
|
|
787
896
|
);
|
|
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
|
|
789
|
-
`),new
|
|
897
|
+
`),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 iw()}catch(i){let{failMigration:a}=await Promise.resolve().then(()=>(Vt(),zs)),d=i instanceof Error?i.message:String(i);throw a(e,d),i}if(!o.ok){let{failMigration:i}=await Promise.resolve().then(()=>(Vt(),zs));throw i(e,o.message),new Error(`atomic swap verification failed: ${o.message}`)}try{Zo(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 iw(){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)}
|
|
898
|
+
`),new ri(`post-swap verification failed: ${t instanceof Error?t.message:String(t)}`,t)}}var ri,Nd=w(()=>{"use strict";k();Vt();ri=class extends Error{cause;constructor(t,n){super(t),this.name="MigrationVerificationError",this.cause=n}}});import{promises as aw}from"node:fs";import{dirname as ii}from"node:path";function Od(e){return e<=0?0:Math.ceil(e*(cw+lw)*dw)}async function vd(e){let t=e.dbPath??ee,n;try{n=await aw.statfs(ii(t))}catch(i){return{ok:!1,message:`disk space check failed: could not read filesystem at ${ii(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 ${ii(t)} or run on a different filesystem.`;return{ok:r,message:o,availableBytes:s,requiredBytes:e.requiredBytes}}function Id(e={}){let t=E(),n=Z().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
899
|
JOIN sessions s ON s.id = cm.session_id
|
|
791
900
|
WHERE s.project_id = ?
|
|
792
901
|
AND cm.embedding_model_id != ?
|
|
@@ -796,14 +905,14 @@ ${t.message}
|
|
|
796
905
|
WHERE embedding_model_id != ?
|
|
797
906
|
AND NOT EXISTS (
|
|
798
907
|
SELECT 1 FROM vec_chunks_v2 v WHERE v.rowid = chunk_meta.rowid
|
|
799
|
-
)`).get(n).n}async function
|
|
908
|
+
)`).get(n).n}async function Md(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(()=>(Us(),js));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 Dd(e,t){return e<=0?0:t<=0?null:Math.ceil(e/t/60)}async function $d(e={}){let t=e.sampleSize??ai,n=E(),s=Z().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
909
|
JOIN sessions s ON s.id = cm.session_id
|
|
801
910
|
WHERE s.project_id = ?
|
|
802
911
|
AND cm.embedding_model_id != ?
|
|
803
912
|
AND cm.text IS NOT NULL
|
|
804
913
|
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
|
|
806
|
-
`),!1;let n=
|
|
914
|
+
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 cw,lw,dw,ai,Pd=w(()=>{"use strict";k();Pe();wn();D();cw=768*4,lw=64,dw=1.2;ai=50});var Ys={};V(Ys,{MigrationLoopError:()=>An,createParallelVectorTable:()=>jd,maybeAutoPruneExpiredBackup:()=>yw,migrateChunkRange:()=>Ud,migrateOneChunk:()=>hw,pruneRollbackBackup:()=>Sw,rollbackMigration:()=>bw,runMigration:()=>Ew});import{createInterface as uw}from"node:readline/promises";import{stdin as pw,stdout as Fd,stderr as mw}from"node:process";async function gw(e,t){if(!Fd.isTTY)return mw.write(`Interactive confirmation requested but stdout is not a TTY. Re-run with --yes.
|
|
915
|
+
`),!1;let n=uw({input:pw,output:Fd}),s=t===null?"":` (~${t} min)`,r=await n.question(`Migrate ${e} chunks${s}? Type YES to proceed: `);return n.close(),r.trim()==="YES"}function jd(){E().exec(_w)}async function hw(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 Ud(e){let t=E(),n=e.batchSize??50,s=Cn();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
916
|
WHERE rowid > ?
|
|
808
917
|
AND embedding_model_id != ?
|
|
809
918
|
AND NOT EXISTS (
|
|
@@ -814,8 +923,8 @@ ${t.message}
|
|
|
814
923
|
AND NOT EXISTS (
|
|
815
924
|
SELECT 1 FROM vec_chunks_v2 v WHERE v.rowid = chunk_meta.rowid
|
|
816
925
|
)
|
|
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
|
|
818
|
-
`)[0],migrated:0,backupRetained:!1};throw g}
|
|
926
|
+
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 An(`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],y=g[b],T=Buffer.from(y.buffer,y.byteOffset,y.byteLength);h.run(BigInt(S.rowid)),_.run(BigInt(S.rowid),T)}})(),m=p[p.length-1].rowid,a+=p.length,Qo(s.id,{sessionId:p[p.length-1].session_id,chunkId:m}),e.signal?.aborted)return{migrated:a,total:i};if(a-l>=fw&&(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 Ew(e){let t=ni();t&&t.lockTakenByPid!==null&&(console.warn(`[migrate] Stale lock detected from pid ${t.lockTakenByPid}. Releasing.`),si(t.lockTakenByPid,{preserveCursor:!0}));let n=Cn(),r=Z().modelId.includes("gguf")?"llama":"onnx",o=await Md(r);if(!o.ok)return{ok:!1,message:o.message,migrated:0,backupRetained:!1};if(!Z().loaded)try{await Ke()}catch(g){if(g instanceof fe)return{ok:!1,message:g.message.split(`
|
|
927
|
+
`)[0],migrated:0,backupRetained:!1};throw g}jd();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=Id({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=Z().modelId,_=E().prepare("SELECT embedding_model_id FROM chunk_meta WHERE embedding_model_id != ? LIMIT 1").get(f)?.embedding_model_id??"unknown";n=Js({modelIdOld:_,modelIdNew:f})}try{await oi(n.id)}catch(f){return{ok:!1,message:f instanceof Error?f.message:String(f),migrated:0,backupRetained:!1}}try{zo()}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=Od(a),l=await vd({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 ${ai} chunks)...`);let u=await $d({scopeProjectId:i}),m=u.chunksPerSec===null?null:Dd(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(Z().modelId),f=h=>(h/1024**3).toFixed(2);console.log(""),console.log("=== Dry run: migration plan ==="),console.log(`Target backend: ${Z().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 gw(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=Z().modelId,h=50,_=g.prepare(`SELECT cm.rowid, cm.session_id, cm.text FROM chunk_meta cm
|
|
819
928
|
JOIN sessions s ON s.id = cm.session_id
|
|
820
929
|
WHERE s.project_id = ?
|
|
821
930
|
AND cm.embedding_model_id != ?
|
|
@@ -829,22 +938,83 @@ ${t.message}
|
|
|
829
938
|
AND cm.embedding_model_id != ?
|
|
830
939
|
AND NOT EXISTS (
|
|
831
940
|
SELECT 1 FROM vec_chunks_v2 v WHERE v.rowid = cm.rowid
|
|
832
|
-
)`).get(i,f).n,
|
|
941
|
+
)`).get(i,f).n,y=0,T=Date.now();for(;;){let R=_.all(i,f,h);if(R.length===0)break;let B;try{B=await Ne(R.map(v=>v.text))}catch(v){return{ok:!1,message:`embed() failed at scoped migrated=${y}: ${v instanceof Error?v.message:String(v)}`,migrated:y,backupRetained:!1}}if(g.transaction(()=>{let v=g.prepare("INSERT INTO vec_chunks_v2(rowid, embedding) VALUES (?, ?)");for(let O=0;O<R.length;O++){let x=R[O],I=B[O],F=Buffer.from(I.buffer,I.byteOffset,I.byteLength);v.run(BigInt(x.rowid),F)}})(),y+=R.length,e.onProgress){let v=(Date.now()-T)/1e3;e.onProgress({migrated:y,total:S,chunksPerSec:v>0?y/v:0})}}return{ok:!0,message:`scoped: ${y} chunks staged for project ${e.projectName}. Run \`recall semantic migrate\` (no --project) to finalize across the corpus.`,migrated:y,backupRetained:!1}}if(!n){let g=Z().modelId,h=(await Promise.resolve().then(()=>(k(),qn))).getDb().prepare("SELECT embedding_model_id FROM chunk_meta WHERE embedding_model_id != ? LIMIT 1").get(g)?.embedding_model_id??"unknown";n=Js({modelIdOld:h,modelIdNew:g})}E().prepare(`INSERT INTO vec_chunks_v2(rowid, embedding)
|
|
833
942
|
SELECT v.rowid, v.embedding
|
|
834
943
|
FROM vec_chunks v
|
|
835
944
|
JOIN chunk_meta cm ON cm.rowid = v.rowid
|
|
836
945
|
WHERE cm.embedding_model_id = ?
|
|
837
946
|
AND NOT EXISTS (
|
|
838
947
|
SELECT 1 FROM vec_chunks_v2 v2 WHERE v2.rowid = v.rowid
|
|
839
|
-
)`).run(n.modelIdNew);let p;try{p=await
|
|
948
|
+
)`).run(n.modelIdNew);let p;try{p=await Ud({migrationId:n.id,onProgress:e.onProgress})}catch(g){ei(n.id,g instanceof Error?g.message:String(g));let f=g instanceof An?g.partialMigrated:0;return{ok:!1,message:g instanceof Error?g.message:String(g),migrated:f,backupRetained:!1}}try{await oi(n.id)}catch(g){return{ok:!1,message:g instanceof Error?g.message:String(g),migrated:p.migrated,backupRetained:!1}}try{zo()}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(()=>(k(),qn))).getDb().exec("DROP TABLE IF EXISTS vec_chunks_v1_backup;"),{ok:!0,message:"migration complete",migrated:p.migrated,backupRetained:e.keepBackup}}async function bw(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=qt(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)})(),ti(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 Sw(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"}}function yw(){let e=E();if(!e.prepare("SELECT name FROM sqlite_master WHERE name='vec_chunks_v1_backup'").get())return{outcome:"absent",message:"no vec_chunks_v1_backup present"};let n=e.prepare("SELECT completed_at FROM migration_state WHERE status = 'completed' ORDER BY id DESC LIMIT 1").get();return n?e.prepare("SELECT 1 AS x FROM migration_state WHERE status = 'completed' AND completed_at > datetime('now', '-30 days') ORDER BY id DESC LIMIT 1").get()?{outcome:"kept",message:`backup retained \u2014 last migration (${n.completed_at}) is within the 30-day rollback window`}:(e.exec("DROP TABLE vec_chunks_v1_backup;"),{outcome:"pruned",message:`auto-pruned vec_chunks_v1_backup \u2014 last migration (${n.completed_at}) is older than 30 days`}):{outcome:"kept",message:"backup present but no completed migration on record \u2014 left for manual inspection"}}var An,fw,_w,qs=w(()=>{"use strict";k();Pe();Vt();Nd();Pd();Pe();Ds();An=class extends Error{partialMigrated;cause;constructor(t,n,s){super(t),this.name="MigrationLoopError",this.partialMigrated=n,this.cause=s}};fw=5e3,_w=`
|
|
840
949
|
CREATE VIRTUAL TABLE IF NOT EXISTS vec_chunks_v2 USING vec0(
|
|
841
950
|
embedding float[768] distance_metric=cosine
|
|
842
951
|
);
|
|
843
|
-
`});
|
|
952
|
+
`});var Bd=w(()=>{"use strict"});var Hd=w(()=>{"use strict"});import{existsSync as ww,readFileSync as Tw,writeFileSync as Rw}from"node:fs";import{join as kw}from"node:path";import{z as _e}from"zod";function di(e){let t=e.trim();return!!(!t||Xd.test(t)||Gd.test(t))}function Jd(e){let t=e.trim();if(!t||Gd.test(t))return null;let n=t.replace(Xd,"").trim();return n.length>0?n:null}function li(e,t){if(!di(e))return e;let n=Jd(e);return n||(t&&!di(t)?t:e)}function Nw(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 ci,Wd,xw,Cw,Lw,Aw,Xd,Gd,ui,pi,mi=w(()=>{"use strict";D();ci=kw(C,"terminals.json"),Wd=1440*60*1e3,xw=3e4,Cw=6e4,Lw=_e.object({shell_pid:_e.number(),tab_name:_e.string(),cwd:_e.string().nullable().optional(),opened_at:_e.string(),last_seen_at:_e.string()}),Aw=_e.object({schema:_e.string().optional(),saved_at:_e.string().optional(),terminals:_e.array(Lw).max(500).default([]),sessions_by_pid:_e.record(_e.string(),_e.array(_e.string()).max(50)).optional().default({})}),Xd=/^[⠀-⣿✳\s]+/,Gd=/^\d+(\.\d+){1,3}$/;ui=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,!!ww(ci)))try{let t=Tw(ci,"utf8"),n=JSON.parse(t),s=Aw.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)]))};Rw(ci,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let n=new Date().toISOString(),s=this.entries.get(t.shell_pid),r=li(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=li(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>Cw?(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=li(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=Nw({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()-xw;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()-Wd;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()-Wd;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}}},pi=new ui});import{execFile as Ow}from"node:child_process";import{promisify as vw}from"node:util";var w$,zd=w(()=>{"use strict";w$=vw(Ow)});import{execFile as Iw}from"node:child_process";import{promisify as Mw}from"node:util";var O$,v$,Yd=w(()=>{"use strict";mi();At();k();zd();O$=Mw(Iw),v$=3600*1e3});import{execFile as Dw}from"node:child_process";import{promisify as $w}from"node:util";import{stat as Pw}from"node:fs/promises";async function jw(e){try{let{stdout:t}=await qd("git",["rev-parse","--is-inside-work-tree"],{cwd:e,timeout:Vd});return t.trim()==="true"}catch{return!1}}async function Uw(e,t,n){let s=["--no-pager","log","--all","--no-color","--since",t,"--until",n,`--pretty=format:${Fw}`],{stdout:r}=await qd("git",s,{cwd:e,timeout:Vd,maxBuffer:8*1024*1024}),o=[],i=new Set;for(let a of r.split(`
|
|
953
|
+
`)){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 Bw(e){return E().prepare(`SELECT id, cwd, started_at, ended_at
|
|
954
|
+
FROM sessions WHERE id = ?`).get(e)??null}function Hw(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
|
|
955
|
+
(session_id, commit_sha, committed_at, subject, cwd_snapshot, correlated_at)
|
|
956
|
+
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 gi(e){let t=Bw(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 Pw(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 jw(t.cwd))return{sessionId:e,status:"not-a-repo",commitsFound:0,commitsInserted:0};try{let o=await Uw(t.cwd,n,s),i=Hw(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 Kd(e={}){let t=E(),n=Math.max(1,Math.min(1e5,e.limit??1e4)),s=t.prepare(`SELECT id FROM sessions
|
|
957
|
+
WHERE cwd IS NOT NULL AND started_at IS NOT NULL AND ended_at IS NOT NULL
|
|
958
|
+
ORDER BY COALESCE(ended_at, started_at) DESC
|
|
959
|
+
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 gi(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 Vs(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,
|
|
960
|
+
NULLIF(sa.alias, '') AS alias,
|
|
961
|
+
p.name AS project,
|
|
962
|
+
s.started_at AS startedAt,
|
|
963
|
+
s.ended_at AS endedAt,
|
|
964
|
+
sc.commit_sha AS commitSha,
|
|
965
|
+
sc.committed_at AS committedAt,
|
|
966
|
+
sc.subject AS subject
|
|
967
|
+
FROM session_commits sc
|
|
968
|
+
JOIN sessions s ON s.id = sc.session_id
|
|
969
|
+
JOIN projects p ON p.id = s.project_id
|
|
970
|
+
LEFT JOIN session_aliases sa ON sa.session_id = sc.session_id
|
|
971
|
+
WHERE lower(sc.commit_sha) = lower(?)
|
|
972
|
+
OR lower(sc.commit_sha) LIKE ?
|
|
973
|
+
ORDER BY COALESCE(sc.committed_at, s.started_at, '') DESC`).all(n,s)}var qd,Vd,Fw,Nn=w(()=>{"use strict";k();qd=$w(Dw),Vd=1e4,Fw="%H%x09%aI%x09%s"});import{join as Ww}from"node:path";var H$,Qd=w(()=>{"use strict";k();D();H$=Ww(C,"collections.json")});import{basename as Y$,join as fi}from"node:path";var Zd,Q$,Z$,eu=w(()=>{"use strict";k();D();Qd();Zd=fi(C,"auto-rules"),Q$=fi(Zd,"rules.json"),Z$=fi(Zd,"suggestions.json")});var tu=w(()=>{"use strict"});var nu=w(()=>{"use strict"});var su=w(()=>{"use strict"});var ru=w(()=>{"use strict";su()});var ou=w(()=>{"use strict"});var iu=w(()=>{"use strict"});function au(e){return e.replace(/\\/g,"/").includes("/subagents/")}function cu(e){let t=e.split(/[/\\]/),n=t.findIndex(s=>s==="projects");return n===-1||n+1>=t.length?null:t[n+1]??null}var lu=w(()=>{"use strict"});import{watch as LP}from"chokidar";import{readdirSync as Gw,statSync as NP}from"node:fs";import{basename as FP,join as Jw}from"node:path";function*_i(e){let t;try{t=Gw(e,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let n of t){if(n.isSymbolicLink())continue;let s=Jw(e,n.name);n.isDirectory()?yield*_i(s):n.isFile()&&n.name.endsWith(".jsonl")&&(yield s)}}var du,uu,pu=w(()=>{"use strict";D();k();Kn();Bd();Hd();Yd();jt();Rs();Qn();Nn();so();ao();eu();dn();tu();At();nu();ru();Ds();ou();iu();lu();du=cu,uu=au});import{existsSync as zw,readFileSync as Yw,renameSync as mu,writeFileSync as qw}from"node:fs";import{join as Vw}from"node:path";function gu(){return Vw(C,"doctor-state.json")}function fu(){let e=gu();if(!zw(e))return{chunkQueue:{samples:[]}};let t;try{t=Yw(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{mu(e,`${e}.corrupt.${Date.now()}`)}catch{}return{chunkQueue:{samples:[]}}}}function _u(e){let t=gu(),n=`${t}.tmp`;try{qw(n,JSON.stringify(e,null,2),{mode:384}),mu(n,t)}catch{}}function hi(){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=fu(),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<=Qw).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(-Kw)}};s.autoPruneCounters&&(u.autoPruneCounters=s.autoPruneCounters),_u(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>Zw&&!n&&a!==null&&a>eT?(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>tT?(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>nT&&(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 hu(e={}){let t=e.now??Date.now(),n=fu(),s=n.autoPruneCounters?.events??[],r=s.filter(l=>{let u=Date.parse(l.ts);return Number.isFinite(u)&&t-u<=sT});if(r.length!==s.length)try{let l={chunkQueue:n.chunkQueue,autoPruneCounters:{events:r}};_u(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 Kw,Qw,Zw,eT,tT,nT,sT,Ei=w(()=>{"use strict";D();k();Kw=24,Qw=3600*1e3,Zw=1e3,eT=1e3,tT=1e4,nT=5e3,sT=1440*60*1e3});function bu(e=process.env){let t=e.RECALL_AUTO_PRUNE;if(typeof t!="string")return Eu;let n=t.trim().toLowerCase();return n==="off"||n==="dry-run"||n==="enabled"?n:Eu}var Eu,Su=w(()=>{"use strict";En();Ei();Eu="dry-run"});import{existsSync as rT,readFileSync as oT,renameSync as wu,writeFileSync as iT}from"node:fs";import{join as aT}from"node:path";function Tu(){return aT(C,"doctor-alerts.json")}function Ks(){let e=Tu();if(!rT(e))return{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]};let t;try{t=oT(e,"utf8")}catch{return{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]}}let n;try{n=JSON.parse(t)}catch{return yu(e),{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]}}if(!n||typeof n!="object"||Array.isArray(n))return yu(e),{version:1,lastTickAt:new Date(0).toISOString(),alerts:[]};let s=n,r=Array.isArray(s.alerts)?s.alerts.filter(cT):[];return{version:1,lastTickAt:typeof s.lastTickAt=="string"?s.lastTickAt:new Date(0).toISOString(),alerts:r}}function yu(e){try{wu(e,`${e}.corrupt.${Date.now()}`)}catch{}}function cT(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 Ru(e){let t=Tu(),n=`${t}.tmp`;try{iT(n,JSON.stringify(e,null,2),{mode:384}),wu(n,t)}catch{}}function ku(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 xu(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 bi=w(()=>{"use strict";D()});var zu={};V(zu,{WATCHER_REFLAG_CRITICAL:()=>Iu,buildHealthReport:()=>Uu,buildPipelineDiagnostic:()=>Pu,checkChunkQueueGrowth:()=>hi,checkDaemonSiblings:()=>Bu,checkDaemonStateFiles:()=>Hu,checkDiskPressureAndBackups:()=>Fu,checkIngestStaleness:()=>Xu,checkSemanticGateDrift:()=>Ju,checkStaleClaudeJsonMcpPaths:()=>Gu,checkWatcherReflagLoop:()=>Mu,countBackupOrphans:()=>RT,detectLabelCollisions:()=>ju,getFreeDiskBytes:()=>xT,renderMigrationDoctorSection:()=>Wu,runDoctor:()=>wi});import{existsSync as On,readdirSync as lT,readFileSync as Si,statSync as Qs,statfsSync as Ou}from"node:fs";import{homedir as dT}from"node:os";import{join as Zs}from"node:path";import*as vu from"node:http";function mT(e){let t=[];for(let n of e)if(n.alias){if(pT.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}function Mu(e,t=Iu){let n=[];for(let s of e){if(s.noProgressCount<=t)continue;let r=s.path.replace(/'/g,"''");n.push(`Watcher reindexed ${s.path} ${s.count.toLocaleString()} times in the last hour (${s.noProgressCount.toLocaleString()} with no new content) \u2014 reflag loop. Mark it skipped with a SQL one-liner:
|
|
974
|
+
sqlite3 ~/.recall/db.sqlite "UPDATE sessions SET skipped_reason='reflag_loop_breaker' WHERE file_path = '${r}';"`)}return n}function gT(){let e=Zs(C,"daemon.port");if(!On(e))return null;try{let t=Si(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 Cu(e,t,n){return new Promise(s=>{let r=vu.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({ok:!1,reason:"http"});return}try{s({ok:!0,json:JSON.parse(Buffer.concat(i).toString("utf8"))})}catch{s({ok:!1,reason:"parse"})}})});r.on("error",()=>s({ok:!1,reason:"error"})),r.on("timeout",()=>{r.destroy(),s({ok:!1,reason:"timeout"})}),r.end()})}async function fT(e){let t=await Cu(e,"/api/health",Lu);if(t.ok)return t.json;if(t.reason!=="timeout")return null;let n=await Cu(e,"/api/health",Lu);return n.ok?n.json:null}function _T(){let e=Zs(C,"terminals.json");if(!On(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 hT(){let e=new Date(Date.now()-yi*36e5).toISOString();try{let t=E().prepare(`SELECT
|
|
975
|
+
COUNT(*) AS total,
|
|
976
|
+
SUM(CASE WHEN sa.alias IS NULL OR sa.alias = '' THEN 1 ELSE 0 END) AS without_alias,
|
|
977
|
+
SUM(CASE WHEN (sa.alias IS NULL OR sa.alias = '')
|
|
978
|
+
AND s.auto_title_source = 'heuristic' THEN 1 ELSE 0 END) AS heuristic_only
|
|
979
|
+
FROM sessions s
|
|
980
|
+
LEFT JOIN session_aliases sa ON sa.session_id = s.id
|
|
981
|
+
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 Pu(){let e=gT(),t=_T(),n=hT(),s=!1,r=null,o=null,i=null,a=null,d=null,l=[];if(e){let p=await fT(e);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,noProgressCount:typeof h.noProgressCount=="number"?h.noProgressCount:h.count})))}}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>Du&&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>=$u&&u.push(`${n.heuristicOnly}/${n.total} sessions in the last ${yi}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 Mu(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 Au(e){try{return Qs(e).size}catch{return 0}}function ET(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 Fu(e=C){let t=0,n=0;try{let g=Ou(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=lT(e)}catch{r=[]}let o=0,i=0,a=[],d=Date.now(),l=720*60*60*1e3;for(let g of r){if(!ET(g))continue;let f;try{f=Qs(Zs(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 Nu(e){try{return E().prepare(`SELECT COUNT(*) AS n FROM ${e}_data WHERE block = 1`).get().n}catch{return 0}}function ju(){try{return E().prepare(`SELECT p.name AS project,
|
|
982
|
+
COALESCE(NULLIF(sa.alias, ''), s.auto_title, substr(s.first_user_message, 1, 60)) AS label,
|
|
983
|
+
COUNT(*) AS count,
|
|
984
|
+
MAX(CASE WHEN sa.alias IS NOT NULL AND sa.alias != '' THEN 1 ELSE 0 END) AS any_aliased
|
|
985
|
+
FROM sessions s
|
|
986
|
+
JOIN projects p ON p.id = s.project_id
|
|
987
|
+
LEFT JOIN session_aliases sa ON sa.session_id = s.id
|
|
988
|
+
WHERE s.indexed_at > datetime('now', '-7 days')
|
|
989
|
+
AND COALESCE(NULLIF(sa.alias, ''), s.auto_title, s.first_user_message) IS NOT NULL
|
|
990
|
+
GROUP BY p.name, label
|
|
991
|
+
HAVING count >= 2
|
|
992
|
+
ORDER BY count DESC, label ASC
|
|
993
|
+
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 Uu(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(L){o=`check failed: ${L.message}`}let i=Au(ee),a=Au(`${ee}-wal`),d=Fu(),l=d.freeBytes,u=d.totalBytes;e?.("Counting rows");let m=t.prepare(`SELECT
|
|
994
|
+
(SELECT COUNT(*) FROM projects) AS projects,
|
|
995
|
+
(SELECT COUNT(*) FROM sessions) AS sessions,
|
|
996
|
+
(SELECT COUNT(*) FROM messages) AS messages,
|
|
997
|
+
(SELECT COUNT(*) FROM message_usage) AS message_usage`).get(),p=0;try{lt(t),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 L=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=`[${L}] disk pressure \u2014 ${v.join(" \xB7 ")}`;if(d.oldFiles.length>0){let x=d.oldFiles.map(I=>` \u2022 ${I.name} ${ce(I.sizeBytes)} (${I.ageDays} days old)`);O+=`
|
|
998
|
+
Snapshots older than 30 days:
|
|
999
|
+
`+x.join(`
|
|
1000
|
+
`)+"\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+=`
|
|
1001
|
+
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>=Vc?"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 _=Lt(),b=_.filter(L=>L.orphan);b.length>0&&g.push(`${b.length} orphaned MCP child${b.length===1?"":"ren"} (pid${b.length===1?"":"s"}: ${b.map(L=>L.pid).join(", ")}). Each holds a SQLite read connection. Run \`recall mcp-prune\` to clean up.`);let S=_.filter(us);S.length>0&&g.push(`${S.length} MCP child${S.length===1?"":"ren"} burning CPU (pid${S.length===1?"":"s"}: ${S.map(L=>L.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 y=Nu("messages_fts"),T=Nu("sessions_fts");y>16&&g.push(`messages_fts has ${y} segments \u2014 \`recall optimize\` will merge them.`);let R=0;try{R=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&&R>0?g.push(`${R.toLocaleString()} rows in chunk_queue with semantic disabled \u2014 schema gate is stale; restart daemon (v0.67+) to migrate.`):R>1e5&&g.push(`chunk_queue has ${R.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:y},sessions:{fragments:T}},vectors:{rows:p},rows:{projects:m.projects,sessions:m.sessions,messages:m.messages,messageUsage:m.message_usage},chunkQueue:{size:R,semanticEnabled:B},wal:{sizeBytes:a,level:h},mcpProcesses:_,warnings:g}}function bT(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})`)}
|
|
1002
|
+
`):process.stderr.write(` \u2713 ${n} (${i})
|
|
1003
|
+
`),n=""};return{stage(o){r(),n=o,s=Date.now(),t?process.stderr.write(` ${c.dim("\u2026")} ${n}`):process.stderr.write(` \u2026 ${n}
|
|
1004
|
+
`)},done:r}}function Bu(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=fn(o.etimeSeconds)??o.etime;return`pid=${o.pid} started=${i} age=${pt(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 Hu(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??cs(),s=e.existsSync??On,r=e.isProcessAlive??hn,o=!1;if(s(n.pid))try{let d=JSON.parse(Si(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 ST(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 yT(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=fn(t.etimeSeconds)??t.etime;console.log(` ${c.dim("\u2022")} pid ${t.pid} started ${n} age ${pt(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 wT(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 ${pt(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 ${pt(i.etimeSeconds)} ppid=${i.ppid} (gone)`)}let r=e.filter(us);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 ${pt(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=qc(e);o.flagged&&o.message&&console.log(c.warn(` ! ${o.message}`))}function TT(){let e=bu(),t=hu();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 Wu(){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(o){let i=o instanceof Error?o.message:String(o);return i.includes("no such table: migration_state")||process.stderr.write(`[doctor] renderMigrationDoctorSection: ${i}
|
|
1005
|
+
`),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 i=e.prepare("SELECT CAST((julianday('now') - julianday(?)) AS INTEGER) AS days").get(n.completed_at),a=Math.max(0,30-i.days);r.push(` Backup retained: ${a} day(s) until auto-prune (daemon drops it automatically).`),r.push(` Last completed: ${n.completed_at}`),r.push(` Old model: ${n.model_id_old}`),r.push(` New model: ${n.model_id_new}`)}return 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")),r.join(`
|
|
1006
|
+
`)}function RT(){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 Xu(){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 _i(Ft)){if(uu(l))continue;let u=du(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 Gu(e=Zs(dT(),".claude.json")){let t={status:"ok",configPath:e,configExists:On(e),findings:[]};if(!t.configExists)return t;let n;try{n=Si(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("~")||On(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 Ju(){let e=null;try{e=we().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 kT(e,t){let n=Ks(),{file:s,result:r}=ku(n,e);if((r.outcome==="acknowledged"||r.outcome==="already-acknowledged")&&s!==n&&Ru(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)}
|
|
1007
|
+
`)}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 wi(e={}){if(typeof e.ack=="string"&&e.ack.length>0)return kT(e.ack,{json:!!e.json});let t=bT(!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
|
|
1008
|
+
FROM session_aliases sa
|
|
1009
|
+
LEFT JOIN sessions s ON s.id = sa.session_id
|
|
1010
|
+
WHERE sa.alias IS NOT NULL AND sa.alias != ''`).all(),s=mT(n);t.stage("Probing daemon");let r=await Pu(),o=Uu(t.stage);t.stage("Detecting label collisions");let i=ju();t.stage("Checking ingest freshness");let a=Xu();t.stage("Checking ~/.claude.json MCP paths");let d=Gu();t.stage("Checking semantic gate drift");let l=Ju();t.stage("Sampling chunk_queue growth");let u=hi();t.stage("Scanning for sibling daemons");let m=Bu();t.stage("Checking daemon state files");let p=Hu({liveDaemons:m.processes});if(t.done(),e.json){process.stdout.write(JSON.stringify({scanned:n.length,violations:s.length,items:s,health:o,pipeline:r,labelCollisions:i,ingestFreshness:a,staleMcpEntries:d,semanticGateDrift:l,chunkQueueGrowth:u,siblingDaemons:m,daemonStateFiles:p},null,2)),process.stdout.write(`
|
|
1011
|
+
`);let _=r.state==="degraded",b=a.status==="fail",S=d.status==="fail",y=u.status==="critical",T=m.flagged,R=p.flagged,B=l.status==="critical";return s.length===0&&o.db.integrity==="ok"&&!_&&!b&&!S&&!y&&!T&&!B&&!R?0:1}console.log(c.dim("\u2014 System health \u2014")),console.log(` Database ${ce(o.db.sizeBytes)} (${o.rows.messages.toLocaleString()} messages across ${o.rows.sessions.toLocaleString()} sessions, ${o.rows.projects.toLocaleString()} projects)`);{let _=o.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 ${o.db.freelistCount.toLocaleString()} (${ce(o.db.freelistBytes)} reclaimable via VACUUM)`);{let _=o.fts.messages.fragments,b=o.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(o.chunkQueue.size>0){let b=o.chunkQueue.size>1e5?c.warn(o.chunkQueue.size.toLocaleString()):o.chunkQueue.size.toLocaleString();console.log(` Embed queue ${b}`+(o.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 ${o.vectors.rows.toLocaleString()}`),o.disk.totalBytes>0){let _=o.disk.freeBytes/o.disk.totalBytes*100,b=o.disk.backups.severity,S=`${_.toFixed(1)}%`,y=b==="high"?c.err(S):b==="medium"?c.warn(S):c.ok(S);console.log(` Disk free ${ce(o.disk.freeBytes)} of ${ce(o.disk.totalBytes)} (${y})`)}if(o.disk.backups.fileCount>0){let _=o.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 ${o.db.integrity==="ok"?c.ok("ok"):c.err(o.db.integrity)}`),o.warnings.length>0){console.log("");for(let _ of o.warnings)console.log(` ${c.warn("!")} ${_}`)}console.log(""),yT(m),ST(p),wT(o.mcpProcesses),console.log(""),TT();let g=Wu();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(),y=_.lastHourGrowth!==null?`\u0394 ${_.lastHourGrowth>=0?"+":""}${_.lastHourGrowth.toLocaleString()}/h`:"no prior sample in last hour",T=_.semanticEnabled?c.dim("semantic=on"):c.dim("semantic=off");b==="ok"?console.log(c.ok(` \u2713 ${S} rows, ${y}`)+` ${T}`+c.dim(` (${_.priorSampleCount} prior sample${_.priorSampleCount===1?"":"s"} in last hour)`)):b==="critical"?(console.log(` ${c.err("CRITICAL")} ${S} rows, ${y} ${T}`),_.remediation&&console.log(c.dim(` ${_.remediation}`))):b==="high"?(console.log(` ${c.warn("HIGH")} ${S} rows, ${y} ${T}`),_.remediation&&console.log(c.dim(` ${_.remediation}`))):(console.log(` ${c.warn("MEDIUM")} ${S} rows, ${y} ${T}`),_.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")),r.daemon.running?console.log(` Daemon ${c.ok("running")} (port ${r.daemon.port}, version ${r.daemon.version??"?"}, up ${r.daemon.uptimeSeconds!==null?Math.round(r.daemon.uptimeSeconds/60)+" min":"?"})`):console.log(` Daemon ${c.warn("not reachable")}`),r.runtime.silentTerminalRejections!==null){let _=r.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(r.runtime.autoExtract){let _=r.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(r.runtime.lastTerminalSyncAt!==null){let _=Date.now()-Date.parse(r.runtime.lastTerminalSyncAt),b=Number.isFinite(_)?Math.round(_/6e4):null,S=b!==null&&_>Du;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 r.daemon.running&&console.log(` Last ext sync ${c.warn("never")} (no extension has called /api/terminal/sync since the daemon started)`);if(r.terminalsJson.exists&&r.terminalsJson.ageSeconds!==null){let _=Math.round(r.terminalsJson.ageSeconds/3600),b=r.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=r.recentSessions;if(f.total>0){let _=Math.round(f.fractionHeuristic*100),b=f.fractionHeuristic>=$u&&f.total>=3;console.log(` Recent titles ${b?c.err(`${_}% heuristic`):c.ok(`${_}% heuristic`)} (${f.heuristicOnly}/${f.total} sessions in last ${yi}h fell back to first-message title)`)}if(r.flags.length>0){console.log("");for(let _ of r.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"),y=b.label.length>60?`${b.label.slice(0,57)}\u2026`:b.label;console.log(` ${c.dim(`${b.count}\xD7`)} ${y} ${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
|
|
1012
|
+
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
|
|
1013
|
+
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.")),o.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}function xT(){try{let e=Ou(C);return Number(e.bavail)*Number(e.bsize)}catch{return 0}}var uT,pT,Iu,Du,yi,$u,Lu,Ti=w(()=>{"use strict";$();k();Yn();D();pu();En();_n();Xe();gs();ps();Ei();Su();bi();Sn();uT=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],pT=new RegExp(`^(${uT.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);Iu=50;Du=5*6e4,yi=24,$u=.5;Lu=5e3});import{writeFileSync as ap,readFileSync as k1,existsSync as cp,mkdirSync as lp,readdirSync as x1}from"node:fs";import{join as nr}from"node:path";function pR(){j(),cp(Ai)||lp(Ai,{recursive:!0})}function mR(){j(),cp(Ni)||lp(Ni,{recursive:!0})}function dp(e){try{return JSON.parse(e)}catch{return e}}function Oi(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:dp(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function vi(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:dp(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function gR(e){if(!Number.isFinite(e)||e<0||e>1)throw new Error("confidence must be a number in [0, 1]")}function up(e){if(!aR.has(e))throw new Error(`invalid link_type: ${e}`)}function pp(e){if(!dR.has(e))throw new Error(`invalid inferred_by: ${e}`)}function fR(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 _R(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&&(up(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}
|
|
844
1014
|
ORDER BY confidence DESC, updated_at DESC
|
|
845
|
-
LIMIT ?`).all(...s,o).map(
|
|
1015
|
+
LIMIT ?`).all(...s,o).map(Oi)}function Ii(e){return E().prepare(`SELECT * FROM session_links
|
|
846
1016
|
WHERE source_session_id = ? OR target_session_id = ?
|
|
847
|
-
ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(
|
|
1017
|
+
ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(Oi)}function gt(e,t={}){fR(e.source_session_id,e.target_session_id),up(e.link_type),gR(e.confidence),pp(e.inferred_by);let n=E(),s=new Date().toISOString(),r=JSON.stringify(e.evidence??null);n.prepare(`INSERT INTO session_link_suggestions
|
|
848
1018
|
(source_session_id, target_session_id, link_type,
|
|
849
1019
|
confidence, evidence, status, inferred_by,
|
|
850
1020
|
created_at, decided_at)
|
|
@@ -860,13 +1030,13 @@ CREATE VIRTUAL TABLE IF NOT EXISTS vec_chunks_v2 USING vec0(
|
|
|
860
1030
|
THEN excluded.evidence
|
|
861
1031
|
ELSE session_link_suggestions.evidence
|
|
862
1032
|
END,
|
|
863
|
-
created_at = session_link_suggestions.created_at`).run(e.source_session_id,e.target_session_id,e.link_type,e.confidence,
|
|
1033
|
+
created_at = session_link_suggestions.created_at`).run(e.source_session_id,e.target_session_id,e.link_type,e.confidence,r,e.inferred_by,s);let o=n.prepare(`SELECT * FROM session_link_suggestions
|
|
864
1034
|
WHERE source_session_id = ?
|
|
865
1035
|
AND target_session_id = ?
|
|
866
1036
|
AND link_type = ?
|
|
867
|
-
AND inferred_by = ?`).get(e.source_session_id,e.target_session_id,e.link_type,e.inferred_by);if(!
|
|
1037
|
+
AND inferred_by = ?`).get(e.source_session_id,e.target_session_id,e.link_type,e.inferred_by);if(!o)throw new Error("createSuggestion succeeded but read-back failed");return t.deferMirror||ft(),vi(o)}function sr(e={}){let t=E(),n=[],s=[];if(e.status){if(!lR.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&&(pp(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}
|
|
868
1038
|
ORDER BY confidence DESC, created_at DESC
|
|
869
|
-
LIMIT ?`).all(...s,o).map(
|
|
1039
|
+
LIMIT ?`).all(...s,o).map(vi)}function mp(e,t,n={}){if(t!=="approved"&&t!=="rejected")throw new Error(`invalid decision: ${t}`);let s=n.source??"manual";if(!cR.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
|
|
870
1040
|
SET status = ?, decided_at = ?
|
|
871
1041
|
WHERE id = ?`).run(t,i,e),t==="approved"&&(r.prepare(`INSERT INTO session_links
|
|
872
1042
|
(source_session_id, target_session_id, link_type,
|
|
@@ -881,7 +1051,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS vec_chunks_v2 USING vec0(
|
|
|
881
1051
|
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
|
|
882
1052
|
WHERE source_session_id = ?
|
|
883
1053
|
AND target_session_id = ?
|
|
884
|
-
AND link_type = ?`).get(o.source_session_id,o.target_session_id,o.link_type))})(),
|
|
1054
|
+
AND link_type = ?`).get(o.source_session_id,o.target_session_id,o.link_type))})(),ft(),t==="approved"&&hR(o.source_session_id);let d=r.prepare("SELECT * FROM session_link_suggestions WHERE id = ?").get(e);return{suggestion:vi(d),link:a?Oi(a):null}}function hR(e){try{pR();let t=_R({sourceSessionId:e}),n=nr(Ai,`${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};ap(n,JSON.stringify(s,null,2))}catch(t){console.error("[session-links] backup failed:",t)}}function ft(){try{mR();let e=sr({limit:5e3}),t={schema:"claude-recall.session-link-suggestions.v1",backed_up_at:new Date().toISOString(),suggestions:e};ap(uR,JSON.stringify(t,null,2))}catch(e){console.error("[session-links] suggestions backup failed:",e)}}var aR,cR,lR,dR,Ai,Ni,uR,Dn=w(()=>{"use strict";k();D();aR=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),cR=new Set(["regex","llm","embedding","manual","auto","citation","git","terminal-registry"]),lR=new Set(["pending","approved","rejected"]),dR=new Set(["L1","L2","L3","L4","user"]),Ai=nr(C,"links"),Ni=nr(C,"suggestions"),uR=nr(Ni,"index.json")});function cr(e){return e?Math.ceil(e.length/4):0}function Fp(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/Dk);return Math.max($k,t)}function jp(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 Up(e){return E().prepare(`SELECT s.id,
|
|
885
1055
|
NULLIF(sa.alias, '') AS alias,
|
|
886
1056
|
s.auto_title,
|
|
887
1057
|
s.auto_title_source,
|
|
@@ -892,37 +1062,37 @@ CREATE VIRTUAL TABLE IF NOT EXISTS vec_chunks_v2 USING vec0(
|
|
|
892
1062
|
FROM sessions s
|
|
893
1063
|
LEFT JOIN session_aliases sa ON sa.session_id = s.id
|
|
894
1064
|
LEFT JOIN projects p ON p.id = s.project_id
|
|
895
|
-
WHERE s.id = ?`).get(e)??null}function
|
|
1065
|
+
WHERE s.id = ?`).get(e)??null}function Bp(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 Hp(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 Fk(e){let n=E().prepare(`SELECT id, auto_title, started_at
|
|
896
1066
|
FROM sessions
|
|
897
1067
|
WHERE project_id = ?
|
|
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
|
|
1068
|
+
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 jk(e){return{table:e!==null?Fk(e):null,originProjectId:e,cache:new Map}}function lr(e,t){let n=e.cache.get(t);if(n)return n;let s=Up(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:Hp(s),decimal:r,summary:Bp(s.id),project:s.project,started_at:s.started_at};return e.cache.set(t,o),o}function Uk(e,t){let s=E().prepare(`SELECT DISTINCT te.parent_session_id AS pid
|
|
899
1069
|
FROM thread_edges te
|
|
900
1070
|
WHERE te.session_id = ?
|
|
901
|
-
AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let o of s){if(!o.pid)continue;let i=
|
|
1071
|
+
AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let o of s){if(!o.pid)continue;let i=lr(e,o.pid);i&&r.push(i)}return r}function Bk(e,t){let s=E().prepare(`SELECT DISTINCT te.session_id AS sid
|
|
902
1072
|
FROM thread_edges te
|
|
903
|
-
WHERE te.parent_session_id = ?`).all(t),r=[];for(let o of s){if(!o.sid)continue;let i=
|
|
904
|
-
${o}`}return r}function
|
|
1073
|
+
WHERE te.parent_session_id = ?`).all(t),r=[];for(let o of s){if(!o.sid)continue;let i=lr(e,o.sid);i&&r.push(i)}return r}function Wp(e){let t=Pk[e.linkType]??.5,n=Kt(e.confidence),s=t*n,r=Fp(e.daysApart),o=e.embeddingCosine??.5,i=Kt(e.pagerank);if(e.scoring==="pagerank")return Kt(i);if(e.scoring==="embedding-rerank")return e.embeddingCosine===null?Kt(s):Kt(o);let a=.35*s+.2*r+.2*o+.25*i;return Kt(a)}function Kt(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}function Hk(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=Ii(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 Wk(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 Gp(e,t){let n=e.replace(/\s+/g," ").trim();return n.length<=t?n:`${n.slice(0,t-1).trimEnd()}\u2026`}function Xk(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=Gp(e.summary,Xp);return`${r}
|
|
1074
|
+
${o}`}return r}function Gk(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+=cr(a),e.summary){let d=Gp(e.summary,Xp*4);s.push(d),o+=cr(d)}s.push("");for(let d of t){if(d.refs.length===0)continue;let l=`## ${d.heading}`,u=cr(l),m=[],p=0;for(let g of d.refs){let f=Xk(g),h=cr(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(`
|
|
905
1075
|
`)+`
|
|
906
|
-
`,budgetUsed:o,truncated:r}}function
|
|
1076
|
+
`,budgetUsed:o,truncated:r}}function Jk(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=lr(e,d);if(!l)continue;let u=jp(t.started_at,l.started_at),m=Wp({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 dr(e,t={}){let n=Math.max(100,Math.floor(t.budget??Ik)),s=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??Mk)),o=t.includeWikiLinks??!0,i=t.includeSuggestions??!1,a=t.edgeTypes?new Set(t.edgeTypes):null,d=Up(e);if(!d)throw new Error(`session not found: ${e}`);let l=jk(d.project_id),u={session_id:d.id,title:Hp(d),decimal:l.table?.byId.get(d.id)??null,summary:Bp(d.id),project:d.project,started_at:d.started_at};l.cache.set(d.id,u);let m=Uk(l,e),p=Bk(l,e),g=Ii(e).filter(L=>L.approved).filter(L=>!a||a.has(L.link_type)).filter(L=>o||L.link_type!=="wiki_link"),f=Hk(e,g,m,p,r),h=Wk(f),_=[],b=[],S=[],y=[];for(let L of g){let v=L.source_session_id===e?L.target_session_id:L.source_session_id,O=lr(l,v);if(!O)continue;let x=jp(u.started_at,O.started_at),I=Wp({confidence:L.confidence,linkType:L.link_type,daysApart:x,embeddingCosine:null,pagerank:h.get(v)??0,scoring:s}),F=Fp(x),ne=`${L.link_type} confidence=${L.confidence.toFixed(2)} recency=${F.toFixed(2)} (${Math.round(x)}d apart)`,et={...O,score:I,evidence:ne,link_type:L.link_type};L.link_type==="citation"?_.push(et):L.link_type==="similar"?b.push(et):L.link_type==="wiki_link"?y.push(et):S.push(et)}if(i){let L=sr({sourceSessionId:e,status:"pending",limit:100}),v=sr({targetSessionId:e,status:"pending",limit:100}),O=[...L,...v],x=new Set,I=O.filter(ne=>x.has(ne.id)?!1:(x.add(ne.id),!0)),F=Jk(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"?y.push(ne):S.push(ne)}let T=(L,v)=>v.score-L.score;_.sort(T),b.sort(T),S.sort(T),y.sort(T);let B=Gk(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:y}],n);return{origin:u,parents:m,children:p,citations:_,similar:b,cousins:S,wikiLinks:y,bundle:B.bundle,budgetUsed:B.budgetUsed,budgetRemaining:Math.max(0,n-B.budgetUsed),truncated:B.truncated}}var Ik,Mk,Dk,$k,Pk,Xp,Bi=w(()=>{"use strict";k();Dn();Ik=4e3,Mk=2,Dk=30,$k=.2,Pk={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};Xp=240});var Im={};V(Im,{computeAllHealthScores:()=>ea,computeHealthScore:()=>Qi,computeHealthScoreByName:()=>Zi});function Pn(e){return Math.max(0,Math.min(1,e))}function Qi(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,
|
|
907
1077
|
MAX(started_at) AS latest
|
|
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=
|
|
909
|
-
FROM sessions WHERE project_id = ?`).get(e).avg_msgs??0,u=
|
|
1078
|
+
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=Pn(r/10),i=s.latest?(Date.now()-new Date(s.latest).getTime())/(1e3*60*60*24):90,a=Pn(1-i/90),l=t.prepare(`SELECT AVG(message_count) AS avg_msgs
|
|
1079
|
+
FROM sessions WHERE project_id = ?`).get(e).avg_msgs??0,u=Pn((l-2)/3),m=t.prepare(`SELECT COUNT(*) AS total,
|
|
910
1080
|
SUM(CASE WHEN m.content_text IS NOT NULL AND m.content_text != '' THEN 1 ELSE 0 END) AS covered
|
|
911
1081
|
FROM messages m
|
|
912
1082
|
JOIN sessions s ON s.id = m.session_id
|
|
913
|
-
WHERE s.project_id = ?`).get(e),p=m.total>0?m.covered/m.total:.5,g=
|
|
1083
|
+
WHERE s.project_id = ?`).get(e),p=m.total>0?m.covered/m.total:.5,g=Pn(p),f=t.prepare(`SELECT COUNT(DISTINCT s.id) AS total,
|
|
914
1084
|
COUNT(DISTINCT st.session_id) AS tagged
|
|
915
1085
|
FROM sessions s
|
|
916
1086
|
LEFT JOIN session_tags st ON st.session_id = s.id
|
|
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
|
|
1087
|
+
WHERE s.project_id = ?`).get(e),h=f.total>0?f.tagged/f.total:0,_=Pn(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 Zi(e){let n=E().prepare("SELECT id FROM projects WHERE name = ?").get(e);return n?Qi(n.id):null}function ea(){let t=E().prepare("SELECT id FROM projects ORDER BY name").all(),n=[];for(let s of t){let r=Qi(s.id);r&&n.push(r)}return n}var ta=w(()=>{"use strict";k()});function Gg(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:Mt,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 ht(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:[Gg(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 Et(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:[Gg(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 ZC(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 eL(e){return e.toLocaleString("en-US")}function bt(e){return e<1e3?String(e):`${ZC(e)} (${eL(e)})`}function St(e){return new Date(e).toLocaleDateString("en-US",{month:"long",day:"numeric",year:"numeric"})}var Mt,jn=w(()=>{"use strict";Mt="#f97316"});var zg={};V(zg,{render:()=>sL});function Cr(e,t){return{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"4px"},children:[{type:"span",props:{style:{fontSize:"34px",fontWeight:700,color:Un,fontFamily:"JetBrains Mono"},children:e}},{type:"span",props:{style:{fontSize:"14px",color:Lr,textTransform:"uppercase",letterSpacing:"0.16em"},children:t}}]}}}function sL(e){let n=[St(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?`+ ${bt(e.cachedTokens)} cached`:null,i=[ht(Jg,"session receipt"),{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"8px"},children:[{type:"div",props:{style:{fontSize:"44px",fontWeight:700,color:Un,lineHeight:1.15,wordBreak:"break-word"},children:e.sessionTitle}},{type:"div",props:{style:{fontSize:"20px",color:Lr},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:Mt,fontFamily:"JetBrains Mono",lineHeight:1,letterSpacing:"-0.04em"},children:r}},{type:"span",props:{style:{marginTop:"16px",fontSize:"16px",color:Lr,textTransform:"uppercase",letterSpacing:"0.24em",fontWeight:600},children:"session cost"}}]}},{type:"div",props:{style:{display:"flex",width:"100%",height:"1px",backgroundColor:nL}}},{type:"div",props:{style:{display:"flex",justifyContent:"space-between",gap:"24px"},children:[Cr(String(e.messageCount),"msgs"),Cr(String(e.filesReferenced),"files"),Cr(String(e.toolCallCount),"tools"),Cr(e.model??"\u2014","model")]}}];return o&&i.push({type:"div",props:{style:{fontSize:"14px",color:Lr,fontFamily:"JetBrains Mono",opacity:.7},children:o}}),e.verdict&&i.push({type:"div",props:{style:{display:"flex",fontSize:"24px",fontStyle:"italic",color:Un,borderLeft:`3px solid ${Mt}`,paddingLeft:"20px",marginTop:"8px"},children:`"${e.verdict}"`}}),i.push({type:"div",props:{style:{display:"flex",flex:1}}}),i.push(Et(Jg)),{type:"div",props:{style:{display:"flex",flexDirection:"column",width:"100%",height:"100%",backgroundColor:tL,padding:"64px 72px",fontFamily:"Inter",gap:"28px"},children:i}}}var tL,Un,Lr,nL,Jg,Yg=w(()=>{"use strict";jn();tL="#1a1b1e",Un="#e7e9ee",Lr="#8b9098",nL="#2a2c33",Jg={markStroke:Un,wordmarkFg:Un}});var Vg={};V(Vg,{render:()=>iL});function Ar(e,t){return{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"4px"},children:[{type:"span",props:{style:{fontSize:"34px",fontWeight:700,color:Bn,fontFamily:"JetBrains Mono"},children:e}},{type:"span",props:{style:{fontSize:"14px",color:Nr,fontFamily:"JetBrains Mono",textTransform:"lowercase"},children:`// ${t}`}}]}}}function iL(e){let t=[St(e.sessionDate),e.durationLabel].filter(Boolean).join(" \xB7 "),n=e.costDollars??"$\u2014",s=typeof e.cachedTokens=="number"&&e.cachedTokens>=1e5?`# ${bt(e.cachedTokens)} tokens cache-replayed (free)`:null,r=[ht(qg,"recall.cli/share"),{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"6px"},children:[{type:"div",props:{style:{fontSize:"18px",color:ma,fontFamily:"JetBrains Mono"},children:"$ recall share --session"}},{type:"div",props:{style:{fontSize:"40px",fontWeight:700,color:Bn,lineHeight:1.15,wordBreak:"break-word",fontFamily:"JetBrains Mono"},children:e.sessionTitle}},{type:"div",props:{style:{fontSize:"18px",color:Nr,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:Nr,fontFamily:"JetBrains Mono"},children:"> total_cost:"}},{type:"span",props:{style:{fontSize:"180px",fontWeight:700,color:ma,fontFamily:"JetBrains Mono",lineHeight:1,letterSpacing:"-0.04em"},children:n}}]}},{type:"div",props:{style:{display:"flex",width:"100%",height:"1px",backgroundColor:oL}}},{type:"div",props:{style:{display:"flex",justifyContent:"space-between",gap:"24px"},children:[Ar(String(e.messageCount),"msgs"),Ar(String(e.filesReferenced),"files"),Ar(String(e.toolCallCount),"tools"),Ar(e.model??"\u2014","model")]}}];return s&&r.push({type:"div",props:{style:{fontSize:"14px",color:Nr,fontFamily:"JetBrains Mono",opacity:.7},children:s}}),e.verdict&&r.push({type:"div",props:{style:{display:"flex",fontSize:"22px",color:Bn,fontFamily:"JetBrains Mono",borderLeft:`3px solid ${ma}`,paddingLeft:"20px",marginTop:"8px"},children:`// ${e.verdict}`}}),r.push({type:"div",props:{style:{display:"flex",flex:1}}}),r.push(Et(qg)),{type:"div",props:{style:{display:"flex",flexDirection:"column",width:"100%",height:"100%",backgroundColor:rL,padding:"64px 72px",fontFamily:"Inter",gap:"28px"},children:r}}}var rL,Bn,Nr,oL,ma,qg,Kg=w(()=>{"use strict";jn();rL="#0d0d0f",Bn="#e1e7ee",Nr="#6b7480",oL="#1f2229",ma="#7ee787",qg={markStroke:Bn,wordmarkFg:Bn}});var Zg={};V(Zg,{render:()=>cL});function Or(e,t){return{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"4px"},children:[{type:"span",props:{style:{fontSize:"34px",fontWeight:700,color:Dt,fontFamily:"JetBrains Mono"},children:e}},{type:"span",props:{style:{fontSize:"14px",color:vr,textTransform:"uppercase",letterSpacing:"0.16em",fontWeight:600},children:t}}]}}}function cL(e){let t=[St(e.sessionDate),e.durationLabel].filter(Boolean).join(" \xB7 "),n=e.costDollars??"$\u2014",s=typeof e.cachedTokens=="number"&&e.cachedTokens>=1e5?`+ ${bt(e.cachedTokens)} cached`:null,r=[ht(Qg,"session receipt"),{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"8px"},children:[{type:"div",props:{style:{fontSize:"44px",fontWeight:700,color:Dt,lineHeight:1.15,wordBreak:"break-word"},children:e.sessionTitle}},{type:"div",props:{style:{fontSize:"20px",color:vr},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:Dt,fontFamily:"JetBrains Mono",lineHeight:1,letterSpacing:"-0.04em"},children:n}},{type:"span",props:{style:{marginTop:"16px",fontSize:"16px",color:vr,textTransform:"uppercase",letterSpacing:"0.24em",fontWeight:600},children:"session cost"}}]}},{type:"div",props:{style:{display:"flex",width:"100%",height:"1px",backgroundColor:aL}}},{type:"div",props:{style:{display:"flex",justifyContent:"space-between",gap:"24px"},children:[Or(String(e.messageCount),"msgs"),Or(String(e.filesReferenced),"files"),Or(String(e.toolCallCount),"tools"),Or(e.model??"\u2014","model")]}}];return s&&r.push({type:"div",props:{style:{fontSize:"14px",color:vr,fontFamily:"JetBrains Mono",opacity:.85},children:s}}),e.verdict&&r.push({type:"div",props:{style:{display:"flex",fontSize:"24px",fontStyle:"italic",color:Dt,borderLeft:`3px solid ${Dt}`,paddingLeft:"20px",marginTop:"8px"},children:`"${e.verdict}"`}}),r.push({type:"div",props:{style:{display:"flex",flex:1}}}),r.push(Et(Qg)),{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 Dt,vr,aL,Qg,ef=w(()=>{"use strict";jn();Dt="#ffffff",vr="rgba(255,255,255,0.7)",aL="rgba(255,255,255,0.18)",Qg={markStroke:Dt,wordmarkFg:Dt}});var nf={};V(nf,{render:()=>mL});function Ir(e,t){return{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"4px"},children:[{type:"span",props:{style:{fontSize:"34px",fontWeight:800,color:tn,fontFamily:"JetBrains Mono"},children:e}},{type:"span",props:{style:{fontSize:"14px",color:Hn,textTransform:"uppercase",letterSpacing:"0.16em",fontWeight:600},children:t}}]}}}function pL(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:uL,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:Hn,fontWeight:600,textTransform:"uppercase",letterSpacing:"0.14em"},children:e.map(n=>({type:"span",props:{style:{display:"flex"},children:`${n.label} \xB7 ${n.value}`}}))}}]}}}function mL(e){let t=[St(e.sessionDate),e.durationLabel].filter(Boolean).join(" \xB7 "),n=e.costDollars??"$\u2014",s=typeof e.cachedTokens=="number"&&e.cachedTokens>=1e5?`+ ${bt(e.cachedTokens)} cached`:null,r=[ht(tf,"session receipt"),{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"8px"},children:[{type:"div",props:{style:{fontSize:"40px",fontWeight:700,color:tn,lineHeight:1.15,wordBreak:"break-word"},children:e.sessionTitle}},{type:"div",props:{style:{fontSize:"18px",color:Hn},children:t}}]}},{type:"div",props:{style:{display:"flex",flexDirection:"column",alignItems:"flex-start",padding:"8px 0"},children:[{type:"span",props:{style:{fontSize:"14px",color:Hn,textTransform:"uppercase",letterSpacing:"0.24em",fontWeight:700},children:"session cost"}},{type:"span",props:{style:{fontSize:"180px",fontWeight:800,color:tn,fontFamily:"JetBrains Mono",lineHeight:1,letterSpacing:"-0.04em"},children:n}}]}},{type:"div",props:{style:{display:"flex",width:"100%",height:"1px",backgroundColor:dL}}},{type:"div",props:{style:{display:"flex",justifyContent:"space-between",gap:"24px"},children:[Ir(String(e.messageCount),"msgs"),Ir(String(e.filesReferenced),"files"),Ir(String(e.toolCallCount),"tools"),Ir(e.model??"\u2014","model")]}},pL([{value:e.messageCount,color:Mt,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:Hn,fontFamily:"JetBrains Mono",opacity:.85},children:s}}),e.verdict&&r.push({type:"div",props:{style:{display:"flex",fontSize:"22px",fontStyle:"italic",color:tn,borderLeft:`3px solid ${Mt}`,paddingLeft:"20px",marginTop:"8px"},children:`"${e.verdict}"`}}),r.push({type:"div",props:{style:{display:"flex",flex:1}}}),r.push(Et(tf)),{type:"div",props:{style:{display:"flex",flexDirection:"column",width:"100%",height:"100%",backgroundColor:lL,padding:"64px 72px",fontFamily:"Inter",gap:"24px"},children:r}}}var lL,tn,Hn,dL,uL,tf,sf=w(()=>{"use strict";jn();lL="#f6f7f9",tn="#0a0a0a",Hn="#5a6068",dL="#e3e6eb",uL="#dde1e7",tf={markStroke:tn,wordmarkFg:tn}});var rf={};V(rf,{render:()=>_L});function ga(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:Mr},children:t}},{type:"div",props:{style:{fontSize:"16px",color:sn,textTransform:"uppercase",letterSpacing:"2px"},children:e}}]}}}function fa(e,t){return{type:"div",props:{style:{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"16px 24px",backgroundColor:_a,borderRadius:"12px"},children:[{type:"span",props:{style:{fontSize:"18px",color:sn},children:e}},{type:"span",props:{style:{fontSize:"20px",fontWeight:700,color:Mr},children:t}}]}}}function _L(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:sn,textTransform:"uppercase",letterSpacing:"6px"},children:"YOUR MONTH IN CODE"}},{type:"div",props:{style:{fontSize:"44px",fontWeight:700,color:Mr},children:e.month}}]}},{type:"div",props:{style:{display:"flex",flexDirection:"column",alignItems:"center",gap:"12px",backgroundColor:_a,borderRadius:"16px",padding:"28px",border:`1px solid ${nn}40`},children:[{type:"div",props:{style:{fontSize:"16px",color:fL,textTransform:"uppercase",letterSpacing:"2px"},children:"You are a"}},{type:"div",props:{style:{fontSize:"36px",fontWeight:700,color:nn},children:e.archetype}}]}},{type:"div",props:{style:{display:"flex",gap:"16px",width:"100%"},children:[ga("Recalls",String(e.totalRecalls)),ga("Tokens piped",e.totalTokensPiped>999999?`${(e.totalTokensPiped/1e6).toFixed(1)}M`:e.totalTokensPiped.toLocaleString()),ga("Sessions",String(e.sessionsIndexed))]}},{type:"div",props:{style:{display:"flex",flexDirection:"column",gap:"8px",width:"100%"},children:[{type:"div",props:{style:{fontSize:"14px",color:sn,textTransform:"uppercase",letterSpacing:"2px",marginBottom:"4px"},children:"Highlights"}},fa("Most recalled",e.mostRecalledSession),fa("Biggest recall",`${e.biggestRecallTokens.toLocaleString()} tokens`),fa("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 ${nn}`,display:"flex",alignItems:"center",justifyContent:"center"},children:{type:"span",props:{style:{fontSize:"32px",fontWeight:700,color:nn},children:`${t}`}}}},{type:"div",props:{style:{fontSize:"14px",color:sn,textTransform:"uppercase",letterSpacing:"2px"},children:"Memory health"}}]}}];return e.verdict&&n.push({type:"div",props:{style:{backgroundColor:_a,borderLeft:`3px solid ${nn}`,borderRadius:"8px",padding:"20px 24px",fontSize:"20px",color:Mr,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:nn},children:"Claude Recall"}},{type:"span",props:{style:{fontSize:"18px",color:sn},children:"clauderecall.com"}}]}}),{type:"div",props:{style:{display:"flex",flexDirection:"column",width:"100%",height:"100%",backgroundColor:gL,padding:"72px 64px",fontFamily:"Inter",gap:"36px"},children:n}}}var gL,_a,Mr,sn,nn,fL,of=w(()=>{"use strict";gL="#0b0c0f",_a="#15171c",Mr="#e7e9ee",sn="#8b9098",nn="#f97316",fL="#fb923c"});var ya,Sf,Y,A,re,Ue,yf,wf,Tf,Rf,wa,Ta,yt=w(()=>{"use strict";ya=[String.raw` ___ _ _ _ _ ___ ___ ___ ___ ___ _ _ _ `,String.raw` / __| | /_\ | | | | \| __| | _ \ __/ __| /_\ | | | | `,String.raw`| (__| |__ / _ \| |_| | |) | _| | / _| (__ / _ \| |__| |__ `,String.raw` \___|____/_/ \_\\___/|___/|___| |_|_\___\___/_/ \_\____|____|`],Sf=ya[0]?.length??64,Y="#f97316",A="#8b9098",re="#10b981",Ue="#f59e0b",yf="#60a5fa",wf="#34d399",Tf="#c084fc",Rf="CLAUDE RECALL",wa=100,Ta=30});import{useEffect as zL,useState as YL}from"react";import{Box as kf,Text as be}from"ink";import{createRequire as qL}from"node:module";import{existsSync as VL}from"node:fs";import{jsx as ue,jsxs as Cf}from"react/jsx-runtime";function xf({cols:e}){let[t,n]=YL(ZL);zL(()=>{let r=!1,o=ie(),i=0,a=0;if(VL(ee))try{let l=E().prepare(`SELECT
|
|
918
1088
|
(SELECT COUNT(*) FROM sessions) AS sessions,
|
|
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})),
|
|
1089
|
+
(SELECT COUNT(*) FROM projects) AS projects`).get();i=l.sessions,a=l.projects}catch{}return n(d=>({...d,daemon:o,sessions:i,projects:a})),Le().then(d=>{r||n(l=>({...l,tier:d.tier}))}).catch(()=>{}),()=>{r=!0}},[]);let s=e>=Sf+2;return Cf(kf,{flexDirection:"column",children:[s?ya.map((r,o)=>ue(be,{color:Y,bold:!0,children:r},o)):ue(be,{color:Y,bold:!0,children:Rf}),ue(eA,{state:t})]})}function eA({state:e}){let t=ue(be,{color:A,children:" \xB7 "});return Cf(kf,{children:[ue(be,{color:A,children:"v"}),ue(be,{color:Y,children:QL}),t,ue(be,{color:A,children:"daemon: "}),e.daemon?ue(be,{color:re,children:`running 127.0.0.1:${e.daemon.port}`}):ue(be,{color:Ue,children:"stopped"}),t,ue(be,{color:A,children:"sessions: "}),ue(be,{color:Y,children:e.sessions}),e.projects>0?ue(be,{color:A,children:` across ${e.projects} ${e.projects===1?"project":"projects"}`}):null,t,ue(be,{color:A,children:"license: "}),e.tier==="pro"?ue(be,{color:re,children:"Pro"}):ue(be,{color:A,children:"Free"})]})}var KL,QL,ZL,Lf=w(()=>{"use strict";Xe();D();k();ge();yt();KL=qL(import.meta.url),QL=KL("../../../package.json").version,ZL={daemon:null,sessions:0,projects:0,tier:"free"}});import{Box as ct,Text as pe}from"ink";import{jsx as q,jsxs as Pr}from"react/jsx-runtime";function nA(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 wt(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 Af({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=nA(n,e.length,g),h=e.slice(f.start,f.end);if(!i)return Pr(ct,{flexDirection:"column",width:s,height:r,borderStyle:"round",borderColor:A,children:[q(pe,{color:Y,bold:!0,children:" Sessions "}),q(ct,{paddingX:1,paddingY:1,children:q(pe,{color:A,wrap:"wrap",children:"No sessions indexed yet. Quit (q) and run `recall index` first."})})]});let S=[tA[d],l?"grouped":""].filter(Boolean).join(" \xB7 "),y=o?" Sessions \xB7 loading\u2026 ":a?` Sessions \xB7 ${e.length} of ${t} match "${wt(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),Pr(ct,{flexDirection:"column",width:s,height:r,borderStyle:"round",borderColor:A,children:[q(ct,{width:u+2,paddingLeft:1,children:q(pe,{color:Y,bold:!0,children:wt(y,u)})}),q(ct,{flexDirection:"column",flexGrow:1,paddingX:1,children:h.length===0?q(pe,{color:A,children:a?"no matches.":"no sessions."}):h.flatMap((R,B)=>{let L=f.start+B,v=L===n,O=[];return l&&R.project_name!==T&&(O.push(q(rA,{project:R.project_name,width:u},`hdr-${R.project_name}-${L}`)),T=R.project_name),O.push(q(sA,{session:R,width:u,isSelected:v,indented:l},R.id)),O})}),q(ct,{width:u+2,paddingLeft:1,children:q(pe,{color:A,children:e.length>0?wt(` ${n+1} / ${e.length}${f.end<e.length?" \xB7 scroll for more":""} `,u):""})})]})}function sA({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),_=wt(g,h),b=wt(r,f),S=Math.max(1,t-i.length-o.length-_.length-b.length);return Pr(ct,{children:[q(pe,{children:o}),q(pe,{color:n?Y:A,bold:n,children:i}),q(pe,{color:n?Y:"#fefce8",bold:n,children:_}),q(pe,{children:" ".repeat(S)}),q(pe,{color:A,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=wt(a,d),m=wt(r,l),p=Math.max(1,t-i.length-u.length-m.length);return Pr(ct,{children:[q(pe,{color:n?Y:A,bold:n,children:i}),q(pe,{color:n?Y:"#67e8f9",bold:n,children:u}),q(pe,{children:" ".repeat(p)}),q(pe,{color:A,children:m})]})}function rA({project:e,width:t}){return q(ct,{children:q(pe,{color:"#67e8f9",bold:!0,children:wt(`\u25BE ${e}`,t)})})}var tA,Nf=w(()=>{"use strict";$();yt();tA={recent:"recent",longest:"longest",busiest:"busiest project"}});import{useEffect as oA,useState as Ra}from"react";function Of(e,t,n){let s=n?"":"AND is_sidechain = 0";return e.prepare(`SELECT uuid, role, type, timestamp, content_text
|
|
920
1090
|
FROM messages
|
|
921
1091
|
WHERE session_id = @id
|
|
922
1092
|
${s}
|
|
923
1093
|
ORDER BY timestamp DESC
|
|
924
|
-
LIMIT @limit`).all({id:t,limit:
|
|
925
|
-
`),u=l.slice(0,Math.max(1,a-1)),m=l.length-u.length;return
|
|
1094
|
+
LIMIT @limit`).all({id:t,limit:iA})}function vf(e){let[t,n]=Ra([]),[s,r]=Ra(!1),[o,i]=Ra(!1);return oA(()=>{if(!e){n([]),i(!1);return}r(!0);try{let a=E(),d=Of(a,e,!1),l=!1;d.length===0&&(d=Of(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 iA,If=w(()=>{"use strict";k();iA=6});import{Box as Be,Text as Qe}from"ink";import{Fragment as dA,jsx as se,jsxs as on}from"react/jsx-runtime";function aA(e){let t=(e.role??"").toLowerCase();return t==="user"?{label:"user",color:yf}:t==="assistant"?{label:"asst",color:wf}:t==="tool"?{label:"tool",color:Tf}:t==="system"?{label:"sys ",color:A}:e.type==="summary"?{label:"sum ",color:A}:{label:"----",color:A}}function cA(e,t){return e?e.replace(/\s+/g," ").trim().slice(0,t+1).replace(/.{1}$/,n=>e.length>t?"\u2026":n):"(empty)"}function Mf({session:e,width:t,height:n}){let s=e?.id??null,{messages:r,loading:o,fallbackToSidechain:i}=vf(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?on(Be,{flexDirection:"column",width:t,height:n,borderStyle:"round",borderColor:A,children:[on(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:A,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:A,children:"loading messages\u2026"}):r.length===0?se(Qe,{color:A,children:"no messages indexed for this session."}):on(dA,{children:[i?se(Qe,{color:A,children:"(showing sidechain / subagent messages)"}):null,r.map(m=>se(lA,{message:m,width:a,perMessageLines:u},m.uuid))]})})]}):on(Be,{flexDirection:"column",width:t,height:n,borderStyle:"round",borderColor:A,children:[se(Be,{paddingX:1,children:se(Qe,{color:Y,bold:!0,children:"Preview"})}),se(Be,{paddingX:1,paddingY:1,children:se(Qe,{color:A,children:"Select a session on the left to preview."})})]})}function lA({message:e,width:t,perMessageLines:n}){let s=aA(e),r=Math.max(1,n-1),o=Math.max(20,t-2),i=r*o,a=cA(e.content_text,i);return on(Be,{flexDirection:"column",marginBottom:0,children:[on(Be,{children:[se(Qe,{color:s.color,bold:!0,children:s.label}),se(Qe,{color:A,children:` ${e.timestamp?G(e.timestamp):""}`})]}),se(Be,{width:t,children:se(Qe,{wrap:"truncate-end",children:a})})]})}var Df=w(()=>{"use strict";$();If();yt()});import{useMemo as uA}from"react";import{Box as He,Text as Tt}from"ink";import{jsx as Se,jsxs as Wn}from"react/jsx-runtime";function pA(e,t){try{return{ok:!0,result:dr(e,{budget:t})}}catch(n){return{ok:!1,error:n instanceof Error?n.message:String(n)}}}function $f({session:e,width:t,height:n,budget:s}){let r=uA(()=>e?pA(e.id,s):null,[e?.id,s]),o=Math.max(20,t-2),a=Math.max(4,n-2-2);if(!e)return Wn(He,{flexDirection:"column",width:t,height:n,borderStyle:"round",borderColor:A,children:[Se(He,{paddingX:1,children:Se(Tt,{color:Y,bold:!0,children:"Neighborhood"})}),Se(He,{paddingX:1,paddingY:1,children:Se(Tt,{color:A,children:"Select a session and press `n` to assemble its neighborhood."})})]});if(!r)return null;if(!r.ok)return Wn(He,{flexDirection:"column",width:t,height:n,borderStyle:"round",borderColor:Ue,children:[Se(He,{paddingX:1,children:Se(Tt,{color:Ue,bold:!0,children:"Neighborhood error"})}),Se(He,{paddingX:1,paddingY:1,children:Se(Tt,{color:Ue,children:r.error})})]});let{result:d}=r,l=d.bundle.replace(/\n+$/,"").split(`
|
|
1095
|
+
`),u=l.slice(0,Math.max(1,a-1)),m=l.length-u.length;return Wn(He,{flexDirection:"column",width:t,height:n,borderStyle:"round",borderColor:re,children:[Wn(He,{paddingX:1,flexDirection:"column",children:[Se(He,{width:o,children:Se(Tt,{color:re,bold:!0,wrap:"truncate-end",children:"Neighborhood"})}),Se(He,{width:o,children:Se(Tt,{color:A,wrap:"truncate-end",children:`budget=${s} used=${d.budgetUsed} remaining=${d.budgetRemaining} truncated=${d.truncated.length}`})})]}),Wn(He,{flexDirection:"column",paddingX:1,flexGrow:1,children:[u.map((p,g)=>Se(Tt,{wrap:"truncate-end",children:p||" "},g)),m>0?Se(Tt,{color:A,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 Pf=w(()=>{"use strict";Bi();yt()});import{useEffect as mA,useState as Ff}from"react";import{Box as Xn,Text as Rt}from"ink";import ka from"ink-text-input";import{jsx as ke,jsxs as xa}from"react/jsx-runtime";function jf({mode:e,query:t,onQueryChange:n,onSearchSubmit:s,onAliasSubmit:r,onTagSubmit:o,aliasInitial:i,toast:a}){let[d,l]=Ff(""),[u,m]=Ff("");return mA(()=>{e==="alias"&&l(i),e==="tag"&&m("")},[e,i]),a&&e==="normal"?ke(Xn,{children:ke(Rt,{color:Ue,children:`\u25B6 ${a}`})}):e==="search"?xa(Xn,{children:[ke(Rt,{color:Y,bold:!0,children:"/ "}),ke(ka,{value:t,onChange:n,onSubmit:s,placeholder:"filter sessions by project, alias, or opening message..."}),ke(Rt,{color:A,children:" esc clear \xB7 enter confirm"})]}):e==="alias"?xa(Xn,{children:[ke(Rt,{color:re,bold:!0,children:"alias \u203A "}),ke(ka,{value:d,onChange:l,onSubmit:p=>r(p),placeholder:"terminal-tab name for this session"}),ke(Rt,{color:A,children:" esc cancel \xB7 enter save"})]}):e==="tag"?xa(Xn,{children:[ke(Rt,{color:re,bold:!0,children:"tag \u203A "}),ke(ka,{value:u,onChange:m,onSubmit:p=>o(p),placeholder:"add a tag to this session"}),ke(Rt,{color:A,children:" esc cancel \xB7 enter save"})]}):ke(Xn,{children:ke(Rt,{color:A,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 Uf=w(()=>{"use strict";yt()});import{Box as ye,Text as W}from"ink";import{jsx as U,jsxs as xe}from"react/jsx-runtime";function Bf({width:e,height:t}){return xe(ye,{flexDirection:"column",width:e,height:t,borderStyle:"round",borderColor:Y,paddingX:2,paddingY:1,children:[xe(ye,{children:[U(W,{color:Y,bold:!0,children:"Claude Recall TUI \u2014 Help"}),U(W,{color:A,children:" \xB7 press ? or esc to close"})]}),xe(ye,{marginTop:1,flexDirection:"column",children:[U(W,{color:re,bold:!0,children:"Navigation"}),gA.map(n=>U(Ca,{row:n},n.key))]}),xe(ye,{marginTop:1,flexDirection:"column",children:[U(W,{color:re,bold:!0,children:"Actions on the selected session"}),fA.map(n=>U(Ca,{row:n},n.key))]}),xe(ye,{marginTop:1,flexDirection:"column",children:[U(W,{color:re,bold:!0,children:"Right-pane views"}),_A.map(n=>U(Ca,{row:n},n.key))]}),xe(ye,{marginTop:1,flexDirection:"column",children:[U(W,{color:re,bold:!0,children:"What is Neighborhood?"}),U(W,{color:A,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(ye,{marginTop:1,flexDirection:"column",children:[U(W,{color:re,bold:!0,children:"Maintenance & diagnostics"}),U(W,{color:A,wrap:"wrap",children:"Two commands cover everything. Quit the TUI with `q` first, then:"}),xe(ye,{marginTop:1,children:[U(W,{color:A,children:" \u2022 "}),U(W,{children:"recall doctor"})]}),U(W,{color:A,wrap:"wrap",children:" Health report: DB size, WAL, FTS fragmentation, integrity, disk free."}),U(W,{color:A,wrap:"wrap",children:" Run this first if anything feels slow or wrong. Add --json for machine output."}),xe(ye,{marginTop:1,children:[U(W,{color:A,children:" \u2022 "}),U(W,{children:"recall optimize"})]}),U(W,{color:A,wrap:"wrap",children:" Maintenance: WAL truncate + FTS5 merge + planner stats. Safe while daemon is running."}),U(W,{color:A,wrap:"wrap",children:" Add --vacuum (with daemon stopped) to also reclaim disk pages from deleted rows."})]}),xe(ye,{marginTop:1,flexDirection:"column",children:[U(W,{color:re,bold:!0,children:"How to pipe a session into Claude"}),U(W,{color:A,wrap:"wrap",children:"Two recipes. Both work in any shell \u2014 exit this TUI with `q` first, then run them."}),xe(ye,{marginTop:1,children:[U(W,{color:A,children:" 1. "}),U(W,{children:"recall context <id> | claude"})]}),U(W,{color:A,wrap:"wrap",children:" Pipes one session's full transcript as markdown into a fresh `claude` chat."}),U(W,{color:A,wrap:"wrap",children:" Use when you want to continue exactly where one session left off."}),xe(ye,{marginTop:1,children:[U(W,{color:A,children:" 2. "}),U(W,{children:"recall neighborhood <id> | claude"})]}),U(W,{color:A,wrap:"wrap",children:" Pipes the bundle (parents + children + citations + similar + bug matches)."}),U(W,{color:A,wrap:"wrap",children:" Use when you want broader context, not just one transcript."})]})]})}function Ca({row:e}){return xe(ye,{children:[U(ye,{width:16,children:U(W,{color:Y,children:e.key})}),U(W,{color:A,wrap:"truncate-end",children:e.description})]})}var gA,fA,_A,Hf=w(()=>{"use strict";yt();gA=[{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"}],fA=[{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)"}],_A=[{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 hA,useMemo as EA,useState as Fr}from"react";import{existsSync as bA}from"node:fs";function Wf(e){let[t,n]=Fr([]),[s,r]=Fr(!0),[o,i]=Fr(null),[a,d]=Fr(!0);return hA(()=>{if(!bA(ee)){d(!1),r(!1);return}try{let m=E().prepare(`SELECT s.id, p.name AS project_name, s.started_at,
|
|
926
1096
|
s.message_count, s.first_user_message,
|
|
927
1097
|
NULLIF(sa.alias, '') AS alias,
|
|
928
1098
|
s.auto_title
|
|
@@ -934,62 +1104,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS vec_chunks_v2 USING vec0(
|
|
|
934
1104
|
AND COALESCE(s.auto_title, '') NOT LIKE '[output-index]%'
|
|
935
1105
|
AND COALESCE(s.auto_title, '') NOT LIKE '[skill]%'
|
|
936
1106
|
ORDER BY COALESCE(s.started_at, '') DESC
|
|
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}
|
|
938
|
-
|
|
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}\`**
|
|
941
|
-
|
|
942
|
-
\`\`\`json
|
|
943
|
-
${o}
|
|
944
|
-
\`\`\``);continue}if(s.type==="tool_result"){let r=vr(Ef(s));if(r){let o=Ca(r,"tool result");t.push(`**Tool result**
|
|
945
|
-
|
|
946
|
-
\`\`\`
|
|
947
|
-
${o}
|
|
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(`
|
|
949
|
-
|
|
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(`
|
|
951
|
-
INSERT INTO message_usage (
|
|
952
|
-
message_uuid, session_id, model,
|
|
953
|
-
input_tokens, output_tokens, cache_create_tokens, cache_read_tokens,
|
|
954
|
-
timestamp
|
|
955
|
-
) VALUES (
|
|
956
|
-
@uuid, @session_id, @model,
|
|
957
|
-
@input, @output, @cc, @cr,
|
|
958
|
-
@ts
|
|
959
|
-
)
|
|
960
|
-
ON CONFLICT(message_uuid) DO UPDATE SET
|
|
961
|
-
model = excluded.model,
|
|
962
|
-
input_tokens = excluded.input_tokens,
|
|
963
|
-
output_tokens = excluded.output_tokens,
|
|
964
|
-
cache_create_tokens = excluded.cache_create_tokens,
|
|
965
|
-
cache_read_tokens = excluded.cache_read_tokens,
|
|
966
|
-
timestamp = excluded.timestamp
|
|
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
|
|
968
|
-
COALESCE(SUM(input_tokens), 0) AS input_tokens,
|
|
969
|
-
COALESCE(SUM(output_tokens), 0) AS output_tokens,
|
|
970
|
-
COALESCE(SUM(cache_create_tokens), 0) AS cache_create_tokens,
|
|
971
|
-
COALESCE(SUM(cache_read_tokens), 0) AS cache_read_tokens
|
|
972
|
-
FROM message_usage WHERE session_id = ?`).get(t),s=e.prepare(`SELECT model, SUM(output_tokens) AS out
|
|
973
|
-
FROM message_usage
|
|
974
|
-
WHERE session_id = ? AND model IS NOT NULL
|
|
975
|
-
GROUP BY model
|
|
976
|
-
ORDER BY out DESC LIMIT 1`).get(t);e.prepare(`UPDATE sessions SET
|
|
977
|
-
total_input_tokens = @input,
|
|
978
|
-
total_output_tokens = @output,
|
|
979
|
-
total_cache_create_tokens = @cc,
|
|
980
|
-
total_cache_read_tokens = @cr,
|
|
981
|
-
primary_model = @model
|
|
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(`
|
|
983
|
-
`).map(l=>l.trim()).find(l=>l.length>=4);if(d)return d.slice(0,60)}let a=e.split(`
|
|
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
|
|
986
|
-
SET auto_title = ?,
|
|
987
|
-
auto_title_source = ?,
|
|
988
|
-
auto_title_generated_at = ?,
|
|
989
|
-
auto_title_history = ?
|
|
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(`
|
|
1107
|
+
LIMIT @limit`).all({limit:SA});n(m)}catch(u){i(u instanceof Error?u.message:String(u))}finally{r(!1)}},[]),{sessions:EA(()=>{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 SA,Xf=w(()=>{"use strict";k();D();SA=200});import{useEffect as La,useMemo as yA,useState as Ze}from"react";import{Box as kt,Text as jr,useApp as wA,useInput as TA}from"ink";import{spawn as RA}from"node:child_process";import{platform as Ur}from"node:os";import{jsx as me,jsxs as Na}from"react/jsx-runtime";function Jf(){return{cols:process.stdout.columns??100,rows:process.stdout.rows??30}}function AA(e){let t=Ur()==="darwin"?"open":Ur()==="win32"?"start":"xdg-open",n=Ur()==="win32"?["",e]:[e];RA(t,n,{detached:!0,stdio:"ignore",shell:Ur()==="win32"}).unref()}function zf({onShowSession:e}){let{exit:t}=wA(),n=Jf(),[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"),[y,T]=Ze(!1),[R,B]=Ze(!1),{sessions:L,total:v,loading:O,error:x,dbExists:I}=Wf(l),F=yA(()=>{if(L.length===0)return L;let H=new Map;if(b==="busiest"||y)for(let M of L)H.set(M.project_name,(H.get(M.project_name)??0)+1);let J=[...L];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??"")}),y){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 Fa=H.get(Ie)??0,ja=H.get(tt)??0;if(ja!==Fa)return ja-Fa}return Ie.localeCompare(tt)}),ve=[];for(let Ie of z)for(let tt of M.get(Ie)??[])ve.push(tt);return ve}return J},[L,b,y]);La(()=>{let H=()=>{let J=Jf();r(J.cols),i(J.rows)};return process.stdout.on("resize",H),()=>{process.stdout.off("resize",H)}},[]),La(()=>{if(F.length===0){a!==0&&d(0);return}a>=F.length&&d(F.length-1)},[F.length,a]),La(()=>{if(!g)return;let H=setTimeout(()=>f(null),2500);return()=>clearTimeout(H)},[g]),TA((H,J)=>{if(m==="search"){J.escape&&(p("normal"),u(""));return}if(m==="alias"||m==="tag"){J.escape&&p("normal");return}if(R){(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=ie();if(!z){f("start the daemon first (`recall start`)");return}let ve=`http://127.0.0.1:${z.port}/sessions/${M.id}`;AA(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: ${NA[ve]}`),ve});return}if(H==="g"){T(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{Gt(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=Ul(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<wa||o<Ta)return Na(kt,{flexDirection:"column",padding:1,children:[me(jr,{color:Ue,children:"Terminal too small."}),me(jr,{color:A,children:`Resize to at least ${wa} cols x ${Ta} rows. Current: ${s} x ${o}.`}),me(jr,{color:A,children:"Press q to quit."})]});let We=Math.max(10,o-LA-Gf),xt=Math.max(36,Math.floor(s*.4)),Pa=s-xt-1,Hr=F[a]??null;return Na(kt,{flexDirection:"column",width:s,height:o,children:[me(xf,{cols:s}),me(kt,{height:1}),x?me(kt,{paddingX:1,children:me(jr,{color:Ue,children:`Error loading sessions: ${x}`})}):R?me(kt,{height:We,children:me(Bf,{width:s,height:We})}):Na(kt,{flexDirection:"row",height:We,children:[me(Af,{sessions:F,total:v,selected:a,width:xt,height:We,loading:O,dbExists:I,filter:l,sortMode:b,groupByProject:y}),me(kt,{width:1,height:We}),h==="neighborhood"?me($f,{session:Hr,width:Pa,height:We,budget:4e3}):me(Mf,{session:Hr,width:Pa,height:We})]}),me(kt,{height:Gf,width:s,paddingX:1,children:me(jf,{mode:m,query:l,onQueryChange:u,onSearchSubmit:()=>p("normal"),onAliasSubmit:ne,onTagSubmit:et,aliasInitial:Hr?.alias??"",toast:g})})]})}var kA,xA,CA,Gf,LA,Aa,NA,Yf=w(()=>{"use strict";Lf();Nf();Df();Pf();Uf();Hf();Xf();Xe();At();No();yt();kA=4,xA=1,CA=1,Gf=1,LA=kA+xA+CA,Aa=["recent","longest","busiest"];NA={recent:"most recent first",longest:"longest sessions first",busiest:"busiest project first"}});import{render as OA}from"ink";import{jsx as vA}from"react/jsx-runtime";async function qf(){let e={showSessionId:null};return await OA(vA(zf,{onShowSession:s=>{e.showSessionId=s}})).waitUntilExit(),e}var Vf=w(()=>{"use strict";Yf()});var Kf={};V(Kf,{runTui:()=>MA});import{spawn as IA}from"node:child_process";async function MA(){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 qf();if(!e.showSessionId)return;let t=process.argv[1];t&&await new Promise(n=>{let s=IA(process.execPath,[t,"show",e.showSessionId],{stdio:"inherit"});s.on("exit",()=>n()),s.on("error",()=>n())})}var Qf=w(()=>{"use strict";Vf()});function va(e,t={}){let n=t.limit??DA()??75e3,r=e.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get()?.n??0;if(r>n)throw new Oa(r,n)}function DA(){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 Oa,Zf=w(()=>{"use strict";Oa=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 $A}from"node:worker_threads";import{join as PA}from"node:path";function jA(){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??jA()??FA,n=(e.workerFactory??UA)();return new Promise((s,r)=>{let o=setTimeout(()=>{n.terminate().catch(()=>{}),r(new Ia(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 UA(){let e=PA(te(),"dist","daemon","query-worker.js"),t={...process.env,RECALL_DB_PROFILE:"worker"};return new $A(e,{env:t})}var Ia,FA,e_=w(()=>{"use strict";Ge();Ia=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"},FA=1e4});var t_={};V(t_,{findSimilarSessions:()=>HA,vectorSearch:()=>BA});async function BA(e,t=50){let n=E();return lt(n),va(n),Ma({query:e,limit:t})}async function HA(e,t=10,n=.65){let s=E();lt(s),va(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 n_=w(()=>{"use strict";k();Yn();Zf();e_()});import{createRequire as WA}from"module";import{Command as XA}from"commander";k();D();Kn();$();jt();Qn();so();ao();import{basename as ch}from"node:path";var lh=[/^<local-command-caveat>/,/^<command-name>/,/^<command-message>/,/^<system-reminder>/,/^<user-prompt-submit-hook>/,/^Caveat: The messages below were generated/i];function dh(e){let t=e.trim();return t?lh.some(n=>n.test(t)):!0}function uh(e){let t=e;return t=t.replace(/^(?:<[^>]+>[\s\S]*?<\/[^>]+>\s*)+/,"").trim(),t=t.replace(/^<command-[^>]+>[^<]*<\/command-[^>]+>\s*/gm,"").trim(),!t||dh(t)?null:t}function ph(e,t,n){let s=Gr(t),r=n??s,o=n?ch(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 mh(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 nc(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 _=uh(f.contentText);_&&(h.firstUserMessage=Q(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=ph(e,t.encodedProject,o),a=new Date().toISOString(),d=e.prepare(`
|
|
993
1108
|
INSERT INTO sessions (
|
|
994
1109
|
id, project_id, file_path, file_mtime,
|
|
995
1110
|
started_at, ended_at, message_count,
|
|
@@ -1024,30 +1139,30 @@ ${o}
|
|
|
1024
1139
|
@is_sidechain, @content_text, @tool_names, @raw_json
|
|
1025
1140
|
)
|
|
1026
1141
|
ON CONFLICT(uuid) DO NOTHING
|
|
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}
|
|
1142
|
+
`),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}Vr(e,f.sessionId,f.entries),ln(e,f.sessionId),m+=1}})(),io().heuristicEnabled)for(let f of r.values()){let h=to(f.firstUserMessage);h&&no(f.sessionId,h,"heuristic")}return{inserted:!0,sessionCount:m,messageCount:p}}async function pc(e){let t=E(),n=Xa();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 mh(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)`))}k();$();import gh from"cli-table3";function mc(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,
|
|
1028
1143
|
s.message_count, s.first_user_message, s.git_branch
|
|
1029
1144
|
FROM sessions s
|
|
1030
1145
|
JOIN projects p ON p.id = s.project_id
|
|
1031
1146
|
WHERE ${r}
|
|
1032
1147
|
ORDER BY COALESCE(s.started_at, '') DESC
|
|
1033
|
-
LIMIT @limit`).all(s);if(o.length===0){console.log(c.dim("no sessions found. run `recall index` first."));return}let i=new
|
|
1034
|
-
`,"utf-8"),
|
|
1148
|
+
LIMIT @limit`).all(s);if(o.length===0){console.log(c.dim("no sessions found. run `recall index` first."));return}let i=new gh({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(Q(a.project_name,20)),c.dim(G(a.started_at)),String(a.message_count),Q(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.`))}k();$();import{spawn as Rh}from"node:child_process";import{readFileSync as fh,writeFileSync as _h,mkdirSync as hh,chmodSync as Eh}from"node:fs";import{join as gc}from"node:path";import{homedir as fc}from"node:os";function _c(){return gc(fc(),".recall","config.json")}function hc(){try{return JSON.parse(fh(_c(),"utf-8"))}catch{return{}}}function bh(e){let t=_c();hh(gc(fc(),".recall"),{recursive:!0}),_h(t,JSON.stringify(e,null,2)+`
|
|
1149
|
+
`,"utf-8"),Eh(t,384)}function es(){let t=hc().verification;return typeof t=="object"&&t!==null&&"enabled"in t?!!t.enabled:!1}function co(e){let t=hc();t.verification={...typeof t.verification=="object"&&t.verification!==null?t.verification:{},enabled:e},bh(t)}k();var Sh=[/\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],yh=1440*60*1e3;function wh(e){let t=E(),n=t.prepare(`SELECT content_text, tool_names FROM messages
|
|
1035
1150
|
WHERE session_id = ? AND role = 'assistant'
|
|
1036
|
-
ORDER BY timestamp DESC LIMIT 5`).all(e),s=!1;for(let d of n)if(d.content_text&&
|
|
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
|
|
1151
|
+
ORDER BY timestamp DESC LIMIT 5`).all(e),s=!1;for(let d of n)if(d.content_text&&Sh.some(l=>l.test(d.content_text))){s=!0;break}let r=t.prepare(`SELECT content_text, tool_names FROM messages
|
|
1152
|
+
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 Th(e){let t=wh(e);return E().prepare("UPDATE sessions SET verification_status = ?, verification_computed_at = ? WHERE id = ?").run(t.status,Date.now(),e),t}function Ec(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<yh){let s={fileWrites:!1,testRuns:!1,commits:!1,buildSuccess:!1};return{status:n.verification_status,evidence:s,claimFound:n.verification_status!=="neutral"}}return Th(e)}function kh(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 xh(e,t){let n=E(),s=n.prepare(`SELECT s.id, p.name AS project_name, p.decoded_path,
|
|
1038
1153
|
s.started_at, s.ended_at, s.message_count,
|
|
1039
1154
|
s.git_branch, s.version, s.cwd
|
|
1040
1155
|
FROM sessions s
|
|
1041
1156
|
JOIN projects p ON p.id = s.project_id
|
|
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}`:""))),
|
|
1157
|
+
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}`:""))),es()){let d=Ec(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
|
|
1043
1158
|
FROM messages
|
|
1044
1159
|
WHERE session_id = ?
|
|
1045
1160
|
ORDER BY COALESCE(timestamp, ''), rowid
|
|
1046
1161
|
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
1162
|
`).map(p=>" "+p).join(`
|
|
1048
1163
|
`);r.push(m)}r.push("")}return r.push(o),r.push(c.dim(`end of session ${s.id}`)),r.join(`
|
|
1049
|
-
`)}function
|
|
1050
|
-
`).length>n}function
|
|
1164
|
+
`)}function Ch(e,t){if(!t||!process.stdout.isTTY)return!1;let n=process.stdout.rows||24;return e.split(`
|
|
1165
|
+
`).length>n}function Lh(e){let t=Rh("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 bc(e,t){let n=E(),s=kh(n,e);if(!s){e.length>=32&&console.error(c.err(`session not found: ${e}`)),process.exitCode=1;return}let r=xh(s,t);if(r===null){console.error(c.err(`session metadata missing for ${s}`)),process.exitCode=1;return}let o=t.pager!==!1;Ch(r,o)?Lh(r):console.log(r)}k();$();ge();D();import{existsSync as lE,readFileSync as dE}from"node:fs";import{join as uE}from"node:path";So();function vc(e){let t=Oc(),n={...e};return t&&(n["x-recall-token"]=t),n}async function nt(e,t){let n=t?.headers??{};return fetch(e,{...t,headers:vc(n)})}async function ut(e,t,n,s){let r=n!==void 0?{"content-type":"application/json",...s}:{...s};return fetch(t,{method:e,headers:vc(r),body:n!==void 0?JSON.stringify(n):void 0})}function Ic(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 pE(e,t){let n=uE(C,"daemon.port");lE(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=dE(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=Ic(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)=>qr(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(Q(h,20))} ${c.dim(G(m.started_at))} ${g} ${f}`),console.log(` ${Q(p,200)}`),console.log("")}console.log(c.dim("`recall show <id>` to read any session."))}async function Mc(e,t){if(await oe("Full-text search"),t.semantic){await pE(e,t);return}let n=E(),s=Ic(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,
|
|
1051
1166
|
p.name AS project_name,
|
|
1052
1167
|
s.started_at AS started_at,
|
|
1053
1168
|
snippet(messages_fts, 0, '<<', '>>', '\u2026', 16) AS snippet,
|
|
@@ -1060,13 +1175,13 @@ ${o}
|
|
|
1060
1175
|
WHERE messages_fts MATCH @q
|
|
1061
1176
|
${i}
|
|
1062
1177
|
ORDER BY bm25(messages_fts)
|
|
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)
|
|
1178
|
+
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)=>qr(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(Q(d.project_name,20))} ${c.dim(G(d.started_at))} ${u}`),console.log(` ${Q(l,200)}`),console.log("")}console.log(c.dim("`recall show <id>` to read any session."))}k();D();Xe();$();import{statSync as kE,existsSync as xE}from"node:fs";function Hc(){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")),!xE(ee)){console.log(c.dim(" no database yet. run `recall index`."));return}let t=E().prepare(`SELECT
|
|
1064
1179
|
(SELECT COUNT(*) FROM projects) AS projects,
|
|
1065
1180
|
(SELECT COUNT(*) FROM sessions) AS sessions,
|
|
1066
1181
|
(SELECT COUNT(*) FROM messages) AS messages,
|
|
1067
1182
|
(SELECT MIN(started_at) FROM sessions WHERE started_at IS NOT NULL) AS earliest,
|
|
1068
1183
|
(SELECT MAX(started_at) FROM sessions WHERE started_at IS NOT NULL) AS latest,
|
|
1069
|
-
(SELECT MAX(indexed_at) FROM sessions) AS last_indexed`).get(),s=(
|
|
1184
|
+
(SELECT MAX(indexed_at) FROM sessions) AS last_indexed`).get(),s=(kE(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=ie();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("")}k();$();import CE from"cli-table3";function Wc(){let t=E().prepare(`SELECT p.name,
|
|
1070
1185
|
p.decoded_path,
|
|
1071
1186
|
COUNT(s.id) AS session_count,
|
|
1072
1187
|
COALESCE(SUM(s.message_count), 0) AS message_count,
|
|
@@ -1074,14 +1189,12 @@ ${o}
|
|
|
1074
1189
|
FROM projects p
|
|
1075
1190
|
LEFT JOIN sessions s ON s.project_id = p.id
|
|
1076
1191
|
GROUP BY p.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
|
|
1078
|
-
`)}async function
|
|
1079
|
-
`)){
|
|
1080
|
-
|
|
1081
|
-
`)
|
|
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,
|
|
1192
|
+
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 CE({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(Q(s.name,30)),String(s.session_count),String(s.message_count),c.dim(G(s.latest)),c.dim(Q(s.decoded_path,50))]);console.log(n.toString())}Xe();Xe();D();$();Ge();_n();import{spawn as NE}from"node:child_process";import{openSync as OE}from"node:fs";import{join as vE}from"node:path";function IE(){return vE(te(),"dist","daemon","entrypoint.js")}function ME(e){return e.list({excludePids:[e.selfPid]})}function DE(e){let t=[];t.push("ERROR: another Recall daemon is already running:");for(let n of e){let s=fn(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(`
|
|
1193
|
+
`)}async function ds(e={}){let t=e.listDaemonProcesses??st,n=e.selfPid??process.pid,s=e.getRunningDaemon??ie,r=e.logger??(p=>console.log(p)),o=e.logErr??(p=>console.error(p)),i=ME({list:t,selfPid:n});if(i.length>0){o(DE(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=OE(as,"a"),l=IE();NE(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: ${as}`));return}}o(c.err("daemon did not come up within 5s \u2014 check the log file")),o(c.dim(` ${as}`)),process.exitCode=1}Xe();_n();En();k();gs();ps();import*as Kc from"node:readline";var qE=2e3;async function fs(e={}){let t=e.list??Lt,n=e.kill??VE,s=e.confirm??ZE,r=e.sleep??KE,o=e.logger??(g=>{process.stdout.write(g+`
|
|
1194
|
+
`)}),i=e.truncateWal??QE,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 ${pt(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:To(f)})}await r(qE);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:To(f)})}}catch{}if(p.killed.length>0)try{i(),p.walTruncated=!0}catch(g){a||o(` (WAL truncate skipped: ${To(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 VE(e,t){process.kill(e,t)}function KE(e){return new Promise(t=>{setTimeout(t,e)})}function QE(){ms(E(),"TRUNCATE")}function ZE(e){return new Promise(t=>{let n=Kc.createInterface({input:process.stdin,output:process.stderr});n.question(e,s=>{n.close();let r=s.trim().toLowerCase();t(r==="y"||r==="yes")})})}function To(e){if(e instanceof Error){let t=e.code;return t?`${t} ${e.message}`:e.message}return String(e)}k();gs();$();async function Qc(e={}){let t=e.logger??(y=>console.log(y)),n=e.logErr??(y=>console.error(y)),s=e.getRunningDaemon??ie,r=e.isProcessAlive??hn,o=e.clearDaemonInfo??Uc,i=e.sleep??(y=>new Promise(T=>setTimeout(T,y))),a=e.kill??((y,T)=>{process.kill(y,T)}),d=e.truncateWal??nb,l=e.listDaemonProcesses??st,u=e.selfPid??process.pid,p=s()?.pid,{ok:g,killed:f}=await tb({log:t,logErr:n,getDaemon:s,isAlive:r,clearInfo:o,kill:a,sleep:i}),h=p!=null?[u,p]:[u],_=await eb({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 fs({all:!0,yes:!0,list:e.listMcpProcesses,kill:e.kill,sleep:e.sleep,logger:t,truncateWal:d}),d()),g?0:1}async function eb(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 tb(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 nb(){try{ms(E(),"TRUNCATE")}catch(e){console.warn(c.dim(`WAL truncate skipped: ${e.message}`))}}Xe();import{spawn as sb}from"node:child_process";import{platform as _s}from"node:os";$();function rb(e){let t=_s()==="darwin"?"open":_s()==="win32"?"start":"xdg-open",n=_s()==="win32"?["",e]:[e];sb(t,n,{detached:!0,stdio:"ignore",shell:_s()==="win32"}).unref()}async function Zc(){let e=ie();if(!e&&(console.log(c.dim("daemon not running \u2014 starting it\u2026")),await ds(),e=ie(),!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}`),rb(t)}k();var ob=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,ib=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,ab=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function cb(e){return e.replace(ob,"").trim()}function lb(e){let t=e.replace(ib,"[tool call]");return t=t.replace(ab,"[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,`
|
|
1195
|
+
|
|
1196
|
+
`),t.trim()}function db(e){return e.role??e.type??"message"}function el(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=cb(m);s==="condensed"&&(p=lb(p));let g=p.length>0,f=!!u.tool_names&&u.tool_names.length>0;if(!g&&!f){l+=1;continue}let h=db(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(`
|
|
1197
|
+
`)}k();D();import{randomUUID as tl}from"node:crypto";import{writeFileSync as nl,readFileSync as pI,existsSync as ub,mkdirSync as pb}from"node:fs";import{join as Ro}from"node:path";var hs=Ro(C,"threads"),mb=Ro(hs,"index.json");function sl(){j(),ub(hs)||pb(hs,{recursive:!0})}function rl(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 ol(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,
|
|
1085
1198
|
p.name AS project,
|
|
1086
1199
|
COUNT(*) AS n,
|
|
1087
1200
|
SUM(CASE WHEN te.role = 'origin' THEN 1 ELSE 0 END) AS origin_n
|
|
@@ -1089,7 +1202,7 @@ ${o}
|
|
|
1089
1202
|
LEFT JOIN sessions s ON s.id = te.session_id
|
|
1090
1203
|
LEFT JOIN projects p ON p.id = s.project_id
|
|
1091
1204
|
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
|
|
1205
|
+
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 il(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 al(e){let n=E().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
|
|
1093
1206
|
s.auto_title AS auto_title,
|
|
1094
1207
|
s.auto_title_source AS auto_title_source,
|
|
1095
1208
|
s.first_user_message AS first_user_message,
|
|
@@ -1097,11 +1210,11 @@ ${o}
|
|
|
1097
1210
|
FROM (SELECT ? AS sid) q
|
|
1098
1211
|
LEFT JOIN sessions s ON s.id = q.sid
|
|
1099
1212
|
LEFT JOIN session_aliases sa ON sa.session_id = q.sid
|
|
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
|
|
1213
|
+
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 cl(e){let n=E().prepare(`SELECT
|
|
1101
1214
|
COUNT(*) AS session_count,
|
|
1102
1215
|
SUM(CASE WHEN role = 'origin' THEN 1 ELSE 0 END) AS origin_count
|
|
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&&(
|
|
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
|
|
1216
|
+
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&&(sl(),nl(Ro(hs,`${e}.json`),JSON.stringify(t,null,2)),ll())}function ll(){sl();let e=ko({includeArchived:!0});nl(mb,JSON.stringify({threads:e},null,2))}function dl(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let n=E(),s=tl(),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)
|
|
1217
|
+
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 ko(e={}){let t=E(),n=e.includeArchived?"":"WHERE archived = 0",s=t.prepare(`SELECT * FROM threads ${n} ORDER BY created_at DESC`).all(),r=ol(s.map(o=>o.id));return s.map(o=>rl(o,cl(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.*,
|
|
1105
1218
|
NULLIF(sa.alias, '') AS alias,
|
|
1106
1219
|
s.auto_title AS auto_title,
|
|
1107
1220
|
s.auto_title_source AS auto_title_source,
|
|
@@ -1112,7 +1225,7 @@ ${o}
|
|
|
1112
1225
|
LEFT JOIN session_aliases sa ON sa.session_id = e.session_id
|
|
1113
1226
|
LEFT JOIN projects p ON p.id = s.project_id
|
|
1114
1227
|
WHERE e.thread_id = ?
|
|
1115
|
-
ORDER BY e.added_at ASC`).all(e).map(
|
|
1228
|
+
ORDER BY e.added_at ASC`).all(e).map(il),r=ol([e]).get(e);return{...rl(n,cl(n.id),r),edges:s}}function ul(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
|
|
1116
1229
|
(thread_id, session_id, parent_session_id, role, confidence, source, added_at)
|
|
1117
1230
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1118
1231
|
ON CONFLICT(thread_id, session_id) DO UPDATE SET
|
|
@@ -1120,57 +1233,57 @@ ${o}
|
|
|
1120
1233
|
role = excluded.role,
|
|
1121
1234
|
confidence = excluded.confidence,
|
|
1122
1235
|
source = excluded.source,
|
|
1123
|
-
added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,o,i,a,s),De(e.threadId);let d=
|
|
1236
|
+
added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,o,i,a,s),De(e.threadId);let d=al(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 pl(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 ml(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
|
|
1124
1237
|
SET parent_session_id = ?, role = ?, added_at = ?
|
|
1125
|
-
WHERE thread_id = ? AND session_id = ?`).run(n,o,new Date().toISOString(),e,t),De(e);let i=
|
|
1238
|
+
WHERE thread_id = ? AND session_id = ?`).run(n,o,new Date().toISOString(),e,t),De(e);let i=al(t);return il({...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 gl(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 fl(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 _l(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 hl(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 El(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
|
|
1126
1239
|
(thread_id, session_id, parent_session_id, role, confidence, source, added_at)
|
|
1127
1240
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1128
1241
|
ON CONFLICT(thread_id, session_id) DO UPDATE SET
|
|
1129
1242
|
parent_session_id = COALESCE(thread_edges.parent_session_id, excluded.parent_session_id),
|
|
1130
1243
|
role = CASE WHEN thread_edges.role = 'origin' OR excluded.role = 'origin' THEN 'origin' ELSE 'child' END,
|
|
1131
1244
|
confidence = MAX(thread_edges.confidence, excluded.confidence),
|
|
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),
|
|
1245
|
+
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),ll();let r=$e(t);if(!r)throw new Error("merge destination disappeared");return r}function bl(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=E(),n=new Date().toISOString(),s=tl();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
|
|
1133
1246
|
(thread_id, session_id, parent_session_id, role, confidence, source, added_at)
|
|
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
|
|
1247
|
+
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 Sl(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();k();D();import{writeFileSync as gb}from"node:fs";import{join as fb}from"node:path";var _b=fb(C,"recall-events.json");function xo(e,t,n,s="cli"){E().prepare(`
|
|
1135
1248
|
INSERT INTO recall_events (session_id, recalled_at, token_count, mode, caller)
|
|
1136
1249
|
VALUES (?, datetime('now'), ?, ?, ?)
|
|
1137
|
-
`).run(e,t,n,s),
|
|
1138
|
-
`,"utf-8")}function
|
|
1139
|
-
`),null)}function
|
|
1250
|
+
`).run(e,t,n,s),hb()}function hb(){j();let t=E().prepare("SELECT id, session_id, recalled_at, token_count, mode, caller FROM recall_events ORDER BY recalled_at DESC").all();gb(_b,JSON.stringify(t,null,2)+`
|
|
1251
|
+
`,"utf-8")}function Eb(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.
|
|
1252
|
+
`),null)}function bb(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 Sb(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
1253
|
`);for(let s of n)process.stderr.write(` ${X(s.id)} ${s.name}
|
|
1141
|
-
`);return null}function
|
|
1254
|
+
`);return null}function yl(e,t,n){let s=e.prepare(`SELECT s.id, p.name AS project_name, p.decoded_path,
|
|
1142
1255
|
s.started_at, s.ended_at, s.message_count, s.git_branch
|
|
1143
1256
|
FROM sessions s JOIN projects p ON p.id = s.project_id
|
|
1144
1257
|
WHERE s.id = ?`).get(t);if(!s)return process.stderr.write(`session metadata missing for ${t}
|
|
1145
1258
|
`),null;let r=e.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
|
|
1146
1259
|
FROM messages
|
|
1147
1260
|
WHERE session_id = ?
|
|
1148
|
-
ORDER BY COALESCE(timestamp, ''), rowid`).all(t),o=n.full?"full":"condensed";return
|
|
1149
|
-
`),process.exitCode=1;return}let a=
|
|
1150
|
-
`),process.exitCode=1;return}let d=
|
|
1151
|
-
`),process.exitCode=1;return}for(let l=0;l<d.length;l+=1){let u=d[l],m=
|
|
1261
|
+
ORDER BY COALESCE(timestamp, ''), rowid`).all(t),o=n.full?"full":"condensed";return el(s,r,{mode:o,includeSidechain:n.subagents===!0,prelude:n.prelude??null,since:bb(n.since)})}async function wl(e,t){await oe("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
|
|
1262
|
+
`),process.exitCode=1;return}let a=Sb(n,i);if(!a){process.stderr.write(`thread not found: ${i}
|
|
1263
|
+
`),process.exitCode=1;return}let d=Sl(a);if(d.length===0){process.stderr.write(`thread ${i} has no linked sessions
|
|
1264
|
+
`),process.exitCode=1;return}for(let l=0;l<d.length;l+=1){let u=d[l],m=yl(n,u,{...t,prelude:l===0?t.prelude:void 0});if(m===null){process.exitCode=1;continue}l>0&&process.stdout.write(`
|
|
1152
1265
|
---
|
|
1153
1266
|
|
|
1154
1267
|
`),process.stdout.write(m),m.endsWith(`
|
|
1155
1268
|
`)||process.stdout.write(`
|
|
1156
|
-
`),
|
|
1157
|
-
`),process.exitCode=1;return}let r=
|
|
1269
|
+
`),xo(u,Math.ceil(m.length/4),"thread","cli")}return}let s=Eb(n,e);if(!s){e.length>=32&&process.stderr.write(`session not found: ${e}
|
|
1270
|
+
`),process.exitCode=1;return}let r=yl(n,s,t);if(r===null){process.exitCode=1;return}process.stdout.write(r),r.endsWith(`
|
|
1158
1271
|
`)||process.stdout.write(`
|
|
1159
|
-
`);let o=t.since?"since":t.full?"full":"condensed";
|
|
1160
|
-
`)}ge();Ge();import{join as
|
|
1161
|
-
`)),process.exit(1));let n=
|
|
1272
|
+
`);let o=t.since?"since":t.full?"full":"condensed";xo(s,Math.ceil(r.length/4),o,"cli"),process.stderr.isTTY&&process.stderr.write(`\x1B[2mShare this recall? \u2192 recall share\x1B[0m
|
|
1273
|
+
`)}ge();Ge();import{join as yb}from"node:path";import{spawn as wb}from"node:child_process";async function Tl(e={}){await oe("MCP server");let t=yb(te(),"dist","mcp-server.js"),n={...process.env};e.allowWrites&&(n.RECALL_MCP_ALLOW_WRITES="1");let s=wb(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()})})}k();jt();$();import{execSync as Co}from"node:child_process";import{randomUUID as Tb}from"node:crypto";import Rb from"node:readline/promises";async function Rl(e){if(e.list){kb();return}if(e.purge){xb(e.purge);return}let t=await Cb();t||(process.stderr.write(c.err(`clipboard empty / nothing on stdin
|
|
1274
|
+
`)),process.exit(1));let n=cn(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
1275
|
`));for(let a of n.slice(0,8))process.stderr.write(` ${c.err(a.pattern)} ${c.dim("\u2192")} ${a.maskedPreview}
|
|
1163
1276
|
`);n.length>8&&process.stderr.write(c.dim(` \u2026 ${n.length-8} more
|
|
1164
1277
|
`)),e.dryRun&&(process.stderr.write(c.dim(`
|
|
1165
1278
|
(dry run \u2014 nothing archived)
|
|
1166
1279
|
`)),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.
|
|
1167
|
-
`)),e.pipe&&process.stdout.write(t),process.exit(1));let o=
|
|
1280
|
+
`)),e.pipe&&process.stdout.write(t),process.exit(1));let o=Rb.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.
|
|
1168
1281
|
`)),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)
|
|
1169
|
-
`)),e.pipe&&process.stdout.write(t),process.exit(0));let s=
|
|
1282
|
+
`)),e.pipe&&process.stdout.write(t),process.exit(0));let s=Tb(),r=new Date().toISOString();E().prepare(`INSERT INTO paste_archives (id, created_at, content, size_bytes, source, label)
|
|
1170
1283
|
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)}
|
|
1171
1284
|
`)),e.label&&process.stderr.write(c.dim(` label: ${e.label}
|
|
1172
1285
|
`)),process.stderr.write(c.dim(` purge any time with: recall paste --purge ${s.slice(0,8)}
|
|
1173
|
-
`)),e.pipe&&process.stdout.write(t)}function
|
|
1286
|
+
`)),e.pipe&&process.stdout.write(t)}function kb(){let e=E().prepare(`SELECT id, created_at, size_bytes, source, label,
|
|
1174
1287
|
substr(replace(replace(content, char(10), '\u21B5'), char(13), ''), 1, 80) AS preview
|
|
1175
1288
|
FROM paste_archives
|
|
1176
1289
|
ORDER BY created_at DESC
|
|
@@ -1182,16 +1295,16 @@ ${c.bold(`${e.length} archived paste${e.length===1?"":"s"}`)}
|
|
|
1182
1295
|
show full content: recall paste --show <id>
|
|
1183
1296
|
`)+c.dim(`purge one (permanent): recall paste --purge <id>
|
|
1184
1297
|
|
|
1185
|
-
`))}function
|
|
1298
|
+
`))}function xb(e){e.length<8&&(process.stderr.write(c.err(`provide at least 8 characters of the paste id to purge.
|
|
1186
1299
|
`)),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
1300
|
`)),process.exit(1)),n.length>1&&(process.stderr.write(c.err(`prefix ${e} is ambiguous. be more specific.
|
|
1188
1301
|
`)),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
|
|
1302
|
+
`))}async function Cb(){if(!process.stdin.isTTY)return await Lb();try{return Co("pbpaste",{encoding:"utf8",maxBuffer:16*1024*1024})}catch{}try{return Co("xclip -o -selection clipboard",{encoding:"utf8",maxBuffer:16*1024*1024})}catch{}try{return Co("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 Lb(){let e=[];for await(let t of process.stdin)e.push(Buffer.isBuffer(t)?t:Buffer.from(t));return Buffer.concat(e).toString("utf8")}k();jt();$();async function kl(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
|
|
1190
1303
|
FROM messages
|
|
1191
|
-
WHERE content_text IS NOT NULL`).all(),d=t.prepare("UPDATE messages SET content_text = ?, raw_json = ? WHERE uuid = ?"),l=(f,h)=>{let _=
|
|
1304
|
+
WHERE content_text IS NOT NULL`).all(),d=t.prepare("UPDATE messages SET content_text = ?, raw_json = ? WHERE uuid = ?"),l=(f,h)=>{let _=cn(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
1305
|
`));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
1306
|
`)),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)
|
|
1307
|
+
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)cn(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
1308
|
`),n.size===0){process.stdout.write(c.ok(`\u2713 clean \u2014 no secrets detected across ${s.toLocaleString()} messages.
|
|
1196
1309
|
`));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.
|
|
1197
1310
|
|
|
@@ -1201,64 +1314,34 @@ show full content: recall paste --show <id>
|
|
|
1201
1314
|
`)}process.stdout.write(`
|
|
1202
1315
|
`),e.redact?(process.stdout.write(c.ok(`\u2713 redacted in place: ${o.toLocaleString()} messages, ${p.toLocaleString()} session previews.
|
|
1203
1316
|
`)),process.stdout.write(c.dim(` (The source JSONL files at ~/.claude/projects/ were not modified.)
|
|
1204
|
-
`))):process.stdout.write(c.dim(" Rerun with --redact to scrub these in place, or run `recall index --force`.\n"))}
|
|
1317
|
+
`))):process.stdout.write(c.dim(" Rerun with --redact to scrub these in place, or run `recall index --force`.\n"))}k();dn();$();import{basename as Ab}from"node:path";async function Cl(e){let t=E(),n=await Nb(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,
|
|
1205
1318
|
s.auto_title,
|
|
1206
1319
|
s.auto_title_source,
|
|
1207
1320
|
CASE WHEN sa.session_id IS NOT NULL THEN 1 ELSE 0 END AS has_alias
|
|
1208
1321
|
FROM sessions s
|
|
1209
1322
|
LEFT JOIN session_aliases sa ON sa.session_id = s.id
|
|
1210
1323
|
WHERE s.project_id = ?
|
|
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=
|
|
1324
|
+
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=ac(m);r[p]+=1,o.push({id:u.id,quality:p})}if(!e.dryRun){let u=t.prepare(`UPDATE sessions
|
|
1212
1325
|
SET title_quality = ?,
|
|
1213
1326
|
title_quality_computed_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=
|
|
1327
|
+
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=Ob(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 Nb(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(Ab(n));return r||null}var xl=28;function Ob(e,t){let n=Math.round(e/t*xl);return c.dim("\u2588".repeat(n)+"\xB7".repeat(xl-n))}At();$();k();zn();k();At();import{readFileSync as $b,existsSync as Ao,statSync as Pb,readdirSync as Fb}from"node:fs";import{join as bs}from"node:path";import{homedir as jb}from"node:os";var bn=["vscode","cursor","windsurf"],Ub={vscode:"Code",cursor:"Cursor",windsurf:"Windsurf"};var Bb=.7,Hb=300*1e3;function Wb(e){let t=e?.homeDir??jb(),n=e?.sources??bn,s=[];for(let r of n){let o=bs(t,"Library","Application Support",Ub[r],"User","workspaceStorage");Ao(o)&&s.push({source:r,root:o})}return s}function Xb(e,t){let n=bs(e,"workspace.json"),s=bs(e,"state.vscdb");if(!Ao(n)||!Ao(s))return[];let r;try{let d=JSON.parse($b(n,"utf8"));if(!d.folder||!d.folder.startsWith("file://"))return[];r=decodeURIComponent(d.folder.replace(/^file:\/\//,""))}catch{return[]}let o;try{o=Pb(s).mtime.toISOString()}catch{return[]}let i;try{i=new Pt(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 Gb(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)<=Hb&&(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 Jb=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 zb(e){return Jb.has(e.trim().toLowerCase())}function Yb(e){let t=e.tab_name?.trim();return!t||zb(t)?null:t}function Nl(e){let t=e?.minScore??Bb,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
|
|
1215
1328
|
FROM sessions s
|
|
1216
1329
|
JOIN projects p ON p.id = s.project_id
|
|
1217
1330
|
LEFT JOIN session_aliases sa ON sa.session_id = s.id
|
|
1218
|
-
WHERE (sa.alias IS NULL OR sa.alias = '')${r}`).all(...o),a=e?.sources??
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
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,
|
|
1224
|
-
p.name AS project,
|
|
1225
|
-
NULLIF(sa.alias, '') AS alias
|
|
1226
|
-
FROM sessions s
|
|
1227
|
-
JOIN projects p ON p.id = s.project_id
|
|
1228
|
-
LEFT JOIN session_aliases sa ON sa.session_id = s.id
|
|
1229
|
-
WHERE s.id = ?`).get(e);if(!n)return null;let s=t.prepare(`SELECT role, content_text
|
|
1230
|
-
FROM messages
|
|
1231
|
-
WHERE session_id = ? AND is_sidechain = 0
|
|
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(`
|
|
1233
|
-
|
|
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
|
|
1236
|
-
(session_id, summary, keywords, model, source_message_count, generated_at)
|
|
1237
|
-
VALUES (@session_id, @summary, @keywords, @model, @source_message_count, @generated_at)
|
|
1238
|
-
ON CONFLICT(session_id) DO UPDATE SET
|
|
1239
|
-
summary = excluded.summary,
|
|
1240
|
-
keywords = excluded.keywords,
|
|
1241
|
-
model = excluded.model,
|
|
1242
|
-
source_message_count = excluded.source_message_count,
|
|
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
|
|
1244
|
-
FROM sessions s
|
|
1245
|
-
LEFT JOIN session_semantic ss ON ss.session_id = s.id
|
|
1246
|
-
WHERE ${o}
|
|
1247
|
-
ORDER BY COALESCE(s.started_at, '') ASC, s.id ASC
|
|
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(`
|
|
1331
|
+
WHERE (sa.alias IS NULL OR sa.alias = '')${r}`).all(...o),a=e?.sources??bn,d=Wb({sources:a,homeDir:e?.homeDir}),l=[];for(let{source:m,root:p}of d){let g;try{g=Fb(p)}catch{continue}for(let f of g){let h=bs(p,f);l.push(...Xb(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:_}=Gb(m,f);h<t||(!p||h>p.score)&&(p={entry:f,score:h,matched:_})}if(!p)continue;let g=Yb(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 Ol(e){let t=[],n=[];for(let s of e){let r=Es(s.session_id);if(r&&r.trim()!==""){n.push(s);continue}t.push(s)}return{applicable:t,skipped:n}}import{basename as qb}from"node:path";async function Il(e){let t=Kb(e.source);if(!t){console.error(c.err("Invalid --source. Allowed: vscode | cursor | windsurf | all (default).")),process.exitCode=1;return}let n=Qb(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?Zb(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=Nl({projectId:r??void 0,sources:t,minScore:n,limit:s}),{applicable:i,skipped:a}=Ol(o);if(e.apply){let d=0;for(let l of i)try{Gt(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(vl)},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(vl),skipped_due_to_existing_alias:a.length},null,2));return}Vb(i,t,n,a.length)}function Vb(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(Q(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(Q(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 vl(e){return e}function Kb(e){if(!e||e==="all")return bn;let t=e.split(",").map(s=>s.trim().toLowerCase()).filter(Boolean),n=[];for(let s of t)if(bn.includes(s))n.push(s);else return null;return n.length>0?n:null}function Qb(e,t){if(e===void 0)return t;let n=Number.parseFloat(e);return Number.isFinite(n)?n:null}function Zb(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=qb(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}Sn();Rs();Ye();wn();Pe();Ds();Xo();k();ge();Jo();async function Yu(e,t){let n=(e??"status").toLowerCase();if(n==="on"||n==="enable"){if(!ae()){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=we();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 Ts({limit:g,force:f,onProgress:y=>{if(y.processed===_)return;_=y.processed;let T=y.total>0?` (${Math.round(y.processed/y.total*100)}%)`:"";process.stdout.write(`\r ${y.processed}/${y.total}${T} ok=${y.ok} failed=${y.failed} `)}});process.stdout.write(`
|
|
1332
|
+
`);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(()=>(gd(),md)),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(()=>(Us(),js));if(_()){let y=await S();y.ok?console.log("GGUF model already installed and verified."):(console.log(`Re-downloading GGUF model (existing file ${y.reason}).`),await b((T,R)=>{let B=R>0?Math.round(T/R*100):0;process.stdout.write(`\r bge-base-en-v1.5-q8_0.gguf: ${B}% `)}),process.stdout.write(`
|
|
1333
|
+
`))}else console.log("Downloading bge-base-en-v1.5-q8_0.gguf (~113MB)..."),await b((y,T)=>{let R=T>0?Math.round(y/T*100):0;process.stdout.write(`\r bge-base-en-v1.5-q8_0.gguf: ${R}% `)}),process.stdout.write(`
|
|
1334
|
+
`);console.log("Loading embedder (llamacpp backend)...")}else{let{ensureTransformersInstalled:f}=await Promise.resolve().then(()=>(Ad(),Ld)),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 Mo((_,b,S)=>{let y=S>0?Math.round(b/S*100):0;process.stdout.write(`\r ${_}: ${y}% `)}),process.stdout.write(`
|
|
1335
|
+
`)),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(),Ms));f instanceof h?(console.log("Model files installed."),console.log(` Embedder load skipped: ${f.message.split(`
|
|
1253
1336
|
`)[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"){
|
|
1337
|
+
`)[0]:String(f)}`),console.error(" This is unexpected. Run `recall doctor` for diagnostics."),process.exitCode=1)}return}if(n==="uninstall"){Do();let{isGgufModelInstalled:p,uninstallGgufModel:g}=await Promise.resolve().then(()=>(Us(),js));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 oe("Vector reindex"),!Z().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(`
|
|
1255
1338
|
SELECT s.id FROM sessions s
|
|
1256
1339
|
WHERE s.message_count >= 3
|
|
1257
1340
|
AND NOT EXISTS (SELECT 1 FROM chunk_meta cm WHERE cm.session_id = s.id)
|
|
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=
|
|
1259
|
-
VALUES (?, ?, ?, ?, 768, 0, datetime('now'))`),v=p.prepare("INSERT INTO vec_chunks(rowid, embedding) VALUES (?, ?)");for(let O=0;O<
|
|
1260
|
-
`),console.log(`Reindexed ${_} sessions.`);return}if(n==="migrate"){await
|
|
1261
|
-
`);let
|
|
1341
|
+
`).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=Wo(b),y=f>0?S.slice(0,f):S;if(y.length===0){_++;continue}let T=y.map(O=>O.text),R=await Ne(T);Go(b);let B=Z().modelId,L=p.prepare(`INSERT INTO chunk_meta(session_id, message_uuids, text, embedding_model_id, embedding_dim, stale, generated_at)
|
|
1342
|
+
VALUES (?, ?, ?, ?, 768, 0, datetime('now'))`),v=p.prepare("INSERT INTO vec_chunks(rowid, embedding) VALUES (?, ?)");for(let O=0;O<y.length;O++){let x=L.run(b,JSON.stringify(y[O].messageUuids),y[O].text,B),I=Buffer.from(R[O].buffer,R[O].byteOffset,R[O].byteLength);v.run(BigInt(x.lastInsertRowid),I)}_++,_%10===0&&process.stdout.write(`\r ${_}/${g.length} `)}process.stdout.write(`
|
|
1343
|
+
`),console.log(`Reindexed ${_} sessions.`);return}if(n==="migrate"){await oe("Vector migration");let{runMigration:p}=await Promise.resolve().then(()=>(qs(),Ys)),{getActiveMigration:g,pauseMigration:f}=await Promise.resolve().then(()=>(Vt(),zs)),h=t.yes??!1,_=t.noBackup??!1,b=t.dryRun??!1,S=()=>{try{let R=g();R&&(f(R.id),console.error("\n[migrate] received SIGINT; migration paused (cursor preserved). Re-run `recall semantic migrate` to resume."))}catch(R){console.error("[migrate] failed to pause migration on SIGINT:",R)}process.exit(130)};b||process.on("SIGINT",S);let y;try{y=await p({interactive:!h,keepBackup:!_,dryRun:b,projectName:t.project,onProgress:R=>{let L=(R.total>0?R.migrated/R.total*100:0).toFixed(1);process.stdout.write(`\r ${R.migrated}/${R.total} (${L}%) ${R.chunksPerSec.toFixed(1)} chunks/sec `)}})}finally{b||process.off("SIGINT",S)}process.stdout.write(`
|
|
1344
|
+
`);let T=0;y.ok?t.project?console.log(y.message):(console.log(`Migration complete. ${y.migrated} chunks re-embedded.`),y.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: ${y.message}`),/nothing to migrate|user declined confirmation/.test(y.message)||(T=1)),process.exit(T)}if(n==="rollback-migration"){await oe("Vector migration rollback");let{rollbackMigration:p}=await Promise.resolve().then(()=>(qs(),Ys)),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 oe("Vector migration rollback pruning");let{pruneRollbackBackup:p}=await Promise.resolve().then(()=>(qs(),Ys)),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-backup"){await oe("Vector backup verification");let{getDb:p}=await Promise.resolve().then(()=>(k(),qn));p().prepare("SELECT name FROM sqlite_master WHERE name='vec_chunks_v1_backup'").get()||(console.log("No vec_chunks_v1_backup table present (already pruned, or no migration has run). Nothing to verify."),process.exit(0));let{countBackupOrphans:h}=await Promise.resolve().then(()=>(Ti(),zu));console.log("Scanning vec_chunks_v1_backup for orphan rows \u2014 this can take several minutes on a large backup (it scans every vector row).");let _=Date.now(),b=h(),S=((Date.now()-_)/1e3).toFixed(1);b===0&&(console.log(`\u2713 No orphan rows. Backup is consistent with chunk_meta (${S}s).`),process.exit(0)),console.error(`! ${b} orphan row(s) in vec_chunks_v1_backup (${S}s) \u2014 the backup-aware delete path missed these. This is a maintainer-facing bug; please report it. The backup is still safe for rollback.`),process.exit(1)}if(n==="verify-spawn"){if(!ae()){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(),ys)),{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 x=f(_,O.name);for(let I of h(x,{withFileTypes:!0}))I.isFile()&&I.name.endsWith(".jsonl")&&v.add(f(x,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 y=Date.now(),T=await p("Reply with the single word: ok",[],{}),R=((Date.now()-y)/1e3).toFixed(1);if(console.log(` CLI exit: ${T.exitCode}, ${R}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 B=b();console.log(` After: ${B.size} JSONL file(s)`);let L=[];for(let v of B)S.has(v)||L.push(v);if(L.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: ${L.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 L.slice(0,5))console.error(` - ${v}`);L.length>5&&console.error(` ... and ${L.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=td(),r=rt(),o=Z(),i=pd(),a=we(),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}`)}k();k();k();Pe();var mt=400,er=768,CT="bge-base-en-v1.5";function LT(e){return Buffer.from(e.buffer,e.byteOffset,e.byteLength)}function AT(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!==er)throw new Error(`embedding dim mismatch: got ${e.embedding.length}, expected ${er}`);let t=e.embedding_model_id??CT,n=new Date().toISOString();return E().prepare(`INSERT INTO message_embeddings
|
|
1262
1345
|
(message_uuid, session_id, embedding,
|
|
1263
1346
|
embedding_model_id, embedding_dim, text_length, generated_at)
|
|
1264
1347
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
@@ -1268,7 +1351,7 @@ show full content: recall paste --show <id>
|
|
|
1268
1351
|
embedding_model_id = excluded.embedding_model_id,
|
|
1269
1352
|
embedding_dim = excluded.embedding_dim,
|
|
1270
1353
|
text_length = excluded.text_length,
|
|
1271
|
-
generated_at = excluded.generated_at`).run(e.message_uuid,e.session_id,
|
|
1354
|
+
generated_at = excluded.generated_at`).run(e.message_uuid,e.session_id,LT(e.embedding),t,er,e.text_length,n),{message_uuid:e.message_uuid,session_id:e.session_id,embedding_model_id:t,embedding_dim:er,text_length:e.text_length,generated_at:n}}function NT(e,t={}){let n=E(),s=t.force?`m.session_id = ? AND m.is_sidechain = 0
|
|
1272
1355
|
AND m.content_text IS NOT NULL
|
|
1273
1356
|
AND length(m.content_text) > ?`:`m.session_id = ? AND m.is_sidechain = 0
|
|
1274
1357
|
AND m.content_text IS NOT NULL
|
|
@@ -1278,7 +1361,7 @@ show full content: recall paste --show <id>
|
|
|
1278
1361
|
LEFT JOIN message_embeddings me ON me.message_uuid = m.uuid
|
|
1279
1362
|
WHERE ${s}
|
|
1280
1363
|
ORDER BY m.timestamp ASC, m.rowid ASC
|
|
1281
|
-
LIMIT ?`).all(e,mt,r)}var
|
|
1364
|
+
LIMIT ?`).all(e,mt,r)}var qu=null;async function OT(){return qu||(Z().loaded||await Ke(),Ne)}async function vT(e,t={}){let n={embedded:0,skipped:0,failed:0,failures:[]},s=NT(e,{limit:t.limit,force:t.force});if(s.length===0)return n;let r=await OT(),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{AT({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 Vu(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
|
|
1282
1365
|
FROM sessions s
|
|
1283
1366
|
JOIN messages m ON m.session_id = s.id
|
|
1284
1367
|
LEFT JOIN message_embeddings me ON me.message_uuid = m.uuid
|
|
@@ -1288,7 +1371,7 @@ show full content: recall paste --show <id>
|
|
|
1288
1371
|
AND length(m.content_text) > ?
|
|
1289
1372
|
AND me.message_uuid IS NULL
|
|
1290
1373
|
ORDER BY s.started_at ASC
|
|
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
|
|
1374
|
+
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 vT(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 tr(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 Ku(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
|
|
1292
1375
|
COUNT(*) AS total,
|
|
1293
1376
|
SUM(CASE WHEN ss.session_id IS NOT NULL THEN 1 ELSE 0 END) AS with_semantic,
|
|
1294
1377
|
SUM(CASE WHEN cm_count.session_id IS NOT NULL THEN 1 ELSE 0 END) AS with_chunks,
|
|
@@ -1315,8 +1398,8 @@ show full content: recall paste --show <id>
|
|
|
1315
1398
|
FROM chunk_meta cm
|
|
1316
1399
|
${f}`).get(...r),_=n?" JOIN sessions s ON s.id = cq.session_id WHERE s.project_id = ?":"",b=t.prepare(`SELECT COUNT(*) AS n
|
|
1317
1400
|
FROM chunk_queue cq
|
|
1318
|
-
${_}`).get(...r);return{project:n,sessions:{total:i,with_semantic:a,with_semantic_pct
|
|
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
|
|
1401
|
+
${_}`).get(...r);return{project:n,sessions:{total:i,with_semantic:a,with_semantic_pct:tr(a,i),with_chunks:d,with_chunks_pct:tr(d,i),with_message_embeddings:l,with_message_embeddings_pct:tr(l,i)},messages:{total:m.total??0,eligible:m.eligible??0,embedded:g.embedded,embedded_pct:tr(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 Qu(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}}Rs();Sn();ge();wn();Pe();async function Zu(e,t){let n=(e??"audit").toLowerCase();if(n==="audit")return IT(t);if(n==="backfill-summaries")return DT(t);if(n==="backfill-messages")return $T(t);console.error(`Unknown embeddings action: ${e}. Supported: audit | backfill-summaries | backfill-messages`),process.exitCode=1}function Ri(e){if(!e)return null;let t=Te(e);return t||"not-found"}async function IT(e){let t=Ri(e.project);if(t==="not-found"){console.error(`Project "${e.project}" not found.`),process.exitCode=1;return}let n=Ku({projectId:t?t.id:void 0}),s=Qu(n);if(e.json){console.log(JSON.stringify({report:n,verdict:s},null,2));return}MT(n,s),s.passes||(process.exitCode=1)}function MT(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 DT(e){if(!we().enabled){console.error("Semantic search is disabled. Run `recall semantic on` first."),process.exitCode=1;return}let n=Ri(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 Ts({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(`
|
|
1402
|
+
`);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 $T(e){if(await oe("Per-message embeddings"),!rt()){console.error("Model not installed. Run `recall semantic install` first."),process.exitCode=1;return}Z().loaded||await Ke();let t=Ri(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 Vu({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
1403
|
`);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
|
|
1321
1404
|
FROM sessions s
|
|
1322
1405
|
JOIN messages m ON m.session_id = s.id
|
|
@@ -1325,7 +1408,7 @@ show full content: recall paste --show <id>
|
|
|
1325
1408
|
AND m.content_text IS NOT NULL
|
|
1326
1409
|
AND length(m.content_text) > ?
|
|
1327
1410
|
AND me.message_uuid IS NULL
|
|
1328
|
-
${l}`).get(mt,...u);m.n>0&&console.log(` ${m.n} session(s) still have unembedded eligible messages. Re-run to continue.`)}
|
|
1411
|
+
${l}`).get(mt,...u);m.n>0&&console.log(` ${m.n} session(s) still have unembedded eligible messages. Re-run to continue.`)}k();jt();Ye();import{createHash as WT}from"node:crypto";k();D();import{writeFileSync as PT,readFileSync as c1,existsSync as FT,mkdirSync as jT,readdirSync as l1}from"node:fs";import{join as ep}from"node:path";var ki=ep(C,"output-index");function UT(){j(),FT(ki)||jT(ki,{recursive:!0})}function vn(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function BT(e){if(!e)return null;try{return JSON.parse(e)}catch{return e}}function tp(e){return{session_id:e.session_id,files_written:vn(e.files_written),brands_mentioned:vn(e.brands_mentioned),terms_introduced:vn(e.terms_introduced),plan_ids_referenced:vn(e.plan_ids_referenced),bug_signatures:vn(e.bug_signatures),raw_extraction:BT(e.raw_extraction),extracted_at:e.extracted_at,extractor_version:e.extractor_version}}function np(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
|
|
1329
1412
|
(session_id, files_written, brands_mentioned, terms_introduced,
|
|
1330
1413
|
plan_ids_referenced, bug_signatures, raw_extraction,
|
|
1331
1414
|
extracted_at, extractor_version)
|
|
@@ -1338,7 +1421,7 @@ show full content: recall paste --show <id>
|
|
|
1338
1421
|
bug_signatures = excluded.bug_signatures,
|
|
1339
1422
|
raw_extraction = excluded.raw_extraction,
|
|
1340
1423
|
extracted_at = excluded.extracted_at,
|
|
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=
|
|
1424
|
+
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=tp(u);return HT(e.session_id),m}function In(e){let n=E().prepare("SELECT * FROM session_output_index WHERE session_id = ?").get(e);return n?tp(n):null}function HT(e){try{UT();let t=In(e);if(!t)return;let n=ep(ki,`${e}.json`),s={schema:"claude-recall.session-output-index.v1",backed_up_at:new Date().toISOString(),...t};PT(n,JSON.stringify(s,null,2))}catch(t){console.error("[output-index] backup failed:",t)}}var Mn=1,Li="claude-haiku-4-5-20251001",XT=3,GT=32e3,sp=2e3,JT=30,zT=30,YT=30,qT=30;function VT(e){let n=E().prepare(`SELECT s.id,
|
|
1342
1425
|
NULLIF(sa.alias, '') AS alias,
|
|
1343
1426
|
s.auto_title,
|
|
1344
1427
|
s.auto_title_source,
|
|
@@ -1349,15 +1432,15 @@ show full content: recall paste --show <id>
|
|
|
1349
1432
|
FROM sessions s
|
|
1350
1433
|
LEFT JOIN session_aliases sa ON sa.session_id = s.id
|
|
1351
1434
|
LEFT JOIN projects p ON p.id = s.project_id
|
|
1352
|
-
WHERE s.id = ?`).get(e);return n?{...n,alias_source:n.alias?"manual":null}:null}function
|
|
1435
|
+
WHERE s.id = ?`).get(e);return n?{...n,alias_source:n.alias?"manual":null}:null}function rp(e,t={}){if(e.message_count<XT)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=In(e.id);if(s&&s.extractor_version>=Mn)return{eligible:!1,reason:"already-extracted"}}return{eligible:!0}}function KT(e){let t=VT(e);if(!t)return null;let s=E().prepare(`SELECT role, content_text
|
|
1353
1436
|
FROM messages
|
|
1354
1437
|
WHERE session_id = ?
|
|
1355
1438
|
AND is_sidechain = 0
|
|
1356
1439
|
AND content_text IS NOT NULL
|
|
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>
|
|
1440
|
+
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>sp?d.slice(0,sp)+"\u2026":d,u=`${a}: ${l}`;if(o+u.length>GT)break;r.push(u),o+=u.length}return{meta:t,excerpt:r.join(`
|
|
1358
1441
|
|
|
1359
|
-
`)}}function
|
|
1360
|
-
`)}function
|
|
1442
|
+
`)}}function QT(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(`
|
|
1443
|
+
`)}function ZT(e){return Array.isArray(e)&&e.every(t=>typeof t=="string")}function xi(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 eR(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 tR(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=WT("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 nR(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=xi(o.files_written,200),a=xi(o.brands_mentioned,zT),d=eR(o.terms_introduced,JT),l=xi(o.plan_ids_referenced,YT),u=tR(o.bug_signatures,qT);return i.length===0&&a.length===0&&d.length===0&&l.length===0&&u.length===0&&!ZT(o.files_written)?null:{files_written:i,brands_mentioned:a,terms_introduced:d,plan_ids_referenced:l,bug_signatures:u}}var Ci=null;async function sR(e,t){return Ci?Ci(e,t):Nt(e,[],{model:t})}async function rR(e,t={}){if(t.signal?.aborted)return{session_id:e,ok:!1,failed:"aborted"};let n=KT(e);if(!n)return{session_id:e,ok:!1,skipped:"session-not-found"};let s=rp(n.meta,{force:t.force});if(!s.eligible)return{session_id:e,ok:!1,skipped:s.reason};if(!Ci&&!ae())return{session_id:e,ok:!1,failed:"claude-cli-missing"};let r=QT(n),o=t.model??Li,i=await sR(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=nR(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=oR(i.stdout),l=np({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:Mn});return{session_id:e,ok:!0,index:l,usage:d}}function oR(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 iR(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,
|
|
1361
1444
|
NULLIF(sa.alias, '') AS alias,
|
|
1362
1445
|
s.auto_title,
|
|
1363
1446
|
s.auto_title_source,
|
|
@@ -1369,31 +1452,36 @@ show full content: recall paste --show <id>
|
|
|
1369
1452
|
LEFT JOIN session_aliases sa ON sa.session_id = s.id
|
|
1370
1453
|
LEFT JOIN projects p ON p.id = s.project_id
|
|
1371
1454
|
${o}
|
|
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=
|
|
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:
|
|
1455
|
+
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=rp(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 op(e={}){let t=iR({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 rR(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 ip(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(!ae()){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??Li;e.json||console.log(`Extracting Output Index \u2014 project "${t.name}" \u2014 extractor v${Mn} \u2014 model ${s} \u2014 up to ${n} sessions\u2026`);let r=Date.now(),o=-1,{progress:i,results:a}=await op({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(`
|
|
1456
|
+
`);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:Mn,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).")}k();Dn();var or={citation:"same-project",similar:"same-project",skill_track:"same-project",bug_pattern:"cross-project",wiki_link:"cross-project",temporal_proximity:"same-project"},ER=2,bR=.25,SR=5,yR=60,wR=25;function rr(e){return e.trim().toLowerCase()}function TR(e){let t=new Set;for(let n of e.files_written)t.add(`file:${rr(n)}`);for(let n of e.brands_mentioned)t.add(`brand:${rr(n)}`);for(let n of e.terms_introduced)t.add(`term:${rr(n)}`);for(let n of e.plan_ids_referenced)t.add(`plan:${rr(n)}`);return t}function RR(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/yR);return Math.max(.2,t)}function kR(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 xR(e){let t=In(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 CR(e){return E().prepare(`SELECT s.id AS id, s.project_id AS project_id, s.started_at AS started_at,
|
|
1457
|
+
s.rowid AS rowid,
|
|
1458
|
+
(SELECT COALESCE(MAX(m.rowid), 0) FROM messages m WHERE m.session_id = s.id)
|
|
1459
|
+
AS maxMessageRowid
|
|
1374
1460
|
FROM sessions s
|
|
1375
1461
|
JOIN session_output_index oi ON oi.session_id = s.id
|
|
1376
1462
|
WHERE s.project_id = ?
|
|
1377
|
-
ORDER BY COALESCE(s.started_at, ''), s.id`).all(e)}function
|
|
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
|
|
1379
|
-
s.project_id
|
|
1380
|
-
FROM messages m
|
|
1381
|
-
JOIN sessions s ON s.id = m.session_id
|
|
1382
|
-
WHERE s.project_id = ?
|
|
1383
|
-
AND m.is_sidechain = 0
|
|
1384
|
-
AND m.content_text IS NOT NULL
|
|
1385
|
-
AND length(m.content_text) > 0`).all(e):t.prepare(`SELECT m.uuid AS message_uuid, m.session_id, m.content_text,
|
|
1463
|
+
ORDER BY COALESCE(s.started_at, ''), s.id`).all(e)}function LR(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=TR(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 AR(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<ER)continue;let l=t.startedAt.get(i)??null,u=kR(s,l),m=RR(u),p=Math.min(1,d/SR*m);if(p<bR)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,wR)}async function gp(e){if(or.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project \u2014 refusing to run inference");let t=CR(e.projectId),n=new Map;for(let a of t){let d=xR(a.id);d&&n.set(a.id,d)}let s=LR(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 a of t){if(e.signal?.aborted)break;if(typeof e.sinceRowid=="number"&&a.maxMessageRowid<=e.sinceRowid)continue;r.current_session_id=a.id,e.onProgress?.({...r});let d=AR(a.id,s);for(let l of d)o.push({source_session_id:a.id,target_session_id:l.target_session_id,confidence:l.confidence,matched_terms:l.matched_terms,overlap:l.overlap,recency:l.recency,days_apart:l.days_apart});r.processed_sessions+=1,e.onProgress?.({...r})}let i=[];return o.length>0&&(E().transaction(()=>{for(let l of o)try{let u=gt({source_session_id:l.source_session_id,target_session_id:l.target_session_id,link_type:"citation",confidence:l.confidence,evidence:{matched_terms:l.matched_terms,overlap_count:l.overlap,recency:l.recency,days_apart:l.days_apart},inferred_by:"L2"},{deferMirror:!0});i.push(u.id),r.suggestions_created+=1}catch(u){console.error("[citation-inference] createSuggestion failed:",u)}})(),ft()),r.current_session_id=null,e.onProgress?.({...r}),{progress:r,suggestion_ids:i}}async function fp(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 gp({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(`
|
|
1464
|
+
`);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 WR,readFileSync as XR}from"node:fs";import{join as GR}from"node:path";k();Dn();var NR=/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/gi,OR=[/\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],vR=.95,IR=.85,MR=.7,Mi=50,DR=50;function $R(e){if(!e)return[];let t=new Set,n=[],s=e.match(NR);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>=Mi))break}return n}function PR(e){if(!e)return[];let t=new Set,n=[];for(let s of OR){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>=Mi))break}if(n.length>=Mi)break}}return n}function Di(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 _p(e,t){let n=E(),s=typeof t=="number";if(typeof e=="number"){let o=`SELECT m.uuid AS message_uuid, m.session_id, m.content_text,
|
|
1386
1465
|
s.project_id
|
|
1387
1466
|
FROM messages m
|
|
1388
1467
|
JOIN sessions s ON s.id = m.session_id
|
|
1389
|
-
WHERE
|
|
1468
|
+
WHERE s.project_id = ?
|
|
1469
|
+
AND m.is_sidechain = 0
|
|
1390
1470
|
AND m.content_text IS NOT NULL
|
|
1391
|
-
AND length(m.content_text) > 0
|
|
1471
|
+
AND length(m.content_text) > 0`+(s?`
|
|
1472
|
+
AND m.rowid > ?`:"");return s?n.prepare(o).all(e,t):n.prepare(o).all(e)}let r=`SELECT m.uuid AS message_uuid, m.session_id, m.content_text,
|
|
1473
|
+
s.project_id
|
|
1474
|
+
FROM messages m
|
|
1475
|
+
JOIN sessions s ON s.id = m.session_id
|
|
1476
|
+
WHERE m.is_sidechain = 0
|
|
1477
|
+
AND m.content_text IS NOT NULL
|
|
1478
|
+
AND length(m.content_text) > 0`+(s?`
|
|
1479
|
+
AND m.rowid > ?`:"");return s?n.prepare(r).all(t):n.prepare(r).all()}function FR(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,
|
|
1392
1480
|
s.project_id AS project_id,
|
|
1393
1481
|
oi.plan_ids_referenced AS plan_ids_json
|
|
1394
1482
|
FROM session_output_index oi
|
|
1395
1483
|
JOIN sessions s ON s.id = oi.session_id
|
|
1396
|
-
${n}`).all(...s)}function
|
|
1484
|
+
${n}`).all(...s)}function jR(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(n=>typeof n=="string")}catch{}return[]}function UR(e={}){if(or.citation!=="same-project")throw new Error("citation policy unexpectedly not same-project");let t=Di(e.projectId),n=new Set(t.map(a=>a.id)),s=new Map;for(let a of t)s.set(a.id,a.project_id);let r=E(),o=0;return r.transaction(()=>{for(let a of _p(e.projectId,e.sinceRowid)){if(e.signal?.aborted)break;let d=$R(a.content_text);if(d.length===0)continue;let l=s.get(a.session_id);if(l!==void 0)for(let u of d){if(u===a.session_id)continue;let m=s.get(u);if(!(m===void 0&&!n.has(u))&&!(m!==void 0&&l!==void 0&&m!==l))try{gt({source_session_id:a.session_id,target_session_id:u,link_type:"citation",confidence:vR,evidence:{matched_uuid:u,source_message_uuid:a.message_uuid,scanner:"uuid-ref"},inferred_by:"L1"},{deferMirror:!0}),o+=1}catch{}}}})(),o>0&&ft(),{created:o}}function BR(e={}){let t=FR(e.projectId);if(t.length===0)return{created:0};let n=new Map;for(let l of t){let u=jR(l.plan_ids_json);for(let m of u){let p=m.trim().toLowerCase();if(!p)continue;let g=n.get(p);g?g.push({id:l.session_id,project_id:l.project_id}):n.set(p,[{id:l.session_id,project_id:l.project_id}])}}if(n.size===0)return{created:0};let s=Di(e.projectId),r=new Map;for(let l of s)r.set(l.id,l.project_id);let o=0,i=new Map;for(let l of _p(e.projectId,e.sinceRowid)){if(e.signal?.aborted)break;let u=PR(l.content_text);if(u.length===0)continue;let m=r.get(l.session_id);if(m!==void 0)for(let p of u){let g=n.get(p);if(g)for(let f of g){if(f.id===l.session_id||f.project_id!==m)continue;let h=i.get(l.session_id);h||(h=new Map,i.set(l.session_id,h));let _=h.get(f.id);_||(_=new Set,h.set(f.id,_)),_.add(p)}}}return E().transaction(()=>{for(let[l,u]of i)for(let[m,p]of u)try{gt({source_session_id:l,target_session_id:m,link_type:"citation",confidence:IR,evidence:{matched_plan_ids:Array.from(p).slice(0,12),scanner:"plan-ref"},inferred_by:"L1"},{deferMirror:!0}),o+=1}catch{}})(),o>0&&ft(),{created:o}}function HR(e){if(or.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=Di(e.projectId),n=new Map,s=new Map;if(t.length>0){let d=E(),l=t.map(p=>p.id),u=l.map(()=>"?").join(","),m=d.prepare(`SELECT id, started_at FROM sessions WHERE id IN (${u})`).all(...l);for(let p of m)s.set(p.id,p.started_at)}for(let d of t)n.set(d.id,d.project_id);let r=new Map;for(let d of e.registry.terminals){let l=e.registry.sessions_by_pid[String(d.shell_pid)]??[];if(l.length===0)continue;let u=`${d.cwd??"<no-cwd>"}::${d.tab_name}`,m=r.get(u);m||(m=new Set,r.set(u,m));for(let p of l)m.add(p)}let o=E(),i=0;return o.transaction(()=>{for(let[,d]of r){if(d.size<2)continue;let l=new Map;for(let u of d){let m=n.get(u);if(m===void 0||typeof e.projectId=="number"&&m!==e.projectId)continue;let p=l.get(m);p||(p=[],l.set(m,p)),p.push(u)}for(let[,u]of l){if(u.length<2)continue;u.sort((p,g)=>{let f=s.get(p)??"",h=s.get(g)??"";return f<h?-1:f>h?1:p<g?-1:1});let m=0;for(let p=1;p<u.length&&!e.signal?.aborted;p++)for(let g=0;g<p&&!(m>=DR);g++)try{gt({source_session_id:u[p],target_session_id:u[g],link_type:"skill_track",confidence:MR,evidence:{shared_tab_signature:!0,bucket_size:u.length,scanner:"tab-carry"},inferred_by:"L1"},{deferMirror:!0}),i+=1,m+=1}catch{}}}})(),i>0&&ft(),{created:i}}function hp(e={}){let t=UR(e);if(e.signal?.aborted)return{uuid_refs_created:t.created,plan_refs_created:0,tab_carry_created:0,total:t.created};let n=BR(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?HR({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 JR(){let e=GR(C,"terminals.json");if(WR(e))try{let t=XR(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 Ep(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=JR(),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=hp({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)."))}k();Ye();Dn();function $i(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 Fi="claude-haiku-4-5-20251001",ir=.4,ji=.95,bp=30,Sp=240,zR=new Set(["CITATION","SIMILAR","SKILL_TRACK","UNRELATED"]),YR={CITATION:"citation",SIMILAR:"similar",SKILL_TRACK:"skill_track"};function Tp(e){return E().prepare(`SELECT s.id,
|
|
1397
1485
|
s.source_session_id,
|
|
1398
1486
|
s.target_session_id,
|
|
1399
1487
|
s.link_type,
|
|
@@ -1403,7 +1491,7 @@ show full content: recall paste --show <id>
|
|
|
1403
1491
|
FROM session_link_suggestions s
|
|
1404
1492
|
JOIN sessions src ON src.id = s.source_session_id
|
|
1405
1493
|
WHERE s.status = 'pending'
|
|
1406
|
-
AND src.project_id = ?`).all(e)}function
|
|
1494
|
+
AND src.project_id = ?`).all(e)}function qR(e){if(e.length===0)return new Map;let t=E(),n=e.map(()=>"?").join(","),s=t.prepare(`SELECT s.id,
|
|
1407
1495
|
NULLIF(sa.alias, '') AS alias,
|
|
1408
1496
|
s.auto_title,
|
|
1409
1497
|
s.first_user_message,
|
|
@@ -1412,18 +1500,18 @@ show full content: recall paste --show <id>
|
|
|
1412
1500
|
FROM sessions s
|
|
1413
1501
|
LEFT JOIN session_aliases sa ON sa.session_id = s.id
|
|
1414
1502
|
LEFT JOIN projects p ON p.id = s.project_id
|
|
1415
|
-
WHERE s.id IN (${n})`).all(...e),r=new Map;for(let o of s)r.set(o.id,o);return r}function
|
|
1416
|
-
`)}var
|
|
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.`)}}
|
|
1503
|
+
WHERE s.id IN (${n})`).all(...e),r=new Map;for(let o of s)r.set(o.id,o);return r}function yp(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 wp(e){try{return JSON.parse(e)}catch{return e}}function VR(e){let t=e.minConfidence??ir,n=Math.max(1,Math.min(500,e.limit??100)),s=Tp(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=wp(l.evidence))):m.layers.push({inferred_by:l.inferred_by,link_type:l.link_type,confidence:l.confidence,evidence:wp(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=qR(Array.from(i)),d=[];for(let l of o.values()){let u=$i(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:yp(m,l.source_session_id),target_title:yp(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 KR(e){let t;try{t=JSON.stringify(e)}catch{t=String(e)}return t.length>Sp&&(t=t.slice(0,Sp)+"\u2026"),t}function QR(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=${KR(r.evidence)}`)}),t.join(`
|
|
1504
|
+
`)}var Pi=null;async function ZR(e,t){return Pi?Pi(e,t):Nt(e,[],{model:t})}function ek(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 tk(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(!zR.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 Rp(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(!Pi&&!ae())return t.failures.push({batch_index:-1,error:"claude CLI not available on PATH"}),t;let n=VR({projectId:e.projectId,minConfidence:e.minConfidence??ir,limit:e.limit??100});t.candidates_after_filter=n.length;let s=Tp(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??Fi,i=e.autoPromoteThreshold??ji;for(let a=0,d=0;a<n.length&&!e.signal?.aborted;a+=bp,d+=1){let l=n.slice(a,a+bp);e.onBatchStart?.({batch:d,pairs:l.length});let u=QR(l),m=await ZR(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=tk(m.stdout);if(!p){t.failures.push({batch_index:d,error:"parse-failed"});continue}let g=ek(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 _=YR[f.label];if(_)try{let b=gt({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(T=>T.confidence);if(S.push(f.confidence),$i(S)>=i)try{mp(b.id,"approved",{source:"auto"}),t.links_promoted+=1}catch{}}}catch{}}}return t}Ye();var nk=1e3;function sk(e){let t=e.minConf?Math.max(0,Math.min(1,Number(e.minConf))):ir,n=e.autoPromoteThreshold!==void 0?Math.max(0,Math.min(1,Number(e.autoPromoteThreshold))):ji,s=e.limit?Math.max(1,Number(e.limit)):nk,r=e.model??Fi,o=!!e.autoPromote||e.autoPromoteThreshold!==void 0;return{minConfidence:t,autoPromoteThreshold:n,limit:s,model:r,autoPromote:o}}async function kp(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(!ae()){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}=sk(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 Rp({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(`
|
|
1505
|
+
`);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.`)}}k();import{createHash as uk}from"node:crypto";k();D();import{writeFileSync as rk,readFileSync as oj,existsSync as ok,mkdirSync as ik,readdirSync as ij,unlinkSync as aj}from"node:fs";import{join as xp}from"node:path";import{randomUUID as ak}from"node:crypto";var Ui=xp(C,"bug-patterns");function ck(){j(),ok(Ui)||ik(Ui,{recursive:!0})}function ar(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 lk(e){return{cluster_id:e.cluster_id,session_id:e.session_id,matched_at:e.matched_at}}function Cp(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??ak(),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
|
|
1418
1506
|
(id, signature_hash, example_message, occurrence_count,
|
|
1419
1507
|
first_seen_at, last_seen_at, resolved_in_session_id, fix_summary)
|
|
1420
1508
|
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)
|
|
1421
1509
|
VALUES (?, ?, ?)
|
|
1422
|
-
ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let l of i)d.run(s,l,n)})();let a=
|
|
1510
|
+
ON CONFLICT(cluster_id, session_id) DO NOTHING`);for(let l of i)d.run(s,l,n)})();let a=Lp(s);if(!a)throw new Error("createCluster succeeded but read-back failed");return Op(s),a}function Lp(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:ar(n),members:s.map(lk)}}function Ap(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)
|
|
1423
1511
|
VALUES (?, ?, ?)
|
|
1424
1512
|
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
|
|
1425
1513
|
SET occurrence_count = ?, last_seen_at = ?
|
|
1426
|
-
WHERE id = ?`).run(d,r,e)}})(),o>0&&
|
|
1514
|
+
WHERE id = ?`).run(d,r,e)}})(),o>0&&Op(e);let i=n.prepare("SELECT * FROM bug_pattern_clusters WHERE id = ?").get(e);return{cluster:ar(i),added:o}}function dk(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 Np(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,
|
|
1427
1515
|
NULLIF(sa.alias, '') AS alias,
|
|
1428
1516
|
s.auto_title,
|
|
1429
1517
|
s.first_user_message,
|
|
@@ -1434,37 +1522,37 @@ show full content: recall paste --show <id>
|
|
|
1434
1522
|
LEFT JOIN session_aliases sa ON sa.session_id = m.session_id
|
|
1435
1523
|
LEFT JOIN projects p ON p.id = s.project_id
|
|
1436
1524
|
WHERE m.cluster_id = ?
|
|
1437
|
-
ORDER BY COALESCE(s.started_at, ''), m.matched_at, m.session_id`).all(e);return{cluster:
|
|
1525
|
+
ORDER BY COALESCE(s.started_at, ''), m.matched_at, m.session_id`).all(e);return{cluster:ar(n),members:s.map(dk)}}function Op(e){try{ck();let t=Lp(e);if(!t)return;let n=xp(Ui,`${e}.json`),s={schema:"claude-recall.bug-pattern-cluster.v1",cluster:t.cluster,members:t.members,backed_up_at:new Date().toISOString()};rk(n,JSON.stringify(s,null,2))}catch(t){console.error("[bug-patterns] backup failed:",t)}}function vp(e){return e?E().prepare(`SELECT * FROM bug_pattern_clusters
|
|
1438
1526
|
WHERE signature_hash = ?
|
|
1439
|
-
ORDER BY last_seen_at DESC, id ASC`).all(e).map(
|
|
1527
|
+
ORDER BY last_seen_at DESC, id ASC`).all(e).map(ar):[]}function Ip(e){if(!e)return new Set;let n=E().prepare(`SELECT DISTINCT m.session_id
|
|
1440
1528
|
FROM bug_pattern_members m
|
|
1441
1529
|
JOIN bug_pattern_clusters c ON c.id = m.cluster_id
|
|
1442
|
-
WHERE c.signature_hash = ?`).all(e);return new Set(n.map(s=>s.session_id))}var
|
|
1530
|
+
WHERE c.signature_hash = ?`).all(e);return new Set(n.map(s=>s.session_id))}var pk=/\b0x[0-9a-fA-F]+\b/g,mk=/\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,gk=/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?\b/g,fk=/:\d+:\d+/g,_k=/\bline\s+\d+\b/gi,hk=/\bcolumn\s+\d+\b/gi,Ek=/\b(?:pid|PID|process(?:\s+id)?)\s*[:=]?\s*\d+\b/gi,bk=/\b(?:port|:)\s*[:=]?\s*\d{2,5}\b/gi,Sk=/\b\d{4,}\b/g,yk=/(['"`])[^'"`\n]{1,128}\1/g;function wk(e){if(!e)return"";let t=String(e);return t=t.replace(pk,"<hex>"),t=t.replace(mk,"<uuid>"),t=t.replace(gk,"<ts>"),t=t.replace(fk,":<line>:<col>"),t=t.replace(_k,"line <n>"),t=t.replace(hk,"column <n>"),t=t.replace(Ek,"pid <n>"),t=t.replace(bk,"port <n>"),t=t.replace(Sk,"<num>"),t=t.replace(yk,"<arg>"),t=t.replace(/\s+/g," ").trim(),t.toLowerCase()}function Tk(e){let t=(e.error_type??"unknown").toLowerCase().trim(),n=wk(e.snippet??e.message_hash??""),s=`${t}|${n}`;return uk("sha256").update(s).digest("hex").slice(0,16)}function Rk(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,
|
|
1443
1531
|
p.name AS project,
|
|
1444
1532
|
s.started_at AS started_at,
|
|
1445
1533
|
oi.bug_signatures AS bug_signatures
|
|
1446
1534
|
FROM session_output_index oi
|
|
1447
1535
|
LEFT JOIN sessions s ON s.id = oi.session_id
|
|
1448
1536
|
LEFT JOIN projects p ON p.id = s.project_id
|
|
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=
|
|
1450
|
-
`)[0];console.warn(`[bug-pattern] --semantic requested but the embedder is unavailable: ${
|
|
1451
|
-
Falling back to exact-match-only clustering. Run \`recall semantic install\` to enable the semantic pass.`),l=!0}if(S){let
|
|
1452
|
-
WHERE id IN (${
|
|
1537
|
+
${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=Tk(l);i.push({session_id:a.session_id,project:a.project,started_at:a.started_at,signature:l,fingerprint:m})}}return i}function kk(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 xk(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 Ck(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-xk(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 Lk(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=Ck({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??"",y=b.started_at??"";return S<y?-1:S>y?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 Mp(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=vp(s.fingerprint),o=Ip(s.fingerprint),i=s.members.map(l=>l.session_id).filter(l=>!o.has(l));if(r.length===0){let l=Cp({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=Ap(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 Dp(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=Rk(e.project),i=new Set(o.map(S=>S.session_id)),a=kk(o),d=[],l=!1;if(e.semantic){let S=e.embedder??null;if(!S)try{S=await Ok()}catch(y){let R=(y instanceof Error?y.message:String(y)).split(`
|
|
1538
|
+
`)[0];console.warn(`[bug-pattern] --semantic requested but the embedder is unavailable: ${R}
|
|
1539
|
+
Falling back to exact-match-only clustering. Run \`recall semantic install\` to enable the semantic pass.`),l=!0}if(S){let y=[];for(let T of a)T.members.length===1&&y.push(T.members[0]);y.length>=2&&(d=await Lk(y,S,s,r))}}let u=a.filter(S=>S.members.length>=t),m=d.filter(S=>S.members.length>=t),p=Mp(u,t),g=Mp(m,t),f=[...p.cluster_ids,...g.cluster_ids],h=Array.from(new Set(f)),_=[];if(h.length>0){let S=E(),y=h.map(()=>"?").join(","),T=S.prepare(`SELECT * FROM bug_pattern_clusters
|
|
1540
|
+
WHERE id IN (${y})
|
|
1453
1541
|
ORDER BY occurrence_count DESC, last_seen_at DESC
|
|
1454
|
-
LIMIT ?`).all(...h,n);for(let
|
|
1542
|
+
LIMIT ?`).all(...h,n);for(let R of T)_.push({id:R.id,signature_hash:R.signature_hash,example_message:R.example_message,occurrence_count:R.occurrence_count,first_seen_at:R.first_seen_at,last_seen_at:R.last_seen_at,resolved_in_session_id:R.resolved_in_session_id,fix_summary:R.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 Ak=async()=>{let{embed:e,loadEmbedder:t,getEmbedderStatus:n}=await Promise.resolve().then(()=>(Pe(),Ms));return n().loaded||await t(),e},Nk=Ak;async function Ok(){return Nk()}function vk(e,t){let n=Np(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 $p(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 Dp(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=vk(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
1543
|
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(`
|
|
1456
1544
|
[${u}] occurs=${l.occurrence_count} hash=${l.signature_hash.slice(0,8)}
|
|
1457
1545
|
${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.")}
|
|
1546
|
+
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.")}k();import{z as kj}from"zod";k();function Pp(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}Bi();var Jp=new Set(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"]),zp=new Set(["pagerank","embedding-rerank","hybrid"]);function zk(e){if(!e)return;let t=e.split(",").map(n=>n.trim()).filter(Boolean);for(let n of t)if(!Jp.has(n))throw new Error(`invalid --edge-types value: '${n}'. Valid: ${Array.from(Jp).join(", ")}`);return t}function Yk(e){if(!e)return"hybrid";if(!zp.has(e))throw new Error(`invalid --scoring value: '${e}'. Valid: ${Array.from(zp).join(", ")}`);return e}async function Yp(e,t){let n=Pp(e);if(!n){console.error(`session not found or prefix ambiguous: ${e}`),process.exitCode=1;return}let s,r;try{s=Yk(t.scoring),r=zk(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=dr(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)+`
|
|
1459
1547
|
`);return}process.stdout.write(a.bundle),a.truncated.length>0&&console.error(`
|
|
1460
|
-
[truncated ${a.truncated.length} refs to fit ${o}-token budget; ${a.budgetUsed} used / ${a.budgetRemaining} remaining]`)}
|
|
1548
|
+
[truncated ${a.truncated.length} refs to fit ${o}-token budget; ${a.budgetUsed} used / ${a.budgetRemaining} remaining]`)}k();$();var qk=[[/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}]],qp={label:"unknown",inputCentsPerMtok:300,outputCentsPerMtok:1500,cacheCreateCentsPerMtok:375,cacheReadCentsPerMtok:30};function _t(e){if(!e)return qp;for(let[t,n]of qk)if(t.test(e))return n;return qp}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=_t(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=_t(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 le(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 he(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`}k();k();Kn();Qn();var Vk=500,ur=new Set;function Vp(){return ur.size}function Kk(){return`
|
|
1461
1549
|
SELECT m.uuid, m.session_id, m.timestamp, m.raw_json
|
|
1462
1550
|
FROM messages m
|
|
1463
1551
|
LEFT JOIN message_usage mu ON mu.message_uuid = m.uuid
|
|
1464
1552
|
WHERE m.role = 'assistant' AND mu.message_uuid IS NULL
|
|
1465
1553
|
AND m.uuid NOT IN (SELECT value FROM json_each(?))
|
|
1466
1554
|
LIMIT ?
|
|
1467
|
-
`}function
|
|
1555
|
+
`}function Qk(e,t){let n=t.limit??Number.MAX_SAFE_INTEGER,s=Math.max(1,t.chunkSize??Vk),r=e.prepare(Kk()),o=e.prepare(`
|
|
1468
1556
|
INSERT INTO message_usage (
|
|
1469
1557
|
message_uuid, session_id, model,
|
|
1470
1558
|
input_tokens, output_tokens, cache_create_tokens, cache_read_tokens,
|
|
@@ -1474,7 +1562,7 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
|
|
|
1474
1562
|
@input, @output, @cc, @cr, @ts
|
|
1475
1563
|
)
|
|
1476
1564
|
ON CONFLICT(message_uuid) DO NOTHING
|
|
1477
|
-
`),i=0,a=0,d=new Set;for(;i<n;){let l=Math.min(s,n-i),u=JSON.stringify([...
|
|
1565
|
+
`),i=0,a=0,d=new Set;for(;i<n;){let l=Math.min(s,n-i),u=JSON.stringify([...ur]),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{ur.add(f.uuid);continue}let _=Yr(h.message);if(!_){ur.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)ln(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 Kp(e={}){return Qk(E(),e)}function Hi(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:_t(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 Wi(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 Qp(e){let t=E(),n=t.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
|
|
1478
1566
|
s.message_count,
|
|
1479
1567
|
s.total_input_tokens, s.total_output_tokens,
|
|
1480
1568
|
s.total_cache_create_tokens, s.total_cache_read_tokens,
|
|
@@ -1489,7 +1577,7 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
|
|
|
1489
1577
|
COUNT(*) AS n
|
|
1490
1578
|
FROM message_usage
|
|
1491
1579
|
WHERE session_id = ?
|
|
1492
|
-
GROUP BY model`).all(e),r=
|
|
1580
|
+
GROUP BY model`).all(e),r=Hi(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,...Wi(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:_t(n.primary_model).label,inputTokens:o,outputTokens:i,cacheCreateTokens:a,cacheReadTokens:d,totalTokens:l.totalTokens,cost:l,byModel:r,display:{dollars:le(l.cents),tokens:he(l.totalTokens),model:_t(n.primary_model).label}}}function Zp(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,
|
|
1493
1581
|
COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
|
|
1494
1582
|
COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
|
|
1495
1583
|
COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
|
|
@@ -1498,12 +1586,12 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
|
|
|
1498
1586
|
FROM message_usage mu
|
|
1499
1587
|
JOIN sessions s ON s.id = mu.session_id
|
|
1500
1588
|
WHERE s.project_id = ?
|
|
1501
|
-
GROUP BY mu.model`).all(n.id),r=
|
|
1589
|
+
GROUP BY mu.model`).all(n.id),r=Hi(s),o=t.prepare(`SELECT COALESCE(SUM(total_input_tokens), 0) AS input_tokens,
|
|
1502
1590
|
COALESCE(SUM(total_output_tokens), 0) AS output_tokens,
|
|
1503
1591
|
COALESCE(SUM(total_cache_create_tokens), 0) AS cache_create_tokens,
|
|
1504
1592
|
COALESCE(SUM(total_cache_read_tokens), 0) AS cache_read_tokens,
|
|
1505
1593
|
COUNT(*) AS session_count
|
|
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,...
|
|
1594
|
+
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,...Wi(r)},null),d=t.prepare(`SELECT s.id, sa.alias, s.started_at,
|
|
1507
1595
|
s.total_input_tokens, s.total_output_tokens,
|
|
1508
1596
|
s.total_cache_create_tokens, s.total_cache_read_tokens,
|
|
1509
1597
|
s.primary_model
|
|
@@ -1514,7 +1602,7 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
|
|
|
1514
1602
|
+ COALESCE(s.total_output_tokens,0)
|
|
1515
1603
|
+ COALESCE(s.total_cache_create_tokens,0)
|
|
1516
1604
|
+ COALESCE(s.total_cache_read_tokens,0)) DESC
|
|
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:
|
|
1605
|
+
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:le(i.cents),tokens:he(i.totalTokens)}}}function em(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=x=>n?t.prepare(x).get(o):t.prepare(x).get(),a=x=>n?t.prepare(x).all(o):t.prepare(x).all(),d=a(`SELECT mu.model,
|
|
1518
1606
|
COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
|
|
1519
1607
|
COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
|
|
1520
1608
|
COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
|
|
@@ -1523,7 +1611,7 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
|
|
|
1523
1611
|
FROM message_usage mu
|
|
1524
1612
|
JOIN sessions s ON s.id = mu.session_id
|
|
1525
1613
|
${s}
|
|
1526
|
-
GROUP BY mu.model`),l=
|
|
1614
|
+
GROUP BY mu.model`),l=Hi(d),u=0,m=0,p=0,g=0;for(let x of l)u+=x.inputTokens,m+=x.outputTokens,p+=x.cacheCreateTokens,g+=x.cacheReadTokens;let f=Fe({inputTokens:u,outputTokens:m,cacheCreateTokens:p,cacheReadTokens:g,...Wi(l)},null),h=n?i(`SELECT
|
|
1527
1615
|
(SELECT COUNT(DISTINCT m.session_id)
|
|
1528
1616
|
FROM messages m
|
|
1529
1617
|
JOIN sessions s2 ON s2.id = m.session_id
|
|
@@ -1547,7 +1635,7 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
|
|
|
1547
1635
|
JOIN sessions s ON s.id = mu.session_id
|
|
1548
1636
|
${s}
|
|
1549
1637
|
GROUP BY day, mu.model
|
|
1550
|
-
ORDER BY day ASC`),b=new Map;for(let
|
|
1638
|
+
ORDER BY day ASC`),b=new Map;for(let x of _){if(!x.day)continue;let I=Fe({inputTokens:x.input_tokens,outputTokens:x.output_tokens,cacheCreateTokens:x.cache_create_tokens,cacheReadTokens:x.cache_read_tokens},x.model),F=b.get(x.day)??{tokens:0,cents:0};F.tokens+=I.totalTokens,F.cents+=I.cents,b.set(x.day,F)}let S=[...b.entries()].map(([x,I])=>({day:x,tokens:I.tokens,cents:I.cents})).sort((x,I)=>x.day.localeCompare(I.day)),T=a(`SELECT s.id, p.name AS project, sa.alias, s.started_at,
|
|
1551
1639
|
COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
|
|
1552
1640
|
COALESCE(SUM(mu.output_tokens), 0) AS output_tokens,
|
|
1553
1641
|
COALESCE(SUM(mu.cache_create_tokens), 0) AS cache_create_tokens,
|
|
@@ -1563,7 +1651,7 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
|
|
|
1563
1651
|
+ COALESCE(SUM(mu.output_tokens),0)
|
|
1564
1652
|
+ COALESCE(SUM(mu.cache_create_tokens),0)
|
|
1565
1653
|
+ COALESCE(SUM(mu.cache_read_tokens),0)) DESC
|
|
1566
|
-
LIMIT 10`).map(
|
|
1654
|
+
LIMIT 10`).map(x=>{let I=Fe({inputTokens:x.input_tokens,outputTokens:x.output_tokens,cacheCreateTokens:x.cache_create_tokens,cacheReadTokens:x.cache_read_tokens},x.primary_model);return{sessionId:x.id,project:x.project,alias:x.alias,startedAt:x.started_at,totalTokens:I.totalTokens,cost:I}}),R=a(`SELECT p.id AS project_id,
|
|
1567
1655
|
p.name AS project,
|
|
1568
1656
|
mu.model,
|
|
1569
1657
|
COALESCE(SUM(mu.input_tokens), 0) AS input_tokens,
|
|
@@ -1575,96 +1663,35 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
|
|
|
1575
1663
|
JOIN sessions s ON s.id = mu.session_id
|
|
1576
1664
|
LEFT JOIN projects p ON p.id = s.project_id
|
|
1577
1665
|
${s}
|
|
1578
|
-
GROUP BY p.id, mu.model`),B=new Map;for(let
|
|
1666
|
+
GROUP BY p.id, mu.model`),B=new Map;for(let x of R){let I=x.project_id??"__none__",F=B.get(I);F||(F={project:x.project??"(no project)",sessionIds:new Set,sessionsApprox:0,byModel:{}},B.set(I,F)),x.sessions>F.sessionsApprox&&(F.sessionsApprox=x.sessions),F.byModel[x.model??"__unknown__"]={inputTokens:x.input_tokens,outputTokens:x.output_tokens,cacheCreateTokens:x.cache_create_tokens,cacheReadTokens:x.cache_read_tokens}}let L=[...B.values()].map(x=>{let I=0,F=0,ne=0,et=0;for(let xt of Object.values(x.byModel))I+=xt.inputTokens,F+=xt.outputTokens,ne+=xt.cacheCreateTokens,et+=xt.cacheReadTokens;let We=Fe({inputTokens:I,outputTokens:F,cacheCreateTokens:ne,cacheReadTokens:et,byModel:x.byModel},null);return{project:x.project,sessions:x.sessionsApprox,totalTokens:We.totalTokens,cost:We}});L.sort((x,I)=>I.totalTokens-x.totalTokens);let v=L.slice(0,20),O=t.prepare(`SELECT
|
|
1579
1667
|
(SELECT COUNT(*) FROM messages WHERE role='assistant') AS assistant_messages,
|
|
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:
|
|
1581
|
-
`)){
|
|
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
|
|
1583
|
-
(session_id, commit_sha, committed_at, subject, cwd_snapshot, correlated_at)
|
|
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
|
|
1585
|
-
WHERE cwd IS NOT NULL AND started_at IS NOT NULL AND ended_at IS NOT NULL
|
|
1586
|
-
ORDER BY COALESCE(ended_at, started_at) DESC
|
|
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,
|
|
1588
|
-
NULLIF(sa.alias, '') AS alias,
|
|
1589
|
-
p.name AS project,
|
|
1590
|
-
s.started_at AS startedAt,
|
|
1591
|
-
s.ended_at AS endedAt,
|
|
1592
|
-
sc.commit_sha AS commitSha,
|
|
1593
|
-
sc.committed_at AS committedAt,
|
|
1594
|
-
sc.subject AS subject
|
|
1595
|
-
FROM session_commits sc
|
|
1596
|
-
JOIN sessions s ON s.id = sc.session_id
|
|
1597
|
-
JOIN projects p ON p.id = s.project_id
|
|
1598
|
-
LEFT JOIN session_aliases sa ON sa.session_id = sc.session_id
|
|
1599
|
-
WHERE lower(sc.commit_sha) = lower(?)
|
|
1600
|
-
OR lower(sc.commit_sha) LIKE ?
|
|
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
|
|
1668
|
+
(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:T,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(Vp(),Math.max(0,O.assistant_messages-O.messages_with_usage))},display:{dollars:le(f.cents),tokens:he(f.totalTokens)}}}var at=e=>e.toLocaleString();function Zk(e){return E().prepare("SELECT id FROM sessions WHERE id = ? OR id LIKE ? LIMIT 1").get(e,`${e}%`)?.id??null}async function tm(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=Kp({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=Zk(e);if(!r){console.error(c.err(`no session matches '${e}'`)),process.exit(1);return}let o=Qp(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(le(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)} ${he(i.inputTokens+i.outputTokens+i.cacheCreateTokens+i.cacheReadTokens).padStart(10)} ${c.accent(le(i.cost.cents).padStart(10))}`)}console.log("");return}if(t.project){let r=Zp(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(he(r.totalTokens).padStart(12))}`),console.log(` estimated cost ${c.accent(le(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)} ${he(o.inputTokens+o.outputTokens+o.cacheCreateTokens+o.cacheReadTokens).padStart(10)} ${c.accent(le(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)} ${he(o.totalTokens).padStart(10)} ${c.accent(le(o.cost.cents).padStart(10))}`)}}console.log("");return}let n=t.days==="7"?"7d":t.days==="30"?"30d":"all",s=em(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(he(s.totalTokens).padStart(12))}`),console.log(` estimated cost ${c.accent(le(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)} ${he(r.inputTokens+r.outputTokens+r.cacheCreateTokens+r.cacheReadTokens).padStart(10)} ${c.accent(le(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)} ${he(r.totalTokens).padStart(10)} ${c.accent(le(r.cost.cents).padStart(10))}`)}}console.log("")}$();k();Nn();function ex(e){return E().prepare("SELECT id FROM sessions WHERE id = ? OR id LIKE ? LIMIT 1").get(e,`${e}%`)?.id??null}function tx(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 nm(e,t){if(e){let r=ex(e);if(!r){console.error(c.err(`no session matches '${e}'`)),process.exit(1);return}let o=await gi(r);if(t.json){console.log(JSON.stringify(o,null,2));return}tx(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 Kd({limit:n,onProgress:(r,o)=>{(r%25===0||r===o)&&process.stderr.write(`\r${c.dim(` ${r}/${o}`)}`)}});if(process.stderr.write(`
|
|
1669
|
+
`),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("")}$();k();At();mi();import{existsSync as Xi,readFileSync as Gi}from"node:fs";D();function sx(){let e=`${C}/daemon.port`;if(!Xi(e))return null;try{let t=Gi(e,"utf8").trim();return/^\d+$/.test(t)?t:null}catch{return null}}async function rx(e,t){try{return(await ut("POST",`http://127.0.0.1:${e}/api/sessions/${encodeURIComponent(t)}/relink`,{clear:!0})).ok}catch{return!1}}function ox(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
|
|
1603
1670
|
FROM sessions s
|
|
1604
1671
|
JOIN projects p ON p.id = s.project_id
|
|
1605
1672
|
JOIN session_aliases sa ON sa.session_id = s.id
|
|
1606
1673
|
WHERE s.cwd IS NOT NULL
|
|
1607
1674
|
AND s.started_at IS NOT NULL
|
|
1608
1675
|
AND sa.alias IS NOT NULL
|
|
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
|
|
1676
|
+
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 ix(e){let t=[];try{let n=`${C}/terminals.json`;if(!Xi(n))return t;let s=JSON.parse(Gi(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,
|
|
1610
1677
|
NULLIF(sa.alias, '') AS alias
|
|
1611
1678
|
FROM sessions s
|
|
1612
1679
|
JOIN projects p ON p.id = s.project_id
|
|
1613
1680
|
LEFT JOIN session_aliases sa ON sa.session_id = s.id
|
|
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
|
|
1681
|
+
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 ax(e){let t=[],n=`${C}/terminals.json`;if(!Xi(n))return t;let s;try{s=JSON.parse(Gi(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
|
|
1615
1682
|
FROM sessions s
|
|
1616
1683
|
JOIN projects p ON p.id = s.project_id
|
|
1617
1684
|
JOIN session_aliases sa ON sa.session_id = s.id
|
|
1618
1685
|
WHERE s.started_at IS NOT NULL
|
|
1619
1686
|
AND sa.alias IS NOT NULL
|
|
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
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
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,
|
|
1629
|
-
COALESCE(NULLIF(sa.alias, ''), s.auto_title, substr(s.first_user_message, 1, 60)) AS label,
|
|
1630
|
-
COUNT(*) AS count,
|
|
1631
|
-
MAX(CASE WHEN sa.alias IS NOT NULL AND sa.alias != '' THEN 1 ELSE 0 END) AS any_aliased
|
|
1632
|
-
FROM sessions s
|
|
1633
|
-
JOIN projects p ON p.id = s.project_id
|
|
1634
|
-
LEFT JOIN session_aliases sa ON sa.session_id = s.id
|
|
1635
|
-
WHERE s.indexed_at > datetime('now', '-7 days')
|
|
1636
|
-
AND COALESCE(NULLIF(sa.alias, ''), s.auto_title, s.first_user_message) IS NOT NULL
|
|
1637
|
-
GROUP BY p.name, label
|
|
1638
|
-
HAVING count >= 2
|
|
1639
|
-
ORDER BY count DESC, label ASC
|
|
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
|
|
1641
|
-
(SELECT COUNT(*) FROM projects) AS projects,
|
|
1642
|
-
(SELECT COUNT(*) FROM sessions) AS sessions,
|
|
1643
|
-
(SELECT COUNT(*) FROM messages) AS messages,
|
|
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
|
|
1655
|
-
FROM session_aliases sa
|
|
1656
|
-
LEFT JOIN sessions s ON s.id = sa.session_id
|
|
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(`
|
|
1687
|
+
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 cx(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 sm(e){let t=e.window?Math.max(5,Math.min(600,Number(e.window)||60)):60,n=e.project?.trim()||void 0,s=ox(t,n),r=ix(n),o=ax(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(cx(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=sx(),l=0,u=0;for(let m of a){if(d){await rx(d,m.session_id)?l++:(u++,console.error(c.err(`failed to clear ${m.session_id.slice(0,8)} via daemon`)));continue}try{Ll(m.session_id),pi.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."))}Ti();$();k();D();import{existsSync as lx,readFileSync as dx}from"node:fs";import{join as ux}from"node:path";function px(){let e=ux(C,"daemon.pid");if(!lx(e))return!1;try{let t=parseInt(dx(e,"utf-8").trim(),10);return!Number.isFinite(t)||t<=0?!1:(process.kill(t,0),!0)}catch{return!1}}async function $n(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 rm(e={}){let t=E(),n=[];if(e.vacuum&&px())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)+`
|
|
1688
|
+
`),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 $n("wal_checkpoint(TRUNCATE)",()=>{t.pragma("wal_checkpoint(TRUNCATE)")})),n.push(await $n("messages_fts optimize",()=>{t.exec("INSERT INTO messages_fts(messages_fts) VALUES('optimize');")})),n.push(await $n("sessions_fts optimize",()=>{t.exec("INSERT INTO sessions_fts(sessions_fts) VALUES('optimize');")})),n.push(await $n("PRAGMA optimize",()=>{t.exec("PRAGMA optimize")})),e.vacuum&&n.push(await $n("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)+`
|
|
1689
|
+
`),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)}$();k();D();dn();import{copyFileSync as mx,existsSync as gx,readFileSync as fx}from"node:fs";import{join as om}from"node:path";function _x(){let e=om(C,"daemon.pid");if(!gx(e))return!1;try{let t=parseInt(fx(e,"utf-8").trim(),10);return!Number.isFinite(t)||t<=0?!1:(process.kill(t,0),!0)}catch{return!1}}function hx(){let e=[];for(let t of Ut){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 im(e={}){if(_x()&&!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)+`
|
|
1690
|
+
`),2):(console.error(c.err(`\u2717 ${p}`)),2)}let t=hx();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})+`
|
|
1691
|
+
`),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:Ut.length,vacuumHint:!1,dryRun:!!e.dryRun},null,2)+`
|
|
1692
|
+
`),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:Ut.length,vacuumHint:!1,dryRun:!0};return e.json?(process.stdout.write(JSON.stringify(p,null,2)+`
|
|
1693
|
+
`),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=om(C,"db.sqlite"),a=new Date().toISOString().replace(/[-:T]/g,"").replace(/\..+$/,"").slice(0,15),d=`${i}.pre-phantom-purge-${a}`;mx(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:Ut.length,vacuumHint:s>100,dryRun:!1};return e.json?(process.stdout.write(JSON.stringify(m,null,2)+`
|
|
1694
|
+
`),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(Ut.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)}$();k();D();k();import{existsSync as Ex}from"node:fs";import{join as bx}from"node:path";var pr=bx(C,"archive.sqlite");var am=!1;function cm(){if(am&&Ex(pr))return;j();let e=E(),t=pr.replace(/'/g,"''");e.exec(`ATTACH DATABASE '${t}' AS archive`);try{e.exec(`
|
|
1668
1695
|
CREATE TABLE IF NOT EXISTS archive.messages_archive (
|
|
1669
1696
|
uuid TEXT PRIMARY KEY,
|
|
1670
1697
|
session_id TEXT NOT NULL,
|
|
@@ -1679,7 +1706,7 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
|
|
|
1679
1706
|
archived_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1680
1707
|
);
|
|
1681
1708
|
CREATE INDEX IF NOT EXISTS archive.idx_messages_archive_session ON messages_archive(session_id);
|
|
1682
|
-
`)}finally{e.exec("DETACH DATABASE archive")}
|
|
1709
|
+
`)}finally{e.exec("DETACH DATABASE archive")}am=!0}import{existsSync as lm,mkdirSync as Sx,readFileSync as yx,writeFileSync as wx}from"node:fs";import{homedir as Tx}from"node:os";import{join as dm}from"node:path";import{z as mr}from"zod";function um(){return process.env.RECALL_HOME??dm(Tx(),".recall")}function Rx(){let e=um();lm(e)||Sx(e,{recursive:!0})}function pm(){return dm(um(),"config.json")}var mm=mr.object({autoArchiveEnabled:mr.boolean().default(!1),autoArchiveAfterDays:mr.number().int().min(7).max(3650).default(90),lastRunAt:mr.string().nullable().default(null)}),gr={autoArchiveEnabled:!1,autoArchiveAfterDays:90,lastRunAt:null};function gm(){let e=pm();if(!lm(e))return{};try{return JSON.parse(yx(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 fm(){let e=gm().retention;if(!e)return{...gr};let t=mm.safeParse({...gr,...e});return t.success?t.data:{...gr}}function Ji(e){Rx();let t=gm(),n=mm.parse({...gr,...t.retention??{},...e}),s={...t,retention:n};return wx(pm(),JSON.stringify(s,null,2)),n}function zi(e){cm();let t=E(),n=pr.replace(/'/g,"''");t.exec(`ATTACH DATABASE '${n}' AS archive`);try{return e(t)}finally{t.exec("DETACH DATABASE archive")}}function kx(){return zi(e=>{let t=e.prepare(`SELECT
|
|
1683
1710
|
SUM(CASE WHEN archive_status = 'archived' THEN 1 ELSE 0 END) AS archived,
|
|
1684
1711
|
SUM(CASE WHEN archive_status != 'archived' THEN 1 ELSE 0 END) AS live
|
|
1685
1712
|
FROM sessions`).get(),n=e.prepare("SELECT COUNT(*) AS n FROM messages").get().n,s=e.prepare(`SELECT
|
|
@@ -1688,15 +1715,15 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
|
|
|
1688
1715
|
SELECT MAX(timestamp) AS t FROM main.messages_archive WHERE timestamp IS NOT NULL
|
|
1689
1716
|
UNION ALL
|
|
1690
1717
|
SELECT MAX(timestamp) AS t FROM archive.messages_archive WHERE timestamp IS NOT NULL
|
|
1691
|
-
)`).get();return{liveSessions:t.live??0,archivedSessions:t.archived??0,liveMessages:n,archivedMessages:s,oldestLiveTimestamp:r.t,newestArchivedTimestamp:o.t}})}function
|
|
1718
|
+
)`).get();return{liveSessions:t.live??0,archivedSessions:t.archived??0,liveMessages:n,archivedMessages:s,oldestLiveTimestamp:r.t,newestArchivedTimestamp:o.t}})}function _m(e){return e?e.slice(0,10):"\u2014"}function xx(){let e=kx();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 ${_m(e.oldestLiveTimestamp)}`),console.log(` Newest archived ${_m(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 Cx(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
|
|
1692
1719
|
FROM sessions s
|
|
1693
1720
|
WHERE s.archive_status != 'archived'
|
|
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=
|
|
1721
|
+
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=zi(a=>a.transaction(l=>{let u=0,m=a.prepare(`INSERT OR IGNORE INTO archive.messages_archive
|
|
1695
1722
|
(uuid, session_id, parent_uuid, type, role, timestamp,
|
|
1696
1723
|
is_sidechain, content_text, tool_names, raw_json, archived_at)
|
|
1697
1724
|
SELECT uuid, session_id, parent_uuid, type, role, timestamp,
|
|
1698
1725
|
is_sidechain, content_text, tool_names, raw_json, datetime('now')
|
|
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
|
|
1726
|
+
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 Lx(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=zi(r=>r.transaction(()=>{let i=r.prepare(`INSERT OR IGNORE INTO messages
|
|
1700
1727
|
(uuid, session_id, parent_uuid, type, role, timestamp,
|
|
1701
1728
|
is_sidechain, content_text, tool_names, raw_json)
|
|
1702
1729
|
SELECT uuid, session_id, parent_uuid, type, role, timestamp,
|
|
@@ -1706,33 +1733,33 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
|
|
|
1706
1733
|
is_sidechain, content_text, tool_names, raw_json)
|
|
1707
1734
|
SELECT uuid, session_id, parent_uuid, type, role, timestamp,
|
|
1708
1735
|
is_sidechain, content_text, tool_names, raw_json
|
|
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
|
|
1736
|
+
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 Ax(){let e=fm();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 Nx(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=Ji({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 Ox(){return Ji({autoArchiveEnabled:!1}),console.log("Auto-archive: DISABLED. Existing archived sessions stay archived."),0}async function hm(e){let t=e._action??"list";if(t==="list"||t==="stats")return xx();if(t==="run")return e.before?Cx({before:e.before,dryRun:e.dryRun===!0}):(console.error("Usage: recall archive run --before YYYY-MM-DD [--dry-run]"),1);if(t==="restore")return Lx(e._sessionId??"");if(t==="auto"){let n=e._subAction??"status";return n==="status"?Ax():n==="on"||n==="enable"?Nx(e.after):n==="off"||n==="disable"?Ox():(console.error("Usage: recall archive auto <status|on|off> [--after <days>]"),1)}return console.error(`Usage: recall archive <list|run|restore|auto> [args]
|
|
1710
1737
|
list \u2014 show archive counts
|
|
1711
1738
|
run --before YYYY-MM-DD [--dry-run] \u2014 move sessions older than DATE
|
|
1712
1739
|
restore <session-id> \u2014 pull a session back from archive
|
|
1713
|
-
auto <status|on|off> [--after N] \u2014 daemon auto-archives sessions older than N days`),1}$();
|
|
1740
|
+
auto <status|on|off> [--after N] \u2014 daemon auto-archives sessions older than N days`),1}$();k();D();import{existsSync as vx,readFileSync as Ix}from"node:fs";function Mx(){let e=`${C}/daemon.port`;if(!vx(e))return null;try{let t=Ix(e,"utf8").trim();return/^\d+$/.test(t)?t:null}catch{return null}}function Dx(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
1741
|
`),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}"
|
|
1715
1742
|
`),null):(process.stderr.write(`ambiguous session prefix "${e}". be more specific.
|
|
1716
|
-
`),null)}async function
|
|
1717
|
-
`),process.exitCode=1;return}let o=
|
|
1743
|
+
`),null)}async function Em(e,t,n){let s=Dx(e);if(!s){process.exitCode=1;return}let r=t.trim();if(!r){process.stderr.write(`name cannot be empty
|
|
1744
|
+
`),process.exitCode=1;return}let o=Mx();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 ut("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}
|
|
1718
1745
|
`),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}
|
|
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}"`)}`)}$();
|
|
1746
|
+
`),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}"`)}`)}$();k();var ym=90;async function $x(){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 Px(){let e=Date.now()/1e3-ym;return E().prepare(`SELECT s.id, s.cwd, s.file_mtime, NULLIF(sa.alias, '') AS alias
|
|
1720
1747
|
FROM sessions s
|
|
1721
1748
|
LEFT JOIN session_aliases sa ON sa.session_id = s.id
|
|
1722
1749
|
WHERE s.cwd IS NOT NULL AND s.file_mtime > ?
|
|
1723
|
-
ORDER BY s.cwd, s.file_mtime DESC`).all(e)}var
|
|
1750
|
+
ORDER BY s.cwd, s.file_mtime DESC`).all(e)}var Fx=new Set(["zsh","bash","fish","sh","dash","ksh","tcsh","csh","pwsh","powershell","cmd","nu"]);function bm(e){let t=e.trim().toLowerCase().replace(/^[-/]+/,"").replace(/^.*\//,"").replace(/\s*\(\d+\)\s*$/,"").trim();return!t||Fx.has(t)}var jx=/^[⠀-⣿✳\s]+/,Ux=/^\d+(\.\d+){1,3}$/;function Sm(e){let t=e.trim();return!!(!t||jx.test(t)||Ux.test(t))}async function wm(e){let t=await $x(),n=Px();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=bm(a.tab_name),l=Sm(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 ${ym}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=>!bm(l.tab_name)&&!Sm(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()}$();k();D();import{existsSync as Bx,readFileSync as Hx}from"node:fs";function Wx(){let e=`${C}/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{let t=await nt(`http://127.0.0.1:${e}/api/terminal/registry`);return t.ok?(await t.json()).terminals??[]:[]}catch{return[]}}async function Gx(e,t,n){try{return(await ut("POST",`http://127.0.0.1:${e}/api/sessions/${encodeURIComponent(t)}/relink`,{shell_pid:n})).ok}catch{return!1}}function Jx(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 zx(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
|
|
1724
1751
|
FROM session_aliases sa
|
|
1725
1752
|
JOIN sessions s ON s.id = sa.session_id
|
|
1726
|
-
WHERE sa.alias = '' AND sa.previous_aliases != '[]' AND s.cwd IS NOT NULL`).all(),o=[];for(let i of r){let a=
|
|
1753
|
+
WHERE sa.alias = '' AND sa.previous_aliases != '[]' AND s.cwd IS NOT NULL`).all(),o=[];for(let i of r){let a=Jx(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 Tm(e){let t=Wx();t||(console.error(c.err("Daemon is not running. Run `recall start` first.")),process.exit(1));let n=await Xx(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=zx(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 Gx(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."))}$();Nn();async function Rm(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=Vs(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("")}$();k();import{execFile as Yx}from"node:child_process";import{promisify as qx}from"node:util";import{stat as Vx}from"node:fs/promises";Nn();var Kx=qx(Yx),Qx=60,Zx=7,e0=7,t0=5e3;function n0(){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
|
|
1727
1754
|
WHERE (COALESCE(total_input_tokens,0)
|
|
1728
1755
|
+ COALESCE(total_output_tokens,0)
|
|
1729
1756
|
+ COALESCE(total_cache_create_tokens,0)
|
|
1730
1757
|
+ COALESCE(total_cache_read_tokens,0)) > 0
|
|
1731
|
-
LIMIT 1`),git:t("SELECT 1 FROM session_commits LIMIT 1")}}function
|
|
1758
|
+
LIMIT 1`),git:t("SELECT 1 FROM session_commits LIMIT 1")}}function Yi(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 km(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 s0(){let e=E(),t=e.prepare(`SELECT ss.keywords
|
|
1732
1759
|
FROM session_semantic ss
|
|
1733
1760
|
JOIN sessions s ON s.id = ss.session_id
|
|
1734
1761
|
WHERE s.started_at IS NOT NULL
|
|
1735
|
-
AND julianday('now') - julianday(s.started_at) <= @windowDays`).all({windowDays:
|
|
1762
|
+
AND julianday('now') - julianday(s.started_at) <= @windowDays`).all({windowDays:Zx});if(t.length===0)return null;let n=new Set;for(let o of t)for(let i of Yi(o.keywords))n.add(i);if(n.size===0)return null;let s=e.prepare(`SELECT ss.session_id AS session_id,
|
|
1736
1763
|
ss.summary AS summary,
|
|
1737
1764
|
ss.keywords AS keywords,
|
|
1738
1765
|
p.name AS project,
|
|
@@ -1748,7 +1775,7 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
|
|
|
1748
1775
|
WHERE s.started_at IS NOT NULL
|
|
1749
1776
|
AND s.message_count > 2
|
|
1750
1777
|
AND julianday('now') - julianday(s.started_at) >= @ageDays
|
|
1751
|
-
ORDER BY s.started_at ASC`).all({ageDays:
|
|
1778
|
+
ORDER BY s.started_at ASC`).all({ageDays:Qx});if(s.length===0)return null;let r=null;for(let o of s){let a=Yi(o.keywords).filter(d=>n.has(d));a.length!==0&&(!r||a.length>r.overlap.length)&&(r={row:o,overlap:a})}return r?{...km(r.row),summary:r.row.summary,keywords:Yi(r.row.keywords),matchedKeywords:r.overlap,daysAgo:Math.max(0,Math.round(r.row.days_old))}:null}function r0(){let t=E().prepare(`SELECT s.id AS session_id,
|
|
1752
1779
|
p.name AS project,
|
|
1753
1780
|
NULLIF(sa.alias, '') AS alias,
|
|
1754
1781
|
s.started_at AS started_at,
|
|
@@ -1767,54 +1794,54 @@ Top ${d.length} cluster(s):`);for(let l of d){let u=l.status==="resolved"?"\u271
|
|
|
1767
1794
|
AND (COALESCE(s.total_input_tokens, 0)
|
|
1768
1795
|
+ COALESCE(s.total_output_tokens, 0)
|
|
1769
1796
|
+ COALESCE(s.total_cache_create_tokens, 0)
|
|
1770
|
-
+ COALESCE(s.total_cache_read_tokens, 0)) > 0`).all({windowDays:
|
|
1797
|
+
+ COALESCE(s.total_cache_read_tokens, 0)) > 0`).all({windowDays:e0});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?{...km(n.row),totalTokens:n.totalTokens,costCents:n.cents,costDisplay:le(n.cents),tokensDisplay:he(n.totalTokens),primaryModel:n.row.primary_model,primaryModelLabel:_t(n.row.primary_model).label}:null}async function o0(e){try{if(!(await Vx(e)).isDirectory())return null}catch{return null}try{let{stdout:t}=await Kx("git",["rev-parse","HEAD"],{cwd:e,timeout:t0}),n=t.trim();return/^[0-9a-f]{40}$/.test(n)?n:null}catch{return null}}async function i0(){let e=E(),t=e.prepare(`SELECT s.id AS id, s.cwd AS cwd
|
|
1771
1798
|
FROM sessions s
|
|
1772
1799
|
WHERE s.cwd IS NOT NULL AND s.started_at IS NOT NULL
|
|
1773
1800
|
ORDER BY COALESCE(s.ended_at, s.started_at, '') DESC
|
|
1774
|
-
LIMIT 1`).get();if(!t?.cwd)return null;let n=await
|
|
1801
|
+
LIMIT 1`).get();if(!t?.cwd)return null;let n=await o0(t.cwd);if(!n)return null;let s=Vs(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
|
|
1775
1802
|
FROM sessions s
|
|
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
|
|
1803
|
+
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 xm(){let e=n0(),t=e.semantic?Promise.resolve().then(()=>{try{return s0()}catch(a){return console.error("[discover.rediscovered]",a),null}}):Promise.resolve(null),n=e.cost?Promise.resolve().then(()=>{try{return r0()}catch(a){return console.error("[discover.expensive]",a),null}}):Promise.resolve(null),s=e.git?i0().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 Vi(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 qi(e){return e.alias??(e.firstUserMessage?Vi(e.firstUserMessage,60):null)??e.sessionId.slice(0,8)}function a0(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(qi(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(Vi(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(qi(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(qi(n))} ${c.dim(n.project?`\xB7 ${n.project}`:"")}`),n.subject&&console.log(` ${c.dim(Vi(n.subject,80))}`),console.log(` ${c.dim(n.sessionId.slice(0,8))} ${c.dim("cwd: "+n.cwd)}`)}console.log("")}async function Cm(e){let t=await xm();if(e.json){console.log(JSON.stringify(t,null,2));return}a0(t)}$();Ge();import{spawnSync as Om}from"node:child_process";import{readdirSync as c0}from"node:fs";import{join as l0,resolve as d0}from"node:path";var Ki=["code","cursor","code-insiders","windsurf"],Lm=[{id:"code",label:"VS Code"},{id:"cursor",label:"Cursor"},{id:"code-insiders",label:"VS Code Insiders"},{id:"windsurf",label:"Windsurf"}],u0="https://marketplace.visualstudio.com/items?itemName=clauderecallhq.clauderecall-vscode";function p0(){return l0(te(),"extensions","vscode")}function m0(){let e=p0(),t;try{t=c0(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})),d0(e,n[0]))}function Am(e){let t=Om(e,["--version"],{stdio:"ignore",shell:process.platform==="win32"});return t.error?!1:t.status===0}function g0(e,t){let n=Om(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 Nm(){process.stderr.write(c.err(`no bundled .vsix found at extensions/vscode/(clauderecall|claude-recall)-vscode-*.vsix
|
|
1777
1804
|
`)),process.stderr.write(c.dim(`see PUBLISHING.md for how to build the extension, or run:
|
|
1778
1805
|
`)),process.stderr.write(c.dim(` (cd extensions/vscode && npm install && npm run build && npm run package)
|
|
1779
|
-
`))}function
|
|
1780
|
-
`)),process.stderr.write(c.dim(`valid values: ${
|
|
1781
|
-
`)),process.exitCode=1;return}let t=
|
|
1782
|
-
`);return}if(!t){
|
|
1806
|
+
`))}function f0(e){return Ki.includes(e)}async function vm(e){if(e.editor!==void 0&&!f0(e.editor)){process.stderr.write(c.err(`unknown --editor target: ${e.editor}
|
|
1807
|
+
`)),process.stderr.write(c.dim(`valid values: ${Ki.join(", ")}
|
|
1808
|
+
`)),process.exitCode=1;return}let t=m0();if(e.printPath){if(!t){Nm(),process.exitCode=1;return}process.stdout.write(t+`
|
|
1809
|
+
`);return}if(!t){Nm(),process.exitCode=1;return}let n;if(e.editor){let r=e.editor;if(!Am(r)){process.stderr.write(c.err(`editor CLI not found on PATH: ${r}
|
|
1783
1810
|
`)),process.stderr.write(c.dim(`install the editor shell integration, or omit --editor to install into every detected editor.
|
|
1784
1811
|
`)),process.stderr.write(c.dim(`see PUBLISHING.md for manual install instructions.
|
|
1785
|
-
`)),process.exitCode=1;return}n=[
|
|
1786
|
-
`)),process.stderr.write(c.dim(`looked for: ${
|
|
1812
|
+
`)),process.exitCode=1;return}n=[Lm.find(i=>i.id===r)??{id:r,label:r}]}else n=Lm.filter(r=>Am(r.id));if(n.length===0){process.stderr.write(c.err(`no supported editor CLI detected on PATH.
|
|
1813
|
+
`)),process.stderr.write(c.dim(`looked for: ${Ki.join(", ")}.
|
|
1787
1814
|
`)),process.stderr.write(c.dim(`install the editor shell integration and try again.
|
|
1788
1815
|
`)),process.stderr.write(c.dim(`see PUBLISHING.md for manual install instructions.
|
|
1789
1816
|
`)),process.exitCode=1;return}process.stderr.write(c.dim(`bundled .vsix: ${t}
|
|
1790
1817
|
|
|
1791
|
-
`));let s=!1;for(let r of n){let{ok:o,message:i}=
|
|
1818
|
+
`));let s=!1;for(let r of n){let{ok:o,message:i}=g0(r.id,t);o?process.stderr.write(c.ok(`\u2713 installed into ${r.label} (${r.id})
|
|
1792
1819
|
`)):(s=!0,process.stderr.write(c.err(`\u2717 ${r.label} (${r.id}): ${i}
|
|
1793
1820
|
`)))}process.stderr.write(`
|
|
1794
1821
|
`),process.stderr.write(c.bold(`next steps:
|
|
1795
1822
|
`)),process.stderr.write(` 1. reload the editor window: Cmd/Ctrl+Shift+P then pick "Developer: Reload Window"
|
|
1796
1823
|
`),process.stderr.write(` 2. enable the extension: open settings and set "claude-recall.autoAlias" to true
|
|
1797
1824
|
`),process.stderr.write(`
|
|
1798
|
-
`),process.stderr.write(c.dim(`marketplace install: ${
|
|
1799
|
-
`)),s&&(process.exitCode=1)}
|
|
1825
|
+
`),process.stderr.write(c.dim(`marketplace install: ${u0}
|
|
1826
|
+
`)),s&&(process.exitCode=1)}ta();$();function na(e){return e>=70?c.ok:e>=40?c.warn:c.err}function Mm(e,t=20){let n=Math.round(e/100*t);return"\u2588".repeat(n)+"\u2591".repeat(t-n)}function _0(e){let t=na(e.score);process.stdout.write(`
|
|
1800
1827
|
${c.bold(e.projectName)} ${t(String(e.score)+"/100")}
|
|
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=
|
|
1802
|
-
`)}}function
|
|
1828
|
+
`);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=na(o);process.stdout.write(` ${r.padEnd(10)} ${a(Mm(o,16))} ${String(Math.round(o)).padStart(3)}% ${c.dim(i)}
|
|
1829
|
+
`)}}function Dm(e,t){if(e){let r=Zi(e);if(!r){process.stderr.write(c.err(`project "${e}" not found
|
|
1803
1830
|
`)),process.exitCode=1;return}if(t.json){process.stdout.write(JSON.stringify(r,null,2)+`
|
|
1804
|
-
`);return}
|
|
1831
|
+
`);return}_0(r);return}let n=ea();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
1832
|
`);return}let s=[...n].sort((r,o)=>r.score-o.score);process.stdout.write(c.bold("Memory Health Scores")+` (worst first)
|
|
1806
1833
|
|
|
1807
|
-
`);for(let r of s){let o=
|
|
1834
|
+
`);for(let r of s){let o=na(r.score);process.stdout.write(` ${o(Mm(r.score,12))} ${String(r.score).padStart(3)}/100 ${r.projectName}
|
|
1808
1835
|
`)}process.stdout.write(`
|
|
1809
|
-
`)}$();function
|
|
1810
|
-
`);return}if(e==="off"){
|
|
1811
|
-
`);return}let t=
|
|
1836
|
+
`)}$();function $m(e){if(e==="on"){co(!0),process.stdout.write(c.ok("Verification badges enabled.")+`
|
|
1837
|
+
`);return}if(e==="off"){co(!1),process.stdout.write(`Verification badges disabled.
|
|
1838
|
+
`);return}let t=es();process.stdout.write(`Verification badges: ${t?c.ok("ON"):"OFF"}
|
|
1812
1839
|
`),process.stdout.write(`
|
|
1813
1840
|
Toggle with: recall verify on | off
|
|
1814
|
-
`)}$();
|
|
1841
|
+
`)}$();mn();ss();Bt();rs();import{hostname as J0}from"node:os";import{randomBytes as z0}from"node:crypto";Xe();ge();D();k();Ge();import{existsSync as h0}from"node:fs";import E0 from"node:readline";import{createRequire as b0}from"node:module";import{Chalk as S0}from"chalk";var y0=b0(import.meta.url),w0=y0(`${te()}/package.json`).version,Um="#f97316",T0="#8b9098",R0="#10b981",k0="#f59e0b",Qt=new S0({level:process.env.NO_COLOR?0:3}),Re=Qt.hex(Um),de=Qt.hex(Um).bold,P=Qt.hex(T0),Ot=Qt.hex(R0),fr=Qt.hex(k0),Bm=Qt.bold,Hm=[" \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"],x0=Hm[0]?.length??49,Pm="Never lose a Claude Code session again.",C0="CLAUDE RECALL";function L0(){if(!h0(ee))return{sessions:0,projects:0};try{let t=E().prepare(`SELECT
|
|
1815
1842
|
(SELECT COUNT(*) FROM sessions) AS sessions,
|
|
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
|
|
1843
|
+
(SELECT COUNT(*) FROM projects) AS projects`).get();return{sessions:t.sessions,projects:t.projects}}catch{return{sessions:0,projects:0}}}function A0(){let e=process.stdout.columns;return typeof e!="number"||e<=0?!1:e<x0+2}function Wm(){if(A0()){console.log(`${de(C0)} ${P("\xB7")} ${P(Pm)}`);return}for(let e of Hm)console.log(de(e));console.log(P(Pm))}async function N0(){return{daemon:ie(),counts:L0(),license:await Le()}}function O0(e){let t=[];if(t.push(` ${P("Version:")} ${Re(`v${w0}`)} ${P("\xB7 CLI \xB7 @clauderecallhq/cli")}`),e.daemon){let n=`http://127.0.0.1:${e.daemon.port}`;t.push(` ${P("Daemon:")} ${Ot("running")} on ${n} ${P(`(pid ${e.daemon.pid})`)}`)}else t.push(` ${P("Daemon:")} ${fr("stopped")}`);if(e.counts.sessions===0)t.push(` ${P("Sessions:")} ${fr("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:")} ${Ot("Pro")}`):t.push(` ${P("License:")} Free`),t}function v0(e){let t=Re(">");if(e.counts.sessions===0)return`${t} Run ${de("recall index")} to scan ~/.claude/projects/`;if(!e.daemon)return`${t} Run ${de("recall start")} to launch the local daemon`;if(e.license.tier==="free")return`${t} Run ${de("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 ${de("recall --help")} for all commands`}var _r=[{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"}]}],I0=_r.reduce((e,t)=>e+t.commands.length,0),M0=new Set([..._r.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 Fm(){let e=_r.flatMap(n=>n.commands.map(s=>`recall ${s.name}`)),t=Math.max(...e.map(n=>n.length));console.log(),console.log(`${Bm("COMMANDS")} ${P(`\xB7 ${I0} total \xB7 q to quit \xB7 recall --help for full details`)}`),console.log(` ${P("All commands run as subcommands of")} ${de("recall")}${P(". There is no separate")} ${Re("threads")}${P(",")} ${Re("thread")}${P(", or")} ${Re("titles")} ${P("binary.")}`);for(let n of _r){console.log(),console.log(` ${de(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 D0(){return!!process.stdin.isTTY&&!!process.stdout.isTTY}async function $0(){if(!D0())return;let e=E0.createInterface({input:process.stdin,output:process.stdout}),t=`${P("Type")} ${de("/")} ${P("for commands, or press")} ${de("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==="?"){Fm(),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===""){Fm(),s();return}let d=(i.split(/\s+/)[0]??"").toLowerCase();M0.has(d)?console.log(` ${P("Run")} ${de(`recall ${i}`)} ${P("in your shell to invoke that command.")}`):console.log(` ${P(`No command matches "${o}". Type`)} ${de("/")} ${P("to browse, or")} ${de("recall --help")} ${P("for the full list.")}`),s()})};e.on("close",()=>{console.log(),n()}),s()})}async function Xm(){let e=await N0();console.log(),Wm(),console.log();for(let t of O0(e))console.log(t);console.log(),console.log(v0(e)),console.log(),await $0()}var P0=[{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 F0(){return process.env.NO_COLOR||process.env.CI||process.env.RECALL_NO_ANIMATION?!1:!!process.stdout.isTTY}function sa(e){return new Promise(t=>setTimeout(t,e))}function j0(e){let n=(e.split("@")[0]??"").split(/[._+\-]/)[0]??"";return n?n.charAt(0).toUpperCase()+n.slice(1).toLowerCase():""}function jm(){process.stdout.write("\r\x1B[2K")}async function hr(e){let t=F0();console.log(),Wm(),console.log(),t&&(process.stdout.write(` ${P("Verifying license...")}`),await sa(260),jm()),console.log(` ${Ot("\u2713")} ${P("License verified")}`),console.log(` ${Ot("\u2713")} ${P("Tier:")} ${de("Pro")} ${P("\xB7 Lifetime")}`),console.log(` ${Ot("\u2713")} ${P("Key:")} ${Re(e.key_short)}`),console.log(` ${Ot("\u2713")} ${P("Email:")} ${e.email}`),e.test_mode&&console.log(` ${fr("!")} ${fr("Test mode:")} this license was issued in test mode.`),console.log(),console.log(` ${Bm("Unlocking Pro features")}`),console.log();for(let r of P0)t&&(process.stdout.write(` ${P("\xB7")} ${P(r.name)}`),await sa(110),jm()),console.log(` ${Ot("\u2713")} ${r.name} ${P(r.detail)}`);t&&await sa(160),console.log();let n=j0(e.email),s=n?`Welcome aboard, ${n}.`:"Welcome aboard.";console.log(` ${de(s)}`),console.log(` ${P("Run")} ${Re("recall")} ${P("to open the dashboard, or")} ${Re("recall help")} ${P("for the command list.")}`),console.log()}$();mn();ss();Bt();rs();import{hostname as U0}from"node:os";import{randomBytes as B0}from"node:crypto";import{createInterface as H0}from"node:readline/promises";async function W0(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 X0(e={}){let t=H0({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 G0(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 Er(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(),gn)),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??X0)();G0(r)||(console.error(c.warn(`"${r}" doesn't look like an email address.`)),process.exit(1));let o=t.instanceName??`${U0()}-${B0(4).toString("hex")}`,i=t.fingerprint??Wt(),a=t.apiBaseUrl??dt(),d;try{d=await W0({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 Ht(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 hr({...l.claims,key_short:d.body.key_short??l.claims.key_short,email:d.body.customer_email??l.claims.email})}function Y0(e){return/^recall-pro-/i.test(e)}async function Gm(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)),!Y0(t)){await Er(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=`${J0()}-${z0(4).toString("hex")}`,s=`${dt()}/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:Wt()})})}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 Ht(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 hr({...i.claims,key_short:o.key_short??i.claims.key_short,email:o.customer_email??i.claims.email})}$();import{spawn as q0}from"node:child_process";import{platform as br}from"node:os";var Jm="https://clauderecall.com/pricing";function V0(e){let t=br()==="darwin"?"open":br()==="win32"?"start":"xdg-open",n=br()==="win32"?["",e]:[e];q0(t,n,{detached:!0,stdio:"ignore",shell:br()==="win32"}).unref()}async function zm(){let{getLicenseStatus:e}=await Promise.resolve().then(()=>(ge(),gn)),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")} ${Jm}`),console.log(c.dim("After purchase, run: recall activate <license-key>")),console.log(),V0(Jm)}$();import{spawn as K0}from"node:child_process";import{platform as Sr}from"node:os";var Ym="https://clauderecall.com/pricing?trial=1&utm_source=cli&utm_medium=recall_trial";function Q0(e){let t=Sr()==="darwin"?"open":Sr()==="win32"?"start":"xdg-open",n=Sr()==="win32"?["",e]:[e];K0(t,n,{detached:!0,stdio:"ignore",shell:Sr()==="win32"}).unref()}async function qm(e){if(e&&e.trim().length>0){await Er(e);return}let{getLicenseStatus:t}=await Promise.resolve().then(()=>(ge(),gn)),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")} ${Ym}`),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(),Q0(Ym)}$();mn();import{existsSync as Vm,mkdirSync as Z0,readFileSync as eC,writeFileSync as tC}from"node:fs";import{homedir as nC}from"node:os";import{join as Km}from"node:path";import{randomBytes as sC}from"node:crypto";var oa=Km(nC(),".recall"),wr=Km(oa,"telemetry.json"),ra={decided_at:null,decision:null,last_ping_month:null,nonce_month:null,nonce:null,last_error:null};function vt(){if(!Vm(wr))return{...ra};try{let e=eC(wr,"utf8"),t=JSON.parse(e);return{...ra,...t}}catch{return{...ra}}}function yr(e){Vm(oa)||Z0(oa,{recursive:!0}),tC(wr,JSON.stringify(e,null,2)+`
|
|
1844
|
+
`,{mode:384})}function Qm(){return wr}function ia(e=new Date){let t=e.getUTCFullYear(),n=String(e.getUTCMonth()+1).padStart(2,"0");return`${t}-${n}`}function Zm(e,t){return e.nonce&&e.nonce_month===t?e:{...e,nonce:sC(16).toString("hex"),nonce_month:t}}function rC(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 eg(e){let t=vt();if(t.decision!=="on")return{status:t.decision==="off"?"opted-out":"no-decision"};let n=ia();if(t.last_ping_month===n)return{status:"already-this-month"};t=Zm(t,n);let s=rC(e,t);if(!s)return{status:"error",detail:"no nonce"};try{let r=await fetch(`${dt()}/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 yr({...t,last_error:`${new Date().toISOString()} ${i}`}),{status:"error",detail:i}}let o=await r.json().catch(()=>({}));return yr({...t,last_ping_month:n,last_error:null}),{status:o.deduped?"deduped":"sent"}}catch(r){let o=r instanceof Error?r.message:"unknown";return yr({...t,last_error:`${new Date().toISOString()} ${o}`}),{status:"error",detail:o}}}function Zt(e){let n={...vt(),decision:e,decided_at:new Date().toISOString(),last_error:null,...e==="off"?{nonce:null,nonce_month:null,last_ping_month:null}:{}};return yr(n),n}function Tr(e){let t=Zm(vt(),ia());return{event:"install",version:e,platform:process.platform,arch:process.arch,month:t.nonce_month??ia(),nonce:t.nonce??"0".repeat(32)}}async function aa(e){let t=vt();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:")} ${Qm()}`),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(Tr(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 tg(){Zt("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 ng(){Zt("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 sg(e){await aa(e)}import{createInterface as oC}from"node:readline/promises";import{stdin as rg,stdout as Ee}from"node:process";var iC=new Set(["telemetry","trial","upgrade","activate","license","help","version"]);function aC(e){return!(process.env.CI==="true"||process.env.RECALL_QUIET_INSTALL==="1"||process.env.RECALL_DISABLE_TELEMETRY_PROMPT==="1"||!rg.isTTY||!Ee.isTTY||e&&iC.has(e)||vt().decision!==null)}async function og(e,t){if(!aC(e))return;let n=Tr(t);Ee.write(`
|
|
1818
1845
|
`),Ee.write(` Claude Recall \u2014 anonymous install ping?
|
|
1819
1846
|
|
|
1820
1847
|
`),Ee.write(` npm download counts mix real installs with bots and scanners.
|
|
@@ -1830,55 +1857,55 @@ Toggle with: recall verify on | off
|
|
|
1830
1857
|
|
|
1831
1858
|
`),Ee.write(` Full disclosure: https://clauderecall.com/telemetry
|
|
1832
1859
|
|
|
1833
|
-
`);let s=
|
|
1860
|
+
`);let s=oC({input:rg,output:Ee}),r;try{r=(await s.question(" Send the install ping? [y/N]: ")).trim().toLowerCase()}finally{s.close()}r==="y"||r==="yes"?(Zt("on"),Ee.write(`
|
|
1834
1861
|
Opted in. Reverse any time with \`recall telemetry off\`.
|
|
1835
1862
|
|
|
1836
|
-
`)):(
|
|
1863
|
+
`)):(Zt("off"),Ee.write(`
|
|
1837
1864
|
Opted out. No pings will be sent. Reverse with \`recall telemetry on\`.
|
|
1838
1865
|
|
|
1839
|
-
`))}var
|
|
1840
|
-
`)}$();ge();
|
|
1866
|
+
`))}bi();var cC="RECALL_NO_BANNER",lC=new Set(["doctor"]),ig=!1;function ag(e){if(ig||lC.has(e.commandName)||(e.optOut!==void 0?e.optOut:process.env[cC]==="1"))return 0;let n=e.write??(r=>process.stderr.write(r)),s=0;try{let r=Ks(),o=xu(r,"critical");if(o.length===0)return 0;s=o.length,n(dC(o)),ig=!0}catch{return 0}return s}function dC(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(`
|
|
1867
|
+
`)}$();ge();Bt();po();async function cg(){let e=await Le();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=os();t&&console.log(` ${c.dim("Last server check:")} ${t.last_checked_at}`),console.log()}async function lg(){let e=await Eo({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 dg(){yc(),console.log(),console.log("License removed from this machine."),console.log(c.dim(`(${Ct})`)),console.log()}k();$();import uC 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
1868
|
`),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}"
|
|
1842
1869
|
`),null;process.stderr.write(`ambiguous thread prefix "${e}" \u2014 candidates:
|
|
1843
1870
|
`);for(let s of n)process.stderr.write(` ${X(s.id)} ${s.name}
|
|
1844
|
-
`);return null}function
|
|
1871
|
+
`);return null}function It(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
1872
|
`),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}"
|
|
1846
1873
|
`),null):(process.stderr.write(`ambiguous session prefix "${e}". be more specific.
|
|
1847
|
-
`),null)}function
|
|
1874
|
+
`),null)}function pC(e){let t=new Map;if(e.length===0)return t;let n=e.map(()=>"?").join(","),r=E().prepare(`SELECT s.id,
|
|
1848
1875
|
s.first_user_message,
|
|
1849
1876
|
p.name AS project_name,
|
|
1850
1877
|
a.alias AS alias
|
|
1851
1878
|
FROM sessions s
|
|
1852
1879
|
LEFT JOIN projects p ON p.id = s.project_id
|
|
1853
1880
|
LEFT JOIN session_aliases a ON a.session_id = s.id
|
|
1854
|
-
WHERE s.id IN (${n})`).all(...e);for(let o of r)t.set(o.id,o);return t}function
|
|
1881
|
+
WHERE s.id IN (${n})`).all(...e);for(let o of r)t.set(o.id,o);return t}function ug(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}${Q(r,80)}`}function ca(e){return e.archived?"archived":e.closed_at?"closed":"open"}function en(e){let t=ca(e);process.stderr.write(c.ok(`\u2713 ${e.name} ${c.dim(`(${X(e.id)})`)} [${t}]
|
|
1855
1882
|
`))}function Oe(e,t,n){if(e){process.stdout.write(JSON.stringify(t,null,2)+`
|
|
1856
|
-
`);return}n()}function
|
|
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("? ")}${
|
|
1858
|
-
`)}function
|
|
1859
|
-
`),process.exitCode=1;return}let r=
|
|
1883
|
+
`);return}n()}function pg(e){let t=ko({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 uC({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)),Q(s.name,38),String(s.session_count),String(s.origin_count),ca(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 mC(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}${ug(a.session_id,t)}
|
|
1884
|
+
`);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("? ")}${ug(a.session_id,t)}
|
|
1885
|
+
`)}function mg(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}
|
|
1886
|
+
`),process.exitCode=1;return}let r=pC(s.edges.map(o=>o.session_id));Oe(t.json===!0,s,()=>{let o=ca(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}mC(s,r)})}function gg(e,t){let n;if(t.origin){let r=It(t.origin);if(!r){process.exitCode=1;return}n=r}let s=dl({name:e,summary:t.summary??null,originSessionId:n});Oe(t.json===!0,s,()=>{en(s),process.stderr.write(c.dim(`id ${s.id}
|
|
1860
1887
|
`)),n&&process.stderr.write(c.dim(`origin: ${X(n)}
|
|
1861
|
-
`))})}function
|
|
1862
|
-
`))})}function
|
|
1888
|
+
`))})}function fg(e,t){let n=je(t.thread);if(!n){process.exitCode=1;return}let s=It(e);if(!s){process.exitCode=1;return}let r=null;if(t.parent){let a=It(t.parent);if(!a){process.exitCode=1;return}r=a}let o=t.role??(r?"child":"origin"),i=ul({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}]
|
|
1889
|
+
`))})}function _g(e,t){let n=je(t.thread);if(!n){process.exitCode=1;return}let s=It(e);if(!s){process.exitCode=1;return}let r=pl(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
1890
|
`));return}process.stderr.write(c.ok(`\u2713 unlinked ${X(s)} from ${X(n)}
|
|
1864
|
-
`))})}function
|
|
1865
|
-
`))})}function
|
|
1866
|
-
`)),process.exitCode=1;return}let r=
|
|
1867
|
-
`))})}function
|
|
1868
|
-
`)),process.exitCode=1;return}let r=[];for(let i of s){let a=
|
|
1869
|
-
`))})}
|
|
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=
|
|
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??{},
|
|
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
|
|
1891
|
+
`))})}function hg(e,t){let n=je(t.thread);if(!n){process.exitCode=1;return}let s=It(e);if(!s){process.exitCode=1;return}let r=null;if(t.parent&&t.parent.toLowerCase()!=="none"){let i=It(t.parent);if(!i){process.exitCode=1;return}r=i}let o=ml(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")+`
|
|
1892
|
+
`))})}function Eg(e,t,n){let s=je(e);if(!s){process.exitCode=1;return}let r=gl(s,t);Oe(n.json===!0,r,()=>en(r))}function bg(e,t){let n=je(e);if(!n){process.exitCode=1;return}let s=fl(n);Oe(t.json===!0,s,()=>en(s))}function Sg(e,t){let n=je(e);if(!n){process.exitCode=1;return}let s=_l(n);Oe(t.json===!0,s,()=>en(s))}function yg(e,t){let n=je(e);if(!n){process.exitCode=1;return}let s=hl(n);Oe(t.json===!0,s,()=>en(s))}function wg(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
|
|
1893
|
+
`)),process.exitCode=1;return}let r=El(n,s);Oe(t.json===!0,r,()=>{process.stderr.write(c.ok(`\u2713 merged ${X(n)} \u2192 ${X(s)} (${r.session_count} sessions)
|
|
1894
|
+
`))})}function Tg(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
|
|
1895
|
+
`)),process.exitCode=1;return}let r=[];for(let i of s){let a=It(i);if(!a){process.exitCode=1;return}r.push(a)}let o=bl({threadId:n,sessionIds:r,newThreadName:t.name});Oe(t.json===!0,o,()=>{en(o),process.stderr.write(c.dim(`split off ${r.length} session${r.length===1?"":"s"} from ${X(n)}
|
|
1896
|
+
`))})}k();$();import{randomUUID as $C}from"node:crypto";import{readFileSync as gC,statSync as fC}from"node:fs";var _C=200*1024*1024,da=.7,ua=.5,xg=ua,hC=[{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"}],EC=["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 Rg(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 bC(e,t){let n=t-e;if(n<0)return{weight:0,label:null};for(let s of hC)if(n<=s.maxGapMs)return{weight:s.weight,label:s.label};return{weight:0,label:null}}function SC(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 EC)if(o.includes(i))return{weight:t[s],matched:i,matchedIndex:s}}return{weight:0,matched:null,matchedIndex:-1}}function la(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 yC(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=la(s,r);o>n&&(n=o)}return n}function wC(e,t){let n=la(e.mean_embedding,t.mean_embedding),s=la(e.tail_pool,t.head_pool),r=yC(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 TC(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 RC(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 kC(e,t){let n=Rg(e),s=Rg(t);return n&&s&&n===s?{weight:.1,brand:n}:{weight:0,brand:null}}function kg(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function xC(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(`
|
|
1897
|
+
`).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=kg(t.recent_user_messages[0]);if(o.length>=200)for(let i of e.authored_content){let a=kg(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 CC(e,t,n=xg){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=bC(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=SC(o),a=RC(e.touched_files,t.touched_files),d=kC(e.auto_title,t.auto_title),l=wC(e,t),u=TC(e,t),m=xC(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 Cg(e,t=xg){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=CC(a,r,t);d&&(!o||d.confidence>o.confidence)&&(o=d)}o&&n.push(o)}return n}function Lg(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 Ag(e,t={}){let n=t.maxUserMessages??5,s=t.userMessageMaxLen??2e3,r=new Set,o=[],i=new Set,a=[],d;try{if(fC(e).size>_C)return{touched_files:r,recent_user_messages:o,authored_paths:i,authored_content:a};d=gC(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(`
|
|
1898
|
+
`,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??{},y=typeof S.file_path=="string"?S.file_path:null;if(y){let T=Rr(y);T&&r.add(T)}if((b.name==="Write"||b.name==="Edit"||b.name==="MultiEdit")&&y){let T=Rr(y);T&&i.add(T);let R=typeof S.content=="string"?S.content:typeof S.new_string=="string"?S.new_string:null;R&&R.length>=200&&a.push(R.length>4096?R.slice(0,4096):R)}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 R=Rr(T[1]);R&&r.add(R)}if((b.name==="Glob"||b.name==="Grep")&&typeof S.pattern=="string"){let T=Rr(S.pattern);T&&!T.includes("*")&&r.add(T)}}}return{touched_files:r,recent_user_messages:o,authored_paths:i,authored_content:a}}function Rr(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}k();var Ng=10,Og=20;function LC(e){if(!e)return!1;let t=e.split(`
|
|
1899
|
+
`);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 AC(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 vg(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 pa(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 vg(n)?n:null}function Ig(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,
|
|
1873
1900
|
cm.text AS text, v.embedding AS embedding
|
|
1874
1901
|
FROM chunk_meta cm
|
|
1875
1902
|
JOIN vec_chunks v ON v.rowid = cm.rowid
|
|
1876
1903
|
WHERE cm.stale = 0
|
|
1877
1904
|
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(
|
|
1879
|
-
`),d=
|
|
1905
|
+
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=>!LC(T.text)),l=d.length>0?d:a,u=[];for(let T of l){let R=AC(T.embedding);R&&vg(R)&&u.push(R)}if(u.length===0)continue;let m=Math.min(Ng,u.length),p=Math.max(0,u.length-Ng),g=u.slice(0,m),f=u.slice(p),h=pa(g),_=pa(f),b=pa(u),S=new Map;for(let T=0;T<g.length&&S.size<Og;T++)S.set(T,g[T]);for(let T=0;T<f.length&&S.size<Og;T++)S.set(p+T,f[T]);let y=Array.from(S.values());t.set(i,{session_id:i,full_mean:b,head_pool:h,tail_pool:_,sample_chunks:y})}return t}function NC(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 Mg(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++)NC(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 Fn={lo:.4,hi:.7},kr="claude-haiku-4-5-20251001",Dg=50;var OC={same_workflow:.15,unrelated:-.2,unsure:0};function $g(e,t,n=Fn){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 vC(e,t){let n=e.replace(/\s+/g," ").trim();return n.length>t?n.slice(0,t-1)+"\u2026":n}function IC(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 MC(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=>` - ${vC(u,500)}`).join(`
|
|
1906
|
+
`),d=IC(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(`
|
|
1880
1907
|
`),` 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(`
|
|
1881
|
-
`)}function
|
|
1908
|
+
`)}function DC(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:OC[i]}}async function Pg(e,t={}){if(t.signal?.aborted)return null;let n=t.model??kr,s=MC(e),r=t.spawn;if(!r)try{let i=await Promise.resolve().then(()=>(Ye(),ys));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:DC(o.stdout)}function Fg(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 jg(e){return`${e.parent_id}::${e.child_id}`}async function PC(e){if(e.signal?.aborted)return null;let t,n;try{({spawnClaudePrompt:t,isClaudeCliAvailable:n}=await Promise.resolve().then(()=>(Ye(),ys)))}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:
|
|
1882
1909
|
- 4 to 8 words
|
|
1883
1910
|
- Title-case, no trailing punctuation
|
|
1884
1911
|
- Capture the WORKFLOW (e.g., "Update Remotion deps + lint cleanup"), not any one session
|
|
@@ -1889,7 +1916,7 @@ Sessions:
|
|
|
1889
1916
|
`+s.join(`
|
|
1890
1917
|
`)+`
|
|
1891
1918
|
|
|
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
|
|
1919
|
+
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 FC(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=Ig(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=Mg(a,.8),l=[];for(let u of i){let m=UC(u,s,d);m&&l.push(m)}r.set(o,l)}return{byProject:t,scannablesByProject:r}}function jC(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,
|
|
1893
1920
|
s.first_user_message, s.auto_title,
|
|
1894
1921
|
NULLIF(sa.alias, '') AS alias,
|
|
1895
1922
|
s.file_path
|
|
@@ -1897,40 +1924,40 @@ Return ONLY the workflow name on a single line.`;try{let o=t(r,[],e.model?{model
|
|
|
1897
1924
|
JOIN projects p ON p.id = s.project_id
|
|
1898
1925
|
LEFT JOIN session_aliases sa ON sa.session_id = s.id
|
|
1899
1926
|
WHERE ${s}
|
|
1900
|
-
ORDER BY p.name ASC, s.started_at ASC`).all(n)}function
|
|
1927
|
+
ORDER BY p.name ASC, s.started_at ASC`).all(n)}function UC(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=Ag(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 xr(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 Ug(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 Bg(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 BC(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??Fn,n=e.rescore.cap??Dg,s=e.rescore.model??kr,r=new Map(e.scannables.map(p=>[p.id,p])),o=$g(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 Pg(g,{model:s,signal:e.signal});f?(i.set(jg(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:Fg({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 HC(e){let t=E(),n=new Map(e.rows.map(l=>[l.id,l])),s=Lg(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=Bg(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 PC({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-${$C()}`,p=i.get(l.rootId)??Bg(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
|
|
1901
1928
|
(thread_id, session_id, parent_session_id, role, confidence, source, added_at)
|
|
1902
1929
|
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
|
|
1903
1930
|
(thread_id, session_id, parent_session_id, role, confidence, source, added_at)
|
|
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
|
|
1931
|
+
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 Hg(e){let t=!!e.apply,n=e.confidenceMin?Number(e.confidenceMin):t?da:ua;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):Fn.lo,hi:e.rescoreBandHi?Number(e.rescoreBandHi):Fn.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??kr,i=jC({project:e.project});if(i.length===0){console.log(c.dim("no sessions matched"));return}let{byProject:a,scannablesByProject:d}=FC(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=Cg(f,l);if(s&&h.length>0){let _=await BC({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 HC({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:xr(_.get(b.parent_id)),child_id:b.child_id,child_label:xr(_.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,y)=>{let T=_.get(S.child_id),R=_.get(y.child_id);return(T.started_at??"").localeCompare(R.started_at??"")});for(let S of b){let y=_.get(S.parent_id),T=_.get(S.child_id),R=S.confidence.toFixed(2);console.log(` ${c.dim(Ug(y.started_at).padEnd(16))} ${c.bold("PARENT")} ${xr(y)}`),console.log(` ${c.dim(Ug(T.started_at).padEnd(16))} ${c.bold(" \u2514\u2500 child")} ${xr(T)} ${c.dim(`[conf ${R}]`)}`),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 ${da}).`))}$();D();import{existsSync as WC,readFileSync as XC}from"node:fs";function GC(){let e=`${C}/daemon.port`;if(!WC(e))return null;try{let t=XC(e,"utf8").trim();return/^\d+$/.test(t)?t:null}catch{return null}}async function JC(e){try{return(await nt(`http://127.0.0.1:${e}/api/health`,{signal:AbortSignal.timeout(1e4)})).ok}catch{return!1}}async function zC(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 YC(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 qC(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 Wg(e){return e.alias||e.auto_title||(e.first_user_message?Q(e.first_user_message,60):"(no title)")}function VC(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 KC(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=VC(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))} ${Wg(u)}`);let b=" ".repeat(d+2);if(g&&o.has(g.parent_id)){let S=g.confidence.toFixed(2),y=Wg(e.candidates.find(T=>T.session_id===g.parent_id));console.log(` ${b}${" ".repeat(28)} parent ${c.dim(g.parent_id.slice(0,8))} (${y}) ${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 QC(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 Xg(e){let t=GC();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 JC(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 zC(t)}catch(l){console.error(c.err(l.message)),process.exitCode=1;return}let r;if(e.project){if(r=qC(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=YC(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 ut("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}KC(d.plan,i),i==="apply"&&d.result?QC(d.result):i==="preflight"&&(console.log(),console.log(c.dim(" This is a PREFLIGHT. To write: re-run without --preflight.")))}k();import{writeFileSync as yL,existsSync as wL,mkdirSync as TL}from"node:fs";import{join as df}from"node:path";import{homedir as RL}from"node:os";import{execSync as rn}from"node:child_process";Ge();import hL from"satori";import EL from"sharp";import{readFileSync as ha}from"node:fs";import{join as Dr}from"node:path";var Ea=Dr(te(),"dist","share","fonts"),af=!1,cf=[];function bL(){return af||(cf=[{name:"Inter",data:ha(Dr(Ea,"Inter-Regular.woff")),weight:400,style:"normal"},{name:"Inter",data:ha(Dr(Ea,"Inter-Bold.woff")),weight:700,style:"normal"},{name:"JetBrains Mono",data:ha(Dr(Ea,"JetBrainsMono-Regular.woff")),weight:400,style:"normal"}],af=!0),cf}async function SL(e){switch(e){case"A":return await Promise.resolve().then(()=>(Yg(),zg));case"B":return await Promise.resolve().then(()=>(Kg(),Vg));case"C":return await Promise.resolve().then(()=>(ef(),Zg));case"D":return await Promise.resolve().then(()=>(sf(),nf));case"E":return await Promise.resolve().then(()=>(of(),rf))}}async function $r(e,t){let s=(await SL(e)).render(t),i=await hL(s,{width:1080,height:e==="E"?1920:1350,fonts:bL()});return await EL(Buffer.from(i)).png({quality:90}).toBuffer()}function lf(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 kL(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.
|
|
1932
|
+
`),null)}function xL(e){return e.prepare("SELECT session_id FROM recall_events ORDER BY recalled_at DESC LIMIT 1").get()?.session_id??null}function CL(e,t){let n=e.prepare(`
|
|
1906
1933
|
SELECT s.id, s.started_at, s.message_count, s.first_user_message, s.auto_title,
|
|
1907
1934
|
s.total_input_tokens, s.total_output_tokens
|
|
1908
1935
|
FROM sessions s WHERE s.id = ?
|
|
1909
1936
|
`).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(`
|
|
1910
1937
|
SELECT tool_names, raw_json FROM messages
|
|
1911
1938
|
WHERE session_id = ? AND tool_names IS NOT NULL AND tool_names != ''
|
|
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
|
|
1913
|
-
`),process.exitCode=1;return}}else if(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
|
|
1915
|
-
`),t.clipboard&&(
|
|
1916
|
-
`)),t.link){let u=
|
|
1917
|
-
`),process.platform==="darwin"&&(
|
|
1918
|
-
`))}t.open!==!1&&
|
|
1939
|
+
`).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 LL(e){process.platform==="darwin"?rn(`osascript -e 'set the clipboard to (read (POSIX file "${e}") as \xABclass PNGf\xBB)'`):process.platform==="linux"&&rn(`xclip -selection clipboard -t image/png -i "${e}"`)}function AL(e){try{process.platform==="darwin"?rn(`open "${e}"`):process.platform==="linux"?rn(`xdg-open "${e}"`):process.platform==="win32"&&rn(`start "" "${e}"`)}catch{}}async function uf(e,t){let n=E(),s;if(e){if(s=kL(n,e),!s){process.stderr.write(`session not found: ${e}
|
|
1940
|
+
`),process.exitCode=1;return}}else if(s=xL(n),!s){process.stderr.write("No recalled sessions yet. Run `recall context <id>` first, then `recall share`.\n"),process.exitCode=1;return}let r=CL(n,s);if(!r){process.stderr.write(`failed to load metadata for session ${s}
|
|
1941
|
+
`),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 $r(o,r),a=s.slice(0,8),d=t.out??df(RL(),"Downloads");wL(d)||TL(d,{recursive:!0});let l=df(d,`recall-card-${a}.png`);if(yL(l,i),process.stderr.write(`Card saved to ${l}
|
|
1942
|
+
`),t.clipboard&&(LL(l),process.stderr.write(`Copied PNG to clipboard.
|
|
1943
|
+
`)),t.link){let u=lf(r,o);process.stdout.write(u+`
|
|
1944
|
+
`),process.platform==="darwin"&&(rn("pbcopy",{input:u}),process.stderr.write(`Link copied to clipboard.
|
|
1945
|
+
`))}t.open!==!1&&AL(l)}k();import{createInterface as NL}from"node:readline";import{writeFileSync as OL,existsSync as vL,mkdirSync as IL}from"node:fs";import{join as mf}from"node:path";import{homedir as ML}from"node:os";function pf(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 $t=["january","february","march","april","may","june","july","august","september","october","november","december"],DL=["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"];function $L(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:`${$t[a][0].toUpperCase()}${$t[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:`${$t[a][0].toUpperCase()}${$t[a].slice(1)} ${i}`}}let s=$t.indexOf(e.toLowerCase()),r=s===-1?DL.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:`${$t[o][0].toUpperCase()}${$t[o].slice(1)} ${i}`}}return null}function PL(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 gf(e,t){let n=$L(e);if(!n){process.stderr.write(`invalid month format: ${e}. Use YYYY-MM, month name, or abbreviation.
|
|
1919
1946
|
`),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
|
|
1920
1947
|
WHERE recalled_at >= ? AND recalled_at < ?
|
|
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),
|
|
1948
|
+
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),x=s.prepare("SELECT auto_title, first_user_message, id FROM sessions WHERE id = ?").get(a.session_id);d=O?.alias||x?.auto_title||x?.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
|
|
1922
1949
|
FROM sessions WHERE started_at >= ? AND started_at < ?
|
|
1923
|
-
GROUP BY h ORDER BY c DESC LIMIT 3`).all(n.start,n.end),m=
|
|
1950
|
+
GROUP BY h ORDER BY c DESC LIMIT 3`).all(n.start,n.end),m=PL(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
1951
|
FROM sessions WHERE started_at >= ? AND started_at < ?`).get(n.start,n.end),f=s.prepare(`SELECT COUNT(*) AS c FROM sessions
|
|
1925
1952
|
WHERE started_at >= ? AND started_at < ?
|
|
1926
1953
|
AND (CAST(strftime('%H', started_at) AS INTEGER) >= 22
|
|
1927
1954
|
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
|
|
1928
1955
|
FROM session_tags st JOIN sessions s ON s.id = st.session_id
|
|
1929
1956
|
WHERE s.started_at >= ? AND s.started_at < ?
|
|
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=
|
|
1931
|
-
`)}ge();
|
|
1932
|
-
`);let t=await
|
|
1933
|
-
`);let o=await
|
|
1957
|
+
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=pf(_),S=50;try{let{computeAllHealthScores:O}=await Promise.resolve().then(()=>(ta(),Im)),x=O();x.length>0&&(S=Math.round(x.reduce((I,F)=>I+F.score,0)/x.length))}catch{}let y=t.verdict??"";if(!t.verdict){let O=NL({input:process.stdin,output:process.stderr});y=await new Promise(x=>{O.question("Add your take (one line, or press Enter to skip): ",I=>{O.close(),x(I.trim())})})}let T={month:n.label,totalRecalls:r,totalTokensPiped:o,sessionsIndexed:i,mostRecalledSession:d,biggestRecallTokens:l,healthScore:S,mostActiveHours:m,archetype:b,verdict:y},R=await $r("E",T),B=n.start.slice(0,7),L=t.out??mf(ML(),"Downloads");vL(L)||IL(L,{recursive:!0});let v=mf(L,`recall-wrapped-${B}.png`);OL(v,R),process.stderr.write(`Wrapped card saved to ${v}
|
|
1958
|
+
`)}ge();Bt();Ge();import{createRequire as FL}from"node:module";import{createInterface as jL}from"node:readline";import{stdin as UL,stdout as BL}from"node:process";var HL=FL(import.meta.url),WL=HL(`${te()}/package.json`).version,hf="https://clauderecall.com/api/feedback",ba=process.env.RECALL_FEEDBACK_API??hf,ff=ba===hf,Sa=2e3;function Ef(e){let t=jL({input:UL,output:BL});return new Promise(n=>t.question(e,s=>{t.close(),n(s.trim())}))}function _f(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 XL(e){if(e.score!==void 0){let s=_f(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 Ef("Score (1-5, q to quit): ");if(t.toLowerCase()==="q"||t==="")return console.log("No worries, skipped."),null;let n=_f(t);return n===null?(console.error("Please enter an integer 1 through 5."),null):n}async function GL(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 Ef(n)}function JL(e){return e.length<=Sa?{value:e,truncated:!1}:{value:e.slice(0,Sa),truncated:!0}}async function bf(e={}){ff||process.stderr.write(`[recall] RECALL_FEEDBACK_API override active (${ba}); license token will NOT be sent.
|
|
1959
|
+
`);let t=await XL(e);if(t===null)return e.score!==void 0||!process.stdin.isTTY?1:0;let n=await GL(e,t),{value:s,truncated:r}=JL(n);r&&process.stderr.write(`[recall] --message truncated to ${Sa} characters before send.
|
|
1960
|
+
`);let o=await Le(),i=pn(),a=ff&&o.tier==="pro"&&i?i.license_jwt:null,d={score:t,comment:s.length>0?s:null,surface:"cli",version:WL,os:process.platform,trigger_kind:"manual",license_jwt:a};try{let l=await fetch(ba,{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 GA=WA(import.meta.url),Gn=GA("../package.json").version,N=new XA;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(Gn);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 pc(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=>{mc(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)=>{bc(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 Mc(e.join(" "),t)});N.command("projects").description("List every project with how many sessions are in each.").action(()=>{Wc()});N.command("status").description("Show database size, session counts, and whether the daemon is running.").action(()=>{Hc()});N.command("start").description("Start the background daemon. Required for live tab-name tracking and the web UI.").action(async()=>{await ds()});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 Qc({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 Zc()});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(()=>(Qf(),Kf));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 wl(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 Tl({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 fs({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 Rl(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 kl(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 Cl(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 Il(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), verify-backup (on-demand orphan check of the migration rollback backup; slow on large backups).').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",`
|
|
1934
1961
|
Two lanes:
|
|
1935
1962
|
Tier-2 (default, recommended): on-device 768-d embeddings via bge-base-en-v1.5
|
|
1936
1963
|
+ sqlite-vec. No network, no token spend. Powered by:
|
|
@@ -1946,7 +1973,7 @@ Two lanes:
|
|
|
1946
1973
|
Reindex chunks-per-session cap (advanced):
|
|
1947
1974
|
Set RECALL_REINDEX_MAX_CHUNKS=200 to cap each session at 200 chunks for a
|
|
1948
1975
|
faster first pass. Default is 0 (no cap, full accuracy).
|
|
1949
|
-
`).action(async(e,t,n)=>{await
|
|
1950
|
-
`)}},r=performance.now(),{requireProOrExit:o}=await Promise.resolve().then(()=>(ge(),
|
|
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
|
|
1976
|
+
`).action(async(e,t,n)=>{await Yu(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 Zu(e,t)});var Jn=N.command("infer").description("Discover links between sessions. Subcommands: outputs | citations | l1 | links | bug-patterns.");function s_(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 ip(t)})}function r_(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 fp(t)})}function o_(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 Ep(t)})}function i_(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 kp(t)})}function a_(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 $p(t)})}s_(Jn.command("outputs"));r_(Jn.command("citations"));o_(Jn.command("l1"));i_(Jn.command("links"));a_(Jn.command("bug-patterns"));s_(N.command("extract-outputs"));r_(N.command("infer-citations"));o_(N.command("infer-l1"));i_(N.command("infer-links"));a_(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 Yp(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
|
|
1977
|
+
`)}},r=performance.now(),{requireProOrExit:o}=await Promise.resolve().then(()=>(ge(),gn));await o("Similar sessions"),s("requireProOrExit",r);let i=performance.now(),{isModelInstalled:a}=await Promise.resolve().then(()=>(wn(),rd)),{loadEmbedder:d,getEmbedderStatus:l}=await Promise.resolve().then(()=>(Pe(),Ms)),{findSimilarSessions:u}=await Promise.resolve().then(()=>(n_(),t_));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
|
|
1978
|
+
`);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 tm(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 nm(e,t)});var Da=N.command("correlator").description("Diagnose and fix terminal-to-session matching. Subcommands: audit | debug | restore.");function c_(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 wm(t)})}function l_(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 Tm(t)})}function d_(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 sm(t)})}c_(Da.command("debug"));l_(Da.command("restore"));d_(Da.command("audit"));c_(N.command("correlator-debug"));l_(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 Em(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 wi(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 rm(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 im(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 hm({_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)});d_(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 Rm(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 Cm(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 vm(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)=>{Dm(e,t)});N.command("verify [action]").description('Toggle verification badges in the UI. Defaults to "status". Actions: on, off, status.').action(e=>{$m(e)});N.command("activate <license-key>").description("Activate your Pro license. Run once after purchase.").action(async e=>{await Gm(e)});N.command("upgrade").description("Open the Pro pricing page in your browser.").action(async()=>{await zm()});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 qm(e)});var Br=N.command("telemetry").description('Manage the opt-in anonymous install ping. Defaults to "view".');Br.command("view",{isDefault:!0}).description("Show current decision, state file path, and next-ping payload.").action(async()=>{await sg(Gn)});Br.command("on").description("Opt in. One anonymous ping per machine per month. See https://clauderecall.com/telemetry").action(async()=>{await tg()});Br.command("off").description("Opt out. Deletes the nonce. No further pings.").action(async()=>{await ng()});Br.command("status").description("Alias for view.").action(async()=>{await aa(Gn)});var $a=N.command("license").description('Show or remove the Pro license on this machine. Defaults to "status".');$a.command("status",{isDefault:!0}).description("Show the current license tier.").action(async()=>{await cg()});$a.command("deactivate").description("Remove the stored license from this machine. Reverses `recall activate`.").action(()=>{dg()});$a.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 lg()});var JA=N.command("thread").description("Group related sessions into threads, then list, show, link, merge, or scan them."),zA=N.command("threads").description("Alias for `thread`. Both forms accept the same subcommands.");function u_(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 Xg(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=>{Hg(t)}),e.command("list").description("List threads, newest first.").option("--archived","include archived threads").option("--json","emit JSON instead of a table").action(t=>{pg(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)=>{mg(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)=>{gg(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)=>{fg(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)=>{_g(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)=>{hg(t,n)}),e.command("rename <id-prefix> <new-name>").description("Rename a thread.").option("--json","emit JSON").action((t,n,s)=>{Eg(t,n,s)}),e.command("close <id-prefix>").description("Mark a thread as closed (work complete).").option("--json","emit JSON").action((t,n)=>{bg(t,n)}),e.command("reopen <id-prefix>").description("Reopen a closed thread.").option("--json","emit JSON").action((t,n)=>{Sg(t,n)}),e.command("archive <id-prefix>").description("Archive a thread (hidden from the default list).").option("--json","emit JSON").action((t,n)=>{yg(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)=>{wg(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)=>{Tg(t,n)})}u_(JA);u_(zA);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 uf(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 bf(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 gf(e,t)});process.env.CLAUDECODE&&!process.stdout.isTTY&&process.stderr.write(`<claude-code-hint v="1" type="plugin" value="claude-recall@claude-plugins-official" />
|
|
1979
|
+
`);N.action(async()=>{await Xm()});N.hook("preAction",async(e,t)=>{let n=t.name();try{await og(n,Gn)}catch{}eg(Gn).catch(()=>{});try{ag({commandName:n})}catch{}});N.parseAsync(process.argv).catch(e=>{console.error(e),process.exit(1)});
|