@clauderecallhq/cli 0.75.0 → 0.75.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 kr=Object.defineProperty;var y=(e,t)=>()=>(e&&(t=e(e=0)),t);var Xe=(e,t)=>{for(var s in t)kr(e,s,{get:t[s],enumerable:!0})};import{createRequire as vr}from"node:module";var Dr,Mr,Fr,We,Ge,$t,jt=y(()=>{"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)}}Dr=vr(import.meta.url),Mr=["node","sqlite"].join(":"),Fr=Dr(Mr),We=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)}},Ge=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,s={}){this.inner=new Fr.DatabaseSync(t,{readOnly:s.readonly??!1,allowExtension:!0})}prepare(t){return new We(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)}},$t=Ge});import{homedir as Ht}from"node:os";import{join as Ye,basename as Zc}from"node:path";import{existsSync as Pr,mkdirSync as Ur,chmodSync as $r,readdirSync as el,statSync as tl}from"node:fs";function O(){Pr(T)||Ur(T,{recursive:!0,mode:448}),process.platform!=="win32"&&$r(T,448)}var ze,T,pe,k=y(()=>{"use strict";ze=process.env.CLAUDE_PROJECTS_DIR?process.env.CLAUDE_PROJECTS_DIR:Ye(Ht(),".claude","projects"),T=process.env.RECALL_HOME?process.env.RECALL_HOME:Ye(Ht(),".recall"),pe=Ye(T,"db.sqlite")});function Xt(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)),l=[["auto_synopsis","TEXT"],["auto_synopsis_generated_at","INTEGER"],["auto_synopsis_history","TEXT"]];for(let[g,_]of l)c.has(g)||e.exec(`ALTER TABLE session_notes ADD COLUMN ${g} ${_}`);let d=e.prepare("PRAGMA table_info(threads)").all();new Set(d.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 Mr=Object.defineProperty;var w=(e,t)=>()=>(e&&(t=e(e=0)),t);var Xe=(e,t)=>{for(var s in t)Mr(e,s,{get:t[s],enumerable:!0})};import{createRequire as Fr}from"node:module";var Pr,Ur,$r,We,Ge,jt,Ht=w(()=>{"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)}}Pr=Fr(import.meta.url),Ur=["node","sqlite"].join(":"),$r=Pr(Ur),We=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)}},Ge=class{inner;extensionLoadingEnabled=!1;txDepth=0;constructor(t,s={}){this.inner=new $r.DatabaseSync(t,{readOnly:s.readonly??!1,allowExtension:!0})}prepare(t){return new We(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)}},jt=Ge});import{homedir as Bt}from"node:os";import{join as Ye,basename as tl}from"node:path";import{existsSync as jr,mkdirSync as Hr,chmodSync as Br,readdirSync as nl,statSync as rl}from"node:fs";function O(){jr(S)||Hr(S,{recursive:!0,mode:448}),process.platform!=="win32"&&Br(S,448)}var ze,S,me,v=w(()=>{"use strict";ze=process.env.CLAUDE_PROJECTS_DIR?process.env.CLAUDE_PROJECTS_DIR:Ye(Bt(),".claude","projects"),S=process.env.RECALL_HOME?process.env.RECALL_HOME:Ye(Bt(),".recall"),me=Ye(S,"db.sqlite")});function Wt(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(`
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 kr=Object.defineProperty;var y=(e,t)=>()=>(e&&(t=e(e=0)),t);var Xe=(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 Bt,Wt=y(()=>{"use strict";Bt=`
15
+ `)}var Xt,Gt=w(()=>{"use strict";Xt=`
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 Gt from"sqlite-vec";function m(){if(I)return I;O(),I=new $t(pe),Gt.load(I),I.pragma("cache_size = -64000"),I.pragma("mmap_size = 268435456"),I.pragma("temp_store = MEMORY"),I.pragma("busy_timeout = 5000"),I.pragma("journal_size_limit = 67108864"),I.pragma("wal_autocheckpoint = 1000"),I.exec(Bt),Xt(I);try{I.exec("PRAGMA optimize")}catch{}return I}function Yt(){if(I){try{I.exec("PRAGMA optimize")}catch{}try{I.exec("INSERT INTO messages_fts(messages_fts, rank) VALUES('merge', 4);")}catch{}try{I.exec("INSERT INTO sessions_fts(sessions_fts, rank) VALUES('merge', 4);")}catch{}try{I.pragma("wal_checkpoint(TRUNCATE)")}catch{}I.close(),I=null}}var I,R=y(()=>{"use strict";jt();k();Wt();I=null});import{writeFileSync as Yr}from"node:fs";import{join as zr}from"node:path";function Q(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function ye(e,t){let s=Q(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)})(),qt(),{tag:s,added:!0})}function Jt(e,t){let s=Q(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)})(),qt(),{tag:s,removed:!0}):{tag:s,removed:!1}}function we(e){return m().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function Kt(){return m().prepare(`SELECT tag, COUNT(*) AS count FROM session_tags
672
- GROUP BY tag ORDER BY count DESC, tag ASC`).all()}function qt(){try{O();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};Yr(Jr,JSON.stringify(n,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var Jr,Re=y(()=>{"use strict";R();k();Jr=zr(T,"tags.json")});function Kr(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 Ne(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 Yt from"sqlite-vec";function m(){if(I)return I;O(),I=new jt(me),Yt.load(I),I.pragma("cache_size = -64000"),I.pragma("mmap_size = 268435456"),I.pragma("temp_store = MEMORY"),I.pragma("busy_timeout = 5000"),I.pragma("journal_size_limit = 67108864"),I.pragma("wal_autocheckpoint = 1000"),I.exec(Xt),Wt(I);try{I.exec("PRAGMA optimize")}catch{}return I}function zt(){if(I){try{I.exec("PRAGMA optimize")}catch{}try{I.exec("INSERT INTO messages_fts(messages_fts, rank) VALUES('merge', 4);")}catch{}try{I.exec("INSERT INTO sessions_fts(sessions_fts, rank) VALUES('merge', 4);")}catch{}try{I.pragma("wal_checkpoint(TRUNCATE)")}catch{}I.close(),I=null}}var I,R=w(()=>{"use strict";Ht();v();Gt();I=null});import{writeFileSync as Kr}from"node:fs";import{join as qr}from"node:path";function ee(e){return e.trim().toLowerCase().replace(/^#+/,"").replace(/[\s/\\]+/g,"-").replace(/[^a-z0-9\-._]/g,"").slice(0,40)}function ye(e,t){let s=ee(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)})(),Vt(),{tag:s,added:!0})}function Kt(e,t){let s=ee(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)})(),Vt(),{tag:s,removed:!0}):{tag:s,removed:!1}}function we(e){return m().prepare("SELECT tag FROM session_tags WHERE session_id = ? ORDER BY tag").all(e).map(t=>t.tag)}function qt(){return m().prepare(`SELECT tag, COUNT(*) AS count FROM session_tags
672
+ GROUP BY tag ORDER BY count DESC, tag ASC`).all()}function Vt(){try{O();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};Kr(Vr,JSON.stringify(n,null,2))}catch(e){console.error("[tags] backup failed:",e)}}var Vr,Re=w(()=>{"use strict";R();v();Vr=qr(S,"tags.json")});function Zr(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 Ne(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),l=Kr(a,5).map(d=>`${d.role}: ${d.content_text.slice(0,400)}`).join(`
682
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(o.id),d=Zr(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:l,current_tags:we(o.id)}})}var Je=y(()=>{"use strict";R();Re()});import{z as H}from"zod";function Ve(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 ii(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 ai(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 li(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 ni,ri,oi,ci,di,ui,pi,mi,Zt,Ze=y(()=>{"use strict";ni={project:H.string().optional().describe("Exact project name match (optional)."),collectionId:H.string().optional().describe("Restrict to sessions in this collection (optional)."),sessionId:H.string().optional().describe("Full session UUID to tag just one session (optional)."),untaggedOnly:H.boolean().optional().describe("Skip sessions that already have any tag (default: true)."),limit:H.number().int().min(1).max(500).optional().describe("Max sessions to process (default: 100)."),minTags:H.number().int().min(1).max(10).optional().describe("Minimum tags per session (default: 2)."),maxTags:H.number().int().min(1).max(10).optional().describe("Maximum tags per session (default: 4).")};ri={sessionId:H.string().describe("Session UUID (or 8+-char prefix) to summarize."),mode:H.enum(["brief","detailed"]).optional().describe("brief = 3-5 bullets; detailed = paragraph + bullets. Default: brief.")};oi={sessionId:H.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")};ci={sessionId:H.string().describe("Session UUID (or 8+-char prefix) to find similar sessions to."),limit:H.number().int().min(1).max(20).optional().describe("How many similar sessions to surface (default: 5).")};di={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:ni,build:Ve,allowedTools:["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"]},ui={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:ri,build:ii,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},pi={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:oi,build:ai,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},mi={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:ci,build:li,allowedTools:["mcp__recall__get_session","mcp__recall__search","mcp__recall__list_sessions","mcp__recall__list_tags"]},Zt=[di,ui,pi,mi]});import{writeFileSync as gi}from"node:fs";import{join as _i}from"node:path";function Qt(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function hi(){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:Qt(t.previous_aliases)}))}function Le(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=Qt(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:we(o.id)}})}var Je=w(()=>{"use strict";R();Re()});import{z as B}from"zod";function Ve(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 ci(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 di(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 pi(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 oi,ai,li,ui,mi,gi,_i,fi,Qt,Ze=w(()=>{"use strict";oi={project:B.string().optional().describe("Exact project name match (optional)."),collectionId:B.string().optional().describe("Restrict to sessions in this collection (optional)."),sessionId:B.string().optional().describe("Full session UUID to tag just one session (optional)."),untaggedOnly:B.boolean().optional().describe("Skip sessions that already have any tag (default: true)."),limit:B.number().int().min(1).max(500).optional().describe("Max sessions to process (default: 100)."),minTags:B.number().int().min(1).max(10).optional().describe("Minimum tags per session (default: 2)."),maxTags:B.number().int().min(1).max(10).optional().describe("Maximum tags per session (default: 4).")};ai={sessionId:B.string().describe("Session UUID (or 8+-char prefix) to summarize."),mode:B.enum(["brief","detailed"]).optional().describe("brief = 3-5 bullets; detailed = paragraph + bullets. Default: brief.")};li={sessionId:B.string().describe("Session UUID (or 8+-char prefix) to extract decisions from.")};ui={sessionId:B.string().describe("Session UUID (or 8+-char prefix) to find similar sessions to."),limit:B.number().int().min(1).max(20).optional().describe("How many similar sessions to surface (default: 5).")};mi={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:oi,build:Ve,allowedTools:["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"]},gi={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:ai,build:ci,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},_i={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:li,build:di,allowedTools:["mcp__recall__get_session","mcp__recall__context_for_session"]},fi={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:ui,build:pi,allowedTools:["mcp__recall__get_session","mcp__recall__search","mcp__recall__list_sessions","mcp__recall__list_tags"]},Qt=[mi,gi,_i,fi]});import{writeFileSync as hi}from"node:fs";import{join as Ei}from"node:path";function es(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Si(){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:es(t.previous_aliases)}))}function Le(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=es(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)),Ei(),{session_id:e,alias:s,updated_at:r,previous_aliases:o}}function Ei(){try{O();let e=hi(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};gi(fi,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}var fi,Ae=y(()=>{"use strict";R();k();fi=_i(T,"aliases.json")});import{randomUUID as Ai}from"node:crypto";import{writeFileSync as Oi,readFileSync as $l,existsSync as jl}from"node:fs";import{join as xi}from"node:path";function Ci(e){return{...e}}function tt(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 ki(e){let t=m().prepare("SELECT * FROM collections WHERE id = ?").get(e);if(!t)throw new Error(`collection not found: ${e}`);return t}function vi(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 rs(e){let t=m().prepare("SELECT * FROM collections WHERE id = ?").get(e);return t?Ci(t):null}function st(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=Ai();if(e.parent_id){if(!rs(e.parent_id))throw new Error("parent collection not found");if(vi(e.parent_id)>=ns-1)throw new Error(`max collection depth is ${ns}`)}return s.transaction(()=>{s.prepare(`INSERT INTO collections
693
+ previous_aliases = excluded.previous_aliases`).run(e,s,r,JSON.stringify(o)),bi(),{session_id:e,alias:s,updated_at:r,previous_aliases:o}}function bi(){try{O();let e=Si(),t={schema:"claude-recall.aliases.v1",backed_up_at:new Date().toISOString(),aliases:e};hi(Ti,JSON.stringify(t,null,2))}catch(e){console.error("[aliases] backup failed:",e)}}var Ti,Ae=w(()=>{"use strict";R();v();Ti=Ei(S,"aliases.json")});import{randomUUID as Ii}from"node:crypto";import{writeFileSync as Ci,readFileSync as Bl,existsSync as Xl}from"node:fs";import{join as ki}from"node:path";function Di(e){return{...e}}function tt(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 Mi(e){let t=m().prepare("SELECT * FROM collections WHERE id = ?").get(e);if(!t)throw new Error(`collection not found: ${e}`);return t}function Fi(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 is(e){let t=m().prepare("SELECT * FROM collections WHERE id = ?").get(e);return t?Di(t):null}function st(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=Ii();if(e.parent_id){if(!is(e.parent_id))throw new Error("parent collection not found");if(Fi(e.parent_id)>=rs-1)throw new Error(`max collection depth is ${rs}`)}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),tt(r,"create",{name:t,parent_id:e.parent_id??null,icon:e.icon??null,color:e.color??null},null,n)})(),rt(),rs(r)}function nt(e,t,s=null,n={}){let r=m();if(ki(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 l=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,l,s,a,c),tt(e,"add",{note:s,source:a,rule_id:c},t,l)})(),rt(),{added:!0}}function is(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),tt(e,"remove",null,t,r)})(),rt(),{removed:!0}}function Di(){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),tt(r,"create",{name:t,parent_id:e.parent_id??null,icon:e.icon??null,color:e.color??null},null,n)})(),rt(),is(r)}function nt(e,t,s=null,n={}){let r=m();if(Mi(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),tt(e,"add",{note:s,source:a,rule_id:c},t,d)})(),rt(),{added:!0}}function os(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),tt(e,"remove",null,t,r)})(),rt(),{removed:!0}}function Pi(){return m().prepare(`SELECT id, collection_id, session_id, action, payload, at
698
698
  FROM collection_events
699
699
  ORDER BY at ASC, id ASC`).all()}function rt(){try{O();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=Di(),r={schema:"claude-recall.collections.v1",backed_up_at:new Date().toISOString(),collections:t,memberships:s,events:n};Oi(Ii,JSON.stringify(r,null,2))}catch(e){console.error("[collections] backup failed:",e)}}var Ii,ns,it=y(()=>{"use strict";R();k();Ii=xi(T,"collections.json"),ns=8});function ws(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>ys?s.slice(0,ys)+"\u2026":s}function Bi(e){return m().prepare(`SELECT s.id AS id,
704
+ ORDER BY collection_id, added_at`).all(),n=Pi(),r={schema:"claude-recall.collections.v1",backed_up_at:new Date().toISOString(),collections:t,memberships:s,events:n};Ci(vi,JSON.stringify(r,null,2))}catch(e){console.error("[collections] backup failed:",e)}}var vi,rs,it=w(()=>{"use strict";R();v();vi=ki(S,"collections.json"),rs=8});function Rs(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>ws?s.slice(0,ws)+"\u2026":s}function Gi(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 Xi(e){let t=Bi(e);return t?ws(t):e.slice(0,8)}function Rs(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 Yi(e){let t=Gi(e);return t?Rs(t):e.slice(0,8)}function Ns(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:Xi(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:Yi(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,39 +727,39 @@ 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?ws(c):c.session_id.slice(0,8)}));return{thread_id:s.thread_id,thread_name:s.thread_name,parent_session:n,siblings:a}}var ys,Ns=y(()=>{"use strict";R();ys=80});var pt=y(()=>{"use strict"});var Ls=y(()=>{"use strict"});import{writeFileSync as Wi,mkdirSync as Gi,existsSync as Yi}from"node:fs";import{join as As}from"node:path";function Os(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 xs(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?Rs(c):c.session_id.slice(0,8)}));return{thread_id:s.thread_id,thread_name:s.thread_name,parent_session:n,siblings:a}}var ws,Ls=w(()=>{"use strict";R();ws=80});var pt=w(()=>{"use strict"});var As=w(()=>{"use strict"});import{writeFileSync as zi,mkdirSync as Ji,existsSync as Ki}from"node:fs";import{join as Os}from"node:path";function xs(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 Is(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,ge),n=t.prepare(`SELECT rowid AS rid, content_text
735
+ LIMIT ?`).all(e,_e),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,ke),r=new Map;for(let d of s)r.set(d.rid,d.content_text);for(let d of n)r.set(d.rid,d.content_text);if(r.size===0)throw new Error("no user messages available to summarise");let i=Array.from(r.entries()).sort((d,u)=>d[0]-u[0]).map(([,d])=>({content_text:d})),o=s.length===ge&&n.length===ke&&r.size===ge+ke,a=i.map((d,u)=>{let p=(d.content_text??"").slice(0,zi);return o&&u===ge?`--- (middle of session omitted) ---
740
+ LIMIT ?`).all(e,ke),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===ke&&r.size===_e+ke,a=i.map((l,u)=>{let p=(l.content_text??"").slice(0,qi);return o&&u===_e?`--- (middle of session omitted) ---
741
741
  ${u+1}. ${p}`:`${u+1}. ${p}`}).join(`
742
- `),c=null;try{c=Rs(e)}catch(d){console.error("[autoTitle] thread context resolution failed:",d),c=null}let l=[];return c&&(l.push(Ji(c)),l.push("")),l.push(`You will receive a sample of user messages from a Claude Code session: the first ${ge}`,`messages (initial intent) and the last ${ke} 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),l.join(`
743
- `)}function Ji(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,mt).map(o=>`"${o.title}"`).join(", "),i=s>mt?`, and ${s-mt} 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(`
742
+ `),c=null;try{c=Ns(e)}catch(l){console.error("[autoTitle] thread context resolution failed:",l),c=null}let d=[];return c&&(d.push(Vi(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 ${ke} 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 Vi(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,mt).map(o=>`"${o.title}"`).join(", "),i=s>mt?`, and ${s-mt} 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
744
  `)}function _t(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=Os(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
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=xs(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),qi(e,n,s,a)}function Is(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:Os(t.auto_title_history)}:null}function Ki(){O(),Yi(gt)||Gi(gt,{recursive:!0})}function qi(e,t,s,n){try{Ki();let r=As(gt,`${e}.txt`),i=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${s} \xB7 updated ${n}
752
- `;Wi(r,i+t+`
753
- `)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}var gt,ge,ke,zi,mt,ft=y(()=>{"use strict";R();k();Ns();pt();Ls();gt=As(T,"titles"),ge=5,ke=15,zi=500;mt=5});function Cs(e,t){let s=Vi.get(e);if(!(!s||s.size===0))for(let n of s)try{n(t)}catch{}}var Vi,ks=y(()=>{"use strict";Vi=new Map});import{existsSync as Zi,statSync as Qi}from"node:fs";import{delimiter as eo,join as to}from"node:path";function vs(e){if(e.includes("/")||e.includes("\\")||e.includes(".."))return null;let t=(process.env.PATH??"").split(eo).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=to(n,r);try{if(Zi(i)&&Qi(i).isFile())return i}catch{}}return null}var Ds=y(()=>{"use strict"});var js={};Xe(js,{_resetClaudePathCacheForTests:()=>io,buildScanPrompt:()=>Ps,isClaudeCliAvailable:()=>Fs,runClaudeCliScan:()=>uo,spawnClaudePrompt:()=>Us});import{spawn as so}from"node:child_process";function Ms(){if(oe!==void 0&&_e!==void 0)return{path:oe,available:_e};let e=vs("claude");return oe=e??"claude",_e=e!==null,{path:oe,available:_e}}function ro(){return Ms().path}function Fs(){return Ms().available}function io(){oe=void 0,_e=void 0}function Ps(e){return Ve({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 oo(e,t){let s=t.get(e);return s||e.slice(0,8)}function ao(e){try{return Ne(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 co(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 l of c.message.content){if(l?.type!=="tool_use"||l.name!=="mcp__recall__apply_tags")continue;let d=l.input,u=typeof d?.sessionId=="string"?d.sessionId:null;!u||r.has(u)||(r.add(u),Cs(t,{type:"progress",current:r.size,total:s,sessionId:u,sessionLabel:oo(u,n)}))}}}function lo(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),Qi(e,n,s,a)}function Cs(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:xs(t.auto_title_history)}:null}function Zi(){O(),Ki(gt)||Ji(gt,{recursive:!0})}function Qi(e,t,s,n){try{Zi();let r=Os(gt,`${e}.txt`),i=`# Claude Recall auto-title \xB7 session ${e} \xB7 source ${s} \xB7 updated ${n}
752
+ `;zi(r,i+t+`
753
+ `)}catch(r){console.error("[autoTitle] mirror write failed:",r)}}var gt,_e,ke,qi,mt,ft=w(()=>{"use strict";R();v();Ls();pt();As();gt=Os(S,"titles"),_e=5,ke=15,qi=500;mt=5});function ks(e,t){let s=eo.get(e);if(!(!s||s.size===0))for(let n of s)try{n(t)}catch{}}var eo,vs=w(()=>{"use strict";eo=new Map});import{existsSync as to,statSync as so}from"node:fs";import{delimiter as no,join as ro}from"node:path";function Ds(e){if(e.includes("/")||e.includes("\\")||e.includes(".."))return null;let t=(process.env.PATH??"").split(no).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=ro(n,r);try{if(to(i)&&so(i).isFile())return i}catch{}}return null}function Ms(e){if(process.platform!=="win32"||!e)return!1;let t=e.toLowerCase();return t.endsWith(".cmd")||t.endsWith(".bat")}var Fs=w(()=>{"use strict"});var Bs={};Xe(Bs,{_resetClaudePathCacheForTests:()=>co,buildScanPrompt:()=>$s,isClaudeCliAvailable:()=>Us,runClaudeCliScan:()=>go,spawnClaudePrompt:()=>js});import{spawn as io}from"node:child_process";function Ps(){if(ae!==void 0&&fe!==void 0)return{path:ae,available:fe};let e=Ds("claude");return ae=e??"claude",fe=e!==null,{path:ae,available:fe}}function ao(){return Ps().path}function Us(){return Ps().available}function co(){ae=void 0,fe=void 0}function $s(e){return Ve({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 lo(e,t){let s=t.get(e);return s||e.slice(0,8)}function uo(e){try{return Ne(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 po(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),ks(t,{type:"progress",current:r.size,total:s,sessionId:u,sessionLabel:lo(u,n)}))}}}function mo(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 uo(e,t={},s){let n=!!t.scanId,r=n?ao(e):[],i=new Map(r.map(c=>[c.id,c.label])),o=r.length,a;return n&&t.scanId&&(a=co({scanId:t.scanId,total:o,labelTable:i})),$s({prompt:Ps(e),allowedTools:no.split(","),opts:t,onProgress:s,onStdoutLine:a,outputFormat:n?"stream-json":"json"})}async function Us(e,t,s={},n){return $s({prompt:e,allowedTools:t,opts:s,onProgress:n,outputFormat:"json"})}function $s(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 l=so(ro(),a,{stdio:["ignore","pipe","pipe"],shell:process.platform==="win32"&&oe==="claude"}),d=[],u=[],p=i?lo(i):void 0;l.stdout.on("data",_=>{d.push(_),p&&p(_)}),l.stderr.on("data",_=>{if(u.push(_),r){let f=_.toString("utf8").trim();f&&r(f)}});let g=setTimeout(()=>{l.kill("SIGKILL")},1800*1e3);l.on("close",_=>{clearTimeout(g),c({success:_===0,stdout:Buffer.concat(d).toString("utf8"),stderr:Buffer.concat(u).toString("utf8"),exitCode:_})}),l.on("error",_=>{clearTimeout(g),c({success:!1,stdout:"",stderr:String(_),exitCode:null})})})}var no,oe,_e,ht=y(()=>{"use strict";Je();Ze();ks();Ds();no=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});import Y from"chalk";import{formatDistanceToNowStrict as Dp,parseISO as Mp}from"date-fns";var h,Ot=y(()=>{"use strict";h={dim:Y.gray,bold:Y.bold,project:Y.cyan,user:Y.blue,assistant:Y.green,tool:Y.magenta,warn:Y.yellow,err:Y.red,ok:Y.green,accent:Y.hex("#f97316")}});var Kn=y(()=>{"use strict"});import{existsSync as Va,readFileSync as Za,writeFileSync as Qa}from"node:fs";import{join as ec}from"node:path";import{z as U}from"zod";function Ct(e){let t=e.trim();return!!(!t||Vn.test(t)||Zn.test(t))}function Qn(e){let t=e.trim();if(!t||Zn.test(t))return null;let s=t.replace(Vn,"").trim();return s.length>0?s:null}function It(e,t){if(!Ct(e))return e;let s=Qn(e);return s||(t&&!Ct(t)?t:e)}function ic(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 xt,qn,tc,sc,nc,rc,Vn,Zn,kt,oc,er=y(()=>{"use strict";k();xt=ec(T,"terminals.json"),qn=1440*60*1e3,tc=3e4,sc=6e4,nc=U.object({shell_pid:U.number(),tab_name:U.string(),cwd:U.string().nullable().optional(),opened_at:U.string(),last_seen_at:U.string()}),rc=U.object({schema:U.string().optional(),saved_at:U.string().optional(),terminals:U.array(nc).max(500).default([]),sessions_by_pid:U.record(U.string(),U.array(U.string()).max(50)).optional().default({})}),Vn=/^[⠀-⣿✳\s]+/,Zn=/^\d+(\.\d+){1,3}$/;kt=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,!!Va(xt)))try{let t=Za(xt,"utf8"),s=JSON.parse(t),n=rc.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 l of o)l.length>0&&c.add(l);c.size>0&&this.sessionsByPid.set(a,c)}this.gc()}catch{}}save(){try{O();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)]))};Qa(xt,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let s=new Date().toISOString(),n=this.entries.get(t.shell_pid),r=It(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=It(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>sc?(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=It(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=ic({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()-tc;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()-qn;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()-qn;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 l=Date.parse(c.last_seen_at);if(Number.isFinite(l)&&l>=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}}},oc=new kt});import{execFile as ac}from"node:child_process";import{promisify as cc}from"node:util";var Gp,tr=y(()=>{"use strict";Gp=cc(ac)});import{execFile as dc}from"node:child_process";import{promisify as uc}from"node:util";var em,tm,sr=y(()=>{"use strict";er();Ae();R();tr();em=uc(dc),tm=3600*1e3});var nr=y(()=>{"use strict"});import{z}from"zod";var om,rr=y(()=>{"use strict";R();om=z.object({enabled:z.boolean().default(!1),model:z.string().optional(),ratePerMinute:z.number().int().min(1).max(600).default(30),lastProcessedSessionId:z.string().nullable().default(null),backfillPaused:z.boolean().default(!1),autoExtractEnabled:z.boolean().default(!1),autoExtractIntervalMinutes:z.number().int().min(5).max(720).default(60),autoExtractBatchSize:z.number().int().min(1).max(20).default(1),autoResumeWorker:z.boolean().default(!1)})});var ir=y(()=>{"use strict";R();ht();rr()});var or=y(()=>{"use strict"});import{execFile as pc}from"node:child_process";import{promisify as mc}from"node:util";var Em,ar=y(()=>{"use strict";R();Em=mc(pc)});import{z as vt}from"zod";var bm,cr=y(()=>{"use strict";bm=vt.object({heuristicEnabled:vt.boolean().default(!0),agentEnabled:vt.boolean().default(!1)})});import{basename as Nm,join as Dt}from"node:path";var lr,xm,Im,dr=y(()=>{"use strict";R();k();it();lr=Dt(T,"auto-rules"),xm=Dt(lr,"rules.json"),Im=Dt(lr,"suggestions.json")});var ur=y(()=>{"use strict"});import{watch as Ym}from"chokidar";import{readdirSync as gc,statSync as Jm}from"node:fs";import{basename as Qm,join as _c}from"node:path";function pr(e){let t=e.split(/[/\\]/),s=t.findIndex(n=>n==="projects");return s===-1||s+1>=t.length?null:t[s+1]??null}function mr(e){return e.replace(/\\/g,"/").includes("/subagents/")}function*Mt(e){let t;try{t=gc(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*Mt(n):s.isFile()&&s.name.endsWith(".jsonl")&&(yield n)}}var gr=y(()=>{"use strict";k();R();Kn();sr();nr();ir();or();ar();ft();cr();dr();pt();ur();Ae()});var Ar={};Xe(Ar,{buildHealthReport:()=>Nr,buildPipelineDiagnostic:()=>wr,checkIngestStaleness:()=>Lr,detectLabelCollisions:()=>Rr,getFreeDiskBytes:()=>Lc,runDoctor:()=>Nc});import{existsSync as hr,readFileSync as fc,statSync as Ft,statfsSync as Er}from"node:fs";import{join as Tr}from"node:path";import*as Sr from"node:http";function Tc(e){let t=[];for(let s of e)if(s.alias){if(Ec.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 Sc(){let e=Tr(T,"daemon.port");if(!hr(e))return null;try{let t=fc(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 bc(e,t,s=1500){return new Promise(n=>{let r=Sr.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 yc(){let e=Tr(T,"terminals.json");if(!hr(e))return{exists:!1,mtimeMs:null,ageSeconds:null};try{let t=Ft(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 wc(){let e=new Date(Date.now()-Pt*36e5).toISOString();try{let t=m().prepare(`SELECT
755
+ `)}}}async function go(e,t={},s){let n=!!t.scanId,r=n?uo(e):[],i=new Map(r.map(c=>[c.id,c.label])),o=r.length,a;return n&&t.scanId&&(a=po({scanId:t.scanId,total:o,labelTable:i})),Hs({prompt:$s(e),allowedTools:oo.split(","),opts:t,onProgress:s,onStdoutLine:a,outputFormat:n?"stream-json":"json"})}async function js(e,t,s={},n){return Hs({prompt:e,allowedTools:t,opts:s,onProgress:n,outputFormat:"json"})}function Hs(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=ao(),l=io(d,a,{stdio:["ignore","pipe","pipe"],shell:Ms(d)||process.platform==="win32"&&ae==="claude"}),u=[],p=[],g=i?mo(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 oo,ae,fe,ht=w(()=>{"use strict";Je();Ze();vs();Fs();oo=["mcp__recall__list_tags","mcp__recall__list_sessions_to_tag","mcp__recall__apply_tags"].join(",")});import J from"chalk";import{formatDistanceToNowStrict as Pp,parseISO as Up}from"date-fns";var h,xt=w(()=>{"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 qn=w(()=>{"use strict"});import{existsSync as tc,readFileSync as sc,writeFileSync as nc}from"node:fs";import{join as rc}from"node:path";import{z as $}from"zod";function kt(e){let t=e.trim();return!!(!t||Zn.test(t)||Qn.test(t))}function er(e){let t=e.trim();if(!t||Qn.test(t))return null;let s=t.replace(Zn,"").trim();return s.length>0?s:null}function Ct(e,t){if(!kt(e))return e;let s=er(e);return s||(t&&!kt(t)?t:e)}function lc(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 It,Vn,ic,oc,ac,cc,Zn,Qn,vt,dc,tr=w(()=>{"use strict";v();It=rc(S,"terminals.json"),Vn=1440*60*1e3,ic=3e4,oc=6e4,ac=$.object({shell_pid:$.number(),tab_name:$.string(),cwd:$.string().nullable().optional(),opened_at:$.string(),last_seen_at:$.string()}),cc=$.object({schema:$.string().optional(),saved_at:$.string().optional(),terminals:$.array(ac).max(500).default([]),sessions_by_pid:$.record($.string(),$.array($.string()).max(50)).optional().default({})}),Zn=/^[⠀-⣿✳\s]+/,Qn=/^\d+(\.\d+){1,3}$/;vt=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,!!tc(It)))try{let t=sc(It,"utf8"),s=JSON.parse(t),n=cc.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{O();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)]))};nc(It,JSON.stringify(t,null,2))}catch{}}upsert(t){this.ensureLoaded();let s=new Date().toISOString(),n=this.entries.get(t.shell_pid),r=Ct(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=Ct(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>oc?(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=Ct(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=lc({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()-ic;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()-Vn;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()-Vn;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}}},dc=new vt});import{execFile as uc}from"node:child_process";import{promisify as pc}from"node:util";var Jp,sr=w(()=>{"use strict";Jp=pc(uc)});import{execFile as gc}from"node:child_process";import{promisify as _c}from"node:util";var nm,rm,nr=w(()=>{"use strict";tr();Ae();R();sr();nm=_c(gc),rm=3600*1e3});var rr=w(()=>{"use strict"});import{z as K}from"zod";var lm,ir=w(()=>{"use strict";R();lm=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 or=w(()=>{"use strict";R();ht();ir()});var ar=w(()=>{"use strict"});import{execFile as fc}from"node:child_process";import{promisify as hc}from"node:util";var bm,cr=w(()=>{"use strict";R();bm=hc(fc)});import{z as Dt}from"zod";var Rm,lr=w(()=>{"use strict";Rm=Dt.object({heuristicEnabled:Dt.boolean().default(!0),agentEnabled:Dt.boolean().default(!1)})});import{basename as Om,join as Mt}from"node:path";var dr,km,vm,ur=w(()=>{"use strict";R();v();it();dr=Mt(S,"auto-rules"),km=Mt(dr,"rules.json"),vm=Mt(dr,"suggestions.json")});var pr=w(()=>{"use strict"});var mr=w(()=>{"use strict"});import{watch as Vm}from"chokidar";import{readdirSync as Ec,statSync as Qm}from"node:fs";import{basename as rg,join as Tc}from"node:path";function gr(e){let t=e.split(/[/\\]/),s=t.findIndex(n=>n==="projects");return s===-1||s+1>=t.length?null:t[s+1]??null}function _r(e){return e.replace(/\\/g,"/").includes("/subagents/")}function*Ft(e){let t;try{t=Ec(e,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let s of t){if(s.isSymbolicLink())continue;let n=Tc(e,s.name);s.isDirectory()?yield*Ft(n):s.isFile()&&s.name.endsWith(".jsonl")&&(yield n)}}var fr=w(()=>{"use strict";v();R();qn();nr();rr();or();ar();cr();ft();lr();ur();pt();pr();Ae();mr()});var xr={};Xe(xr,{buildHealthReport:()=>Ar,buildPipelineDiagnostic:()=>Nr,checkIngestStaleness:()=>Or,detectLabelCollisions:()=>Lr,getFreeDiskBytes:()=>Ic,runDoctor:()=>xc});import{existsSync as Tr,readFileSync as Sc,statSync as Pt,statfsSync as Sr}from"node:fs";import{join as br}from"node:path";import*as yr from"node:http";function wc(e){let t=[];for(let s of e)if(s.alias){if(yc.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 Rc(){let e=br(S,"daemon.port");if(!Tr(e))return null;try{let t=Sc(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 Nc(e,t,s=1500){return new Promise(n=>{let r=yr.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 Lc(){let e=br(S,"terminals.json");if(!Tr(e))return{exists:!1,mtimeMs:null,ageSeconds:null};try{let t=Pt(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 Ac(){let e=new Date(Date.now()-Ut*36e5).toISOString();try{let t=m().prepare(`SELECT
756
756
  COUNT(*) AS total,
757
757
  SUM(CASE WHEN sa.alias IS NULL OR sa.alias = '' THEN 1 ELSE 0 END) AS without_alias,
758
758
  SUM(CASE WHEN (sa.alias IS NULL OR sa.alias = '')
759
759
  AND s.auto_title_source = 'heuristic' THEN 1 ELSE 0 END) AS heuristic_only
760
760
  FROM sessions s
761
761
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
762
- 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 wr(){let e=Sc(),t=yc(),s=wc(),n=!1,r=null,i=null,o=null,a=null,c=null;if(e){let u=await bc(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 l=[];if(n||l.push("Daemon not reachable on 127.0.0.1 \u2014 start it with `recall start` before further diagnosis."),o!==null&&o>0&&l.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)l.push(`Daemon has been running ${Math.round(r/60)} min but has not yet seen a single successful /api/terminal/sync. Either no editor extension is installed/active, or every attempt is being rejected (see counter above).`);else{let u=Date.now()-Date.parse(a);Number.isFinite(u)&&u>br&&l.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&&l.push(`~/.recall/terminals.json is ${Math.round(t.ageSeconds/3600)}h old \u2014 pipeline has not produced fresh data recently. Likely root cause is the same as the rejection counter would show; start the daemon and re-run.`),c?.circuitBroken&&l.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>=yr&&l.push(`${s.heuristicOnly}/${s.total} sessions in the last ${Pt}h (${Math.round(s.fractionHeuristic*100)}%) fell back to the heuristic first-message title. A healthy pipeline rate is < 20%. Either the extension is not syncing tab names, or the correlator cannot disambiguate. Reinstall the extension and verify the rejection counter drops to 0.`),{state:n?l.length>0?"degraded":"ok":"down",flags:l,daemon:{running:n,port:e,uptimeSeconds:r,version:i},runtime:{silentTerminalRejections:o,lastTerminalSyncAt:a,autoExtract:c},terminalsJson:t,recentSessions:s}}function se(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 _r(e){try{return Ft(e).size}catch{return 0}}function fr(e){try{return m().prepare(`SELECT COUNT(*) AS n FROM ${e}_data WHERE block = 1`).get().n}catch{return 0}}function Rr(){try{return m().prepare(`SELECT p.name AS project,
762
+ 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 Nr(){let e=Rc(),t=Lc(),s=Ac(),n=!1,r=null,i=null,o=null,a=null,c=null;if(e){let u=await Nc(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>wr&&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>=Rr&&d.push(`${s.heuristicOnly}/${s.total} sessions in the last ${Ut}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 ne(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 hr(e){try{return Pt(e).size}catch{return 0}}function Er(e){try{return m().prepare(`SELECT COUNT(*) AS n FROM ${e}_data WHERE block = 1`).get().n}catch{return 0}}function Lr(){try{return m().prepare(`SELECT p.name AS project,
763
763
  COALESCE(NULLIF(sa.alias, ''), s.auto_title, substr(s.first_user_message, 1, 60)) AS label,
764
764
  COUNT(*) AS count,
765
765
  MAX(CASE WHEN sa.alias IS NOT NULL AND sa.alias != '' THEN 1 ELSE 0 END) AS any_aliased
@@ -771,36 +771,36 @@ ${u+1}. ${p}`:`${u+1}. ${p}`}).join(`
771
771
  GROUP BY p.name, label
772
772
  HAVING count >= 2
773
773
  ORDER BY count DESC, label ASC
774
- 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 Nr(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 N=t.pragma("quick_check").map(P=>P.quick_check);i=N.length===1&&N[0]==="ok"?"ok":N.join("; ")}catch(b){i=`check failed: ${b.message}`}let o=_r(pe),a=_r(`${pe}-wal`),c=0,l=0;try{let b=Er(T);c=Number(b.bavail)*Number(b.bsize),l=Number(b.blocks)*Number(b.bsize)}catch{}e?.("Counting rows");let d=t.prepare(`SELECT
774
+ 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 Ar(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 y=t.pragma("quick_check").map(k=>k.quick_check);i=y.length===1&&y[0]==="ok"?"ok":y.join("; ")}catch(T){i=`check failed: ${T.message}`}let o=hr(me),a=hr(`${me}-wal`),c=0,d=0;try{let T=Sr(S);c=Number(T.bavail)*Number(T.bsize),d=Number(T.blocks)*Number(T.bsize)}catch{}e?.("Counting rows");let l=t.prepare(`SELECT
775
775
  (SELECT COUNT(*) FROM projects) AS projects,
776
776
  (SELECT COUNT(*) FROM sessions) AS sessions,
777
777
  (SELECT COUNT(*) FROM messages) AS messages,
778
- (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 ${se(c)} \u2014 heavy operations (synthesis, extract-outputs, vector ingest) may fail.`),a>50*1024**2&&p.push(`WAL is ${se(a)} \u2014 run \`recall optimize\` to truncate it.`),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 g=fr("messages_fts"),_=fr("sessions_fts");g>16&&p.push(`messages_fts has ${g} segments \u2014 \`recall optimize\` will merge them.`);let f=0;try{f=t.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}catch{}let E=!1;try{E=t.prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value==="1"}catch{}return!E&&f>0?p.push(`${f.toLocaleString()} rows in chunk_queue with semantic disabled \u2014 schema gate is stale; restart daemon (v0.67+) to migrate.`):f>1e5&&p.push(`chunk_queue has ${f.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:l},fts:{messages:{fragments:g},sessions:{fragments:_}},vectors:{rows:u},rows:{projects:d.projects,sessions:d.sessions,messages:d.messages,messageUsage:d.message_usage},chunkQueue:{size:f,semanticEnabled:E},warnings:p}}function Rc(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})`)}
778
+ (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 ${ne(c)} \u2014 heavy operations (synthesis, extract-outputs, vector ingest) may fail.`),a>50*1024**2&&p.push(`WAL is ${ne(a)} \u2014 run \`recall optimize\` to truncate it.`),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 g=Er("messages_fts"),_=Er("sessions_fts");g>16&&p.push(`messages_fts has ${g} segments \u2014 \`recall optimize\` will merge them.`);let f=0;try{f=t.prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}catch{}let E=!1;try{E=t.prepare("SELECT value FROM app_settings WHERE key = 'semantic_enabled'").get()?.value==="1"}catch{}return!E&&f>0?p.push(`${f.toLocaleString()} rows in chunk_queue with semantic disabled \u2014 schema gate is stale; restart daemon (v0.67+) to migrate.`):f>1e5&&p.push(`chunk_queue has ${f.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:g},sessions:{fragments:_}},vectors:{rows:u},rows:{projects:l.projects,sessions:l.sessions,messages:l.messages,messageUsage:l.message_usage},chunkQueue:{size:f,semanticEnabled:E},warnings:p}}function Oc(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})`)}
779
779
  `):process.stderr.write(` \u2713 ${s} (${o})
780
780
  `),s=""};return{stage(i){r(),s=i,n=Date.now(),t?process.stderr.write(` ${h.dim("\u2026")} ${s}`):process.stderr.write(` \u2026 ${s}
781
- `)},done:r}}function Lr(){let e=m(),t=e.prepare("SELECT encoded_path FROM projects").all(),s=new Set(t.map(l=>l.encoded_path));if(s.size===0)return{status:"ok",staleCount:0,scanned:0,sampleFiles:[],message:"Ingest freshness: ok (no projects indexed yet \u2014 nothing to monitor)."};let n=e.prepare("SELECT file_mtime FROM sessions WHERE file_path = ? LIMIT 1"),r=0,i=0,o=[];for(let l of Mt(ze)){if(mr(l))continue;let d=pr(l);if(!d||!s.has(d))continue;r+=1;let u;try{u=Ft(l).mtimeMs}catch{continue}let p=n.get(l);(!p||p.file_mtime<u)&&(i+=1,o.length<5&&o.push(l))}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(l=>l.split(/[/\\]/).pop()??l).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 Nc(e={}){let t=Rc(!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
781
+ `)},done:r}}function Or(){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 Ft(ze)){if(_r(d))continue;let l=gr(d);if(!l||!s.has(l))continue;r+=1;let u;try{u=Pt(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 xc(e={}){let t=Oc(!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
782
  FROM session_aliases sa
783
783
  LEFT JOIN sessions s ON s.id = sa.session_id
784
- WHERE sa.alias IS NOT NULL AND sa.alias != ''`).all(),n=Tc(s),r=Nr(t.stage);t.stage("Probing daemon");let i=await wr();t.stage("Detecting label collisions");let o=Rr();t.stage("Checking ingest freshness");let a=Lr();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(`
785
- `);let d=i.state==="degraded",u=a.status==="fail";return n.length===0&&r.db.integrity==="ok"&&!d&&!u?0:1}console.log(h.dim("\u2014 System health \u2014")),console.log(` Database ${se(r.db.sizeBytes)} (${r.rows.messages.toLocaleString()} messages across ${r.rows.sessions.toLocaleString()} sessions, ${r.rows.projects.toLocaleString()} projects)`),console.log(` WAL ${se(r.db.walSizeBytes)} (capped at 64 MB; truncated on clean shutdown)`),console.log(` Free pages ${r.db.freelistCount.toLocaleString()} (${se(r.db.freelistBytes)} reclaimable via VACUUM)`);{let d=r.fts.messages.fragments,u=r.fts.sessions.fragments,p=d===0&&u===0;console.log(` FTS segments messages=${d}, 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 d=r.disk.freeBytes/r.disk.totalBytes*100;console.log(` Disk free ${se(r.disk.freeBytes)} of ${se(r.disk.totalBytes)} (${d.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 d of r.warnings)console.log(` ${h.warn("!")} ${d}`)}if(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 d of a.sampleFiles.slice(0,5))console.log(` ${h.dim("\u2022")} ${d}`);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 d of a.sampleFiles.slice(0,5))console.log(` ${h.dim("\u2022")} ${d}`);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 d=i.runtime.silentTerminalRejections;console.log(` Auth rejections ${d===0?h.ok("0"):h.err(d.toLocaleString())} (extension /api/terminal/* requests denied without a valid X-Recall-Token)`)}if(i.runtime.autoExtract){let d=i.runtime.autoExtract;d.circuitBroken?console.log(` Auto-extract ${h.err("circuit broken")} (${d.reason??"unknown"} \u2014 toggle off/on to reset)`):d.consecutiveZeroTokenRuns>0&&console.log(` Auto-extract ${h.warn(`${d.consecutiveZeroTokenRuns} zero-token run(s)`)} (breaker trips at 3)`)}if(i.runtime.lastTerminalSyncAt!==null){let d=Date.now()-Date.parse(i.runtime.lastTerminalSyncAt),u=Number.isFinite(d)?Math.round(d/6e4):null,p=u!==null&&d>br;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 d=Math.round(i.terminalsJson.ageSeconds/3600),u=i.terminalsJson.ageSeconds>24*3600;console.log(` terminals.json ${u?h.warn(`${d}h old`):h.ok(`${d}h old`)} (persisted registry mtime \u2014 fresh means extensions are connecting)`)}let c=i.recentSessions;if(c.total>0){let d=Math.round(c.fractionHeuristic*100),u=c.fractionHeuristic>=yr&&c.total>=3;console.log(` Recent titles ${u?h.err(`${d}% heuristic`):h.ok(`${d}% heuristic`)} (${c.heuristicOnly}/${c.total} sessions in last ${Pt}h fell back to first-message title)`)}if(i.flags.length>0){console.log("");for(let d of i.flags)console.log(` ${h.warn("!")} ${d}`)}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 d=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
786
- displayed first user message. See docs/HANDOFF.md \u2192 "Alias header convention".`)),d.length>0&&(console.log(""),console.log(h.warn(` ${d.length} of these groups have NO alias on any row \u2014 strongest candidates
787
- 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 l=new Map;for(let d of n){let u=l.get(d.violation)??[];u.push(d),l.set(d.violation,u)}for(let[d,u]of l){console.log(h.warn(` ${d} (${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 Lc(){try{let e=Er(T);return Number(e.bavail)*Number(e.bsize)}catch{return 0}}var hc,Ec,br,Pt,yr,Or=y(()=>{"use strict";Ot();R();k();gr();hc=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],Ec=new RegExp(`^(${hc.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);br=5*6e4,Pt=24,yr=.5});var xr={};Xe(xr,{runOptimize:()=>Cc});import{existsSync as Ac,readFileSync as Oc}from"node:fs";import{join as xc}from"node:path";function Ic(){let e=xc(T,"daemon.pid");if(!Ac(e))return!1;try{let t=parseInt(Oc(e,"utf-8").trim(),10);return!Number.isFinite(t)||t<=0?!1:(process.kill(t,0),!0)}catch{return!1}}async function Te(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 Cc(e={}){let t=m(),s=[];if(e.vacuum&&Ic())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)+`
788
- `),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 Te("wal_checkpoint(TRUNCATE)",()=>{t.pragma("wal_checkpoint(TRUNCATE)")})),s.push(await Te("messages_fts optimize",()=>{t.exec("INSERT INTO messages_fts(messages_fts) VALUES('optimize');")})),s.push(await Te("sessions_fts optimize",()=>{t.exec("INSERT INTO sessions_fts(sessions_fts) VALUES('optimize');")})),s.push(await Te("PRAGMA optimize",()=>{t.exec("PRAGMA optimize")})),e.vacuum&&s.push(await Te("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)+`
789
- `),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 Ir=y(()=>{"use strict";Ot();R();k()});R();import{McpServer as kc}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as vc}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as S}from"zod";import{fileURLToPath as Dc}from"node:url";var jr=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,Hr=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,Br=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function Xr(e){return e.replace(jr,"").trim()}function Wr(e){let t=e.replace(Hr,"[tool call]");return t=t.replace(Br,"[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,`
790
-
791
- `),t.trim()}function Gr(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(d=>!(!r&&d.is_sidechain===1||i&&d.timestamp&&Date.parse(d.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,l=0;for(let d of o){let u=d.content_text??"",p=Xr(u);n==="condensed"&&(p=Wr(p));let g=p.length>0,_=!!d.tool_names&&d.tool_names.length>0;if(!g&&!_){l+=1;continue}let f=Gr(d),E=d.timestamp?` \`${d.timestamp}\``:"";a.push(`## ${f}${E}`),a.push(""),_&&n==="condensed"&&(a.push(`_tools used: ${d.tool_names}_`),a.push("")),g&&(a.push(p),a.push("")),c+=1}return a.push("---"),a.push(""),a.push(`_${c} messages included_`+(l?`, ${l} empty skipped`:"")+(r?"":", subagent/sidechain hidden")+"."),a.join(`
792
- `)}Re();Je();import{existsSync as qr,mkdirSync as El,readFileSync as Vr,writeFileSync as Tl,chmodSync as Sl}from"node:fs";import{homedir as Zr}from"node:os";import{join as Vt}from"node:path";import{z as K}from"zod";function Qr(){return process.env.RECALL_HOME??Vt(Zr(),".recall")}function ei(){return Vt(Qr(),"config.json")}var ti=K.object({enabled:K.boolean().default(!1),backend:K.enum(["api","mcp"]).default("api"),apiKey:K.string().optional(),model:K.string().default("claude-opus-4-7"),maxTagsPerSession:K.number().int().min(1).max(10).default(4),minTagsPerSession:K.number().int().min(1).max(10).default(2),autopilot:K.boolean().default(!1)}),Ke={enabled:!1,backend:"api",model:"claude-opus-4-7",maxTagsPerSession:4,minTagsPerSession:2,autopilot:!1};function si(){let e=ei();if(!qr(e))return{};try{return JSON.parse(Vr(e,"utf8"))}catch(t){return console.error("[auto-tag-config] failed to parse config.json, using defaults:",t),{}}}function qe(){let e=si().autoTag;if(!e)return{...Ke};let t=ti.safeParse({...Ke,...e});return t.success?t.data:{...Ke}}Ze();R();Re();Ae();import{z as v}from"zod";R();k();import{writeFileSync as Ti,mkdirSync as Si,existsSync as bi}from"node:fs";import{join as es}from"node:path";var Qe=es(T,"notes");function ts(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function yi(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 wi(){O(),bi(Qe)||Si(Qe,{recursive:!0})}function Ri(e){return{session_id:e.session_id,content:e.content,updated_at:e.updated_at,previous_versions:ts(e.previous_versions),auto_synopsis:e.auto_synopsis??null,auto_synopsis_generated_at:e.auto_synopsis_generated_at??null,auto_synopsis_history:yi(e.auto_synopsis_history)}}var Ni="session_id, content, updated_at, previous_versions, auto_synopsis, auto_synopsis_generated_at, auto_synopsis_history";function et(e){let t=m().prepare(`SELECT ${Ni} FROM session_notes WHERE session_id = ?`).get(e);return t?Ri(t):null}function ss(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=ts(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)
784
+ WHERE sa.alias IS NOT NULL AND sa.alias != ''`).all(),n=wc(s),r=Ar(t.stage);t.stage("Probing daemon");let i=await Nr();t.stage("Detecting label collisions");let o=Lr();t.stage("Checking ingest freshness");let a=Or();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(`
785
+ `);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 ${ne(r.db.sizeBytes)} (${r.rows.messages.toLocaleString()} messages across ${r.rows.sessions.toLocaleString()} sessions, ${r.rows.projects.toLocaleString()} projects)`),console.log(` WAL ${ne(r.db.walSizeBytes)} (capped at 64 MB; truncated on clean shutdown)`),console.log(` Free pages ${r.db.freelistCount.toLocaleString()} (${ne(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 ${ne(r.disk.freeBytes)} of ${ne(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(""),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>wr;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>=Rr&&c.total>=3;console.log(` Recent titles ${u?h.err(`${l}% heuristic`):h.ok(`${l}% heuristic`)} (${c.heuristicOnly}/${c.total} sessions in last ${Ut}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
786
+ 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
787
+ 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 Ic(){try{let e=Sr(S);return Number(e.bavail)*Number(e.bsize)}catch{return 0}}var bc,yc,wr,Ut,Rr,Ir=w(()=>{"use strict";xt();R();v();fr();bc=["VS Code","VS Code Insiders","Cursor","Windsurf","Warp","iTerm","Terminal","WezTerm","Windows Terminal","Kitty","Alacritty"],yc=new RegExp(`^(${bc.map(e=>e.replace(/ /g,"\\s")).join("|")})\\s\xB7\\s`);wr=5*6e4,Ut=24,Rr=.5});var Cr={};Xe(Cr,{runOptimize:()=>Mc});import{existsSync as Cc,readFileSync as kc}from"node:fs";import{join as vc}from"node:path";function Dc(){let e=vc(S,"daemon.pid");if(!Cc(e))return!1;try{let t=parseInt(kc(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 Mc(e={}){let t=m(),s=[];if(e.vacuum&&Dc())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)+`
788
+ `),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)+`
789
+ `),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 kr=w(()=>{"use strict";xt();R();v()});R();import{McpServer as Fc}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as Pc}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as b}from"zod";import{fileURLToPath as Uc}from"node:url";var Xr=/<(local-command-caveat|local-command-stdout|command-name|command-message|command-args|system-reminder|user-prompt-submit-hook|task-notification)[\s\S]*?<\/\1>/gi,Wr=/⚡ \*\*Tool call · `[^`]+`\*\*\s*\n+```json\n[\s\S]*?\n```/g,Gr=/\*\*Tool result\*\*\s*\n+```\n[\s\S]*?\n```/g;function Yr(e){return e.replace(Xr,"").trim()}function zr(e){let t=e.replace(Wr,"[tool call]");return t=t.replace(Gr,"[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,`
790
+
791
+ `),t.trim()}function Jr(e){return e.role??e.type??"message"}function Jt(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=Yr(u);n==="condensed"&&(p=zr(p));let g=p.length>0,_=!!l.tool_names&&l.tool_names.length>0;if(!g&&!_){d+=1;continue}let f=Jr(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(`
792
+ `)}Re();Je();import{existsSync as Qr,mkdirSync as bl,readFileSync as ei,writeFileSync as yl,chmodSync as wl}from"node:fs";import{homedir as ti}from"node:os";import{join as Zt}from"node:path";import{z as q}from"zod";function si(){return process.env.RECALL_HOME??Zt(ti(),".recall")}function ni(){return Zt(si(),"config.json")}var ri=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)}),Ke={enabled:!1,backend:"api",model:"claude-opus-4-7",maxTagsPerSession:4,minTagsPerSession:2,autopilot:!1};function ii(){let e=ni();if(!Qr(e))return{};try{return JSON.parse(ei(e,"utf8"))}catch(t){return console.error("[auto-tag-config] failed to parse config.json, using defaults:",t),{}}}function qe(){let e=ii().autoTag;if(!e)return{...Ke};let t=ri.safeParse({...Ke,...e});return t.success?t.data:{...Ke}}Ze();R();Re();Ae();import{z as D}from"zod";R();v();import{writeFileSync as yi,mkdirSync as wi,existsSync as Ri}from"node:fs";import{join as ts}from"node:path";var Qe=ts(S,"notes");function ss(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return[]}function Ni(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 Li(){O(),Ri(Qe)||wi(Qe,{recursive:!0})}function Ai(e){return{session_id:e.session_id,content:e.content,updated_at:e.updated_at,previous_versions:ss(e.previous_versions),auto_synopsis:e.auto_synopsis??null,auto_synopsis_generated_at:e.auto_synopsis_generated_at??null,auto_synopsis_history:Ni(e.auto_synopsis_history)}}var Oi="session_id, content, updated_at, previous_versions, auto_synopsis, auto_synopsis_generated_at, auto_synopsis_history";function et(e){let t=m().prepare(`SELECT ${Oi} FROM session_notes WHERE session_id = ?`).get(e);return t?Ai(t):null}function ns(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=ss(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)
793
793
  VALUES (?, ?, ?, ?)
794
794
  ON CONFLICT(session_id) DO UPDATE SET
795
795
  content = excluded.content,
796
796
  updated_at = excluded.updated_at,
797
- previous_versions = excluded.previous_versions`).run(e,t,n,JSON.stringify(i)),Li(e,t,n),et(e)??{session_id:e,content:t,updated_at:n,previous_versions:i,auto_synopsis:null,auto_synopsis_generated_at:null,auto_synopsis_history:[]}}function Li(e,t,s){try{wi();let n=es(Qe,`${e}.md`),r=`<!-- Claude Recall note \xB7 session ${e} \xB7 updated ${s} -->
798
- `;Ti(n,r+t)}catch(n){console.error("[notes] mirror write failed:",n)}}it();R();var at=60,Mi=6e4,ie=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 ot(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)
799
- VALUES (?, ?, ?, ?, ?, ?)`).run(e.tool,s,e.result,e.errorMessage??null,e.caller??null,t)}var q=class{capacity;windowMs;hits=[];constructor(t=at,s=Mi){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 ie(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 A(e){try{e.limiter.consume()}catch(t){throw t instanceof ie&&ot({tool:e.tool,args:e.args,result:"rate_limited",errorMessage:t.message,caller:e.caller}),t}try{let t=await e.run();return ot({tool:e.tool,args:e.args,result:"ok",caller:e.caller}),t}catch(t){let s=t instanceof Error?t.message:String(t);throw ot({tool:e.tool,args:e.args,result:"error",errorMessage:s,caller:e.caller}),t}}R();import{z as Fi}from"zod";function B(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 Oe(e){return{content:[{type:"text",text:e}],isError:!0}}function L(e){return e instanceof ie?Oe(e.message):e instanceof Fi.ZodError?Oe(`invalid input: ${e.message}`):e instanceof Error&&e.message.startsWith("SQLITE_")?Oe("database constraint error"):Oe(e instanceof Error?e.message:String(e))}function ee(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function V(e){return{content:[{type:"text",text:e}],isError:!0}}var Pi=`
797
+ previous_versions = excluded.previous_versions`).run(e,t,n,JSON.stringify(i)),xi(e,t,n),et(e)??{session_id:e,content:t,updated_at:n,previous_versions:i,auto_synopsis:null,auto_synopsis_generated_at:null,auto_synopsis_history:[]}}function xi(e,t,s){try{Li();let n=ts(Qe,`${e}.md`),r=`<!-- Claude Recall note \xB7 session ${e} \xB7 updated ${s} -->
798
+ `;yi(n,r+t)}catch(n){console.error("[notes] mirror write failed:",n)}}it();R();var at=60,Ui=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 ot(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)
799
+ VALUES (?, ?, ?, ?, ?, ?)`).run(e.tool,s,e.result,e.errorMessage??null,e.caller??null,t)}var V=class{capacity;windowMs;hits=[];constructor(t=at,s=Ui){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 L(e){try{e.limiter.consume()}catch(t){throw t instanceof oe&&ot({tool:e.tool,args:e.args,result:"rate_limited",errorMessage:t.message,caller:e.caller}),t}try{let t=await e.run();return ot({tool:e.tool,args:e.args,result:"ok",caller:e.caller}),t}catch(t){let s=t instanceof Error?t.message:String(t);throw ot({tool:e.tool,args:e.args,result:"error",errorMessage:s,caller:e.caller}),t}}R();import{z as $i}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 Oe(e){return{content:[{type:"text",text:e}],isError:!0}}function N(e){return e instanceof oe?Oe(e.message):e instanceof $i.ZodError?Oe(`invalid input: ${e.message}`):e instanceof Error&&e.message.startsWith("SQLITE_")?Oe("database constraint error"):Oe(e instanceof Error?e.message:String(e))}function te(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function Z(e){return{content:[{type:"text",text:e}],isError:!0}}var ji=`
800
800
 
801
801
  ---
802
802
 
803
- `,Ui=5e5;function os(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:v.string().describe("Full session UUID or 8+-character prefix."),tag:v.string().min(1).describe("Tag to add. Normalized: lowercase, dashes, max 40 chars.")}},async n=>{try{let r=B(n.sessionId);if(!r)return V(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await A({tool:"add_tag",args:{sessionId:r,tag:n.tag},limiter:s,run:()=>ye(r,n.tag)});return ee({sessionId:r,...i})}catch(r){return L(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:v.string().describe("Full session UUID or 8+-character prefix."),tag:v.string().min(1).describe("Tag to remove (normalized server-side).")}},async n=>{try{let r=B(n.sessionId);if(!r)return V(`session not found or prefix ambiguous: ${n.sessionId}`);let i=Q(n.tag);if(!i)return V("tag must contain at least one alphanumeric character");let o=await A({tool:"remove_tag",args:{sessionId:r,tag:i},limiter:s,run:()=>Jt(r,i)});return ee({sessionId:r,...o})}catch(r){return L(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:v.string().describe("Full session UUID or 8+-character prefix."),alias:v.string().min(1).max(120).describe("New alias (non-empty, max 120 chars).")}},async n=>{try{let r=B(n.sessionId);if(!r)return V(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await A({tool:"set_alias",args:{sessionId:r,alias:n.alias},limiter:s,run:()=>Le(r,n.alias)});return ee(i)}catch(r){return L(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:v.string().describe("Full session UUID or 8+-character prefix."),markdown:v.string().min(1).max(5e4).describe("Markdown to append.")}},async n=>{try{let r=B(n.sessionId);if(!r)return V(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await A({tool:"append_note",args:{sessionId:r,length:n.markdown.length},limiter:s,run:()=>{let o=et(r),a=o&&o.content.length>0?`${o.content}${Pi}${n.markdown}`:n.markdown;if(a.length>Ui)throw new Error(`note would exceed the 500 KB cumulative limit (current: ${o?.content.length??0} bytes, adding: ${n.markdown.length} bytes)`);return ss(r,a)}});return ee(i)}catch(r){return L(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:v.string().min(1).max(120),parent_id:v.string().optional().describe("Parent collection id (omit for root)."),icon:v.string().max(32).optional(),color:v.string().max(32).optional(),description:v.string().max(2e3).optional()}},async n=>{try{let r=await A({tool:"create_collection",args:n,limiter:s,run:()=>st({name:n.name,parent_id:n.parent_id??null,icon:n.icon??null,color:n.color??null,description:n.description??null})});return ee(r)}catch(r){return L(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:v.string().min(1),sessionId:v.string().describe("Full session UUID or 8+-character prefix."),note:v.string().max(2e3).optional()}},async n=>{try{let r=B(n.sessionId);if(!r)return V(`session not found or prefix ambiguous: ${n.sessionId}`);if(!m().prepare("SELECT 1 FROM collections WHERE id = ?").get(n.collectionId))return V("collection not found");let a=await A({tool:"add_session_to_collection",args:{collectionId:n.collectionId,sessionId:r,note:n.note??null},limiter:s,run:()=>nt(n.collectionId,r,n.note??null)});return ee({collectionId:n.collectionId,sessionId:r,...a})}catch(r){return L(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:v.string().min(1),sessionId:v.string().describe("Full session UUID or 8+-character prefix.")}},async n=>{try{let r=B(n.sessionId);if(!r)return V(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await A({tool:"remove_session_from_collection",args:{collectionId:n.collectionId,sessionId:r},limiter:s,run:()=>is(n.collectionId,r)});return ee({collectionId:n.collectionId,sessionId:r,...i})}catch(r){return L(r)}})}import{z as $}from"zod";R();k();import{randomUUID as as}from"node:crypto";import{writeFileSync as cs,readFileSync as cd,existsSync as $i,mkdirSync as ji}from"node:fs";import{join as ct}from"node:path";var xe=ct(T,"threads"),Hi=ct(xe,"index.json");function ls(){O(),$i(xe)||ji(xe,{recursive:!0})}function lt(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 ds(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,
803
+ `,Hi=5e5;function as(e,t={}){let s=t.limiter??new V;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:D.string().describe("Full session UUID or 8+-character prefix."),tag:D.string().min(1).describe("Tag to add. Normalized: lowercase, dashes, max 40 chars.")}},async n=>{try{let r=X(n.sessionId);if(!r)return Z(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await L({tool:"add_tag",args:{sessionId:r,tag:n.tag},limiter:s,run:()=>ye(r,n.tag)});return te({sessionId:r,...i})}catch(r){return N(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:D.string().describe("Full session UUID or 8+-character prefix."),tag:D.string().min(1).describe("Tag to remove (normalized server-side).")}},async n=>{try{let r=X(n.sessionId);if(!r)return Z(`session not found or prefix ambiguous: ${n.sessionId}`);let i=ee(n.tag);if(!i)return Z("tag must contain at least one alphanumeric character");let o=await L({tool:"remove_tag",args:{sessionId:r,tag:i},limiter:s,run:()=>Kt(r,i)});return te({sessionId:r,...o})}catch(r){return N(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:D.string().describe("Full session UUID or 8+-character prefix."),alias:D.string().min(1).max(120).describe("New alias (non-empty, max 120 chars).")}},async n=>{try{let r=X(n.sessionId);if(!r)return Z(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await L({tool:"set_alias",args:{sessionId:r,alias:n.alias},limiter:s,run:()=>Le(r,n.alias)});return te(i)}catch(r){return N(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:D.string().describe("Full session UUID or 8+-character prefix."),markdown:D.string().min(1).max(5e4).describe("Markdown to append.")}},async n=>{try{let r=X(n.sessionId);if(!r)return Z(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await L({tool:"append_note",args:{sessionId:r,length:n.markdown.length},limiter:s,run:()=>{let o=et(r),a=o&&o.content.length>0?`${o.content}${ji}${n.markdown}`:n.markdown;if(a.length>Hi)throw new Error(`note would exceed the 500 KB cumulative limit (current: ${o?.content.length??0} bytes, adding: ${n.markdown.length} bytes)`);return ns(r,a)}});return te(i)}catch(r){return N(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:D.string().min(1).max(120),parent_id:D.string().optional().describe("Parent collection id (omit for root)."),icon:D.string().max(32).optional(),color:D.string().max(32).optional(),description:D.string().max(2e3).optional()}},async n=>{try{let r=await L({tool:"create_collection",args:n,limiter:s,run:()=>st({name:n.name,parent_id:n.parent_id??null,icon:n.icon??null,color:n.color??null,description:n.description??null})});return te(r)}catch(r){return N(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:D.string().min(1),sessionId:D.string().describe("Full session UUID or 8+-character prefix."),note:D.string().max(2e3).optional()}},async n=>{try{let r=X(n.sessionId);if(!r)return Z(`session not found or prefix ambiguous: ${n.sessionId}`);if(!m().prepare("SELECT 1 FROM collections WHERE id = ?").get(n.collectionId))return Z("collection not found");let a=await L({tool:"add_session_to_collection",args:{collectionId:n.collectionId,sessionId:r,note:n.note??null},limiter:s,run:()=>nt(n.collectionId,r,n.note??null)});return te({collectionId:n.collectionId,sessionId:r,...a})}catch(r){return N(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:D.string().min(1),sessionId:D.string().describe("Full session UUID or 8+-character prefix.")}},async n=>{try{let r=X(n.sessionId);if(!r)return Z(`session not found or prefix ambiguous: ${n.sessionId}`);let i=await L({tool:"remove_session_from_collection",args:{collectionId:n.collectionId,sessionId:r},limiter:s,run:()=>os(n.collectionId,r)});return te({collectionId:n.collectionId,sessionId:r,...i})}catch(r){return N(r)}})}import{z as j}from"zod";R();v();import{randomUUID as cs}from"node:crypto";import{writeFileSync as ls,readFileSync as ud,existsSync as Bi,mkdirSync as Xi}from"node:fs";import{join as ct}from"node:path";var xe=ct(S,"threads"),Wi=ct(xe,"index.json");function ds(){O(),Bi(xe)||Xi(xe,{recursive:!0})}function lt(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 us(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,
804
804
  p.name AS project,
805
805
  COUNT(*) AS n,
806
806
  SUM(CASE WHEN te.role = 'origin' THEN 1 ELSE 0 END) AS origin_n
@@ -808,7 +808,7 @@ ${u+1}. ${p}`:`${u+1}. ${p}`}).join(`
808
808
  LEFT JOIN sessions s ON s.id = te.session_id
809
809
  LEFT JOIN projects p ON p.id = s.project_id
810
810
  WHERE te.thread_id IN (${n})
811
- 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),l=c.length,d=null;c.length>0&&(d=[...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:d,project_count:l})}return t}function us(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 ps(e){let s=m().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
811
+ 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 ps(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 ms(e){let s=m().prepare(`SELECT NULLIF(sa.alias, '') AS alias,
812
812
  s.auto_title AS auto_title,
813
813
  s.auto_title_source AS auto_title_source,
814
814
  s.first_user_message AS first_user_message,
@@ -819,8 +819,8 @@ ${u+1}. ${p}`:`${u+1}. ${p}`}).join(`
819
819
  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 dt(e){let s=m().prepare(`SELECT
820
820
  COUNT(*) AS session_count,
821
821
  SUM(CASE WHEN role = 'origin' THEN 1 ELSE 0 END) AS origin_count
822
- FROM thread_edges WHERE thread_id = ?`).get(e);return{session_count:s?.session_count??0,origin_count:s?.origin_count??0}}function W(e){let t=M(e);t&&(ls(),cs(ct(xe,`${e}.json`),JSON.stringify(t,null,2)),ms())}function ms(){ls();let e=ut({includeArchived:!0});cs(Hi,JSON.stringify({threads:e},null,2))}function Ie(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let s=m(),n=as(),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)
823
- VALUES (?, ?, NULL, 'origin', 1.0, 'manual', ?)`).run(n,e.originSessionId,r),W(n);let i=M(n);if(!i)throw new Error("thread creation succeeded but read-back failed");return i}function ut(e={}){let t=m(),s=e.includeArchived?"":"WHERE archived = 0",n=t.prepare(`SELECT * FROM threads ${s} ORDER BY created_at DESC`).all(),r=ds(n.map(i=>i.id));return n.map(i=>lt(i,dt(i.id),r.get(i.id)))}function M(e){let t=m(),s=t.prepare("SELECT * FROM threads WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT e.*,
822
+ 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=P(e);t&&(ds(),ls(ct(xe,`${e}.json`),JSON.stringify(t,null,2)),gs())}function gs(){ds();let e=ut({includeArchived:!0});ls(Wi,JSON.stringify({threads:e},null,2))}function Ie(e){let t=e.name.trim();if(!t)throw new Error("thread name cannot be empty");let s=m(),n=cs(),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)
823
+ VALUES (?, ?, NULL, 'origin', 1.0, 'manual', ?)`).run(n,e.originSessionId,r),Y(n);let i=P(n);if(!i)throw new Error("thread creation succeeded but read-back failed");return i}function ut(e={}){let t=m(),s=e.includeArchived?"":"WHERE archived = 0",n=t.prepare(`SELECT * FROM threads ${s} ORDER BY created_at DESC`).all(),r=us(n.map(i=>i.id));return n.map(i=>lt(i,dt(i.id),r.get(i.id)))}function P(e){let t=m(),s=t.prepare("SELECT * FROM threads WHERE id = ?").get(e);if(!s)return null;let n=t.prepare(`SELECT e.*,
824
824
  NULLIF(sa.alias, '') AS alias,
825
825
  s.auto_title AS auto_title,
826
826
  s.auto_title_source AS auto_title_source,
@@ -831,7 +831,7 @@ ${u+1}. ${p}`:`${u+1}. ${p}`}).join(`
831
831
  LEFT JOIN session_aliases sa ON sa.session_id = e.session_id
832
832
  LEFT JOIN projects p ON p.id = s.project_id
833
833
  WHERE e.thread_id = ?
834
- ORDER BY e.added_at ASC`).all(e).map(us),r=ds([e]).get(e);return{...lt(s,dt(s.id),r),edges:n}}function gs(e){return m().prepare(`SELECT t.* FROM threads t
834
+ ORDER BY e.added_at ASC`).all(e).map(ps),r=us([e]).get(e);return{...lt(s,dt(s.id),r),edges:n}}function _s(e){return m().prepare(`SELECT t.* FROM threads t
835
835
  JOIN thread_edges e ON e.thread_id = t.id
836
836
  WHERE e.session_id = ? AND t.archived = 0
837
837
  ORDER BY t.created_at DESC`).all(e).map(n=>lt(n,dt(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
@@ -842,35 +842,35 @@ ${u+1}. ${p}`:`${u+1}. ${p}`}).join(`
842
842
  role = excluded.role,
843
843
  confidence = excluded.confidence,
844
844
  source = excluded.source,
845
- added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,i,o,a,n),W(e.threadId);let c=ps(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 _s(e,t){let n=m().prepare("DELETE FROM thread_edges WHERE thread_id = ? AND session_id = ?").run(e,t);return n.changes>0&&W(e),{removed:n.changes}}function me(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,l=new Set;for(;c!==null;){if(c===t)throw new Error(`cycle detected: setting parent of ${t} to ${s} would create a loop`);if(l.has(c))break;l.add(c),c=a.get(e,c)?.parent_session_id??null}}let i=s?"child":"origin";n.prepare(`UPDATE thread_edges
845
+ added_at = excluded.added_at`).run(e.threadId,e.sessionId,r,i,o,a,n),Y(e.threadId);let c=ms(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 fs(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
846
846
  SET parent_session_id = ?, role = ?, added_at = ?
847
- WHERE thread_id = ? AND session_id = ?`).run(s,i,new Date().toISOString(),e,t),W(e);let o=ps(t);return us({...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 fs(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),W(e);let r=M(e);if(!r)throw new Error(`thread ${e} not found`);return r}function hs(e){m().prepare("UPDATE threads SET closed_at = ? WHERE id = ?").run(new Date().toISOString(),e),W(e);let s=M(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Es(e){m().prepare("UPDATE threads SET closed_at = NULL WHERE id = ?").run(e),W(e);let s=M(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Ts(e){m().prepare("UPDATE threads SET archived = 1 WHERE id = ?").run(e),W(e);let s=M(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Ss(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
847
+ WHERE thread_id = ? AND session_id = ?`).run(s,i,new Date().toISOString(),e,t),Y(e);let o=ms(t);return ps({...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 hs(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=P(e);if(!r)throw new Error(`thread ${e} not found`);return r}function Es(e){m().prepare("UPDATE threads SET closed_at = ? WHERE id = ?").run(new Date().toISOString(),e),Y(e);let s=P(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Ts(e){m().prepare("UPDATE threads SET closed_at = NULL WHERE id = ?").run(e),Y(e);let s=P(e);if(!s)throw new Error(`thread ${e} not found`);return s}function Ss(e){m().prepare("UPDATE threads SET archived = 1 WHERE id = ?").run(e),Y(e);let s=P(e);if(!s)throw new Error(`thread ${e} not found`);return s}function bs(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
848
848
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
849
849
  VALUES (?, ?, ?, ?, ?, ?, ?)
850
850
  ON CONFLICT(thread_id, session_id) DO UPDATE SET
851
851
  parent_session_id = COALESCE(thread_edges.parent_session_id, excluded.parent_session_id),
852
852
  role = CASE WHEN thread_edges.role = 'origin' OR excluded.role = 'origin' THEN 'origin' ELSE 'child' END,
853
853
  confidence = MAX(thread_edges.confidence, excluded.confidence),
854
- 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)})(),W(t),ms();let r=M(t);if(!r)throw new Error("merge destination disappeared");return r}function bs(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=m(),s=new Date().toISOString(),n=as();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
854
+ 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),gs();let r=P(t);if(!r)throw new Error("merge destination disappeared");return r}function ys(e){if(e.sessionIds.length===0)throw new Error("no sessions to split off");let t=m(),s=new Date().toISOString(),n=cs();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
855
855
  (thread_id, session_id, parent_session_id, role, confidence, source, added_at)
856
- 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))}})(),W(e.threadId),W(n);let r=M(n);if(!r)throw new Error("split destination disappeared");return r}ft();var po=50;function mo(e){if(e.length===0)return[];let t=[...e].sort((c,l)=>c.added_at<l.added_at?-1:c.added_at>l.added_at?1:0),s=new Map,n=new Set,r=[];for(let c of t)if(c.parent_session_id){let l=s.get(c.parent_session_id)??[];l.push(c),s.set(c.parent_session_id,l)}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 l=s.get(c.session_id)??[];for(let d of l)n.has(d.session_id)||a.push(d)}for(let c of t)n.has(c.session_id)||(n.add(c.session_id),r.push(c.session_id));return r}function go(e){let t=xs(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(`
856
+ 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=P(n);if(!r)throw new Error("split destination disappeared");return r}ft();var _o=50;function fo(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 ho(e){let t=Is(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(`
857
857
  `);return`${t}
858
- ${s}`}function _o(e){return Is(e)?.auto_title_source??null}async function fo(e){let{spawnClaudePrompt:t,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(ht(),js));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=ho(n.stdout);if(!r)throw new Error("claude CLI returned an empty title");return r.slice(0,po)}function ho(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 Hs(n)}}catch{}return Hs(t)}function Hs(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}async function Xs(e,t={}){let s=M(e);if(!s)throw new Error(`thread not found: ${e}`);let n=mo(s.edges),r=n.length,i=t.force??!1,o=t.signal,a=[],c=[],l=[];for(let d=0;d<n.length;d++){let u=n[d],p=d+1;if(o?.aborted){let _={sessionId:u,reason:"cancelled"};c.push(_),t.onSkipped?.(_);continue}if(!i&&_o(u)==="agent"){let _={sessionId:u,reason:"already-titled"};c.push(_),t.onSkipped?.(_);continue}let g;try{if(Bs)g=await Bs({sessionId:u,current:p,total:r});else{let _=go({sessionId:u,current:p,total:r});g=await fo({prompt:_,model:t.model})}}catch(_){let f=_ instanceof Error?_.message:String(_??"unknown error"),E={sessionId:u,error:f};l.push(E),t.onFailed?.(E);continue}try{_t(u,g,"agent")}catch(_){let f=_ instanceof Error?_.message:String(_??"unknown error"),E={sessionId:u,error:`setAutoTitle failed: ${f}`};l.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:l}}var Bs=null;R();import{execFile as ko}from"node:child_process";import{promisify as vo}from"node:util";import{readlink as Do,readFile as Ks}from"node:fs/promises";import{platform as De}from"node:os";import{readFileSync as Eo,statSync as To}from"node:fs";var So=200*1024*1024;var Tt=.5,Ys=Tt,bo=[{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"}],yo=["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 Ws(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 wo(e,t){let s=t-e;if(s<0)return{weight:0,label:null};for(let n of bo)if(s<=n.maxGapMs)return{weight:n.weight,label:n.label};return{weight:0,label:null}}function Ro(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 yo)if(i.includes(o))return{weight:t[n],matched:o,matchedIndex:n}}return{weight:0,matched:null,matchedIndex:-1}}function Et(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 No(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=Et(n,r);i>s&&(s=i)}return s}function Lo(e,t){let s=Et(e.mean_embedding,t.mean_embedding),n=Et(e.tail_pool,t.head_pool),r=No(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 Ao(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 Oo(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 xo(e,t){let s=Ws(e),n=Ws(t);return s&&n&&s===n?{weight:.1,brand:s}:{weight:0,brand:null}}function Gs(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function Io(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(`
859
- `).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=Gs(t.recent_user_messages[0]);if(i.length>=200)for(let o of e.authored_content){let a=Gs(o);if(a.length<200)continue;let c=Math.min(a.length,240),l=a.slice(0,c);if(i.includes(l)){s+=.4,r=!0;break}}}return{weight:Math.min(.6,s),pathMatch:n,contentMatch:r}}function Co(e,t,s=Ys){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=wo(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=Ro(i),a=Oo(e.touched_files,t.touched_files),c=xo(e.auto_title,t.auto_title),l=Lo(e,t),d=Ao(e,t),u=Io(e,t),p=r.weight+o.weight+a.weight+c.weight+l.weight+d.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})`),l.weight>0&&l.mode&&g.push(`semantic ${l.mode==="asymmetric"?"tail\u2192head":l.mode==="max_pool"?"best-chunk":"mean"} ${l.cosine.toFixed(2)} (+${l.weight.toFixed(2)})`),d.same&&g.push(`same cluster (+${d.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:l.weight,cluster:d.weight,doc_authorship:u.weight},reasons:g}}function zs(e,t=Ys){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=Co(a,r,t);c&&(!i||c.confidence>i.confidence)&&(i=c)}i&&s.push(i)}return s}function Js(e,t={}){let s=t.maxUserMessages??5,n=t.userMessageMaxLen??2e3,r=new Set,i=[],o=new Set,a=[],c;try{if(To(e).size>So)return{touched_files:r,recent_user_messages:i,authored_paths:o,authored_content:a};c=Eo(e,"utf8")}catch{return{touched_files:r,recent_user_messages:i,authored_paths:o,authored_content:a}}let l=0;for(;l<c.length;){let d=c.indexOf(`
860
- `,l),u=d===-1?c.length:d,p=c.slice(l,u);if(l=d===-1?c.length:d+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 b=E;if(b.type!=="tool_use")continue;let N=b.input??{},P=typeof N.file_path=="string"?N.file_path:null;if(P){let w=ve(P);w&&r.add(w)}if((b.name==="Write"||b.name==="Edit"||b.name==="MultiEdit")&&P){let w=ve(P);w&&o.add(w);let J=typeof N.content=="string"?N.content:typeof N.new_string=="string"?N.new_string:null;J&&J.length>=200&&a.push(J.length>4096?J.slice(0,4096):J)}if(b.name==="Bash"&&typeof N.command=="string")for(let w of N.command.matchAll(/(?:^|[\s'"`(=])((?:\.\.?\/|\/|src\/|test\/|docs\/|site\/)[A-Za-z0-9_./-]+)/g)){let J=ve(w[1]);J&&r.add(J)}if((b.name==="Glob"||b.name==="Grep")&&typeof N.pattern=="string"){let w=ve(N.pattern);w&&!w.includes("*")&&r.add(w)}}}return{touched_files:r,recent_user_messages:i,authored_paths:o,authored_content:a}}function ve(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}var bt=vo(ko),Mo=6,qs="Active ",Vs=" sessions \u2014 ",Fo=6e4;async function Po(){if(De()==="win32")return[];for(let t of["/bin/ps","/usr/bin/ps"])try{let{stdout:s}=await bt(t,["-eo","pid=,comm="],{timeout:2e3}),n=[];for(let r of s.split(`
861
- `)){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 Uo(e){let t=De();if(t==="linux")try{return(await Do(`/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 bt(s,["-a","-p",String(e),"-d","cwd","-Fn"],{timeout:2e3});for(let r of n.split(`
862
- `))if(r.startsWith("n"))return r.slice(1).replace(/\/+$/,"");return null}catch{continue}return null}async function $o(e){let t=De();if(t==="linux")try{let s=await Ks(`/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 Ks("/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 bt(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 jo(e,t){let s=await Po();if(s.length===0)return null;let n=e.replace(/\/+$/,""),r=[];for(let o of s){let a=await Uo(o);if(a&&(a===n||a.startsWith(n+"/"))){let c=await $o(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=Fo;for(let l of t){if(i.has(l.session_id)||!l.started_at)continue;let d=Date.parse(l.started_at);if(!Number.isFinite(d))continue;let u=Math.abs(d-o);u<c&&(c=u,a=l)}a&&i.add(a.session_id)}return i}function Ho(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 Bo(e,t){let s=m(),n=`${qs}${t}${Vs}%`,r=s.prepare(`SELECT t.id
858
+ ${s}`}function Eo(e){return Cs(e)?.auto_title_source??null}async function To(e){let{spawnClaudePrompt:t,isClaudeCliAvailable:s}=await Promise.resolve().then(()=>(ht(),Bs));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=So(n.stdout);if(!r)throw new Error("claude CLI returned an empty title");return r.slice(0,_o)}function So(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 Xs(n)}}catch{}return Xs(t)}function Xs(e){return e.trim().replace(/^["'`]+|["'`]+$/g,"").replace(/\s+/g," ").replace(/[.!?]+$/g,"").trim()}async function Gs(e,t={}){let s=P(e);if(!s)throw new Error(`thread not found: ${e}`);let n=fo(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&&Eo(u)==="agent"){let _={sessionId:u,reason:"already-titled"};c.push(_),t.onSkipped?.(_);continue}let g;try{if(Ws)g=await Ws({sessionId:u,current:p,total:r});else{let _=ho({sessionId:u,current:p,total:r});g=await To({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{_t(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 Ws=null;R();import{execFile as Mo}from"node:child_process";import{promisify as Fo}from"node:util";import{readlink as Po,readFile as Vs}from"node:fs/promises";import{platform as De}from"node:os";import{readFileSync as bo,statSync as yo}from"node:fs";var wo=200*1024*1024;var Tt=.5,Js=Tt,Ro=[{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"}],No=["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 Ys(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 Lo(e,t){let s=t-e;if(s<0)return{weight:0,label:null};for(let n of Ro)if(s<=n.maxGapMs)return{weight:n.weight,label:n.label};return{weight:0,label:null}}function Ao(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 No)if(i.includes(o))return{weight:t[n],matched:o,matchedIndex:n}}return{weight:0,matched:null,matchedIndex:-1}}function Et(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 Oo(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=Et(n,r);i>s&&(s=i)}return s}function xo(e,t){let s=Et(e.mean_embedding,t.mean_embedding),n=Et(e.tail_pool,t.head_pool),r=Oo(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 Io(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 Co(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 ko(e,t){let s=Ys(e),n=Ys(t);return s&&n&&s===n?{weight:.1,brand:s}:{weight:0,brand:null}}function zs(e){return e.replace(/\s+/g," ").trim().toLowerCase()}function vo(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(`
859
+ `).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=zs(t.recent_user_messages[0]);if(i.length>=200)for(let o of e.authored_content){let a=zs(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 Do(e,t,s=Js){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=Lo(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=Ao(i),a=Co(e.touched_files,t.touched_files),c=ko(e.auto_title,t.auto_title),d=xo(e,t),l=Io(e,t),u=vo(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 Ks(e,t=Js){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=Do(a,r,t);c&&(!i||c.confidence>i.confidence)&&(i=c)}i&&s.push(i)}return s}function qs(e,t={}){let s=t.maxUserMessages??5,n=t.userMessageMaxLen??2e3,r=new Set,i=[],o=new Set,a=[],c;try{if(yo(e).size>wo)return{touched_files:r,recent_user_messages:i,authored_paths:o,authored_content:a};c=bo(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(`
860
+ `,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 y=T.input??{},k=typeof y.file_path=="string"?y.file_path:null;if(k){let C=ve(k);C&&r.add(C)}if((T.name==="Write"||T.name==="Edit"||T.name==="MultiEdit")&&k){let C=ve(k);C&&o.add(C);let G=typeof y.content=="string"?y.content:typeof y.new_string=="string"?y.new_string:null;G&&G.length>=200&&a.push(G.length>4096?G.slice(0,4096):G)}if(T.name==="Bash"&&typeof y.command=="string")for(let C of y.command.matchAll(/(?:^|[\s'"`(=])((?:\.\.?\/|\/|src\/|test\/|docs\/|site\/)[A-Za-z0-9_./-]+)/g)){let G=ve(C[1]);G&&r.add(G)}if((T.name==="Glob"||T.name==="Grep")&&typeof y.pattern=="string"){let C=ve(y.pattern);C&&!C.includes("*")&&r.add(C)}}}return{touched_files:r,recent_user_messages:i,authored_paths:o,authored_content:a}}function ve(e){let t=e.trim().replace(/^['"]|['"]$/g,"");return!t||t.length<4||/[<>|;&\$`]/.test(t)?null:t}var bt=Fo(Mo),Uo=6,Zs="Active ",Qs=" sessions \u2014 ",$o=6e4;async function jo(){if(De()==="win32")return[];for(let t of["/bin/ps","/usr/bin/ps"])try{let{stdout:s}=await bt(t,["-eo","pid=,comm="],{timeout:2e3}),n=[];for(let r of s.split(`
861
+ `)){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 Ho(e){let t=De();if(t==="linux")try{return(await Po(`/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 bt(s,["-a","-p",String(e),"-d","cwd","-Fn"],{timeout:2e3});for(let r of n.split(`
862
+ `))if(r.startsWith("n"))return r.slice(1).replace(/\/+$/,"");return null}catch{continue}return null}async function Bo(e){let t=De();if(t==="linux")try{let s=await Vs(`/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 Vs("/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 bt(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 Xo(e,t){let s=await jo();if(s.length===0)return null;let n=e.replace(/\/+$/,""),r=[];for(let o of s){let a=await Ho(o);if(a&&(a===n||a.startsWith(n+"/"))){let c=await Bo(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=$o;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 Wo(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 Go(e,t){let s=m(),n=`${Zs}${t}${Qs}%`,r=s.prepare(`SELECT t.id
863
863
  FROM threads t
864
864
  WHERE t.archived = 0
865
865
  AND t.name LIKE ?
866
- ORDER BY t.created_at DESC`).all(n);for(let o of r){let a=M(o.id);if(a&&a.project===t)return a}let i=s.prepare(`SELECT DISTINCT te.thread_id AS id
866
+ ORDER BY t.created_at DESC`).all(n);for(let o of r){let a=P(o.id);if(a&&a.project===t)return a}let i=s.prepare(`SELECT DISTINCT te.thread_id AS id
867
867
  FROM thread_edges te
868
868
  JOIN sessions s ON s.id = te.session_id
869
869
  JOIN threads t ON t.id = te.thread_id
870
870
  WHERE s.project_id = ?
871
871
  AND t.archived = 0
872
872
  AND t.name LIKE ?
873
- LIMIT 1`).get(e,n);return i?M(i.id):null}function Zs(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 St(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,
873
+ LIMIT 1`).get(e,n);return i?P(i.id):null}function en(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 St(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,
874
874
  sa.alias AS alias,
875
875
  s.auto_title AS auto_title,
876
876
  s.first_user_message AS first_user_message,
@@ -890,29 +890,29 @@ ${s}`}function _o(e){return Is(e)?.auto_title_source??null}async function fo(e){
890
890
  FROM sessions s
891
891
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
892
892
  WHERE s.project_id = ?
893
- ORDER BY s.started_at ASC`).all(e)}function Xo(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=Js(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 Wo(e){let s=m().prepare(`SELECT session_id, parent_session_id, source
893
+ ORDER BY s.started_at ASC`).all(e)}function Yo(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=qs(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 zo(e){let s=m().prepare(`SELECT session_id, parent_session_id, source
894
894
  FROM thread_edges
895
- 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 yt(e,t={}){let s=Ho(e),n=t.windowHours??Mo,r=t.scoreThreshold??Tt,i=t.useLivePids??!0,o=[],a=[];if(i&&s.decoded_path){let f=St(e,0),E=await jo(s.decoded_path,f);if(E===null){let N=De()==="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(N),a=St(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(b=>E.has(b.session_id))}else a=St(e,n);a.length===0&&!o.length&&o.push(`No active sessions in ${s.name} within the last ${n}h.`);let c=Bo(e,s.name),l=new Set(c?c.edges.map(f=>f.session_id):[]),d=a.filter(f=>!l.has(f.session_id)),u=Xo(a);u.sort((f,E)=>f.started_at_ms-E.started_at_ms);let p=zs(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:`${qs}${s.name}${Vs}${Zs(t.todayIso)}`;return{project:s,thread:{id:c?.id??null,name:_,exists:!!c,existing_session_count:c?.edges.length??0},candidates:a,proposed_additions:d,proposed_edges:p,preserved_manual_edges:g,warnings:o}}function Qs(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=Ie({name:e.thread.name,summary:`Auto-captured by sync-active on ${Zs()}. 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=Wo(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{me(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
895
+ 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 yt(e,t={}){let s=Wo(e),n=t.windowHours??Uo,r=t.scoreThreshold??Tt,i=t.useLivePids??!0,o=[],a=[];if(i&&s.decoded_path){let f=St(e,0),E=await Xo(s.decoded_path,f);if(E===null){let y=De()==="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(y),a=St(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=St(e,n);a.length===0&&!o.length&&o.push(`No active sessions in ${s.name} within the last ${n}h.`);let c=Go(e,s.name),d=new Set(c?c.edges.map(f=>f.session_id):[]),l=a.filter(f=>!d.has(f.session_id)),u=Yo(a);u.sort((f,E)=>f.started_at_ms-E.started_at_ms);let p=Ks(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:`${Zs}${s.name}${Qs}${en(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 tn(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=Ie({name:e.thread.name,summary:`Auto-captured by sync-active on ${en()}. 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=zo(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
896
896
  WHERE thread_id = ?
897
897
  AND source = 'auto-active'
898
898
  AND parent_session_id IS NULL
899
- AND role = 'child'`).all(s);for(let o of i)try{me(s,o.session_id,null)}catch{}return t}function D(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function Go(e){return{content:[{type:"text",text:e}],isError:!0}}function F(e,t){return $.object(e).strict().parse(t)}var mn=$.string().uuid(),G=mn.describe("Thread id (UUID)."),Z=mn.describe("Session UUID (exact, not a prefix)."),en={include_archived:$.boolean().optional().describe("Include archived threads (default false).")},tn={id:G},sn={session_id:Z},nn={name:$.string().min(1).max(200).describe("Human-readable thread name."),summary:$.string().max(4e3).optional().describe("Optional short description."),origin_session_id:Z.optional().describe("Seed the thread with this session as its origin.")},rn={thread_id:G,session_id:Z,parent_session_id:Z.optional().describe("If present, the edge is role=child with this parent; otherwise role=origin."),role:$.enum(["origin","child"]).optional()},on={thread_id:G,session_id:Z,parent_session_id:Z.nullable().describe("New parent (null to clear and promote the edge back to role=origin).")},an={thread_id:G,session_id:Z},cn={thread_id:G,name:$.string().min(1).max(200)},ae={thread_id:G},ln={source_id:G.describe("Thread to dissolve \u2014 its edges move into dest_id."),dest_id:G.describe("Thread that absorbs source_id.")},dn={thread_id:G,session_ids:$.array(Z).min(1).max(500),new_thread_name:$.string().min(1).max(200)},un={thread_id:G,force:$.boolean().optional().describe("When true, regenerate titles for sessions that already have an agent-sourced title. Default false skips them.")},pn={project_id:$.number().int().positive().describe("Numeric project id from list_projects. The sync is repo-scoped and never crosses projects."),mode:$.enum(["preflight","apply"]).describe("preflight returns the proposed plan without writing; apply writes the thread + edges and is idempotent."),window_hours:$.number().min(.5).max(168).optional().describe("Rolling activity window in hours. Default 6.")};function gn(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:en},async t=>{try{let{include_archived:s}=F(en,t);return D(ut({includeArchived:s??!1}))}catch(s){return L(s)}}),e.registerTool("thread_get",{title:"Get thread with edges",description:"Full thread detail including every session edge (origin + children).",inputSchema:tn},async t=>{try{let{id:s}=F(tn,t),n=M(s);return n?D(n):Go(`thread not found: ${s}`)}catch(s){return L(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:sn},async t=>{try{let{session_id:s}=F(sn,t);return D(gs(s))}catch(s){return L(s)}})}function _n(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:nn},async n=>{try{let r=F(nn,n),i=await A({tool:"thread_create",args:r,limiter:s,run:()=>Ie({name:r.name,summary:r.summary??null,originSessionId:r.origin_session_id})});return D(i)}catch(r){return L(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:rn},async n=>{try{let r=F(rn,n),i=await A({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 D(i)}catch(r){return L(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:on},async n=>{try{let r=F(on,n),i=await A({tool:"thread_set_parent",args:r,limiter:s,run:()=>me(r.thread_id,r.session_id,r.parent_session_id)});return D(i)}catch(r){return L(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:an},async n=>{try{let r=F(an,n),i=await A({tool:"thread_remove_session",args:r,limiter:s,run:()=>_s(r.thread_id,r.session_id)});return D({thread_id:r.thread_id,session_id:r.session_id,...i})}catch(r){return L(r)}}),e.registerTool("thread_rename",{title:"Rename thread",description:"Change the display name of a thread.",inputSchema:cn},async n=>{try{let r=F(cn,n),i=await A({tool:"thread_rename",args:r,limiter:s,run:()=>fs(r.thread_id,r.name)});return D(i)}catch(r){return L(r)}}),e.registerTool("thread_close",{title:"Close thread",description:"Mark the thread as closed (sets closed_at). Thread remains listed; reopen to clear.",inputSchema:ae},async n=>{try{let r=F(ae,n),i=await A({tool:"thread_close",args:r,limiter:s,run:()=>hs(r.thread_id)});return D(i)}catch(r){return L(r)}}),e.registerTool("thread_reopen",{title:"Reopen thread",description:"Clear closed_at on a closed thread.",inputSchema:ae},async n=>{try{let r=F(ae,n),i=await A({tool:"thread_reopen",args:r,limiter:s,run:()=>Es(r.thread_id)});return D(i)}catch(r){return L(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:ae},async n=>{try{let r=F(ae,n),i=await A({tool:"thread_archive",args:r,limiter:s,run:()=>Ts(r.thread_id)});return D(i)}catch(r){return L(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:ln},async n=>{try{let r=F(ln,n),i=await A({tool:"thread_merge",args:r,limiter:s,run:()=>Ss(r.source_id,r.dest_id)});return D(i)}catch(r){return L(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:dn},async n=>{try{let r=F(dn,n),i=await A({tool:"thread_split",args:r,limiter:s,run:()=>bs({threadId:r.thread_id,sessionIds:r.session_ids,newThreadName:r.new_thread_name})});return D(i)}catch(r){return L(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:pn},async n=>{try{let r=F(pn,n);if(r.mode==="preflight"){let o=await yt(r.project_id,{windowHours:r.window_hours});return D({plan:o})}let i=await A({tool:"sync_active_sessions",args:r,limiter:s,run:async()=>{let o=await yt(r.project_id,{windowHours:r.window_hours}),a=Qs(o);return{plan:o,result:a}}});return D(i)}catch(r){return L(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:un},async n=>{try{let r=F(un,n),i=await A({tool:"generate_thread_titles",args:r,limiter:s,run:async()=>{let o=await Xs(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 D(i)}catch(r){return L(r)}})}import{Worker as Ko}from"node:worker_threads";import{join as qo}from"node:path";import{existsSync as Vo}from"node:fs";import{existsSync as Yo}from"node:fs";import{dirname as fn}from"node:path";import{fileURLToPath as zo}from"node:url";var Me=null;function hn(){if(Me)return Me;let e=fn(zo(import.meta.url));for(;!Yo(`${e}/package.json`);){let t=fn(e);if(t===e)throw new Error(`package.json not found walking up from ${import.meta.url}`);e=t}return Me=e,Me}var En="BAAI/bge-base-en-v1.5";function Zo(){return qo(hn(),"dist","daemon","embedder-worker.js")}var ce=null,fe=new Map,wt=0,te=!1,le=class extends Error{cause;constructor(t){let s=t instanceof Error?t.message:String(t);super(["Semantic search is unavailable on this platform.","",`Reason: ${s}`,"",`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(`
900
- `)),this.name="EmbedderUnavailableError",this.cause=t}};function Tn(e){for(let t of fe.values())t.reject(e);fe.clear()}function Qo(){if(ce)return ce;let e=Zo();if(!Vo(e))throw new le(new Error(`embedder-worker bundle not found at ${e}. Run \`npm run build:cli\` to emit it.`));let t=new Ko(e);return t.on("message",s=>{let n=fe.get(s.id);n&&(fe.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));Tn(n),ce=null,te=!1}),t.on("exit",s=>{s!==0&&(console.error(`[embedder-worker] exited with code ${s}`),Tn(new Error(`embedder worker exited with code ${s}`))),ce=null,te=!1}),ce=t,t}function Sn(e){return new Promise((t,s)=>{let n;try{n=Qo()}catch(r){s(r instanceof Error?r:new Error(String(r)));return}fe.set(e.id,{resolve:t,reject:s}),n.postMessage(e)})}function bn(){return wt=wt+1>>>0,String(wt)}function yn(e){if(!e.ok)throw e.unavailable?new le(new Error(e.error)):new Error(e.error);return e}async function Fe(){if(!(te&&ce))try{yn(await Sn({id:bn(),type:"load"})),te=!0}catch(e){throw te=!1,e}}function he(){return{loaded:te,modelId:En,dim:768}}async function wn(e){if(!te)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=yn(await Sn({id:bn(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}R();k();import{writeFileSync as ea}from"node:fs";import{join as ta}from"node:path";var sa=ta(T,"recall-events.json");function Rn(e,t,s,n="cli"){m().prepare(`
899
+ AND role = 'child'`).all(s);for(let o of i)try{ge(s,o.session_id,null)}catch{}return t}function F(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function Jo(e){return{content:[{type:"text",text:e}],isError:!0}}function U(e,t){return j.object(e).strict().parse(t)}var _n=j.string().uuid(),z=_n.describe("Thread id (UUID)."),Q=_n.describe("Session UUID (exact, not a prefix)."),sn={include_archived:j.boolean().optional().describe("Include archived threads (default false).")},nn={id:z},rn={session_id:Q},on={name:j.string().min(1).max(200).describe("Human-readable thread name."),summary:j.string().max(4e3).optional().describe("Optional short description."),origin_session_id:Q.optional().describe("Seed the thread with this session as its origin.")},an={thread_id:z,session_id:Q,parent_session_id:Q.optional().describe("If present, the edge is role=child with this parent; otherwise role=origin."),role:j.enum(["origin","child"]).optional()},cn={thread_id:z,session_id:Q,parent_session_id:Q.nullable().describe("New parent (null to clear and promote the edge back to role=origin).")},ln={thread_id:z,session_id:Q},dn={thread_id:z,name:j.string().min(1).max(200)},ce={thread_id:z},un={source_id:z.describe("Thread to dissolve \u2014 its edges move into dest_id."),dest_id:z.describe("Thread that absorbs source_id.")},pn={thread_id:z,session_ids:j.array(Q).min(1).max(500),new_thread_name:j.string().min(1).max(200)},mn={thread_id:z,force:j.boolean().optional().describe("When true, regenerate titles for sessions that already have an agent-sourced title. Default false skips them.")},gn={project_id:j.number().int().positive().describe("Numeric project id from list_projects. The sync is repo-scoped and never crosses projects."),mode:j.enum(["preflight","apply"]).describe("preflight returns the proposed plan without writing; apply writes the thread + edges and is idempotent."),window_hours:j.number().min(.5).max(168).optional().describe("Rolling activity window in hours. Default 6.")};function fn(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:sn},async t=>{try{let{include_archived:s}=U(sn,t);return F(ut({includeArchived:s??!1}))}catch(s){return N(s)}}),e.registerTool("thread_get",{title:"Get thread with edges",description:"Full thread detail including every session edge (origin + children).",inputSchema:nn},async t=>{try{let{id:s}=U(nn,t),n=P(s);return n?F(n):Jo(`thread not found: ${s}`)}catch(s){return N(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:rn},async t=>{try{let{session_id:s}=U(rn,t);return F(_s(s))}catch(s){return N(s)}})}function hn(e,t={}){let s=t.limiter??new V;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:on},async n=>{try{let r=U(on,n),i=await L({tool:"thread_create",args:r,limiter:s,run:()=>Ie({name:r.name,summary:r.summary??null,originSessionId:r.origin_session_id})});return F(i)}catch(r){return N(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:an},async n=>{try{let r=U(an,n),i=await L({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 F(i)}catch(r){return N(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:cn},async n=>{try{let r=U(cn,n),i=await L({tool:"thread_set_parent",args:r,limiter:s,run:()=>ge(r.thread_id,r.session_id,r.parent_session_id)});return F(i)}catch(r){return N(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:ln},async n=>{try{let r=U(ln,n),i=await L({tool:"thread_remove_session",args:r,limiter:s,run:()=>fs(r.thread_id,r.session_id)});return F({thread_id:r.thread_id,session_id:r.session_id,...i})}catch(r){return N(r)}}),e.registerTool("thread_rename",{title:"Rename thread",description:"Change the display name of a thread.",inputSchema:dn},async n=>{try{let r=U(dn,n),i=await L({tool:"thread_rename",args:r,limiter:s,run:()=>hs(r.thread_id,r.name)});return F(i)}catch(r){return N(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=U(ce,n),i=await L({tool:"thread_close",args:r,limiter:s,run:()=>Es(r.thread_id)});return F(i)}catch(r){return N(r)}}),e.registerTool("thread_reopen",{title:"Reopen thread",description:"Clear closed_at on a closed thread.",inputSchema:ce},async n=>{try{let r=U(ce,n),i=await L({tool:"thread_reopen",args:r,limiter:s,run:()=>Ts(r.thread_id)});return F(i)}catch(r){return N(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=U(ce,n),i=await L({tool:"thread_archive",args:r,limiter:s,run:()=>Ss(r.thread_id)});return F(i)}catch(r){return N(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:un},async n=>{try{let r=U(un,n),i=await L({tool:"thread_merge",args:r,limiter:s,run:()=>bs(r.source_id,r.dest_id)});return F(i)}catch(r){return N(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:pn},async n=>{try{let r=U(pn,n),i=await L({tool:"thread_split",args:r,limiter:s,run:()=>ys({threadId:r.thread_id,sessionIds:r.session_ids,newThreadName:r.new_thread_name})});return F(i)}catch(r){return N(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:gn},async n=>{try{let r=U(gn,n);if(r.mode==="preflight"){let o=await yt(r.project_id,{windowHours:r.window_hours});return F({plan:o})}let i=await L({tool:"sync_active_sessions",args:r,limiter:s,run:async()=>{let o=await yt(r.project_id,{windowHours:r.window_hours}),a=tn(o);return{plan:o,result:a}}});return F(i)}catch(r){return N(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:mn},async n=>{try{let r=U(mn,n),i=await L({tool:"generate_thread_titles",args:r,limiter:s,run:async()=>{let o=await Gs(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 N(r)}})}import{Worker as Zo}from"node:worker_threads";import{join as Qo}from"node:path";import{existsSync as ea}from"node:fs";import{existsSync as Ko}from"node:fs";import{dirname as En}from"node:path";import{fileURLToPath as qo}from"node:url";var Me=null;function Tn(){if(Me)return Me;let e=En(qo(import.meta.url));for(;!Ko(`${e}/package.json`);){let t=En(e);if(t===e)throw new Error(`package.json not found walking up from ${import.meta.url}`);e=t}return Me=e,Me}var Sn="BAAI/bge-base-en-v1.5";function ta(){return Qo(Tn(),"dist","daemon","embedder-worker.js")}var le=null,he=new Map,wt=0,se=!1,de=class extends Error{kind;path;underlying;cause;constructor(t){super(sa(t)),this.name="EmbedderUnavailableError",this.kind=t.kind,this.path=t.path,this.underlying=t.underlying,this.cause=t}};function sa(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(`
900
+ `)}function bn(e){for(let t of he.values())t.reject(e);he.clear()}function na(){if(le)return le;let e=ta();if(!ea(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 Zo(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));bn(n),le=null,se=!1}),t.on("exit",s=>{s!==0&&(console.error(`[embedder-worker] exited with code ${s}`),bn(new Error(`embedder worker exited with code ${s}`))),le=null,se=!1}),le=t,t}function yn(e){return new Promise((t,s)=>{let n;try{n=na()}catch(r){s(r instanceof Error?r:new Error(String(r)));return}he.set(e.id,{resolve:t,reject:s}),n.postMessage(e)})}function wn(){return wt=wt+1>>>0,String(wt)}function Rn(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 Fe(){if(!(se&&le))try{Rn(await yn({id:wn(),type:"load"})),se=!0}catch(e){throw se=!1,e}}function Ee(){return{loaded:se,modelId:Sn,dim:768}}async function Nn(e){if(!se)throw new Error("[embedder] Model not loaded. Call loadEmbedder() before embedding.");let t=Rn(await yn({id:wn(),type:"embedQuery",text:e}));return new Float32Array(t.embedding)}R();v();import{writeFileSync as ra}from"node:fs";import{join as ia}from"node:path";var oa=ia(S,"recall-events.json");function Ln(e,t,s,n="cli"){m().prepare(`
901
901
  INSERT INTO recall_events (session_id, recalled_at, token_count, mode, caller)
902
902
  VALUES (?, datetime('now'), ?, ?, ?)
903
- `).run(e,t,s,n),na()}function na(){O();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)+`
904
- `,"utf-8")}R();async function Nn(e,t=50){let s=await wn(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
903
+ `).run(e,t,s,n),aa()}function aa(){O();let t=m().prepare("SELECT id, session_id, recalled_at, token_count, mode, caller FROM recall_events ORDER BY recalled_at DESC").all();ra(oa,JSON.stringify(t,null,2)+`
904
+ `,"utf-8")}R();async function Rt(e,t=50){let s=await Nn(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
905
905
  FROM vec_chunks v JOIN chunk_meta cm ON cm.rowid = v.rowid
906
- 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 Ln(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
907
- WHERE v.embedding MATCH ? AND k = ? ORDER BY v.distance`).all(i.embedding,t*5),a=new Map;for(let l of o){if(l.session_id===e)continue;let d=a.get(l.session_id);(d===void 0||l.distance<d)&&a.set(l.session_id,l.distance)}let c=[];for(let[l,d]of a){let u=1-d;u>=s&&c.push({sessionId:l,similarity:u})}return c.sort((l,d)=>d.similarity-l.similarity),c.slice(0,t)}function ra(){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 An(e){let t=ra(),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 ia=!1,oa=null;var aa=new Set;function ca(){return m().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}function On(){return{running:ia,queueDepth:ca(),lastProcessedAt:oa,blacklistedCount:aa.size}}import{existsSync as la,mkdirSync as Du,rmSync as Mu,createWriteStream as Fu,statSync as Pu}from"node:fs";import{join as xn}from"node:path";k();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 ua(){return xn(T,"models","BAAI","bge-base-en-v1.5")}function Ee(){let e=ua();return da.every(t=>la(xn(e,t.path)))}k();import{existsSync as pa,readFileSync as ma,writeFileSync as Bu,unlinkSync as Xu}from"node:fs";import{join as ga}from"node:path";var In=ga(T,"license.json");function Cn(){if(!pa(In))return null;try{let e=ma(In,"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 fa}from"jose";var kn=`-----BEGIN PUBLIC KEY-----
906
+ 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 An(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
907
+ 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 ca(){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 On(e){let t=ca(),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 la=!1,da=null;var ua=new Set;function pa(){return m().prepare("SELECT COUNT(*) AS n FROM chunk_queue").get().n}function xn(){return{running:la,queueDepth:pa(),lastProcessedAt:da,blacklistedCount:ua.size}}import{existsSync as ma,mkdirSync as Pu,rmSync as Uu,createWriteStream as $u,statSync as ju}from"node:fs";import{join as In}from"node:path";v();var ga=[{path:"config.json",sha256:"bc00af31a4a31b74040d73370aa83b62da34c90b75eb77bfa7db039d90abd591"},{path:"tokenizer.json",sha256:"d241a60d5e8f04cc1b2b3e9ef7a4921b27bf526d9f6050ab90f9267a1f9e5c66"},{path:"tokenizer_config.json",sha256:"9261e7d79b44c8195c1cada2b453e55b00aeb81e907a6664974b4d7776172ab3"},{path:"onnx/model.onnx",sha256:"9bc579acdba21c253c62a9bf866891355a63ffa3442b52c8a37d75b2ccb91848"}];function _a(){return In(S,"models","BAAI","bge-base-en-v1.5")}function Te(){let e=_a();return ga.every(t=>ma(In(e,t.path)))}v();import{existsSync as fa,readFileSync as ha,writeFileSync as Gu,unlinkSync as Yu}from"node:fs";import{join as Ea}from"node:path";var Cn=Ea(S,"license.json");function kn(){if(!fa(Cn))return null;try{let e=ha(Cn,"utf8"),t=JSON.parse(e);return typeof t.license_jwt!="string"||t.license_jwt.length===0?null:t}catch{return null}}import{jwtVerify as Ta,importSPKI as Sa}from"jose";var vn=`-----BEGIN PUBLIC KEY-----
908
908
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZysO2FffTLdyxQnTmnt78/ayvqz9
909
909
  kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
910
910
  -----END PUBLIC KEY-----
911
- `,Rt="ES256",vn="clauderecall.com",Dn="clauderecall-cli";var Pe=null;async function ha(){return Pe||(Pe=await fa(kn,Rt),Pe)}async function Mn(e){try{let t=await ha(),{payload:s}=await _a(e,t,{issuer:vn,audience:Dn,algorithms:[Rt]});return{valid:!0,claims:s}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}import{createHash as Ea}from"node:crypto";import{hostname as Ta,userInfo as Sa,platform as ba,arch as ya}from"node:os";function Fn(){let e="unknown";try{e=Sa().username}catch{}let t=[Ta(),e,ba(),ya()];return Ea("sha256").update(t.join("\0")).digest("hex")}k();import{existsSync as wa,readFileSync as Ra,writeFileSync as np}from"node:fs";import{join as Na}from"node:path";var Pn=Na(T,"license-check.json"),ap=1440*60*1e3,La=720*60*60*1e3;function Aa(){if(!wa(Pn))return null;try{let e=JSON.parse(Ra(Pn,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function Un(e){let t=Aa();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()>La?{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 Oa=Date.UTC(2026,5,1,7,0,0);var xa=1440*60*1e3,up=60*xa;async function Ue(){let e=Cn();if(!e)return{tier:"free"};let t=await Mn(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==Fn())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let s=Un(e.license_key);return s?.revoked?{tier:"free",invalid_reason:s.reason}:Ia(e,t.claims)}function Ia(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();k();import{join as Nt}from"node:path";var Ca=new Set(["pending","approved","rejected"]),ka=new Set(["L1","L2","L3","L4","user"]),Lp=Nt(T,"links"),va=Nt(T,"suggestions"),Ap=Nt(va,"index.json");function $n(e){try{return JSON.parse(e)}catch{return e}}function Da(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:$n(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function Ma(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:$n(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function Fa(e){if(!ka.has(e))throw new Error(`invalid inferred_by: ${e}`)}function Lt(e){return m().prepare(`SELECT * FROM session_links
911
+ `,Nt="ES256",Dn="clauderecall.com",Mn="clauderecall-cli";var Pe=null;async function ba(){return Pe||(Pe=await Sa(vn,Nt),Pe)}async function Fn(e){try{let t=await ba(),{payload:s}=await Ta(e,t,{issuer:Dn,audience:Mn,algorithms:[Nt]});return{valid:!0,claims:s}}catch(t){return{valid:!1,reason:t instanceof Error?t.message:"verification failed"}}}import{createHash as ya}from"node:crypto";import{hostname as wa,userInfo as Ra,platform as Na,arch as La}from"node:os";function Pn(){let e="unknown";try{e=Ra().username}catch{}let t=[wa(),e,Na(),La()];return ya("sha256").update(t.join("\0")).digest("hex")}v();import{existsSync as Aa,readFileSync as Oa,writeFileSync as op}from"node:fs";import{join as xa}from"node:path";var Un=xa(S,"license-check.json"),dp=1440*60*1e3,Ia=720*60*60*1e3;function Ca(){if(!Aa(Un))return null;try{let e=JSON.parse(Oa(Un,"utf8"));return typeof e.license_key!="string"||typeof e.last_checked_at!="string"||typeof e.revoked!="boolean"?null:e}catch{return null}}function $n(e){let t=Ca();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()>Ia?{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 ka=Date.UTC(2026,5,1,7,0,0);var va=1440*60*1e3,gp=60*va;async function Ue(){let e=kn();if(!e)return{tier:"free"};let t=await Fn(e.license_jwt);if(!t.valid||!t.claims)return{tier:"free",invalid_reason:t.reason};if(t.claims.machine_fp&&t.claims.machine_fp!==Pn())return{tier:"free",invalid_reason:"machine fingerprint mismatch \u2014 re-activate on this device"};let s=$n(e.license_key);return s?.revoked?{tier:"free",invalid_reason:s.reason}:Da(e,t.claims)}function Da(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();v();import{join as Lt}from"node:path";var Ma=new Set(["pending","approved","rejected"]),Fa=new Set(["L1","L2","L3","L4","user"]),xp=Lt(S,"links"),Pa=Lt(S,"suggestions"),Ip=Lt(Pa,"index.json");function jn(e){try{return JSON.parse(e)}catch{return e}}function Ua(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:jn(e.evidence),approved:e.approved===1,created_at:e.created_at,updated_at:e.updated_at}}function $a(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:jn(e.evidence),status:e.status,inferred_by:e.inferred_by,created_at:e.created_at,decided_at:e.decided_at}}function ja(e){if(!Fa.has(e))throw new Error(`invalid inferred_by: ${e}`)}function At(e){return m().prepare(`SELECT * FROM session_links
912
912
  WHERE source_session_id = ? OR target_session_id = ?
913
- ORDER BY confidence DESC, updated_at DESC`).all(e,e).map(Da)}function At(e={}){let t=m(),s=[],n=[];if(e.status){if(!Ca.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&&(Fa(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(Ua)}function Ot(e={}){let t=m(),s=[],n=[];if(e.status){if(!Ma.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&&(ja(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}
914
914
  ORDER BY confidence DESC, created_at DESC
915
- LIMIT ?`).all(...n,i).map(Ma)}var Pa=4e3,Ua=2,$a=30,ja=.2,Ha={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};function $e(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/$a);return Math.max(ja,t)}function Hn(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 Bn(e){return m().prepare(`SELECT s.id,
915
+ LIMIT ?`).all(...n,i).map($a)}var Ha=4e3,Ba=2,Xa=30,Wa=.2,Ga={citation:1,wiki_link:.9,similar:.7,skill_track:.5,bug_pattern:.4,temporal_proximity:.3};function $e(e){return e?Math.ceil(e.length/4):0}function Hn(e){if(!Number.isFinite(e)||e<0)return 1;let t=Math.exp(-e/Xa);return Math.max(Wa,t)}function Bn(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 Xn(e){return m().prepare(`SELECT s.id,
916
916
  NULLIF(sa.alias, '') AS alias,
917
917
  s.auto_title,
918
918
  s.auto_title_source,
@@ -923,27 +923,27 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
923
923
  FROM sessions s
924
924
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
925
925
  LEFT JOIN projects p ON p.id = s.project_id
926
- WHERE s.id = ?`).get(e)??null}function Xn(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 Wn(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 Ba(e){let s=m().prepare(`SELECT id, auto_title, started_at
926
+ WHERE s.id = ?`).get(e)??null}function Wn(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 Gn(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 Ya(e){let s=m().prepare(`SELECT id, auto_title, started_at
927
927
  FROM sessions
928
928
  WHERE project_id = ?
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(),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(),l=new Map;c.forEach((p,g)=>l.set(p,g));let d=new Map,u=new Map;for(let p of i){if(!p.brand||!p.skill)continue;let g=a.get(p.brand),_=l.get(p.skill);if(g===void 0||_===void 0)continue;let f=`${g}.${_}`,E=(d.get(f)??0)+1;d.set(f,E),u.set(p.id,`${g}.${_}.${E}`)}return{byId:u}}function Xa(e){return{table:e!==null?Ba(e):null,originProjectId:e,cache:new Map}}function je(e,t){let s=e.cache.get(t);if(s)return s;let n=Bn(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:Wn(n),decimal:r,summary:Xn(n.id),project:n.project,started_at:n.started_at};return e.cache.set(t,i),i}function Wa(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(),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 za(e){return{table:e!==null?Ya(e):null,originProjectId:e,cache:new Map}}function je(e,t){let s=e.cache.get(t);if(s)return s;let n=Xn(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:Gn(n),decimal:r,summary:Wn(n.id),project:n.project,started_at:n.started_at};return e.cache.set(t,i),i}function Ja(e,t){let n=m().prepare(`SELECT DISTINCT te.parent_session_id AS pid
930
930
  FROM thread_edges te
931
931
  WHERE te.session_id = ?
932
- AND te.parent_session_id IS NOT NULL`).all(t),r=[];for(let i of n){if(!i.pid)continue;let o=je(e,i.pid);o&&r.push(o)}return r}function Ga(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=je(e,i.pid);o&&r.push(o)}return r}function Ka(e,t){let n=m().prepare(`SELECT DISTINCT te.session_id AS sid
933
933
  FROM thread_edges te
934
- WHERE te.parent_session_id = ?`).all(t),r=[];for(let i of n){if(!i.sid)continue;let o=je(e,i.sid);o&&r.push(o)}return r}function Gn(e){let t=Ha[e.linkType]??.5,s=de(e.confidence),n=t*s,r=jn(e.daysApart),i=e.embeddingCosine??.5,o=de(e.pagerank);if(e.scoring==="pagerank")return de(o);if(e.scoring==="embedding-rerank")return e.embeddingCosine===null?de(n):de(i);let a=.35*n+.2*r+.2*i+.25*o;return de(a)}function de(e){return!Number.isFinite(e)||e<0?0:e>1?1:e}function Ya(e,t,s,n,r){let i=new Map;function o(a,c){if(a===c)return;let l=i.get(a);l||(l=new Set,i.set(a,l)),l.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 l=1;l<r;l++){let d=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 _=Lt(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),d.add(g)}}if(d.size===0)break;for(let u of d)a.add(u)}}return{edges:i}}function za(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(l=>[l,i]));for(let l=0;l<s;l++){let d=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)d.set(_,(d.get(_)??0)+n*g)}o=d}let a=0;for(let l of o.values())l>a&&(a=l);if(a<=0)return o;let c=new Map;for(let[l,d]of o)c.set(l,d/a);return c}var Yn=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 Ja(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,Yn);return`${r}
935
- ${i}`}return r}function Ka(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+=$e(a),e.summary){let c=zn(e.summary,Yn*4);n.push(c),i+=$e(c)}n.push("");for(let c of t){if(c.refs.length===0)continue;let l=`## ${c.heading}`,d=$e(l),u=[],p=0;for(let g of c.refs){let _=Ja(g),f=$e(_);if(i+d+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(l);for(let g of u)n.push(g);n.push(""),i+=d+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=je(e,i.sid);o&&r.push(o)}return r}function Yn(e){let t=Ga[e.linkType]??.5,s=ue(e.confidence),n=t*s,r=Hn(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 qa(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 _=At(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 Va(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 zn=240;function Jn(e,t){let s=e.replace(/\s+/g," ").trim();return s.length<=t?s:`${s.slice(0,t-1).trimEnd()}\u2026`}function Za(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=Jn(e.summary,zn);return`${r}
935
+ ${i}`}return r}function Qa(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+=$e(a),e.summary){let c=Jn(e.summary,zn*4);n.push(c),i+=$e(c)}n.push("");for(let c of t){if(c.refs.length===0)continue;let d=`## ${c.heading}`,l=$e(d),u=[],p=0;for(let g of c.refs){let _=Za(g),f=$e(_);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(`
936
936
  `)+`
937
- `,budgetUsed:i,truncated:r}}function qa(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 l=je(e,c);if(!l)continue;let d=Hn(t.started_at,l.started_at),u=Gn({confidence:a.confidence,linkType:a.link_type,daysApart:d,embeddingCosine:null,pagerank:i.get(c)??0,scoring:r});o.push({...l,score:u,evidence:`(suggestion, ${a.inferred_by}) confidence=${a.confidence.toFixed(2)} ${Math.round(d)}d apart`,link_type:a.link_type})}return o}function Jn(e,t={}){let s=Math.max(100,Math.floor(t.budget??Pa)),n=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??Ua)),i=t.includeWikiLinks??!0,o=t.includeSuggestions??!1,a=t.edgeTypes?new Set(t.edgeTypes):null,c=Bn(e);if(!c)throw new Error(`session not found: ${e}`);let l=Xa(c.project_id),d={session_id:c.id,title:Wn(c),decimal:l.table?.byId.get(c.id)??null,summary:Xn(c.id),project:c.project,started_at:c.started_at};l.cache.set(c.id,d);let u=Wa(l,e),p=Ga(l,e),g=Lt(e).filter(x=>x.approved).filter(x=>!a||a.has(x.link_type)).filter(x=>i||x.link_type!=="wiki_link"),_=Ya(e,g,u,p,r),f=za(_),E=[],b=[],N=[],P=[];for(let x of g){let ne=x.source_session_id===e?x.target_session_id:x.source_session_id,ue=je(l,ne);if(!ue)continue;let re=Hn(d.started_at,ue.started_at),He=Gn({confidence:x.confidence,linkType:x.link_type,daysApart:re,embeddingCosine:null,pagerank:f.get(ne)??0,scoring:n}),Be=jn(re),j=`${x.link_type} confidence=${x.confidence.toFixed(2)} recency=${Be.toFixed(2)} (${Math.round(re)}d apart)`,be={...ue,score:He,evidence:j,link_type:x.link_type};x.link_type==="citation"?E.push(be):x.link_type==="similar"?b.push(be):x.link_type==="wiki_link"?P.push(be):N.push(be)}if(o){let x=At({sourceSessionId:e,status:"pending",limit:100}),ne=At({targetSessionId:e,status:"pending",limit:100}),ue=[...x,...ne],re=new Set,He=ue.filter(j=>re.has(j.id)?!1:(re.add(j.id),!0)),Be=qa(l,d,He,a,n,f);for(let j of Be)j.link_type==="citation"?E.push(j):j.link_type==="similar"?b.push(j):j.link_type==="wiki_link"?P.push(j):N.push(j)}let w=(x,ne)=>ne.score-x.score;E.sort(w),b.sort(w),N.sort(w),P.sort(w);let Se=Ka(d,[{heading:"Parents",refs:u},{heading:"Children",refs:p},{heading:"Citations (approved)",refs:E},{heading:"Similar sessions",refs:b},{heading:"Cousins (skill track + temporal)",refs:N},{heading:"Wiki links (manual)",refs:P}],s);return{origin:d,parents:u,children:p,citations:E,similar:b,cousins:N,wikiLinks:P,bundle:Se.bundle,budgetUsed:Se.budgetUsed,budgetRemaining:Math.max(0,s-Se.budgetUsed),truncated:Se.truncated}}import{readFileSync as Mc}from"node:fs";import{dirname as Fc,join as Pc}from"node:path";var Uc=(()=>{try{let e=Fc(Dc(import.meta.url));return Pc(e,"..","..","package.json")}catch{return"package.json"}})(),$c=(()=>{try{return JSON.parse(Mc(Uc,"utf8")).version??"0.0.0"}catch{return"0.0.0"}})();function jc(){let e=process.env.RECALL_MCP_ALLOW_WRITES;return e==="1"||e==="true"}function C(e){return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}function Ut(e){return{content:[{type:"text",text:e}]}}function X(e){return{content:[{type:"text",text:e}],isError:!0}}function Cr(e){return e instanceof le?"embedder not available \u2014 run `recall semantic install`":"embedder load failed \u2014 see daemon logs for details"}async function Hc(){try{if((await Ue()).tier!=="pro"||!Ee())return;await Fe(),process.stderr.write(`[mcp] embedder loaded
938
- `)}catch(e){process.stderr.write(`[mcp] embedder preload failed: ${Cr(e)}
939
- `)}}async function Bc(){if((await Ue()).tier!=="pro")return C({upgrade_required:!0,reason:"Semantic vector search requires Pro.",buy_url:"https://clauderecall.com/pro"});if(!Ee())return C({error:"embedder_model_missing",reason:"Run `recall semantic install` to download the embedding model."});if(!he().loaded)try{await Fe()}catch(t){return C({error:"embedder_load_failed",reason:Cr(t)})}return null}async function Xc(){try{return(await Ue()).tier!=="pro"||!Ee()?!1:(he().loaded||await Fe(),!0)}catch{return!1}}function Wc(){let e=new kc({name:"claude-recall",version:$c}),t=jc();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 ec(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=je(e,c);if(!d)continue;let l=Bn(t.started_at,d.started_at),u=Yn({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 Kn(e,t={}){let s=Math.max(100,Math.floor(t.budget??Ha)),n=t.scoring??"hybrid",r=Math.max(1,Math.min(5,t.maxDepth??Ba)),i=t.includeWikiLinks??!0,o=t.includeSuggestions??!1,a=t.edgeTypes?new Set(t.edgeTypes):null,c=Xn(e);if(!c)throw new Error(`session not found: ${e}`);let d=za(c.project_id),l={session_id:c.id,title:Gn(c),decimal:d.table?.byId.get(c.id)??null,summary:Wn(c.id),project:c.project,started_at:c.started_at};d.cache.set(c.id,l);let u=Ja(d,e),p=Ka(d,e),g=At(e).filter(x=>x.approved).filter(x=>!a||a.has(x.link_type)).filter(x=>i||x.link_type!=="wiki_link"),_=qa(e,g,u,p,r),f=Va(_),E=[],T=[],y=[],k=[];for(let x of g){let re=x.source_session_id===e?x.target_session_id:x.source_session_id,pe=je(d,re);if(!pe)continue;let ie=Bn(l.started_at,pe.started_at),He=Yn({confidence:x.confidence,linkType:x.link_type,daysApart:ie,embeddingCosine:null,pagerank:f.get(re)??0,scoring:n}),Be=Hn(ie),H=`${x.link_type} confidence=${x.confidence.toFixed(2)} recency=${Be.toFixed(2)} (${Math.round(ie)}d apart)`,be={...pe,score:He,evidence:H,link_type:x.link_type};x.link_type==="citation"?E.push(be):x.link_type==="similar"?T.push(be):x.link_type==="wiki_link"?k.push(be):y.push(be)}if(o){let x=Ot({sourceSessionId:e,status:"pending",limit:100}),re=Ot({targetSessionId:e,status:"pending",limit:100}),pe=[...x,...re],ie=new Set,He=pe.filter(H=>ie.has(H.id)?!1:(ie.add(H.id),!0)),Be=ec(d,l,He,a,n,f);for(let H of Be)H.link_type==="citation"?E.push(H):H.link_type==="similar"?T.push(H):H.link_type==="wiki_link"?k.push(H):y.push(H)}let C=(x,re)=>re.score-x.score;E.sort(C),T.sort(C),y.sort(C),k.sort(C);let M=Qa(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:y},{heading:"Wiki links (manual)",refs:k}],s);return{origin:l,parents:u,children:p,citations:E,similar:T,cousins:y,wikiLinks:k,bundle:M.bundle,budgetUsed:M.budgetUsed,budgetRemaining:Math.max(0,s-M.budgetUsed),truncated:M.truncated}}import{readFileSync as $c}from"node:fs";import{dirname as jc,join as Hc}from"node:path";var Bc=(()=>{try{let e=jc(Uc(import.meta.url));return Hc(e,"..","..","package.json")}catch{return"package.json"}})(),Xc=(()=>{try{return JSON.parse($c(Bc,"utf8")).version??"0.0.0"}catch{return"0.0.0"}})();function Wc(){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 $t(e){return{content:[{type:"text",text:e}]}}function W(e){return{content:[{type:"text",text:e}],isError:!0}}function Dr(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 Gc(){try{if((await Ue()).tier!=="pro"||!Te())return;await Fe(),process.stderr.write(`[mcp] embedder loaded
938
+ `)}catch(e){process.stderr.write(`[mcp] embedder preload failed: ${Dr(e)}
939
+ `)}}async function Yc(){if((await Ue()).tier!=="pro")return A({upgrade_required:!0,reason:"Semantic vector search requires Pro.",buy_url:"https://clauderecall.com/pro"});if(!Te())return A({error:"embedder_model_missing",reason:"Run `recall semantic install` to download the embedding model."});if(!Ee().loaded)try{await Fe()}catch(t){return A({error:"embedder_load_failed",reason:Dr(t)})}return null}async function vr(){try{return(await Ue()).tier!=="pro"||!Te()?!1:(Ee().loaded||await Fe(),!0)}catch{return!1}}function zc(){let e=new Fc({name:"claude-recall",version:Xc}),t=Wc();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,
940
940
  COUNT(s.id) AS session_count,
941
941
  COALESCE(SUM(s.message_count), 0) AS message_count,
942
942
  MAX(s.started_at) AS latest
943
943
  FROM projects p
944
944
  LEFT JOIN sessions s ON s.project_id = p.id
945
945
  GROUP BY p.id
946
- ORDER BY MAX(COALESCE(s.started_at, '')) DESC`).all();return C(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:S.string().optional().describe("Substring match against project name or decoded filesystem path."),tag:S.string().optional().describe("Only sessions carrying this tag (leading # optional)."),since:S.string().optional().describe("Only sessions started at or after this ISO timestamp or YYYY-MM-DD date."),until:S.string().optional().describe("Only sessions started at or before this ISO timestamp or YYYY-MM-DD date."),limit:S.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},l="s.message_count > 2";if(s&&(l+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",c.proj=`%${s}%`),r&&(l+=" AND s.started_at >= @since",c.since=r),i&&(l+=" AND s.started_at <= @until",c.until=/^\d{4}-\d{2}-\d{2}$/.test(i)?`${i}T23:59:59.999Z`:i),n){let p=Q(n);p&&(l+=" 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:b.string().optional().describe("Substring match against project name or decoded filesystem path."),tag:b.string().optional().describe("Only sessions carrying this tag (leading # optional)."),since:b.string().optional().describe("Only sessions started at or after this ISO timestamp or YYYY-MM-DD date."),until:b.string().optional().describe("Only sessions started at or before this ISO timestamp or YYYY-MM-DD date."),limit:b.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=ee(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,
947
947
  s.message_count, s.first_user_message, s.git_branch,
948
948
  NULLIF(sa.alias, '') AS alias,
949
949
  CASE WHEN sn.content IS NOT NULL AND sn.content != '' THEN 1 ELSE 0 END AS has_notes,
@@ -956,9 +956,9 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
956
956
  JOIN projects p ON p.id = s.project_id
957
957
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
958
958
  LEFT JOIN session_notes sn ON sn.session_id = s.id
959
- WHERE ${l}
959
+ WHERE ${d}
960
960
  ORDER BY COALESCE(s.started_at, '') DESC
961
- LIMIT @limit`).all(c).map(({tags_csv:p,...g})=>({...g,tags:p?p.split(","):[]}));return C(u)}),e.registerTool("list_tags",{title:"List tags",description:"Every tag currently applied to a session, with its count, most popular first.",inputSchema:{}},async()=>C(Kt())),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.",inputSchema:{query:S.string().describe("Text to find. Supports inline `#tag-name` tokens to narrow to sessions with the tag."),project:S.string().optional().describe("Substring match against project name or path."),limit:S.number().int().min(1).max(200).optional()}},async({query:s,project:n,limit:r})=>{let i=m(),o=s.trim();if(!o)return C({query:"",hits:[],tags:[]});let a=o.split(/\s+/).filter(Boolean),c=a.filter(f=>f.startsWith("#")).map(Q).filter(f=>!!f),d=a.filter(f=>!f.startsWith("#")).map(f=>`"${f.replace(/"/g,"")}"`).join(" "),u=Math.max(1,Math.min(200,r??30));if(!d&&c.length>0){let f=`
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(qt())),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:b.string().describe("Text to find. Supports inline `#tag-name` tokens to narrow to sessions with the tag."),project:b.string().optional().describe("Substring match against project name or path."),limit:b.number().int().min(1).max(200).optional(),mode:b.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(ee).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=`
962
962
  SELECT s.id AS session_id,
963
963
  s.id AS message_uuid,
964
964
  p.name AS project,
@@ -971,7 +971,7 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
971
971
  JOIN projects p ON p.id = s.project_id
972
972
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
973
973
  WHERE 1=1
974
- `,E={limit:u};return n&&(f+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",E.proj=`%${n}%`),c.forEach((b,N)=>{f+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${N})`,E[`tag_${N}`]=b}),f+=" ORDER BY COALESCE(s.started_at, '') DESC LIMIT @limit",C({query:o,hits:i.prepare(f).all(E),tags:c})}if(!d)return C({query:o,hits:[],tags:c});let p=`
974
+ `,y={limit:g};return n&&(T+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",y.proj=`%${n}%`),l.forEach((k,C)=>{T+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${C})`,y[`tag_${C}`]=k}),T+=" ORDER BY COALESCE(s.started_at, '') DESC LIMIT @limit",A({query:a,hits:o.prepare(T).all(y),tags:l})}if(!p)return A({query:a,hits:[],tags:l,mode:c});if(c==="semantic"){if(!await vr())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 y=(await Rt(a,g)).map(k=>({session_id:k.sessionId,snippet:k.text,matched_via:"vector"}));return A({query:a,hits:y,tags:l,mode:"semantic"})}catch(T){return A({query:a,hits:[],tags:l,mode:"semantic",error:"semantic_failed",reason:T instanceof Error?T.message:"vector search failed"})}}let _=`
975
975
  SELECT m.session_id AS session_id,
976
976
  m.uuid AS message_uuid,
977
977
  p.name AS project,
@@ -986,7 +986,7 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
986
986
  JOIN projects p ON p.id = s.project_id
987
987
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
988
988
  WHERE messages_fts MATCH @fts
989
- `,g={fts:d,limit:u};n&&(p+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",g.proj=`%${n}%`),c.forEach((f,E)=>{p+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${E})`,g[`tag_${E}`]=f}),p+=" ORDER BY bm25(messages_fts) LIMIT @limit";let _=i.prepare(p).all(g);if(await Xc())try{let f=await Nn(o,u),E=_.map(w=>({id:String(w.session_id),data:w,lane:"bm25"})),b=f.map(w=>({id:w.sessionId,data:{session_id:w.sessionId,snippet:w.text,matched_via:"vector"},lane:"vector"})),P=An([E,b]).slice(0,u).map(w=>({...w.data,session_id:w.id,rrf_score:w.score,lanes:w.lanes}));return C({query:o,hits:P,tags:c,fusion:"rrf"})}catch{}return C({query:o,hits:_,tags:c})}),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:S.string().uuid().describe("Session UUID to find similar sessions for."),limit:S.number().int().min(1).max(50).optional().describe("Max results (default 10)."),min_cosine:S.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 Bc();if(i)return i;try{let o=await Ln(s,n??10,r??.65);return C({session_id:s,similar:o})}catch(o){return X(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=he(),n=On(),r=Ee();return C({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:S.string().describe("Session id (full UUID or 8+-character prefix).")}},async({id:s})=>{let n=B(s);if(!n)return X(`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
+ `,f={fts:p,limit:g};n&&(_+=" AND (p.name LIKE @proj OR p.decoded_path LIKE @proj)",f.proj=`%${n}%`),l.forEach((T,y)=>{_+=` AND s.id IN (SELECT session_id FROM session_tags WHERE tag = @tag_${y})`,f[`tag_${y}`]=T}),_+=" ORDER BY bm25(messages_fts) LIMIT @limit";let E=o.prepare(_).all(f);if(c==="fts")return A({query:a,hits:E,tags:l,mode:"fts"});if(await vr())try{let T=await Rt(a,g),y=E.map(M=>({id:String(M.session_id),data:M,lane:"bm25"})),k=T.map(M=>({id:M.sessionId,data:{session_id:M.sessionId,snippet:M.text,matched_via:"vector"},lane:"vector"})),G=On([y,k]).slice(0,g).map(M=>({...M.data,session_id:M.id,rrf_score:M.score,lanes:M.lanes}));return A({query:a,hits:G,tags:l,fusion:"rrf",mode:"fused"})}catch{}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:b.string().uuid().describe("Session UUID to find similar sessions for."),limit:b.number().int().min(1).max(50).optional().describe("Max results (default 10)."),min_cosine:b.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 Yc();if(i)return i;try{let o=await An(s,n??10,r??.65);return A({session_id:s,similar:o})}catch(o){return W(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=xn(),r=Te();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:b.string().describe("Session id (full UUID or 8+-character prefix).")}},async({id:s})=>{let n=X(s);if(!n)return W(`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,
990
990
  s.message_count, s.user_message_count, s.assistant_message_count,
991
991
  s.first_user_message, s.git_branch, s.version, s.indexed_at,
992
992
  p.name AS project_name,
@@ -994,11 +994,11 @@ kEgDDGWLdQN7jrx/W+Nxz9yOJbwTPDI4jv24IztWSEtJuqH+0KvrDbdfFA==
994
994
  FROM sessions s
995
995
  JOIN projects p ON p.id = s.project_id
996
996
  LEFT JOIN session_aliases sa ON sa.session_id = s.id
997
- WHERE s.id = ?`).get(n);if(!i)return X(`session metadata missing for ${n}`);let o=we(n),a=r.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
997
+ WHERE s.id = ?`).get(n);if(!i)return W(`session metadata missing for ${n}`);let o=we(n),a=r.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
998
998
  FROM messages WHERE session_id = ?
999
- ORDER BY COALESCE(timestamp, ''), rowid`).all(n);return C({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:S.string().describe("Session id (full UUID or 8+-character prefix)."),mode:S.enum(["condensed","full"]).optional().describe("`condensed` (default) strips tool-call JSON; `full` keeps everything."),includeSidechain:S.boolean().optional().describe("Include subagent / sidechain messages (default false)."),prelude:S.string().max(1e4).optional().describe("Optional header prepended above the transcript (max 10 000 chars)."),since:S.string().optional().describe("Only messages at or after this ISO timestamp.")}},async({id:s,mode:n,includeSidechain:r,prelude:i,since:o})=>{let a=B(s);if(!a)return X(`session not found or prefix ambiguous: ${s}`);let c=m(),l=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:b.string().describe("Session id (full UUID or 8+-character prefix)."),mode:b.enum(["condensed","full"]).optional().describe("`condensed` (default) strips tool-call JSON; `full` keeps everything."),includeSidechain:b.boolean().optional().describe("Include subagent / sidechain messages (default false)."),prelude:b.string().max(1e4).optional().describe("Optional header prepended above the transcript (max 10 000 chars)."),since:b.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 W(`session not found or prefix ambiguous: ${s}`);let c=m(),d=c.prepare(`SELECT s.id, p.name AS project_name,
1000
1000
  s.started_at, s.ended_at, s.message_count, s.git_branch
1001
1001
  FROM sessions s JOIN projects p ON p.id = s.project_id
1002
- WHERE s.id = ?`).get(a);if(!l)return X(`session metadata missing for ${a}`);let d=c.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
1002
+ WHERE s.id = ?`).get(a);if(!d)return W(`session metadata missing for ${a}`);let l=c.prepare(`SELECT uuid, type, role, timestamp, is_sidechain, content_text, tool_names
1003
1003
  FROM messages WHERE session_id = ?
1004
- ORDER BY COALESCE(timestamp, ''), rowid`).all(a),u=zt(l,d,{mode:n??"condensed",includeSidechain:r??!1,prelude:i??null,since:o??null}),p=o?"since":n??"condensed";return Rn(a,Math.ceil(u.length/4),p,"mcp"),Ut(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:S.string().describe("Session UUID or 8+-character prefix."),budget:S.number().int().min(100).max(5e4).optional().describe("Token budget for the assembled bundle. Default 4000."),scoring:S.enum(["pagerank","embedding-rerank","hybrid"]).optional().describe("Scoring mode (default hybrid)."),edge_types:S.array(S.enum(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"])).optional().describe("Restrict to certain link types. Default: all approved types."),max_depth:S.number().int().min(1).max(5).optional().describe("Pagerank traversal depth on the local subgraph (default 2)."),include_wiki_links:S.boolean().optional().describe("Include manual wiki_link rows. Default true."),include_suggestions:S.boolean().optional().describe("Surface pending session_link_suggestions. Debug only; default false."),format:S.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:l})=>{let d=B(s);if(!d)return X(`session not found or prefix ambiguous: ${s}`);try{let u=Jn(d,{budget:n,scoring:r,edgeTypes:i,maxDepth:o,includeWikiLinks:a,includeSuggestions:c});return l==="json"?C(u):Ut(u.bundle)}catch(u){return X(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(()=>(Or(),Ar));return C(s())}),gn(e),t){let s=Number(process.env.RECALL_MCP_RATE_LIMIT),n=Number.isFinite(s)&&s>0?s:at,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 l=new Date(c.at).getTime();if(Number.isFinite(l))try{a.consume(l)}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:S.boolean().optional().describe("Only sessions with zero tags (default false)."),project:S.string().optional().describe("Exact project name match."),collectionId:S.string().optional().describe("Only sessions in this collection."),limit:S.number().int().min(1).max(200).optional()}},async c=>{let l=qe();if(!l.enabled)return X("auto-tagging is disabled; enable it in Recall settings before scanning");let d=Ne({untaggedOnly:c.untaggedOnly,project:c.project,collectionId:c.collectionId,limit:c.limit??50});return C({count:d.length,sessions:d,guidance:`Produce ${l.minTagsPerSession}-${l.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:S.string().describe("Full session UUID or 8+-character prefix."),tags:S.array(S.string()).min(1).max(10).describe("Tags to add. Normalized server-side (lowercase, hyphens, strip #).")}},async({sessionId:c,tags:l})=>{if(!qe().enabled)return X("auto-tagging is disabled; enable it in Recall settings before writing tags");let u=B(c);if(!u)return X(`session not found or prefix ambiguous: ${c}`);try{let p=await A({tool:"apply_tags",args:{sessionId:u,tags:l},limiter:a,run:()=>{let g=[],_=[];for(let f of l)try{let{tag:E,added:b}=ye(u,f);b?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 C(p)}catch(p){return p instanceof Error&&p.message.startsWith("SQLITE_")?X("database constraint error"):X(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:S.boolean().optional().describe("Also run VACUUM. Requires the daemon to be stopped.")}},async({vacuum:c})=>{let{runOptimize:l}=await Promise.resolve().then(()=>(Ir(),xr)),d=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 l({vacuum:!!c,json:!0})}finally{process.stdout.write=d}try{return C(JSON.parse(u.trim()))}catch{return Ut(u.trim())}}),os(e,{limiter:a}),_n(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 Zt)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 Gc(){let e=Wc();await Hc();let t=new vc;await e.connect(t);let s=async()=>{try{await e.close()}catch{}Yt(),process.exit(0)};process.on("SIGINT",s),process.on("SIGTERM",s)}var Yc=(()=>{try{let e=process.argv[1];return e?import.meta.url===new URL(`file://${e}`).href:!1}catch{return!1}})();Yc&&Gc().catch(e=>{console.error("[claude-recall-mcp] fatal:",e),process.exit(1)});export{Wc as buildServer};
1004
+ ORDER BY COALESCE(timestamp, ''), rowid`).all(a),u=Jt(d,l,{mode:n??"condensed",includeSidechain:r??!1,prelude:i??null,since:o??null}),p=o?"since":n??"condensed";return Ln(a,Math.ceil(u.length/4),p,"mcp"),$t(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:b.string().describe("Session UUID or 8+-character prefix."),budget:b.number().int().min(100).max(5e4).optional().describe("Token budget for the assembled bundle. Default 4000."),scoring:b.enum(["pagerank","embedding-rerank","hybrid"]).optional().describe("Scoring mode (default hybrid)."),edge_types:b.array(b.enum(["citation","similar","skill_track","bug_pattern","wiki_link","temporal_proximity"])).optional().describe("Restrict to certain link types. Default: all approved types."),max_depth:b.number().int().min(1).max(5).optional().describe("Pagerank traversal depth on the local subgraph (default 2)."),include_wiki_links:b.boolean().optional().describe("Include manual wiki_link rows. Default true."),include_suggestions:b.boolean().optional().describe("Surface pending session_link_suggestions. Debug only; default false."),format:b.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 W(`session not found or prefix ambiguous: ${s}`);try{let u=Kn(l,{budget:n,scoring:r,edgeTypes:i,maxDepth:o,includeWikiLinks:a,includeSuggestions:c});return d==="json"?A(u):$t(u.bundle)}catch(u){return W(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(()=>(Ir(),xr));return A(s())}),fn(e),t){let s=Number(process.env.RECALL_MCP_RATE_LIMIT),n=Number.isFinite(s)&&s>0?s:at,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 V(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:b.boolean().optional().describe("Only sessions with zero tags (default false)."),project:b.string().optional().describe("Exact project name match."),collectionId:b.string().optional().describe("Only sessions in this collection."),limit:b.number().int().min(1).max(200).optional()}},async c=>{let d=qe();if(!d.enabled)return W("auto-tagging is disabled; enable it in Recall settings before scanning");let l=Ne({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:b.string().describe("Full session UUID or 8+-character prefix."),tags:b.array(b.string()).min(1).max(10).describe("Tags to add. Normalized server-side (lowercase, hyphens, strip #).")}},async({sessionId:c,tags:d})=>{if(!qe().enabled)return W("auto-tagging is disabled; enable it in Recall settings before writing tags");let u=X(c);if(!u)return W(`session not found or prefix ambiguous: ${c}`);try{let p=await L({tool:"apply_tags",args:{sessionId:u,tags:d},limiter:a,run:()=>{let g=[],_=[];for(let f of d)try{let{tag:E,added:T}=ye(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 A(p)}catch(p){return p instanceof Error&&p.message.startsWith("SQLITE_")?W("database constraint error"):W(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:b.boolean().optional().describe("Also run VACUUM. Requires the daemon to be stopped.")}},async({vacuum:c})=>{let{runOptimize:d}=await Promise.resolve().then(()=>(kr(),Cr)),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 $t(u.trim())}}),as(e,{limiter:a}),hn(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 Qt)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 Jc(){let e=zc();await Gc();let t=new Pc;await e.connect(t);let s=async()=>{try{await e.close()}catch{}zt(),process.exit(0)};process.on("SIGINT",s),process.on("SIGTERM",s)}var Kc=(()=>{try{let e=process.argv[1];return e?import.meta.url===new URL(`file://${e}`).href:!1}catch{return!1}})();Kc&&Jc().catch(e=>{console.error("[claude-recall-mcp] fatal:",e),process.exit(1)});export{zc as buildServer};