@clauderecallhq/cli 0.76.3 → 0.77.1

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.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /* Claude Recall (proprietary). See LICENSE for terms. */
3
- var Gr=Object.defineProperty;var b=(e,t)=>()=>(e&&(t=e(e=0)),t);var Ge=(e,t)=>{for(var s in t)Gr(e,s,{get:t[s],enumerable:!0})};import{createRequire as Yr}from"node:module";var zr,Jr,Kr,Ye,ze,Wt,Xt=b(()=>{"use strict";{let e=process.emit.bind(process);process.emit=function(t,...s){let n=s[0];return t==="warning"&&n instanceof Error&&n.name==="ExperimentalWarning"&&/SQLite/i.test(n.message)?!1:e(t,...s)}}zr=Yr(import.meta.url),Jr=["node","sqlite"].join(":"),Kr=zr(Jr),Ye=class{inner;constructor(t){this.inner=t}get(...t){return t.length===0?this.inner.get():this.inner.get(...t)}all(...t){return t.length===0?this.inner.all():this.inner.all(...t)}run(...t){let s=t.length===0?this.inner.run():this.inner.run(...t);return{changes:s.changes,lastInsertRowid:s.lastInsertRowid}}iterate(...t){return t.length===0?this.inner.iterate():this.inner.iterate(...t)}},ze=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,s={}){this.inner=new Kr.DatabaseSync(t,{readOnly:s.readonly??!1,allowExtension:!0})}prepare(t){return new Ye(this.inner.prepare(t))}exec(t){this.inner.exec(t)}close(){this.inner.close()}pragma(t,s={}){if(t.includes("=")){this.inner.exec(`PRAGMA ${t}`);return}if(s.simple){let n=this.inner.prepare(`PRAGMA ${t}`).get();return n&&typeof n=="object"?Object.values(n)[0]:void 0}return this.inner.prepare(`PRAGMA ${t}`).all()}transaction(t){return((...n)=>{this.txDepth===0?this.inner.exec("BEGIN"):this.inner.exec(`SAVEPOINT sp_${this.txDepth}`),this.txDepth+=1;try{let r=t(...n);return this.txDepth-=1,this.txDepth===0?this.inner.exec("COMMIT"):this.inner.exec(`RELEASE sp_${this.txDepth}`),r}catch(r){this.txDepth-=1;try{this.txDepth===0?this.inner.exec("ROLLBACK"):(this.inner.exec(`ROLLBACK TO sp_${this.txDepth}`),this.inner.exec(`RELEASE sp_${this.txDepth}`))}catch{}throw r}})}loadExtension(t,s){this.extensionLoadingEnabled||(this.inner.enableLoadExtension(!0),this.extensionLoadingEnabled=!0),s===void 0?this.inner.loadExtension(t):this.inner.loadExtension(t,s)}},Wt=ze});import{homedir as Gt}from"node:os";import{join as Je,basename as hl}from"node:path";import{existsSync as Vr,mkdirSync as qr,chmodSync as Zr,readdirSync as Tl,statSync as Sl}from"node:fs";function v(){Vr(S)||qr(S,{recursive:!0,mode:448}),process.platform!=="win32"&&Zr(S,448)}var Ke,S,me,M=b(()=>{"use strict";Ke=process.env.CLAUDE_PROJECTS_DIR?process.env.CLAUDE_PROJECTS_DIR:Je(Gt(),".claude","projects"),S=process.env.RECALL_HOME?process.env.RECALL_HOME:Je(Gt(),".recall"),me=Je(S,"db.sqlite")});function zt(e){let t=e.prepare("PRAGMA table_info(sessions)").all(),s=new Set(t.map(g=>g.name)),n=[["total_input_tokens","INTEGER"],["total_output_tokens","INTEGER"],["total_cache_create_tokens","INTEGER"],["total_cache_read_tokens","INTEGER"],["primary_model","TEXT"],["auto_title","TEXT"],["auto_title_source","TEXT"],["auto_title_generated_at","INTEGER"],["auto_title_history","TEXT"],["verification_status","TEXT"],["verification_computed_at","INTEGER"],["title_quality","TEXT"],["title_quality_computed_at","INTEGER"],["archive_status","TEXT NOT NULL DEFAULT 'live'"],["archived_at","TEXT"]];for(let[g,_]of n)s.has(g)||e.exec(`ALTER TABLE sessions ADD COLUMN ${g} ${_}`);let r=e.prepare("PRAGMA table_info(collection_sessions)").all(),i=new Set(r.map(g=>g.name)),o=[["source","TEXT NOT NULL DEFAULT 'manual'"],["rule_id","TEXT"]];for(let[g,_]of o)i.has(g)||e.exec(`ALTER TABLE collection_sessions ADD COLUMN ${g} ${_}`);let a=e.prepare("PRAGMA table_info(session_notes)").all(),c=new Set(a.map(g=>g.name)),d=[["auto_synopsis","TEXT"],["auto_synopsis_generated_at","INTEGER"],["auto_synopsis_history","TEXT"]];for(let[g,_]of d)c.has(g)||e.exec(`ALTER TABLE session_notes ADD COLUMN ${g} ${_}`);let l=e.prepare("PRAGMA table_info(threads)").all();new Set(l.map(g=>g.name)).has("folder_id")||(e.exec("ALTER TABLE threads ADD COLUMN folder_id TEXT REFERENCES thread_folders(id) ON DELETE SET NULL"),e.exec("CREATE INDEX IF NOT EXISTS idx_threads_folder ON threads(folder_id)"));let u=e.prepare("PRAGMA table_info(thread_folders)").all(),p=new Set(u.map(g=>g.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(`
3
+ var Yr=Object.defineProperty;var b=(e,t)=>()=>(e&&(t=e(e=0)),t);var Ke=(e,t)=>{for(var s in t)Yr(e,s,{get:t[s],enumerable:!0})};import{createRequire as zr}from"node:module";var Jr,Kr,Vr,Ve,qe,qt,Zt=b(()=>{"use strict";{let e=process.emit.bind(process);process.emit=function(t,...s){let n=s[0];return t==="warning"&&n instanceof Error&&n.name==="ExperimentalWarning"&&/SQLite/i.test(n.message)?!1:e(t,...s)}}Jr=zr(import.meta.url),Kr=["node","sqlite"].join(":"),Vr=Jr(Kr),Ve=class{inner;constructor(t){this.inner=t}get(...t){return t.length===0?this.inner.get():this.inner.get(...t)}all(...t){return t.length===0?this.inner.all():this.inner.all(...t)}run(...t){let s=t.length===0?this.inner.run():this.inner.run(...t);return{changes:s.changes,lastInsertRowid:s.lastInsertRowid}}iterate(...t){return t.length===0?this.inner.iterate():this.inner.iterate(...t)}},qe=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,s={}){this.inner=new Vr.DatabaseSync(t,{readOnly:s.readonly??!1,allowExtension:!0})}prepare(t){return new Ve(this.inner.prepare(t))}exec(t){this.inner.exec(t)}close(){this.inner.close()}pragma(t,s={}){if(t.includes("=")){this.inner.exec(`PRAGMA ${t}`);return}if(s.simple){let n=this.inner.prepare(`PRAGMA ${t}`).get();return n&&typeof n=="object"?Object.values(n)[0]:void 0}return this.inner.prepare(`PRAGMA ${t}`).all()}transaction(t){return((...n)=>{this.txDepth===0?this.inner.exec("BEGIN"):this.inner.exec(`SAVEPOINT sp_${this.txDepth}`),this.txDepth+=1;try{let r=t(...n);return this.txDepth-=1,this.txDepth===0?this.inner.exec("COMMIT"):this.inner.exec(`RELEASE sp_${this.txDepth}`),r}catch(r){this.txDepth-=1;try{this.txDepth===0?this.inner.exec("ROLLBACK"):(this.inner.exec(`ROLLBACK TO sp_${this.txDepth}`),this.inner.exec(`RELEASE sp_${this.txDepth}`))}catch{}throw r}})}loadExtension(t,s){this.extensionLoadingEnabled||(this.inner.enableLoadExtension(!0),this.extensionLoadingEnabled=!0),s===void 0?this.inner.loadExtension(t):this.inner.loadExtension(t,s)}},qt=qe});import{homedir as Qt}from"node:os";import{join as Ze,basename as xl}from"node:path";import{existsSync as qr,mkdirSync as Zr,chmodSync as Qr,readdirSync as kl,statSync as Cl}from"node:fs";function k(){qr(S)||Zr(S,{recursive:!0,mode:448}),process.platform!=="win32"&&Qr(S,448)}var Qe,S,ge,D=b(()=>{"use strict";Qe=process.env.CLAUDE_PROJECTS_DIR?process.env.CLAUDE_PROJECTS_DIR:Ze(Qt(),".claude","projects"),S=process.env.RECALL_HOME?process.env.RECALL_HOME:Ze(Qt(),".recall"),ge=Ze(S,"db.sqlite")});function ts(e){let t=e.prepare("PRAGMA table_info(sessions)").all(),s=new Set(t.map(g=>g.name)),n=[["total_input_tokens","INTEGER"],["total_output_tokens","INTEGER"],["total_cache_create_tokens","INTEGER"],["total_cache_read_tokens","INTEGER"],["primary_model","TEXT"],["auto_title","TEXT"],["auto_title_source","TEXT"],["auto_title_generated_at","INTEGER"],["auto_title_history","TEXT"],["verification_status","TEXT"],["verification_computed_at","INTEGER"],["title_quality","TEXT"],["title_quality_computed_at","INTEGER"],["archive_status","TEXT NOT NULL DEFAULT 'live'"],["archived_at","TEXT"],["skipped_reason","TEXT"]];for(let[g,_]of n)s.has(g)||e.exec(`ALTER TABLE sessions ADD COLUMN ${g} ${_}`);let r=e.prepare("PRAGMA table_info(collection_sessions)").all(),i=new Set(r.map(g=>g.name)),o=[["source","TEXT NOT NULL DEFAULT 'manual'"],["rule_id","TEXT"]];for(let[g,_]of o)i.has(g)||e.exec(`ALTER TABLE collection_sessions ADD COLUMN ${g} ${_}`);let a=e.prepare("PRAGMA table_info(session_notes)").all(),c=new Set(a.map(g=>g.name)),d=[["auto_synopsis","TEXT"],["auto_synopsis_generated_at","INTEGER"],["auto_synopsis_history","TEXT"]];for(let[g,_]of d)c.has(g)||e.exec(`ALTER TABLE session_notes ADD COLUMN ${g} ${_}`);let l=e.prepare("PRAGMA table_info(threads)").all();new Set(l.map(g=>g.name)).has("folder_id")||(e.exec("ALTER TABLE threads ADD COLUMN folder_id TEXT REFERENCES thread_folders(id) ON DELETE SET NULL"),e.exec("CREATE INDEX IF NOT EXISTS idx_threads_folder ON threads(folder_id)"));let u=e.prepare("PRAGMA table_info(thread_folders)").all(),p=new Set(u.map(g=>g.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 Gr=Object.defineProperty;var b=(e,t)=>()=>(e&&(t=e(e=0)),t);var Ge=(e,t)=>{f
12
12
  );
13
13
  CREATE INDEX IF NOT EXISTS idx_message_embeddings_session ON message_embeddings(session_id);
14
14
  CREATE INDEX IF NOT EXISTS idx_message_embeddings_generated ON message_embeddings(generated_at DESC);
15
- `)}var Yt,Jt=b(()=>{"use strict";Yt=`
15
+ `)}var es,ss=b(()=>{"use strict";es=`
16
16
  PRAGMA journal_mode = WAL;
17
17
  PRAGMA synchronous = NORMAL;
18
18
  PRAGMA foreign_keys = ON;
@@ -668,8 +668,8 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_target
668
668
  ON bug_synthesis_results(scope, target_id, created_at DESC);
669
669
  CREATE INDEX IF NOT EXISTS idx_synth_results_created
670
670
  ON bug_synthesis_results(created_at DESC);
671
- `});import*as Kt from"sqlite-vec";function m(){if(C)return C;v(),C=new Wt(me),Kt.load(C),C.pragma("cache_size = -64000"),C.pragma("mmap_size = 268435456"),C.pragma("temp_store = MEMORY"),C.pragma("busy_timeout = 5000"),C.pragma("journal_size_limit = 67108864"),C.pragma("wal_autocheckpoint = 1000"),C.exec(Yt),zt(C);try{C.exec("PRAGMA optimize")}catch{}return C}function Vt(){if(C){try{C.exec("PRAGMA optimize")}catch{}try{C.exec("INSERT INTO messages_fts(messages_fts, rank) VALUES('merge', 4);")}catch{}try{C.exec("INSERT INTO sessions_fts(sessions_fts, rank) VALUES('merge', 4);")}catch{}try{C.pragma("wal_checkpoint(TRUNCATE)")}catch{}C.close(),C=null}}var C,R=b(()=>{"use strict";Xt();M();Jt();C=null});function Ve(e){if(e<60)return`${e}s`;if(e<3600)return`${Math.floor(e/60)}m`;if(e<86400)return`${Math.floor(e/3600)}h`;let t=Math.floor(e/86400),s=Math.floor(e%86400/3600);return s>0?`${t}d ${s}h`:`${t}d`}function ye(e){if(e instanceof Error)return e.stack??e.message;try{return JSON.stringify(e)}catch{return String(e)}}var we=b(()=>{"use strict"});import{writeFileSync as ii}from"node:fs";import{join as oi}from"node:path";function te(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function Re(e,t){let s=te(t);if(!s)throw new Error("tag must contain at least one alphanumeric character");let n=m(),r=new Date().toISOString();return n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,s)?{tag:s,added:!1}:(n.transaction(()=>{n.prepare("INSERT INTO session_tags (session_id, tag, created_at) VALUES (?, ?, ?)").run(e,s,r),n.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'add', ?)").run(e,s,r)})(),ts(),{tag:s,added:!0})}function Qt(e,t){let s=te(t);if(!s)return{tag:"",removed:!1};let n=m(),r=new Date().toISOString();return n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,s)?(n.transaction(()=>{n.prepare("DELETE FROM session_tags WHERE session_id = ? AND tag = ?").run(e,s),n.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'remove', ?)").run(e,s,r)})(),ts(),{tag:s,removed:!0}):{tag:s,removed:!1}}function Ne(e){return m().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function es(){return m().prepare(`SELECT tag, COUNT(*) AS count FROM session_tags
672
- GROUP BY tag ORDER BY count DESC, tag ASC`).all()}function ts(){try{v();let e=m(),t=e.prepare("SELECT session_id, tag, created_at FROM session_tags ORDER BY session_id, tag").all(),s=e.prepare("SELECT id, session_id, tag, action, at FROM tag_events ORDER BY at ASC, id ASC").all(),n={schema:"claude-recall.tags.v1",backed_up_at:new Date().toISOString(),current:t,events:s};ii(ai,JSON.stringify(n,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var ai,Le=b(()=>{"use strict";R();M();ai=oi(S,"tags.json")});function ci(e,t){let s=e.filter(i=>i.content_text&&i.content_text.trim().length>0);if(s.length<=t)return s;let n=new Set;n.add(0),n.add(s.length-1);let r=(s.length-2)/Math.max(1,t-2);for(let i=1;i<t-1;i++)n.add(Math.floor(i*r));return Array.from(n).sort((i,o)=>i-o).slice(0,t).map(i=>s[i])}function Ae(e){let t=m(),s={limit:e.limit??500},n=e.sessionIds&&e.sessionIds.length>0,r=n?"1=1":"s.message_count > 2";if(n){let o=e.sessionIds.map((a,c)=>`@sid_${c}`).join(", ");r+=` AND s.id IN (${o})`,e.sessionIds.forEach((a,c)=>{s[`sid_${c}`]=a})}return e.untaggedOnly&&(r+=" AND NOT EXISTS (SELECT 1 FROM session_tags st WHERE st.session_id = s.id)"),e.project&&(r+=" AND p.name = @project",s.project=e.project),e.collectionId&&(r+=" AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id = @col)",s.col=e.collectionId),t.prepare(`SELECT s.id, p.name AS project, s.git_branch,
671
+ `});import*as ns from"sqlite-vec";function m(){if(C)return C;k(),C=new qt(ge),ns.load(C),C.pragma("cache_size = -64000"),C.pragma("mmap_size = 268435456"),C.pragma("temp_store = MEMORY"),C.pragma("busy_timeout = 5000"),C.pragma("journal_size_limit = 67108864"),C.pragma("wal_autocheckpoint = 1000"),C.exec(es),ts(C);try{C.exec("PRAGMA optimize")}catch{}return C}function rs(){if(C){try{C.exec("PRAGMA optimize")}catch{}try{C.exec("INSERT INTO messages_fts(messages_fts, rank) VALUES('merge', 4);")}catch{}try{C.exec("INSERT INTO sessions_fts(sessions_fts, rank) VALUES('merge', 4);")}catch{}try{C.pragma("wal_checkpoint(TRUNCATE)")}catch{}C.close(),C=null}}var C,N=b(()=>{"use strict";Zt();D();ss();C=null});function we(e){if(e<60)return`${e}s`;if(e<3600)return`${Math.floor(e/60)}m`;if(e<86400)return`${Math.floor(e/3600)}h`;let t=Math.floor(e/86400),s=Math.floor(e%86400/3600);return s>0?`${t}d ${s}h`:`${t}d`}function Re(e){if(e instanceof Error)return e.stack??e.message;try{return JSON.stringify(e)}catch{return String(e)}}var Ne=b(()=>{"use strict"});import{writeFileSync as oi}from"node:fs";import{join as ai}from"node:path";function se(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function Le(e,t){let s=se(t);if(!s)throw new Error("tag must contain at least one alphanumeric character");let n=m(),r=new Date().toISOString();return n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,s)?{tag:s,added:!1}:(n.transaction(()=>{n.prepare("INSERT INTO session_tags (session_id, tag, created_at) VALUES (?, ?, ?)").run(e,s,r),n.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'add', ?)").run(e,s,r)})(),ls(),{tag:s,added:!0})}function as(e,t){let s=se(t);if(!s)return{tag:"",removed:!1};let n=m(),r=new Date().toISOString();return n.prepare("SELECT 1 FROM session_tags WHERE session_id = ? AND tag = ?").get(e,s)?(n.transaction(()=>{n.prepare("DELETE FROM session_tags WHERE session_id = ? AND tag = ?").run(e,s),n.prepare("INSERT INTO tag_events (session_id, tag, action, at) VALUES (?, ?, 'remove', ?)").run(e,s,r)})(),ls(),{tag:s,removed:!0}):{tag:s,removed:!1}}function Ae(e){return m().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function cs(){return m().prepare(`SELECT tag, COUNT(*) AS count FROM session_tags
672
+ GROUP BY tag ORDER BY count DESC, tag ASC`).all()}function ls(){try{k();let e=m(),t=e.prepare("SELECT session_id, tag, created_at FROM session_tags ORDER BY session_id, tag").all(),s=e.prepare("SELECT id, session_id, tag, action, at FROM tag_events ORDER BY at ASC, id ASC").all(),n={schema:"claude-recall.tags.v1",backed_up_at:new Date().toISOString(),current:t,events:s};oi(ci,JSON.stringify(n,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var ci,Oe=b(()=>{"use strict";N();D();ci=ai(S,"tags.json")});function li(e,t){let s=e.filter(i=>i.content_text&&i.content_text.trim().length>0);if(s.length<=t)return s;let n=new Set;n.add(0),n.add(s.length-1);let r=(s.length-2)/Math.max(1,t-2);for(let i=1;i<t-1;i++)n.add(Math.floor(i*r));return Array.from(n).sort((i,o)=>i-o).slice(0,t).map(i=>s[i])}function xe(e){let t=m(),s={limit:e.limit??500},n=e.sessionIds&&e.sessionIds.length>0,r=n?"1=1":"s.message_count > 2";if(n){let o=e.sessionIds.map((a,c)=>`@sid_${c}`).join(", ");r+=` AND s.id IN (${o})`,e.sessionIds.forEach((a,c)=>{s[`sid_${c}`]=a})}return e.untaggedOnly&&(r+=" AND NOT EXISTS (SELECT 1 FROM session_tags st WHERE st.session_id = s.id)"),e.project&&(r+=" AND p.name = @project",s.project=e.project),e.collectionId&&(r+=" AND s.id IN (SELECT session_id FROM collection_sessions WHERE collection_id = @col)",s.col=e.collectionId),t.prepare(`SELECT s.id, p.name AS project, s.git_branch,
673
673
  NULLIF(sa.alias, '') AS alias,
674
674
  COALESCE(s.first_user_message, '') AS first_user_message
675
675
  FROM sessions s
@@ -679,36 +679,36 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
679
679
  ORDER BY COALESCE(s.started_at, '') DESC
680
680
  LIMIT @limit`).all(s).map(o=>{let a=t.prepare(`SELECT role, COALESCE(content_text, '') AS content_text
681
681
  FROM messages WHERE session_id = ?
682
- ORDER BY COALESCE(timestamp, ''), rowid`).all(o.id),d=ci(a,5).map(l=>`${l.role}: ${l.content_text.slice(0,400)}`).join(`
682
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(o.id),d=li(a,5).map(l=>`${l.role}: ${l.content_text.slice(0,400)}`).join(`
683
683
  ---
684
- `);return{id:o.id,project:o.project,git_branch:o.git_branch,alias:o.alias,first_user_message:o.first_user_message,message_sample:d,current_tags:Ne(o.id)}})}var qe=b(()=>{"use strict";R();Le()});import{z as W}from"zod";function et(e){let t=e.minTags??2,s=e.maxTags??4,n=e.untaggedOnly??!e.sessionId,r=["Auto-tag my Claude Recall sessions using the MCP tools available to you.","","1. Call `list_tags` first to see which tags I already use (prefer those for consistency over inventing new ones).","2. Call `list_sessions_to_tag` with these filters:"],i=[];return e.sessionId?(i.push("limit: 1"),r.push(` ${i.join(", ")}`),r.push(` Then match the session id ${e.sessionId} from the returned list.`)):(n&&i.push("untaggedOnly: true"),e.project&&i.push(`project: "${e.project}"`),e.collectionId&&i.push(`collectionId: "${e.collectionId}"`),i.push(`limit: ${e.limit??100}`),r.push(` ${i.join(", ")}`)),r.push(""),r.push(`3. For each session returned, look at the alias, first user message, git branch, and sampled messages. Pick ${t}-${s} concise, lowercase, hyphen-separated tags describing:`),r.push(" - domain/subsystem (auth, db, frontend, billing, etc.)"),r.push(" - kind of work (bugfix, feature, refactor, research)"),r.push(" - prominent tools or libraries if relevant"),r.push(""),r.push("4. Call `apply_tags` once per session to write the tags."),r.push(""),r.push("Work through EVERY session returned \u2014 do not stop partway. When done, reply with a short summary of how many sessions were tagged. Do not ask clarifying questions; just do the work."),r.join(`
685
- `)}function Ei(e){let t=e.mode==="detailed";return[`Summarize Claude Recall session ${e.sessionId} using the MCP tools available to you.`,"",`1. Call \`context_for_session\` with id "${e.sessionId}" and mode "condensed" to get the transcript as markdown.`,t?"2. Write a 1-paragraph overview (\u22643 sentences) of what this session was for, then 5-8 bullet points covering:":"2. Write 3-5 bullet points covering:"," - What was accomplished (shipped, decided, learned)"," - What was tried and abandoned"," - Any explicit open questions or follow-ups","","Rules:",'- Be concrete. Name files, functions, and decisions. Avoid vague "discussed X".',"- If nothing was actually shipped or decided, say so plainly.",'- Reply with just the summary \u2014 no preamble, no "Here is the summary:".'].join(`
686
- `)}function Si(e){return[`Extract every architectural or product decision made in Claude Recall session ${e.sessionId}.`,"",`1. Call \`context_for_session\` with id "${e.sessionId}" and mode "full" (so tool I/O is included \u2014 sometimes the decision lives in a commit message or diff).`,"2. For each distinct decision, emit one block:",""," - **Decision:** one sentence."," - **Why:** the stated rationale (quote if possible).",' - **Alternatives considered:** bullet list, or "none mentioned".',' - **Where it landed:** file path / function name / commit SHA if identifiable, otherwise "TBD".',"","Rules:","- Include only decisions that were actually made, not ideas merely discussed.","- If an alternative was rejected, list it with a one-line reason.","- Group related decisions under a short heading when useful.",'- If the session made zero real decisions, reply exactly with: "No decisions made in this session."',"- No preamble, no closing, just the decision blocks."].join(`
687
- `)}function yi(e){let t=e.limit??5;return[`Find ${t} Recall sessions most similar to session ${e.sessionId}.`,"",`1. Call \`get_session\` with id "${e.sessionId}" \u2014 note its alias, first user message, tags, and git branch.`,'2. Derive 2-3 short search queries from that content (topic words, library names, error strings \u2014 NOT generic words like "fix" or "add").',`3. Call \`search\` once per query (limit: ${Math.max(5,t*2)}). Dedupe hits by session_id.`,"4. Also call `list_sessions` with the same tag(s) if the target session has any (pick the most specific tag).","5. Union the results. Exclude the target session itself. Rank by a mix of:"," - Shared tags (strongest signal)"," - Matching search hits across multiple queries"," - Recency as a tiebreaker","",`6. Return the top ${t} as a numbered list. For each:`," - **<short_id> \xB7 <project>** \u2014 <alias or first_user_message truncated>",' - One sentence on WHY it is similar (not a generic "same topic" \u2014 be specific).',"","If fewer than 2 genuinely-similar sessions exist, say so rather than padding with weak matches.","No preamble. Just the ranked list."].join(`
688
- `)}var fi,hi,Ti,bi,wi,Ri,Ni,Li,ns,tt=b(()=>{"use strict";fi={project:W.string().optional().describe("Exact project name match (optional)."),collectionId:W.string().optional().describe("Restrict to sessions in this collection (optional)."),sessionId:W.string().optional().describe("Full session UUID to tag just one session (optional)."),untaggedOnly:W.boolean().optional().describe("Skip sessions that already have any tag (default: true)."),limit:W.number().int().min(1).max(500).optional().describe("Max sessions to process (default: 100)."),minTags:W.number().int().min(1).max(10).optional().describe("Minimum tags per session (default: 2)."),maxTags:W.number().int().min(1).max(10).optional().describe("Maximum tags per session (default: 4).")};hi={sessionId:W.string().describe("Session UUID (or 8+-char prefix) to summarize."),mode:W.enum(["brief","detailed"]).optional().describe("brief = 3-5 bullets; detailed = paragraph + bullets. Default: brief.")};Ti={sessionId:W.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")};bi={sessionId:W.string().describe("Session UUID (or 8+-char prefix) to find similar sessions to."),limit:W.number().int().min(1).max(20).optional().describe("How many similar sessions to surface (default: 5).")};wi={name:"auto_tag_sessions",title:"Auto-tag Recall sessions",description:"Have the agent auto-tag Recall sessions using the Recall MCP tools. Scope can be restricted to a project, collection, or single session.",argsSchema:fi,build:et,allowedTools:["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"]},Ri={name:"summarize_session",title:"Summarize a session",description:"Produce a concise, concrete summary of one session \u2014 what shipped, what was tried, what's still open.",argsSchema:hi,build:Ei,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},Ni={name:"extract_decisions",title:"Extract architectural decisions",description:"Scan a session and emit one structured block per architectural / product decision: what, why, alternatives, where it landed.",argsSchema:Ti,build:Si,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},Li={name:"find_similar_sessions",title:"Find similar sessions",description:"Given a session, find other sessions that touched the same topic / library / error \u2014 ranked with reasons.",argsSchema:bi,build:yi,allowedTools:["mcp__recall__get_session","mcp__recall__search","mcp__recall__list_sessions","mcp__recall__list_tags"]},ns=[wi,Ri,Ni,Li]});import{writeFileSync as Ai}from"node:fs";import{join as Oi}from"node:path";function rs(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Ii(){return m().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:rs(t.previous_aliases)}))}function Oe(e,t){let s=t.trim();if(!s)throw new Error("alias must be non-empty");let n=m(),r=new Date().toISOString(),i=n.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e),o=[];return i&&(o=rs(i.previous_aliases),i.alias!==s&&o.push({alias:i.alias,replaced_at:r})),n.prepare(`INSERT INTO session_aliases (session_id, alias, updated_at, previous_aliases)
684
+ `);return{id:o.id,project:o.project,git_branch:o.git_branch,alias:o.alias,first_user_message:o.first_user_message,message_sample:d,current_tags:Ae(o.id)}})}var et=b(()=>{"use strict";N();Oe()});import{z as W}from"zod";function nt(e){let t=e.minTags??2,s=e.maxTags??4,n=e.untaggedOnly??!e.sessionId,r=["Auto-tag my Claude Recall sessions using the MCP tools available to you.","","1. Call `list_tags` first to see which tags I already use (prefer those for consistency over inventing new ones).","2. Call `list_sessions_to_tag` with these filters:"],i=[];return e.sessionId?(i.push("limit: 1"),r.push(` ${i.join(", ")}`),r.push(` Then match the session id ${e.sessionId} from the returned list.`)):(n&&i.push("untaggedOnly: true"),e.project&&i.push(`project: "${e.project}"`),e.collectionId&&i.push(`collectionId: "${e.collectionId}"`),i.push(`limit: ${e.limit??100}`),r.push(` ${i.join(", ")}`)),r.push(""),r.push(`3. For each session returned, look at the alias, first user message, git branch, and sampled messages. Pick ${t}-${s} concise, lowercase, hyphen-separated tags describing:`),r.push(" - domain/subsystem (auth, db, frontend, billing, etc.)"),r.push(" - kind of work (bugfix, feature, refactor, research)"),r.push(" - prominent tools or libraries if relevant"),r.push(""),r.push("4. Call `apply_tags` once per session to write the tags."),r.push(""),r.push("Work through EVERY session returned \u2014 do not stop partway. When done, reply with a short summary of how many sessions were tagged. Do not ask clarifying questions; just do the work."),r.join(`
685
+ `)}function Ti(e){let t=e.mode==="detailed";return[`Summarize Claude Recall session ${e.sessionId} using the MCP tools available to you.`,"",`1. Call \`context_for_session\` with id "${e.sessionId}" and mode "condensed" to get the transcript as markdown.`,t?"2. Write a 1-paragraph overview (\u22643 sentences) of what this session was for, then 5-8 bullet points covering:":"2. Write 3-5 bullet points covering:"," - What was accomplished (shipped, decided, learned)"," - What was tried and abandoned"," - Any explicit open questions or follow-ups","","Rules:",'- Be concrete. Name files, functions, and decisions. Avoid vague "discussed X".',"- If nothing was actually shipped or decided, say so plainly.",'- Reply with just the summary \u2014 no preamble, no "Here is the summary:".'].join(`
686
+ `)}function bi(e){return[`Extract every architectural or product decision made in Claude Recall session ${e.sessionId}.`,"",`1. Call \`context_for_session\` with id "${e.sessionId}" and mode "full" (so tool I/O is included \u2014 sometimes the decision lives in a commit message or diff).`,"2. For each distinct decision, emit one block:",""," - **Decision:** one sentence."," - **Why:** the stated rationale (quote if possible).",' - **Alternatives considered:** bullet list, or "none mentioned".',' - **Where it landed:** file path / function name / commit SHA if identifiable, otherwise "TBD".',"","Rules:","- Include only decisions that were actually made, not ideas merely discussed.","- If an alternative was rejected, list it with a one-line reason.","- Group related decisions under a short heading when useful.",'- If the session made zero real decisions, reply exactly with: "No decisions made in this session."',"- No preamble, no closing, just the decision blocks."].join(`
687
+ `)}function wi(e){let t=e.limit??5;return[`Find ${t} Recall sessions most similar to session ${e.sessionId}.`,"",`1. Call \`get_session\` with id "${e.sessionId}" \u2014 note its alias, first user message, tags, and git branch.`,'2. Derive 2-3 short search queries from that content (topic words, library names, error strings \u2014 NOT generic words like "fix" or "add").',`3. Call \`search\` once per query (limit: ${Math.max(5,t*2)}). Dedupe hits by session_id.`,"4. Also call `list_sessions` with the same tag(s) if the target session has any (pick the most specific tag).","5. Union the results. Exclude the target session itself. Rank by a mix of:"," - Shared tags (strongest signal)"," - Matching search hits across multiple queries"," - Recency as a tiebreaker","",`6. Return the top ${t} as a numbered list. For each:`," - **<short_id> \xB7 <project>** \u2014 <alias or first_user_message truncated>",' - One sentence on WHY it is similar (not a generic "same topic" \u2014 be specific).',"","If fewer than 2 genuinely-similar sessions exist, say so rather than padding with weak matches.","No preamble. Just the ranked list."].join(`
688
+ `)}var hi,Ei,Si,yi,Ri,Ni,Li,Ai,us,rt=b(()=>{"use strict";hi={project:W.string().optional().describe("Exact project name match (optional)."),collectionId:W.string().optional().describe("Restrict to sessions in this collection (optional)."),sessionId:W.string().optional().describe("Full session UUID to tag just one session (optional)."),untaggedOnly:W.boolean().optional().describe("Skip sessions that already have any tag (default: true)."),limit:W.number().int().min(1).max(500).optional().describe("Max sessions to process (default: 100)."),minTags:W.number().int().min(1).max(10).optional().describe("Minimum tags per session (default: 2)."),maxTags:W.number().int().min(1).max(10).optional().describe("Maximum tags per session (default: 4).")};Ei={sessionId:W.string().describe("Session UUID (or 8+-char prefix) to summarize."),mode:W.enum(["brief","detailed"]).optional().describe("brief = 3-5 bullets; detailed = paragraph + bullets. Default: brief.")};Si={sessionId:W.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")};yi={sessionId:W.string().describe("Session UUID (or 8+-char prefix) to find similar sessions to."),limit:W.number().int().min(1).max(20).optional().describe("How many similar sessions to surface (default: 5).")};Ri={name:"auto_tag_sessions",title:"Auto-tag Recall sessions",description:"Have the agent auto-tag Recall sessions using the Recall MCP tools. Scope can be restricted to a project, collection, or single session.",argsSchema:hi,build:nt,allowedTools:["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"]},Ni={name:"summarize_session",title:"Summarize a session",description:"Produce a concise, concrete summary of one session \u2014 what shipped, what was tried, what's still open.",argsSchema:Ei,build:Ti,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},Li={name:"extract_decisions",title:"Extract architectural decisions",description:"Scan a session and emit one structured block per architectural / product decision: what, why, alternatives, where it landed.",argsSchema:Si,build:bi,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},Ai={name:"find_similar_sessions",title:"Find similar sessions",description:"Given a session, find other sessions that touched the same topic / library / error \u2014 ranked with reasons.",argsSchema:yi,build:wi,allowedTools:["mcp__recall__get_session","mcp__recall__search","mcp__recall__list_sessions","mcp__recall__list_tags"]},us=[Ri,Ni,Li,Ai]});import{writeFileSync as Oi}from"node:fs";import{join as xi}from"node:path";function ps(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function ki(){return m().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:ps(t.previous_aliases)}))}function Ie(e,t){let s=t.trim();if(!s)throw new Error("alias must be non-empty");let n=m(),r=new Date().toISOString(),i=n.prepare("SELECT alias, previous_aliases FROM session_aliases WHERE session_id = ?").get(e),o=[];return i&&(o=ps(i.previous_aliases),i.alias!==s&&o.push({alias:i.alias,replaced_at:r})),n.prepare(`INSERT INTO session_aliases (session_id, alias, updated_at, previous_aliases)
689
689
  VALUES (?, ?, ?, ?)
690
690
  ON CONFLICT(session_id) DO UPDATE SET
691
691
  alias = excluded.alias,
692
692
  updated_at = excluded.updated_at,
693
- previous_aliases = excluded.previous_aliases`).run(e,s,r,JSON.stringify(o)),ki(),{session_id:e,alias:s,updated_at:r,previous_aliases:o}}function ki(){try{v();let e=Ii(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};Ai(xi,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}var xi,xe=b(()=>{"use strict";R();M();xi=Oi(S,"aliases.json")});import{randomUUID as ji}from"node:crypto";import{writeFileSync as Hi,readFileSync as ad,existsSync as cd}from"node:fs";import{join as Bi}from"node:path";function Xi(e){return{...e}}function rt(e,t,s,n=null,r=new Date().toISOString()){m().prepare(`INSERT INTO collection_events (collection_id, session_id, action, payload, at)
694
- VALUES (?, ?, ?, ?, ?)`).run(e,n,t,s?JSON.stringify(s):null,r)}function Gi(e){let t=m().prepare("SELECT * FROM collections WHERE id = ?").get(e);if(!t)throw new Error(`collection not found: ${e}`);return t}function Yi(e){if(!e)return 0;let t=0,s=e,n=new Set,r=m();for(;s;){if(n.has(s))throw new Error("collection cycle detected");n.add(s);let i=r.prepare("SELECT parent_id FROM collections WHERE id = ?").get(s);if(!i)break;t+=1,s=i.parent_id}return t}function ls(e){let t=m().prepare("SELECT * FROM collections WHERE id = ?").get(e);return t?Xi(t):null}function it(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");if(t.length>120)throw new Error("name too long (max 120 chars)");let s=m(),n=new Date().toISOString(),r=ji();if(e.parent_id){if(!ls(e.parent_id))throw new Error("parent collection not found");if(Yi(e.parent_id)>=cs-1)throw new Error(`max collection depth is ${cs}`)}return s.transaction(()=>{s.prepare(`INSERT INTO collections
693
+ previous_aliases = excluded.previous_aliases`).run(e,s,r,JSON.stringify(o)),Ci(),{session_id:e,alias:s,updated_at:r,previous_aliases:o}}function Ci(){try{k();let e=ki(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};Oi(Ii,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}var Ii,ke=b(()=>{"use strict";N();D();Ii=xi(S,"aliases.json")});import{randomUUID as Hi}from"node:crypto";import{writeFileSync as Bi,readFileSync as Td,existsSync as Sd}from"node:fs";import{join as Wi}from"node:path";function Gi(e){return{...e}}function at(e,t,s,n=null,r=new Date().toISOString()){m().prepare(`INSERT INTO collection_events (collection_id, session_id, action, payload, at)
694
+ VALUES (?, ?, ?, ?, ?)`).run(e,n,t,s?JSON.stringify(s):null,r)}function Yi(e){let t=m().prepare("SELECT * FROM collections WHERE id = ?").get(e);if(!t)throw new Error(`collection not found: ${e}`);return t}function zi(e){if(!e)return 0;let t=0,s=e,n=new Set,r=m();for(;s;){if(n.has(s))throw new Error("collection cycle detected");n.add(s);let i=r.prepare("SELECT parent_id FROM collections WHERE id = ?").get(s);if(!i)break;t+=1,s=i.parent_id}return t}function hs(e){let t=m().prepare("SELECT * FROM collections WHERE id = ?").get(e);return t?Gi(t):null}function ct(e){let t=(e.name??"").trim();if(!t)throw new Error("name required");if(t.length>120)throw new Error("name too long (max 120 chars)");let s=m(),n=new Date().toISOString(),r=Hi();if(e.parent_id){if(!hs(e.parent_id))throw new Error("parent collection not found");if(zi(e.parent_id)>=fs-1)throw new Error(`max collection depth is ${fs}`)}return s.transaction(()=>{s.prepare(`INSERT INTO collections
695
695
  (id, name, description, icon, color, parent_id, sort_key, created_at, updated_at, archived_at)
696
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`).run(r,t,e.description??null,e.icon??null,e.color??null,e.parent_id??null,e.sort_key??"",n,n),rt(r,"create",{name:t,parent_id:e.parent_id??null,icon:e.icon??null,color:e.color??null},null,n)})(),at(),ls(r)}function ot(e,t,s=null,n={}){let r=m();if(Gi(e),!r.prepare("SELECT 1 FROM sessions WHERE id = ?").get(t))throw new Error(`session not found: ${t}`);if(r.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{added:!1};let a=n.source??"manual",c=n.rule_id??null;if(a==="auto"&&!c)throw new Error("auto membership requires a rule_id");let d=new Date().toISOString();return r.transaction(()=>{r.prepare(`INSERT INTO collection_sessions (collection_id, session_id, added_at, note, source, rule_id)
697
- VALUES (?, ?, ?, ?, ?, ?)`).run(e,t,d,s,a,c),rt(e,"add",{note:s,source:a,rule_id:c},t,d)})(),at(),{added:!0}}function ds(e,t){let s=m();if(!s.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{removed:!1};let r=new Date().toISOString();return s.transaction(()=>{s.prepare("DELETE FROM collection_sessions WHERE collection_id = ? AND session_id = ?").run(e,t),rt(e,"remove",null,t,r)})(),at(),{removed:!0}}function zi(){return m().prepare(`SELECT id, collection_id, session_id, action, payload, at
696
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`).run(r,t,e.description??null,e.icon??null,e.color??null,e.parent_id??null,e.sort_key??"",n,n),at(r,"create",{name:t,parent_id:e.parent_id??null,icon:e.icon??null,color:e.color??null},null,n)})(),dt(),hs(r)}function lt(e,t,s=null,n={}){let r=m();if(Yi(e),!r.prepare("SELECT 1 FROM sessions WHERE id = ?").get(t))throw new Error(`session not found: ${t}`);if(r.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{added:!1};let a=n.source??"manual",c=n.rule_id??null;if(a==="auto"&&!c)throw new Error("auto membership requires a rule_id");let d=new Date().toISOString();return r.transaction(()=>{r.prepare(`INSERT INTO collection_sessions (collection_id, session_id, added_at, note, source, rule_id)
697
+ VALUES (?, ?, ?, ?, ?, ?)`).run(e,t,d,s,a,c),at(e,"add",{note:s,source:a,rule_id:c},t,d)})(),dt(),{added:!0}}function Es(e,t){let s=m();if(!s.prepare("SELECT 1 FROM collection_sessions WHERE collection_id = ? AND session_id = ?").get(e,t))return{removed:!1};let r=new Date().toISOString();return s.transaction(()=>{s.prepare("DELETE FROM collection_sessions WHERE collection_id = ? AND session_id = ?").run(e,t),at(e,"remove",null,t,r)})(),dt(),{removed:!0}}function Ji(){return m().prepare(`SELECT id, collection_id, session_id, action, payload, at
698
698
  FROM collection_events
699
- ORDER BY at ASC, id ASC`).all()}function at(){try{v();let e=m(),t=e.prepare(`SELECT id, name, description, icon, color, parent_id, sort_key,
699
+ ORDER BY at ASC, id ASC`).all()}function dt(){try{k();let e=m(),t=e.prepare(`SELECT id, name, description, icon, color, parent_id, sort_key,
700
700
  created_at, updated_at, archived_at
701
701
  FROM collections
702
702
  ORDER BY COALESCE(parent_id, ''), sort_key, LOWER(name)`).all(),s=e.prepare(`SELECT collection_id, session_id, added_at, note, source, rule_id
703
703
  FROM collection_sessions
704
- ORDER BY collection_id, added_at`).all(),n=zi(),r={schema:"claude-recall.collections.v1",backed_up_at:new Date().toISOString(),collections:t,memberships:s,events:n};Hi(Wi,JSON.stringify(r,null,2))}catch(e){console.error("[collections] backup failed:",e)}}var Wi,cs,ct=b(()=>{"use strict";R();M();Wi=Bi(S,"collections.json"),cs=8});function Os(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return e.id.slice(0,8);let s=t.split(`
705
- `)[0].trim();return s.length>As?s.slice(0,As)+"\u2026":s}function to(e){return m().prepare(`SELECT s.id AS id,
704
+ ORDER BY collection_id, added_at`).all(),n=Ji(),r={schema:"claude-recall.collections.v1",backed_up_at:new Date().toISOString(),collections:t,memberships:s,events:n};Bi(Xi,JSON.stringify(r,null,2))}catch(e){console.error("[collections] backup failed:",e)}}var Xi,fs,ut=b(()=>{"use strict";N();D();Xi=Wi(S,"collections.json"),fs=8});function Fs(e){if(e.alias&&e.alias.trim())return e.alias.trim();if(e.auto_title&&e.auto_title.trim())return e.auto_title.trim();let t=(e.first_user_message??"").trim();if(!t)return e.id.slice(0,8);let s=t.split(`
705
+ `)[0].trim();return s.length>Ms?s.slice(0,Ms)+"\u2026":s}function so(e){return m().prepare(`SELECT s.id AS id,
706
706
  sa.alias AS alias,
707
707
  s.auto_title AS auto_title,
708
708
  s.first_user_message AS first_user_message
709
709
  FROM sessions s
710
710
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
711
- WHERE s.id = ?`).get(e)??null}function so(e){let t=to(e);return t?Os(t):e.slice(0,8)}function xs(e){if(!e)return null;let t=m(),s=t.prepare(`SELECT e.thread_id AS thread_id,
711
+ WHERE s.id = ?`).get(e)??null}function no(e){let t=so(e);return t?Fs(t):e.slice(0,8)}function Ps(e){if(!e)return null;let t=m(),s=t.prepare(`SELECT e.thread_id AS thread_id,
712
712
  t.name AS thread_name,
713
713
  e.parent_session_id AS parent_session_id,
714
714
  e.added_at AS added_at
@@ -717,7 +717,7 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
717
717
  WHERE e.session_id = ?
718
718
  AND t.archived = 0
719
719
  ORDER BY e.added_at DESC
720
- LIMIT 1`).get(e);if(!s)return null;let n=s.parent_session_id?{id:s.parent_session_id,title:so(s.parent_session_id)}:null,r=s.parent_session_id?[e,s.parent_session_id]:[e],i=r.map(()=>"?").join(", "),a=t.prepare(`SELECT e.session_id AS session_id,
720
+ LIMIT 1`).get(e);if(!s)return null;let n=s.parent_session_id?{id:s.parent_session_id,title:no(s.parent_session_id)}:null,r=s.parent_session_id?[e,s.parent_session_id]:[e],i=r.map(()=>"?").join(", "),a=t.prepare(`SELECT e.session_id AS session_id,
721
721
  s.id AS id,
722
722
  sa.alias AS alias,
723
723
  s.auto_title AS auto_title,
@@ -727,41 +727,41 @@ CREATE INDEX IF NOT EXISTS idx_synth_results_created
727
727
  LEFT JOIN session_aliases sa ON sa.session_id = e.session_id
728
728
  WHERE e.thread_id = ?
729
729
  AND e.session_id NOT IN (${i})
730
- ORDER BY e.added_at ASC`).all(s.thread_id,...r).map(c=>({id:c.session_id,title:c.id?Os(c):c.session_id.slice(0,8)}));return{thread_id:s.thread_id,thread_name:s.thread_name,parent_session:n,siblings:a}}var As,Is=b(()=>{"use strict";R();As=80});var _t=b(()=>{"use strict"});var ks=b(()=>{"use strict"});import{writeFileSync as no,mkdirSync as ro,existsSync as io}from"node:fs";import{join as vs}from"node:path";function Cs(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(s=>!!s&&typeof s=="object"&&typeof s.title=="string"&&typeof s.replaced_at=="string")}catch{}return[]}function Ds(e){let t=m(),s=t.prepare(`SELECT rowid AS rid, content_text
730
+ ORDER BY e.added_at ASC`).all(s.thread_id,...r).map(c=>({id:c.session_id,title:c.id?Fs(c):c.session_id.slice(0,8)}));return{thread_id:s.thread_id,thread_name:s.thread_name,parent_session:n,siblings:a}}var Ms,Us=b(()=>{"use strict";N();Ms=80});var Et=b(()=>{"use strict"});var $s=b(()=>{"use strict"});import{writeFileSync as ro,mkdirSync as io,existsSync as oo}from"node:fs";import{join as js}from"node:path";function Hs(e){if(!e)return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t.filter(s=>!!s&&typeof s=="object"&&typeof s.title=="string"&&typeof s.replaced_at=="string")}catch{}return[]}function Bs(e){let t=m(),s=t.prepare(`SELECT rowid AS rid, content_text
731
731
  FROM messages
732
732
  WHERE session_id = ? AND role = 'user' AND is_sidechain = 0
733
733
  AND content_text IS NOT NULL AND content_text != ''
734
734
  ORDER BY COALESCE(timestamp, ''), rowid ASC
735
- LIMIT ?`).all(e,_e),n=t.prepare(`SELECT rowid AS rid, content_text
735
+ LIMIT ?`).all(e,fe),n=t.prepare(`SELECT rowid AS rid, content_text
736
736
  FROM messages
737
737
  WHERE session_id = ? AND role = 'user' AND is_sidechain = 0
738
738
  AND content_text IS NOT NULL AND content_text != ''
739
739
  ORDER BY COALESCE(timestamp, '') DESC, rowid DESC
740
- LIMIT ?`).all(e,De),r=new Map;for(let l of s)r.set(l.rid,l.content_text);for(let l of n)r.set(l.rid,l.content_text);if(r.size===0)throw new Error("no user messages available to summarise");let i=Array.from(r.entries()).sort((l,u)=>l[0]-u[0]).map(([,l])=>({content_text:l})),o=s.length===_e&&n.length===De&&r.size===_e+De,a=i.map((l,u)=>{let p=(l.content_text??"").slice(0,oo);return o&&u===_e?`--- (middle of session omitted) ---
740
+ LIMIT ?`).all(e,Fe),r=new Map;for(let l of s)r.set(l.rid,l.content_text);for(let l of n)r.set(l.rid,l.content_text);if(r.size===0)throw new Error("no user messages available to summarise");let i=Array.from(r.entries()).sort((l,u)=>l[0]-u[0]).map(([,l])=>({content_text:l})),o=s.length===fe&&n.length===Fe&&r.size===fe+Fe,a=i.map((l,u)=>{let p=(l.content_text??"").slice(0,ao);return o&&u===fe?`--- (middle of session omitted) ---
741
741
  ${u+1}. ${p}`:`${u+1}. ${p}`}).join(`
742
- `),c=null;try{c=xs(e)}catch(l){console.error("[autoTitle] thread context resolution failed:",l),c=null}let d=[];return c&&(d.push(ao(c)),d.push("")),d.push(`You will receive a sample of user messages from a Claude Code session: the first ${_e}`,`messages (initial intent) and the last ${De} messages (current direction).`,"Write a single descriptive title, max 50 characters, focused on what the user is","currently trying to accomplish. If initial intent and current direction differ, prefer","the current direction. Output ONLY the title, with no quotes and no trailing punctuation.","","Messages:",a),d.join(`
743
- `)}function ao(e){let t=[];t.push("Thread context:"),t.push(`- This session is part of thread "${e.thread_name}".`),e.parent_session&&t.push(`- Parent session: "${e.parent_session.title}"`);let s=e.siblings.length;if(s>0){let r=e.siblings.slice(0,ft).map(o=>`"${o.title}"`).join(", "),i=s>ft?`, and ${s-ft} more`:"";t.push(`- Sibling sessions (${s}): ${r}${i}`)}return t.push(""),t.push("Generate a title that reflects this session's role in the thread."),t.push('If siblings use a pattern like "Phase A / Phase B" or "Wave 1 of 4",'),t.push("follow the same pattern."),t.join(`
744
- `)}function Et(e,t,s){let n=t.trim();if(!n)return;let r=m(),i=r.prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
745
- FROM sessions WHERE id = ?`).get(e);if(!i||s==="heuristic"&&i.auto_title_source==="agent"&&i.auto_title||i.auto_title===n&&i.auto_title_source===s)return;let o=Cs(i.auto_title_history),a=new Date().toISOString();i.auto_title&&i.auto_title_source&&o.push({title:i.auto_title,source:i.auto_title_source,replaced_at:a}),r.prepare(`UPDATE sessions
742
+ `),c=null;try{c=Ps(e)}catch(l){console.error("[autoTitle] thread context resolution failed:",l),c=null}let d=[];return c&&(d.push(co(c)),d.push("")),d.push(`You will receive a sample of user messages from a Claude Code session: the first ${fe}`,`messages (initial intent) and the last ${Fe} messages (current direction).`,"Write a single descriptive title, max 50 characters, focused on what the user is","currently trying to accomplish. If initial intent and current direction differ, prefer","the current direction. Output ONLY the title, with no quotes and no trailing punctuation.","","Messages:",a),d.join(`
743
+ `)}function co(e){let t=[];t.push("Thread context:"),t.push(`- This session is part of thread "${e.thread_name}".`),e.parent_session&&t.push(`- Parent session: "${e.parent_session.title}"`);let s=e.siblings.length;if(s>0){let r=e.siblings.slice(0,Tt).map(o=>`"${o.title}"`).join(", "),i=s>Tt?`, and ${s-Tt} more`:"";t.push(`- Sibling sessions (${s}): ${r}${i}`)}return t.push(""),t.push("Generate a title that reflects this session's role in the thread."),t.push('If siblings use a pattern like "Phase A / Phase B" or "Wave 1 of 4",'),t.push("follow the same pattern."),t.join(`
744
+ `)}function bt(e,t,s){let n=t.trim();if(!n)return;let r=m(),i=r.prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
745
+ FROM sessions WHERE id = ?`).get(e);if(!i||s==="heuristic"&&i.auto_title_source==="agent"&&i.auto_title||i.auto_title===n&&i.auto_title_source===s)return;let o=Hs(i.auto_title_history),a=new Date().toISOString();i.auto_title&&i.auto_title_source&&o.push({title:i.auto_title,source:i.auto_title_source,replaced_at:a}),r.prepare(`UPDATE sessions
746
746
  SET auto_title = ?,
747
747
  auto_title_source = ?,
748
748
  auto_title_generated_at = ?,
749
749
  auto_title_history = ?
750
- WHERE id = ?`).run(n,s,Date.now(),JSON.stringify(o),e),lo(e,n,s,a)}function Ms(e){let t=m().prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
751
- FROM sessions WHERE id = ?`).get(e);return t?{auto_title:t.auto_title,auto_title_source:t.auto_title_source??null,auto_title_generated_at:t.auto_title_generated_at,auto_title_history:Cs(t.auto_title_history)}:null}function co(){v(),io(ht)||ro(ht,{recursive:!0})}function lo(e,t,s,n){try{co();let r=vs(ht,`${e}.txt`),i=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${s} \xB7 updated ${n}
752
- `;no(r,i+t+`
753
- `)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}var ht,_e,De,oo,ft,Tt=b(()=>{"use strict";R();M();Is();_t();ks();ht=vs(S,"titles"),_e=5,De=15,oo=500;ft=5});function Fs(e,t){let s=uo.get(e);if(!(!s||s.size===0))for(let n of s)try{n(t)}catch{}}var uo,Ps=b(()=>{"use strict";uo=new Map});import{existsSync as po,statSync as mo}from"node:fs";import{delimiter as go,join as _o}from"node:path";function Us(e){if(e.includes("/")||e.includes("\\")||e.includes(".."))return null;let t=(process.env.PATH??"").split(go).filter(Boolean),s=process.platform==="win32"?[e,`${e}.exe`,`${e}.cmd`,`${e}.bat`]:[e];for(let n of t)for(let r of s){let i=_o(n,r);try{if(po(i)&&mo(i).isFile())return i}catch{}}return null}function $s(e){if(process.platform!=="win32"||!e)return!1;let t=e.toLowerCase();return t.endsWith(".cmd")||t.endsWith(".bat")}var js=b(()=>{"use strict"});var Ys={};Ge(Ys,{_resetClaudePathCacheForTests:()=>To,buildScanPrompt:()=>Ws,isClaudeCliAvailable:()=>Bs,runClaudeCliScan:()=>Ro,spawnClaudePrompt:()=>Xs});import{spawn as fo}from"node:child_process";function Hs(){if(ae!==void 0&&fe!==void 0)return{path:ae,available:fe};let e=Us("claude");return ae=e??"claude",fe=e!==null,{path:ae,available:fe}}function Eo(){return Hs().path}function Bs(){return Hs().available}function To(){ae=void 0,fe=void 0}function Ws(e){return et({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 So(e,t){let s=t.get(e);return s||e.slice(0,8)}function bo(e){try{return Ae(e).map(s=>({id:s.id,label:s.alias&&s.alias.trim().length>0?s.alias:s.first_user_message&&s.first_user_message.trim().length>0?s.first_user_message.slice(0,60):s.id.slice(0,8)}))}catch{return[]}}function yo(e){let{scanId:t,total:s,labelTable:n}=e,r=new Set;return i=>{let o=i.trim();if(!o.startsWith("{"))return;let a;try{a=JSON.parse(o)}catch{return}if(!a||typeof a!="object")return;let c=a;if(!(c.type!=="assistant"||!c.message?.content))for(let d of c.message.content){if(d?.type!=="tool_use"||d.name!=="mcp__recall__apply_tags")continue;let l=d.input,u=typeof l?.sessionId=="string"?l.sessionId:null;!u||r.has(u)||(r.add(u),Fs(t,{type:"progress",current:r.size,total:s,sessionId:u,sessionLabel:So(u,n)}))}}}function wo(e){let t="";return s=>{t+=s.toString("utf8");let n=t.indexOf(`
750
+ WHERE id = ?`).run(n,s,Date.now(),JSON.stringify(o),e),uo(e,n,s,a)}function Ws(e){let t=m().prepare(`SELECT auto_title, auto_title_source, auto_title_generated_at, auto_title_history
751
+ FROM sessions WHERE id = ?`).get(e);return t?{auto_title:t.auto_title,auto_title_source:t.auto_title_source??null,auto_title_generated_at:t.auto_title_generated_at,auto_title_history:Hs(t.auto_title_history)}:null}function lo(){k(),oo(St)||io(St,{recursive:!0})}function uo(e,t,s,n){try{lo();let r=js(St,`${e}.txt`),i=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${s} \xB7 updated ${n}
752
+ `;ro(r,i+t+`
753
+ `)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}var St,fe,Fe,ao,Tt,yt=b(()=>{"use strict";N();D();Us();Et();$s();St=js(S,"titles"),fe=5,Fe=15,ao=500;Tt=5});function Xs(e,t){let s=po.get(e);if(!(!s||s.size===0))for(let n of s)try{n(t)}catch{}}var po,Gs=b(()=>{"use strict";po=new Map});import{existsSync as mo,statSync as go}from"node:fs";import{delimiter as _o,join as fo}from"node:path";function Ys(e){if(e.includes("/")||e.includes("\\")||e.includes(".."))return null;let t=(process.env.PATH??"").split(_o).filter(Boolean),s=process.platform==="win32"?[e,`${e}.exe`,`${e}.cmd`,`${e}.bat`]:[e];for(let n of t)for(let r of s){let i=fo(n,r);try{if(mo(i)&&go(i).isFile())return i}catch{}}return null}function zs(e){if(process.platform!=="win32"||!e)return!1;let t=e.toLowerCase();return t.endsWith(".cmd")||t.endsWith(".bat")}var Js=b(()=>{"use strict"});var en={};Ke(en,{_resetClaudePathCacheForTests:()=>So,buildScanPrompt:()=>qs,isClaudeCliAvailable:()=>Vs,runClaudeCliScan:()=>No,spawnClaudePrompt:()=>Zs});import{spawn as ho}from"node:child_process";function Ks(){if(ae!==void 0&&he!==void 0)return{path:ae,available:he};let e=Ys("claude");return ae=e??"claude",he=e!==null,{path:ae,available:he}}function To(){return Ks().path}function Vs(){return Ks().available}function So(){ae=void 0,he=void 0}function qs(e){return nt({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 bo(e,t){let s=t.get(e);return s||e.slice(0,8)}function yo(e){try{return xe(e).map(s=>({id:s.id,label:s.alias&&s.alias.trim().length>0?s.alias:s.first_user_message&&s.first_user_message.trim().length>0?s.first_user_message.slice(0,60):s.id.slice(0,8)}))}catch{return[]}}function wo(e){let{scanId:t,total:s,labelTable:n}=e,r=new Set;return i=>{let o=i.trim();if(!o.startsWith("{"))return;let a;try{a=JSON.parse(o)}catch{return}if(!a||typeof a!="object")return;let c=a;if(!(c.type!=="assistant"||!c.message?.content))for(let d of c.message.content){if(d?.type!=="tool_use"||d.name!=="mcp__recall__apply_tags")continue;let l=d.input,u=typeof l?.sessionId=="string"?l.sessionId:null;!u||r.has(u)||(r.add(u),Xs(t,{type:"progress",current:r.size,total:s,sessionId:u,sessionLabel:bo(u,n)}))}}}function Ro(e){let t="";return s=>{t+=s.toString("utf8");let n=t.indexOf(`
754
754
  `);for(;n!==-1;){let r=t.slice(0,n);t=t.slice(n+1),r.length>0&&e(r),n=t.indexOf(`
755
- `)}}}async function Ro(e,t={},s){let n=!!t.scanId,r=n?bo(e):[],i=new Map(r.map(c=>[c.id,c.label])),o=r.length,a;return n&&t.scanId&&(a=yo({scanId:t.scanId,total:o,labelTable:i})),Gs({prompt:Ws(e),allowedTools:ho.split(","),opts:t,onProgress:s,onStdoutLine:a,outputFormat:n?"stream-json":"json"})}async function Xs(e,t,s={},n){return Gs({prompt:e,allowedTools:t,opts:s,onProgress:n,outputFormat:"json"})}function Gs(e){let{prompt:t,allowedTools:s,opts:n,onProgress:r,onStdoutLine:i,outputFormat:o}=e,a=["-p",t,"--output-format",o,"--allowedTools",s.join(","),"--permission-mode","bypassPermissions","--no-session-persistence"];return o==="stream-json"&&a.push("--verbose"),n.model&&a.push("--model",n.model),new Promise(c=>{let d=Eo(),l=fo(d,a,{stdio:["ignore","pipe","pipe"],shell:$s(d)||process.platform==="win32"&&ae==="claude"}),u=[],p=[],g=i?wo(i):void 0;l.stdout.on("data",f=>{u.push(f),g&&g(f)}),l.stderr.on("data",f=>{if(p.push(f),r){let E=f.toString("utf8").trim();E&&r(E)}});let _=setTimeout(()=>{l.kill("SIGKILL")},1800*1e3);l.on("close",f=>{clearTimeout(_),c({success:f===0,stdout:Buffer.concat(u).toString("utf8"),stderr:Buffer.concat(p).toString("utf8"),exitCode:f})}),l.on("error",f=>{clearTimeout(_),c({success:!1,stdout:"",stderr:String(f),exitCode:null})})})}var ho,ae,fe,St=b(()=>{"use strict";qe();tt();Ps();js();ho=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});import J from"chalk";import{formatDistanceToNowStrict as sm,parseISO as nm}from"date-fns";var h,vt=b(()=>{"use strict";h={dim:J.gray,bold:J.bold,project:J.cyan,user:J.blue,assistant:J.green,tool:J.magenta,warn:J.yellow,err:J.red,ok:J.green,accent:J.hex("#f97316")}});var er=b(()=>{"use strict"});import{existsSync as uc,readFileSync as pc,writeFileSync as mc}from"node:fs";import{join as gc}from"node:path";import{z as j}from"zod";function Mt(e){let t=e.trim();return!!(!t||sr.test(t)||nr.test(t))}function rr(e){let t=e.trim();if(!t||nr.test(t))return null;let s=t.replace(sr,"").trim();return s.length>0?s:null}function Dt(e,t){if(!Mt(e))return e;let s=rr(e);return s||(t&&!Mt(t)?t:e)}function Tc(e){let t=e.now-e.withinMs,s=e.pending.filter(n=>{let r=Date.parse(n.started_at);return Number.isFinite(r)&&r>=t});if(s.length===0)return{kind:"none"};if(e.shellPid!=null){let n=s.find(r=>r.shell_pid===e.shellPid);if(n)return{kind:"pid-match",entry:n}}if(e.cwd){let n=e.cwd.replace(/\/+$/,""),r=s.filter(i=>i.cwd&&i.cwd.replace(/\/+$/,"")===n);if(r.length===1)return{kind:"singleton-cwd",entry:r[0]};if(r.length>=2)return{kind:"ambiguous",candidates:r}}return{kind:"none"}}var Ct,tr,_c,fc,hc,Ec,sr,nr,Ft,Sc,ir=b(()=>{"use strict";M();Ct=gc(S,"terminals.json"),tr=1440*60*1e3,_c=3e4,fc=6e4,hc=j.object({shell_pid:j.number(),tab_name:j.string(),cwd:j.string().nullable().optional(),opened_at:j.string(),last_seen_at:j.string()}),Ec=j.object({schema:j.string().optional(),saved_at:j.string().optional(),terminals:j.array(hc).max(500).default([]),sessions_by_pid:j.record(j.string(),j.array(j.string()).max(50)).optional().default({})}),sr=/^[⠀-⣿✳\s]+/,nr=/^\d+(\.\d+){1,3}$/;Ft=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,!!uc(Ct)))try{let t=pc(Ct,"utf8"),s=JSON.parse(t),n=Ec.safeParse(s);if(!n.success){console.warn("[terminal-registry] terminals.json failed validation, starting with empty registry:",n.error.issues);return}let r=n.data;for(let i of r.terminals)this.entries.set(i.shell_pid,{shell_pid:i.shell_pid,tab_name:i.tab_name,cwd:i.cwd??null,opened_at:i.opened_at,last_seen_at:i.last_seen_at});for(let[i,o]of Object.entries(r.sessions_by_pid??{})){let a=Number(i);if(!Number.isFinite(a))continue;let c=new Set;for(let d of o)d.length>0&&c.add(d);c.size>0&&this.sessionsByPid.set(a,c)}this.gc()}catch{}}save(){try{v();let t={schema:"claude-recall.terminals.v1",saved_at:new Date().toISOString(),terminals:Array.from(this.entries.values()),sessions_by_pid:Object.fromEntries(Array.from(this.sessionsByPid.entries()).map(([s,n])=>[String(s),Array.from(n)]))};mc(Ct,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let s=new Date().toISOString(),n=this.entries.get(t.shell_pid),r=Dt(t.tab_name,n?.tab_name),i=n?.opened_at??t.opened_at,o={...t,tab_name:r,opened_at:i,last_seen_at:s};return this.entries.set(t.shell_pid,o),this.gc(),this.save(),o}rename(t,s){this.ensureLoaded();let n=this.entries.get(t);if(!n)return null;let r=Dt(s,n.tab_name),i={...n,tab_name:r,last_seen_at:new Date().toISOString()};return this.entries.set(t,i),this.save(),i}remove(t){this.ensureLoaded();let s=this.entries.delete(t),n=this.sessionsByPid.delete(t);return this.outputTails.delete(t),this.pidOwnership.delete(t),(s||n)&&this.save(),s}claimPidOwnership(t,s,n=Date.now()){if(this.ensureLoaded(),!s)return"anonymous";let r=this.pidOwnership.get(t);return r?r.instance_id===s?(r.last_claim_at=n,"refreshed"):n-r.last_claim_at>fc?(this.pidOwnership.set(t,{instance_id:s,last_claim_at:n}),"claimed"):"rejected":(this.pidOwnership.set(t,{instance_id:s,last_claim_at:n}),"claimed")}pidOwnershipSnapshot(){return this.ensureLoaded(),Array.from(this.pidOwnership.entries()).map(([t,s])=>({shell_pid:t,instance_id:s.instance_id,last_claim_at:s.last_claim_at}))}sync(t){this.ensureLoaded();let s=new Date().toISOString(),n=0,r=0;for(let i of t){let o=this.entries.get(i.shell_pid),a=Dt(i.tab_name,o?.tab_name),c=o?.opened_at??i.opened_at;this.entries.set(i.shell_pid,{...i,tab_name:a,opened_at:c,last_seen_at:s}),o?(o.tab_name!==a||o.cwd!==i.cwd)&&r++:n++}return this.gc(),this.save(),{added:n,updated:r,removed:0}}get(t){return this.ensureLoaded(),this.entries.get(t)??null}all(){return this.ensureLoaded(),this.gc(),Array.from(this.entries.values())}size(){return this.ensureLoaded(),this.entries.size}linkSession(t,s){this.ensureLoaded();let n=this.sessionsByPid.get(s);n||(n=new Set,this.sessionsByPid.set(s,n)),n.has(t)||(n.add(t),this.save())}sessionsFor(t){return this.ensureLoaded(),Array.from(this.sessionsByPid.get(t)??[])}isSessionAutoLinked(t){this.ensureLoaded();for(let s of this.sessionsByPid.values())if(s.has(t))return!0;return!1}unlinkSession(t){this.ensureLoaded();let s=!1;for(let[n,r]of this.sessionsByPid)r.delete(t)&&(s=!0,r.size===0&&this.sessionsByPid.delete(n));return s&&this.save(),s}pushPending(t){this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.push({...t})}takePendingMatched(t){this.ensureLoaded(),this.gcPending();let s=Tc({pending:this.pendingClaudeStarts,shellPid:t.shellPid,cwd:t.cwd,withinMs:t.withinMs,now:Date.now()});if(s.kind==="pid-match"||s.kind==="singleton-cwd"){let n=this.pendingClaudeStarts.indexOf(s.entry);n>=0&&this.pendingClaudeStarts.splice(n,1)}return s}pendingSize(){return this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.length}deferSessionLink(t,s,n,r){this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.set(t,{parent_shell_pid:s,queued_at:Date.now(),cwd:n,git_branch:r})}allDeferredLinks(){return this.ensureLoaded(),this.gcDeferredLinks(),Array.from(this.deferredLinks.entries()).map(([t,s])=>({session_id:t,...s}))}resolveDeferredLink(t){return this.deferredLinks.delete(t)}deferredLinkSize(){return this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.size}gcDeferredLinks(){let t=Date.now()-9e4;for(let[s,n]of this.deferredLinks)n.queued_at<t&&this.deferredLinks.delete(s)}gcPending(){let t=Date.now()-_c;this.pendingClaudeStarts.length!==0&&(this.pendingClaudeStarts=this.pendingClaudeStarts.filter(s=>{let n=Date.parse(s.started_at);return Number.isFinite(n)&&n>=t}))}outputTails=new Map;setOutputTail(t,s,n){this.ensureLoaded(),this.outputTails.set(t,{text:s,captured_at:n})}getOutputTail(t){return this.ensureLoaded(),this.outputTails.get(t)??null}allOutputTails(){return this.ensureLoaded(),new Map(this.outputTails)}removeOutputTail(t){return this.outputTails.delete(t)}setOrigin(t,s){this.ensureLoaded(),this.origins.set(t,s),this.gcOrigins()}getOrigin(t){return this.ensureLoaded(),this.origins.get(t)??null}removeOrigin(t){return this.ensureLoaded(),this.origins.delete(t)}allOrigins(){return this.ensureLoaded(),this.gcOrigins(),new Map(this.origins)}originSize(){return this.ensureLoaded(),this.origins.size}gc(){let t=Date.now()-tr;for(let[s,n]of this.entries){let r=Date.parse(n.last_seen_at);!Number.isNaN(r)&&r<t&&this.entries.delete(s)}}gcOrigins(){let t=Date.now()-tr;for(let[s,n]of this.origins)n.detectedAt<t&&this.origins.delete(s)}reapStaleLinks(t=10080*60*1e3){this.ensureLoaded();let n=Date.now()-t,r=0,i=0;for(let[o,a]of this.sessionsByPid){let c=this.entries.get(o);if(c){let d=Date.parse(c.last_seen_at);if(Number.isFinite(d)&&d>=n)continue}i+=a.size,this.sessionsByPid.delete(o),c&&(this.entries.delete(o),r++)}return(r||i)&&this.save(),{pruned_pids:r,pruned_sessions:i}}gcDeadPids(){this.ensureLoaded();let t=0,s=0;for(let n of[...this.entries.keys()]){let r=!0;try{process.kill(n,0)}catch(o){o.code==="ESRCH"&&(r=!1)}if(r)continue;this.entries.delete(n),t++;let i=this.sessionsByPid.get(n);i&&(s+=i.size,this.sessionsByPid.delete(n)),this.outputTails.delete(n),this.pidOwnership.delete(n)}return(t||s)&&this.save(),{pruned_pids:t,pruned_sessions:s}}},Sc=new Ft});import{execFile as bc}from"node:child_process";import{promisify as yc}from"node:util";var mm,or=b(()=>{"use strict";mm=yc(bc)});import{execFile as Rc}from"node:child_process";import{promisify as Nc}from"node:util";var ym,wm,ar=b(()=>{"use strict";ir();xe();R();or();ym=Nc(Rc),wm=3600*1e3});var cr=b(()=>{"use strict"});import{z as K}from"zod";var Om,lr=b(()=>{"use strict";R();Om=K.object({enabled:K.boolean().default(!1),model:K.string().optional(),ratePerMinute:K.number().int().min(1).max(600).default(30),lastProcessedSessionId:K.string().nullable().default(null),backfillPaused:K.boolean().default(!1),autoExtractEnabled:K.boolean().default(!1),autoExtractIntervalMinutes:K.number().int().min(5).max(720).default(60),autoExtractBatchSize:K.number().int().min(1).max(20).default(1),autoResumeWorker:K.boolean().default(!1)})});var dr=b(()=>{"use strict";R();St();lr()});var ur=b(()=>{"use strict"});import{execFile as Lc}from"node:child_process";import{promisify as Ac}from"node:util";var jm,pr=b(()=>{"use strict";R();jm=Ac(Lc)});import{z as Pt}from"zod";var Wm,mr=b(()=>{"use strict";Wm=Pt.object({heuristicEnabled:Pt.boolean().default(!0),agentEnabled:Pt.boolean().default(!1)})});import{basename as zm,join as Ut}from"node:path";var gr,qm,Zm,_r=b(()=>{"use strict";R();M();ct();gr=Ut(S,"auto-rules"),qm=Ut(gr,"rules.json"),Zm=Ut(gr,"suggestions.json")});var fr=b(()=>{"use strict"});var hr=b(()=>{"use strict"});import{watch as fg}from"chokidar";import{readdirSync as Oc,statSync as Eg}from"node:fs";import{basename as wg,join as xc}from"node:path";function Er(e){let t=e.split(/[/\\]/),s=t.findIndex(n=>n==="projects");return s===-1||s+1>=t.length?null:t[s+1]??null}function Tr(e){return e.replace(/\\/g,"/").includes("/subagents/")}function*$t(e){let t;try{t=Oc(e,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let s of t){if(s.isSymbolicLink())continue;let n=xc(e,s.name);s.isDirectory()?yield*$t(n):s.isFile()&&s.name.endsWith(".jsonl")&&(yield n)}}var Sr=b(()=>{"use strict";M();R();er();ar();cr();dr();ur();pr();Tt();mr();_r();_t();fr();xe();hr()});import{execFileSync as yr}from"node:child_process";function wr(e={}){let t=e.psOutput??Ic(),s=e.isProcessAlive??kc,n=e.getParentCommand??vc,r=[];for(let i of t.split(`
756
- `)){let o=i.trim();if(!o||!o.includes("dist/mcp/server.js")||Cc(o))continue;let a=o.split(/\s+/);if(a.length<4)continue;let c=Number(a[0]),d=Number(a[1]),l=a[2];if(!Number.isFinite(c)||!Number.isFinite(d))continue;let u=d>1&&s(d);r.push({pid:c,ppid:d,parentAlive:u,etimeSeconds:Dc(l),orphan:!u,parentCommand:u?n(d):null})}return r}function Ic(){try{return yr("ps",["-eo","pid,ppid,etime,command"],{encoding:"utf8",timeout:2e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[mcp-processes] ps -eo failed: ${t}
757
- `),""}}function kc(e){if(!Number.isFinite(e)||e<=1)return!1;try{return process.kill(e,0),!0}catch{return!1}}function vc(e){if(!Number.isFinite(e)||e<=1)return null;try{let s=yr("ps",["-p",String(e),"-o","command="],{encoding:"utf8",timeout:1e3,maxBuffer:1048576}).trim();return s.length?s.slice(0,200):null}catch{return null}}function Cc(e){let t=e.split(/\s+/);if(t.length<4)return!1;let s=t[3]??"";return s.endsWith("/grep")||s==="grep"||s.endsWith("/awk")||s==="awk"||s.endsWith("/rg")||s==="rg"}function Dc(e){if(!e)return 0;let t=0,s=e,n=e.indexOf("-");n>=0&&(t=br(e.slice(0,n)),s=e.slice(n+1));let r=s.split(":").map(br),i=0,o=0,a=0;return r.length===3?[i,o,a]=r:r.length===2?[o,a]=r:r.length===1&&(a=r[0]),t*86400+i*3600+o*60+a}function br(e){let t=Number(e);return Number.isFinite(t)?t:0}var Rr=b(()=>{"use strict"});var jg,Nr,Lr=b(()=>{"use strict";we();jg=5*6e4,Nr=1073741824});var $r={};Ge($r,{buildHealthReport:()=>Pr,buildPipelineDiagnostic:()=>Mr,checkIngestStaleness:()=>Ur,detectLabelCollisions:()=>Fr,getFreeDiskBytes:()=>Yc,runDoctor:()=>Gc});import{existsSync as xr,readFileSync as Mc,statSync as jt,statfsSync as Ir}from"node:fs";import{join as kr}from"node:path";import*as vr from"node:http";function Uc(e){let t=[];for(let s of e)if(s.alias){if(Pc.test(s.alias)){t.push({session_id:s.session_id,alias:s.alias,violation:"fabricated-origin-label",cwd:s.cwd});continue}if(s.cwd){let n=s.cwd.replace(/\/+$/,"").split("/").pop();n&&s.alias.startsWith(`${n} \xB7 `)&&t.push({session_id:s.session_id,alias:s.alias,violation:"fabricated-cwd-branch",cwd:s.cwd})}}return t}function $c(){let e=kr(S,"daemon.port");if(!xr(e))return null;try{let t=Mc(e,"utf8").trim();if(t.startsWith("{")){let n=JSON.parse(t);return typeof n.port=="number"?n.port:null}let s=Number.parseInt(t,10);return Number.isFinite(s)&&s>0&&s<65536?s:null}catch{return null}}function jc(e,t,s=1500){return new Promise(n=>{let r=vr.request({host:"127.0.0.1",port:e,path:t,method:"GET",timeout:s,headers:{host:"127.0.0.1","user-agent":"recall-doctor"}},i=>{let o=[];i.on("data",a=>o.push(Buffer.isBuffer(a)?a:Buffer.from(a))),i.on("end",()=>{if(!i.statusCode||i.statusCode<200||i.statusCode>=300){n(null);return}try{n(JSON.parse(Buffer.concat(o).toString("utf8")))}catch{n(null)}})});r.on("error",()=>n(null)),r.on("timeout",()=>{r.destroy(),n(null)}),r.end()})}function Hc(){let e=kr(S,"terminals.json");if(!xr(e))return{exists:!1,mtimeMs:null,ageSeconds:null};try{let t=jt(e),s=Math.floor((Date.now()-t.mtimeMs)/1e3);return{exists:!0,mtimeMs:t.mtimeMs,ageSeconds:s}}catch{return{exists:!1,mtimeMs:null,ageSeconds:null}}}function Bc(){let e=new Date(Date.now()-Ht*36e5).toISOString();try{let t=m().prepare(`SELECT
755
+ `)}}}async function No(e,t={},s){let n=!!t.scanId,r=n?yo(e):[],i=new Map(r.map(c=>[c.id,c.label])),o=r.length,a;return n&&t.scanId&&(a=wo({scanId:t.scanId,total:o,labelTable:i})),Qs({prompt:qs(e),allowedTools:Eo.split(","),opts:t,onProgress:s,onStdoutLine:a,outputFormat:n?"stream-json":"json"})}async function Zs(e,t,s={},n){return Qs({prompt:e,allowedTools:t,opts:s,onProgress:n,outputFormat:"json"})}function Qs(e){let{prompt:t,allowedTools:s,opts:n,onProgress:r,onStdoutLine:i,outputFormat:o}=e,a=["-p",t,"--output-format",o,"--allowedTools",s.join(","),"--permission-mode","bypassPermissions","--no-session-persistence"];return o==="stream-json"&&a.push("--verbose"),n.model&&a.push("--model",n.model),new Promise(c=>{let d=To(),l=ho(d,a,{stdio:["ignore","pipe","pipe"],shell:zs(d)||process.platform==="win32"&&ae==="claude"}),u=[],p=[],g=i?Ro(i):void 0;l.stdout.on("data",h=>{u.push(h),g&&g(h)}),l.stderr.on("data",h=>{if(p.push(h),r){let E=h.toString("utf8").trim();E&&r(E)}});let _=setTimeout(()=>{l.kill("SIGKILL")},1800*1e3);l.on("close",h=>{clearTimeout(_),c({success:h===0,stdout:Buffer.concat(u).toString("utf8"),stderr:Buffer.concat(p).toString("utf8"),exitCode:h})}),l.on("error",h=>{clearTimeout(_),c({success:!1,stdout:"",stderr:String(h),exitCode:null})})})}var Eo,ae,he,wt=b(()=>{"use strict";et();rt();Gs();Js();Eo=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});import J from"chalk";import{formatDistanceToNowStrict as Sm,parseISO as bm}from"date-fns";var f,Ut=b(()=>{"use strict";f={dim:J.gray,bold:J.bold,project:J.cyan,user:J.blue,assistant:J.green,tool:J.magenta,warn:J.yellow,err:J.red,ok:J.green,accent:J.hex("#f97316")}});var nr=b(()=>{"use strict"});import{existsSync as bc,readFileSync as yc,writeFileSync as wc}from"node:fs";import{join as Rc}from"node:path";import{z as j}from"zod";function Ht(e){let t=e.trim();return!!(!t||ir.test(t)||or.test(t))}function ar(e){let t=e.trim();if(!t||or.test(t))return null;let s=t.replace(ir,"").trim();return s.length>0?s:null}function jt(e,t){if(!Ht(e))return e;let s=ar(e);return s||(t&&!Ht(t)?t:e)}function xc(e){let t=e.now-e.withinMs,s=e.pending.filter(n=>{let r=Date.parse(n.started_at);return Number.isFinite(r)&&r>=t});if(s.length===0)return{kind:"none"};if(e.shellPid!=null){let n=s.find(r=>r.shell_pid===e.shellPid);if(n)return{kind:"pid-match",entry:n}}if(e.cwd){let n=e.cwd.replace(/\/+$/,""),r=s.filter(i=>i.cwd&&i.cwd.replace(/\/+$/,"")===n);if(r.length===1)return{kind:"singleton-cwd",entry:r[0]};if(r.length>=2)return{kind:"ambiguous",candidates:r}}return{kind:"none"}}var $t,rr,Nc,Lc,Ac,Oc,ir,or,Bt,Ic,cr=b(()=>{"use strict";D();$t=Rc(S,"terminals.json"),rr=1440*60*1e3,Nc=3e4,Lc=6e4,Ac=j.object({shell_pid:j.number(),tab_name:j.string(),cwd:j.string().nullable().optional(),opened_at:j.string(),last_seen_at:j.string()}),Oc=j.object({schema:j.string().optional(),saved_at:j.string().optional(),terminals:j.array(Ac).max(500).default([]),sessions_by_pid:j.record(j.string(),j.array(j.string()).max(50)).optional().default({})}),ir=/^[⠀-⣿✳\s]+/,or=/^\d+(\.\d+){1,3}$/;Bt=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,!!bc($t)))try{let t=yc($t,"utf8"),s=JSON.parse(t),n=Oc.safeParse(s);if(!n.success){console.warn("[terminal-registry] terminals.json failed validation, starting with empty registry:",n.error.issues);return}let r=n.data;for(let i of r.terminals)this.entries.set(i.shell_pid,{shell_pid:i.shell_pid,tab_name:i.tab_name,cwd:i.cwd??null,opened_at:i.opened_at,last_seen_at:i.last_seen_at});for(let[i,o]of Object.entries(r.sessions_by_pid??{})){let a=Number(i);if(!Number.isFinite(a))continue;let c=new Set;for(let d of o)d.length>0&&c.add(d);c.size>0&&this.sessionsByPid.set(a,c)}this.gc()}catch{}}save(){try{k();let t={schema:"claude-recall.terminals.v1",saved_at:new Date().toISOString(),terminals:Array.from(this.entries.values()),sessions_by_pid:Object.fromEntries(Array.from(this.sessionsByPid.entries()).map(([s,n])=>[String(s),Array.from(n)]))};wc($t,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let s=new Date().toISOString(),n=this.entries.get(t.shell_pid),r=jt(t.tab_name,n?.tab_name),i=n?.opened_at??t.opened_at,o={...t,tab_name:r,opened_at:i,last_seen_at:s};return this.entries.set(t.shell_pid,o),this.gc(),this.save(),o}rename(t,s){this.ensureLoaded();let n=this.entries.get(t);if(!n)return null;let r=jt(s,n.tab_name),i={...n,tab_name:r,last_seen_at:new Date().toISOString()};return this.entries.set(t,i),this.save(),i}remove(t){this.ensureLoaded();let s=this.entries.delete(t),n=this.sessionsByPid.delete(t);return this.outputTails.delete(t),this.pidOwnership.delete(t),(s||n)&&this.save(),s}claimPidOwnership(t,s,n=Date.now()){if(this.ensureLoaded(),!s)return"anonymous";let r=this.pidOwnership.get(t);return r?r.instance_id===s?(r.last_claim_at=n,"refreshed"):n-r.last_claim_at>Lc?(this.pidOwnership.set(t,{instance_id:s,last_claim_at:n}),"claimed"):"rejected":(this.pidOwnership.set(t,{instance_id:s,last_claim_at:n}),"claimed")}pidOwnershipSnapshot(){return this.ensureLoaded(),Array.from(this.pidOwnership.entries()).map(([t,s])=>({shell_pid:t,instance_id:s.instance_id,last_claim_at:s.last_claim_at}))}sync(t){this.ensureLoaded();let s=new Date().toISOString(),n=0,r=0;for(let i of t){let o=this.entries.get(i.shell_pid),a=jt(i.tab_name,o?.tab_name),c=o?.opened_at??i.opened_at;this.entries.set(i.shell_pid,{...i,tab_name:a,opened_at:c,last_seen_at:s}),o?(o.tab_name!==a||o.cwd!==i.cwd)&&r++:n++}return this.gc(),this.save(),{added:n,updated:r,removed:0}}get(t){return this.ensureLoaded(),this.entries.get(t)??null}all(){return this.ensureLoaded(),this.gc(),Array.from(this.entries.values())}size(){return this.ensureLoaded(),this.entries.size}linkSession(t,s){this.ensureLoaded();let n=this.sessionsByPid.get(s);n||(n=new Set,this.sessionsByPid.set(s,n)),n.has(t)||(n.add(t),this.save())}sessionsFor(t){return this.ensureLoaded(),Array.from(this.sessionsByPid.get(t)??[])}isSessionAutoLinked(t){this.ensureLoaded();for(let s of this.sessionsByPid.values())if(s.has(t))return!0;return!1}unlinkSession(t){this.ensureLoaded();let s=!1;for(let[n,r]of this.sessionsByPid)r.delete(t)&&(s=!0,r.size===0&&this.sessionsByPid.delete(n));return s&&this.save(),s}pushPending(t){this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.push({...t})}takePendingMatched(t){this.ensureLoaded(),this.gcPending();let s=xc({pending:this.pendingClaudeStarts,shellPid:t.shellPid,cwd:t.cwd,withinMs:t.withinMs,now:Date.now()});if(s.kind==="pid-match"||s.kind==="singleton-cwd"){let n=this.pendingClaudeStarts.indexOf(s.entry);n>=0&&this.pendingClaudeStarts.splice(n,1)}return s}pendingSize(){return this.ensureLoaded(),this.gcPending(),this.pendingClaudeStarts.length}deferSessionLink(t,s,n,r){this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.set(t,{parent_shell_pid:s,queued_at:Date.now(),cwd:n,git_branch:r})}allDeferredLinks(){return this.ensureLoaded(),this.gcDeferredLinks(),Array.from(this.deferredLinks.entries()).map(([t,s])=>({session_id:t,...s}))}resolveDeferredLink(t){return this.deferredLinks.delete(t)}deferredLinkSize(){return this.ensureLoaded(),this.gcDeferredLinks(),this.deferredLinks.size}gcDeferredLinks(){let t=Date.now()-9e4;for(let[s,n]of this.deferredLinks)n.queued_at<t&&this.deferredLinks.delete(s)}gcPending(){let t=Date.now()-Nc;this.pendingClaudeStarts.length!==0&&(this.pendingClaudeStarts=this.pendingClaudeStarts.filter(s=>{let n=Date.parse(s.started_at);return Number.isFinite(n)&&n>=t}))}outputTails=new Map;setOutputTail(t,s,n){this.ensureLoaded(),this.outputTails.set(t,{text:s,captured_at:n})}getOutputTail(t){return this.ensureLoaded(),this.outputTails.get(t)??null}allOutputTails(){return this.ensureLoaded(),new Map(this.outputTails)}removeOutputTail(t){return this.outputTails.delete(t)}setOrigin(t,s){this.ensureLoaded(),this.origins.set(t,s),this.gcOrigins()}getOrigin(t){return this.ensureLoaded(),this.origins.get(t)??null}removeOrigin(t){return this.ensureLoaded(),this.origins.delete(t)}allOrigins(){return this.ensureLoaded(),this.gcOrigins(),new Map(this.origins)}originSize(){return this.ensureLoaded(),this.origins.size}gc(){let t=Date.now()-rr;for(let[s,n]of this.entries){let r=Date.parse(n.last_seen_at);!Number.isNaN(r)&&r<t&&this.entries.delete(s)}}gcOrigins(){let t=Date.now()-rr;for(let[s,n]of this.origins)n.detectedAt<t&&this.origins.delete(s)}reapStaleLinks(t=10080*60*1e3){this.ensureLoaded();let n=Date.now()-t,r=0,i=0;for(let[o,a]of this.sessionsByPid){let c=this.entries.get(o);if(c){let d=Date.parse(c.last_seen_at);if(Number.isFinite(d)&&d>=n)continue}i+=a.size,this.sessionsByPid.delete(o),c&&(this.entries.delete(o),r++)}return(r||i)&&this.save(),{pruned_pids:r,pruned_sessions:i}}gcDeadPids(){this.ensureLoaded();let t=0,s=0;for(let n of[...this.entries.keys()]){let r=!0;try{process.kill(n,0)}catch(o){o.code==="ESRCH"&&(r=!1)}if(r)continue;this.entries.delete(n),t++;let i=this.sessionsByPid.get(n);i&&(s+=i.size,this.sessionsByPid.delete(n)),this.outputTails.delete(n),this.pidOwnership.delete(n)}return(t||s)&&this.save(),{pruned_pids:t,pruned_sessions:s}}},Ic=new Bt});import{execFile as kc}from"node:child_process";import{promisify as Cc}from"node:util";var km,lr=b(()=>{"use strict";km=Cc(kc)});import{execFile as Dc}from"node:child_process";import{promisify as Mc}from"node:util";var jm,Hm,dr=b(()=>{"use strict";cr();ke();N();lr();jm=Mc(Dc),Hm=3600*1e3});var ur=b(()=>{"use strict"});import{z as K}from"zod";var Ym,pr=b(()=>{"use strict";N();Ym=K.object({enabled:K.boolean().default(!1),model:K.string().optional(),ratePerMinute:K.number().int().min(1).max(600).default(30),lastProcessedSessionId:K.string().nullable().default(null),backfillPaused:K.boolean().default(!1),autoExtractEnabled:K.boolean().default(!1),autoExtractIntervalMinutes:K.number().int().min(5).max(720).default(60),autoExtractBatchSize:K.number().int().min(1).max(20).default(1),autoResumeWorker:K.boolean().default(!1)})});var mr=b(()=>{"use strict";N();wt();pr()});var gr=b(()=>{"use strict"});import{execFile as Fc}from"node:child_process";import{promisify as Pc}from"node:util";var rg,_r=b(()=>{"use strict";N();rg=Pc(Fc)});import{z as Wt}from"zod";var ag,fr=b(()=>{"use strict";ag=Wt.object({heuristicEnabled:Wt.boolean().default(!0),agentEnabled:Wt.boolean().default(!1)})});import{basename as ug,join as Xt}from"node:path";var hr,_g,fg,Er=b(()=>{"use strict";N();D();ut();hr=Xt(S,"auto-rules"),_g=Xt(hr,"rules.json"),fg=Xt(hr,"suggestions.json")});var Tr=b(()=>{"use strict"});var Sr=b(()=>{"use strict"});import{watch as Dg}from"chokidar";import{readdirSync as Uc,statSync as Fg}from"node:fs";import{basename as Hg,join as $c}from"node:path";function br(e){let t=e.split(/[/\\]/),s=t.findIndex(n=>n==="projects");return s===-1||s+1>=t.length?null:t[s+1]??null}function yr(e){return e.replace(/\\/g,"/").includes("/subagents/")}function*Gt(e){let t;try{t=Uc(e,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let s of t){if(s.isSymbolicLink())continue;let n=$c(e,s.name);s.isDirectory()?yield*Gt(n):s.isFile()&&s.name.endsWith(".jsonl")&&(yield n)}}var wr=b(()=>{"use strict";D();N();nr();dr();ur();mr();gr();_r();yt();fr();Er();Et();Tr();ke();Sr()});import{execFileSync as Nr}from"node:child_process";function Yt(e={}){let t=e.psOutput??jc(),s=e.isProcessAlive??Hc,n=e.getParentCommand??Bc,r=[];for(let i of t.split(`
756
+ `)){let o=i.trim();if(!o||!o.includes("dist/mcp/server.js")||Wc(o))continue;let a=o.split(/\s+/);if(a.length<5)continue;let c=Number(a[0]),d=Number(a[1]),l=a[2],u=Number(a[3]);if(!Number.isFinite(c)||!Number.isFinite(d))continue;let p=d>1&&s(d);r.push({pid:c,ppid:d,parentAlive:p,etimeSeconds:Xc(l),pcpu:Number.isFinite(u)?u:0,orphan:!p,parentCommand:p?n(d):null})}return r}function jc(){try{return Nr("ps",["-eo","pid,ppid,etime,pcpu,command"],{encoding:"utf8",timeout:2e3,maxBuffer:5*1024*1024})}catch(e){let t=e instanceof Error?e.message:String(e);return process.stderr.write(`[mcp-processes] ps -eo failed: ${t}
757
+ `),""}}function Hc(e){if(!Number.isFinite(e)||e<=1)return!1;try{return process.kill(e,0),!0}catch{return!1}}function Bc(e){if(!Number.isFinite(e)||e<=1)return null;try{let s=Nr("ps",["-p",String(e),"-o","command="],{encoding:"utf8",timeout:1e3,maxBuffer:1048576}).trim();return s.length?s.slice(0,200):null}catch{return null}}function Wc(e){let t=e.split(/\s+/);if(t.length<5)return!1;let s=t[4]??"";return s.endsWith("/grep")||s==="grep"||s.endsWith("/awk")||s==="awk"||s.endsWith("/rg")||s==="rg"}function Xc(e){if(!e)return 0;let t=0,s=e,n=e.indexOf("-");n>=0&&(t=Rr(e.slice(0,n)),s=e.slice(n+1));let r=s.split(":").map(Rr),i=0,o=0,a=0;return r.length===3?[i,o,a]=r:r.length===2?[o,a]=r:r.length===1&&(a=r[0]),t*86400+i*3600+o*60+a}function Rr(e){let t=Number(e);return Number.isFinite(t)?t:0}function Ye(e){return e.pcpu>=Gc&&e.etimeSeconds>=Yc}var Gc,Yc,zt=b(()=>{"use strict";Gc=50,Yc=60});var i_,Lr,Ar=b(()=>{"use strict";Ne();zt();i_=5*6e4,Lr=1073741824});var jr={};Ke(jr,{buildHealthReport:()=>Ur,buildPipelineDiagnostic:()=>Fr,checkIngestStaleness:()=>$r,detectLabelCollisions:()=>Pr,getFreeDiskBytes:()=>rl,runDoctor:()=>nl});import{existsSync as Ir,readFileSync as zc,statSync as Jt,statfsSync as kr}from"node:fs";import{join as Cr}from"node:path";import*as vr from"node:http";function Vc(e){let t=[];for(let s of e)if(s.alias){if(Kc.test(s.alias)){t.push({session_id:s.session_id,alias:s.alias,violation:"fabricated-origin-label",cwd:s.cwd});continue}if(s.cwd){let n=s.cwd.replace(/\/+$/,"").split("/").pop();n&&s.alias.startsWith(`${n} \xB7 `)&&t.push({session_id:s.session_id,alias:s.alias,violation:"fabricated-cwd-branch",cwd:s.cwd})}}return t}function qc(){let e=Cr(S,"daemon.port");if(!Ir(e))return null;try{let t=zc(e,"utf8").trim();if(t.startsWith("{")){let n=JSON.parse(t);return typeof n.port=="number"?n.port:null}let s=Number.parseInt(t,10);return Number.isFinite(s)&&s>0&&s<65536?s:null}catch{return null}}function Zc(e,t,s=1500){return new Promise(n=>{let r=vr.request({host:"127.0.0.1",port:e,path:t,method:"GET",timeout:s,headers:{host:"127.0.0.1","user-agent":"recall-doctor"}},i=>{let o=[];i.on("data",a=>o.push(Buffer.isBuffer(a)?a:Buffer.from(a))),i.on("end",()=>{if(!i.statusCode||i.statusCode<200||i.statusCode>=300){n(null);return}try{n(JSON.parse(Buffer.concat(o).toString("utf8")))}catch{n(null)}})});r.on("error",()=>n(null)),r.on("timeout",()=>{r.destroy(),n(null)}),r.end()})}function Qc(){let e=Cr(S,"terminals.json");if(!Ir(e))return{exists:!1,mtimeMs:null,ageSeconds:null};try{let t=Jt(e),s=Math.floor((Date.now()-t.mtimeMs)/1e3);return{exists:!0,mtimeMs:t.mtimeMs,ageSeconds:s}}catch{return{exists:!1,mtimeMs:null,ageSeconds:null}}}function el(){let e=new Date(Date.now()-Kt*36e5).toISOString();try{let t=m().prepare(`SELECT
758
758
  COUNT(*) AS total,
759
759
  SUM(CASE WHEN sa.alias IS NULL OR sa.alias = '' THEN 1 ELSE 0 END) AS without_alias,
760
760
  SUM(CASE WHEN (sa.alias IS NULL OR sa.alias = '')
761
761
  AND s.auto_title_source = 'heuristic' THEN 1 ELSE 0 END) AS heuristic_only
762
762
  FROM sessions s
763
763
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
764
- WHERE s.started_at >= ?`).get(e),s=t.total??0,n=t.without_alias??0,r=t.heuristic_only??0,i=s>0?r/s:0;return{total:s,withoutAlias:n,heuristicOnly:r,fractionHeuristic:i}}catch{return{total:0,withoutAlias:0,heuristicOnly:0,fractionHeuristic:0}}}async function Mr(){let e=$c(),t=Hc(),s=Bc(),n=!1,r=null,i=null,o=null,a=null,c=null;if(e){let u=await jc(e,"/api/health");if(u){n=!0,r=typeof u.uptimeSeconds=="number"?u.uptimeSeconds:null,i=typeof u.version=="string"?u.version:null,o=typeof u.pipeline?.silentTerminalRejections=="number"?u.pipeline.silentTerminalRejections:null,a=u.pipeline?.lastTerminalSyncAt??null;let p=u.pipeline?.autoExtract;p&&(c={circuitBroken:p.circuitBroken===!0,reason:p.reason??null,brokenAt:typeof p.brokenAt=="number"?p.brokenAt:null,consecutiveZeroTokenRuns:typeof p.consecutiveZeroTokenRuns=="number"?p.consecutiveZeroTokenRuns:0})}}let d=[];if(n||d.push("Daemon not reachable on 127.0.0.1 \u2014 start it with `recall start` before further diagnosis."),o!==null&&o>0&&d.push(`Daemon rejected ${o.toLocaleString()} /api/terminal/* request(s) without a valid X-Recall-Token. The editor extension is outdated relative to this daemon \u2014 tab names are not flowing through. Reinstall: \`code --install-extension extensions/vscode/clauderecall-vscode-*.vsix\`, then reload the extension host (Cmd/Ctrl+Shift+P \u2192 "Developer: Restart Extension Host").`),n&&r!==null&&r>30)if(!a)d.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 u=Date.now()-Date.parse(a);Number.isFinite(u)&&u>Cr&&d.push(`Last successful /api/terminal/sync was ${Math.round(u/6e4)} min ago \u2014 extension may have crashed, been disabled, or is failing auth. Run \`recall doctor\` again after restarting the extension host.`)}return!n&&t.exists&&t.ageSeconds!==null&&t.ageSeconds>24*3600&&d.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.`),c?.circuitBroken&&d.push(`Auto-extract circuit breaker tripped \u2014 ${c.reason??"reason unknown"}. Run \`recall semantic auto-extract off\` then \`... on\` to reset, or restart the daemon.`),s.total>=3&&s.fractionHeuristic>=Dr&&d.push(`${s.heuristicOnly}/${s.total} sessions in the last ${Ht}h (${Math.round(s.fractionHeuristic*100)}%) fell back to the heuristic first-message title. A healthy pipeline rate is < 20%. Either the extension is not syncing tab names, or the correlator cannot disambiguate. Reinstall the extension and verify the rejection counter drops to 0.`),{state:n?d.length>0?"degraded":"ok":"down",flags:d,daemon:{running:n,port:e,uptimeSeconds:r,version:i},runtime:{silentTerminalRejections:o,lastTerminalSyncAt:a,autoExtract:c},terminalsJson:t,recentSessions:s}}function V(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 Ar(e){try{return jt(e).size}catch{return 0}}function Or(e){try{return m().prepare(`SELECT COUNT(*) AS n FROM ${e}_data WHERE block = 1`).get().n}catch{return 0}}function Fr(){try{return m().prepare(`SELECT p.name AS project,
764
+ WHERE s.started_at >= ?`).get(e),s=t.total??0,n=t.without_alias??0,r=t.heuristic_only??0,i=s>0?r/s:0;return{total:s,withoutAlias:n,heuristicOnly:r,fractionHeuristic:i}}catch{return{total:0,withoutAlias:0,heuristicOnly:0,fractionHeuristic:0}}}async function Fr(){let e=qc(),t=Qc(),s=el(),n=!1,r=null,i=null,o=null,a=null,c=null;if(e){let u=await Zc(e,"/api/health");if(u){n=!0,r=typeof u.uptimeSeconds=="number"?u.uptimeSeconds:null,i=typeof u.version=="string"?u.version:null,o=typeof u.pipeline?.silentTerminalRejections=="number"?u.pipeline.silentTerminalRejections:null,a=u.pipeline?.lastTerminalSyncAt??null;let p=u.pipeline?.autoExtract;p&&(c={circuitBroken:p.circuitBroken===!0,reason:p.reason??null,brokenAt:typeof p.brokenAt=="number"?p.brokenAt:null,consecutiveZeroTokenRuns:typeof p.consecutiveZeroTokenRuns=="number"?p.consecutiveZeroTokenRuns:0})}}let d=[];if(n||d.push("Daemon not reachable on 127.0.0.1 \u2014 start it with `recall start` before further diagnosis."),o!==null&&o>0&&d.push(`Daemon rejected ${o.toLocaleString()} /api/terminal/* request(s) without a valid X-Recall-Token. The editor extension is outdated relative to this daemon \u2014 tab names are not flowing through. Reinstall: \`code --install-extension extensions/vscode/clauderecall-vscode-*.vsix\`, then reload the extension host (Cmd/Ctrl+Shift+P \u2192 "Developer: Restart Extension Host").`),n&&r!==null&&r>30)if(!a)d.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 u=Date.now()-Date.parse(a);Number.isFinite(u)&&u>Dr&&d.push(`Last successful /api/terminal/sync was ${Math.round(u/6e4)} min ago \u2014 extension may have crashed, been disabled, or is failing auth. Run \`recall doctor\` again after restarting the extension host.`)}return!n&&t.exists&&t.ageSeconds!==null&&t.ageSeconds>24*3600&&d.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.`),c?.circuitBroken&&d.push(`Auto-extract circuit breaker tripped \u2014 ${c.reason??"reason unknown"}. Run \`recall semantic auto-extract off\` then \`... on\` to reset, or restart the daemon.`),s.total>=3&&s.fractionHeuristic>=Mr&&d.push(`${s.heuristicOnly}/${s.total} sessions in the last ${Kt}h (${Math.round(s.fractionHeuristic*100)}%) fell back to the heuristic first-message title. A healthy pipeline rate is < 20%. Either the extension is not syncing tab names, or the correlator cannot disambiguate. Reinstall the extension and verify the rejection counter drops to 0.`),{state:n?d.length>0?"degraded":"ok":"down",flags:d,daemon:{running:n,port:e,uptimeSeconds:r,version:i},runtime:{silentTerminalRejections:o,lastTerminalSyncAt:a,autoExtract:c},terminalsJson:t,recentSessions:s}}function V(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 Or(e){try{return Jt(e).size}catch{return 0}}function xr(e){try{return m().prepare(`SELECT COUNT(*) AS n FROM ${e}_data WHERE block = 1`).get().n}catch{return 0}}function Pr(){try{return m().prepare(`SELECT p.name AS project,
765
765
  COALESCE(NULLIF(sa.alias, ''), s.auto_title, substr(s.first_user_message, 1, 60)) AS label,
766
766
  COUNT(*) AS count,
767
767
  MAX(CASE WHEN sa.alias IS NOT NULL AND sa.alias != '' THEN 1 ELSE 0 END) AS any_aliased
@@ -773,37 +773,37 @@ ${u+1}. ${p}`:`${u+1}. ${p}`}).join(`
773
773
  GROUP BY p.name, label
774
774
  HAVING count >= 2
775
775
  ORDER BY count DESC, label ASC
776
- 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 Pr(e){let t=m(),s=t.pragma("page_size",{simple:!0})||4096,n=t.pragma("page_count",{simple:!0})||0,r=t.pragma("freelist_count",{simple:!0})||0;e?.("Checking database integrity");let i="ok";try{let D=t.pragma("quick_check").map(x=>x.quick_check);i=D.length===1&&D[0]==="ok"?"ok":D.join("; ")}catch(A){i=`check failed: ${A.message}`}let o=Ar(me),a=Ar(`${me}-wal`),c=0,d=0;try{let A=Ir(S);c=Number(A.bavail)*Number(A.bsize),d=Number(A.blocks)*Number(A.bsize)}catch{}e?.("Counting rows");let l=t.prepare(`SELECT
776
+ 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 Ur(e){let t=m(),s=t.pragma("page_size",{simple:!0})||4096,n=t.pragma("page_count",{simple:!0})||0,r=t.pragma("freelist_count",{simple:!0})||0;e?.("Checking database integrity");let i="ok";try{let O=t.pragma("quick_check").map(q=>q.quick_check);i=O.length===1&&O[0]==="ok"?"ok":O.join("; ")}catch(R){i=`check failed: ${R.message}`}let o=Or(ge),a=Or(`${ge}-wal`),c=0,d=0;try{let R=kr(S);c=Number(R.bavail)*Number(R.bsize),d=Number(R.blocks)*Number(R.bsize)}catch{}e?.("Counting rows");let l=t.prepare(`SELECT
777
777
  (SELECT COUNT(*) FROM projects) AS projects,
778
778
  (SELECT COUNT(*) FROM sessions) AS sessions,
779
779
  (SELECT COUNT(*) FROM messages) AS messages,
780
- (SELECT COUNT(*) FROM message_usage) AS message_usage`).get(),u=0;try{u=t.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get().n}catch{}e?.("Measuring FTS fragmentation");let p=[];c>0&&c<1*1024**3&&p.push(`Disk free is ${V(c)} \u2014 heavy operations (synthesis, extract-outputs, vector ingest) may fail.`);let g=100*1024**2,_=a>=Nr?"error":a>=g?"warn":"ok";_==="error"?p.push(`WAL is ${V(a)} \u2014 readers are pinning the checkpoint frontier. Run \`recall mcp-prune\` to release stuck MCP children, then \`recall optimize\` to truncate.`):_==="warn"&&p.push(`WAL is ${V(a)} \u2014 run \`recall optimize\` to truncate it.`);let f=wr(),E=f.filter(A=>A.orphan);E.length>0&&p.push(`${E.length} orphaned MCP child${E.length===1?"":"ren"} (pid${E.length===1?"":"s"}: ${E.map(A=>A.pid).join(", ")}). Each holds a SQLite read connection. Run \`recall mcp-prune\` to clean up.`),r>n*.2&&n>1e3&&p.push(`${r.toLocaleString()} free pages (${(r/n*100).toFixed(0)}% of file) \u2014 \`recall optimize --vacuum\` will reclaim them.`);let T=Or("messages_fts"),w=Or("sessions_fts");T>16&&p.push(`messages_fts has ${T} segments \u2014 \`recall optimize\` will merge them.`);let N=0;try{N=t.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}catch{}let L=!1;try{L=t.prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value==="1"}catch{}return!L&&N>0?p.push(`${N.toLocaleString()} rows in chunk_queue with semantic disabled \u2014 schema gate is stale; restart daemon (v0.67+) to migrate.`):N>1e5&&p.push(`chunk_queue has ${N.toLocaleString()} pending rows \u2014 embedder is behind. \`recall semantic backfill\` to drain manually.`),{db:{sizeBytes:o,walSizeBytes:a,pageCount:n,pageSize:s,freelistCount:r,freelistBytes:r*s,integrity:i},disk:{freeBytes:c,totalBytes:d},fts:{messages:{fragments:T},sessions:{fragments:w}},vectors:{rows:u},rows:{projects:l.projects,sessions:l.sessions,messages:l.messages,messageUsage:l.message_usage},chunkQueue:{size:N,semanticEnabled:L},wal:{sizeBytes:a,level:_},mcpProcesses:f,warnings:p}}function Wc(e){if(!e)return{stage:()=>{},done:()=>{}};let t=!!process.stderr.isTTY,s="",n=0,r=()=>{if(!s)return;let i=Date.now()-n,o=i<1e3?`${i}ms`:`${(i/1e3).toFixed(1)}s`;t?process.stderr.write(`\r\x1B[2K ${h.ok("\u2713")} ${s} ${h.dim(`(${o})`)}
780
+ (SELECT COUNT(*) FROM message_usage) AS message_usage`).get(),u=0;try{u=t.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get().n}catch{}e?.("Measuring FTS fragmentation");let p=[];c>0&&c<1*1024**3&&p.push(`Disk free is ${V(c)} \u2014 heavy operations (synthesis, extract-outputs, vector ingest) may fail.`);let g=100*1024**2,_=a>=Lr?"error":a>=g?"warn":"ok";_==="error"?p.push(`WAL is ${V(a)} \u2014 readers are pinning the checkpoint frontier. Run \`recall mcp-prune\` to release stuck MCP children, then \`recall optimize\` to truncate.`):_==="warn"&&p.push(`WAL is ${V(a)} \u2014 run \`recall optimize\` to truncate it.`);let h=Yt(),E=h.filter(R=>R.orphan);E.length>0&&p.push(`${E.length} orphaned MCP child${E.length===1?"":"ren"} (pid${E.length===1?"":"s"}: ${E.map(R=>R.pid).join(", ")}). Each holds a SQLite read connection. Run \`recall mcp-prune\` to clean up.`);let T=h.filter(Ye);T.length>0&&p.push(`${T.length} MCP child${T.length===1?"":"ren"} burning CPU (pid${T.length===1?"":"s"}: ${T.map(R=>R.pid).join(", ")}). Likely runaway vec0 kNN. Run \`recall stop --all\` or \`recall mcp-prune --all\`.`),r>n*.2&&n>1e3&&p.push(`${r.toLocaleString()} free pages (${(r/n*100).toFixed(0)}% of file) \u2014 \`recall optimize --vacuum\` will reclaim them.`);let w=xr("messages_fts"),v=xr("sessions_fts");w>16&&p.push(`messages_fts has ${w} segments \u2014 \`recall optimize\` will merge them.`);let L=0;try{L=t.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}catch{}let P=!1;try{P=t.prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value==="1"}catch{}return!P&&L>0?p.push(`${L.toLocaleString()} rows in chunk_queue with semantic disabled \u2014 schema gate is stale; restart daemon (v0.67+) to migrate.`):L>1e5&&p.push(`chunk_queue has ${L.toLocaleString()} pending rows \u2014 embedder is behind. \`recall semantic backfill\` to drain manually.`),{db:{sizeBytes:o,walSizeBytes:a,pageCount:n,pageSize:s,freelistCount:r,freelistBytes:r*s,integrity:i},disk:{freeBytes:c,totalBytes:d},fts:{messages:{fragments:w},sessions:{fragments:v}},vectors:{rows:u},rows:{projects:l.projects,sessions:l.sessions,messages:l.messages,messageUsage:l.message_usage},chunkQueue:{size:L,semanticEnabled:P},wal:{sizeBytes:a,level:_},mcpProcesses:h,warnings:p}}function tl(e){if(!e)return{stage:()=>{},done:()=>{}};let t=!!process.stderr.isTTY,s="",n=0,r=()=>{if(!s)return;let i=Date.now()-n,o=i<1e3?`${i}ms`:`${(i/1e3).toFixed(1)}s`;t?process.stderr.write(`\r\x1B[2K ${f.ok("\u2713")} ${s} ${f.dim(`(${o})`)}
781
781
  `):process.stderr.write(` \u2713 ${s} (${o})
782
- `),s=""};return{stage(i){r(),s=i,n=Date.now(),t?process.stderr.write(` ${h.dim("\u2026")} ${s}`):process.stderr.write(` \u2026 ${s}
783
- `)},done:r}}function Xc(e){if(console.log(h.dim("\u2014 MCP processes \u2014")),e.length===0){console.log(h.ok(" \u2713 no MCP children running"));return}let t=e.filter(r=>r.orphan),s=e.reduce((r,i)=>Math.max(r,i.etimeSeconds),0),n=e.length-t.length;if(console.log(` ${e.length} total, ${n} with live parent, ${t.length===0?h.ok("0 orphaned"):h.err(`${t.length} orphaned`)} (oldest ${Ve(s)})`),t.length>0){console.log(h.dim(" Orphans hold a SQLite read connection and can stall WAL checkpoints. Run `recall mcp-prune` to kill them."));for(let r of t.slice(0,5))console.log(` ${h.dim("\u2022")} pid ${r.pid} age ${Ve(r.etimeSeconds)} ppid=${r.ppid} (gone)`)}}function Ur(){let e=m(),t=e.prepare("SELECT encoded_path FROM projects").all(),s=new Set(t.map(d=>d.encoded_path));if(s.size===0)return{status:"ok",staleCount:0,scanned:0,sampleFiles:[],message:"Ingest freshness: ok (no projects indexed yet \u2014 nothing to monitor)."};let n=e.prepare("SELECT file_mtime FROM sessions WHERE file_path = ? LIMIT 1"),r=0,i=0,o=[];for(let d of $t(Ke)){if(Tr(d))continue;let l=Er(d);if(!l||!s.has(l))continue;r+=1;let u;try{u=jt(d).mtimeMs}catch{continue}let p=n.get(d);(!p||p.file_mtime<u)&&(i+=1,o.length<5&&o.push(d))}if(i===0)return{status:"ok",staleCount:0,scanned:r,sampleFiles:[],message:`Ingest freshness: ok (scanned ${r} JSONL${r===1?"":"s"} in ${s.size} known project${s.size===1?"":"s"}, none newer than the index).`};let a=i>10?"fail":"warn",c=o.slice(0,3).map(d=>d.split(/[/\\]/).pop()??d).join(", ");return{status:a,staleCount:i,scanned:r,sampleFiles:o,message:`Ingest freshness: ${a} \u2014 ${i} JSONL${i===1?"":" files"} newer than the index (sample: ${c}). Run \`recall index\` to force a reindex, or restart the daemon if this is persistent.`}}async function Gc(e={}){let t=Wc(!e.json);t.stage("Scanning session aliases");let s=m().prepare(`SELECT sa.session_id AS session_id, sa.alias AS alias, s.cwd AS cwd
782
+ `),s=""};return{stage(i){r(),s=i,n=Date.now(),t?process.stderr.write(` ${f.dim("\u2026")} ${s}`):process.stderr.write(` \u2026 ${s}
783
+ `)},done:r}}function sl(e){if(console.log(f.dim("\u2014 MCP processes \u2014")),e.length===0){console.log(f.ok(" \u2713 no MCP children running"));return}let t=e.filter(i=>i.orphan),s=e.reduce((i,o)=>Math.max(i,o.etimeSeconds),0),n=e.length-t.length;if(console.log(` ${e.length} total, ${n} with live parent, ${t.length===0?f.ok("0 orphaned"):f.err(`${t.length} orphaned`)} (oldest ${we(s)})`),t.length>0){console.log(f.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(` ${f.dim("\u2022")} pid ${i.pid} age ${we(i.etimeSeconds)} ppid=${i.ppid} (gone)`)}let r=e.filter(Ye);if(r.length>0){console.log(f.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 o=i.parentCommand?`parent ${i.parentCommand}`:`ppid ${i.ppid}`;console.log(f.dim(` pid ${i.pid} ${i.pcpu.toFixed(0)}%cpu ${we(i.etimeSeconds)} elapsed (${o})`))}console.log(f.dim(" Run `recall stop --all` for one-command recovery, or `recall mcp-prune --all` to keep the daemon alive."))}}function $r(){let e=m(),t=e.prepare("SELECT encoded_path FROM projects").all(),s=new Set(t.map(d=>d.encoded_path));if(s.size===0)return{status:"ok",staleCount:0,scanned:0,sampleFiles:[],message:"Ingest freshness: ok (no projects indexed yet \u2014 nothing to monitor)."};let n=e.prepare("SELECT file_mtime FROM sessions WHERE file_path = ? LIMIT 1"),r=0,i=0,o=[];for(let d of Gt(Qe)){if(yr(d))continue;let l=br(d);if(!l||!s.has(l))continue;r+=1;let u;try{u=Jt(d).mtimeMs}catch{continue}let p=n.get(d);(!p||p.file_mtime<u)&&(i+=1,o.length<5&&o.push(d))}if(i===0)return{status:"ok",staleCount:0,scanned:r,sampleFiles:[],message:`Ingest freshness: ok (scanned ${r} JSONL${r===1?"":"s"} in ${s.size} known project${s.size===1?"":"s"}, none newer than the index).`};let a=i>10?"fail":"warn",c=o.slice(0,3).map(d=>d.split(/[/\\]/).pop()??d).join(", ");return{status:a,staleCount:i,scanned:r,sampleFiles:o,message:`Ingest freshness: ${a} \u2014 ${i} JSONL${i===1?"":" files"} newer than the index (sample: ${c}). Run \`recall index\` to force a reindex, or restart the daemon if this is persistent.`}}async function nl(e={}){let t=tl(!e.json);t.stage("Scanning session aliases");let s=m().prepare(`SELECT sa.session_id AS session_id, sa.alias AS alias, s.cwd AS cwd
784
784
  FROM session_aliases sa
785
785
  LEFT JOIN sessions s ON s.id = sa.session_id
786
- WHERE sa.alias IS NOT NULL AND sa.alias != ''`).all(),n=Uc(s),r=Pr(t.stage);t.stage("Probing daemon");let i=await Mr();t.stage("Detecting label collisions");let o=Fr();t.stage("Checking ingest freshness");let a=Ur();if(t.done(),e.json){process.stdout.write(JSON.stringify({scanned:s.length,violations:n.length,items:n,health:r,pipeline:i,labelCollisions:o,ingestFreshness:a},null,2)),process.stdout.write(`
787
- `);let l=i.state==="degraded",u=a.status==="fail";return n.length===0&&r.db.integrity==="ok"&&!l&&!u?0:1}console.log(h.dim("\u2014 System health \u2014")),console.log(` Database ${V(r.db.sizeBytes)} (${r.rows.messages.toLocaleString()} messages across ${r.rows.sessions.toLocaleString()} sessions, ${r.rows.projects.toLocaleString()} projects)`);{let l=r.wal,u=l.level==="error"?h.err(V(l.sizeBytes)):l.level==="warn"?h.warn(V(l.sizeBytes)):h.ok(V(l.sizeBytes)),p=l.level==="error"?" (readers are pinning the checkpoint frontier \u2014 see warnings below)":l.level==="warn"?" (above 100 MB \u2014 daemon will WARN until it drops)":" (daemon checkpoints PASSIVE every 60s, RESTART above 5 GB)";console.log(` WAL ${u}${p}`)}console.log(` Free pages ${r.db.freelistCount.toLocaleString()} (${V(r.db.freelistBytes)} reclaimable via VACUUM)`);{let l=r.fts.messages.fragments,u=r.fts.sessions.fragments,p=l===0&&u===0;console.log(` FTS segments messages=${l}, sessions=${u}`+(p?" (merged \u2014 search uses the index)":" (lower is faster \u2014 `recall optimize` merges them)"))}if(r.chunkQueue.size>0){let u=r.chunkQueue.size>1e5?h.warn(r.chunkQueue.size.toLocaleString()):r.chunkQueue.size.toLocaleString();console.log(` Embed queue ${u}`+(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 l=r.disk.freeBytes/r.disk.totalBytes*100;console.log(` Disk free ${V(r.disk.freeBytes)} of ${V(r.disk.totalBytes)} (${l.toFixed(1)}%)`)}if(console.log(` Integrity ${r.db.integrity==="ok"?h.ok("ok"):h.err(r.db.integrity)}`),r.warnings.length>0){console.log("");for(let l of r.warnings)console.log(` ${h.warn("!")} ${l}`)}if(console.log(""),Xc(r.mcpProcesses),console.log(""),console.log(h.dim("\u2014 Ingest freshness \u2014")),a.status==="ok")console.log(h.ok(` \u2713 ${a.scanned.toLocaleString()} JSONL${a.scanned===1?"":"s"} scanned, none newer than the index`));else if(a.status==="warn"){console.log(` ${h.warn(`${a.staleCount} stale file${a.staleCount===1?"":"s"}`)} newer than the index \u2014 live watcher may be missing events`);for(let l of a.sampleFiles.slice(0,5))console.log(` ${h.dim("\u2022")} ${l}`);console.log(h.dim(" Run `recall index` to force a reindex, or restart the daemon if persistent."))}else{console.log(` ${h.err(`${a.staleCount} stale file${a.staleCount===1?"":"s"}`)} newer than the index \u2014 significant ingest gap`);for(let l of a.sampleFiles.slice(0,5))console.log(` ${h.dim("\u2022")} ${l}`);console.log(h.err(" Restart the daemon (recall stop && recall start) \u2014 the live watcher has fallen behind."))}if(console.log(""),console.log(h.dim("\u2014 Pipeline health (tab-name \u2192 session alias) \u2014")),i.daemon.running?console.log(` Daemon ${h.ok("running")} (port ${i.daemon.port}, version ${i.daemon.version??"?"}, up ${i.daemon.uptimeSeconds!==null?Math.round(i.daemon.uptimeSeconds/60)+" min":"?"})`):console.log(` Daemon ${h.warn("not reachable")}`),i.runtime.silentTerminalRejections!==null){let l=i.runtime.silentTerminalRejections;console.log(` Auth rejections ${l===0?h.ok("0"):h.err(l.toLocaleString())} (extension /api/terminal/* requests denied without a valid X-Recall-Token)`)}if(i.runtime.autoExtract){let l=i.runtime.autoExtract;l.circuitBroken?console.log(` Auto-extract ${h.err("circuit broken")} (${l.reason??"unknown"} \u2014 toggle off/on to reset)`):l.consecutiveZeroTokenRuns>0&&console.log(` Auto-extract ${h.warn(`${l.consecutiveZeroTokenRuns} zero-token run(s)`)} (breaker trips at 3)`)}if(i.runtime.lastTerminalSyncAt!==null){let l=Date.now()-Date.parse(i.runtime.lastTerminalSyncAt),u=Number.isFinite(l)?Math.round(l/6e4):null,p=u!==null&&l>Cr;console.log(` Last ext sync ${p?h.warn(`${u} min ago`):h.ok(u===0?"just now":`${u} min ago`)} (most recent successful POST /api/terminal/sync)`)}else i.daemon.running&&console.log(` Last ext sync ${h.warn("never")} (no extension has called /api/terminal/sync since the daemon started)`);if(i.terminalsJson.exists&&i.terminalsJson.ageSeconds!==null){let l=Math.round(i.terminalsJson.ageSeconds/3600),u=i.terminalsJson.ageSeconds>24*3600;console.log(` terminals.json ${u?h.warn(`${l}h old`):h.ok(`${l}h old`)} (persisted registry mtime \u2014 fresh means extensions are connecting)`)}let c=i.recentSessions;if(c.total>0){let l=Math.round(c.fractionHeuristic*100),u=c.fractionHeuristic>=Dr&&c.total>=3;console.log(` Recent titles ${u?h.err(`${l}% heuristic`):h.ok(`${l}% heuristic`)} (${c.heuristicOnly}/${c.total} sessions in last ${Ht}h fell back to first-message title)`)}if(i.flags.length>0){console.log("");for(let l of i.flags)console.log(` ${h.warn("!")} ${l}`)}if(console.log(""),console.log(h.dim("\u2014 Label collisions (last 7d) \u2014")),o.length===0)console.log(h.ok(" \u2713 no session-list label collisions in the last week"));else{let l=o.filter(u=>!u.anyAliased);console.log(` ${h.warn(`${o.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 u of o.slice(0,5)){let p=u.anyAliased?h.dim("partial alias"):h.warn("NO alias"),g=u.label.length>60?`${u.label.slice(0,57)}\u2026`:u.label;console.log(` ${h.dim(`${u.count}\xD7`)} ${g} ${p} ${h.dim(`(${u.project})`)}`)}o.length>5&&console.log(h.dim(` \u2026 and ${o.length-5} more (--json for full list)`)),console.log(""),console.log(h.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(h.dim(" <!-- claude-recall-alias: T2.1 Run-Prospects -->")),console.log(h.dim(` The watcher will read the header, alias the session, and strip the marker from the
788
- displayed first user message. See docs/HANDOFF.md \u2192 "Alias header convention".`)),l.length>0&&(console.log(""),console.log(h.warn(` ${l.length} of these groups have NO alias on any row \u2014 strongest candidates
789
- for the header-alias fix above.`)))}if(console.log(""),console.log(h.dim("\u2014 Tab-name invariant \u2014")),n.length===0)return console.log(h.ok(` \u2713 holds across ${s.length.toLocaleString()} aliased session${s.length===1?"":"s"}`)),console.log(h.dim(" No fabricated origin labels (`VS Code \xB7 cwd \xB7 branch`) found. No deprecated cwd-branch synthesis found.")),r.db.integrity==="ok"&&a.status!=="fail"?0:1;console.log(h.err(`\u2717 ${n.length} invariant violation${n.length===1?"":"s"} found across ${s.length.toLocaleString()} aliased sessions`)),console.log("");let d=new Map;for(let l of n){let u=d.get(l.violation)??[];u.push(l),d.set(l.violation,u)}for(let[l,u]of d){console.log(h.warn(` ${l} (${u.length})`));for(let p of u.slice(0,10))console.log(` ${p.session_id.slice(0,8)} ${h.dim("\u2192")} ${JSON.stringify(p.alias)}`);u.length>10&&console.log(h.dim(` \u2026 and ${u.length-10} more (rerun with --json for the full list)`)),console.log("")}return console.log(h.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 Yc(){try{let e=Ir(S);return Number(e.bavail)*Number(e.bsize)}catch{return 0}}var Fc,Pc,Cr,Ht,Dr,jr=b(()=>{"use strict";vt();R();M();Sr();Rr();Lr();we();Fc=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],Pc=new RegExp(`^(${Fc.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);Cr=5*6e4,Ht=24,Dr=.5});var Hr={};Ge(Hr,{runOptimize:()=>qc});import{existsSync as zc,readFileSync as Jc}from"node:fs";import{join as Kc}from"node:path";function Vc(){let e=Kc(S,"daemon.pid");if(!zc(e))return!1;try{let t=parseInt(Jc(e,"utf-8").trim(),10);return!Number.isFinite(t)||t<=0?!1:(process.kill(t,0),!0)}catch{return!1}}async function Se(e,t){let s=Date.now();try{return t(),{step:e,ok:!0,durationMs:Date.now()-s}}catch(n){return{step:e,ok:!1,durationMs:Date.now()-s,error:n.message}}}async function qc(e={}){let t=m(),s=[];if(e.vacuum&&Vc())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)+`
790
- `),2):(console.error(h.err("\u2717 VACUUM requires the daemon to be stopped. Run `recall stop` first, then re-run with --vacuum.")),2);s.push(await Se("wal_checkpoint(TRUNCATE)",()=>{t.pragma("wal_checkpoint(TRUNCATE)")})),s.push(await Se("messages_fts optimize",()=>{t.exec("INSERT INTO messages_fts(messages_fts) VALUES('optimize');")})),s.push(await Se("sessions_fts optimize",()=>{t.exec("INSERT INTO sessions_fts(sessions_fts) VALUES('optimize');")})),s.push(await Se("PRAGMA optimize",()=>{t.exec("PRAGMA optimize")})),e.vacuum&&s.push(await Se("VACUUM",()=>{t.exec("VACUUM")}));let n=s.filter(r=>!r.ok);if(e.json)return process.stdout.write(JSON.stringify({ok:n.length===0,steps:s,vacuum:!!e.vacuum},null,2)+`
791
- `),n.length===0?0:1;for(let r of s){let i=r.ok?h.ok("\u2713"):h.err("\u2717"),o=`${r.durationMs} ms`;console.log(` ${i} ${r.step.padEnd(28)} ${h.dim(o)}`),r.error&&console.log(` ${h.err(r.error)}`)}return n.length===0?(console.log(""),console.log(h.ok("All maintenance passes completed.")),e.vacuum||console.log(h.dim(" Run `recall optimize --vacuum` (with the daemon stopped) to reclaim disk pages from deleted rows.")),0):(console.log(""),console.log(h.warn(`${n.length} step(s) failed \u2014 review the errors above.`)),1)}var Br=b(()=>{"use strict";vt();R();M()});R();import{McpServer as Zc}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as Qc}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as y}from"zod";import{fileURLToPath as el}from"node:url";we();function qt(e){let t=e.onShutdown,s=e.pollIntervalMs??5e3,n=e.logger??(_=>{process.stderr.write(_+`
792
- `)}),r=e.stdin??process.stdin,i=e.getPpid??(()=>process.ppid),o=e.getPid??(()=>process.pid),a=!1,c=null,d=i(),l=()=>{c&&(clearInterval(c),c=null),r.removeListener("end",p),r.removeListener("close",g)},u=_=>{if(a)return;a=!0,n(`[parent-death-guard] shutdown triggered: ${_} (pid=${o()} initialPpid=${d} currentPpid=${i()})`),l();let f;try{f=t()}catch(E){n(`[parent-death-guard] onShutdown threw: ${ye(E)}`);return}f&&typeof f.then=="function"&&f.catch(E=>{n(`[parent-death-guard] onShutdown rejected: ${ye(E)}`)})},p=()=>u("stdin end"),g=()=>u("stdin close");return r.on("end",p),r.on("close",g),d!==1&&(c=setInterval(()=>{let _=i();_!==d&&u(`ppid changed ${d} -> ${_}`)},s),typeof c.unref=="function"&&c.unref()),{stop:l}}var Qr=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,ei=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,ti=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function si(e){return e.replace(Qr,"").trim()}function ni(e){let t=e.replace(ei,"[tool call]");return t=t.replace(ti,"[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,`
793
-
794
- `),t.trim()}function ri(e){return e.role??e.type??"message"}function Zt(e,t,s={}){let n=s.mode??"condensed",r=s.includeSidechain===!0,i=s.since?Date.parse(s.since):0,o=t.filter(l=>!(!r&&l.is_sidechain===1||i&&l.timestamp&&Date.parse(l.timestamp)<i)),a=[];s.prelude&&(a.push(s.prelude.trim()),a.push("")),a.push(`# Claude Recall, past session context (${n})`),a.push(""),a.push(`- **Project**: ${e.project_name}`),e.decoded_path&&a.push(`- **Path**: \`${e.decoded_path}\``),a.push(`- **Session ID**: \`${e.id}\``),e.started_at&&a.push(`- **Started**: ${e.started_at}`),e.ended_at&&a.push(`- **Ended**: ${e.ended_at}`),e.git_branch&&a.push(`- **Branch**: \`${e.git_branch}\``),a.push(`- **Messages**: ${o.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 c=0,d=0;for(let l of o){let u=l.content_text??"",p=si(u);n==="condensed"&&(p=ni(p));let g=p.length>0,_=!!l.tool_names&&l.tool_names.length>0;if(!g&&!_){d+=1;continue}let f=ri(l),E=l.timestamp?` \`${l.timestamp}\``:"";a.push(`## ${f}${E}`),a.push(""),_&&n==="condensed"&&(a.push(`_tools used: ${l.tool_names}_`),a.push("")),g&&(a.push(p),a.push("")),c+=1}return a.push("---"),a.push(""),a.push(`_${c} messages included_`+(d?`, ${d} empty skipped`:"")+(r?"":", subagent/sidechain hidden")+"."),a.join(`
795
- `)}Le();qe();import{existsSync as li,mkdirSync as jl,readFileSync as di,writeFileSync as Hl,chmodSync as Bl}from"node:fs";import{homedir as ui}from"node:os";import{join as ss}from"node:path";import{z as q}from"zod";function pi(){return process.env.RECALL_HOME??ss(ui(),".recall")}function mi(){return ss(pi(),"config.json")}var gi=q.object({enabled:q.boolean().default(!1),backend:q.enum(["api","mcp"]).default("api"),apiKey:q.string().optional(),model:q.string().default("claude-opus-4-7"),maxTagsPerSession:q.number().int().min(1).max(10).default(4),minTagsPerSession:q.number().int().min(1).max(10).default(2),autopilot:q.boolean().default(!1)}),Ze={enabled:!1,backend:"api",model:"claude-opus-4-7",maxTagsPerSession:4,minTagsPerSession:2,autopilot:!1};function _i(){let e=mi();if(!li(e))return{};try{return JSON.parse(di(e,"utf8"))}catch(t){return console.error("[auto-tag-config] failed to parse config.json, using defaults:",t),{}}}function Qe(){let e=_i().autoTag;if(!e)return{...Ze};let t=gi.safeParse({...Ze,...e});return t.success?t.data:{...Ze}}tt();R();Le();xe();import{z as F}from"zod";R();M();import{writeFileSync as vi,mkdirSync as Ci,existsSync as Di}from"node:fs";import{join as is}from"node:path";var st=is(S,"notes");function os(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Mi(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(s=>!!s&&typeof s=="object"&&typeof s.synopsis=="string"&&typeof s.replaced_at=="string"):[]}catch{return[]}}function Fi(){v(),Di(st)||Ci(st,{recursive:!0})}function Pi(e){return{session_id:e.session_id,content:e.content,updated_at:e.updated_at,previous_versions:os(e.previous_versions),auto_synopsis:e.auto_synopsis??null,auto_synopsis_generated_at:e.auto_synopsis_generated_at??null,auto_synopsis_history:Mi(e.auto_synopsis_history)}}var Ui="session_id, content, updated_at, previous_versions, auto_synopsis, auto_synopsis_generated_at, auto_synopsis_history";function nt(e){let t=m().prepare(`SELECT ${Ui} FROM session_notes WHERE session_id = ?`).get(e);return t?Pi(t):null}function as(e,t){let s=m(),n=new Date().toISOString(),r=s.prepare("SELECT content, previous_versions FROM session_notes WHERE session_id = ?").get(e),i=[];return r&&(i=os(r.previous_versions),r.content!==t&&r.content.length>0&&i.push({content:r.content,replaced_at:n})),s.prepare(`INSERT INTO session_notes (session_id, content, updated_at, previous_versions)
786
+ WHERE sa.alias IS NOT NULL AND sa.alias != ''`).all(),n=Vc(s),r=Ur(t.stage);t.stage("Probing daemon");let i=await Fr();t.stage("Detecting label collisions");let o=Pr();t.stage("Checking ingest freshness");let a=$r();if(t.done(),e.json){process.stdout.write(JSON.stringify({scanned:s.length,violations:n.length,items:n,health:r,pipeline:i,labelCollisions:o,ingestFreshness:a},null,2)),process.stdout.write(`
787
+ `);let l=i.state==="degraded",u=a.status==="fail";return n.length===0&&r.db.integrity==="ok"&&!l&&!u?0:1}console.log(f.dim("\u2014 System health \u2014")),console.log(` Database ${V(r.db.sizeBytes)} (${r.rows.messages.toLocaleString()} messages across ${r.rows.sessions.toLocaleString()} sessions, ${r.rows.projects.toLocaleString()} projects)`);{let l=r.wal,u=l.level==="error"?f.err(V(l.sizeBytes)):l.level==="warn"?f.warn(V(l.sizeBytes)):f.ok(V(l.sizeBytes)),p=l.level==="error"?" (readers are pinning the checkpoint frontier \u2014 see warnings below)":l.level==="warn"?" (above 100 MB \u2014 daemon will WARN until it drops)":" (daemon checkpoints PASSIVE every 60s, RESTART above 5 GB)";console.log(` WAL ${u}${p}`)}console.log(` Free pages ${r.db.freelistCount.toLocaleString()} (${V(r.db.freelistBytes)} reclaimable via VACUUM)`);{let l=r.fts.messages.fragments,u=r.fts.sessions.fragments,p=l===0&&u===0;console.log(` FTS segments messages=${l}, sessions=${u}`+(p?" (merged \u2014 search uses the index)":" (lower is faster \u2014 `recall optimize` merges them)"))}if(r.chunkQueue.size>0){let u=r.chunkQueue.size>1e5?f.warn(r.chunkQueue.size.toLocaleString()):r.chunkQueue.size.toLocaleString();console.log(` Embed queue ${u}`+(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 l=r.disk.freeBytes/r.disk.totalBytes*100;console.log(` Disk free ${V(r.disk.freeBytes)} of ${V(r.disk.totalBytes)} (${l.toFixed(1)}%)`)}if(console.log(` Integrity ${r.db.integrity==="ok"?f.ok("ok"):f.err(r.db.integrity)}`),r.warnings.length>0){console.log("");for(let l of r.warnings)console.log(` ${f.warn("!")} ${l}`)}if(console.log(""),sl(r.mcpProcesses),console.log(""),console.log(f.dim("\u2014 Ingest freshness \u2014")),a.status==="ok")console.log(f.ok(` \u2713 ${a.scanned.toLocaleString()} JSONL${a.scanned===1?"":"s"} scanned, none newer than the index`));else if(a.status==="warn"){console.log(` ${f.warn(`${a.staleCount} stale file${a.staleCount===1?"":"s"}`)} newer than the index \u2014 live watcher may be missing events`);for(let l of a.sampleFiles.slice(0,5))console.log(` ${f.dim("\u2022")} ${l}`);console.log(f.dim(" Run `recall index` to force a reindex, or restart the daemon if persistent."))}else{console.log(` ${f.err(`${a.staleCount} stale file${a.staleCount===1?"":"s"}`)} newer than the index \u2014 significant ingest gap`);for(let l of a.sampleFiles.slice(0,5))console.log(` ${f.dim("\u2022")} ${l}`);console.log(f.err(" Restart the daemon (recall stop && recall start) \u2014 the live watcher has fallen behind."))}if(console.log(""),console.log(f.dim("\u2014 Pipeline health (tab-name \u2192 session alias) \u2014")),i.daemon.running?console.log(` Daemon ${f.ok("running")} (port ${i.daemon.port}, version ${i.daemon.version??"?"}, up ${i.daemon.uptimeSeconds!==null?Math.round(i.daemon.uptimeSeconds/60)+" min":"?"})`):console.log(` Daemon ${f.warn("not reachable")}`),i.runtime.silentTerminalRejections!==null){let l=i.runtime.silentTerminalRejections;console.log(` Auth rejections ${l===0?f.ok("0"):f.err(l.toLocaleString())} (extension /api/terminal/* requests denied without a valid X-Recall-Token)`)}if(i.runtime.autoExtract){let l=i.runtime.autoExtract;l.circuitBroken?console.log(` Auto-extract ${f.err("circuit broken")} (${l.reason??"unknown"} \u2014 toggle off/on to reset)`):l.consecutiveZeroTokenRuns>0&&console.log(` Auto-extract ${f.warn(`${l.consecutiveZeroTokenRuns} zero-token run(s)`)} (breaker trips at 3)`)}if(i.runtime.lastTerminalSyncAt!==null){let l=Date.now()-Date.parse(i.runtime.lastTerminalSyncAt),u=Number.isFinite(l)?Math.round(l/6e4):null,p=u!==null&&l>Dr;console.log(` Last ext sync ${p?f.warn(`${u} min ago`):f.ok(u===0?"just now":`${u} min ago`)} (most recent successful POST /api/terminal/sync)`)}else i.daemon.running&&console.log(` Last ext sync ${f.warn("never")} (no extension has called /api/terminal/sync since the daemon started)`);if(i.terminalsJson.exists&&i.terminalsJson.ageSeconds!==null){let l=Math.round(i.terminalsJson.ageSeconds/3600),u=i.terminalsJson.ageSeconds>24*3600;console.log(` terminals.json ${u?f.warn(`${l}h old`):f.ok(`${l}h old`)} (persisted registry mtime \u2014 fresh means extensions are connecting)`)}let c=i.recentSessions;if(c.total>0){let l=Math.round(c.fractionHeuristic*100),u=c.fractionHeuristic>=Mr&&c.total>=3;console.log(` Recent titles ${u?f.err(`${l}% heuristic`):f.ok(`${l}% heuristic`)} (${c.heuristicOnly}/${c.total} sessions in last ${Kt}h fell back to first-message title)`)}if(i.flags.length>0){console.log("");for(let l of i.flags)console.log(` ${f.warn("!")} ${l}`)}if(console.log(""),console.log(f.dim("\u2014 Label collisions (last 7d) \u2014")),o.length===0)console.log(f.ok(" \u2713 no session-list label collisions in the last week"));else{let l=o.filter(u=>!u.anyAliased);console.log(` ${f.warn(`${o.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 u of o.slice(0,5)){let p=u.anyAliased?f.dim("partial alias"):f.warn("NO alias"),g=u.label.length>60?`${u.label.slice(0,57)}\u2026`:u.label;console.log(` ${f.dim(`${u.count}\xD7`)} ${g} ${p} ${f.dim(`(${u.project})`)}`)}o.length>5&&console.log(f.dim(` \u2026 and ${o.length-5} more (--json for full list)`)),console.log(""),console.log(f.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(f.dim(" <!-- claude-recall-alias: T2.1 Run-Prospects -->")),console.log(f.dim(` The watcher will read the header, alias the session, and strip the marker from the
788
+ displayed first user message. See docs/HANDOFF.md \u2192 "Alias header convention".`)),l.length>0&&(console.log(""),console.log(f.warn(` ${l.length} of these groups have NO alias on any row \u2014 strongest candidates
789
+ for the header-alias fix above.`)))}if(console.log(""),console.log(f.dim("\u2014 Tab-name invariant \u2014")),n.length===0)return console.log(f.ok(` \u2713 holds across ${s.length.toLocaleString()} aliased session${s.length===1?"":"s"}`)),console.log(f.dim(" No fabricated origin labels (`VS Code \xB7 cwd \xB7 branch`) found. No deprecated cwd-branch synthesis found.")),r.db.integrity==="ok"&&a.status!=="fail"?0:1;console.log(f.err(`\u2717 ${n.length} invariant violation${n.length===1?"":"s"} found across ${s.length.toLocaleString()} aliased sessions`)),console.log("");let d=new Map;for(let l of n){let u=d.get(l.violation)??[];u.push(l),d.set(l.violation,u)}for(let[l,u]of d){console.log(f.warn(` ${l} (${u.length})`));for(let p of u.slice(0,10))console.log(` ${p.session_id.slice(0,8)} ${f.dim("\u2192")} ${JSON.stringify(p.alias)}`);u.length>10&&console.log(f.dim(` \u2026 and ${u.length-10} more (rerun with --json for the full list)`)),console.log("")}return console.log(f.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 rl(){try{let e=kr(S);return Number(e.bavail)*Number(e.bsize)}catch{return 0}}var Jc,Kc,Dr,Kt,Mr,Hr=b(()=>{"use strict";Ut();N();D();wr();zt();Ar();Ne();Jc=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],Kc=new RegExp(`^(${Jc.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);Dr=5*6e4,Kt=24,Mr=.5});var Br={};Ke(Br,{runOptimize:()=>ll});import{existsSync as il,readFileSync as ol}from"node:fs";import{join as al}from"node:path";function cl(){let e=al(S,"daemon.pid");if(!il(e))return!1;try{let t=parseInt(ol(e,"utf-8").trim(),10);return!Number.isFinite(t)||t<=0?!1:(process.kill(t,0),!0)}catch{return!1}}async function be(e,t){let s=Date.now();try{return t(),{step:e,ok:!0,durationMs:Date.now()-s}}catch(n){return{step:e,ok:!1,durationMs:Date.now()-s,error:n.message}}}async function ll(e={}){let t=m(),s=[];if(e.vacuum&&cl())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)+`
790
+ `),2):(console.error(f.err("\u2717 VACUUM requires the daemon to be stopped. Run `recall stop` first, then re-run with --vacuum.")),2);s.push(await be("wal_checkpoint(TRUNCATE)",()=>{t.pragma("wal_checkpoint(TRUNCATE)")})),s.push(await be("messages_fts optimize",()=>{t.exec("INSERT INTO messages_fts(messages_fts) VALUES('optimize');")})),s.push(await be("sessions_fts optimize",()=>{t.exec("INSERT INTO sessions_fts(sessions_fts) VALUES('optimize');")})),s.push(await be("PRAGMA optimize",()=>{t.exec("PRAGMA optimize")})),e.vacuum&&s.push(await be("VACUUM",()=>{t.exec("VACUUM")}));let n=s.filter(r=>!r.ok);if(e.json)return process.stdout.write(JSON.stringify({ok:n.length===0,steps:s,vacuum:!!e.vacuum},null,2)+`
791
+ `),n.length===0?0:1;for(let r of s){let i=r.ok?f.ok("\u2713"):f.err("\u2717"),o=`${r.durationMs} ms`;console.log(` ${i} ${r.step.padEnd(28)} ${f.dim(o)}`),r.error&&console.log(` ${f.err(r.error)}`)}return n.length===0?(console.log(""),console.log(f.ok("All maintenance passes completed.")),e.vacuum||console.log(f.dim(" Run `recall optimize --vacuum` (with the daemon stopped) to reclaim disk pages from deleted rows.")),0):(console.log(""),console.log(f.warn(`${n.length} step(s) failed \u2014 review the errors above.`)),1)}var Wr=b(()=>{"use strict";Ut();N();D()});N();import{McpServer as dl}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as ul}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as y}from"zod";import{fileURLToPath as pl}from"node:url";Ne();function is(e){let t=e.onShutdown,s=e.pollIntervalMs??5e3,n=e.logger??(_=>{process.stderr.write(_+`
792
+ `)}),r=e.stdin??process.stdin,i=e.getPpid??(()=>process.ppid),o=e.getPid??(()=>process.pid),a=!1,c=null,d=i(),l=()=>{c&&(clearInterval(c),c=null),r.removeListener("end",p),r.removeListener("close",g)},u=_=>{if(a)return;a=!0,n(`[parent-death-guard] shutdown triggered: ${_} (pid=${o()} initialPpid=${d} currentPpid=${i()})`),l();let h;try{h=t()}catch(E){n(`[parent-death-guard] onShutdown threw: ${Re(E)}`);return}h&&typeof h.then=="function"&&h.catch(E=>{n(`[parent-death-guard] onShutdown rejected: ${Re(E)}`)})},p=()=>u("stdin end"),g=()=>u("stdin close");return r.on("end",p),r.on("close",g),d!==1&&(c=setInterval(()=>{let _=i();_!==d&&u(`ppid changed ${d} -> ${_}`)},s),typeof c.unref=="function"&&c.unref()),{stop:l}}var ei=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,ti=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,si=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function ni(e){return e.replace(ei,"").trim()}function ri(e){let t=e.replace(ti,"[tool call]");return t=t.replace(si,"[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,`
793
+
794
+ `),t.trim()}function ii(e){return e.role??e.type??"message"}function os(e,t,s={}){let n=s.mode??"condensed",r=s.includeSidechain===!0,i=s.since?Date.parse(s.since):0,o=t.filter(l=>!(!r&&l.is_sidechain===1||i&&l.timestamp&&Date.parse(l.timestamp)<i)),a=[];s.prelude&&(a.push(s.prelude.trim()),a.push("")),a.push(`# Claude Recall, past session context (${n})`),a.push(""),a.push(`- **Project**: ${e.project_name}`),e.decoded_path&&a.push(`- **Path**: \`${e.decoded_path}\``),a.push(`- **Session ID**: \`${e.id}\``),e.started_at&&a.push(`- **Started**: ${e.started_at}`),e.ended_at&&a.push(`- **Ended**: ${e.ended_at}`),e.git_branch&&a.push(`- **Branch**: \`${e.git_branch}\``),a.push(`- **Messages**: ${o.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 c=0,d=0;for(let l of o){let u=l.content_text??"",p=ni(u);n==="condensed"&&(p=ri(p));let g=p.length>0,_=!!l.tool_names&&l.tool_names.length>0;if(!g&&!_){d+=1;continue}let h=ii(l),E=l.timestamp?` \`${l.timestamp}\``:"";a.push(`## ${h}${E}`),a.push(""),_&&n==="condensed"&&(a.push(`_tools used: ${l.tool_names}_`),a.push("")),g&&(a.push(p),a.push("")),c+=1}return a.push("---"),a.push(""),a.push(`_${c} messages included_`+(d?`, ${d} empty skipped`:"")+(r?"":", subagent/sidechain hidden")+"."),a.join(`
795
+ `)}Oe();et();import{existsSync as di,mkdirSync as Zl,readFileSync as ui,writeFileSync as Ql,chmodSync as ed}from"node:fs";import{homedir as pi}from"node:os";import{join as ds}from"node:path";import{z as Z}from"zod";function mi(){return process.env.RECALL_HOME??ds(pi(),".recall")}function gi(){return ds(mi(),"config.json")}var _i=Z.object({enabled:Z.boolean().default(!1),backend:Z.enum(["api","mcp"]).default("api"),apiKey:Z.string().optional(),model:Z.string().default("claude-opus-4-7"),maxTagsPerSession:Z.number().int().min(1).max(10).default(4),minTagsPerSession:Z.number().int().min(1).max(10).default(2),autopilot:Z.boolean().default(!1)}),tt={enabled:!1,backend:"api",model:"claude-opus-4-7",maxTagsPerSession:4,minTagsPerSession:2,autopilot:!1};function fi(){let e=gi();if(!di(e))return{};try{return JSON.parse(ui(e,"utf8"))}catch(t){return console.error("[auto-tag-config] failed to parse config.json, using defaults:",t),{}}}function st(){let e=fi().autoTag;if(!e)return{...tt};let t=_i.safeParse({...tt,...e});return t.success?t.data:{...tt}}rt();N();Oe();ke();import{z as M}from"zod";N();D();import{writeFileSync as vi,mkdirSync as Di,existsSync as Mi}from"node:fs";import{join as ms}from"node:path";var it=ms(S,"notes");function gs(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Fi(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(s=>!!s&&typeof s=="object"&&typeof s.synopsis=="string"&&typeof s.replaced_at=="string"):[]}catch{return[]}}function Pi(){k(),Mi(it)||Di(it,{recursive:!0})}function Ui(e){return{session_id:e.session_id,content:e.content,updated_at:e.updated_at,previous_versions:gs(e.previous_versions),auto_synopsis:e.auto_synopsis??null,auto_synopsis_generated_at:e.auto_synopsis_generated_at??null,auto_synopsis_history:Fi(e.auto_synopsis_history)}}var $i="session_id, content, updated_at, previous_versions, auto_synopsis, auto_synopsis_generated_at, auto_synopsis_history";function ot(e){let t=m().prepare(`SELECT ${$i} FROM session_notes WHERE session_id = ?`).get(e);return t?Ui(t):null}function _s(e,t){let s=m(),n=new Date().toISOString(),r=s.prepare("SELECT content, previous_versions FROM session_notes WHERE session_id = ?").get(e),i=[];return r&&(i=gs(r.previous_versions),r.content!==t&&r.content.length>0&&i.push({content:r.content,replaced_at:n})),s.prepare(`INSERT INTO session_notes (session_id, content, updated_at, previous_versions)
796
796
  VALUES (?, ?, ?, ?)
797
797
  ON CONFLICT(session_id) DO UPDATE SET
798
798
  content = excluded.content,
799
799
  updated_at = excluded.updated_at,
800
- previous_versions = excluded.previous_versions`).run(e,t,n,JSON.stringify(i)),$i(e,t,n),nt(e)??{session_id:e,content:t,updated_at:n,previous_versions:i,auto_synopsis:null,auto_synopsis_generated_at:null,auto_synopsis_history:[]}}function $i(e,t,s){try{Fi();let n=is(st,`${e}.md`),r=`<!-- Claude Recall note \xB7 session ${e} \xB7 updated ${s} -->
801
- `;vi(n,r+t)}catch(n){console.error("[notes] mirror write failed:",n)}}ct();R();var dt=60,Ji=6e4,oe=class extends Error{retryAfterMs;constructor(t){super(`MCP write rate limit exceeded \u2014 try again in ${Math.ceil(t/1e3)}s`),this.name="RateLimitError",this.retryAfterMs=t}};function lt(e){let t=new Date().toISOString(),s;try{s=JSON.stringify(e.args??null)}catch{s='"<unserializable>"'}m().prepare(`INSERT INTO mcp_audit_events (tool, args_json, result, error_message, caller, at)
802
- VALUES (?, ?, ?, ?, ?, ?)`).run(e.tool,s,e.result,e.errorMessage??null,e.caller??null,t)}var Z=class{capacity;windowMs;hits=[];constructor(t=dt,s=Ji){this.capacity=t,this.windowMs=s}consume(t=Date.now()){if(this.evict(t),this.hits.length>=this.capacity){let s=this.hits[0],n=Math.max(1,s+this.windowMs-t);throw new oe(n)}this.hits.push(t)}remaining(t=Date.now()){return this.evict(t),Math.max(0,this.capacity-this.hits.length)}reset(){this.hits.length=0}evict(t){let s=t-this.windowMs;for(;this.hits.length>0&&this.hits[0]<s;)this.hits.shift()}};async function I(e){try{e.limiter.consume()}catch(t){throw t instanceof oe&&lt({tool:e.tool,args:e.args,result:"rate_limited",errorMessage:t.message,caller:e.caller}),t}try{let t=await e.run();return lt({tool:e.tool,args:e.args,result:"ok",caller:e.caller}),t}catch(t){let s=t instanceof Error?t.message:String(t);throw lt({tool:e.tool,args:e.args,result:"error",errorMessage:s,caller:e.caller}),t}}R();import{z as Ki}from"zod";function X(e){let t=e.trim();if(t.length<4)return null;let s=m();if(t.length>=32)return s.prepare("SELECT id FROM sessions WHERE id = ?").get(t)?.id??null;let n=s.prepare("SELECT id FROM sessions WHERE id LIKE ? LIMIT 2").all(`${t}%`);return n.length===1?n[0].id:null}function Ie(e){return{content:[{type:"text",text:e}],isError:!0}}function O(e){return e instanceof oe?Ie(e.message):e instanceof Ki.ZodError?Ie(`invalid input: ${e.message}`):e instanceof Error&&e.message.startsWith("SQLITE_")?Ie("database constraint error"):Ie(e instanceof Error?e.message:String(e))}function se(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function Q(e){return{content:[{type:"text",text:e}],isError:!0}}var Vi=`
800
+ previous_versions = excluded.previous_versions`).run(e,t,n,JSON.stringify(i)),ji(e,t,n),ot(e)??{session_id:e,content:t,updated_at:n,previous_versions:i,auto_synopsis:null,auto_synopsis_generated_at:null,auto_synopsis_history:[]}}function ji(e,t,s){try{Pi();let n=ms(it,`${e}.md`),r=`<!-- Claude Recall note \xB7 session ${e} \xB7 updated ${s} -->
801
+ `;vi(n,r+t)}catch(n){console.error("[notes] mirror write failed:",n)}}ut();N();var mt=60,Ki=6e4,oe=class extends Error{retryAfterMs;constructor(t){super(`MCP write rate limit exceeded \u2014 try again in ${Math.ceil(t/1e3)}s`),this.name="RateLimitError",this.retryAfterMs=t}};function pt(e){let t=new Date().toISOString(),s;try{s=JSON.stringify(e.args??null)}catch{s='"<unserializable>"'}m().prepare(`INSERT INTO mcp_audit_events (tool, args_json, result, error_message, caller, at)
802
+ VALUES (?, ?, ?, ?, ?, ?)`).run(e.tool,s,e.result,e.errorMessage??null,e.caller??null,t)}var Q=class{capacity;windowMs;hits=[];constructor(t=mt,s=Ki){this.capacity=t,this.windowMs=s}consume(t=Date.now()){if(this.evict(t),this.hits.length>=this.capacity){let s=this.hits[0],n=Math.max(1,s+this.windowMs-t);throw new oe(n)}this.hits.push(t)}remaining(t=Date.now()){return this.evict(t),Math.max(0,this.capacity-this.hits.length)}reset(){this.hits.length=0}evict(t){let s=t-this.windowMs;for(;this.hits.length>0&&this.hits[0]<s;)this.hits.shift()}};async function I(e){try{e.limiter.consume()}catch(t){throw t instanceof oe&&pt({tool:e.tool,args:e.args,result:"rate_limited",errorMessage:t.message,caller:e.caller}),t}try{let t=await e.run();return pt({tool:e.tool,args:e.args,result:"ok",caller:e.caller}),t}catch(t){let s=t instanceof Error?t.message:String(t);throw pt({tool:e.tool,args:e.args,result:"error",errorMessage:s,caller:e.caller}),t}}N();import{z as Vi}from"zod";function X(e){let t=e.trim();if(t.length<4)return null;let s=m();if(t.length>=32)return s.prepare("SELECT id FROM sessions WHERE id = ?").get(t)?.id??null;let n=s.prepare("SELECT id FROM sessions WHERE id LIKE ? LIMIT 2").all(`${t}%`);return n.length===1?n[0].id:null}function Ce(e){return{content:[{type:"text",text:e}],isError:!0}}function x(e){return e instanceof oe?Ce(e.message):e instanceof Vi.ZodError?Ce(`invalid input: ${e.message}`):e instanceof Error&&e.message.startsWith("SQLITE_")?Ce("database constraint error"):Ce(e instanceof Error?e.message:String(e))}function ne(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function ee(e){return{content:[{type:"text",text:e}],isError:!0}}var qi=`
803
803
 
804
804
  ---
805
805
 
806
- `,qi=5e5;function us(e,t={}){let s=t.limiter??new Z;e.registerTool("add_tag",{title:"Add tag to a session",description:"Apply a single tag to a session. Tags are normalized server-side (lowercase, hyphens, strip #). Idempotent.",inputSchema:{sessionId:F.string().describe("Full session UUID or 8+-character prefix."),tag:F.string().min(1).describe("Tag to add. Normalized: lowercase, dashes, max 40 chars.")}},async n=>{try{let r=X(n.sessionId);if(!r)return Q(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await I({tool:"add_tag",args:{sessionId:r,tag:n.tag},limiter:s,run:()=>Re(r,n.tag)});return se({sessionId:r,...i})}catch(r){return O(r)}}),e.registerTool("remove_tag",{title:"Remove tag from a session",description:"Remove a single tag from a session. The removal is recorded in tag_events (append-only) so history is preserved.",inputSchema:{sessionId:F.string().describe("Full session UUID or 8+-character prefix."),tag:F.string().min(1).describe("Tag to remove (normalized server-side).")}},async n=>{try{let r=X(n.sessionId);if(!r)return Q(`session not found or prefix ambiguous: ${n.sessionId}`);let i=te(n.tag);if(!i)return Q("tag must contain at least one alphanumeric character");let o=await I({tool:"remove_tag",args:{sessionId:r,tag:i},limiter:s,run:()=>Qt(r,i)});return se({sessionId:r,...o})}catch(r){return O(r)}}),e.registerTool("set_alias",{title:"Set session alias",description:"Set a human-friendly alias for a session. Previous alias is archived to previous_aliases (never destroyed). Session UUID remains the immutable primary key.",inputSchema:{sessionId:F.string().describe("Full session UUID or 8+-character prefix."),alias:F.string().min(1).max(120).describe("New alias (non-empty, max 120 chars).")}},async n=>{try{let r=X(n.sessionId);if(!r)return Q(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await I({tool:"set_alias",args:{sessionId:r,alias:n.alias},limiter:s,run:()=>Oe(r,n.alias)});return se(i)}catch(r){return O(r)}}),e.registerTool("append_note",{title:"Append to session note",description:"Append markdown to a session note. If a note already exists, the new content is added below a `---` separator. Previous version is archived in previous_versions.",inputSchema:{sessionId:F.string().describe("Full session UUID or 8+-character prefix."),markdown:F.string().min(1).max(5e4).describe("Markdown to append.")}},async n=>{try{let r=X(n.sessionId);if(!r)return Q(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await I({tool:"append_note",args:{sessionId:r,length:n.markdown.length},limiter:s,run:()=>{let o=nt(r),a=o&&o.content.length>0?`${o.content}${Vi}${n.markdown}`:n.markdown;if(a.length>qi)throw new Error(`note would exceed the 500 KB cumulative limit (current: ${o?.content.length??0} bytes, adding: ${n.markdown.length} bytes)`);return as(r,a)}});return se(i)}catch(r){return O(r)}}),e.registerTool("create_collection",{title:"Create a collection",description:"Create a new collection for grouping sessions. Optionally nest under a parent. Soft-deletion only.",inputSchema:{name:F.string().min(1).max(120),parent_id:F.string().optional().describe("Parent collection id (omit for root)."),icon:F.string().max(32).optional(),color:F.string().max(32).optional(),description:F.string().max(2e3).optional()}},async n=>{try{let r=await I({tool:"create_collection",args:n,limiter:s,run:()=>it({name:n.name,parent_id:n.parent_id??null,icon:n.icon??null,color:n.color??null,description:n.description??null})});return se(r)}catch(r){return O(r)}}),e.registerTool("add_session_to_collection",{title:"Add session to collection",description:"Add a session to a collection. Idempotent: re-adding returns added=false.",inputSchema:{collectionId:F.string().min(1),sessionId:F.string().describe("Full session UUID or 8+-character prefix."),note:F.string().max(2e3).optional()}},async n=>{try{let r=X(n.sessionId);if(!r)return Q(`session not found or prefix ambiguous: ${n.sessionId}`);if(!m().prepare("SELECT 1 FROM collections WHERE id = ?").get(n.collectionId))return Q("collection not found");let a=await I({tool:"add_session_to_collection",args:{collectionId:n.collectionId,sessionId:r,note:n.note??null},limiter:s,run:()=>ot(n.collectionId,r,n.note??null)});return se({collectionId:n.collectionId,sessionId:r,...a})}catch(r){return O(r)}}),e.registerTool("remove_session_from_collection",{title:"Remove session from collection",description:"Remove a session from a collection. The removal is recorded in collection_events (append-only); the underlying session is untouched.",inputSchema:{collectionId:F.string().min(1),sessionId:F.string().describe("Full session UUID or 8+-character prefix.")}},async n=>{try{let r=X(n.sessionId);if(!r)return Q(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await I({tool:"remove_session_from_collection",args:{collectionId:n.collectionId,sessionId:r},limiter:s,run:()=>ds(n.collectionId,r)});return se({collectionId:n.collectionId,sessionId:r,...i})}catch(r){return O(r)}})}import{z as H}from"zod";R();M();import{randomUUID as ps}from"node:crypto";import{writeFileSync as ms,readFileSync as Id,existsSync as Zi,mkdirSync as Qi}from"node:fs";import{join as ut}from"node:path";var ke=ut(S,"threads"),eo=ut(ke,"index.json");function gs(){v(),Zi(ke)||Qi(ke,{recursive:!0})}function pt(e,t,s){return{id:e.id,name:e.name,summary:e.summary,created_at:e.created_at,closed_at:e.closed_at,archived:e.archived===1,session_count:t.session_count,origin_count:t.origin_count,project:s?.project??null,project_count:s?.project_count??0,folder_id:e.folder_id??null}}function _s(e){let t=new Map;if(e.length===0)return t;let s=m(),n=e.map(()=>"?").join(","),r=s.prepare(`SELECT te.thread_id AS thread_id,
806
+ `,Zi=5e5;function Ts(e,t={}){let s=t.limiter??new Q;e.registerTool("add_tag",{title:"Add tag to a session",description:"Apply a single tag to a session. Tags are normalized server-side (lowercase, hyphens, strip #). Idempotent.",inputSchema:{sessionId:M.string().describe("Full session UUID or 8+-character prefix."),tag:M.string().min(1).describe("Tag to add. Normalized: lowercase, dashes, max 40 chars.")}},async n=>{try{let r=X(n.sessionId);if(!r)return ee(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await I({tool:"add_tag",args:{sessionId:r,tag:n.tag},limiter:s,run:()=>Le(r,n.tag)});return ne({sessionId:r,...i})}catch(r){return x(r)}}),e.registerTool("remove_tag",{title:"Remove tag from a session",description:"Remove a single tag from a session. The removal is recorded in tag_events (append-only) so history is preserved.",inputSchema:{sessionId:M.string().describe("Full session UUID or 8+-character prefix."),tag:M.string().min(1).describe("Tag to remove (normalized server-side).")}},async n=>{try{let r=X(n.sessionId);if(!r)return ee(`session not found or prefix ambiguous: ${n.sessionId}`);let i=se(n.tag);if(!i)return ee("tag must contain at least one alphanumeric character");let o=await I({tool:"remove_tag",args:{sessionId:r,tag:i},limiter:s,run:()=>as(r,i)});return ne({sessionId:r,...o})}catch(r){return x(r)}}),e.registerTool("set_alias",{title:"Set session alias",description:"Set a human-friendly alias for a session. Previous alias is archived to previous_aliases (never destroyed). Session UUID remains the immutable primary key.",inputSchema:{sessionId:M.string().describe("Full session UUID or 8+-character prefix."),alias:M.string().min(1).max(120).describe("New alias (non-empty, max 120 chars).")}},async n=>{try{let r=X(n.sessionId);if(!r)return ee(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await I({tool:"set_alias",args:{sessionId:r,alias:n.alias},limiter:s,run:()=>Ie(r,n.alias)});return ne(i)}catch(r){return x(r)}}),e.registerTool("append_note",{title:"Append to session note",description:"Append markdown to a session note. If a note already exists, the new content is added below a `---` separator. Previous version is archived in previous_versions.",inputSchema:{sessionId:M.string().describe("Full session UUID or 8+-character prefix."),markdown:M.string().min(1).max(5e4).describe("Markdown to append.")}},async n=>{try{let r=X(n.sessionId);if(!r)return ee(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await I({tool:"append_note",args:{sessionId:r,length:n.markdown.length},limiter:s,run:()=>{let o=ot(r),a=o&&o.content.length>0?`${o.content}${qi}${n.markdown}`:n.markdown;if(a.length>Zi)throw new Error(`note would exceed the 500 KB cumulative limit (current: ${o?.content.length??0} bytes, adding: ${n.markdown.length} bytes)`);return _s(r,a)}});return ne(i)}catch(r){return x(r)}}),e.registerTool("create_collection",{title:"Create a collection",description:"Create a new collection for grouping sessions. Optionally nest under a parent. Soft-deletion only.",inputSchema:{name:M.string().min(1).max(120),parent_id:M.string().optional().describe("Parent collection id (omit for root)."),icon:M.string().max(32).optional(),color:M.string().max(32).optional(),description:M.string().max(2e3).optional()}},async n=>{try{let r=await I({tool:"create_collection",args:n,limiter:s,run:()=>ct({name:n.name,parent_id:n.parent_id??null,icon:n.icon??null,color:n.color??null,description:n.description??null})});return ne(r)}catch(r){return x(r)}}),e.registerTool("add_session_to_collection",{title:"Add session to collection",description:"Add a session to a collection. Idempotent: re-adding returns added=false.",inputSchema:{collectionId:M.string().min(1),sessionId:M.string().describe("Full session UUID or 8+-character prefix."),note:M.string().max(2e3).optional()}},async n=>{try{let r=X(n.sessionId);if(!r)return ee(`session not found or prefix ambiguous: ${n.sessionId}`);if(!m().prepare("SELECT 1 FROM collections WHERE id = ?").get(n.collectionId))return ee("collection not found");let a=await I({tool:"add_session_to_collection",args:{collectionId:n.collectionId,sessionId:r,note:n.note??null},limiter:s,run:()=>lt(n.collectionId,r,n.note??null)});return ne({collectionId:n.collectionId,sessionId:r,...a})}catch(r){return x(r)}}),e.registerTool("remove_session_from_collection",{title:"Remove session from collection",description:"Remove a session from a collection. The removal is recorded in collection_events (append-only); the underlying session is untouched.",inputSchema:{collectionId:M.string().min(1),sessionId:M.string().describe("Full session UUID or 8+-character prefix.")}},async n=>{try{let r=X(n.sessionId);if(!r)return ee(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await I({tool:"remove_session_from_collection",args:{collectionId:n.collectionId,sessionId:r},limiter:s,run:()=>Es(n.collectionId,r)});return ne({collectionId:n.collectionId,sessionId:r,...i})}catch(r){return x(r)}})}import{z as H}from"zod";N();D();import{randomUUID as Ss}from"node:crypto";import{writeFileSync as bs,readFileSync as Bd,existsSync as Qi,mkdirSync as eo}from"node:fs";import{join as gt}from"node:path";var ve=gt(S,"threads"),to=gt(ve,"index.json");function ys(){k(),Qi(ve)||eo(ve,{recursive:!0})}function _t(e,t,s){return{id:e.id,name:e.name,summary:e.summary,created_at:e.created_at,closed_at:e.closed_at,archived:e.archived===1,session_count:t.session_count,origin_count:t.origin_count,project:s?.project??null,project_count:s?.project_count??0,folder_id:e.folder_id??null}}function ws(e){let t=new Map;if(e.length===0)return t;let s=m(),n=e.map(()=>"?").join(","),r=s.prepare(`SELECT te.thread_id AS thread_id,
807
807
  p.name AS project,
808
808
  COUNT(*) AS n,
809
809
  SUM(CASE WHEN te.role = 'origin' THEN 1 ELSE 0 END) AS origin_n
@@ -811,7 +811,7 @@ ${u+1}. ${p}`:`${u+1}. ${p}`}).join(`
811
811
  LEFT JOIN sessions s ON s.id = te.session_id
812
812
  LEFT JOIN projects p ON p.id = s.project_id
813
813
  WHERE te.thread_id IN (${n})
814
- GROUP BY te.thread_id, p.name`).all(...e),i=new Map;for(let o of r){let a=i.get(o.thread_id);a||(a=[],i.set(o.thread_id,a)),a.push(o)}for(let[o,a]of i){let c=a.filter(u=>u.project!==null),d=c.length,l=null;c.length>0&&(l=[...c].sort((p,g)=>g.n-p.n||g.origin_n-p.origin_n||(p.project??"").localeCompare(g.project??""))[0].project),t.set(o,{project:l,project_count:d})}return t}function fs(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 hs(e){let s=m().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
814
+ GROUP BY te.thread_id, p.name`).all(...e),i=new Map;for(let o of r){let a=i.get(o.thread_id);a||(a=[],i.set(o.thread_id,a)),a.push(o)}for(let[o,a]of i){let c=a.filter(u=>u.project!==null),d=c.length,l=null;c.length>0&&(l=[...c].sort((p,g)=>g.n-p.n||g.origin_n-p.origin_n||(p.project??"").localeCompare(g.project??""))[0].project),t.set(o,{project:l,project_count:d})}return t}function Rs(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 Ns(e){let s=m().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
815
815
  s.auto_title AS auto_title,
816
816
  s.auto_title_source AS auto_title_source,
817
817
  s.first_user_message AS first_user_message,
@@ -819,11 +819,11 @@ ${u+1}. ${p}`:`${u+1}. ${p}`}).join(`
819
819
  FROM (SELECT ? AS sid) q
820
820
  LEFT JOIN sessions s ON s.id = q.sid
821
821
  LEFT JOIN session_aliases sa ON sa.session_id = q.sid
822
- LEFT JOIN projects p ON p.id = s.project_id`).get(e),n=s?.auto_title_source??null;return{alias:s?.alias??null,auto_title:s?.auto_title??null,auto_title_source:n==="agent"||n==="heuristic"?n:null,first_user_message:s?.first_user_message??null,project:s?.project??null}}function mt(e){let s=m().prepare(`SELECT
822
+ LEFT JOIN projects p ON p.id = s.project_id`).get(e),n=s?.auto_title_source??null;return{alias:s?.alias??null,auto_title:s?.auto_title??null,auto_title_source:n==="agent"||n==="heuristic"?n:null,first_user_message:s?.first_user_message??null,project:s?.project??null}}function ft(e){let s=m().prepare(`SELECT
823
823
  COUNT(*) AS session_count,
824
824
  SUM(CASE WHEN role = 'origin' THEN 1 ELSE 0 END) AS origin_count
825
- FROM thread_edges WHERE thread_id = ?`).get(e);return{session_count:s?.session_count??0,origin_count:s?.origin_count??0}}function Y(e){let t=U(e);t&&(gs(),ms(ut(ke,`${e}.json`),JSON.stringify(t,null,2)),Es())}function Es(){gs();let e=gt({includeArchived:!0});ms(eo,JSON.stringify({threads:e},null,2))}function ve(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let s=m(),n=ps(),r=new Date().toISOString();s.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(n,t,e.summary?.trim()||null,r),e.originSessionId&&s.prepare(`INSERT INTO thread_edges (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
826
- VALUES (?, ?, NULL, 'origin', 1.0, 'manual', ?)`).run(n,e.originSessionId,r),Y(n);let i=U(n);if(!i)throw new Error("thread creation succeeded but read-back failed");return i}function gt(e={}){let t=m(),s=e.includeArchived?"":"WHERE archived = 0",n=t.prepare(`SELECT * FROM threads ${s} ORDER BY created_at DESC`).all(),r=_s(n.map(i=>i.id));return n.map(i=>pt(i,mt(i.id),r.get(i.id)))}function U(e){let t=m(),s=t.prepare("SELECT * FROM threads WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT e.*,
825
+ FROM thread_edges WHERE thread_id = ?`).get(e);return{session_count:s?.session_count??0,origin_count:s?.origin_count??0}}function Y(e){let t=U(e);t&&(ys(),bs(gt(ve,`${e}.json`),JSON.stringify(t,null,2)),Ls())}function Ls(){ys();let e=ht({includeArchived:!0});bs(to,JSON.stringify({threads:e},null,2))}function De(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let s=m(),n=Ss(),r=new Date().toISOString();s.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, ?, ?)").run(n,t,e.summary?.trim()||null,r),e.originSessionId&&s.prepare(`INSERT INTO thread_edges (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
826
+ VALUES (?, ?, NULL, 'origin', 1.0, 'manual', ?)`).run(n,e.originSessionId,r),Y(n);let i=U(n);if(!i)throw new Error("thread creation succeeded but read-back failed");return i}function ht(e={}){let t=m(),s=e.includeArchived?"":"WHERE archived = 0",n=t.prepare(`SELECT * FROM threads ${s} ORDER BY created_at DESC`).all(),r=ws(n.map(i=>i.id));return n.map(i=>_t(i,ft(i.id),r.get(i.id)))}function U(e){let t=m(),s=t.prepare("SELECT * FROM threads WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT e.*,
827
827
  NULLIF(sa.alias, '') AS alias,
828
828
  s.auto_title AS auto_title,
829
829
  s.auto_title_source AS auto_title_source,
@@ -834,10 +834,10 @@ ${u+1}. ${p}`:`${u+1}. ${p}`}).join(`
834
834
  LEFT JOIN session_aliases sa ON sa.session_id = e.session_id
835
835
  LEFT JOIN projects p ON p.id = s.project_id
836
836
  WHERE e.thread_id = ?
837
- ORDER BY e.added_at ASC`).all(e).map(fs),r=_s([e]).get(e);return{...pt(s,mt(s.id),r),edges:n}}function Ts(e){return m().prepare(`SELECT t.* FROM threads t
837
+ ORDER BY e.added_at ASC`).all(e).map(Rs),r=ws([e]).get(e);return{..._t(s,ft(s.id),r),edges:n}}function As(e){return m().prepare(`SELECT t.* FROM threads t
838
838
  JOIN thread_edges e ON e.thread_id = t.id
839
839
  WHERE e.session_id = ? AND t.archived = 0
840
- ORDER BY t.created_at DESC`).all(e).map(n=>pt(n,mt(n.id)))}function Ce(e){let t=m();if(!t.prepare("SELECT * FROM threads WHERE id = ?").get(e.threadId))throw new Error(`thread ${e.threadId} not found`);let n=new Date().toISOString(),r=e.parentSessionId??null,i=e.role??(r?"child":"origin"),o=e.confidence??1,a=e.source??"manual";if(o<0||o>1)throw new Error("confidence must be 0..1");t.prepare(`INSERT INTO thread_edges
840
+ ORDER BY t.created_at DESC`).all(e).map(n=>_t(n,ft(n.id)))}function Me(e){let t=m();if(!t.prepare("SELECT * FROM threads WHERE id = ?").get(e.threadId))throw new Error(`thread ${e.threadId} not found`);let n=new Date().toISOString(),r=e.parentSessionId??null,i=e.role??(r?"child":"origin"),o=e.confidence??1,a=e.source??"manual";if(o<0||o>1)throw new Error("confidence must be 0..1");t.prepare(`INSERT INTO thread_edges
841
841
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
842
842
  VALUES (?, ?, ?, ?, ?, ?, ?)
843
843
  ON CONFLICT(thread_id, session_id) DO UPDATE SET
@@ -845,24 +845,24 @@ ${u+1}. ${p}`:`${u+1}. ${p}`}).join(`
845
845
  role = excluded.role,
846
846
  confidence = excluded.confidence,
847
847
  source = excluded.source,
848
- added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,i,o,a,n),Y(e.threadId);let c=hs(e.sessionId);return{thread_id:e.threadId,session_id:e.sessionId,parent_session_id:r,role:i,confidence:o,source:a,added_at:n,alias:c.alias,auto_title:c.auto_title,auto_title_source:c.auto_title_source,alias_source:c.alias?"manual":null,first_user_message:c.first_user_message,project:c.project}}function Ss(e,t){let n=m().prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e,t);return n.changes>0&&Y(e),{removed:n.changes}}function ge(e,t,s){let n=m(),r=n.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e,t);if(!r)throw new Error("edge not found; add the session first");if(s!==null){if(s===t)throw new Error("cycle detected: session cannot be its own parent");let a=n.prepare("SELECT parent_session_id FROM thread_edges WHERE thread_id = ? AND session_id = ?"),c=s,d=new Set;for(;c!==null;){if(c===t)throw new Error(`cycle detected: setting parent of ${t} to ${s} would create a loop`);if(d.has(c))break;d.add(c),c=a.get(e,c)?.parent_session_id??null}}let i=s?"child":"origin";n.prepare(`UPDATE thread_edges
848
+ added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,i,o,a,n),Y(e.threadId);let c=Ns(e.sessionId);return{thread_id:e.threadId,session_id:e.sessionId,parent_session_id:r,role:i,confidence:o,source:a,added_at:n,alias:c.alias,auto_title:c.auto_title,auto_title_source:c.auto_title_source,alias_source:c.alias?"manual":null,first_user_message:c.first_user_message,project:c.project}}function Os(e,t){let n=m().prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e,t);return n.changes>0&&Y(e),{removed:n.changes}}function _e(e,t,s){let n=m(),r=n.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e,t);if(!r)throw new Error("edge not found; add the session first");if(s!==null){if(s===t)throw new Error("cycle detected: session cannot be its own parent");let a=n.prepare("SELECT parent_session_id FROM thread_edges WHERE thread_id = ? AND session_id = ?"),c=s,d=new Set;for(;c!==null;){if(c===t)throw new Error(`cycle detected: setting parent of ${t} to ${s} would create a loop`);if(d.has(c))break;d.add(c),c=a.get(e,c)?.parent_session_id??null}}let i=s?"child":"origin";n.prepare(`UPDATE thread_edges
849
849
  SET parent_session_id = ?, role = ?, added_at = ?
850
- WHERE thread_id = ? AND session_id = ?`).run(s,i,new Date().toISOString(),e,t),Y(e);let o=hs(t);return fs({...r,parent_session_id:s,role:i,added_at:new Date().toISOString(),alias:o.alias,auto_title:o.auto_title,auto_title_source:o.auto_title_source,first_user_message:o.first_user_message,project:o.project})}function bs(e,t){let s=t.trim();if(!s)throw new Error("name cannot be empty");m().prepare("UPDATE threads SET name = ? WHERE id = ?").run(s,e),Y(e);let r=U(e);if(!r)throw new Error(`thread ${e} not found`);return r}function ys(e){m().prepare("UPDATE threads SET closed_at = ? WHERE id = ?").run(new Date().toISOString(),e),Y(e);let s=U(e);if(!s)throw new Error(`thread ${e} not found`);return s}function ws(e){m().prepare("UPDATE threads SET closed_at = NULL WHERE id = ?").run(e),Y(e);let s=U(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Rs(e){m().prepare("UPDATE threads SET archived = 1 WHERE id = ?").run(e),Y(e);let s=U(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Ns(e,t){if(e===t)throw new Error("cannot merge a thread into itself");let s=m(),n=new Date().toISOString();s.transaction(()=>{let i=s.prepare("SELECT * FROM thread_edges WHERE thread_id = ?").all(e);for(let o of i)s.prepare(`INSERT INTO thread_edges
850
+ WHERE thread_id = ? AND session_id = ?`).run(s,i,new Date().toISOString(),e,t),Y(e);let o=Ns(t);return Rs({...r,parent_session_id:s,role:i,added_at:new Date().toISOString(),alias:o.alias,auto_title:o.auto_title,auto_title_source:o.auto_title_source,first_user_message:o.first_user_message,project:o.project})}function xs(e,t){let s=t.trim();if(!s)throw new Error("name cannot be empty");m().prepare("UPDATE threads SET name = ? WHERE id = ?").run(s,e),Y(e);let r=U(e);if(!r)throw new Error(`thread ${e} not found`);return r}function Is(e){m().prepare("UPDATE threads SET closed_at = ? WHERE id = ?").run(new Date().toISOString(),e),Y(e);let s=U(e);if(!s)throw new Error(`thread ${e} not found`);return s}function ks(e){m().prepare("UPDATE threads SET closed_at = NULL WHERE id = ?").run(e),Y(e);let s=U(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Cs(e){m().prepare("UPDATE threads SET archived = 1 WHERE id = ?").run(e),Y(e);let s=U(e);if(!s)throw new Error(`thread ${e} not found`);return s}function vs(e,t){if(e===t)throw new Error("cannot merge a thread into itself");let s=m(),n=new Date().toISOString();s.transaction(()=>{let i=s.prepare("SELECT * FROM thread_edges WHERE thread_id = ?").all(e);for(let o of i)s.prepare(`INSERT INTO thread_edges
851
851
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
852
852
  VALUES (?, ?, ?, ?, ?, ?, ?)
853
853
  ON CONFLICT(thread_id, session_id) DO UPDATE SET
854
854
  parent_session_id = COALESCE(thread_edges.parent_session_id, excluded.parent_session_id),
855
855
  role = CASE WHEN thread_edges.role = 'origin' OR excluded.role = 'origin' THEN 'origin' ELSE 'child' END,
856
856
  confidence = MAX(thread_edges.confidence, excluded.confidence),
857
- source = thread_edges.source`).run(t,o.session_id,o.parent_session_id,o.role,o.confidence,o.source,n);s.prepare("DELETE FROM threads WHERE id = ?").run(e)})(),Y(t),Es();let r=U(t);if(!r)throw new Error("merge destination disappeared");return r}function Ls(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=m(),s=new Date().toISOString(),n=ps();t.transaction(()=>{t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, NULL, ?)").run(n,e.newThreadName.trim(),s);for(let i of e.sessionIds){let o=t.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e.threadId,i);o&&(t.prepare(`INSERT INTO thread_edges
857
+ source = thread_edges.source`).run(t,o.session_id,o.parent_session_id,o.role,o.confidence,o.source,n);s.prepare("DELETE FROM threads WHERE id = ?").run(e)})(),Y(t),Ls();let r=U(t);if(!r)throw new Error("merge destination disappeared");return r}function Ds(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=m(),s=new Date().toISOString(),n=Ss();t.transaction(()=>{t.prepare("INSERT INTO threads (id, name, summary, created_at) VALUES (?, ?, NULL, ?)").run(n,e.newThreadName.trim(),s);for(let i of e.sessionIds){let o=t.prepare("SELECT * FROM thread_edges WHERE thread_id = ? AND session_id = ?").get(e.threadId,i);o&&(t.prepare(`INSERT INTO thread_edges
858
858
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
859
- VALUES (?, ?, ?, ?, ?, ?, ?)`).run(n,i,o.parent_session_id,o.role,o.confidence,o.source,s),t.prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e.threadId,i))}})(),Y(e.threadId),Y(n);let r=U(n);if(!r)throw new Error("split destination disappeared");return r}Tt();var No=50;function Lo(e){if(e.length===0)return[];let t=[...e].sort((c,d)=>c.added_at<d.added_at?-1:c.added_at>d.added_at?1:0),s=new Map,n=new Set,r=[];for(let c of t)if(c.parent_session_id){let d=s.get(c.parent_session_id)??[];d.push(c),s.set(c.parent_session_id,d)}let i=t.filter(c=>c.role==="origin"),a=[...i.length>0?i:t.filter(c=>!c.parent_session_id)];for(;a.length>0;){let c=a.shift();if(n.has(c.session_id))continue;n.add(c.session_id),r.push(c.session_id);let d=s.get(c.session_id)??[];for(let l of d)n.has(l.session_id)||a.push(l)}for(let c of t)n.has(c.session_id)||(n.add(c.session_id),r.push(c.session_id));return r}function Ao(e){let t=Ds(e.sessionId),s=["",`BULK CONTEXT: You are titling session ${e.current} of ${e.total} in this thread.`,"The parent and earlier siblings already have titles (shown above). YOUR JOB:","",'1. Identify the naming pattern. If parent is "Build Feature X" and earlier',' siblings are "Phase A: API design" and "Phase B: client wiring", the pattern',' is "Phase {LETTER}: {topic}".',"2. If no pattern is yet established (this is the first child), INVENT a pattern",' that will scale to N children. Good patterns: "Phase A/B/C", "Wave 1 of M",',' "Step N", or a domain-specific structure if the thread name suggests one.',"3. Output a title that follows the pattern AND describes what THIS session"," does in 50 characters max.","","Output ONLY the title, no quotes, no trailing punctuation."].join(`
859
+ VALUES (?, ?, ?, ?, ?, ?, ?)`).run(n,i,o.parent_session_id,o.role,o.confidence,o.source,s),t.prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e.threadId,i))}})(),Y(e.threadId),Y(n);let r=U(n);if(!r)throw new Error("split destination disappeared");return r}yt();var Lo=50;function Ao(e){if(e.length===0)return[];let t=[...e].sort((c,d)=>c.added_at<d.added_at?-1:c.added_at>d.added_at?1:0),s=new Map,n=new Set,r=[];for(let c of t)if(c.parent_session_id){let d=s.get(c.parent_session_id)??[];d.push(c),s.set(c.parent_session_id,d)}let i=t.filter(c=>c.role==="origin"),a=[...i.length>0?i:t.filter(c=>!c.parent_session_id)];for(;a.length>0;){let c=a.shift();if(n.has(c.session_id))continue;n.add(c.session_id),r.push(c.session_id);let d=s.get(c.session_id)??[];for(let l of d)n.has(l.session_id)||a.push(l)}for(let c of t)n.has(c.session_id)||(n.add(c.session_id),r.push(c.session_id));return r}function Oo(e){let t=Bs(e.sessionId),s=["",`BULK CONTEXT: You are titling session ${e.current} of ${e.total} in this thread.`,"The parent and earlier siblings already have titles (shown above). YOUR JOB:","",'1. Identify the naming pattern. If parent is "Build Feature X" and earlier',' siblings are "Phase A: API design" and "Phase B: client wiring", the pattern',' is "Phase {LETTER}: {topic}".',"2. If no pattern is yet established (this is the first child), INVENT a pattern",' that will scale to N children. Good patterns: "Phase A/B/C", "Wave 1 of M",',' "Step N", or a domain-specific structure if the thread name suggests one.',"3. Output a title that follows the pattern AND describes what THIS session"," does in 50 characters max.","","Output ONLY the title, no quotes, no trailing punctuation."].join(`
860
860
  `);return`${t}
861
- ${s}`}function Oo(e){return Ms(e)?.auto_title_source??null}async function xo(e){let{spawnClaudePrompt:t,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(St(),Ys));if(!s())throw new Error("claude CLI not found on PATH");let n=await t(e.prompt,[],{model:e.model});if(!n.success)throw new Error(`claude CLI exited ${n.exitCode}: ${n.stderr.slice(-500)}`);let r=Io(n.stdout);if(!r)throw new Error("claude CLI returned an empty title");return r.slice(0,No)}function Io(e){let t=e.trim();if(!t)return"";try{let s=JSON.parse(t);if(s&&typeof s=="object"){let n=s.result;if(typeof n=="string")return zs(n)}}catch{}return zs(t)}function zs(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}async function Ks(e,t={}){let s=U(e);if(!s)throw new Error(`thread not found: ${e}`);let n=Lo(s.edges),r=n.length,i=t.force??!1,o=t.signal,a=[],c=[],d=[];for(let l=0;l<n.length;l++){let u=n[l],p=l+1;if(o?.aborted){let _={sessionId:u,reason:"cancelled"};c.push(_),t.onSkipped?.(_);continue}if(!i&&Oo(u)==="agent"){let _={sessionId:u,reason:"already-titled"};c.push(_),t.onSkipped?.(_);continue}let g;try{if(Js)g=await Js({sessionId:u,current:p,total:r});else{let _=Ao({sessionId:u,current:p,total:r});g=await xo({prompt:_,model:t.model})}}catch(_){let f=_ instanceof Error?_.message:String(_??"unknown error"),E={sessionId:u,error:f};d.push(E),t.onFailed?.(E);continue}try{Et(u,g,"agent")}catch(_){let f=_ instanceof Error?_.message:String(_??"unknown error"),E={sessionId:u,error:`setAutoTitle failed: ${f}`};d.push(E),t.onFailed?.(E);continue}a.push(u),t.onProgress?.({current:p,total:r,sessionId:u,title:g})}return{generated:a,skipped:c,failed:d}}var Js=null;R();import{execFile as Go}from"node:child_process";import{promisify as Yo}from"node:util";import{readlink as zo,readFile as tn}from"node:fs/promises";import{platform as Fe}from"node:os";import{readFileSync as ko,statSync as vo}from"node:fs";var Co=200*1024*1024;var yt=.5,Zs=yt,Do=[{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"}],Mo=["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 Vs(e){if(!e)return null;if(e.startsWith("/")){let s=e.split(" \xB7 ");if(s.length>1)return s[1].trim().toLowerCase()}let t=e.split(" \xB7 ");return t.length>1?t[t.length-1].trim().toLowerCase():null}function Fo(e,t){let s=t-e;if(s<0)return{weight:0,label:null};for(let n of Do)if(s<=n.maxGapMs)return{weight:n.weight,label:n.label};return{weight:0,label:null}}function Po(e){if(e.length===0)return{weight:0,matched:null,matchedIndex:-1};let t=[.4,.35,.3,.25,.2,.15,.1],s=Math.min(e.length,t.length);for(let n=0;n<s;n++){let r=e[n];if(!r)continue;let i=r.toLowerCase();for(let o of Mo)if(i.includes(o))return{weight:t[n],matched:o,matchedIndex:n}}return{weight:0,matched:null,matchedIndex:-1}}function bt(e,t){if(!e||!t||e.length!==t.length)return 0;let s=0;for(let n=0;n<e.length;n++)s+=e[n]*t[n];return s<-1?-1:s>1?1:s}function Uo(e,t){if(e.length===0||t.length===0)return 0;let s=0;for(let n of e)for(let r of t){let i=bt(n,r);i>s&&(s=i)}return s}function $o(e,t){let s=bt(e.mean_embedding,t.mean_embedding),n=bt(e.tail_pool,t.head_pool),r=Uo(e.sample_chunks,t.sample_chunks),i=0,o=null;if(s>i&&(i=s,o="mean"),n>i&&(i=n,o="asymmetric"),r>i&&(i=r,o="max_pool"),i<.65)return{weight:0,cosine:i,mode:null};if(i>=.85)return{weight:.3,cosine:i,mode:o};let a=(i-.65)/.2*.3;return{weight:Math.round(a*100)/100,cosine:i,mode:o}}function jo(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 Ho(e,t){if(e.size===0||t.size===0)return{weight:0,count:0};let s=0;for(let r of t)e.has(r)&&s++;return s===0?{weight:0,count:0}:{weight:Math.min(.4,s*.1),count:s}}function Bo(e,t){let s=Vs(e),n=Vs(t);return s&&n&&s===n?{weight:.1,brand:s}:{weight:0,brand:null}}function qs(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function Wo(e,t){let s=0,n=null,r=!1;if(e.authored_paths.size>0){let i=t.recent_user_messages.slice(0,3).join(`
862
- `).toLowerCase();for(let o of e.authored_paths){let a=o.toLowerCase();if(t.touched_files.has(o)||i.includes(a)){s+=.5,n=o;break}}}if(e.authored_content.length>0&&t.recent_user_messages[0]){let i=qs(t.recent_user_messages[0]);if(i.length>=200)for(let o of e.authored_content){let a=qs(o);if(a.length<200)continue;let c=Math.min(a.length,240),d=a.slice(0,c);if(i.includes(d)){s+=.4,r=!0;break}}}return{weight:Math.min(.6,s),pathMatch:n,contentMatch:r}}function Xo(e,t,s=Zs){if(t.started_at_ms<=e.started_at_ms)return null;let n=e.ended_at_ms??e.started_at_ms;if(t.started_at_ms<n)return null;let r=Fo(n,t.started_at_ms),i=t.recent_user_messages.length>0?t.recent_user_messages:t.first_user_message?[t.first_user_message]:[],o=Po(i),a=Ho(e.touched_files,t.touched_files),c=Bo(e.auto_title,t.auto_title),d=$o(e,t),l=jo(e,t),u=Wo(e,t),p=r.weight+o.weight+a.weight+c.weight+d.weight+l.weight+u.weight;if(p<s)return null;let g=[];if(r.label&&g.push(`temporal ${r.label} (+${r.weight})`),o.matched){let _=o.matchedIndex===0?"opening message":`message #${o.matchedIndex+1}`;g.push(`continuation phrase "${o.matched}" in ${_} (+${o.weight})`)}if(a.count>0&&g.push(`${a.count} file${a.count===1?"":"s"} overlap (+${a.weight.toFixed(1)})`),c.brand&&g.push(`shared brand "${c.brand}" (+${c.weight})`),d.weight>0&&d.mode&&g.push(`semantic ${d.mode==="asymmetric"?"tail\u2192head":d.mode==="max_pool"?"best-chunk":"mean"} ${d.cosine.toFixed(2)} (+${d.weight.toFixed(2)})`),l.same&&g.push(`same cluster (+${l.weight})`),u.weight>0){let _=[];u.pathMatch&&_.push(`opened authored path "${u.pathMatch.split("/").pop()}"`),u.contentMatch&&_.push("verbatim-paste of authored content"),g.push(`doc-authorship: ${_.join(" + ")} (+${u.weight.toFixed(2)})`)}return{parent_id:e.id,child_id:t.id,confidence:Math.min(1,p),signals:{temporal:r.weight,continuation:o.weight,file_overlap:a.weight,same_brand:c.weight,semantic:d.weight,cluster:l.weight,doc_authorship:u.weight},reasons:g}}function Qs(e,t=Zs){let s=[];for(let n=0;n<e.length;n++){let r=e[n],i=null;for(let o=0;o<n;o++){let a=e[o],c=Xo(a,r,t);c&&(!i||c.confidence>i.confidence)&&(i=c)}i&&s.push(i)}return s}function en(e,t={}){let s=t.maxUserMessages??5,n=t.userMessageMaxLen??2e3,r=new Set,i=[],o=new Set,a=[],c;try{if(vo(e).size>Co)return{touched_files:r,recent_user_messages:i,authored_paths:o,authored_content:a};c=ko(e,"utf8")}catch{return{touched_files:r,recent_user_messages:i,authored_paths:o,authored_content:a}}let d=0;for(;d<c.length;){let l=c.indexOf(`
863
- `,d),u=l===-1?c.length:l,p=c.slice(d,u);if(d=l===-1?c.length:l+1,!p.trim())continue;let g;try{g=JSON.parse(p)}catch{continue}let _=g;if(_.type==="user"&&_.message?.role==="user"&&typeof _.message.content=="string"&&i.length<s){let E=_.message.content.trim();E&&i.push(E.length>n?E.slice(0,n):E)}let f=_.message?.content;if(Array.isArray(f))for(let E of f){if(!E||typeof E!="object")continue;let T=E;if(T.type!=="tool_use")continue;let w=T.input??{},N=typeof w.file_path=="string"?w.file_path:null;if(N){let L=Me(N);L&&r.add(L)}if((T.name==="Write"||T.name==="Edit"||T.name==="MultiEdit")&&N){let L=Me(N);L&&o.add(L);let A=typeof w.content=="string"?w.content:typeof w.new_string=="string"?w.new_string:null;A&&A.length>=200&&a.push(A.length>4096?A.slice(0,4096):A)}if(T.name==="Bash"&&typeof w.command=="string")for(let L of w.command.matchAll(/(?:^|[\s'"`(=])((?:\.\.?\/|\/|src\/|test\/|docs\/|site\/)[A-Za-z0-9_./-]+)/g)){let A=Me(L[1]);A&&r.add(A)}if((T.name==="Glob"||T.name==="Grep")&&typeof w.pattern=="string"){let L=Me(w.pattern);L&&!L.includes("*")&&r.add(L)}}}return{touched_files:r,recent_user_messages:i,authored_paths:o,authored_content:a}}function Me(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}var Rt=Yo(Go),Jo=6,sn="Active ",nn=" sessions \u2014 ",Ko=6e4;async function Vo(){if(Fe()==="win32")return[];for(let t of["/bin/ps","/usr/bin/ps"])try{let{stdout:s}=await Rt(t,["-eo","pid=,comm="],{timeout:2e3}),n=[];for(let r of s.split(`
864
- `)){let i=r.trim().match(/^(\d+)\s+(.+)$/);if(!i)continue;let o=Number(i[1]),a=i[2].trim();(a==="claude"||a.endsWith("/claude")||a.endsWith("/bin/claude"))&&Number.isFinite(o)&&n.push(o)}return n}catch{continue}return[]}async function qo(e){let t=Fe();if(t==="linux")try{return(await zo(`/proc/${e}/cwd`)).replace(/\/+$/,"")}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let s of["/usr/sbin/lsof","/usr/bin/lsof"])try{let{stdout:n}=await Rt(s,["-a","-p",String(e),"-d","cwd","-Fn"],{timeout:2e3});for(let r of n.split(`
865
- `))if(r.startsWith("n"))return r.slice(1).replace(/\/+$/,"");return null}catch{continue}return null}async function Zo(e){let t=Fe();if(t==="linux")try{let s=await tn(`/proc/${e}/stat`,"utf8"),n=s.lastIndexOf(")");if(n===-1)return null;let r=s.slice(n+1).trim().split(/\s+/),i=Number(r[19]);if(!Number.isFinite(i))return null;let o=await tn("/proc/uptime","utf8"),a=Number(o.split(/\s+/)[0]);return Number.isFinite(a)?Date.now()-a*1e3+i/100*1e3:null}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let s of["/bin/ps","/usr/bin/ps"])try{let{stdout:n}=await Rt(s,["-o","lstart=","-p",String(e)],{timeout:2e3}),r=Date.parse(n.trim());return Number.isFinite(r)?r:null}catch{continue}return null}async function Qo(e,t){let s=await Vo();if(s.length===0)return null;let n=e.replace(/\/+$/,""),r=[];for(let o of s){let a=await qo(o);if(a&&(a===n||a.startsWith(n+"/"))){let c=await Zo(o);r.push({pid:o,startMs:c})}}if(r.length===0)return new Set;let i=new Set;for(let{startMs:o}of r){if(o==null)continue;let a=null,c=Ko;for(let d of t){if(i.has(d.session_id)||!d.started_at)continue;let l=Date.parse(d.started_at);if(!Number.isFinite(l))continue;let u=Math.abs(l-o);u<c&&(c=u,a=d)}a&&i.add(a.session_id)}return i}function ea(e){let s=m().prepare("SELECT id, name, decoded_path FROM projects WHERE id = ? LIMIT 1").get(e);if(!s)throw new Error(`project ${e} not found`);return s}function ta(e,t){let s=m(),n=`${sn}${t}${nn}%`,r=s.prepare(`SELECT t.id
861
+ ${s}`}function xo(e){return Ws(e)?.auto_title_source??null}async function Io(e){let{spawnClaudePrompt:t,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(wt(),en));if(!s())throw new Error("claude CLI not found on PATH");let n=await t(e.prompt,[],{model:e.model});if(!n.success)throw new Error(`claude CLI exited ${n.exitCode}: ${n.stderr.slice(-500)}`);let r=ko(n.stdout);if(!r)throw new Error("claude CLI returned an empty title");return r.slice(0,Lo)}function ko(e){let t=e.trim();if(!t)return"";try{let s=JSON.parse(t);if(s&&typeof s=="object"){let n=s.result;if(typeof n=="string")return tn(n)}}catch{}return tn(t)}function tn(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}async function nn(e,t={}){let s=U(e);if(!s)throw new Error(`thread not found: ${e}`);let n=Ao(s.edges),r=n.length,i=t.force??!1,o=t.signal,a=[],c=[],d=[];for(let l=0;l<n.length;l++){let u=n[l],p=l+1;if(o?.aborted){let _={sessionId:u,reason:"cancelled"};c.push(_),t.onSkipped?.(_);continue}if(!i&&xo(u)==="agent"){let _={sessionId:u,reason:"already-titled"};c.push(_),t.onSkipped?.(_);continue}let g;try{if(sn)g=await sn({sessionId:u,current:p,total:r});else{let _=Oo({sessionId:u,current:p,total:r});g=await Io({prompt:_,model:t.model})}}catch(_){let h=_ instanceof Error?_.message:String(_??"unknown error"),E={sessionId:u,error:h};d.push(E),t.onFailed?.(E);continue}try{bt(u,g,"agent")}catch(_){let h=_ instanceof Error?_.message:String(_??"unknown error"),E={sessionId:u,error:`setAutoTitle failed: ${h}`};d.push(E),t.onFailed?.(E);continue}a.push(u),t.onProgress?.({current:p,total:r,sessionId:u,title:g})}return{generated:a,skipped:c,failed:d}}var sn=null;N();import{execFile as Yo}from"node:child_process";import{promisify as zo}from"node:util";import{readlink as Jo,readFile as dn}from"node:fs/promises";import{platform as Ue}from"node:os";import{readFileSync as Co,statSync as vo}from"node:fs";var Do=200*1024*1024;var Nt=.5,an=Nt,Mo=[{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"}],Fo=["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 rn(e){if(!e)return null;if(e.startsWith("/")){let s=e.split(" \xB7 ");if(s.length>1)return s[1].trim().toLowerCase()}let t=e.split(" \xB7 ");return t.length>1?t[t.length-1].trim().toLowerCase():null}function Po(e,t){let s=t-e;if(s<0)return{weight:0,label:null};for(let n of Mo)if(s<=n.maxGapMs)return{weight:n.weight,label:n.label};return{weight:0,label:null}}function Uo(e){if(e.length===0)return{weight:0,matched:null,matchedIndex:-1};let t=[.4,.35,.3,.25,.2,.15,.1],s=Math.min(e.length,t.length);for(let n=0;n<s;n++){let r=e[n];if(!r)continue;let i=r.toLowerCase();for(let o of Fo)if(i.includes(o))return{weight:t[n],matched:o,matchedIndex:n}}return{weight:0,matched:null,matchedIndex:-1}}function Rt(e,t){if(!e||!t||e.length!==t.length)return 0;let s=0;for(let n=0;n<e.length;n++)s+=e[n]*t[n];return s<-1?-1:s>1?1:s}function $o(e,t){if(e.length===0||t.length===0)return 0;let s=0;for(let n of e)for(let r of t){let i=Rt(n,r);i>s&&(s=i)}return s}function jo(e,t){let s=Rt(e.mean_embedding,t.mean_embedding),n=Rt(e.tail_pool,t.head_pool),r=$o(e.sample_chunks,t.sample_chunks),i=0,o=null;if(s>i&&(i=s,o="mean"),n>i&&(i=n,o="asymmetric"),r>i&&(i=r,o="max_pool"),i<.65)return{weight:0,cosine:i,mode:null};if(i>=.85)return{weight:.3,cosine:i,mode:o};let a=(i-.65)/.2*.3;return{weight:Math.round(a*100)/100,cosine:i,mode:o}}function Ho(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 Bo(e,t){if(e.size===0||t.size===0)return{weight:0,count:0};let s=0;for(let r of t)e.has(r)&&s++;return s===0?{weight:0,count:0}:{weight:Math.min(.4,s*.1),count:s}}function Wo(e,t){let s=rn(e),n=rn(t);return s&&n&&s===n?{weight:.1,brand:s}:{weight:0,brand:null}}function on(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function Xo(e,t){let s=0,n=null,r=!1;if(e.authored_paths.size>0){let i=t.recent_user_messages.slice(0,3).join(`
862
+ `).toLowerCase();for(let o of e.authored_paths){let a=o.toLowerCase();if(t.touched_files.has(o)||i.includes(a)){s+=.5,n=o;break}}}if(e.authored_content.length>0&&t.recent_user_messages[0]){let i=on(t.recent_user_messages[0]);if(i.length>=200)for(let o of e.authored_content){let a=on(o);if(a.length<200)continue;let c=Math.min(a.length,240),d=a.slice(0,c);if(i.includes(d)){s+=.4,r=!0;break}}}return{weight:Math.min(.6,s),pathMatch:n,contentMatch:r}}function Go(e,t,s=an){if(t.started_at_ms<=e.started_at_ms)return null;let n=e.ended_at_ms??e.started_at_ms;if(t.started_at_ms<n)return null;let r=Po(n,t.started_at_ms),i=t.recent_user_messages.length>0?t.recent_user_messages:t.first_user_message?[t.first_user_message]:[],o=Uo(i),a=Bo(e.touched_files,t.touched_files),c=Wo(e.auto_title,t.auto_title),d=jo(e,t),l=Ho(e,t),u=Xo(e,t),p=r.weight+o.weight+a.weight+c.weight+d.weight+l.weight+u.weight;if(p<s)return null;let g=[];if(r.label&&g.push(`temporal ${r.label} (+${r.weight})`),o.matched){let _=o.matchedIndex===0?"opening message":`message #${o.matchedIndex+1}`;g.push(`continuation phrase "${o.matched}" in ${_} (+${o.weight})`)}if(a.count>0&&g.push(`${a.count} file${a.count===1?"":"s"} overlap (+${a.weight.toFixed(1)})`),c.brand&&g.push(`shared brand "${c.brand}" (+${c.weight})`),d.weight>0&&d.mode&&g.push(`semantic ${d.mode==="asymmetric"?"tail\u2192head":d.mode==="max_pool"?"best-chunk":"mean"} ${d.cosine.toFixed(2)} (+${d.weight.toFixed(2)})`),l.same&&g.push(`same cluster (+${l.weight})`),u.weight>0){let _=[];u.pathMatch&&_.push(`opened authored path "${u.pathMatch.split("/").pop()}"`),u.contentMatch&&_.push("verbatim-paste of authored content"),g.push(`doc-authorship: ${_.join(" + ")} (+${u.weight.toFixed(2)})`)}return{parent_id:e.id,child_id:t.id,confidence:Math.min(1,p),signals:{temporal:r.weight,continuation:o.weight,file_overlap:a.weight,same_brand:c.weight,semantic:d.weight,cluster:l.weight,doc_authorship:u.weight},reasons:g}}function cn(e,t=an){let s=[];for(let n=0;n<e.length;n++){let r=e[n],i=null;for(let o=0;o<n;o++){let a=e[o],c=Go(a,r,t);c&&(!i||c.confidence>i.confidence)&&(i=c)}i&&s.push(i)}return s}function ln(e,t={}){let s=t.maxUserMessages??5,n=t.userMessageMaxLen??2e3,r=new Set,i=[],o=new Set,a=[],c;try{if(vo(e).size>Do)return{touched_files:r,recent_user_messages:i,authored_paths:o,authored_content:a};c=Co(e,"utf8")}catch{return{touched_files:r,recent_user_messages:i,authored_paths:o,authored_content:a}}let d=0;for(;d<c.length;){let l=c.indexOf(`
863
+ `,d),u=l===-1?c.length:l,p=c.slice(d,u);if(d=l===-1?c.length:l+1,!p.trim())continue;let g;try{g=JSON.parse(p)}catch{continue}let _=g;if(_.type==="user"&&_.message?.role==="user"&&typeof _.message.content=="string"&&i.length<s){let E=_.message.content.trim();E&&i.push(E.length>n?E.slice(0,n):E)}let h=_.message?.content;if(Array.isArray(h))for(let E of h){if(!E||typeof E!="object")continue;let T=E;if(T.type!=="tool_use")continue;let w=T.input??{},v=typeof w.file_path=="string"?w.file_path:null;if(v){let L=Pe(v);L&&r.add(L)}if((T.name==="Write"||T.name==="Edit"||T.name==="MultiEdit")&&v){let L=Pe(v);L&&o.add(L);let P=typeof w.content=="string"?w.content:typeof w.new_string=="string"?w.new_string:null;P&&P.length>=200&&a.push(P.length>4096?P.slice(0,4096):P)}if(T.name==="Bash"&&typeof w.command=="string")for(let L of w.command.matchAll(/(?:^|[\s'"`(=])((?:\.\.?\/|\/|src\/|test\/|docs\/|site\/)[A-Za-z0-9_./-]+)/g)){let P=Pe(L[1]);P&&r.add(P)}if((T.name==="Glob"||T.name==="Grep")&&typeof w.pattern=="string"){let L=Pe(w.pattern);L&&!L.includes("*")&&r.add(L)}}}return{touched_files:r,recent_user_messages:i,authored_paths:o,authored_content:a}}function Pe(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}var At=zo(Yo),Ko=6,un="Active ",pn=" sessions \u2014 ",Vo=6e4;async function qo(){if(Ue()==="win32")return[];for(let t of["/bin/ps","/usr/bin/ps"])try{let{stdout:s}=await At(t,["-eo","pid=,comm="],{timeout:2e3}),n=[];for(let r of s.split(`
864
+ `)){let i=r.trim().match(/^(\d+)\s+(.+)$/);if(!i)continue;let o=Number(i[1]),a=i[2].trim();(a==="claude"||a.endsWith("/claude")||a.endsWith("/bin/claude"))&&Number.isFinite(o)&&n.push(o)}return n}catch{continue}return[]}async function Zo(e){let t=Ue();if(t==="linux")try{return(await Jo(`/proc/${e}/cwd`)).replace(/\/+$/,"")}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let s of["/usr/sbin/lsof","/usr/bin/lsof"])try{let{stdout:n}=await At(s,["-a","-p",String(e),"-d","cwd","-Fn"],{timeout:2e3});for(let r of n.split(`
865
+ `))if(r.startsWith("n"))return r.slice(1).replace(/\/+$/,"");return null}catch{continue}return null}async function Qo(e){let t=Ue();if(t==="linux")try{let s=await dn(`/proc/${e}/stat`,"utf8"),n=s.lastIndexOf(")");if(n===-1)return null;let r=s.slice(n+1).trim().split(/\s+/),i=Number(r[19]);if(!Number.isFinite(i))return null;let o=await dn("/proc/uptime","utf8"),a=Number(o.split(/\s+/)[0]);return Number.isFinite(a)?Date.now()-a*1e3+i/100*1e3:null}catch{return null}if(t==="darwin"||t==="freebsd"||t==="openbsd")for(let s of["/bin/ps","/usr/bin/ps"])try{let{stdout:n}=await At(s,["-o","lstart=","-p",String(e)],{timeout:2e3}),r=Date.parse(n.trim());return Number.isFinite(r)?r:null}catch{continue}return null}async function ea(e,t){let s=await qo();if(s.length===0)return null;let n=e.replace(/\/+$/,""),r=[];for(let o of s){let a=await Zo(o);if(a&&(a===n||a.startsWith(n+"/"))){let c=await Qo(o);r.push({pid:o,startMs:c})}}if(r.length===0)return new Set;let i=new Set;for(let{startMs:o}of r){if(o==null)continue;let a=null,c=Vo;for(let d of t){if(i.has(d.session_id)||!d.started_at)continue;let l=Date.parse(d.started_at);if(!Number.isFinite(l))continue;let u=Math.abs(l-o);u<c&&(c=u,a=d)}a&&i.add(a.session_id)}return i}function ta(e){let s=m().prepare("SELECT id, name, decoded_path FROM projects WHERE id = ? LIMIT 1").get(e);if(!s)throw new Error(`project ${e} not found`);return s}function sa(e,t){let s=m(),n=`${un}${t}${pn}%`,r=s.prepare(`SELECT t.id
866
866
  FROM threads t
867
867
  WHERE t.archived = 0
868
868
  AND t.name LIKE ?
@@ -873,7 +873,7 @@ ${s}`}function Oo(e){return Ms(e)?.auto_title_source??null}async function xo(e){
873
873
  WHERE s.project_id = ?
874
874
  AND t.archived = 0
875
875
  AND t.name LIKE ?
876
- LIMIT 1`).get(e,n);return i?U(i.id):null}function rn(e){let t=e?new Date(e):new Date,s=t.getFullYear(),n=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0");return`${s}-${n}-${r}`}function wt(e,t){let s=m(),n=t>0,r=n?Date.now()-t*60*60*1e3:0;return n?s.prepare(`SELECT s.id AS session_id,
876
+ LIMIT 1`).get(e,n);return i?U(i.id):null}function mn(e){let t=e?new Date(e):new Date,s=t.getFullYear(),n=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0");return`${s}-${n}-${r}`}function Lt(e,t){let s=m(),n=t>0,r=n?Date.now()-t*60*60*1e3:0;return n?s.prepare(`SELECT s.id AS session_id,
877
877
  sa.alias AS alias,
878
878
  s.auto_title AS auto_title,
879
879
  s.first_user_message AS first_user_message,
@@ -893,29 +893,26 @@ ${s}`}function Oo(e){return Ms(e)?.auto_title_source??null}async function xo(e){
893
893
  FROM sessions s
894
894
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
895
895
  WHERE s.project_id = ?
896
- ORDER BY s.started_at ASC`).all(e)}function sa(e){let t=m(),s=[];for(let n of e){if(!n.started_at)continue;let r=Date.parse(n.started_at);if(!Number.isFinite(r))continue;let i=t.prepare("SELECT file_path, ended_at FROM sessions WHERE id = ?").get(n.session_id);if(!i?.file_path)continue;let o=i.ended_at?Date.parse(i.ended_at):null,a=en(i.file_path,{maxUserMessages:7});s.push({id:n.session_id,started_at_ms:r,ended_at_ms:Number.isFinite(o)?o:null,first_user_message:n.first_user_message,recent_user_messages:a.recent_user_messages,auto_title:n.auto_title,touched_files:a.touched_files,mean_embedding:null,head_pool:null,tail_pool:null,sample_chunks:[],cluster_id:null,authored_paths:a.authored_paths,authored_content:a.authored_content})}return s}function na(e){let s=m().prepare(`SELECT session_id, parent_session_id, source
896
+ ORDER BY s.started_at ASC`).all(e)}function na(e){let t=m(),s=[];for(let n of e){if(!n.started_at)continue;let r=Date.parse(n.started_at);if(!Number.isFinite(r))continue;let i=t.prepare("SELECT file_path, ended_at FROM sessions WHERE id = ?").get(n.session_id);if(!i?.file_path)continue;let o=i.ended_at?Date.parse(i.ended_at):null,a=ln(i.file_path,{maxUserMessages:7});s.push({id:n.session_id,started_at_ms:r,ended_at_ms:Number.isFinite(o)?o:null,first_user_message:n.first_user_message,recent_user_messages:a.recent_user_messages,auto_title:n.auto_title,touched_files:a.touched_files,mean_embedding:null,head_pool:null,tail_pool:null,sample_chunks:[],cluster_id:null,authored_paths:a.authored_paths,authored_content:a.authored_content})}return s}function ra(e){let s=m().prepare(`SELECT session_id, parent_session_id, source
897
897
  FROM thread_edges
898
- WHERE thread_id = ?`).all(e),n=new Map;for(let r of s)n.set(r.session_id,{parent_session_id:r.parent_session_id,source:r.source});return n}async function Nt(e,t={}){let s=ea(e),n=t.windowHours??Jo,r=t.scoreThreshold??yt,i=t.useLivePids??!0,o=[],a=[];if(i&&s.decoded_path){let f=wt(e,0),E=await Qo(s.decoded_path,f);if(E===null){let w=Fe()==="win32"?"Windows live-PID detection is not yet supported \u2014 falling back to the rolling mtime window.":"No live `claude` processes detected \u2014 falling back to the rolling mtime window. Output may include sessions that are no longer open.";o.push(w),a=wt(e,n)}else E.size===0?(o.push(`No active terminals open in ${s.name} (cwd=${s.decoded_path}). Open a Claude terminal in this repo and re-run.`),a=[]):a=f.filter(T=>E.has(T.session_id))}else a=wt(e,n);a.length===0&&!o.length&&o.push(`No active sessions in ${s.name} within the last ${n}h.`);let c=ta(e,s.name),d=new Set(c?c.edges.map(f=>f.session_id):[]),l=a.filter(f=>!d.has(f.session_id)),u=sa(a);u.sort((f,E)=>f.started_at_ms-E.started_at_ms);let p=Qs(u,r),g=c?c.edges.filter(f=>f.source!=="auto-active"&&(f.parent_session_id||f.role==="origin")).map(f=>({session_id:f.session_id,parent_session_id:f.parent_session_id})):[],_=c?c.name:`${sn}${s.name}${nn}${rn(t.todayIso)}`;return{project:s,thread:{id:c?.id??null,name:_,exists:!!c,existing_session_count:c?.edges.length??0},candidates:a,proposed_additions:l,proposed_edges:p,preserved_manual_edges:g,warnings:o}}function on(e){let t={thread_id:"",added:0,edges_set:0,preserved_manual:e.preserved_manual_edges.length},s;e.thread.exists&&e.thread.id?s=e.thread.id:s=ve({name:e.thread.name,summary:`Auto-captured by sync-active on ${rn()}. Members are sessions in ${e.project.name} that were active within the rolling window. Re-runnable: subsequent runs append new active sessions and never overwrite manual edges.`}).id,t.thread_id=s;let n=na(s);for(let o of e.candidates)n.has(o.session_id)||(Ce({threadId:s,sessionId:o.session_id,source:"auto-active",confidence:.5}),t.added++,n.set(o.session_id,{parent_session_id:null,source:"auto-active"}));for(let o of e.proposed_edges){let a=n.get(o.child_id);if(a&&a.source==="auto-active"&&a.parent_session_id!==o.parent_id&&n.has(o.parent_id))try{ge(s,o.child_id,o.parent_id),t.edges_set++,n.set(o.child_id,{parent_session_id:o.parent_id,source:a.source})}catch{}}let i=m().prepare(`SELECT session_id FROM thread_edges
898
+ WHERE thread_id = ?`).all(e),n=new Map;for(let r of s)n.set(r.session_id,{parent_session_id:r.parent_session_id,source:r.source});return n}async function Ot(e,t={}){let s=ta(e),n=t.windowHours??Ko,r=t.scoreThreshold??Nt,i=t.useLivePids??!0,o=[],a=[];if(i&&s.decoded_path){let h=Lt(e,0),E=await ea(s.decoded_path,h);if(E===null){let w=Ue()==="win32"?"Windows live-PID detection is not yet supported \u2014 falling back to the rolling mtime window.":"No live `claude` processes detected \u2014 falling back to the rolling mtime window. Output may include sessions that are no longer open.";o.push(w),a=Lt(e,n)}else E.size===0?(o.push(`No active terminals open in ${s.name} (cwd=${s.decoded_path}). Open a Claude terminal in this repo and re-run.`),a=[]):a=h.filter(T=>E.has(T.session_id))}else a=Lt(e,n);a.length===0&&!o.length&&o.push(`No active sessions in ${s.name} within the last ${n}h.`);let c=sa(e,s.name),d=new Set(c?c.edges.map(h=>h.session_id):[]),l=a.filter(h=>!d.has(h.session_id)),u=na(a);u.sort((h,E)=>h.started_at_ms-E.started_at_ms);let p=cn(u,r),g=c?c.edges.filter(h=>h.source!=="auto-active"&&(h.parent_session_id||h.role==="origin")).map(h=>({session_id:h.session_id,parent_session_id:h.parent_session_id})):[],_=c?c.name:`${un}${s.name}${pn}${mn(t.todayIso)}`;return{project:s,thread:{id:c?.id??null,name:_,exists:!!c,existing_session_count:c?.edges.length??0},candidates:a,proposed_additions:l,proposed_edges:p,preserved_manual_edges:g,warnings:o}}function gn(e){let t={thread_id:"",added:0,edges_set:0,preserved_manual:e.preserved_manual_edges.length},s;e.thread.exists&&e.thread.id?s=e.thread.id:s=De({name:e.thread.name,summary:`Auto-captured by sync-active on ${mn()}. Members are sessions in ${e.project.name} that were active within the rolling window. Re-runnable: subsequent runs append new active sessions and never overwrite manual edges.`}).id,t.thread_id=s;let n=ra(s);for(let o of e.candidates)n.has(o.session_id)||(Me({threadId:s,sessionId:o.session_id,source:"auto-active",confidence:.5}),t.added++,n.set(o.session_id,{parent_session_id:null,source:"auto-active"}));for(let o of e.proposed_edges){let a=n.get(o.child_id);if(a&&a.source==="auto-active"&&a.parent_session_id!==o.parent_id&&n.has(o.parent_id))try{_e(s,o.child_id,o.parent_id),t.edges_set++,n.set(o.child_id,{parent_session_id:o.parent_id,source:a.source})}catch{}}let i=m().prepare(`SELECT session_id FROM thread_edges
899
899
  WHERE thread_id = ?
900
900
  AND source = 'auto-active'
901
901
  AND parent_session_id IS NULL
902
- AND role = 'child'`).all(s);for(let o of i)try{ge(s,o.session_id,null)}catch{}return t}function P(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function ra(e){return{content:[{type:"text",text:e}],isError:!0}}function $(e,t){return H.object(e).strict().parse(t)}var Tn=H.string().uuid(),z=Tn.describe("Thread id (UUID)."),ee=Tn.describe("Session UUID (exact, not a prefix)."),an={include_archived:H.boolean().optional().describe("Include archived threads (default false).")},cn={id:z},ln={session_id:ee},dn={name:H.string().min(1).max(200).describe("Human-readable thread name."),summary:H.string().max(4e3).optional().describe("Optional short description."),origin_session_id:ee.optional().describe("Seed the thread with this session as its origin.")},un={thread_id:z,session_id:ee,parent_session_id:ee.optional().describe("If present, the edge is role=child with this parent; otherwise role=origin."),role:H.enum(["origin","child"]).optional()},pn={thread_id:z,session_id:ee,parent_session_id:ee.nullable().describe("New parent (null to clear and promote the edge back to role=origin).")},mn={thread_id:z,session_id:ee},gn={thread_id:z,name:H.string().min(1).max(200)},ce={thread_id:z},_n={source_id:z.describe("Thread to dissolve \u2014 its edges move into dest_id."),dest_id:z.describe("Thread that absorbs source_id.")},fn={thread_id:z,session_ids:H.array(ee).min(1).max(500),new_thread_name:H.string().min(1).max(200)},hn={thread_id:z,force:H.boolean().optional().describe("When true, regenerate titles for sessions that already have an agent-sourced title. Default false skips them.")},En={project_id:H.number().int().positive().describe("Numeric project id from list_projects. The sync is repo-scoped and never crosses projects."),mode:H.enum(["preflight","apply"]).describe("preflight returns the proposed plan without writing; apply writes the thread + edges and is idempotent."),window_hours:H.number().min(.5).max(168).optional().describe("Rolling activity window in hours. Default 6.")};function Sn(e){e.registerTool("thread_list",{title:"List threads",description:"All threads (v0.15a intent groups), newest first. Excludes archived threads unless include_archived is true.",inputSchema:an},async t=>{try{let{include_archived:s}=$(an,t);return P(gt({includeArchived:s??!1}))}catch(s){return O(s)}}),e.registerTool("thread_get",{title:"Get thread with edges",description:"Full thread detail including every session edge (origin + children).",inputSchema:cn},async t=>{try{let{id:s}=$(cn,t),n=U(s);return n?P(n):ra(`thread not found: ${s}`)}catch(s){return O(s)}}),e.registerTool("thread_for_session",{title:"List threads containing a session",description:"Return non-archived threads that reference this session (as origin or child).",inputSchema:ln},async t=>{try{let{session_id:s}=$(ln,t);return P(Ts(s))}catch(s){return O(s)}})}function bn(e,t={}){let s=t.limiter??new Z;e.registerTool("thread_create",{title:"Create a thread",description:"Create a new thread. Optionally seed it with an origin session. Name is required; summary is optional.",inputSchema:dn},async n=>{try{let r=$(dn,n),i=await I({tool:"thread_create",args:r,limiter:s,run:()=>ve({name:r.name,summary:r.summary??null,originSessionId:r.origin_session_id})});return P(i)}catch(r){return O(r)}}),e.registerTool("thread_add_session",{title:"Add session to thread",description:"Attach a session to a thread. If parent_session_id is provided the edge is role=child; otherwise role=origin. Upsert: re-adding updates the edge.",inputSchema:un},async n=>{try{let r=$(un,n),i=await I({tool:"thread_add_session",args:r,limiter:s,run:()=>Ce({threadId:r.thread_id,sessionId:r.session_id,parentSessionId:r.parent_session_id??null,role:r.role,source:"manual",confidence:1})});return P(i)}catch(r){return O(r)}}),e.registerTool("thread_set_parent",{title:"Set edge parent within thread",description:"Change the parent session of an existing thread edge. Pass null to clear the parent and promote the edge to role=origin.",inputSchema:pn},async n=>{try{let r=$(pn,n),i=await I({tool:"thread_set_parent",args:r,limiter:s,run:()=>ge(r.thread_id,r.session_id,r.parent_session_id)});return P(i)}catch(r){return O(r)}}),e.registerTool("thread_remove_session",{title:"Remove session from thread",description:"Detach a session from a thread. The session itself is untouched; only the edge is removed. The updated thread is re-mirrored to disk.",inputSchema:mn},async n=>{try{let r=$(mn,n),i=await I({tool:"thread_remove_session",args:r,limiter:s,run:()=>Ss(r.thread_id,r.session_id)});return P({thread_id:r.thread_id,session_id:r.session_id,...i})}catch(r){return O(r)}}),e.registerTool("thread_rename",{title:"Rename thread",description:"Change the display name of a thread.",inputSchema:gn},async n=>{try{let r=$(gn,n),i=await I({tool:"thread_rename",args:r,limiter:s,run:()=>bs(r.thread_id,r.name)});return P(i)}catch(r){return O(r)}}),e.registerTool("thread_close",{title:"Close thread",description:"Mark the thread as closed (sets closed_at). Thread remains listed; reopen to clear.",inputSchema:ce},async n=>{try{let r=$(ce,n),i=await I({tool:"thread_close",args:r,limiter:s,run:()=>ys(r.thread_id)});return P(i)}catch(r){return O(r)}}),e.registerTool("thread_reopen",{title:"Reopen thread",description:"Clear closed_at on a closed thread.",inputSchema:ce},async n=>{try{let r=$(ce,n),i=await I({tool:"thread_reopen",args:r,limiter:s,run:()=>ws(r.thread_id)});return P(i)}catch(r){return O(r)}}),e.registerTool("thread_archive",{title:"Archive thread",description:"Soft-delete a thread by setting archived=1. Archived threads are hidden from thread_list by default but never hard-deleted; data is preserved in SQLite and the JSON mirror.",inputSchema:ce},async n=>{try{let r=$(ce,n),i=await I({tool:"thread_archive",args:r,limiter:s,run:()=>Rs(r.thread_id)});return P(i)}catch(r){return O(r)}}),e.registerTool("thread_merge",{title:"Merge two threads",description:"Move every edge from source_id into dest_id, then delete source_id. Origin roles are preserved when present on either side.",inputSchema:_n},async n=>{try{let r=$(_n,n),i=await I({tool:"thread_merge",args:r,limiter:s,run:()=>Ns(r.source_id,r.dest_id)});return P(i)}catch(r){return O(r)}}),e.registerTool("thread_split",{title:"Split sessions into a new thread",description:"Peel the specified session_ids out of thread_id into a brand-new thread named new_thread_name. Non-member session_ids are silently skipped.",inputSchema:fn},async n=>{try{let r=$(fn,n),i=await I({tool:"thread_split",args:r,limiter:s,run:()=>Ls({threadId:r.thread_id,sessionIds:r.session_ids,newThreadName:r.new_thread_name})});return P(i)}catch(r){return O(r)}}),e.registerTool("sync_active_sessions",{title:"Sync currently active sessions in a repo into a thread",description:'Captures the Captain Code Pattern: scans every Claude session whose JSONL was touched within the rolling window in the given project, infers parent/child edges via the 7-signal scorer (temporal, continuation, file overlap, brand, semantic, cluster, doc-authorship), and stitches them into one thread. Idempotent: re-running reuses the existing "Active <project> sessions \u2014 *" thread, only appends new sessions, and never overwrites edges with source=manual. Use mode=preflight first to show the user the proposed plan, then mode=apply to write.',inputSchema:En},async n=>{try{let r=$(En,n);if(r.mode==="preflight"){let o=await Nt(r.project_id,{windowHours:r.window_hours});return P({plan:o})}let i=await I({tool:"sync_active_sessions",args:r,limiter:s,run:async()=>{let o=await Nt(r.project_id,{windowHours:r.window_hours}),a=on(o);return{plan:o,result:a}}});return P(i)}catch(r){return O(r)}}),e.registerTool("generate_thread_titles",{title:"Generate titles for every session in a thread",description:"Walk a thread DAG in topology order (origins first by added_at, then children breadth-first) and generate a coherent agent title for each session. Each successive title sees already-titled ancestors and earlier siblings as strong context so a naming pattern emerges. Set force=true to regenerate already-titled sessions; default false skips them. Returns the final {generated, skipped, failed} summary after the whole walk completes.",inputSchema:hn},async n=>{try{let r=$(hn,n),i=await I({tool:"generate_thread_titles",args:r,limiter:s,run:async()=>{let o=await Ks(r.thread_id,{force:r.force??!1});if(o.failed.length>0&&o.generated.length===0&&o.skipped.length===0)throw new Error(`all ${o.failed.length} session(s) failed title generation`);return o}});return P(i)}catch(r){return O(r)}})}import{Worker as ca}from"node:worker_threads";import{join as la}from"node:path";import{existsSync as da}from"node:fs";import{existsSync as ia}from"node:fs";import{dirname as yn}from"node:path";import{fileURLToPath as oa}from"node:url";var Pe=null;function wn(){if(Pe)return Pe;let e=yn(oa(import.meta.url));for(;!ia(`${e}/package.json`);){let t=yn(e);if(t===e)throw new Error(`package.json not found walking up from ${import.meta.url}`);e=t}return Pe=e,Pe}var Rn="BAAI/bge-base-en-v1.5";function ua(){return la(wn(),"dist","daemon","embedder-worker.js")}var le=null,he=new Map,Lt=0,ne=!1,de=class extends Error{kind;path;underlying;cause;constructor(t){super(pa(t)),this.name="EmbedderUnavailableError",this.kind=t.kind,this.path=t.path,this.underlying=t.underlying,this.cause=t}};function pa(e){return["Semantic search is unavailable on this platform.","",`Reason: ${e.detail}`,"",`Platform: ${process.platform}/${process.arch}, Node ${process.version}`,"","Claude Recall supports macOS (arm64/x64), Linux (x64/arm64), and Windows (x64).","Core CLI features (search, list, context, daemon) work everywhere.","Only `recall semantic *` requires the on-device embedder.","","See: https://clauderecall.com/docs (Supported platforms) \u2014 or file an issue at","https://gitlab.com/clauderecallhq/clauderecallhq/-/issues with the platform line above."].join(`
903
- `)}function Nn(e){for(let t of he.values())t.reject(e);he.clear()}function ma(){if(le)return le;let e=ua();if(!da(e))throw new de({kind:"bundle-missing",detail:"embedder-worker bundle not found \u2014 run `npm run build:cli` to emit it",path:e});let t=new ca(e);return t.on("message",s=>{let n=he.get(s.id);n&&(he.delete(s.id),n.resolve(s))}),t.on("error",s=>{console.error("[embedder-worker] thread error:",s);let n=s instanceof Error?s:new Error(String(s));Nn(n),le=null,ne=!1}),t.on("exit",s=>{s!==0&&(console.error(`[embedder-worker] exited with code ${s}`),Nn(new Error(`embedder worker exited with code ${s}`))),le=null,ne=!1}),le=t,t}function Ln(e){return new Promise((t,s)=>{let n;try{n=ma()}catch(r){s(r instanceof Error?r:new Error(String(r)));return}he.set(e.id,{resolve:t,reject:s}),n.postMessage(e)})}function An(){return Lt=Lt+1>>>0,String(Lt)}function On(e){if(!e.ok)throw e.unavailable?new de({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 Ue(){if(!(ne&&le))try{On(await Ln({id:An(),type:"load"})),ne=!0}catch(e){throw ne=!1,e}}function Ee(){return{loaded:ne,modelId:Rn,dim:768}}async function xn(e){if(!ne)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=On(await Ln({id:An(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}R();M();import{writeFileSync as ga}from"node:fs";import{join as _a}from"node:path";var fa=_a(S,"recall-events.json");function In(e,t,s,n="cli"){m().prepare(`
902
+ AND role = 'child'`).all(s);for(let o of i)try{_e(s,o.session_id,null)}catch{}return t}function F(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function ia(e){return{content:[{type:"text",text:e}],isError:!0}}function $(e,t){return H.object(e).strict().parse(t)}var An=H.string().uuid(),z=An.describe("Thread id (UUID)."),te=An.describe("Session UUID (exact, not a prefix)."),_n={include_archived:H.boolean().optional().describe("Include archived threads (default false).")},fn={id:z},hn={session_id:te},En={name:H.string().min(1).max(200).describe("Human-readable thread name."),summary:H.string().max(4e3).optional().describe("Optional short description."),origin_session_id:te.optional().describe("Seed the thread with this session as its origin.")},Tn={thread_id:z,session_id:te,parent_session_id:te.optional().describe("If present, the edge is role=child with this parent; otherwise role=origin."),role:H.enum(["origin","child"]).optional()},Sn={thread_id:z,session_id:te,parent_session_id:te.nullable().describe("New parent (null to clear and promote the edge back to role=origin).")},bn={thread_id:z,session_id:te},yn={thread_id:z,name:H.string().min(1).max(200)},ce={thread_id:z},wn={source_id:z.describe("Thread to dissolve \u2014 its edges move into dest_id."),dest_id:z.describe("Thread that absorbs source_id.")},Rn={thread_id:z,session_ids:H.array(te).min(1).max(500),new_thread_name:H.string().min(1).max(200)},Nn={thread_id:z,force:H.boolean().optional().describe("When true, regenerate titles for sessions that already have an agent-sourced title. Default false skips them.")},Ln={project_id:H.number().int().positive().describe("Numeric project id from list_projects. The sync is repo-scoped and never crosses projects."),mode:H.enum(["preflight","apply"]).describe("preflight returns the proposed plan without writing; apply writes the thread + edges and is idempotent."),window_hours:H.number().min(.5).max(168).optional().describe("Rolling activity window in hours. Default 6.")};function On(e){e.registerTool("thread_list",{title:"List threads",description:"All threads (v0.15a intent groups), newest first. Excludes archived threads unless include_archived is true.",inputSchema:_n},async t=>{try{let{include_archived:s}=$(_n,t);return F(ht({includeArchived:s??!1}))}catch(s){return x(s)}}),e.registerTool("thread_get",{title:"Get thread with edges",description:"Full thread detail including every session edge (origin + children).",inputSchema:fn},async t=>{try{let{id:s}=$(fn,t),n=U(s);return n?F(n):ia(`thread not found: ${s}`)}catch(s){return x(s)}}),e.registerTool("thread_for_session",{title:"List threads containing a session",description:"Return non-archived threads that reference this session (as origin or child).",inputSchema:hn},async t=>{try{let{session_id:s}=$(hn,t);return F(As(s))}catch(s){return x(s)}})}function xn(e,t={}){let s=t.limiter??new Q;e.registerTool("thread_create",{title:"Create a thread",description:"Create a new thread. Optionally seed it with an origin session. Name is required; summary is optional.",inputSchema:En},async n=>{try{let r=$(En,n),i=await I({tool:"thread_create",args:r,limiter:s,run:()=>De({name:r.name,summary:r.summary??null,originSessionId:r.origin_session_id})});return F(i)}catch(r){return x(r)}}),e.registerTool("thread_add_session",{title:"Add session to thread",description:"Attach a session to a thread. If parent_session_id is provided the edge is role=child; otherwise role=origin. Upsert: re-adding updates the edge.",inputSchema:Tn},async n=>{try{let r=$(Tn,n),i=await I({tool:"thread_add_session",args:r,limiter:s,run:()=>Me({threadId:r.thread_id,sessionId:r.session_id,parentSessionId:r.parent_session_id??null,role:r.role,source:"manual",confidence:1})});return F(i)}catch(r){return x(r)}}),e.registerTool("thread_set_parent",{title:"Set edge parent within thread",description:"Change the parent session of an existing thread edge. Pass null to clear the parent and promote the edge to role=origin.",inputSchema:Sn},async n=>{try{let r=$(Sn,n),i=await I({tool:"thread_set_parent",args:r,limiter:s,run:()=>_e(r.thread_id,r.session_id,r.parent_session_id)});return F(i)}catch(r){return x(r)}}),e.registerTool("thread_remove_session",{title:"Remove session from thread",description:"Detach a session from a thread. The session itself is untouched; only the edge is removed. The updated thread is re-mirrored to disk.",inputSchema:bn},async n=>{try{let r=$(bn,n),i=await I({tool:"thread_remove_session",args:r,limiter:s,run:()=>Os(r.thread_id,r.session_id)});return F({thread_id:r.thread_id,session_id:r.session_id,...i})}catch(r){return x(r)}}),e.registerTool("thread_rename",{title:"Rename thread",description:"Change the display name of a thread.",inputSchema:yn},async n=>{try{let r=$(yn,n),i=await I({tool:"thread_rename",args:r,limiter:s,run:()=>xs(r.thread_id,r.name)});return F(i)}catch(r){return x(r)}}),e.registerTool("thread_close",{title:"Close thread",description:"Mark the thread as closed (sets closed_at). Thread remains listed; reopen to clear.",inputSchema:ce},async n=>{try{let r=$(ce,n),i=await I({tool:"thread_close",args:r,limiter:s,run:()=>Is(r.thread_id)});return F(i)}catch(r){return x(r)}}),e.registerTool("thread_reopen",{title:"Reopen thread",description:"Clear closed_at on a closed thread.",inputSchema:ce},async n=>{try{let r=$(ce,n),i=await I({tool:"thread_reopen",args:r,limiter:s,run:()=>ks(r.thread_id)});return F(i)}catch(r){return x(r)}}),e.registerTool("thread_archive",{title:"Archive thread",description:"Soft-delete a thread by setting archived=1. Archived threads are hidden from thread_list by default but never hard-deleted; data is preserved in SQLite and the JSON mirror.",inputSchema:ce},async n=>{try{let r=$(ce,n),i=await I({tool:"thread_archive",args:r,limiter:s,run:()=>Cs(r.thread_id)});return F(i)}catch(r){return x(r)}}),e.registerTool("thread_merge",{title:"Merge two threads",description:"Move every edge from source_id into dest_id, then delete source_id. Origin roles are preserved when present on either side.",inputSchema:wn},async n=>{try{let r=$(wn,n),i=await I({tool:"thread_merge",args:r,limiter:s,run:()=>vs(r.source_id,r.dest_id)});return F(i)}catch(r){return x(r)}}),e.registerTool("thread_split",{title:"Split sessions into a new thread",description:"Peel the specified session_ids out of thread_id into a brand-new thread named new_thread_name. Non-member session_ids are silently skipped.",inputSchema:Rn},async n=>{try{let r=$(Rn,n),i=await I({tool:"thread_split",args:r,limiter:s,run:()=>Ds({threadId:r.thread_id,sessionIds:r.session_ids,newThreadName:r.new_thread_name})});return F(i)}catch(r){return x(r)}}),e.registerTool("sync_active_sessions",{title:"Sync currently active sessions in a repo into a thread",description:'Captures the Captain Code Pattern: scans every Claude session whose JSONL was touched within the rolling window in the given project, infers parent/child edges via the 7-signal scorer (temporal, continuation, file overlap, brand, semantic, cluster, doc-authorship), and stitches them into one thread. Idempotent: re-running reuses the existing "Active <project> sessions \u2014 *" thread, only appends new sessions, and never overwrites edges with source=manual. Use mode=preflight first to show the user the proposed plan, then mode=apply to write.',inputSchema:Ln},async n=>{try{let r=$(Ln,n);if(r.mode==="preflight"){let o=await Ot(r.project_id,{windowHours:r.window_hours});return F({plan:o})}let i=await I({tool:"sync_active_sessions",args:r,limiter:s,run:async()=>{let o=await Ot(r.project_id,{windowHours:r.window_hours}),a=gn(o);return{plan:o,result:a}}});return F(i)}catch(r){return x(r)}}),e.registerTool("generate_thread_titles",{title:"Generate titles for every session in a thread",description:"Walk a thread DAG in topology order (origins first by added_at, then children breadth-first) and generate a coherent agent title for each session. Each successive title sees already-titled ancestors and earlier siblings as strong context so a naming pattern emerges. Set force=true to regenerate already-titled sessions; default false skips them. Returns the final {generated, skipped, failed} summary after the whole walk completes.",inputSchema:Nn},async n=>{try{let r=$(Nn,n),i=await I({tool:"generate_thread_titles",args:r,limiter:s,run:async()=>{let o=await nn(r.thread_id,{force:r.force??!1});if(o.failed.length>0&&o.generated.length===0&&o.skipped.length===0)throw new Error(`all ${o.failed.length} session(s) failed title generation`);return o}});return F(i)}catch(r){return x(r)}})}import{Worker as la}from"node:worker_threads";import{join as da}from"node:path";import{existsSync as ua}from"node:fs";import{existsSync as oa}from"node:fs";import{dirname as In}from"node:path";import{fileURLToPath as aa}from"node:url";var $e=null;function je(){if($e)return $e;let e=In(aa(import.meta.url));for(;!oa(`${e}/package.json`);){let t=In(e);if(t===e)throw new Error(`package.json not found walking up from ${import.meta.url}`);e=t}return $e=e,$e}var kn="BAAI/bge-base-en-v1.5";function pa(){return da(je(),"dist","daemon","embedder-worker.js")}var le=null,Ee=new Map,xt=0,de=!1,ue=class extends Error{kind;path;underlying;cause;constructor(t){super(ma(t)),this.name="EmbedderUnavailableError",this.kind=t.kind,this.path=t.path,this.underlying=t.underlying,this.cause=t}};function ma(e){return["Semantic search is unavailable on this platform.","",`Reason: ${e.detail}`,"",`Platform: ${process.platform}/${process.arch}, Node ${process.version}`,"","Claude Recall supports macOS (arm64/x64), Linux (x64/arm64), and Windows (x64).","Core CLI features (search, list, context, daemon) work everywhere.","Only `recall semantic *` requires the on-device embedder.","","See: https://clauderecall.com/docs (Supported platforms) \u2014 or file an issue at","https://gitlab.com/clauderecallhq/clauderecallhq/-/issues with the platform line above."].join(`
903
+ `)}function Cn(e){for(let t of Ee.values())t.reject(e);Ee.clear()}function ga(){if(le)return le;let e=pa();if(!ua(e))throw new ue({kind:"bundle-missing",detail:"embedder-worker bundle not found \u2014 run `npm run build:cli` to emit it",path:e});let t=new la(e);return t.on("message",s=>{let n=Ee.get(s.id);n&&(Ee.delete(s.id),n.resolve(s))}),t.on("error",s=>{console.error("[embedder-worker] thread error:",s);let n=s instanceof Error?s:new Error(String(s));Cn(n),le=null,de=!1}),t.on("exit",s=>{s!==0&&(console.error(`[embedder-worker] exited with code ${s}`),Cn(new Error(`embedder worker exited with code ${s}`))),le=null,de=!1}),le=t,t}function _a(e){return new Promise((t,s)=>{let n;try{n=ga()}catch(r){s(r instanceof Error?r:new Error(String(r)));return}Ee.set(e.id,{resolve:t,reject:s}),n.postMessage(e)})}function fa(){return xt=xt+1>>>0,String(xt)}function ha(e){if(!e.ok)throw e.unavailable?new ue({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 He(){if(!(de&&le))try{ha(await _a({id:fa(),type:"load"})),de=!0}catch(e){throw de=!1,e}}function Te(){return{loaded:de,modelId:kn,dim:768}}N();D();import{writeFileSync as Ea}from"node:fs";import{join as Ta}from"node:path";var Sa=Ta(S,"recall-events.json");function vn(e,t,s,n="cli"){m().prepare(`
904
904
  INSERT INTO recall_events (session_id, recalled_at, token_count, mode, caller)
905
905
  VALUES (?, datetime('now'), ?, ?, ?)
906
- `).run(e,t,s,n),ha()}function ha(){v();let t=m().prepare("SELECT id, session_id, recalled_at, token_count, mode, caller FROM recall_events ORDER BY recalled_at DESC").all();ga(fa,JSON.stringify(t,null,2)+`
907
- `,"utf-8")}R();async function At(e,t=50){let s=await xn(e),n=m(),r=Buffer.from(s.buffer,s.byteOffset,s.byteLength);return n.prepare(`SELECT v.rowid, v.distance, cm.session_id, cm.text, cm.message_uuids
908
- FROM vec_chunks v JOIN chunk_meta cm ON cm.rowid = v.rowid
909
- WHERE v.embedding MATCH ? AND k = ? ORDER BY v.distance`).all(r,t).map(o=>({sessionId:o.session_id,chunkRowid:o.rowid,distance:o.distance,text:o.text,messageUuids:JSON.parse(o.message_uuids)}))}async function kn(e,t=10,s=.65){let n=m(),r=n.prepare("SELECT rowid FROM chunk_meta WHERE session_id = ? ORDER BY rowid LIMIT 1").get(e);if(!r)return[];let i=n.prepare("SELECT embedding FROM vec_chunks WHERE rowid = ?").get(r.rowid);if(!i)return[];let o=n.prepare(`SELECT v.rowid, v.distance, cm.session_id FROM vec_chunks v JOIN chunk_meta cm ON cm.rowid = v.rowid
910
- WHERE v.embedding MATCH ? AND k = ? ORDER BY v.distance`).all(i.embedding,t*5),a=new Map;for(let d of o){if(d.session_id===e)continue;let l=a.get(d.session_id);(l===void 0||d.distance<l)&&a.set(d.session_id,d.distance)}let c=[];for(let[d,l]of a){let u=1-l;u>=s&&c.push({sessionId:d,similarity:u})}return c.sort((d,l)=>l.similarity-d.similarity),c.slice(0,t)}function Ea(){let e=process.env.RECALL_RRF_K;if(e){let t=parseInt(e,10);if(!isNaN(t)&&t>=1&&t<=1e3)return t}return 60}function vn(e){let t=Ea(),s=new Map;for(let r of e)for(let i=0;i<r.length;i++){let o=r[i],a=1/(t+i+1),c=s.get(o.id);c?(c.score+=a,c.lanes.push(o.lane),i+1<c.bestRank&&(c.bestRank=i+1,c.bestData=o.data)):s.set(o.id,{score:a,lanes:[o.lane],bestRank:i+1,bestData:o.data})}let n=[];for(let[r,i]of s)n.push({id:r,score:i.score,lanes:i.lanes,data:i.bestData});return n.sort((r,i)=>i.score-r.score),n}R();R();var Ta=!1,Sa=null;var ba=new Set;function ya(){return m().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}function Cn(){return{running:Ta,queueDepth:ya(),lastProcessedAt:Sa,blacklistedCount:ba.size}}import{existsSync as wa,mkdirSync as sp,rmSync as np,createWriteStream as rp,statSync as ip}from"node:fs";import{join as Dn}from"node:path";M();var Ra=[{path:"config.json",sha256:"bc00af31a4a31b74040d73370aa83b62da34c90b75eb77bfa7db039d90abd591"},{path:"tokenizer.json",sha256:"d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"},{path:"tokenizer_config.json",sha256:"9261e7d79b44c8195c1cada2b453e55b00aeb81e907a6664974b4d7776172ab3"},{path:"onnx/model.onnx",sha256:"9bc579acdba21c253c62a9bf866891355a63ffa3442b52c8a37d75b2ccb91848"}];function Na(){return Dn(S,"models","BAAI","bge-base-en-v1.5")}function Te(){let e=Na();return Ra.every(t=>wa(Dn(e,t.path)))}M();import{existsSync as La,readFileSync as Aa,writeFileSync as dp,unlinkSync as up}from"node:fs";import{join as Oa}from"node:path";var Mn=Oa(S,"license.json");function Fn(){if(!La(Mn))return null;try{let e=Aa(Mn,"utf8"),t=JSON.parse(e);return typeof t.license_jwt!="string"||t.license_jwt.length===0?null:t}catch{return null}}import{jwtVerify as xa,importSPKI as Ia}from"jose";var Pn=`-----BEGIN PUBLIC KEY-----
906
+ `).run(e,t,s,n),ba()}function ba(){k();let t=m().prepare("SELECT id, session_id, recalled_at, token_count, mode, caller FROM recall_events ORDER BY recalled_at DESC").all();Ea(Sa,JSON.stringify(t,null,2)+`
907
+ `,"utf-8")}N();var re=class extends Error{name="CorpusTooLargeError";code="CORPUS_TOO_LARGE";rowCount;limit;constructor(t,s){super(`semantic search refused: vec_chunks has ${t} rows (cap ${s}). An unindexed kNN over this corpus can pin the SQLite WAL for hours. Workarounds: (a) scope the search to a smaller project, (b) raise the cap via RECALL_KNN_MAX_CORPUS at your own risk, (c) wait for ANN indexing (tracked in the WAL death-spiral plan).`),this.rowCount=t,this.limit=s}};function It(e,t={}){let s=t.limit??ya()??75e3,r=e.prepare("SELECT COUNT(*) AS n FROM vec_chunks").get()?.n??0;if(r>s)throw new re(r,s)}function ya(){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)}import{Worker as wa}from"node:worker_threads";import{join as Ra}from"node:path";var kt=class extends Error{constructor(s){super(`semantic kNN exceeded ${s}ms wall-clock budget -- query aborted`);this.timeoutMs=s}timeoutMs;name="KnnTimeoutError";code="KNN_TIMEOUT"},Na=1e4;function La(){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 Ct(e){let t=e.timeoutMs??La()??Na,s=(e.workerFactory??Aa)();return new Promise((n,r)=>{let i=setTimeout(()=>{s.terminate().catch(()=>{}),r(new kt(t))},t);s.once("message",o=>{clearTimeout(i);let a=o;a&&a.ok===!0&&Array.isArray(a.hits)?n(a.hits):r(new Error(a?.error??"worker returned malformed reply")),s.terminate().catch(()=>{})}),s.once("error",o=>{clearTimeout(i),s.terminate().catch(()=>{}),r(o)}),s.postMessage({query:e.query,precomputedVector:e.precomputedVector,limit:e.limit})})}function Aa(){let e=Ra(je(),"dist","daemon","query-worker.js");return new wa(e)}async function vt(e,t=50){return It(m()),Ct({query:e,limit:t})}async function Dn(e,t=10,s=.65){let n=m();It(n);let r=n.prepare("SELECT rowid FROM chunk_meta WHERE session_id = ? ORDER BY rowid LIMIT 1").get(e);if(!r)return[];let i=n.prepare("SELECT embedding FROM vec_chunks WHERE rowid = ?").get(r.rowid);if(!i)return[];let o=await Ct({precomputedVector:i.embedding,limit:t*5}),a=new Map;for(let d of o){if(d.sessionId===e)continue;let l=a.get(d.sessionId);(l===void 0||d.distance<l)&&a.set(d.sessionId,d.distance)}let c=[];for(let[d,l]of a){let u=1-l;u>=s&&c.push({sessionId:d,similarity:u})}return c.sort((d,l)=>l.similarity-d.similarity),c.slice(0,t)}function Oa(){let e=process.env.RECALL_RRF_K;if(e){let t=parseInt(e,10);if(!isNaN(t)&&t>=1&&t<=1e3)return t}return 60}function Mn(e){let t=Oa(),s=new Map;for(let r of e)for(let i=0;i<r.length;i++){let o=r[i],a=1/(t+i+1),c=s.get(o.id);c?(c.score+=a,c.lanes.push(o.lane),i+1<c.bestRank&&(c.bestRank=i+1,c.bestData=o.data)):s.set(o.id,{score:a,lanes:[o.lane],bestRank:i+1,bestData:o.data})}let n=[];for(let[r,i]of s)n.push({id:r,score:i.score,lanes:i.lanes,data:i.bestData});return n.sort((r,i)=>i.score-r.score),n}N();N();var xa=!1,Ia=null;var ka=new Set;function Ca(){return m().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}function Fn(){return{running:xa,queueDepth:Ca(),lastProcessedAt:Ia,blacklistedCount:ka.size}}import{existsSync as va,mkdirSync as Sp,rmSync as bp,createWriteStream as yp,statSync as wp}from"node:fs";import{join as Pn}from"node:path";D();var Da=[{path:"config.json",sha256:"bc00af31a4a31b74040d73370aa83b62da34c90b75eb77bfa7db039d90abd591"},{path:"tokenizer.json",sha256:"d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"},{path:"tokenizer_config.json",sha256:"9261e7d79b44c8195c1cada2b453e55b00aeb81e907a6664974b4d7776172ab3"},{path:"onnx/model.onnx",sha256:"9bc579acdba21c253c62a9bf866891355a63ffa3442b52c8a37d75b2ccb91848"}];function Ma(){return Pn(S,"models","BAAI","bge-base-en-v1.5")}function Se(){let e=Ma();return Da.every(t=>va(Pn(e,t.path)))}D();import{existsSync as Fa,readFileSync as Pa,writeFileSync as Op,unlinkSync as xp}from"node:fs";import{join as Ua}from"node:path";var Un=Ua(S,"license.json");function $n(){if(!Fa(Un))return null;try{let e=Pa(Un,"utf8"),t=JSON.parse(e);return typeof t.license_jwt!="string"||t.license_jwt.length===0?null:t}catch{return null}}import{jwtVerify as $a,importSPKI as ja}from"jose";var jn=`-----BEGIN PUBLIC KEY-----
911
908
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZysO2FffTLdyxQnTmnt78/ayvqz9
912
909
  kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
913
910
  -----END PUBLIC KEY-----
914
- `,Ot="ES256",Un="clauderecall.com",$n="clauderecall-cli";var $e=null;async function ka(){return $e||($e=await Ia(Pn,Ot),$e)}async function jn(e){try{let t=await ka(),{payload:s}=await xa(e,t,{issuer:Un,audience:$n,algorithms:[Ot]});return{valid:!0,claims:s}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}import{createHash as va}from"node:crypto";import{hostname as Ca,userInfo as Da,platform as Ma,arch as Fa}from"node:os";function Hn(){let e="unknown";try{e=Da().username}catch{}let t=[Ca(),e,Ma(),Fa()];return va("sha256").update(t.join("\0")).digest("hex")}M();import{existsSync as Pa,readFileSync as Ua,writeFileSync as Np}from"node:fs";import{join as $a}from"node:path";var Bn=$a(S,"license-check.json"),xp=1440*60*1e3,ja=720*60*60*1e3;function Ha(){if(!Pa(Bn))return null;try{let e=JSON.parse(Ua(Bn,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function Wn(e){let t=Ha();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()>ja?{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 Ba=Date.UTC(2026,5,1,7,0,0);var Wa=1440*60*1e3,Cp=60*Wa;async function je(){let e=Fn();if(!e)return{tier:"free"};let t=await jn(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==Hn())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let s=Wn(e.license_key);return s?.revoked?{tier:"free",invalid_reason:s.reason}:Xa(e,t.claims)}function Xa(e,t){let s=t.test_mode===!0&&process.env.NODE_ENV==="production";return{tier:s?"free":"pro",key_short:e.key_short,customer_email:e.customer_email,activated_at:e.activated_at,test_mode:e.test_mode,...s?{test_mode_blocked:!0}:{},expires_at:typeof t.exp=="number"?new Date(t.exp*1e3).toISOString():null}}R();R();M();import{join as xt}from"node:path";var Ga=new Set(["pending","approved","rejected"]),Ya=new Set(["L1","L2","L3","L4","user"]),Jp=xt(S,"links"),za=xt(S,"suggestions"),Kp=xt(za,"index.json");function Xn(e){try{return JSON.parse(e)}catch{return e}}function Ja(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:Xn(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function Ka(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:Xn(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function Va(e){if(!Ya.has(e))throw new Error(`invalid inferred_by: ${e}`)}function It(e){return m().prepare(`SELECT * FROM session_links
911
+ `,Dt="ES256",Hn="clauderecall.com",Bn="clauderecall-cli";var Be=null;async function Ha(){return Be||(Be=await ja(jn,Dt),Be)}async function Wn(e){try{let t=await Ha(),{payload:s}=await $a(e,t,{issuer:Hn,audience:Bn,algorithms:[Dt]});return{valid:!0,claims:s}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}import{createHash as Ba}from"node:crypto";import{hostname as Wa,userInfo as Xa,platform as Ga,arch as Ya}from"node:os";function Xn(){let e="unknown";try{e=Xa().username}catch{}let t=[Wa(),e,Ga(),Ya()];return Ba("sha256").update(t.join("\0")).digest("hex")}D();import{existsSync as za,readFileSync as Ja,writeFileSync as Wp}from"node:fs";import{join as Ka}from"node:path";var Gn=Ka(S,"license-check.json"),zp=1440*60*1e3,Va=720*60*60*1e3;function qa(){if(!za(Gn))return null;try{let e=JSON.parse(Ja(Gn,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function Yn(e){let t=qa();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()>Va?{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 Za=Date.UTC(2026,5,1,7,0,0);var Qa=1440*60*1e3,qp=60*Qa;async function We(){let e=$n();if(!e)return{tier:"free"};let t=await Wn(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==Xn())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let s=Yn(e.license_key);return s?.revoked?{tier:"free",invalid_reason:s.reason}:ec(e,t.claims)}function ec(e,t){let s=t.test_mode===!0&&process.env.NODE_ENV==="production";return{tier:s?"free":"pro",key_short:e.key_short,customer_email:e.customer_email,activated_at:e.activated_at,test_mode:e.test_mode,...s?{test_mode_blocked:!0}:{},expires_at:typeof t.exp=="number"?new Date(t.exp*1e3).toISOString():null}}N();N();D();import{join as Mt}from"node:path";var tc=new Set(["pending","approved","rejected"]),sc=new Set(["L1","L2","L3","L4","user"]),pm=Mt(S,"links"),nc=Mt(S,"suggestions"),mm=Mt(nc,"index.json");function zn(e){try{return JSON.parse(e)}catch{return e}}function rc(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:zn(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function ic(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:zn(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function oc(e){if(!sc.has(e))throw new Error(`invalid inferred_by: ${e}`)}function Ft(e){return m().prepare(`SELECT * FROM session_links
915
912
  WHERE source_session_id = ? OR target_session_id = ?
916
- ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(Ja)}function kt(e={}){let t=m(),s=[],n=[];if(e.status){if(!Ga.has(e.status))throw new Error(`invalid status: ${e.status}`);s.push("status = ?"),n.push(e.status)}e.sourceSessionId&&(s.push("source_session_id = ?"),n.push(e.sourceSessionId)),e.targetSessionId&&(s.push("target_session_id = ?"),n.push(e.targetSessionId)),e.inferredBy&&(Va(e.inferredBy),s.push("inferred_by = ?"),n.push(e.inferredBy));let r=s.length?`WHERE ${s.join(" AND ")}`:"",i=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_link_suggestions ${r}
913
+ ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(rc)}function Pt(e={}){let t=m(),s=[],n=[];if(e.status){if(!tc.has(e.status))throw new Error(`invalid status: ${e.status}`);s.push("status = ?"),n.push(e.status)}e.sourceSessionId&&(s.push("source_session_id = ?"),n.push(e.sourceSessionId)),e.targetSessionId&&(s.push("target_session_id = ?"),n.push(e.targetSessionId)),e.inferredBy&&(oc(e.inferredBy),s.push("inferred_by = ?"),n.push(e.inferredBy));let r=s.length?`WHERE ${s.join(" AND ")}`:"",i=Math.max(1,Math.min(5e3,e.limit??1e3));return t.prepare(`SELECT * FROM session_link_suggestions ${r}
917
914
  ORDER BY confidence DESC, created_at DESC
918
- LIMIT ?`).all(...n,i).map(Ka)}var qa=4e3,Za=2,Qa=30,ec=.2,tc={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};function He(e){return e?Math.ceil(e.length/4):0}function Gn(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/Qa);return Math.max(ec,t)}function Yn(e,t){if(!e||!t)return 0;let s=Date.parse(e),n=Date.parse(t);return!Number.isFinite(s)||!Number.isFinite(n)?0:Math.abs(n-s)/(1e3*60*60*24)}function zn(e){return m().prepare(`SELECT s.id,
915
+ LIMIT ?`).all(...n,i).map(ic)}var ac=4e3,cc=2,lc=30,dc=.2,uc={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};function Xe(e){return e?Math.ceil(e.length/4):0}function Jn(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/lc);return Math.max(dc,t)}function Kn(e,t){if(!e||!t)return 0;let s=Date.parse(e),n=Date.parse(t);return!Number.isFinite(s)||!Number.isFinite(n)?0:Math.abs(n-s)/(1e3*60*60*24)}function Vn(e){return m().prepare(`SELECT s.id,
919
916
  NULLIF(sa.alias, '') AS alias,
920
917
  s.auto_title,
921
918
  s.auto_title_source,
@@ -926,27 +923,27 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
926
923
  FROM sessions s
927
924
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
928
925
  LEFT JOIN projects p ON p.id = s.project_id
929
- WHERE s.id = ?`).get(e)??null}function Jn(e){let s=m().prepare("SELECT summary FROM session_semantic WHERE session_id = ?").get(e);if(!s||!s.summary)return null;let n=s.summary.trim();return n.length>0?n:null}function Kn(e){let t=e.alias?.trim(),s=e.auto_title?.trim(),n=e.first_user_message?.trim();return s&&e.auto_title_source==="agent"?s:t||s||(n?n.slice(0,80):e.id.slice(0,8))}function sc(e){let s=m().prepare(`SELECT id, auto_title, started_at
926
+ WHERE s.id = ?`).get(e)??null}function qn(e){let s=m().prepare("SELECT summary FROM session_semantic WHERE session_id = ?").get(e);if(!s||!s.summary)return null;let n=s.summary.trim();return n.length>0?n:null}function Zn(e){let t=e.alias?.trim(),s=e.auto_title?.trim(),n=e.first_user_message?.trim();return s&&e.auto_title_source==="agent"?s:t||s||(n?n.slice(0,80):e.id.slice(0,8))}function pc(e){let s=m().prepare(`SELECT id, auto_title, started_at
930
927
  FROM sessions
931
928
  WHERE project_id = ?
932
- ORDER BY COALESCE(started_at, ''), id`).all(e),n=new Set,r=new Set,i=[];for(let p of s){if(!p.auto_title||!p.auto_title.startsWith("/")){i.push({id:p.id,brand:null,skill:null});continue}let g=p.auto_title.split(" \xB7 "),_=g[0].trim(),f=g.length>1?g.slice(1).join(" \xB7 ").trim():null;i.push({id:p.id,brand:f||null,skill:_||null}),f&&n.add(f),_&&r.add(_)}let o=[...n].sort(),a=new Map;o.forEach((p,g)=>a.set(p,g));let c=[...r].sort(),d=new Map;c.forEach((p,g)=>d.set(p,g));let l=new Map,u=new Map;for(let p of i){if(!p.brand||!p.skill)continue;let g=a.get(p.brand),_=d.get(p.skill);if(g===void 0||_===void 0)continue;let f=`${g}.${_}`,E=(l.get(f)??0)+1;l.set(f,E),u.set(p.id,`${g}.${_}.${E}`)}return{byId:u}}function nc(e){return{table:e!==null?sc(e):null,originProjectId:e,cache:new Map}}function Be(e,t){let s=e.cache.get(t);if(s)return s;let n=zn(t);if(!n)return null;let r=e.table&&n.project_id===e.originProjectId?e.table.byId.get(t)??null:null,i={session_id:n.id,title:Kn(n),decimal:r,summary:Jn(n.id),project:n.project,started_at:n.started_at};return e.cache.set(t,i),i}function rc(e,t){let n=m().prepare(`SELECT DISTINCT te.parent_session_id AS pid
929
+ ORDER BY COALESCE(started_at, ''), id`).all(e),n=new Set,r=new Set,i=[];for(let p of s){if(!p.auto_title||!p.auto_title.startsWith("/")){i.push({id:p.id,brand:null,skill:null});continue}let g=p.auto_title.split(" \xB7 "),_=g[0].trim(),h=g.length>1?g.slice(1).join(" \xB7 ").trim():null;i.push({id:p.id,brand:h||null,skill:_||null}),h&&n.add(h),_&&r.add(_)}let o=[...n].sort(),a=new Map;o.forEach((p,g)=>a.set(p,g));let c=[...r].sort(),d=new Map;c.forEach((p,g)=>d.set(p,g));let l=new Map,u=new Map;for(let p of i){if(!p.brand||!p.skill)continue;let g=a.get(p.brand),_=d.get(p.skill);if(g===void 0||_===void 0)continue;let h=`${g}.${_}`,E=(l.get(h)??0)+1;l.set(h,E),u.set(p.id,`${g}.${_}.${E}`)}return{byId:u}}function mc(e){return{table:e!==null?pc(e):null,originProjectId:e,cache:new Map}}function Ge(e,t){let s=e.cache.get(t);if(s)return s;let n=Vn(t);if(!n)return null;let r=e.table&&n.project_id===e.originProjectId?e.table.byId.get(t)??null:null,i={session_id:n.id,title:Zn(n),decimal:r,summary:qn(n.id),project:n.project,started_at:n.started_at};return e.cache.set(t,i),i}function gc(e,t){let n=m().prepare(`SELECT DISTINCT te.parent_session_id AS pid
933
930
  FROM thread_edges te
934
931
  WHERE te.session_id = ?
935
- AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let i of n){if(!i.pid)continue;let o=Be(e,i.pid);o&&r.push(o)}return r}function ic(e,t){let n=m().prepare(`SELECT DISTINCT te.session_id AS sid
932
+ AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let i of n){if(!i.pid)continue;let o=Ge(e,i.pid);o&&r.push(o)}return r}function _c(e,t){let n=m().prepare(`SELECT DISTINCT te.session_id AS sid
936
933
  FROM thread_edges te
937
- WHERE te.parent_session_id = ?`).all(t),r=[];for(let i of n){if(!i.sid)continue;let o=Be(e,i.sid);o&&r.push(o)}return r}function Vn(e){let t=tc[e.linkType]??.5,s=ue(e.confidence),n=t*s,r=Gn(e.daysApart),i=e.embeddingCosine??.5,o=ue(e.pagerank);if(e.scoring==="pagerank")return ue(o);if(e.scoring==="embedding-rerank")return e.embeddingCosine===null?ue(n):ue(i);let a=.35*n+.2*r+.2*i+.25*o;return ue(a)}function ue(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}function oc(e,t,s,n,r){let i=new Map;function o(a,c){if(a===c)return;let d=i.get(a);d||(d=new Set,i.set(a,d)),d.add(c)}for(let a of t)o(a.source_session_id,a.target_session_id),o(a.target_session_id,a.source_session_id);for(let a of s)o(e,a.session_id);for(let a of s)o(a.session_id,e);for(let a of n)o(e,a.session_id);for(let a of n)o(a.session_id,e);if(r>1){let a=new Set([e]),c=new Set([e]);for(let d=1;d<r;d++){let l=new Set;for(let u of a){let p=i.get(u);if(p)for(let g of p){if(c.has(g))continue;let _=It(g).filter(f=>f.approved);for(let f of _)o(f.source_session_id,f.target_session_id),o(f.target_session_id,f.source_session_id);c.add(g),l.add(g)}}if(l.size===0)break;for(let u of l)a.add(u)}}return{edges:i}}function ac(e,t={}){let s=t.iterations??12,n=t.damping??.85,r=Array.from(e.edges.keys());if(r.length===0)return new Map;let i=1/r.length,o=new Map(r.map(d=>[d,i]));for(let d=0;d<s;d++){let l=new Map(r.map(u=>[u,(1-n)/r.length]));for(let u of r){let p=e.edges.get(u);if(!p||p.size===0)continue;let g=(o.get(u)??0)/p.size;for(let _ of p)l.set(_,(l.get(_)??0)+n*g)}o=l}let a=0;for(let d of o.values())d>a&&(a=d);if(a<=0)return o;let c=new Map;for(let[d,l]of o)c.set(d,l/a);return c}var qn=240;function Zn(e,t){let s=e.replace(/\s+/g," ").trim();return s.length<=t?s:`${s.slice(0,t-1).trimEnd()}\u2026`}function cc(e){let t=e.decimal?`${e.decimal} `:"",s=e.session_id.slice(0,8),n="evidence"in e&&e.evidence?` \u2014 ${e.evidence}`:"",r=`- ${t}${e.title} (${s})${n}`;if(e.summary){let i=Zn(e.summary,qn);return`${r}
938
- ${i}`}return r}function lc(e,t,s){let n=[],r=[],i=0,o=e.decimal?`${e.decimal}: `:"",a=`# Neighborhood for ${e.session_id} (${o}${e.title})`;if(n.push(a),i+=He(a),e.summary){let c=Zn(e.summary,qn*4);n.push(c),i+=He(c)}n.push("");for(let c of t){if(c.refs.length===0)continue;let d=`## ${c.heading}`,l=He(d),u=[],p=0;for(let g of c.refs){let _=cc(g),f=He(_);if(i+l+p+f>s){r.push({session_id:g.session_id,title:g.title,decimal:g.decimal,summary:g.summary,project:g.project,started_at:g.started_at});continue}u.push(_),p+=f}if(u.length>0){n.push(d);for(let g of u)n.push(g);n.push(""),i+=l+p}}for(;n.length>0&&n[n.length-1]==="";)n.pop();return{bundle:n.join(`
934
+ WHERE te.parent_session_id = ?`).all(t),r=[];for(let i of n){if(!i.sid)continue;let o=Ge(e,i.sid);o&&r.push(o)}return r}function Qn(e){let t=uc[e.linkType]??.5,s=pe(e.confidence),n=t*s,r=Jn(e.daysApart),i=e.embeddingCosine??.5,o=pe(e.pagerank);if(e.scoring==="pagerank")return pe(o);if(e.scoring==="embedding-rerank")return e.embeddingCosine===null?pe(n):pe(i);let a=.35*n+.2*r+.2*i+.25*o;return pe(a)}function pe(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}function fc(e,t,s,n,r){let i=new Map;function o(a,c){if(a===c)return;let d=i.get(a);d||(d=new Set,i.set(a,d)),d.add(c)}for(let a of t)o(a.source_session_id,a.target_session_id),o(a.target_session_id,a.source_session_id);for(let a of s)o(e,a.session_id);for(let a of s)o(a.session_id,e);for(let a of n)o(e,a.session_id);for(let a of n)o(a.session_id,e);if(r>1){let a=new Set([e]),c=new Set([e]);for(let d=1;d<r;d++){let l=new Set;for(let u of a){let p=i.get(u);if(p)for(let g of p){if(c.has(g))continue;let _=Ft(g).filter(h=>h.approved);for(let h of _)o(h.source_session_id,h.target_session_id),o(h.target_session_id,h.source_session_id);c.add(g),l.add(g)}}if(l.size===0)break;for(let u of l)a.add(u)}}return{edges:i}}function hc(e,t={}){let s=t.iterations??12,n=t.damping??.85,r=Array.from(e.edges.keys());if(r.length===0)return new Map;let i=1/r.length,o=new Map(r.map(d=>[d,i]));for(let d=0;d<s;d++){let l=new Map(r.map(u=>[u,(1-n)/r.length]));for(let u of r){let p=e.edges.get(u);if(!p||p.size===0)continue;let g=(o.get(u)??0)/p.size;for(let _ of p)l.set(_,(l.get(_)??0)+n*g)}o=l}let a=0;for(let d of o.values())d>a&&(a=d);if(a<=0)return o;let c=new Map;for(let[d,l]of o)c.set(d,l/a);return c}var er=240;function tr(e,t){let s=e.replace(/\s+/g," ").trim();return s.length<=t?s:`${s.slice(0,t-1).trimEnd()}\u2026`}function Ec(e){let t=e.decimal?`${e.decimal} `:"",s=e.session_id.slice(0,8),n="evidence"in e&&e.evidence?` \u2014 ${e.evidence}`:"",r=`- ${t}${e.title} (${s})${n}`;if(e.summary){let i=tr(e.summary,er);return`${r}
935
+ ${i}`}return r}function Tc(e,t,s){let n=[],r=[],i=0,o=e.decimal?`${e.decimal}: `:"",a=`# Neighborhood for ${e.session_id} (${o}${e.title})`;if(n.push(a),i+=Xe(a),e.summary){let c=tr(e.summary,er*4);n.push(c),i+=Xe(c)}n.push("");for(let c of t){if(c.refs.length===0)continue;let d=`## ${c.heading}`,l=Xe(d),u=[],p=0;for(let g of c.refs){let _=Ec(g),h=Xe(_);if(i+l+p+h>s){r.push({session_id:g.session_id,title:g.title,decimal:g.decimal,summary:g.summary,project:g.project,started_at:g.started_at});continue}u.push(_),p+=h}if(u.length>0){n.push(d);for(let g of u)n.push(g);n.push(""),i+=l+p}}for(;n.length>0&&n[n.length-1]==="";)n.pop();return{bundle:n.join(`
939
936
  `)+`
940
- `,budgetUsed:i,truncated:r}}function dc(e,t,s,n,r,i){let o=[];for(let a of s){if(n&&!n.has(a.link_type))continue;let c=null;if(a.source_session_id===t.session_id?c=a.target_session_id:a.target_session_id===t.session_id&&(c=a.source_session_id),!c)continue;let d=Be(e,c);if(!d)continue;let l=Yn(t.started_at,d.started_at),u=Vn({confidence:a.confidence,linkType:a.link_type,daysApart:l,embeddingCosine:null,pagerank:i.get(c)??0,scoring:r});o.push({...d,score:u,evidence:`(suggestion, ${a.inferred_by}) confidence=${a.confidence.toFixed(2)} ${Math.round(l)}d apart`,link_type:a.link_type})}return o}function Qn(e,t={}){let s=Math.max(100,Math.floor(t.budget??qa)),n=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??Za)),i=t.includeWikiLinks??!0,o=t.includeSuggestions??!1,a=t.edgeTypes?new Set(t.edgeTypes):null,c=zn(e);if(!c)throw new Error(`session not found: ${e}`);let d=nc(c.project_id),l={session_id:c.id,title:Kn(c),decimal:d.table?.byId.get(c.id)??null,summary:Jn(c.id),project:c.project,started_at:c.started_at};d.cache.set(c.id,l);let u=rc(d,e),p=ic(d,e),g=It(e).filter(x=>x.approved).filter(x=>!a||a.has(x.link_type)).filter(x=>i||x.link_type!=="wiki_link"),_=oc(e,g,u,p,r),f=ac(_),E=[],T=[],w=[],N=[];for(let x of g){let re=x.source_session_id===e?x.target_session_id:x.source_session_id,pe=Be(d,re);if(!pe)continue;let ie=Yn(l.started_at,pe.started_at),We=Vn({confidence:x.confidence,linkType:x.link_type,daysApart:ie,embeddingCosine:null,pagerank:f.get(re)??0,scoring:n}),Xe=Gn(ie),B=`${x.link_type} confidence=${x.confidence.toFixed(2)} recency=${Xe.toFixed(2)} (${Math.round(ie)}d apart)`,be={...pe,score:We,evidence:B,link_type:x.link_type};x.link_type==="citation"?E.push(be):x.link_type==="similar"?T.push(be):x.link_type==="wiki_link"?N.push(be):w.push(be)}if(o){let x=kt({sourceSessionId:e,status:"pending",limit:100}),re=kt({targetSessionId:e,status:"pending",limit:100}),pe=[...x,...re],ie=new Set,We=pe.filter(B=>ie.has(B.id)?!1:(ie.add(B.id),!0)),Xe=dc(d,l,We,a,n,f);for(let B of Xe)B.link_type==="citation"?E.push(B):B.link_type==="similar"?T.push(B):B.link_type==="wiki_link"?N.push(B):w.push(B)}let L=(x,re)=>re.score-x.score;E.sort(L),T.sort(L),w.sort(L),N.sort(L);let D=lc(l,[{heading:"Parents",refs:u},{heading:"Children",refs:p},{heading:"Citations (approved)",refs:E},{heading:"Similar sessions",refs:T},{heading:"Cousins (skill track + temporal)",refs:w},{heading:"Wiki links (manual)",refs:N}],s);return{origin:l,parents:u,children:p,citations:E,similar:T,cousins:w,wikiLinks:N,bundle:D.bundle,budgetUsed:D.budgetUsed,budgetRemaining:Math.max(0,s-D.budgetUsed),truncated:D.truncated}}import{readFileSync as tl}from"node:fs";import{dirname as sl,join as nl}from"node:path";var rl=(()=>{try{let e=sl(el(import.meta.url));return nl(e,"..","..","package.json")}catch{return"package.json"}})(),il=(()=>{try{return JSON.parse(tl(rl,"utf8")).version??"0.0.0"}catch{return"0.0.0"}})();function ol(){let e=process.env.RECALL_MCP_ALLOW_WRITES;return e==="1"||e==="true"}function k(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function Bt(e){return{content:[{type:"text",text:e}]}}function G(e){return{content:[{type:"text",text:e}],isError:!0}}function Xr(e){if(e instanceof de)switch(e.kind){case"bundle-missing":return"embedder bundle missing \u2014 dev install requires `npm run build:cli`";case"platform-unsupported":return"embedder not available on this platform \u2014 run `recall semantic install` for setup";case"load-failed":return"embedder load failed \u2014 see daemon logs for details";default:return"embedder not available \u2014 run `recall semantic install`"}return"embedder load failed \u2014 see daemon logs for details"}async function al(){try{if((await je()).tier!=="pro"||!Te())return;await Ue(),process.stderr.write(`[mcp] embedder loaded
941
- `)}catch(e){process.stderr.write(`[mcp] embedder preload failed: ${Xr(e)}
942
- `)}}async function cl(){if((await je()).tier!=="pro")return k({upgrade_required:!0,reason:"Semantic vector search requires Pro.",buy_url:"https://clauderecall.com/pro"});if(!Te())return k({error:"embedder_model_missing",reason:"Run `recall semantic install` to download the embedding model."});if(!Ee().loaded)try{await Ue()}catch(t){return k({error:"embedder_load_failed",reason:Xr(t)})}return null}async function Wr(){try{return(await je()).tier!=="pro"||!Te()?!1:(Ee().loaded||await Ue(),!0)}catch{return!1}}function ll(){let e=new Zc({name:"claude-recall",version:il}),t=ol();if(e.registerTool("list_projects",{title:"List projects",description:"Every Claude Code project currently indexed by Recall, with session and message counts and the most recent activity timestamp.",inputSchema:{}},async()=>{let n=m().prepare(`SELECT p.name,
937
+ `,budgetUsed:i,truncated:r}}function Sc(e,t,s,n,r,i){let o=[];for(let a of s){if(n&&!n.has(a.link_type))continue;let c=null;if(a.source_session_id===t.session_id?c=a.target_session_id:a.target_session_id===t.session_id&&(c=a.source_session_id),!c)continue;let d=Ge(e,c);if(!d)continue;let l=Kn(t.started_at,d.started_at),u=Qn({confidence:a.confidence,linkType:a.link_type,daysApart:l,embeddingCosine:null,pagerank:i.get(c)??0,scoring:r});o.push({...d,score:u,evidence:`(suggestion, ${a.inferred_by}) confidence=${a.confidence.toFixed(2)} ${Math.round(l)}d apart`,link_type:a.link_type})}return o}function sr(e,t={}){let s=Math.max(100,Math.floor(t.budget??ac)),n=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??cc)),i=t.includeWikiLinks??!0,o=t.includeSuggestions??!1,a=t.edgeTypes?new Set(t.edgeTypes):null,c=Vn(e);if(!c)throw new Error(`session not found: ${e}`);let d=mc(c.project_id),l={session_id:c.id,title:Zn(c),decimal:d.table?.byId.get(c.id)??null,summary:qn(c.id),project:c.project,started_at:c.started_at};d.cache.set(c.id,l);let u=gc(d,e),p=_c(d,e),g=Ft(e).filter(O=>O.approved).filter(O=>!a||a.has(O.link_type)).filter(O=>i||O.link_type!=="wiki_link"),_=fc(e,g,u,p,r),h=hc(_),E=[],T=[],w=[],v=[];for(let O of g){let q=O.source_session_id===e?O.target_session_id:O.source_session_id,me=Ge(d,q);if(!me)continue;let ie=Kn(l.started_at,me.started_at),ze=Qn({confidence:O.confidence,linkType:O.link_type,daysApart:ie,embeddingCosine:null,pagerank:h.get(q)??0,scoring:n}),Je=Jn(ie),B=`${O.link_type} confidence=${O.confidence.toFixed(2)} recency=${Je.toFixed(2)} (${Math.round(ie)}d apart)`,ye={...me,score:ze,evidence:B,link_type:O.link_type};O.link_type==="citation"?E.push(ye):O.link_type==="similar"?T.push(ye):O.link_type==="wiki_link"?v.push(ye):w.push(ye)}if(o){let O=Pt({sourceSessionId:e,status:"pending",limit:100}),q=Pt({targetSessionId:e,status:"pending",limit:100}),me=[...O,...q],ie=new Set,ze=me.filter(B=>ie.has(B.id)?!1:(ie.add(B.id),!0)),Je=Sc(d,l,ze,a,n,h);for(let B of Je)B.link_type==="citation"?E.push(B):B.link_type==="similar"?T.push(B):B.link_type==="wiki_link"?v.push(B):w.push(B)}let L=(O,q)=>q.score-O.score;E.sort(L),T.sort(L),w.sort(L),v.sort(L);let R=Tc(l,[{heading:"Parents",refs:u},{heading:"Children",refs:p},{heading:"Citations (approved)",refs:E},{heading:"Similar sessions",refs:T},{heading:"Cousins (skill track + temporal)",refs:w},{heading:"Wiki links (manual)",refs:v}],s);return{origin:l,parents:u,children:p,citations:E,similar:T,cousins:w,wikiLinks:v,bundle:R.bundle,budgetUsed:R.budgetUsed,budgetRemaining:Math.max(0,s-R.budgetUsed),truncated:R.truncated}}import{readFileSync as ml}from"node:fs";import{dirname as gl,join as _l}from"node:path";var fl=(()=>{try{let e=gl(pl(import.meta.url));return _l(e,"..","..","package.json")}catch{return"package.json"}})(),hl=(()=>{try{return JSON.parse(ml(fl,"utf8")).version??"0.0.0"}catch{return"0.0.0"}})();function El(){let e=process.env.RECALL_MCP_ALLOW_WRITES;return e==="1"||e==="true"}function A(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function Vt(e){return{content:[{type:"text",text:e}]}}function G(e){return{content:[{type:"text",text:e}],isError:!0}}function Gr(e){if(e instanceof ue)switch(e.kind){case"bundle-missing":return"embedder bundle missing \u2014 dev install requires `npm run build:cli`";case"platform-unsupported":return"embedder not available on this platform \u2014 run `recall semantic install` for setup";case"load-failed":return"embedder load failed \u2014 see daemon logs for details";default:return"embedder not available \u2014 run `recall semantic install`"}return"embedder load failed \u2014 see daemon logs for details"}async function Tl(){try{if((await We()).tier!=="pro"||!Se())return;await He(),process.stderr.write(`[mcp] embedder loaded
938
+ `)}catch(e){process.stderr.write(`[mcp] embedder preload failed: ${Gr(e)}
939
+ `)}}async function Sl(){if((await We()).tier!=="pro")return A({upgrade_required:!0,reason:"Semantic vector search requires Pro.",buy_url:"https://clauderecall.com/pro"});if(!Se())return A({error:"embedder_model_missing",reason:"Run `recall semantic install` to download the embedding model."});if(!Te().loaded)try{await He()}catch(t){return A({error:"embedder_load_failed",reason:Gr(t)})}return null}async function Xr(){try{return(await We()).tier!=="pro"||!Se()?!1:(Te().loaded||await He(),!0)}catch{return!1}}function bl(){let e=new dl({name:"claude-recall",version:hl}),t=El();if(e.registerTool("list_projects",{title:"List projects",description:"Every Claude Code project currently indexed by Recall, with session and message counts and the most recent activity timestamp.",inputSchema:{}},async()=>{let n=m().prepare(`SELECT p.name,
943
940
  COUNT(s.id) AS session_count,
944
941
  COALESCE(SUM(s.message_count), 0) AS message_count,
945
942
  MAX(s.started_at) AS latest
946
943
  FROM projects p
947
944
  LEFT JOIN sessions s ON s.project_id = p.id
948
945
  GROUP BY p.id
949
- ORDER BY MAX(COALESCE(s.started_at, '')) DESC`).all();return k(n)}),e.registerTool("list_sessions",{title:"List sessions",description:"Recent sessions with alias, tags, and message counts. Optional filters for project, tag, and date range.",inputSchema:{project:y.string().optional().describe("Substring match against project name or decoded filesystem path."),tag:y.string().optional().describe("Only sessions carrying this tag (leading # optional)."),since:y.string().optional().describe("Only sessions started at or after this ISO timestamp or YYYY-MM-DD date."),until:y.string().optional().describe("Only sessions started at or before this ISO timestamp or YYYY-MM-DD date."),limit:y.number().int().min(1).max(500).optional()}},async({project:s,tag:n,since:r,until:i,limit:o})=>{let a=m(),c={limit:o??100},d="s.message_count > 2";if(s&&(d+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",c.proj=`%${s}%`),r&&(d+=" AND s.started_at >= @since",c.since=r),i&&(d+=" AND s.started_at <= @until",c.until=/^\d{4}-\d{2}-\d{2}$/.test(i)?`${i}T23:59:59.999Z`:i),n){let p=te(n);p&&(d+=" AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag)",c.tag=p)}let u=a.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
946
+ ORDER BY MAX(COALESCE(s.started_at, '')) DESC`).all();return A(n)}),e.registerTool("list_sessions",{title:"List sessions",description:"Recent sessions with alias, tags, and message counts. Optional filters for project, tag, and date range.",inputSchema:{project:y.string().optional().describe("Substring match against project name or decoded filesystem path."),tag:y.string().optional().describe("Only sessions carrying this tag (leading # optional)."),since:y.string().optional().describe("Only sessions started at or after this ISO timestamp or YYYY-MM-DD date."),until:y.string().optional().describe("Only sessions started at or before this ISO timestamp or YYYY-MM-DD date."),limit:y.number().int().min(1).max(500).optional()}},async({project:s,tag:n,since:r,until:i,limit:o})=>{let a=m(),c={limit:o??100},d="s.message_count > 2";if(s&&(d+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",c.proj=`%${s}%`),r&&(d+=" AND s.started_at >= @since",c.since=r),i&&(d+=" AND s.started_at <= @until",c.until=/^\d{4}-\d{2}-\d{2}$/.test(i)?`${i}T23:59:59.999Z`:i),n){let p=se(n);p&&(d+=" AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag)",c.tag=p)}let u=a.prepare(`SELECT s.id, p.name AS project, s.started_at, s.ended_at,
950
947
  s.message_count, s.first_user_message, s.git_branch,
951
948
  NULLIF(sa.alias, '') AS alias,
952
949
  CASE WHEN sn.content IS NOT NULL AND sn.content != '' THEN 1 ELSE 0 END AS has_notes,
@@ -961,7 +958,7 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
961
958
  LEFT JOIN session_notes sn ON sn.session_id = s.id
962
959
  WHERE ${d}
963
960
  ORDER BY COALESCE(s.started_at, '') DESC
964
- LIMIT @limit`).all(c).map(({tags_csv:p,...g})=>({...g,tags:p?p.split(","):[]}));return k(u)}),e.registerTool("list_tags",{title:"List tags",description:"Every tag currently applied to a session, with its count, most popular first.",inputSchema:{}},async()=>k(es())),e.registerTool("search",{title:"Search messages",description:"Full-text search over every message in every indexed session. Use `#tag-name` tokens inside the query string to also filter by tag; plain terms are ANDed together. Pass `mode` to control the search lane: 'fts' = keyword BM25 only (always available, fastest), 'semantic' = vector embedding only (Pro + model installed; uses bge-base on-device), 'fused' = RRF-fused BM25 + vector (default; silently falls back to BM25-only when the vector lane is unavailable).",inputSchema:{query:y.string().describe("Text to find. Supports inline `#tag-name` tokens to narrow to sessions with the tag."),project:y.string().optional().describe("Substring match against project name or path."),limit:y.number().int().min(1).max(200).optional(),mode:y.enum(["fts","semantic","fused"]).optional().describe("Search lane: 'fts' (keyword only), 'semantic' (vector only \u2014 requires Pro + model installed), 'fused' (RRF-fused BM25 + vector, default; falls back to BM25 if vector is unavailable).")}},async({query:s,project:n,limit:r,mode:i})=>{let o=m(),a=s.trim();if(!a)return k({query:"",hits:[],tags:[],mode:i??"fused"});let c=i??"fused",d=a.split(/\s+/).filter(Boolean),l=d.filter(T=>T.startsWith("#")).map(te).filter(T=>!!T),p=d.filter(T=>!T.startsWith("#")).map(T=>`"${T.replace(/"/g,"")}"`).join(" "),g=Math.max(1,Math.min(200,r??30));if(!p&&l.length>0){let T=`
961
+ LIMIT @limit`).all(c).map(({tags_csv:p,...g})=>({...g,tags:p?p.split(","):[]}));return A(u)}),e.registerTool("list_tags",{title:"List tags",description:"Every tag currently applied to a session, with its count, most popular first.",inputSchema:{}},async()=>A(cs())),e.registerTool("search",{title:"Search messages",description:"Full-text search over every message in every indexed session. Use `#tag-name` tokens inside the query string to also filter by tag; plain terms are ANDed together. Pass `mode` to control the search lane: 'fts' = keyword BM25 only (always available, fastest), 'semantic' = vector embedding only (Pro + model installed; uses bge-base on-device), 'fused' = RRF-fused BM25 + vector (default; silently falls back to BM25-only when the vector lane is unavailable).",inputSchema:{query:y.string().describe("Text to find. Supports inline `#tag-name` tokens to narrow to sessions with the tag."),project:y.string().optional().describe("Substring match against project name or path."),limit:y.number().int().min(1).max(200).optional(),mode:y.enum(["fts","semantic","fused"]).optional().describe("Search lane: 'fts' (keyword only), 'semantic' (vector only \u2014 requires Pro + model installed), 'fused' (RRF-fused BM25 + vector, default; falls back to BM25 if vector is unavailable).")}},async({query:s,project:n,limit:r,mode:i})=>{let o=m(),a=s.trim();if(!a)return A({query:"",hits:[],tags:[],mode:i??"fused"});let c=i??"fused",d=a.split(/\s+/).filter(Boolean),l=d.filter(T=>T.startsWith("#")).map(se).filter(T=>!!T),p=d.filter(T=>!T.startsWith("#")).map(T=>`"${T.replace(/"/g,"")}"`).join(" "),g=Math.max(1,Math.min(200,r??30));if(!p&&l.length>0){let T=`
965
962
  SELECT s.id AS session_id,
966
963
  s.id AS message_uuid,
967
964
  p.name AS project,
@@ -974,7 +971,7 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
974
971
  JOIN projects p ON p.id = s.project_id
975
972
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
976
973
  WHERE 1=1
977
- `,w={limit:g};return n&&(T+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",w.proj=`%${n}%`),l.forEach((N,L)=>{T+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${L})`,w[`tag_${L}`]=N}),T+=" ORDER BY COALESCE(s.started_at, '') DESC LIMIT @limit",k({query:a,hits:o.prepare(T).all(w),tags:l})}if(!p)return k({query:a,hits:[],tags:l,mode:c});if(c==="semantic"){if(!await Wr())return k({query:a,hits:[],tags:l,mode:"semantic",error:"semantic_unavailable",reason:'Semantic-only search requires Pro tier with the embedder model installed. Run `recall semantic install`, or call this tool with mode="fts" or mode="fused" for a BM25 fallback.'});try{let w=(await At(a,g)).map(N=>({session_id:N.sessionId,snippet:N.text,matched_via:"vector"}));return k({query:a,hits:w,tags:l,mode:"semantic"})}catch(T){return k({query:a,hits:[],tags:l,mode:"semantic",error:"semantic_failed",reason:T instanceof Error?T.message:"vector search failed"})}}let _=`
974
+ `,w={limit:g};return n&&(T+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",w.proj=`%${n}%`),l.forEach((v,L)=>{T+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${L})`,w[`tag_${L}`]=v}),T+=" ORDER BY COALESCE(s.started_at, '') DESC LIMIT @limit",A({query:a,hits:o.prepare(T).all(w),tags:l})}if(!p)return A({query:a,hits:[],tags:l,mode:c});if(c==="semantic"){if(!await Xr())return A({query:a,hits:[],tags:l,mode:"semantic",error:"semantic_unavailable",reason:'Semantic-only search requires Pro tier with the embedder model installed. Run `recall semantic install`, or call this tool with mode="fts" or mode="fused" for a BM25 fallback.'});try{let w=(await vt(a,g)).map(v=>({session_id:v.sessionId,snippet:v.text,matched_via:"vector"}));return A({query:a,hits:w,tags:l,mode:"semantic"})}catch(T){return T instanceof re?A({query:a,hits:[],tags:l,mode:"semantic",error:"corpus_too_large",message:T.message,meta:{rowCount:T.rowCount,limit:T.limit,env:"RECALL_KNN_MAX_CORPUS"}}):A({query:a,hits:[],tags:l,mode:"semantic",error:"semantic_failed",reason:T instanceof Error?T.message:"vector search failed"})}}let _=`
978
975
  SELECT m.session_id AS session_id,
979
976
  m.uuid AS message_uuid,
980
977
  p.name AS project,
@@ -989,7 +986,7 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
989
986
  JOIN projects p ON p.id = s.project_id
990
987
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
991
988
  WHERE messages_fts MATCH @fts
992
- `,f={fts:p,limit:g};n&&(_+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",f.proj=`%${n}%`),l.forEach((T,w)=>{_+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${w})`,f[`tag_${w}`]=T}),_+=" ORDER BY bm25(messages_fts) LIMIT @limit";let E=o.prepare(_).all(f);if(c==="fts")return k({query:a,hits:E,tags:l,mode:"fts"});if(await Wr())try{let T=await At(a,g),w=E.map(D=>({id:String(D.session_id),data:D,lane:"bm25"})),N=T.map(D=>({id:D.sessionId,data:{session_id:D.sessionId,snippet:D.text,matched_via:"vector"},lane:"vector"})),A=vn([w,N]).slice(0,g).map(D=>({...D.data,session_id:D.id,rrf_score:D.score,lanes:D.lanes}));return k({query:a,hits:A,tags:l,fusion:"rrf",mode:"fused"})}catch{}return k({query:a,hits:E,tags:l,mode:"fused"})}),e.registerTool("find_similar_sessions",{title:"Find similar sessions",description:"Find sessions semantically similar to a given session using vector embeddings (Pro-only). Returns related sessions ranked by cosine similarity.",inputSchema:{session_id:y.string().uuid().describe("Session UUID to find similar sessions for."),limit:y.number().int().min(1).max(50).optional().describe("Max results (default 10)."),min_cosine:y.number().min(0).max(1).optional().describe("Minimum cosine similarity threshold (default 0.65).")}},async({session_id:s,limit:n,min_cosine:r})=>{let i=await cl();if(i)return i;try{let o=await kn(s,n??10,r??.65);return k({session_id:s,similar:o})}catch(o){return G(o instanceof Error?o.message:"vector search failed")}}),e.registerTool("semantic_status",{title:"Semantic search status",description:"Health snapshot of the semantic vector search tier: model status, worker status, queue depth.",inputSchema:{}},async()=>{let s=Ee(),n=Cn(),r=Te();return k({embedder:s,worker:n,modelInstalled:r})}),e.registerTool("get_session",{title:"Get session transcript",description:"Return the full metadata and ordered messages for a session. Accepts a full UUID or an 8+-character id prefix.",inputSchema:{id:y.string().describe("Session id (full UUID or 8+-character prefix).")}},async({id:s})=>{let n=X(s);if(!n)return G(`session not found or prefix ambiguous: ${s}`);let r=m(),i=r.prepare(`SELECT s.id, s.project_id, s.started_at, s.ended_at,
989
+ `,h={fts:p,limit:g};n&&(_+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",h.proj=`%${n}%`),l.forEach((T,w)=>{_+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${w})`,h[`tag_${w}`]=T}),_+=" ORDER BY bm25(messages_fts) LIMIT @limit";let E=o.prepare(_).all(h);if(c==="fts")return A({query:a,hits:E,tags:l,mode:"fts"});if(await Xr())try{let T=await vt(a,g),w=E.map(R=>({id:String(R.session_id),data:R,lane:"bm25"})),v=T.map(R=>({id:R.sessionId,data:{session_id:R.sessionId,snippet:R.text,matched_via:"vector"},lane:"vector"})),P=Mn([w,v]).slice(0,g).map(R=>({...R.data,session_id:R.id,rrf_score:R.score,lanes:R.lanes}));return A({query:a,hits:P,tags:l,fusion:"rrf",mode:"fused"})}catch(T){if(T instanceof re)return A({query:a,hits:E,tags:l,mode:"fused",error:"corpus_too_large",message:T.message,meta:{rowCount:T.rowCount,limit:T.limit,env:"RECALL_KNN_MAX_CORPUS"}})}return A({query:a,hits:E,tags:l,mode:"fused"})}),e.registerTool("find_similar_sessions",{title:"Find similar sessions",description:"Find sessions semantically similar to a given session using vector embeddings (Pro-only). Returns related sessions ranked by cosine similarity.",inputSchema:{session_id:y.string().uuid().describe("Session UUID to find similar sessions for."),limit:y.number().int().min(1).max(50).optional().describe("Max results (default 10)."),min_cosine:y.number().min(0).max(1).optional().describe("Minimum cosine similarity threshold (default 0.65).")}},async({session_id:s,limit:n,min_cosine:r})=>{let i=await Sl();if(i)return i;try{let o=await Dn(s,n??10,r??.65);return A({session_id:s,similar:o})}catch(o){return o instanceof re?A({session_id:s,similar:[],error:"corpus_too_large",message:o.message,meta:{rowCount:o.rowCount,limit:o.limit,env:"RECALL_KNN_MAX_CORPUS"}}):G(o instanceof Error?o.message:"vector search failed")}}),e.registerTool("semantic_status",{title:"Semantic search status",description:"Health snapshot of the semantic vector search tier: model status, worker status, queue depth.",inputSchema:{}},async()=>{let s=Te(),n=Fn(),r=Se();return A({embedder:s,worker:n,modelInstalled:r})}),e.registerTool("get_session",{title:"Get session transcript",description:"Return the full metadata and ordered messages for a session. Accepts a full UUID or an 8+-character id prefix.",inputSchema:{id:y.string().describe("Session id (full UUID or 8+-character prefix).")}},async({id:s})=>{let n=X(s);if(!n)return G(`session not found or prefix ambiguous: ${s}`);let r=m(),i=r.prepare(`SELECT s.id, s.project_id, s.started_at, s.ended_at,
993
990
  s.message_count, s.user_message_count, s.assistant_message_count,
994
991
  s.first_user_message, s.git_branch, s.version, s.indexed_at,
995
992
  p.name AS project_name,
@@ -997,13 +994,13 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
997
994
  FROM sessions s
998
995
  JOIN projects p ON p.id = s.project_id
999
996
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
1000
- WHERE s.id = ?`).get(n);if(!i)return G(`session metadata missing for ${n}`);let o=Ne(n),a=r.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
997
+ WHERE s.id = ?`).get(n);if(!i)return G(`session metadata missing for ${n}`);let o=Ae(n),a=r.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
1001
998
  FROM messages WHERE session_id = ?
1002
- ORDER BY COALESCE(timestamp, ''), rowid`).all(n);return k({session:{...i,tags:o},messages:a})}),e.registerTool("context_for_session",{title:"Export session as context",description:"Render a past session as markdown ready to paste into a fresh Claude conversation. This is the flagship Recall operation: pipe any previous session back into a new chat as memory.",inputSchema:{id:y.string().describe("Session id (full UUID or 8+-character prefix)."),mode:y.enum(["condensed","full"]).optional().describe("`condensed` (default) strips tool-call JSON; `full` keeps everything."),includeSidechain:y.boolean().optional().describe("Include subagent / sidechain messages (default false)."),prelude:y.string().max(1e4).optional().describe("Optional header prepended above the transcript (max 10 000 chars)."),since:y.string().optional().describe("Only messages at or after this ISO timestamp.")}},async({id:s,mode:n,includeSidechain:r,prelude:i,since:o})=>{let a=X(s);if(!a)return G(`session not found or prefix ambiguous: ${s}`);let c=m(),d=c.prepare(`SELECT s.id, p.name AS project_name,
999
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(n);return A({session:{...i,tags:o},messages:a})}),e.registerTool("context_for_session",{title:"Export session as context",description:"Render a past session as markdown ready to paste into a fresh Claude conversation. This is the flagship Recall operation: pipe any previous session back into a new chat as memory.",inputSchema:{id:y.string().describe("Session id (full UUID or 8+-character prefix)."),mode:y.enum(["condensed","full"]).optional().describe("`condensed` (default) strips tool-call JSON; `full` keeps everything."),includeSidechain:y.boolean().optional().describe("Include subagent / sidechain messages (default false)."),prelude:y.string().max(1e4).optional().describe("Optional header prepended above the transcript (max 10 000 chars)."),since:y.string().optional().describe("Only messages at or after this ISO timestamp.")}},async({id:s,mode:n,includeSidechain:r,prelude:i,since:o})=>{let a=X(s);if(!a)return G(`session not found or prefix ambiguous: ${s}`);let c=m(),d=c.prepare(`SELECT s.id, p.name AS project_name,
1003
1000
  s.started_at, s.ended_at, s.message_count, s.git_branch
1004
1001
  FROM sessions s JOIN projects p ON p.id = s.project_id
1005
1002
  WHERE s.id = ?`).get(a);if(!d)return G(`session metadata missing for ${a}`);let l=c.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
1006
1003
  FROM messages WHERE session_id = ?
1007
- ORDER BY COALESCE(timestamp, ''), rowid`).all(a),u=Zt(d,l,{mode:n??"condensed",includeSidechain:r??!1,prelude:i??null,since:o??null}),p=o?"since":n??"condensed";return In(a,Math.ceil(u.length/4),p,"mcp"),Bt(u)}),e.registerTool("recall_neighborhood",{title:"Recall: neighborhood context bundle",description:"Build a ranked, budget-bounded markdown bundle for a session: parents + children from thread_edges, plus approved citations / similar / cousins / wiki_links from the cognitive graph. Pipe-friendly output ready to seed a fresh conversation. Reads only approved edges by default \u2014 pending suggestions are opt-in.",inputSchema:{session_id:y.string().describe("Session UUID or 8+-character prefix."),budget:y.number().int().min(100).max(5e4).optional().describe("Token budget for the assembled bundle. Default 4000."),scoring:y.enum(["pagerank","embedding-rerank","hybrid"]).optional().describe("Scoring mode (default hybrid)."),edge_types:y.array(y.enum(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"])).optional().describe("Restrict to certain link types. Default: all approved types."),max_depth:y.number().int().min(1).max(5).optional().describe("Pagerank traversal depth on the local subgraph (default 2)."),include_wiki_links:y.boolean().optional().describe("Include manual wiki_link rows. Default true."),include_suggestions:y.boolean().optional().describe("Surface pending session_link_suggestions. Debug only; default false."),format:y.enum(["markdown","json"]).optional().describe("markdown (default) returns the bundle as text; json returns the full NeighborhoodResult.")}},async({session_id:s,budget:n,scoring:r,edge_types:i,max_depth:o,include_wiki_links:a,include_suggestions:c,format:d})=>{let l=X(s);if(!l)return G(`session not found or prefix ambiguous: ${s}`);try{let u=Qn(l,{budget:n,scoring:r,edgeTypes:i,maxDepth:o,includeWikiLinks:a,includeSuggestions:c});return d==="json"?k(u):Bt(u.bundle)}catch(u){return G(u instanceof Error?u.message:String(u))}}),e.registerTool("doctor",{title:"Health check",description:"Read-only diagnostic snapshot of the local Claude Recall database: total size, WAL size, free pages, FTS5 fragmentation for messages and sessions, vector index row count, free disk space, integrity check, and row counts per surface table. Returns structured JSON. Equivalent to running `recall doctor --json` from the shell. Safe to call as often as needed; no side effects.",inputSchema:{}},async()=>{let{buildHealthReport:s}=await Promise.resolve().then(()=>(jr(),$r));return k(s())}),Sn(e),t){let s=Number(process.env.RECALL_MCP_RATE_LIMIT),n=Number.isFinite(s)&&s>0?s:dt,r=m(),i=new Date(Date.now()-6e4).toISOString(),o=r.prepare("SELECT at FROM mcp_audit_events WHERE at >= ? ORDER BY at ASC").all(i),a=new Z(n);for(let c of o){let d=new Date(c.at).getTime();if(Number.isFinite(d))try{a.consume(d)}catch{break}}e.registerTool("list_sessions_to_tag",{title:"List sessions to tag",description:"Return session summaries and sampled messages formatted for an agent to propose tags. Respects the same scope filters used by the Recall UI scan. Only returns data if auto-tagging is enabled in config.",inputSchema:{untaggedOnly:y.boolean().optional().describe("Only sessions with zero tags (default false)."),project:y.string().optional().describe("Exact project name match."),collectionId:y.string().optional().describe("Only sessions in this collection."),limit:y.number().int().min(1).max(200).optional()}},async c=>{let d=Qe();if(!d.enabled)return G("auto-tagging is disabled; enable it in Recall settings before scanning");let l=Ae({untaggedOnly:c.untaggedOnly,project:c.project,collectionId:c.collectionId,limit:c.limit??50});return k({count:l.length,sessions:l,guidance:`Produce ${d.minTagsPerSession}-${d.maxTagsPerSession} tags per session. Prefer existing tags from list_tags for consistency. Use apply_tags to write results.`})}),e.registerTool("apply_tags",{title:"Apply tags to a session",description:"Add one or more tags to a session (merge-mode, never removes existing). Only works when auto-tagging is enabled. Accepts full UUID or 8+-character id prefix.",inputSchema:{sessionId:y.string().describe("Full session UUID or 8+-character prefix."),tags:y.array(y.string()).min(1).max(10).describe("Tags to add. Normalized server-side (lowercase, hyphens, strip #).")}},async({sessionId:c,tags:d})=>{if(!Qe().enabled)return G("auto-tagging is disabled; enable it in Recall settings before writing tags");let u=X(c);if(!u)return G(`session not found or prefix ambiguous: ${c}`);try{let p=await I({tool:"apply_tags",args:{sessionId:u,tags:d},limiter:a,run:()=>{let g=[],_=[];for(let f of d)try{let{tag:E,added:T}=Re(u,f);T?g.push(E):_.push({tag:E,reason:"already present"})}catch(E){_.push({tag:f,reason:E instanceof Error?E.message:String(E)})}return{sessionId:u,applied:g,skipped:_}}});return k(p)}catch(p){return p instanceof Error&&p.message.startsWith("SQLITE_")?G("database constraint error"):G(p instanceof Error?p.message:String(p))}}),e.registerTool("optimize",{title:"Optimize the database",description:"Run the local maintenance pass: WAL checkpoint (TRUNCATE), FTS5 segment merge for messages and sessions, refresh planner stats. Equivalent to `recall optimize` from the shell. Safe while the daemon is running. Set vacuum=true to also reclaim free pages \u2014 but VACUUM rewrites the entire DB and requires the daemon to be stopped, so it errors out if the daemon is up. Write-mode only.",inputSchema:{vacuum:y.boolean().optional().describe("Also run VACUUM. Requires the daemon to be stopped.")}},async({vacuum:c})=>{let{runOptimize:d}=await Promise.resolve().then(()=>(Br(),Hr)),l=process.stdout.write.bind(process.stdout),u="";process.stdout.write=p=>(u+=typeof p=="string"?p:Buffer.from(p).toString("utf-8"),!0);try{await d({vacuum:!!c,json:!0})}finally{process.stdout.write=l}try{return k(JSON.parse(u.trim()))}catch{return Bt(u.trim())}}),us(e,{limiter:a}),bn(e,{limiter:a}),console.error(`[claude-recall-mcp] MCP writes ENABLED \u2014 write tools registered (rate limit ${n}/min)`),console.error("[claude-recall-mcp] MCP thread writes ENABLED")}else console.error("[claude-recall-mcp] MCP writes DISABLED (read-only) \u2014 pass --allow-writes to enable"),console.error("[claude-recall-mcp] MCP thread writes DISABLED (read-only)");for(let s of ns)e.registerPrompt(s.name,{title:s.title,description:s.description,argsSchema:s.argsSchema},async n=>({messages:[{role:"user",content:{type:"text",text:s.build(n)}}]}));return e}async function dl(){let e=ll();await al();let t=new Qc;await e.connect(t);let s=!1,n=async r=>{if(!s){s=!0,process.stderr.write(`[claude-recall-mcp] shutting down: ${r}
1004
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(a),u=os(d,l,{mode:n??"condensed",includeSidechain:r??!1,prelude:i??null,since:o??null}),p=o?"since":n??"condensed";return vn(a,Math.ceil(u.length/4),p,"mcp"),Vt(u)}),e.registerTool("recall_neighborhood",{title:"Recall: neighborhood context bundle",description:"Build a ranked, budget-bounded markdown bundle for a session: parents + children from thread_edges, plus approved citations / similar / cousins / wiki_links from the cognitive graph. Pipe-friendly output ready to seed a fresh conversation. Reads only approved edges by default \u2014 pending suggestions are opt-in.",inputSchema:{session_id:y.string().describe("Session UUID or 8+-character prefix."),budget:y.number().int().min(100).max(5e4).optional().describe("Token budget for the assembled bundle. Default 4000."),scoring:y.enum(["pagerank","embedding-rerank","hybrid"]).optional().describe("Scoring mode (default hybrid)."),edge_types:y.array(y.enum(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"])).optional().describe("Restrict to certain link types. Default: all approved types."),max_depth:y.number().int().min(1).max(5).optional().describe("Pagerank traversal depth on the local subgraph (default 2)."),include_wiki_links:y.boolean().optional().describe("Include manual wiki_link rows. Default true."),include_suggestions:y.boolean().optional().describe("Surface pending session_link_suggestions. Debug only; default false."),format:y.enum(["markdown","json"]).optional().describe("markdown (default) returns the bundle as text; json returns the full NeighborhoodResult.")}},async({session_id:s,budget:n,scoring:r,edge_types:i,max_depth:o,include_wiki_links:a,include_suggestions:c,format:d})=>{let l=X(s);if(!l)return G(`session not found or prefix ambiguous: ${s}`);try{let u=sr(l,{budget:n,scoring:r,edgeTypes:i,maxDepth:o,includeWikiLinks:a,includeSuggestions:c});return d==="json"?A(u):Vt(u.bundle)}catch(u){return G(u instanceof Error?u.message:String(u))}}),e.registerTool("doctor",{title:"Health check",description:"Read-only diagnostic snapshot of the local Claude Recall database: total size, WAL size, free pages, FTS5 fragmentation for messages and sessions, vector index row count, free disk space, integrity check, and row counts per surface table. Returns structured JSON. Equivalent to running `recall doctor --json` from the shell. Safe to call as often as needed; no side effects.",inputSchema:{}},async()=>{let{buildHealthReport:s}=await Promise.resolve().then(()=>(Hr(),jr));return A(s())}),On(e),t){let s=Number(process.env.RECALL_MCP_RATE_LIMIT),n=Number.isFinite(s)&&s>0?s:mt,r=m(),i=new Date(Date.now()-6e4).toISOString(),o=r.prepare("SELECT at FROM mcp_audit_events WHERE at >= ? ORDER BY at ASC").all(i),a=new Q(n);for(let c of o){let d=new Date(c.at).getTime();if(Number.isFinite(d))try{a.consume(d)}catch{break}}e.registerTool("list_sessions_to_tag",{title:"List sessions to tag",description:"Return session summaries and sampled messages formatted for an agent to propose tags. Respects the same scope filters used by the Recall UI scan. Only returns data if auto-tagging is enabled in config.",inputSchema:{untaggedOnly:y.boolean().optional().describe("Only sessions with zero tags (default false)."),project:y.string().optional().describe("Exact project name match."),collectionId:y.string().optional().describe("Only sessions in this collection."),limit:y.number().int().min(1).max(200).optional()}},async c=>{let d=st();if(!d.enabled)return G("auto-tagging is disabled; enable it in Recall settings before scanning");let l=xe({untaggedOnly:c.untaggedOnly,project:c.project,collectionId:c.collectionId,limit:c.limit??50});return A({count:l.length,sessions:l,guidance:`Produce ${d.minTagsPerSession}-${d.maxTagsPerSession} tags per session. Prefer existing tags from list_tags for consistency. Use apply_tags to write results.`})}),e.registerTool("apply_tags",{title:"Apply tags to a session",description:"Add one or more tags to a session (merge-mode, never removes existing). Only works when auto-tagging is enabled. Accepts full UUID or 8+-character id prefix.",inputSchema:{sessionId:y.string().describe("Full session UUID or 8+-character prefix."),tags:y.array(y.string()).min(1).max(10).describe("Tags to add. Normalized server-side (lowercase, hyphens, strip #).")}},async({sessionId:c,tags:d})=>{if(!st().enabled)return G("auto-tagging is disabled; enable it in Recall settings before writing tags");let u=X(c);if(!u)return G(`session not found or prefix ambiguous: ${c}`);try{let p=await I({tool:"apply_tags",args:{sessionId:u,tags:d},limiter:a,run:()=>{let g=[],_=[];for(let h of d)try{let{tag:E,added:T}=Le(u,h);T?g.push(E):_.push({tag:E,reason:"already present"})}catch(E){_.push({tag:h,reason:E instanceof Error?E.message:String(E)})}return{sessionId:u,applied:g,skipped:_}}});return A(p)}catch(p){return p instanceof Error&&p.message.startsWith("SQLITE_")?G("database constraint error"):G(p instanceof Error?p.message:String(p))}}),e.registerTool("optimize",{title:"Optimize the database",description:"Run the local maintenance pass: WAL checkpoint (TRUNCATE), FTS5 segment merge for messages and sessions, refresh planner stats. Equivalent to `recall optimize` from the shell. Safe while the daemon is running. Set vacuum=true to also reclaim free pages \u2014 but VACUUM rewrites the entire DB and requires the daemon to be stopped, so it errors out if the daemon is up. Write-mode only.",inputSchema:{vacuum:y.boolean().optional().describe("Also run VACUUM. Requires the daemon to be stopped.")}},async({vacuum:c})=>{let{runOptimize:d}=await Promise.resolve().then(()=>(Wr(),Br)),l=process.stdout.write.bind(process.stdout),u="";process.stdout.write=p=>(u+=typeof p=="string"?p:Buffer.from(p).toString("utf-8"),!0);try{await d({vacuum:!!c,json:!0})}finally{process.stdout.write=l}try{return A(JSON.parse(u.trim()))}catch{return Vt(u.trim())}}),Ts(e,{limiter:a}),xn(e,{limiter:a}),console.error(`[claude-recall-mcp] MCP writes ENABLED \u2014 write tools registered (rate limit ${n}/min)`),console.error("[claude-recall-mcp] MCP thread writes ENABLED")}else console.error("[claude-recall-mcp] MCP writes DISABLED (read-only) \u2014 pass --allow-writes to enable"),console.error("[claude-recall-mcp] MCP thread writes DISABLED (read-only)");for(let s of us)e.registerPrompt(s.name,{title:s.title,description:s.description,argsSchema:s.argsSchema},async n=>({messages:[{role:"user",content:{type:"text",text:s.build(n)}}]}));return e}async function yl(){let e=bl();await Tl();let t=new ul;await e.connect(t);let s=!1,n=async r=>{if(!s){s=!0,process.stderr.write(`[claude-recall-mcp] shutting down: ${r}
1008
1005
  `);try{await e.close()}catch(i){let o=i instanceof Error?i.message:String(i);process.stderr.write(`[claude-recall-mcp] server.close() failed: ${o}
1009
- `)}Vt(),process.exit(0)}};process.on("SIGINT",()=>{n("SIGINT")}),process.on("SIGTERM",()=>{n("SIGTERM")}),qt({onShutdown:()=>n("parent death")})}var ul=(()=>{try{let e=process.argv[1];return e?import.meta.url===new URL(`file://${e}`).href:!1}catch{return!1}})();ul&&dl().catch(e=>{console.error("[claude-recall-mcp] fatal:",e),process.exit(1)});export{ll as buildServer};
1006
+ `)}rs(),process.exit(0)}};process.on("SIGINT",()=>{n("SIGINT")}),process.on("SIGTERM",()=>{n("SIGTERM")}),is({onShutdown:()=>n("parent death")})}var wl=(()=>{try{let e=process.argv[1];return e?import.meta.url===new URL(`file://${e}`).href:!1}catch{return!1}})();wl&&yl().catch(e=>{console.error("[claude-recall-mcp] fatal:",e),process.exit(1)});export{bl as buildServer};