@misterhuydo/cairn-mcp 1.25.0 → 1.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -348,13 +348,25 @@ is least reason to speak. Bytes survive now only as a fallback for when no usage
348
348
  record is reachable, and when that happens the line says so instead of dressing a
349
349
  file size up as a token count.
350
350
 
351
- Deliberately not built: no percentage (the context-window size is not observable
352
- from a hook, so the denominator would be invented the absolute count is reported
353
- instead), no full transcript walk (the tail only; a full walk in this hot path once
354
- froze every terminal in the cockpit for 4.3 seconds), no counting subagent spend as
355
- main-thread context, no repeating the prompt when nothing changed, and no
356
- auto-clearing, auto-compacting, or filling the field in on your behalf. Same shape
357
- as completed-phase detection: detect, surface, confirm with the user, never apply.
351
+ **It only speaks at 90% of the window and above.** The window size is not observable
352
+ no hook input carries it, and the transcript records usage but never a limit so
353
+ cairn infers the smallest window that could already hold what it has measured.
354
+ Having *seen* 482k tokens proves the window is bigger than 200k, and that is the one
355
+ direction the inference needs to be sound in. A fixed absolute threshold cannot
356
+ serve both sizes: 120,000 tokens is 60% of a 200k window but 12% of a 1M one, which
357
+ is why v1.20.0 announced a stopping point on a session sitting at just over half its
358
+ context. Override with `CAIRN_CONTEXT_HINT_TOKENS` if you want a different bar.
359
+
360
+ The one case the inference gets wrong: a 1M-window session between roughly 180k and
361
+ 196k looks like a nearly-full 200k one, so it may speak once. It corrects itself as
362
+ soon as usage passes 200k, and the no-repeat rule means it says it once, not every
363
+ turn.
364
+
365
+ Deliberately not built: no full transcript walk (the tail only; a full walk in this
366
+ hot path once froze every terminal in the cockpit for 4.3 seconds), no counting
367
+ subagent spend as main-thread context, no repeating the prompt when nothing changed,
368
+ and no auto-clearing, auto-compacting, or filling the field in on your behalf. Same
369
+ shape as completed-phase detection: detect, surface, confirm with the user, never apply.
358
370
 
359
371
  ---
360
372
 
package/dist/cairn-cli.js CHANGED
@@ -1,37 +1,37 @@
1
1
  #!/usr/bin/env node
2
- import y from"fs";import Z from"os";import S from"path";import{execSync as ss}from"child_process";import ls from"fast-glob";var Ae={".java":"java",".ts":"typescript",".tsx":"typescript",".js":"javascript",".jsx":"javascript",".mjs":"javascript",".vue":"vue",".py":"python",".sql":"sql",".yml":"config",".yaml":"config",".properties":"config",".env":"config",".xml":"xml",".html":"html",".md":"markdown"};function oe(e,t,n={}){let{noComments:s=!0,noEmptyLines:o=!0,aggressive:i=!1}=n,a=e;return s&&(a=$t(a,t)),o&&(a=Rt(a)),a=vt(a),i&&(a=Ot(a)),a.trim()}function $t(e,t){switch(t){case"java":case"typescript":case"javascript":case"vue":return e.replace(/\/\*[\s\S]*?\*\//g,"").replace(/(["'`])(?:(?!\1)[^\\]|\\.)*\1|\/(?![/*])(?:[^/\\\n]|\\.)*\/[gimsuy]*|\/\/[^\n]*/g,(n,s)=>s||!n.startsWith("//")?n:"");case"python":return e.replace(/#.*$/gm,"").replace(/'''[\s\S]*?'''/g,"").replace(/"""[\s\S]*?"""/g,"");case"sql":return e.replace(/--.*$/gm,"").replace(/\/\*[\s\S]*?\*\//g,"");case"xml":case"html":return e.replace(/<!--[\s\S]*?-->/g,"");case"config":return e.replace(/#.*$/gm,"");default:return e}}function Rt(e){return e.split(`
2
+ import y from"fs";import Z from"os";import S from"path";import{execSync as rs}from"child_process";import ps from"fast-glob";var Fe={".java":"java",".ts":"typescript",".tsx":"typescript",".js":"javascript",".jsx":"javascript",".mjs":"javascript",".vue":"vue",".py":"python",".sql":"sql",".yml":"config",".yaml":"config",".properties":"config",".env":"config",".xml":"xml",".html":"html",".md":"markdown"};function oe(e,t,n={}){let{noComments:s=!0,noEmptyLines:o=!0,aggressive:i=!1}=n,a=e;return s&&(a=Ot(a,t)),o&&(a=jt(a)),a=Pt(a),i&&(a=Tt(a)),a.trim()}function Ot(e,t){switch(t){case"java":case"typescript":case"javascript":case"vue":return e.replace(/\/\*[\s\S]*?\*\//g,"").replace(/(["'`])(?:(?!\1)[^\\]|\\.)*\1|\/(?![/*])(?:[^/\\\n]|\\.)*\/[gimsuy]*|\/\/[^\n]*/g,(n,s)=>s||!n.startsWith("//")?n:"");case"python":return e.replace(/#.*$/gm,"").replace(/'''[\s\S]*?'''/g,"").replace(/"""[\s\S]*?"""/g,"");case"sql":return e.replace(/--.*$/gm,"").replace(/\/\*[\s\S]*?\*\//g,"");case"xml":case"html":return e.replace(/<!--[\s\S]*?-->/g,"");case"config":return e.replace(/#.*$/gm,"");default:return e}}function jt(e){return e.split(`
3
3
  `).filter(t=>t.trim().length>0).join(`
4
- `)}function vt(e){return e.split(`
4
+ `)}function Pt(e){return e.split(`
5
5
  `).map(t=>t.trimEnd()).join(`
6
- `)}function Ot(e){return e.replace(/\s+/g," ").replace(/\s*([=+\-*/%<>!&|^~?:;,{}()[\]])\s*/g,"$1").replace(/;\s*}/g,"}").replace(/\(\s+/g,"(").replace(/\s+\)/g,")").trim()}function Fe(e,t={}){let{noStyle:n=!1,noComments:s=!0,noEmptyLines:o=!0,aggressive:i=!1}=t,a=[],l=e.match(/<template>([\s\S]*?)<\/template>/i);if(l){let r=l[1];s&&(r=r.replace(/<!--[\s\S]*?-->/g,"")),o&&(r=r.split(`
6
+ `)}function Tt(e){return e.replace(/\s+/g," ").replace(/\s*([=+\-*/%<>!&|^~?:;,{}()[\]])\s*/g,"$1").replace(/;\s*}/g,"}").replace(/\(\s+/g,"(").replace(/\s+\)/g,")").trim()}function Le(e,t={}){let{noStyle:n=!1,noComments:s=!0,noEmptyLines:o=!0,aggressive:i=!1}=t,a=[],l=e.match(/<template>([\s\S]*?)<\/template>/i);if(l){let r=l[1];s&&(r=r.replace(/<!--[\s\S]*?-->/g,"")),o&&(r=r.split(`
7
7
  `).filter(c=>c.trim()).join(`
8
8
  `)),r=r.replace(/\s+/g," ").trim(),r&&a.push(`<template>${r}</template>`)}let u=e.match(/<script([^>]*)>([\s\S]*?)<\/script>/i);if(u){let r=u[1],c=oe(u[2],"javascript",{noComments:s,noEmptyLines:o,aggressive:i}),p=/setup/i.test(r)?"<script setup>":"<script>";c&&a.push(`${p}${c}</script>`)}if(!n){let r=e.match(/<style([^>]*)>([\s\S]*?)<\/style>/i);if(r){let c=r[2].replace(/\/\*[\s\S]*?\*\//g,"").replace(/\s+/g," ").trim();c&&a.push(`<style${r[1]}>${c}</style>`)}}return a.join(`
9
9
 
10
- `)}import I from"fs";import Q from"path";import N from"fs";import jt from"os";import D from"path";var de=null;function z(){return de||process.cwd()}function Ce(e){let t=de;return de=e,t}function G(){let e=D.join(z(),".cairn");N.mkdirSync(D.join(e,"bundles"),{recursive:!0});let t=D.join(e,".cairn-project");return N.existsSync(t)||N.writeFileSync(t,"","utf8"),e}function fe(e){return e.replace(/[:/\\]/g,"-").replace(/^-+/,"")}function me(){let e=D.join(jt.homedir(),".claude","projects",fe(z()));return N.existsSync(e)?D.join(e,"memory"):null}function Le(e){let t=D.join(e,"MEMORY.md");N.existsSync(t)||N.writeFileSync(t,`# Memory Index
11
- `,"utf8")}function he(){let e=D.join(G(),"memory");N.mkdirSync(e,{recursive:!0}),Le(e);let t=me();if(!t)return e;N.mkdirSync(t,{recursive:!0}),Le(t);let n=s=>N.readdirSync(s).some(o=>o.endsWith(".md")&&o!=="MEMORY.md");if(!n(t)&&n(e))for(let s of N.readdirSync(e)){let o=D.join(e,s);try{if(!N.statSync(o).isFile())continue;let i=N.statSync(o);N.copyFileSync(o,D.join(t,s));try{N.utimesSync(D.join(t,s),i.atime,i.mtime)}catch{}}catch{}}return t}import J from"fs";import ge from"path";import{execSync as Pt}from"child_process";var O="would_be_lost";var De=2*1024*1024,Tt=12e4,Mt=[256*1024,1024*1024],Ie=".handoff-hint",Nt=220;function ye(e,t=Nt){let n=String(e).replace(/\s+/g," ").trim();return n.length<=t?n:n.slice(0,t-1)+"\u2026"}function At(e,t){try{let n=Pt(`git log --oneline --after="${t}"`,{cwd:e,encoding:"utf8",timeout:5e3,stdio:["ignore","pipe","ignore"]}).trim();return n?n.split(`
12
- `).length:0}catch{return null}}function re(e,t){let n=typeof t?.[O]=="string"?t[O].trim():"";if(!n)return{recorded:!1,text:"",at:null,commits:null,stale:!1};let s=t[`${O}_at`]||t.checkpoint_at||null,o=s?At(e,s):null;return{recorded:!0,text:n,at:s,commits:o,stale:(o??0)>0}}function _e(e){if(!e.recorded)return"No handoff recorded \u2014 nothing was written down about what the last session would lose.";let t=e.stale?` (written before the ${e.commits} commit(s) since \u2014 may be stale)`:"";return`Carried forward: ${ye(e.text)}${t}`}function Ft(){let e=Number(process.env.CAIRN_CONTEXT_HINT_TOKENS);return Number.isFinite(e)&&e>0?e:Tt}function Lt(e,t){let n=J.openSync(e,"r");try{let s=J.fstatSync(n).size,o=Math.min(s,t),i=Buffer.alloc(o);return J.readSync(n,i,0,o,s-o),{text:i.toString("utf8"),truncated:o<s}}finally{J.closeSync(n)}}function Ct(e,t){let n=e.split(`
13
- `),s=t?1:0;for(let o=n.length-1;o>=s;o--){let i=n[o].trim();if(!i.startsWith("{"))continue;let a;try{a=JSON.parse(i)}catch{continue}if(a?.isSidechain===!0)continue;let l=a?.message?.usage;if(!l)continue;let u=(l.input_tokens||0)+(l.cache_read_input_tokens||0)+(l.cache_creation_input_tokens||0)+(l.output_tokens||0);if(u>0)return u}return null}function Dt(e){if(!e)return{tokens:0,source:"none",bytes:0};let t;try{t=J.statSync(e).size}catch{return{tokens:0,source:"none",bytes:0}}for(let n of Mt){let s;try{s=Lt(e,n)}catch{break}let o=Ct(s.text,s.truncated);if(o!=null)return{tokens:o,source:"transcript",bytes:t};if(!s.truncated)break}return{tokens:0,source:"bytes",bytes:t}}function It(e){return e.recorded&&!e.stale?"clear":"compact"}function Wt(e,t){return e.recorded?e.stale?`${t}, and the recorded handoff predates the ${e.commits} commit(s) since, so it no longer describes the work in flight`:`${t}, and the handoff is current as of the last commit, so a reset costs only what is not already in that line`:`${t}, and nothing is written down about what this session would lose, so a hard reset would drop it with no way to get it back`}function We({cwd:e,session:t,transcriptPath:n,bytes:s,usage:o}){let i=o??Dt(n);i.source==="none"&&typeof s=="number"&&(i={tokens:0,source:"bytes",bytes:s});let a=s??i.bytes,l=Ft();if(i.source==="none")return null;let u,r;if(i.source==="transcript"){if(i.tokens<l)return null;u=Math.floor(i.tokens/l),r=`this session is carrying about ${i.tokens.toLocaleString()} tokens of context`}else{if(a<De)return null;u=Math.floor(a/De),r=`this session's transcript is ${(a/1024/1024).toFixed(1)} MB (token count unreadable, so this is a rough size signal only)`}let c=re(e,t),p=It(c),m=Wt(c,r),d;c.recorded?c.stale?d=`[cairn] Stopping point: ${r}. Clearing now would lose: ${ye(c.text)} \u2014 written before the ${c.commits} commit(s) since.`:d=`[cairn] Stopping point: ${r}. The handoff is current: ${ye(c.text)} \u2014 clearing costs only what is not in that line.`:d=`[cairn] Stopping point: ${r}. Nothing is recorded about what clearing would lose \u2014 ask the user first.`;let h=b=>b===p?" (Recommended)":"",f=[` - "Checkpoint + /clear${h("clear")}" \u2014 I write the handoff now, then you type /clear.`," Full reset and maximum headroom; everything not written down is gone."],g=[` - "Checkpoint + /compact${h("compact")}" \u2014 I write the handoff now, then you type /compact.`," Nothing has to be retyped, but the summary is lossy and the window stays partly full."],_=["STOPPING POINT \u2014 this is the user's call, not yours. Ask them before doing anything else.","",`Use AskUserQuestion. Label the options as ACTIONS, put "(Recommended)" on the ${p}`,`option and list it FIRST, and give them cairn's reason in your own words: ${m}.`,"","Offer exactly these:",...p==="clear"?[...f,...g]:[...g,...f],' - "Keep going" \u2014 decline. Cairn will not raise this again until the situation changes.',"",`Whichever they pick, call cairn_checkpoint FIRST and answer ${O} \u2014 including for`,"/clear, which needs it most, not least. That field is the only part of this session that","survives either option.","","You CANNOT run /clear or /compact yourself: no tool or hook can trigger a slash command.","Say so plainly and ask the user to type it.","","After they type it, cairn's SessionStart hook re-injects the checkpoint automatically, so the","next session starts already knowing where it is. If that does not happen the hook is not",'installed (`cairn install-hooks`) \u2014 in that case tell them to type "resume".',"",`Never write ${O} on their behalf, and never tell them there is nothing outstanding:`,"cairn cannot see what was never written down, so it can never issue an all-clear."].join(`
14
- `),w=[u,i.source,c.recorded?c.stale?"stale":"current":"missing",c.commits??"?",p,n?ge.basename(n):"-"].join("|");return{line:d,guidance:_,signature:w,state:c,recommend:p,reason:m,usage:i,bytes:a}}function Ue(e,t){let n=ge.join(e,Ie);try{if(J.readFileSync(n,"utf8").trim()===t)return!1}catch{}try{J.writeFileSync(n,t,"utf8")}catch{}return!0}function He(e){try{J.unlinkSync(ge.join(e,Ie))}catch{}}var Ut=1e3,Ht=100*1024;function Jt(e,t,n,s){if(e&&typeof e=="object"&&e.ref)return e;let o=typeof e=="string"?e:String(e),i=/^\[\d{4}-\d{2}-\d{2}\]/.test(o)?o:`[${n}] ${o}`;if(i.length<=Ut)return i;let a=Q.join(t,"notes");I.mkdirSync(a,{recursive:!0});let u=`note-${new Date().toISOString().replace(/[:.]/g,"-")}-${s}.md`,r=Q.join(a,u);return I.writeFileSync(r,i,"utf8"),{description:i.split(`
15
- `)[0].slice(0,120),ref:Q.join(".cairn","notes",u)}}function Se(e,{message:t,active_files:n=[],notes:s=[],auto:o=!1,...i}){if(typeof s=="string")try{s=JSON.parse(s)}catch{s=s?[s]:[]}Array.isArray(s)||(s=[]);let a=G(),l=Q.join(a,"session.json"),u=new Date().toISOString().slice(0,10),r=new Date().toISOString(),c={};try{c=JSON.parse(I.readFileSync(l,"utf8"))||{}}catch{}let p=typeof i[O]=="string"&&i[O].trim()?i[O].trim():null,m=p??(typeof c[O]=="string"?c[O]:null),d=p?r:c[`${O}_at`]??null,h={};try{let _=e.prepare("SELECT path FROM main.files").all();for(let{path:w}of _)try{h[w]=I.statSync(w).mtimeMs}catch{}}catch{}for(let _ of n)try{h[_]=I.statSync(_).mtimeMs}catch{}let f=s.map((_,w)=>Jt(_,a,u,w));if(I.existsSync(l))try{if(I.statSync(l).size>Ht){let w=1;for(;I.existsSync(Q.join(a,`session.${w}.json`));)w++;I.renameSync(l,Q.join(a,`session.${w}.json`))}}catch{}let g={message:t,checkpoint_at:r,last_manual_checkpoint_at:o?c.last_manual_checkpoint_at??null:r,last_manual_message:o?c.last_manual_message??null:t,active_files:n,notes:f,[O]:m,[`${O}_at`]:d,mtime_snapshot:h};return I.writeFileSync(l,JSON.stringify(g,null,2),"utf8"),{content:[{type:"text",text:JSON.stringify({saved:!0,checkpoint_at:g.checkpoint_at,active_files_tracked:n.length,notes_saved:f.length,[O]:m?p?"recorded":"carried forward from the previous checkpoint":`not recorded \u2014 nothing will tell the next session what this one would lose (pass ${O})`},null,2)}]}}import H from"fs";import A from"path";import Bt from"crypto";var Je=80;function xe(e){let t=A.dirname(A.resolve(e)),n=A.parse(t).root;for(;;){let s=A.join(t,".cairn");try{if(H.statSync(s).isDirectory()&&H.existsSync(A.join(s,".cairn-project"))&&s.split(A.sep).filter(i=>i===".cairn").length===1)return s}catch{}if(t===n)return null;t=A.dirname(t)}}function Be(e,t){let n=Bt.createHash("sha256").update(A.resolve(t)).digest("hex").slice(0,6);return A.join(e,"views",`${n}_${A.basename(t)}`)}function K(e){try{return JSON.parse(H.readFileSync(A.join(e,"minify-map.json"),"utf8"))}catch{return{}}}function C(e,t){H.mkdirSync(e,{recursive:!0}),H.writeFileSync(A.join(e,"minify-map.json"),JSON.stringify(t,null,2),"utf8")}function ie(e,t){let n=0;for(let[s,o]of Object.entries(t)){let i=!1;if(!H.existsSync(s))i=!0;else if(!H.existsSync(o.tempPath))i=!0;else try{H.statSync(s).mtimeMs>o.minifiedAt&&(i=!0)}catch{i=!0}if(i){if(o.tempPath)try{H.unlinkSync(o.tempPath)}catch{}delete t[s],n++}}return n}function Ye(e,t){switch(t){case"javascript":case"typescript":return we(e);case"java":return we(e,{annotations:!0});case"python":return Kt(e);case"vue":return qt(e);case"sql":return Vt(e);default:return e.split(`
10
+ `)}import I from"fs";import Q from"path";import M from"fs";import Nt from"os";import C from"path";var fe=null;function z(){return fe||process.cwd()}function Ce(e){let t=fe;return fe=e,t}function G(){let e=C.join(z(),".cairn");M.mkdirSync(C.join(e,"bundles"),{recursive:!0});let t=C.join(e,".cairn-project");return M.existsSync(t)||M.writeFileSync(t,"","utf8"),e}function me(e){return e.replace(/[:/\\]/g,"-").replace(/^-+/,"")}function he(){let e=C.join(Nt.homedir(),".claude","projects",me(z()));return M.existsSync(e)?C.join(e,"memory"):null}function De(e){let t=C.join(e,"MEMORY.md");M.existsSync(t)||M.writeFileSync(t,`# Memory Index
11
+ `,"utf8")}function ye(){let e=C.join(G(),"memory");M.mkdirSync(e,{recursive:!0}),De(e);let t=he();if(!t)return e;M.mkdirSync(t,{recursive:!0}),De(t);let n=s=>M.readdirSync(s).some(o=>o.endsWith(".md")&&o!=="MEMORY.md");if(!n(t)&&n(e))for(let s of M.readdirSync(e)){let o=C.join(e,s);try{if(!M.statSync(o).isFile())continue;let i=M.statSync(o);M.copyFileSync(o,C.join(t,s));try{M.utimesSync(C.join(t,s),i.atime,i.mtime)}catch{}}catch{}}return t}import J from"fs";import _e from"path";import{execSync as Mt}from"child_process";var O="would_be_lost";var Ie=2*1024*1024,re=[2e5,1e6],We=.9;function Ue(e){for(let t of re)if(e<=t*.98)return t;return re[re.length-1]}var ks=re[0]*We,At=[256*1024,1024*1024],He=".handoff-hint",Ft=220;function ge(e,t=Ft){let n=String(e).replace(/\s+/g," ").trim();return n.length<=t?n:n.slice(0,t-1)+"\u2026"}function Lt(e,t){try{let n=Mt(`git log --oneline --after="${t}"`,{cwd:e,encoding:"utf8",timeout:5e3,stdio:["ignore","pipe","ignore"]}).trim();return n?n.split(`
12
+ `).length:0}catch{return null}}function ie(e,t){let n=typeof t?.[O]=="string"?t[O].trim():"";if(!n)return{recorded:!1,text:"",at:null,commits:null,stale:!1};let s=t[`${O}_at`]||t.checkpoint_at||null,o=s?Lt(e,s):null;return{recorded:!0,text:n,at:s,commits:o,stale:(o??0)>0}}function Se(e){if(!e.recorded)return"No handoff recorded \u2014 nothing was written down about what the last session would lose.";let t=e.stale?` (written before the ${e.commits} commit(s) since \u2014 may be stale)`:"";return`Carried forward: ${ge(e.text)}${t}`}function Dt(e=0){let t=Number(process.env.CAIRN_CONTEXT_HINT_TOKENS);return Number.isFinite(t)&&t>0?t:Math.floor(Ue(e)*We)}function Ct(e,t){let n=J.openSync(e,"r");try{let s=J.fstatSync(n).size,o=Math.min(s,t),i=Buffer.alloc(o);return J.readSync(n,i,0,o,s-o),{text:i.toString("utf8"),truncated:o<s}}finally{J.closeSync(n)}}function It(e,t){let n=e.split(`
13
+ `),s=t?1:0;for(let o=n.length-1;o>=s;o--){let i=n[o].trim();if(!i.startsWith("{"))continue;let a;try{a=JSON.parse(i)}catch{continue}if(a?.isSidechain===!0)continue;let l=a?.message?.usage;if(!l)continue;let u=(l.input_tokens||0)+(l.cache_read_input_tokens||0)+(l.cache_creation_input_tokens||0)+(l.output_tokens||0);if(u>0)return u}return null}function Wt(e){if(!e)return{tokens:0,source:"none",bytes:0};let t;try{t=J.statSync(e).size}catch{return{tokens:0,source:"none",bytes:0}}for(let n of At){let s;try{s=Ct(e,n)}catch{break}let o=It(s.text,s.truncated);if(o!=null)return{tokens:o,source:"transcript",bytes:t};if(!s.truncated)break}return{tokens:0,source:"bytes",bytes:t}}function Ut(e){return e.recorded&&!e.stale?"clear":"compact"}function Ht(e,t){return e.recorded?e.stale?`${t}, and the recorded handoff predates the ${e.commits} commit(s) since, so it no longer describes the work in flight`:`${t}, and the handoff is current as of the last commit, so a reset costs only what is not already in that line`:`${t}, and nothing is written down about what this session would lose, so a hard reset would drop it with no way to get it back`}function Je({cwd:e,session:t,transcriptPath:n,bytes:s,usage:o}){let i=o??Wt(n);i.source==="none"&&typeof s=="number"&&(i={tokens:0,source:"bytes",bytes:s});let a=s??i.bytes,l=Dt(i.tokens);if(i.source==="none")return null;let u,r;if(i.source==="transcript"){if(i.tokens<l)return null;u=Math.floor(i.tokens/l),r="this session is at roughly "+Math.round(i.tokens/Ue(i.tokens)*100)+"% of its context window (about "+i.tokens.toLocaleString()+" tokens)"}else{if(a<Ie)return null;u=Math.floor(a/Ie),r=`this session's transcript is ${(a/1024/1024).toFixed(1)} MB (token count unreadable, so this is a rough size signal only)`}let c=ie(e,t),p=Ut(c),m=Ht(c,r),d;c.recorded?c.stale?d=`[cairn] Stopping point: ${r}. Clearing now would lose: ${ge(c.text)} \u2014 written before the ${c.commits} commit(s) since.`:d=`[cairn] Stopping point: ${r}. The handoff is current: ${ge(c.text)} \u2014 clearing costs only what is not in that line.`:d=`[cairn] Stopping point: ${r}. Nothing is recorded about what clearing would lose \u2014 ask the user first.`;let h=b=>b===p?" (Recommended)":"",f=[` - "Checkpoint + /clear${h("clear")}" \u2014 I write the handoff now, then you type /clear.`," Full reset and maximum headroom; everything not written down is gone."],g=[` - "Checkpoint + /compact${h("compact")}" \u2014 I write the handoff now, then you type /compact.`," Nothing has to be retyped, but the summary is lossy and the window stays partly full."],_=["STOPPING POINT \u2014 this is the user's call, not yours. Ask them before doing anything else.","",`Use AskUserQuestion. Label the options as ACTIONS, put "(Recommended)" on the ${p}`,`option and list it FIRST, and give them cairn's reason in your own words: ${m}.`,"","Offer exactly these:",...p==="clear"?[...f,...g]:[...g,...f],' - "Keep going" \u2014 decline. Cairn will not raise this again until the situation changes.',"",`Whichever they pick, call cairn_checkpoint FIRST and answer ${O} \u2014 including for`,"/clear, which needs it most, not least. That field is the only part of this session that","survives either option.","","You CANNOT run /clear or /compact yourself: no tool or hook can trigger a slash command.","Say so plainly and ask the user to type it.","","After they type it, cairn's SessionStart hook re-injects the checkpoint automatically, so the","next session starts already knowing where it is. If that does not happen the hook is not",'installed (`cairn install-hooks`) \u2014 in that case tell them to type "resume".',"",`Never write ${O} on their behalf, and never tell them there is nothing outstanding:`,"cairn cannot see what was never written down, so it can never issue an all-clear."].join(`
14
+ `),w=[u,i.source,c.recorded?c.stale?"stale":"current":"missing",c.commits??"?",p,n?_e.basename(n):"-"].join("|");return{line:d,guidance:_,signature:w,state:c,recommend:p,reason:m,usage:i,bytes:a}}function Be(e,t){let n=_e.join(e,He);try{if(J.readFileSync(n,"utf8").trim()===t)return!1}catch{}try{J.writeFileSync(n,t,"utf8")}catch{}return!0}function Ye(e){try{J.unlinkSync(_e.join(e,He))}catch{}}var Jt=1e3,Bt=100*1024;function Yt(e,t,n,s){if(e&&typeof e=="object"&&e.ref)return e;let o=typeof e=="string"?e:String(e),i=/^\[\d{4}-\d{2}-\d{2}\]/.test(o)?o:`[${n}] ${o}`;if(i.length<=Jt)return i;let a=Q.join(t,"notes");I.mkdirSync(a,{recursive:!0});let u=`note-${new Date().toISOString().replace(/[:.]/g,"-")}-${s}.md`,r=Q.join(a,u);return I.writeFileSync(r,i,"utf8"),{description:i.split(`
15
+ `)[0].slice(0,120),ref:Q.join(".cairn","notes",u)}}function xe(e,{message:t,active_files:n=[],notes:s=[],auto:o=!1,...i}){if(typeof s=="string")try{s=JSON.parse(s)}catch{s=s?[s]:[]}Array.isArray(s)||(s=[]);let a=G(),l=Q.join(a,"session.json"),u=new Date().toISOString().slice(0,10),r=new Date().toISOString(),c={};try{c=JSON.parse(I.readFileSync(l,"utf8"))||{}}catch{}let p=typeof i[O]=="string"&&i[O].trim()?i[O].trim():null,m=p??(typeof c[O]=="string"?c[O]:null),d=p?r:c[`${O}_at`]??null,h={};try{let _=e.prepare("SELECT path FROM main.files").all();for(let{path:w}of _)try{h[w]=I.statSync(w).mtimeMs}catch{}}catch{}for(let _ of n)try{h[_]=I.statSync(_).mtimeMs}catch{}let f=s.map((_,w)=>Yt(_,a,u,w));if(I.existsSync(l))try{if(I.statSync(l).size>Bt){let w=1;for(;I.existsSync(Q.join(a,`session.${w}.json`));)w++;I.renameSync(l,Q.join(a,`session.${w}.json`))}}catch{}let g={message:t,checkpoint_at:r,last_manual_checkpoint_at:o?c.last_manual_checkpoint_at??null:r,last_manual_message:o?c.last_manual_message??null:t,active_files:n,notes:f,[O]:m,[`${O}_at`]:d,mtime_snapshot:h};return I.writeFileSync(l,JSON.stringify(g,null,2),"utf8"),{content:[{type:"text",text:JSON.stringify({saved:!0,checkpoint_at:g.checkpoint_at,active_files_tracked:n.length,notes_saved:f.length,[O]:m?p?"recorded":"carried forward from the previous checkpoint":`not recorded \u2014 nothing will tell the next session what this one would lose (pass ${O})`},null,2)}]}}import H from"fs";import A from"path";import zt from"crypto";var ze=80;function we(e){let t=A.dirname(A.resolve(e)),n=A.parse(t).root;for(;;){let s=A.join(t,".cairn");try{if(H.statSync(s).isDirectory()&&H.existsSync(A.join(s,".cairn-project"))&&s.split(A.sep).filter(i=>i===".cairn").length===1)return s}catch{}if(t===n)return null;t=A.dirname(t)}}function Ge(e,t){let n=zt.createHash("sha256").update(A.resolve(t)).digest("hex").slice(0,6);return A.join(e,"views",`${n}_${A.basename(t)}`)}function K(e){try{return JSON.parse(H.readFileSync(A.join(e,"minify-map.json"),"utf8"))}catch{return{}}}function D(e,t){H.mkdirSync(e,{recursive:!0}),H.writeFileSync(A.join(e,"minify-map.json"),JSON.stringify(t,null,2),"utf8")}function ae(e,t){let n=0;for(let[s,o]of Object.entries(t)){let i=!1;if(!H.existsSync(s))i=!0;else if(!H.existsSync(o.tempPath))i=!0;else try{H.statSync(s).mtimeMs>o.minifiedAt&&(i=!0)}catch{i=!0}if(i){if(o.tempPath)try{H.unlinkSync(o.tempPath)}catch{}delete t[s],n++}}return n}function Ke(e,t){switch(t){case"javascript":case"typescript":return be(e);case"java":return be(e,{annotations:!0});case"python":return Vt(e);case"vue":return Xt(e);case"sql":return Zt(e);default:return e.split(`
16
16
  `).slice(0,30).join(`
17
17
  `)+`
18
- ...`}}function we(e,{annotations:t=!1}={}){let n=e.split(`
19
- `),s=[],o=0;for(let i=0;i<n.length;i++){let a=n[i].trim();if(!a)continue;let{opens:l,closes:u}=Gt(a),r=l-u;if(o===0){let c=Yt(a);c!==null?s.push(`${i+1}: ${c}`):a==="}"&&s.push("}")}else if(o===1){let c=zt(a,t);c!==null&&s.push(` ${i+1}: ${c}`)}o=Math.max(0,o+r)}return s.join(`
20
- `)}function Yt(e){return/^import\s/.test(e)||/^@\w+/.test(e)||/^package\s/.test(e)?e:/^export\s/.test(e)?/^export\s+(default\s+)?(async\s+)?function[\s*]|^export\s+(default\s+)?(abstract\s+)?class\s/.test(e)?W(e):e:/^(async\s+)?function[\s*]/.test(e)||/^(abstract\s+)?class\s/.test(e)||/^(public\s+|private\s+|protected\s+)?(interface|enum|type|record)\s/.test(e)||/^(const|let|var)\s+\w+/.test(e)||/^(public|private|protected|abstract|final)\s+(class|interface|enum)\s/.test(e)?W(e):e==="}"?"}":null}function zt(e,t){return t&&/^@\w+/.test(e)?e:e==="}"?"}":/^(public|private|protected|static|final|abstract|async|override|readonly|synchronized|native)\s/.test(e)||/^constructor\s*\(/.test(e)||/^(get|set)\s+\w+\s*\(/.test(e)||/^\w[\w$]*\s*[<(]/.test(e)&&!e.startsWith("return")&&!e.startsWith("throw")&&!e.startsWith("if")&&!e.startsWith("for")&&!e.startsWith("while")&&!e.startsWith("switch")&&!e.startsWith("const")&&!e.startsWith("let")&&!e.startsWith("var")?W(e):null}function W(e){let t=0,n=!1,s="";for(let o=0;o<e.length;o++){let i=e[o];if(n)i===s&&e[o-1]!=="\\"&&(n=!1);else if(i==='"'||i==="'"||i==="`")n=!0,s=i;else if(i==="{"){if(t===0)return e.slice(0,o).trimEnd();t++}else i==="}"&&t--}return e}function Gt(e){let t=0,n=0,s=!1,o="";for(let i=0;i<e.length;i++){let a=e[i];s?a===o&&e[i-1]!=="\\"&&(s=!1):a==='"'||a==="'"||a==="`"?(s=!0,o=a):a==="{"?t++:a==="}"&&n++}return{opens:t,closes:n}}function Kt(e){let t=[],n=e.split(`
18
+ ...`}}function be(e,{annotations:t=!1}={}){let n=e.split(`
19
+ `),s=[],o=0;for(let i=0;i<n.length;i++){let a=n[i].trim();if(!a)continue;let{opens:l,closes:u}=qt(a),r=l-u;if(o===0){let c=Gt(a);c!==null?s.push(`${i+1}: ${c}`):a==="}"&&s.push("}")}else if(o===1){let c=Kt(a,t);c!==null&&s.push(` ${i+1}: ${c}`)}o=Math.max(0,o+r)}return s.join(`
20
+ `)}function Gt(e){return/^import\s/.test(e)||/^@\w+/.test(e)||/^package\s/.test(e)?e:/^export\s/.test(e)?/^export\s+(default\s+)?(async\s+)?function[\s*]|^export\s+(default\s+)?(abstract\s+)?class\s/.test(e)?W(e):e:/^(async\s+)?function[\s*]/.test(e)||/^(abstract\s+)?class\s/.test(e)||/^(public\s+|private\s+|protected\s+)?(interface|enum|type|record)\s/.test(e)||/^(const|let|var)\s+\w+/.test(e)||/^(public|private|protected|abstract|final)\s+(class|interface|enum)\s/.test(e)?W(e):e==="}"?"}":null}function Kt(e,t){return t&&/^@\w+/.test(e)?e:e==="}"?"}":/^(public|private|protected|static|final|abstract|async|override|readonly|synchronized|native)\s/.test(e)||/^constructor\s*\(/.test(e)||/^(get|set)\s+\w+\s*\(/.test(e)||/^\w[\w$]*\s*[<(]/.test(e)&&!e.startsWith("return")&&!e.startsWith("throw")&&!e.startsWith("if")&&!e.startsWith("for")&&!e.startsWith("while")&&!e.startsWith("switch")&&!e.startsWith("const")&&!e.startsWith("let")&&!e.startsWith("var")?W(e):null}function W(e){let t=0,n=!1,s="";for(let o=0;o<e.length;o++){let i=e[o];if(n)i===s&&e[o-1]!=="\\"&&(n=!1);else if(i==='"'||i==="'"||i==="`")n=!0,s=i;else if(i==="{"){if(t===0)return e.slice(0,o).trimEnd();t++}else i==="}"&&t--}return e}function qt(e){let t=0,n=0,s=!1,o="";for(let i=0;i<e.length;i++){let a=e[i];s?a===o&&e[i-1]!=="\\"&&(s=!1):a==='"'||a==="'"||a==="`"?(s=!0,o=a):a==="{"?t++:a==="}"&&n++}return{opens:t,closes:n}}function Vt(e){let t=[],n=e.split(`
21
21
  `);for(let s=0;s<n.length;s++){let o=n[s];if(!o.trim())continue;let i=o.trimStart();i.startsWith("import ")||i.startsWith("from ")?t.push(o):(i.startsWith("class ")||i.startsWith("def ")||i.startsWith("async def "))&&t.push(`${s+1}: ${o.trim().replace(/:(\s*)$/,":")}`)}return t.join(`
22
- `)}function qt(e){let t=[];for(let s of e.matchAll(/<(template|script|style)(\s[^>]*)?\s*>/g))t.push(s[0]);let n=e.match(/<script(?:\s[^>]*)?\s*>([\s\S]*?)<\/script>/);if(n){let s=we(n[1]);s.trim()&&(t.push("// script:"),t.push(s))}return t.join(`
23
- `)}function Vt(e){let t=[],n=e.split(`
22
+ `)}function Xt(e){let t=[];for(let s of e.matchAll(/<(template|script|style)(\s[^>]*)?\s*>/g))t.push(s[0]);let n=e.match(/<script(?:\s[^>]*)?\s*>([\s\S]*?)<\/script>/);if(n){let s=be(n[1]);s.trim()&&(t.push("// script:"),t.push(s))}return t.join(`
23
+ `)}function Zt(e){let t=[],n=e.split(`
24
24
  `);for(let s=0;s<n.length;s++){let o=n[s].trim();/^(CREATE|ALTER|DROP|INSERT|UPDATE|DELETE|SELECT|WITH|GRANT|REVOKE|BEGIN|COMMIT)\b/i.test(o)&&t.push(`${s+1}: ${o.slice(0,120)}${o.length>120?"...":""}`)}return t.join(`
25
- `)}import F from"fs";import B from"path";var Xt=["preference","experience","decision","knowledge"];function se(e){if(!e||typeof e!="string")return[];let t=e.split(/\r?\n/),n=!1,s=null,o=[],i=new Set;for(let a of t){if(/^##\s+Phases\s*$/i.test(a)){n=!0;continue}if(/^##\s+/.test(a)&&!/^###/.test(a)){s&&(o.push(s),s=null),n=!1;continue}if(!n)continue;let l=/^###\s+Phase\s+(\d+)\s*:\s*(.+?)\s*$/.exec(a);if(l){s&&o.push(s);let u=parseInt(l[1],10),r=l[2];if(i.has(u)){process.stderr.write(`[cairn] memo phase parser: duplicate Phase ${u} ignored
25
+ `)}import F from"fs";import B from"path";var Qt=["preference","experience","decision","knowledge"];function se(e){if(!e||typeof e!="string")return[];let t=e.split(/\r?\n/),n=!1,s=null,o=[],i=new Set;for(let a of t){if(/^##\s+Phases\s*$/i.test(a)){n=!0;continue}if(/^##\s+/.test(a)&&!/^###/.test(a)){s&&(o.push(s),s=null),n=!1;continue}if(!n)continue;let l=/^###\s+Phase\s+(\d+)\s*:\s*(.+?)\s*$/.exec(a);if(l){s&&o.push(s);let u=parseInt(l[1],10),r=l[2];if(i.has(u)){process.stderr.write(`[cairn] memo phase parser: duplicate Phase ${u} ignored
26
26
  `),s=null;continue}i.add(u),s={phase_number:u,text:r,bodyLines:[]};continue}s&&s.bodyLines.push(a)}return s&&o.push(s),o.map(a=>({phase_number:a.phase_number,text:a.text,body:a.bodyLines.join(`
27
- `).trim()||null}))}var ze=(e,t)=>e+" :: "+t;function Ke(e,t,n,s={}){let o=e.prepare("SELECT id, phase_number, status, text FROM main.roadmap_phases WHERE decision_name = ? AND phase_number IS NOT NULL ORDER BY phase_number").all(t),i=new Map(o.map(b=>[b.phase_number,b])),a=new Set,l=e.prepare("UPDATE main.roadmap_phases SET text = ?, body = ?, status = CASE WHEN status = ? THEN ? ELSE status END WHERE id = ?"),u=e.prepare("INSERT INTO main.roadmap_phases (decision_name, phase_number, parent_phase_id, text, body, status, position) VALUES (?, ?, NULL, ?, ?, ?, ?)"),r=e.prepare("UPDATE main.roadmap_phases SET status = 'stale' WHERE id = ?"),c=e.prepare("UPDATE main.roadmap_phases SET phase_number = NULL WHERE id = ?"),p=e.prepare("UPDATE main.roadmap_phases SET text = ?, body = ?, status = 'open', resolved_at = NULL WHERE id = ?"),m=s.completed??null,d=m===null?null:new Set(m.map(b=>ze(b.number,b.title))),h=[],f=[],g=[],_=[],w=[];for(let b=0;b<n.length;b++){let x=n[b];a.add(x.phase_number);let k=i.get(x.phase_number);if(k&&k.status==="done"&&k.text!==x.text){c.run(k.id);let L=u.run(t,x.phase_number,x.text,x.body,"open",b);_.push({id:Number(L.lastInsertRowid),phase_number:x.phase_number,text:x.text,replaced_shipped:k.id})}else if(k&&k.status==="done"&&d!==null&&!d.has(ze(x.phase_number,x.text)))p.run(x.text,x.body,k.id),h.push({id:k.id,phase_number:x.phase_number,text:x.text});else if(k)l.run(x.text,x.body,"stale","open",k.id),g.push({id:k.id,phase_number:x.phase_number,text:x.text,reactivated:k.status==="stale"});else{let L=u.run(t,x.phase_number,x.text,x.body,"open",b);_.push({id:Number(L.lastInsertRowid),phase_number:x.phase_number,text:x.text})}}for(let[b,x]of i)!a.has(b)&&x.status!=="stale"&&x.status!=="done"&&(r.run(x.id),w.push({id:x.id,phase_number:b}));if(h.length>0){let b=n.reduce((x,k)=>Math.max(x,k.phase_number),0);for(let[x,k]of i)k.status==="done"&&x>b&&(c.run(k.id),f.push({id:k.id,phase_number:x,text:k.text}))}return{updated:g.length,inserted:_.length,staled:w.length,repaired:h.length,retired:f.length,updated_phases:g,inserted_phases:_,staled_phases:w,repaired_phases:h,retired_phases:f}}function Zt(e){let t=e.match(/^---\n([\s\S]*?)\n---/);if(!t)return{};let n={};for(let s of t[1].split(`
28
- `)){let o=s.indexOf(":");o!==-1&&(n[s.slice(0,o).trim()]=s.slice(o+1).trim())}return n}function Qt(e){let t=B.join(e,"MEMORY.md");return F.existsSync(t)?F.readFileSync(t,"utf8"):""}var en=/^- \[(.+?)\]\((.+?)\)\s+—\s+(.*?)(?:\s+`\[(.+?)\]`)?\s*$/;function qe(e){let t=en.exec(e);return t?{name:t[1],file:t[2],description:t[3],type:t[4]||null}:null}function Ge(e){let t="- ["+e.name+"]("+e.file+") \u2014 "+e.description;return e.type?t+" `["+e.type+"]`":t}function tn(e,t){let n=B.join(e,"MEMORY.md"),s=new Map(t.map(l=>[l.file,l])),o=new Set,i=F.existsSync(n)?F.readFileSync(n,"utf8").split(`
29
- `):["# Memory Index",""],a=[];for(let l of i){let u=qe(l);if(!u){a.push(l);continue}let r=s.get(u.file);!r||o.has(r.file)||(o.add(r.file),a.push(Ge(r)))}for(;a.length>0&&a[a.length-1].trim()==="";)a.pop();for(let l of t)o.has(l.file)||(o.add(l.file),a.push(Ge(l)));F.writeFileSync(n,a.join(`
27
+ `).trim()||null}))}var qe=(e,t)=>e+" :: "+t;function Xe(e,t,n,s={}){let o=e.prepare("SELECT id, phase_number, status, text FROM main.roadmap_phases WHERE decision_name = ? AND phase_number IS NOT NULL ORDER BY phase_number").all(t),i=new Map(o.map(b=>[b.phase_number,b])),a=new Set,l=e.prepare("UPDATE main.roadmap_phases SET text = ?, body = ?, status = CASE WHEN status = ? THEN ? ELSE status END WHERE id = ?"),u=e.prepare("INSERT INTO main.roadmap_phases (decision_name, phase_number, parent_phase_id, text, body, status, position) VALUES (?, ?, NULL, ?, ?, ?, ?)"),r=e.prepare("UPDATE main.roadmap_phases SET status = 'stale' WHERE id = ?"),c=e.prepare("UPDATE main.roadmap_phases SET phase_number = NULL WHERE id = ?"),p=e.prepare("UPDATE main.roadmap_phases SET text = ?, body = ?, status = 'open', resolved_at = NULL WHERE id = ?"),m=s.completed??null,d=m===null?null:new Set(m.map(b=>qe(b.number,b.title))),h=[],f=[],g=[],_=[],w=[];for(let b=0;b<n.length;b++){let x=n[b];a.add(x.phase_number);let k=i.get(x.phase_number);if(k&&k.status==="done"&&k.text!==x.text){c.run(k.id);let L=u.run(t,x.phase_number,x.text,x.body,"open",b);_.push({id:Number(L.lastInsertRowid),phase_number:x.phase_number,text:x.text,replaced_shipped:k.id})}else if(k&&k.status==="done"&&d!==null&&!d.has(qe(x.phase_number,x.text)))p.run(x.text,x.body,k.id),h.push({id:k.id,phase_number:x.phase_number,text:x.text});else if(k)l.run(x.text,x.body,"stale","open",k.id),g.push({id:k.id,phase_number:x.phase_number,text:x.text,reactivated:k.status==="stale"});else{let L=u.run(t,x.phase_number,x.text,x.body,"open",b);_.push({id:Number(L.lastInsertRowid),phase_number:x.phase_number,text:x.text})}}for(let[b,x]of i)!a.has(b)&&x.status!=="stale"&&x.status!=="done"&&(r.run(x.id),w.push({id:x.id,phase_number:b}));if(h.length>0){let b=n.reduce((x,k)=>Math.max(x,k.phase_number),0);for(let[x,k]of i)k.status==="done"&&x>b&&(c.run(k.id),f.push({id:k.id,phase_number:x,text:k.text}))}return{updated:g.length,inserted:_.length,staled:w.length,repaired:h.length,retired:f.length,updated_phases:g,inserted_phases:_,staled_phases:w,repaired_phases:h,retired_phases:f}}function en(e){let t=e.match(/^---\n([\s\S]*?)\n---/);if(!t)return{};let n={};for(let s of t[1].split(`
28
+ `)){let o=s.indexOf(":");o!==-1&&(n[s.slice(0,o).trim()]=s.slice(o+1).trim())}return n}function tn(e){let t=B.join(e,"MEMORY.md");return F.existsSync(t)?F.readFileSync(t,"utf8"):""}var nn=/^- \[(.+?)\]\((.+?)\)\s+—\s+(.*?)(?:\s+`\[(.+?)\]`)?\s*$/;function Ze(e){let t=nn.exec(e);return t?{name:t[1],file:t[2],description:t[3],type:t[4]||null}:null}function Ve(e){let t="- ["+e.name+"]("+e.file+") \u2014 "+e.description;return e.type?t+" `["+e.type+"]`":t}function sn(e,t){let n=B.join(e,"MEMORY.md"),s=new Map(t.map(l=>[l.file,l])),o=new Set,i=F.existsSync(n)?F.readFileSync(n,"utf8").split(`
29
+ `):["# Memory Index",""],a=[];for(let l of i){let u=Ze(l);if(!u){a.push(l);continue}let r=s.get(u.file);!r||o.has(r.file)||(o.add(r.file),a.push(Ve(r)))}for(;a.length>0&&a[a.length-1].trim()==="";)a.pop();for(let l of t)o.has(l.file)||(o.add(l.file),a.push(Ve(l)));F.writeFileSync(n,a.join(`
30
30
  `).replace(/\n{3,}/g,`
31
31
 
32
32
  `)+`
33
- `,"utf8")}function nn(e){let t=[];for(let n of Qt(e).split(`
34
- `)){let s=qe(n);s&&t.push(s)}return t}function sn(e){let t=B.join(e,"MEMORY.bak");if(!F.existsSync(t))return t;for(let n=2;n<=1e3;n++){let s=B.join(e,"MEMORY.bak."+n);if(!F.existsSync(s))return s}return null}function Ve(e,{dryRun:t=!1}={}){if(!F.existsSync(e))return{repaired:!1,reason:"no memory directory",dir:e};let n=nn(e),s=new Set(n.map(r=>r.file)),o=F.readdirSync(e).filter(r=>r.endsWith(".md")&&r!=="MEMORY.md"&&!/^MEMORY\.bak(\.\d+)?\.md$/.test(r)).sort(),i=[],a=[];for(let r of o){if(s.has(r))continue;let c;try{c=Zt(F.readFileSync(B.join(e,r),"utf8"))}catch(p){a.push({file:r,reason:p.message});continue}i.push({name:c.name||r.replace(/\.md$/,""),file:r,description:c.description||"(no description in frontmatter)",type:Xt.includes(c.type)?c.type:null})}let l=n.filter(r=>!F.existsSync(B.join(e,r.file))).map(r=>({name:r.name,file:r.file})),u=null;if(i.length>0&&!t){let r=sn(e);if(r)try{F.copyFileSync(B.join(e,"MEMORY.md"),r),u=B.basename(r)}catch{}tn(e,[...n,...i])}return{repaired:i.length>0&&!t,dry_run:t,dir:e,files_on_disk:o.length,already_indexed:n.length,restored:i.length,restored_entries:i.map(r=>({name:r.name,file:r.file,type:r.type})),dangling:l,unreadable:a,backup:u}}import v from"fs";import ee from"path";var P="roadmap",on=`# Roadmap
33
+ `,"utf8")}function on(e){let t=[];for(let n of tn(e).split(`
34
+ `)){let s=Ze(n);s&&t.push(s)}return t}function rn(e){let t=B.join(e,"MEMORY.bak");if(!F.existsSync(t))return t;for(let n=2;n<=1e3;n++){let s=B.join(e,"MEMORY.bak."+n);if(!F.existsSync(s))return s}return null}function Qe(e,{dryRun:t=!1}={}){if(!F.existsSync(e))return{repaired:!1,reason:"no memory directory",dir:e};let n=on(e),s=new Set(n.map(r=>r.file)),o=F.readdirSync(e).filter(r=>r.endsWith(".md")&&r!=="MEMORY.md"&&!/^MEMORY\.bak(\.\d+)?\.md$/.test(r)).sort(),i=[],a=[];for(let r of o){if(s.has(r))continue;let c;try{c=en(F.readFileSync(B.join(e,r),"utf8"))}catch(p){a.push({file:r,reason:p.message});continue}i.push({name:c.name||r.replace(/\.md$/,""),file:r,description:c.description||"(no description in frontmatter)",type:Qt.includes(c.type)?c.type:null})}let l=n.filter(r=>!F.existsSync(B.join(e,r.file))).map(r=>({name:r.name,file:r.file})),u=null;if(i.length>0&&!t){let r=rn(e);if(r)try{F.copyFileSync(B.join(e,"MEMORY.md"),r),u=B.basename(r)}catch{}sn(e,[...n,...i])}return{repaired:i.length>0&&!t,dry_run:t,dir:e,files_on_disk:o.length,already_indexed:n.length,restored:i.length,restored_entries:i.map(r=>({name:r.name,file:r.file,type:r.type})),dangling:l,unreadable:a,backup:u}}import v from"fs";import ee from"path";var P="roadmap",an=`# Roadmap
35
35
 
36
36
  The forward-looking plan: only upcoming and in-progress phases live here. When a
37
37
  phase is marked done (cairn_roadmap set_status <id> done) it is removed from this
@@ -42,16 +42,16 @@ the plan \u2014 Cairn syncs it into the live tracker on the next cairn_roadmap r
42
42
 
43
43
  ### Phase 1: First phase title
44
44
  What this phase delivers.
45
- `;function q(){return ee.join(G(),"roadmap.md")}function rn(){return ee.join(G(),"roadmap_completed.md")}function ae(){let e=q();return v.existsSync(e)?v.readFileSync(e,"utf8"):""}function ke(){let e=q();return v.existsSync(e)?!1:(v.writeFileSync(e,on,"utf8"),!0)}function an(e){let t=[];for(let n of e){let s=(n.text||"").trim();s&&(!Number.isInteger(n.phase_number)||n.phase_number<1||t.push({phase_number:n.phase_number,text:s,body:n.body??null}))}return t}var be="decision_production_roadmap.md";function cn(e){let t=e.match(/^---\n[\s\S]*?\n---\n?/);return t?e.slice(t[0].length):e}function ln(e,t){let n=ee.join(e,"MEMORY.md");if(!v.existsSync(n))return;let s=v.readFileSync(n,"utf8").split(/\r?\n/).filter(o=>!o.includes(`](${t})`));v.writeFileSync(n,s.join(`
46
- `),"utf8")}function un(){let e=q();if(v.existsSync(e))return null;let t=[],n=me();n&&t.push(n),t.push(ee.join(G(),"memory"));let s=null,o=null;for(let a of t){let l=ee.join(a,be);s==null&&v.existsSync(l)&&(s=cn(v.readFileSync(l,"utf8")).trim(),o=l)}if(s==null)return null;let i=/^#\s/m.test(s)?"":`# Roadmap
45
+ `;function q(){return ee.join(G(),"roadmap.md")}function cn(){return ee.join(G(),"roadmap_completed.md")}function ce(){let e=q();return v.existsSync(e)?v.readFileSync(e,"utf8"):""}function Ee(){let e=q();return v.existsSync(e)?!1:(v.writeFileSync(e,an,"utf8"),!0)}function ln(e){let t=[];for(let n of e){let s=(n.text||"").trim();s&&(!Number.isInteger(n.phase_number)||n.phase_number<1||t.push({phase_number:n.phase_number,text:s,body:n.body??null}))}return t}var ke="decision_production_roadmap.md";function un(e){let t=e.match(/^---\n[\s\S]*?\n---\n?/);return t?e.slice(t[0].length):e}function pn(e,t){let n=ee.join(e,"MEMORY.md");if(!v.existsSync(n))return;let s=v.readFileSync(n,"utf8").split(/\r?\n/).filter(o=>!o.includes(`](${t})`));v.writeFileSync(n,s.join(`
46
+ `),"utf8")}function dn(){let e=q();if(v.existsSync(e))return null;let t=[],n=he();n&&t.push(n),t.push(ee.join(G(),"memory"));let s=null,o=null;for(let a of t){let l=ee.join(a,ke);s==null&&v.existsSync(l)&&(s=un(v.readFileSync(l,"utf8")).trim(),o=l)}if(s==null)return null;let i=/^#\s/m.test(s)?"":`# Roadmap
47
47
 
48
48
  `;v.writeFileSync(e,i+s+`
49
- `,"utf8");for(let a of t){let l=ee.join(a,be);try{v.existsSync(l)&&v.unlinkSync(l)}catch{}try{ln(a,be)}catch{}}return{migrated:!0,from:o,to:e}}function te(e){un();let t=q();if(!v.existsSync(t))return null;let n=v.readFileSync(t,"utf8");if(!/^##\s+Phases\s*$/im.test(n))return null;let s=an(se(n));return Ke(e,P,s,{completed:Ee()})}var pn=/^-\s+(\d{4}-\d{2}-\d{2})\s+—\s+Phase\s+(\d+):\s*(.+?)(?:\s+\(([^)]*)\))?\s*$/,Xe=/^phase-(\d+)$/,dn=/^[0-9a-f]{7,40}$/;function Ee(){let e=rn();if(!v.existsSync(e))return null;let t=[];for(let n of v.readFileSync(e,"utf8").split(/\r?\n/)){let s=pn.exec(n);if(!s)continue;let o=s[3],i=null,a=null;if(s[4]!=null){let l=s[4].split(/\s*,\s*/).filter(Boolean);if(l.length>0&&l.every(r=>Xe.test(r)||dn.test(r)))for(let r of l){let c=Xe.exec(r);c?i=parseInt(c[1],10):a=r}else o=`${o} (${s[4]})`}t.push({date:s[1],number:parseInt(s[2],10),title:o,id:i,commit:a})}return t}import ne from"fs";import $e from"path";import{execSync as fn}from"child_process";var Ze=120;function ve(e){if(!e)return"";let t=String(e).replace(/`([^`]*)`/g,"$1").replace(/\*\*([^*]+)\*\*/g,"$1").replace(/\*([^*]+)\*/g,"$1").replace(/\[([^\]]+)\]\([^)]*\)/g,"$1").replace(/^[-*+\s]+/,"").replace(/^#{1,6}\s*/,"").replace(/[*~]/g,"").replace(/\s+/g," ").trim().replace(/[::.\s]+$/,"");return t.length>Ze&&(t=t.slice(0,Ze-1).trimEnd()+"\u2026"),t}function Y(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function mn(e){if(!e||typeof e!="string")return[];let t=e.split(/\r?\n/),n=r=>r.map(c=>({text:ve(c.titleRaw),body:c.bodyLines.join(`
50
- `).trim()||null})).filter(c=>c.text),s=[],o=null;for(let r of t){let c=/^(#{1,6})\s+(.+?)\s*$/.exec(r);if(c&&c[1].length>=2&&c[1].length<=4){o&&s.push(o),o={titleRaw:c[2],bodyLines:[]};continue}o&&o.bodyLines.push(r)}o&&s.push(o);let i=[];o=null;for(let r of t){let c=/^(\d+)[.)]\s+(.+?)\s*$/.exec(r);if(c){o&&i.push(o),o={titleRaw:c[2],bodyLines:[]};continue}o&&o.bodyLines.push(r)}o&&i.push(o);let a=[n(s),n(i)].filter(r=>r.length>=2);if(a.length)return a.sort((r,c)=>c.length-r.length),a[0];let l=t.find(r=>r.trim());return[{text:ve(l||"Plan")||"Plan",body:e.trim()||null}]}function hn(e){let t=[];return e.forEach((n,s)=>{t.push(`### Phase ${s+1}: ${n.text}`),n.body&&n.body.trim()&&t.push(n.body.trim()),t.push("")}),t.join(`
49
+ `,"utf8");for(let a of t){let l=ee.join(a,ke);try{v.existsSync(l)&&v.unlinkSync(l)}catch{}try{pn(a,ke)}catch{}}return{migrated:!0,from:o,to:e}}function te(e){dn();let t=q();if(!v.existsSync(t))return null;let n=v.readFileSync(t,"utf8");if(!/^##\s+Phases\s*$/im.test(n))return null;let s=ln(se(n));return Xe(e,P,s,{completed:$e()})}var fn=/^-\s+(\d{4}-\d{2}-\d{2})\s+—\s+Phase\s+(\d+):\s*(.+?)(?:\s+\(([^)]*)\))?\s*$/,et=/^phase-(\d+)$/,mn=/^[0-9a-f]{7,40}$/;function $e(){let e=cn();if(!v.existsSync(e))return null;let t=[];for(let n of v.readFileSync(e,"utf8").split(/\r?\n/)){let s=fn.exec(n);if(!s)continue;let o=s[3],i=null,a=null;if(s[4]!=null){let l=s[4].split(/\s*,\s*/).filter(Boolean);if(l.length>0&&l.every(r=>et.test(r)||mn.test(r)))for(let r of l){let c=et.exec(r);c?i=parseInt(c[1],10):a=r}else o=`${o} (${s[4]})`}t.push({date:s[1],number:parseInt(s[2],10),title:o,id:i,commit:a})}return t}import ne from"fs";import Re from"path";import{execSync as hn}from"child_process";var tt=120;function Oe(e){if(!e)return"";let t=String(e).replace(/`([^`]*)`/g,"$1").replace(/\*\*([^*]+)\*\*/g,"$1").replace(/\*([^*]+)\*/g,"$1").replace(/\[([^\]]+)\]\([^)]*\)/g,"$1").replace(/^[-*+\s]+/,"").replace(/^#{1,6}\s*/,"").replace(/[*~]/g,"").replace(/\s+/g," ").trim().replace(/[::.\s]+$/,"");return t.length>tt&&(t=t.slice(0,tt-1).trimEnd()+"\u2026"),t}function Y(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function yn(e){if(!e||typeof e!="string")return[];let t=e.split(/\r?\n/),n=r=>r.map(c=>({text:Oe(c.titleRaw),body:c.bodyLines.join(`
50
+ `).trim()||null})).filter(c=>c.text),s=[],o=null;for(let r of t){let c=/^(#{1,6})\s+(.+?)\s*$/.exec(r);if(c&&c[1].length>=2&&c[1].length<=4){o&&s.push(o),o={titleRaw:c[2],bodyLines:[]};continue}o&&o.bodyLines.push(r)}o&&s.push(o);let i=[];o=null;for(let r of t){let c=/^(\d+)[.)]\s+(.+?)\s*$/.exec(r);if(c){o&&i.push(o),o={titleRaw:c[2],bodyLines:[]};continue}o&&o.bodyLines.push(r)}o&&i.push(o);let a=[n(s),n(i)].filter(r=>r.length>=2);if(a.length)return a.sort((r,c)=>c.length-r.length),a[0];let l=t.find(r=>r.trim());return[{text:Oe(l||"Plan")||"Plan",body:e.trim()||null}]}function gn(e){let t=[];return e.forEach((n,s)=>{t.push(`### Phase ${s+1}: ${n.text}`),n.body&&n.body.trim()&&t.push(n.body.trim()),t.push("")}),t.join(`
51
51
  `).replace(/\n{3,}/g,`
52
52
 
53
53
  `).trimEnd()+`
54
- `}function Qe(e){let t=ae(),n=t.split(/\r?\n/),s=n.findIndex(r=>/^##\s+Phases\s*$/i.test(r)),o=hn(e);if(s===-1){let r=t.replace(/\s*$/,"");ne.writeFileSync(q(),`${r}
54
+ `}function nt(e){let t=ce(),n=t.split(/\r?\n/),s=n.findIndex(r=>/^##\s+Phases\s*$/i.test(r)),o=gn(e);if(s===-1){let r=t.replace(/\s*$/,"");ne.writeFileSync(q(),`${r}
55
55
 
56
56
  ## Phases
57
57
 
@@ -60,58 +60,58 @@ ${o}`,"utf8");return}let i=n.length;for(let r=s+1;r<n.length;r++)if(/^##\s+/.tes
60
60
  `).replace(/^\n+/,""),u=`
61
61
 
62
62
  ${o}${l.trim()?`
63
- `+l:""}`;ne.writeFileSync(q(),a+u,"utf8")}function et(e){let t=mn(e);if(t.length===0)return{added:[],skipped:[],total:0};let s=ke()?[]:se(ae()).map(u=>({text:u.text,body:u.body})),o=new Set(s.map(u=>Y(u.text))),i=[...s],a=[],l=[];for(let u of t){let r=Y(u.text);if(o.has(r)){l.push(u.text);continue}o.add(r),i.push(u),a.push(u.text)}return a.length>0&&Qe(i),{added:a,skipped:l,total:i.length}}function yn(e){return e==="completed"?"done":e==="in_progress"?"in_progress":"open"}function gn(e){try{return e.prepare("SELECT phase_id FROM main.roadmap_cursor WHERE singleton = 1").get()?.phase_id??null}catch{return null}}function tt(e,t){if(!Array.isArray(t)||t.length===0)return{changed:!1};let n=t.map(m=>({text:ve(m.content||m.activeForm||""),status:yn(m.status)})).filter(m=>m.text);if(n.length===0)return{changed:!1};let s=e.prepare("SELECT id, status FROM main.roadmap_phases WHERE decision_name = ? AND parent_phase_id IS NULL").all(P).filter(m=>m.status!=="stale");if(s.length===0){let d=ke()?[]:se(ae()).map(w=>({text:w.text,body:w.body})),h=new Set(d.map(w=>Y(w.text))),f=[...d],g=0;for(let w of n){let b=Y(w.text);h.has(b)||(h.add(b),f.push({text:w.text,body:null}),g++)}if(g===0)return{changed:!1};Qe(f);let _=te(e);return{changed:!0,seeded:g,diff:_}}let o=gn(e);o==null&&(o=s.find(m=>m.status==="in_progress")?.id??s.find(m=>m.status==="open")?.id??s[0].id);let i=e.prepare("SELECT id, text, status FROM main.roadmap_phases WHERE parent_phase_id = ?").all(o),a=new Map(i.map(m=>[Y(m.text),m])),l=e.prepare("INSERT INTO main.roadmap_phases (decision_name, phase_number, parent_phase_id, text, status, position) VALUES (NULL, NULL, ?, ?, ?, ?)"),u=e.prepare("UPDATE main.roadmap_phases SET status = ? WHERE id = ?"),r=e.prepare("SELECT COALESCE(MAX(position), -1) + 1 AS n FROM main.roadmap_phases WHERE parent_phase_id = ?").get(o).n,c=0,p=0;for(let m of n){let d=a.get(Y(m.text));if(d){if(d.status==="done"&&m.status!=="done")continue;d.status!==m.status&&(u.run(m.status,d.id),p++)}else l.run(o,m.text,m.status,r++),c++}return{changed:c>0||p>0,parentId:o,inserted:c,updated:p}}var _n=new Set(["the","and","for","with","from","into","that","this","then","your","add","fix","update","make","phase","support","implement","feature","refactor","wire","wires","should","will","also","use","using","via"]);function Sn(e){return[...new Set(Y(e).split(" ").filter(t=>t.length>=4&&!_n.has(t)))]}function xn(e,t){if(!e||!t?.length)return null;let n=String(e),s=/\bphase\s*#?\s*(\d+)\b/i.exec(n);if(s){let a=parseInt(s[1],10),l=t.find(u=>u.phase_number===a);if(l)return{...l,confidence:"high"}}let o=new Set(Y(n).split(" ").filter(Boolean)),i=null;for(let a of t){let l=Sn(a.text);if(l.length<2)continue;let u=l.filter(c=>o.has(c)),r=u.length/l.length;u.length>=2&&r>=.6&&(!i||r>i.ratio)&&(i={...a,confidence:"medium",ratio:r})}return i?(delete i.ratio,i):null}function Re(e,t){return fn(`git ${t}`,{cwd:e,encoding:"utf8",timeout:5e3}).trim()}function nt(e,t){let n=$e.join(t,".cairn"),s=$e.join(n,".last-commit"),o=$e.join(n,"roadmap-pending.json"),i;try{i=Re(t,"rev-parse HEAD")}catch{return{candidates:[],newCommits:0}}let a=null;try{a=ne.readFileSync(s,"utf8").trim()||null}catch{}let l=[];if(a&&a!==i)try{l=Re(t,`rev-list --no-merges --reverse ${a}..HEAD`).split(`
64
- `).filter(Boolean)}catch{l=[]}try{ne.writeFileSync(s,i,"utf8")}catch{}let u=[];try{let r=JSON.parse(ne.readFileSync(o,"utf8"));Array.isArray(r)&&(u=r)}catch{}if(l.length){let r=e.prepare("SELECT id, phase_number, text FROM main.roadmap_phases WHERE decision_name = ? AND parent_phase_id IS NULL AND status IN ('open','in_progress') AND phase_number IS NOT NULL").all(P);for(let c of l){let p="";try{p=Re(t,`show -s --format=%s%n%b ${c}`)}catch{}let m=xn(p,r);m&&!u.some(d=>d.phase_id===m.id)&&u.push({phase_id:m.id,phase_number:m.phase_number,text:m.text,commit:c.slice(0,7),confidence:m.confidence})}}if(u.length){let r=new Set(e.prepare("SELECT id FROM main.roadmap_phases WHERE status IN ('open','in_progress')").all().map(c=>c.id));u=u.filter(c=>r.has(c.phase_id))}try{ne.writeFileSync(o,JSON.stringify(u),"utf8")}catch{}return{candidates:u,newCommits:l.length}}import U from"fs";import T from"path";var ce="cairn",X="roadmap/1",wn="<!-- slot:roadmap";function bn(e){return`${wn} format=${X} provider=${ce} generated=${e} -->`}var kn=`<!-- ${ce}:roadmap -->`,En=/^<!--\s*slot:roadmap\b([^>]*?)-->/,$n=/^<!--\s*([A-Za-z0-9_.-]+)\s*:\s*roadmap\s*-->/,Rn=/([A-Za-z_][A-Za-z0-9_-]*)=("[^"]*"|\S+)/g;function vn(e){if(typeof e!="string")return null;let t=e.indexOf(`
65
- `);return(t===-1?e:e.slice(0,t)).trim()}function On(e){let t=vn(e);if(!t)return null;let n=En.exec(t);if(n){let o={};for(let i of n[1].matchAll(Rn))o[i[1]]=i[2].replace(/^"|"$/g,"");return o.provider?{provider:o.provider.toLowerCase(),format:o.format||null,generated:o.generated||null}:null}let s=$n.exec(t);return s?{provider:s[1].toLowerCase(),format:null,generated:null}:null}var jn=/^```roadmap-json[ \t]*\r?\n([\s\S]*?)\r?\n```[ \t]*$/m;function rt(e){if(typeof e!="string")return null;let t=jn.exec(e);if(!t)return null;try{let n=JSON.parse(t[1]);return n&&typeof n=="object"&&!Array.isArray(n)?n:null}catch{return null}}var Pn=new Set(["format","provider","generated","title","cursor","phases","migrated_from","migrated"]),Tn=new Set(["id","number","title","status","goal","blocked_by","slices","detail","links"]);function it(e,t){let n={};if(!e||typeof e!="object")return n;for(let s of Object.keys(e).sort())t.has(s)||(n[s]=e[s]);return n}function at(e,t=new Map){if(!Array.isArray(e))return t;for(let n of e)n&&typeof n=="object"&&typeof n.id=="string"&&(t.set(n.id,n),at(n.slices,t));return t}function Mn(e){if(typeof e!="string")return null;let t=rt(e)?.migrated_from;if(typeof t=="string"&&t)return t;let n=e.indexOf(`
66
- `);if(n===-1)return null;let s=e.indexOf("-->",n),o=s===-1?e.slice(0,512):e.slice(n,s),i=/^\s*migrated-from=(.+?)\s*$/m.exec(o);return i?i[1]:null}var Nn=/\[\[([^\]|#\n]+?)(?:[|#][^\]\n]*)?\]\]/g,An=["decision","knowledge","experience","preference"];function Fn(e){let t=T.relative(z(),e).split(T.sep).join("/");return!t||t.startsWith("..")||T.isAbsolute(t)?null:t}function Ln(e){let t=T.join(z(),".cairn","memory");return[T.join(t,`${e}.md`),...An.map(n=>T.join(t,`${n}_${e}.md`))]}function Cn(e){if(!e)return[];let t=[],n=new Set;for(let s of String(e).matchAll(Nn)){let o=s[1].trim();if(!(!o||n.has(o))){n.add(o);for(let i of Ln(o)){if(!U.existsSync(i))continue;let a=Fn(i);a&&t.push({kind:"memory",title:o,path:a});break}}}return t}var Dn={open:"open",in_progress:"in progress",done:"done",blocked:"blocked",stale:"stale"},In=new Set(["open","in_progress","done","blocked","stale"]);function Wn(e){return e==="done"?"x":e==="in_progress"?"~":e==="blocked"?"!":" "}function ct(){return T.join(z(),"ROADMAP.md")}function lt(){return new Date().toISOString().replace(/\.\d{3}Z$/,"Z")}function Un(e){try{return e.prepare("SELECT phase_id FROM main.roadmap_cursor WHERE singleton = 1").get()?.phase_id??null}catch{return null}}function Hn(e){let t=new Map,n;try{n=e.prepare("SELECT blocker_id, blocked_id FROM main.roadmap_phase_deps").all()}catch{return t}for(let s of n)t.has(s.blocked_id)||t.set(s.blocked_id,new Set),t.get(s.blocked_id).add(s.blocker_id);return t}function Jn(e,t){let n=e.decision_name??"",s=t.decision_name??"";return n!==s?n<s?-1:1:e.phase_number!=null&&t.phase_number!=null?e.phase_number-t.phase_number:e.position-t.position||e.id-t.id}function Bn(e){let t;try{t=e.prepare("SELECT id, decision_name, phase_number, parent_phase_id, text, body, status, position FROM main.roadmap_phases ORDER BY decision_name, phase_number, position, id").all()}catch{return null}if(t.filter(f=>f.status!=="stale").length===0)return null;let n=Un(e),s=Hn(e),o=new Map(t.map(f=>[f.id,f.status])),i=new Map(t.map(f=>[f.id,f.phase_number!=null?`Phase ${f.phase_number}`:f.text])),a=new Map(t.map(f=>[f.id,f])),l=new Map;for(let f of t){let g=f.parent_phase_id??"root";l.has(g)||l.set(g,[]),l.get(g).push(f)}for(let f of l.values())f.sort(Jn);let u=new Map;for(let f of l.get("root")||[])f.decision_name!=null&&(f.decision_name===P&&f.phase_number==null||(u.has(f.decision_name)||u.set(f.decision_name,[]),u.get(f.decision_name).push(f)));let r=[...u.keys()].sort((f,g)=>f===P?-1:g===P?1:f<g?-1:f>g?1:0),c=new Map,p=!1;for(let f of r){let g=u.get(f).filter(w=>w.status!=="stale"),_=f===P?g.filter(w=>w.status!=="done"):g;_.length!==0&&(p=!0,c.set(f,{visible:_,doneCount:g.filter(w=>w.status==="done").length,total:g.length}))}return p?{cursor:n,decisionNames:r,visibleRoots:c,children:f=>(l.get(f)||[]).filter(g=>g.status!=="stale"),unfinishedBlockers:f=>{let g=s.get(f);return g?[...g].filter(_=>o.get(_)!=="done").map(_=>i.get(_)||`#${_}`):[]},blockerIds:f=>{let g=s.get(f);return g?[...g].filter(_=>a.has(_)).sort((_,w)=>_-w).map(_=>`phase-${_}`):[]}}:null}var Oe=120;function Yn(e){return e.replace(/\s*\((?:see\s+)?\[\[[^\]]+\]\](?:\s*(?:,|and)\s*\[\[[^\]]+\]\])*\s*\)/gi,"").replace(/\[\[([^\]]+)\]\]/g,(t,n)=>n.replace(/_/g," ").trim()).replace(/\s{2,}/g," ").replace(/\s+([.,;:!?])/g,"$1").trim()}function ut(e){if(!e)return null;let t=[];for(let a of e.split(`
67
- `)){let l=a.trim();if(!l){if(t.length)break;continue}t.push(l)}if(!t.length)return null;let n=Yn(t.join(" ").replace(/\s+/g," ")),s=null,o=/[.?!](?=\s|$)/g,i;for(;(i=o.exec(n))!==null&&(s=n.slice(0,i.index+1),!(s.length>=40)););if(s||(s=n),s.length>Oe){let a=s.lastIndexOf(" ",Oe-1);s=s.slice(0,a>60?a:Oe-1).trimEnd()+"\u2026"}return s}var zn=8;function st(){let e=Ee();if(!e||e.length===0)return[];let t=e.slice(-zn).reverse(),n=["","---","","## Recently shipped",""];for(let o of t)n.push(`- ${o.date} \u2014 ${o.title}${o.commit?` \`${o.commit}\``:""}`);let s=e.length-t.length;return n.push("",s>0?`_${e.length} phases shipped in total; ${s} earlier one${s===1?"":"s"} not listed._`:`_${e.length} phase${e.length===1?"":"s"} shipped in total._`),n}function ot(e){let t=e.doneCount,n=e.total;if(!n)return"_No phases yet._";let s=Math.round(t/n*10);return`**${t} of ${n} done** \`${"\u2588".repeat(s)}${"\u2591".repeat(10-s)}\``}function Gn(e,t){let n=[bn(t.generated),"<!--"," Generated by cairn \u2014 do NOT edit by hand."," The plan lives in .cairn/roadmap.md; status and cursor come from cairn_roadmap."," This file is regenerated whenever the roadmap changes; manual edits are overwritten."," Per-phase detail and links are in the roadmap-json block at the end of this file."];if(t.migratedFrom&&(n.push(" The ROADMAP.md that was here before cairn adopted this file is kept at:"),n.push(` migrated-from=${t.migratedFrom}`)),n.push("-->","","# Roadmap",""),e==null)return n.push("_No active plan right now._"),n.push(...st()),n;let{cursor:s,decisionNames:o,visibleRoots:i,children:a,unfinishedBlockers:l}=e;n.push("Legend: `[x]` done \xB7 `[~]` in progress \xB7 `[ ]` open \xB7 `[!]` blocked.");let u=(r,c)=>{let p=" ".repeat(c),m=r.phase_number!=null?`Phase ${r.phase_number}: ${r.text}`:r.text,d=Dn[r.status]||r.status,h=l(r.id);h.length&&(d+=` \xB7 waiting on ${h.join(", ")}`),s===r.id&&(d+=" \xB7 \u2190 current"),n.push(`${p}- [${Wn(r.status)}] **${m}** \u2014 ${d}`);let f=ut(r.body);f&&n.push(`${p} ${f}`);for(let g of a(r.id))u(g,c+1)};for(let r of o){let c=i.get(r);if(c){n.push(""),r===P?n.push("## Active plan","",ot(c)):n.push(`## ${r}`,"",ot(c)),n.push("");for(let p of c.visible)u(p,0)}}return n.push(...st()),n}function pt(e,t,n,s){let o=n.get(`phase-${e.id}`),i=ut(e.body),a=Cn(e.body),l=t.blockerIds(e.id),u={id:`phase-${e.id}`};if(e.phase_number!=null&&(u.number=e.phase_number),u.title=e.text,u.status=In.has(e.status)?e.status:"open",i&&(u.goal=i),l.length&&(u.blocked_by=l),!s){let r=[],c=p=>{for(let m of t.children(p))r.push(pt(m,t,n,!0)),c(m.id)};c(e.id),r.length&&(u.slices=r)}return e.body&&e.body.trim()&&(u.detail=e.body.trim()),a.length&&(u.links=a),{...u,...it(o,Tn)}}function Kn(e,t){let n=at(t.previous?.phases),s=[];for(let a of e?.decisionNames??[]){let l=e.visibleRoots.get(a);if(l)for(let u of l.visible)s.push(pt(u,e,n,!1))}let o={format:X,provider:ce,generated:t.generated,title:`${T.basename(z())} roadmap`,cursor:e?.cursor!=null?`phase-${e.cursor}`:null};t.migratedFrom&&(o.migrated_from=t.migratedFrom,t.migrated&&(o.migrated=t.migrated)),o.phases=s;let i={...o,...it(t.previous,Pn)};return["","<details>","<summary>Machine-readable plan (roadmap/1)</summary>","","```roadmap-json",JSON.stringify(i,null,2),"```","","</details>"]}function je(e,t={}){let n={generated:t.generated||lt(),migratedFrom:t.migratedFrom||null,migrated:t.migrated||null,previous:t.previous&&typeof t.previous=="object"?t.previous:null},s=Bn(e);return s==null&&!t.allowEmpty?null:[...Gn(s,n),...Kn(s,n)].join(`
63
+ `+l:""}`;ne.writeFileSync(q(),a+u,"utf8")}function st(e){let t=yn(e);if(t.length===0)return{added:[],skipped:[],total:0};let s=Ee()?[]:se(ce()).map(u=>({text:u.text,body:u.body})),o=new Set(s.map(u=>Y(u.text))),i=[...s],a=[],l=[];for(let u of t){let r=Y(u.text);if(o.has(r)){l.push(u.text);continue}o.add(r),i.push(u),a.push(u.text)}return a.length>0&&nt(i),{added:a,skipped:l,total:i.length}}function _n(e){return e==="completed"?"done":e==="in_progress"?"in_progress":"open"}function Sn(e){try{return e.prepare("SELECT phase_id FROM main.roadmap_cursor WHERE singleton = 1").get()?.phase_id??null}catch{return null}}function ot(e,t){if(!Array.isArray(t)||t.length===0)return{changed:!1};let n=t.map(m=>({text:Oe(m.content||m.activeForm||""),status:_n(m.status)})).filter(m=>m.text);if(n.length===0)return{changed:!1};let s=e.prepare("SELECT id, status FROM main.roadmap_phases WHERE decision_name = ? AND parent_phase_id IS NULL").all(P).filter(m=>m.status!=="stale");if(s.length===0){let d=Ee()?[]:se(ce()).map(w=>({text:w.text,body:w.body})),h=new Set(d.map(w=>Y(w.text))),f=[...d],g=0;for(let w of n){let b=Y(w.text);h.has(b)||(h.add(b),f.push({text:w.text,body:null}),g++)}if(g===0)return{changed:!1};nt(f);let _=te(e);return{changed:!0,seeded:g,diff:_}}let o=Sn(e);o==null&&(o=s.find(m=>m.status==="in_progress")?.id??s.find(m=>m.status==="open")?.id??s[0].id);let i=e.prepare("SELECT id, text, status FROM main.roadmap_phases WHERE parent_phase_id = ?").all(o),a=new Map(i.map(m=>[Y(m.text),m])),l=e.prepare("INSERT INTO main.roadmap_phases (decision_name, phase_number, parent_phase_id, text, status, position) VALUES (NULL, NULL, ?, ?, ?, ?)"),u=e.prepare("UPDATE main.roadmap_phases SET status = ? WHERE id = ?"),r=e.prepare("SELECT COALESCE(MAX(position), -1) + 1 AS n FROM main.roadmap_phases WHERE parent_phase_id = ?").get(o).n,c=0,p=0;for(let m of n){let d=a.get(Y(m.text));if(d){if(d.status==="done"&&m.status!=="done")continue;d.status!==m.status&&(u.run(m.status,d.id),p++)}else l.run(o,m.text,m.status,r++),c++}return{changed:c>0||p>0,parentId:o,inserted:c,updated:p}}var xn=new Set(["the","and","for","with","from","into","that","this","then","your","add","fix","update","make","phase","support","implement","feature","refactor","wire","wires","should","will","also","use","using","via"]);function wn(e){return[...new Set(Y(e).split(" ").filter(t=>t.length>=4&&!xn.has(t)))]}function bn(e,t){if(!e||!t?.length)return null;let n=String(e),s=/\bphase\s*#?\s*(\d+)\b/i.exec(n);if(s){let a=parseInt(s[1],10),l=t.find(u=>u.phase_number===a);if(l)return{...l,confidence:"high"}}let o=new Set(Y(n).split(" ").filter(Boolean)),i=null;for(let a of t){let l=wn(a.text);if(l.length<2)continue;let u=l.filter(c=>o.has(c)),r=u.length/l.length;u.length>=2&&r>=.6&&(!i||r>i.ratio)&&(i={...a,confidence:"medium",ratio:r})}return i?(delete i.ratio,i):null}function ve(e,t){return hn(`git ${t}`,{cwd:e,encoding:"utf8",timeout:5e3}).trim()}function rt(e,t){let n=Re.join(t,".cairn"),s=Re.join(n,".last-commit"),o=Re.join(n,"roadmap-pending.json"),i;try{i=ve(t,"rev-parse HEAD")}catch{return{candidates:[],newCommits:0}}let a=null;try{a=ne.readFileSync(s,"utf8").trim()||null}catch{}let l=[];if(a&&a!==i)try{l=ve(t,`rev-list --no-merges --reverse ${a}..HEAD`).split(`
64
+ `).filter(Boolean)}catch{l=[]}try{ne.writeFileSync(s,i,"utf8")}catch{}let u=[];try{let r=JSON.parse(ne.readFileSync(o,"utf8"));Array.isArray(r)&&(u=r)}catch{}if(l.length){let r=e.prepare("SELECT id, phase_number, text FROM main.roadmap_phases WHERE decision_name = ? AND parent_phase_id IS NULL AND status IN ('open','in_progress') AND phase_number IS NOT NULL").all(P);for(let c of l){let p="";try{p=ve(t,`show -s --format=%s%n%b ${c}`)}catch{}let m=bn(p,r);m&&!u.some(d=>d.phase_id===m.id)&&u.push({phase_id:m.id,phase_number:m.phase_number,text:m.text,commit:c.slice(0,7),confidence:m.confidence})}}if(u.length){let r=new Set(e.prepare("SELECT id FROM main.roadmap_phases WHERE status IN ('open','in_progress')").all().map(c=>c.id));u=u.filter(c=>r.has(c.phase_id))}try{ne.writeFileSync(o,JSON.stringify(u),"utf8")}catch{}return{candidates:u,newCommits:l.length}}import U from"fs";import T from"path";var le="cairn",X="roadmap/1",kn="<!-- slot:roadmap";function En(e){return`${kn} format=${X} provider=${le} generated=${e} -->`}var $n=`<!-- ${le}:roadmap -->`,Rn=/^<!--\s*slot:roadmap\b([^>]*?)-->/,vn=/^<!--\s*([A-Za-z0-9_.-]+)\s*:\s*roadmap\s*-->/,On=/([A-Za-z_][A-Za-z0-9_-]*)=("[^"]*"|\S+)/g;function jn(e){if(typeof e!="string")return null;let t=e.indexOf(`
65
+ `);return(t===-1?e:e.slice(0,t)).trim()}function Pn(e){let t=jn(e);if(!t)return null;let n=Rn.exec(t);if(n){let o={};for(let i of n[1].matchAll(On))o[i[1]]=i[2].replace(/^"|"$/g,"");return o.provider?{provider:o.provider.toLowerCase(),format:o.format||null,generated:o.generated||null}:null}let s=vn.exec(t);return s?{provider:s[1].toLowerCase(),format:null,generated:null}:null}var Tn=/^```roadmap-json[ \t]*\r?\n([\s\S]*?)\r?\n```[ \t]*$/m;function ct(e){if(typeof e!="string")return null;let t=Tn.exec(e);if(!t)return null;try{let n=JSON.parse(t[1]);return n&&typeof n=="object"&&!Array.isArray(n)?n:null}catch{return null}}var Nn=new Set(["format","provider","generated","title","cursor","phases","migrated_from","migrated"]),Mn=new Set(["id","number","title","status","goal","blocked_by","slices","detail","links"]);function lt(e,t){let n={};if(!e||typeof e!="object")return n;for(let s of Object.keys(e).sort())t.has(s)||(n[s]=e[s]);return n}function ut(e,t=new Map){if(!Array.isArray(e))return t;for(let n of e)n&&typeof n=="object"&&typeof n.id=="string"&&(t.set(n.id,n),ut(n.slices,t));return t}function An(e){if(typeof e!="string")return null;let t=ct(e)?.migrated_from;if(typeof t=="string"&&t)return t;let n=e.indexOf(`
66
+ `);if(n===-1)return null;let s=e.indexOf("-->",n),o=s===-1?e.slice(0,512):e.slice(n,s),i=/^\s*migrated-from=(.+?)\s*$/m.exec(o);return i?i[1]:null}var Fn=/\[\[([^\]|#\n]+?)(?:[|#][^\]\n]*)?\]\]/g,Ln=["decision","knowledge","experience","preference"];function Dn(e){let t=T.relative(z(),e).split(T.sep).join("/");return!t||t.startsWith("..")||T.isAbsolute(t)?null:t}function Cn(e){let t=T.join(z(),".cairn","memory");return[T.join(t,`${e}.md`),...Ln.map(n=>T.join(t,`${n}_${e}.md`))]}function In(e){if(!e)return[];let t=[],n=new Set;for(let s of String(e).matchAll(Fn)){let o=s[1].trim();if(!(!o||n.has(o))){n.add(o);for(let i of Cn(o)){if(!U.existsSync(i))continue;let a=Dn(i);a&&t.push({kind:"memory",title:o,path:a});break}}}return t}var Wn={open:"open",in_progress:"in progress",done:"done",blocked:"blocked",stale:"stale"},Un=new Set(["open","in_progress","done","blocked","stale"]);function Hn(e){return e==="done"?"x":e==="in_progress"?"~":e==="blocked"?"!":" "}function pt(){return T.join(z(),"ROADMAP.md")}function dt(){return new Date().toISOString().replace(/\.\d{3}Z$/,"Z")}function Jn(e){try{return e.prepare("SELECT phase_id FROM main.roadmap_cursor WHERE singleton = 1").get()?.phase_id??null}catch{return null}}function Bn(e){let t=new Map,n;try{n=e.prepare("SELECT blocker_id, blocked_id FROM main.roadmap_phase_deps").all()}catch{return t}for(let s of n)t.has(s.blocked_id)||t.set(s.blocked_id,new Set),t.get(s.blocked_id).add(s.blocker_id);return t}function Yn(e,t){let n=e.decision_name??"",s=t.decision_name??"";return n!==s?n<s?-1:1:e.phase_number!=null&&t.phase_number!=null?e.phase_number-t.phase_number:e.position-t.position||e.id-t.id}function zn(e){let t;try{t=e.prepare("SELECT id, decision_name, phase_number, parent_phase_id, text, body, status, position FROM main.roadmap_phases ORDER BY decision_name, phase_number, position, id").all()}catch{return null}if(t.filter(f=>f.status!=="stale").length===0)return null;let n=Jn(e),s=Bn(e),o=new Map(t.map(f=>[f.id,f.status])),i=new Map(t.map(f=>[f.id,f.phase_number!=null?`Phase ${f.phase_number}`:f.text])),a=new Map(t.map(f=>[f.id,f])),l=new Map;for(let f of t){let g=f.parent_phase_id??"root";l.has(g)||l.set(g,[]),l.get(g).push(f)}for(let f of l.values())f.sort(Yn);let u=new Map;for(let f of l.get("root")||[])f.decision_name!=null&&(f.decision_name===P&&f.phase_number==null||(u.has(f.decision_name)||u.set(f.decision_name,[]),u.get(f.decision_name).push(f)));let r=[...u.keys()].sort((f,g)=>f===P?-1:g===P?1:f<g?-1:f>g?1:0),c=new Map,p=!1;for(let f of r){let g=u.get(f).filter(w=>w.status!=="stale"),_=f===P?g.filter(w=>w.status!=="done"):g;_.length!==0&&(p=!0,c.set(f,{visible:_,doneCount:g.filter(w=>w.status==="done").length,total:g.length}))}return p?{cursor:n,decisionNames:r,visibleRoots:c,children:f=>(l.get(f)||[]).filter(g=>g.status!=="stale"),unfinishedBlockers:f=>{let g=s.get(f);return g?[...g].filter(_=>o.get(_)!=="done").map(_=>i.get(_)||`#${_}`):[]},blockerIds:f=>{let g=s.get(f);return g?[...g].filter(_=>a.has(_)).sort((_,w)=>_-w).map(_=>`phase-${_}`):[]}}:null}var je=120;function Gn(e){return e.replace(/\s*\((?:see\s+)?\[\[[^\]]+\]\](?:\s*(?:,|and)\s*\[\[[^\]]+\]\])*\s*\)/gi,"").replace(/\[\[([^\]]+)\]\]/g,(t,n)=>n.replace(/_/g," ").trim()).replace(/\s{2,}/g," ").replace(/\s+([.,;:!?])/g,"$1").trim()}function ft(e){if(!e)return null;let t=[];for(let a of e.split(`
67
+ `)){let l=a.trim();if(!l){if(t.length)break;continue}t.push(l)}if(!t.length)return null;let n=Gn(t.join(" ").replace(/\s+/g," ")),s=null,o=/[.?!](?=\s|$)/g,i;for(;(i=o.exec(n))!==null&&(s=n.slice(0,i.index+1),!(s.length>=40)););if(s||(s=n),s.length>je){let a=s.lastIndexOf(" ",je-1);s=s.slice(0,a>60?a:je-1).trimEnd()+"\u2026"}return s}var Kn=8;function it(){let e=$e();if(!e||e.length===0)return[];let t=e.slice(-Kn).reverse(),n=["","---","","## Recently shipped",""];for(let o of t)n.push(`- ${o.date} \u2014 ${o.title}${o.commit?` \`${o.commit}\``:""}`);let s=e.length-t.length;return n.push("",s>0?`_${e.length} phases shipped in total; ${s} earlier one${s===1?"":"s"} not listed._`:`_${e.length} phase${e.length===1?"":"s"} shipped in total._`),n}function at(e){let t=e.doneCount,n=e.total;if(!n)return"_No phases yet._";let s=Math.round(t/n*10);return`**${t} of ${n} done** \`${"\u2588".repeat(s)}${"\u2591".repeat(10-s)}\``}function qn(e,t){let n=[En(t.generated),"<!--"," Generated by cairn \u2014 do NOT edit by hand."," The plan lives in .cairn/roadmap.md; status and cursor come from cairn_roadmap."," This file is regenerated whenever the roadmap changes; manual edits are overwritten."," Per-phase detail and links are in the roadmap-json block at the end of this file."];if(t.migratedFrom&&(n.push(" The ROADMAP.md that was here before cairn adopted this file is kept at:"),n.push(` migrated-from=${t.migratedFrom}`)),n.push("-->","","# Roadmap",""),e==null)return n.push("_No active plan right now._"),n.push(...it()),n;let{cursor:s,decisionNames:o,visibleRoots:i,children:a,unfinishedBlockers:l}=e;n.push("Legend: `[x]` done \xB7 `[~]` in progress \xB7 `[ ]` open \xB7 `[!]` blocked.");let u=(r,c)=>{let p=" ".repeat(c),m=r.phase_number!=null?`Phase ${r.phase_number}: ${r.text}`:r.text,d=Wn[r.status]||r.status,h=l(r.id);h.length&&(d+=` \xB7 waiting on ${h.join(", ")}`),s===r.id&&(d+=" \xB7 \u2190 current"),n.push(`${p}- [${Hn(r.status)}] **${m}** \u2014 ${d}`);let f=ft(r.body);f&&n.push(`${p} ${f}`);for(let g of a(r.id))u(g,c+1)};for(let r of o){let c=i.get(r);if(c){n.push(""),r===P?n.push("## Active plan","",at(c)):n.push(`## ${r}`,"",at(c)),n.push("");for(let p of c.visible)u(p,0)}}return n.push(...it()),n}function mt(e,t,n,s){let o=n.get(`phase-${e.id}`),i=ft(e.body),a=In(e.body),l=t.blockerIds(e.id),u={id:`phase-${e.id}`};if(e.phase_number!=null&&(u.number=e.phase_number),u.title=e.text,u.status=Un.has(e.status)?e.status:"open",i&&(u.goal=i),l.length&&(u.blocked_by=l),!s){let r=[],c=p=>{for(let m of t.children(p))r.push(mt(m,t,n,!0)),c(m.id)};c(e.id),r.length&&(u.slices=r)}return e.body&&e.body.trim()&&(u.detail=e.body.trim()),a.length&&(u.links=a),{...u,...lt(o,Mn)}}function Vn(e,t){let n=ut(t.previous?.phases),s=[];for(let a of e?.decisionNames??[]){let l=e.visibleRoots.get(a);if(l)for(let u of l.visible)s.push(mt(u,e,n,!1))}let o={format:X,provider:le,generated:t.generated,title:`${T.basename(z())} roadmap`,cursor:e?.cursor!=null?`phase-${e.cursor}`:null};t.migratedFrom&&(o.migrated_from=t.migratedFrom,t.migrated&&(o.migrated=t.migrated)),o.phases=s;let i={...o,...lt(t.previous,Nn)};return["","<details>","<summary>Machine-readable plan (roadmap/1)</summary>","","```roadmap-json",JSON.stringify(i,null,2),"```","","</details>"]}function Pe(e,t={}){let n={generated:t.generated||dt(),migratedFrom:t.migratedFrom||null,migrated:t.migrated||null,previous:t.previous&&typeof t.previous=="object"?t.previous:null},s=zn(e);return s==null&&!t.allowEmpty?null:[...qn(s,n),...Vn(s,n)].join(`
68
68
  `)+`
69
- `}function qn(e){let t=T.join(e,"ROADMAP.bak.md");if(!U.existsSync(t))return t;for(let n=2;n<=1e3;n++){let s=T.join(e,`ROADMAP.bak.${n}.md`);if(!U.existsSync(s))return s}return null}function Vn(e,t){let n=T.join(T.dirname(e),`.${T.basename(e)}.${process.pid}.tmp`);try{U.writeFileSync(n,t,"utf8"),U.renameSync(n,e)}catch(s){try{U.unlinkSync(n)}catch{}throw s}}function V(e,t,n={}){return{written:!1,reason:e,warning:`[cairn] ROADMAP.md was not updated \u2014 ${e}`,path:t,...n}}function dt(e){let t=ct(),n=null;if(U.existsSync(t))try{n=U.readFileSync(t,"utf8")}catch(f){return V(`the existing file could not be read (${f.message})`,t)}let s=On(n),o=n!=null&&(s?.provider===ce||n.startsWith(kn));if(n!=null&&s&&!o)return{written:!1,reason:`ROADMAP.md is generated by another provider (${s.provider}) \u2014 refusing to overwrite`,conflict_provider:s.provider,warning:`[cairn] ROADMAP.md is published by "${s.provider}", so cairn left it untouched and the file may not reflect cairn's plan. Two roadmap providers are claiming the same file \u2014 decide which one owns it.`,path:t};let i=o?rt(n):null,a=o?Mn(n):null,l=o&&typeof i?.migrated=="string"?i.migrated:null,u=typeof i?.generated=="string"?i.generated:null,r=!1,c=je(e,{generated:u||"1970-01-01T00:00:00Z",migratedFrom:a,migrated:l,previous:i});if(c==null){if(!o)return{written:!1,reason:"empty roadmap \u2014 no file written",path:t};r=!0,c=je(e,{generated:u||"1970-01-01T00:00:00Z",migratedFrom:a,migrated:l,previous:i,allowEmpty:!0})}if(o&&n===c)return{written:!1,unchanged:!0,path:t,bytes:Buffer.byteLength(c)};let p=lt(),m=null,d=null;if(n!=null&&!o){let f=qn(T.dirname(t));if(f==null)return V("no free ROADMAP.bak name was available to preserve the existing file",t);try{U.renameSync(t,f)}catch(g){return V(`the existing file could not be preserved (${g.message})`,t)}m=T.basename(f),d=f,a=m,l=p}c=je(e,{generated:p,migratedFrom:a,migrated:l,previous:i,allowEmpty:r});try{Vn(t,c)}catch(f){if(d)try{return U.renameSync(d,t),V(`write failed (${f.message}); the original ROADMAP.md was restored`,t)}catch{return V(`write failed (${f.message}); the original ROADMAP.md is at ${m} and must be restored by hand`,t,{backed_up_to:m})}return V(`write failed (${f.message})`,t)}let h={written:!0,path:t,bytes:Buffer.byteLength(c),format:X,generated:p};return m&&(h.backed_up_to=m,h.warning=`[cairn] The existing ROADMAP.md was not cairn-generated. It has been preserved as ${m}, and ROADMAP.md is now generated from cairn's roadmap.`),h}function Xn(e){try{return dt(e)}catch(t){process.stderr.write(`[cairn] ROADMAP.md publish skipped: ${t.message}
70
- `);let n=null;try{n=ct()}catch{}return V(`publish failed (${t.message})`,n)}}function le(e){let t=Xn(e);return t&&t.warning?t.warning:null}var Zn=new Set(["open","in_progress"]);function Qn(e,t){e.prepare("INSERT INTO main.roadmap_cursor (singleton, phase_id) VALUES (1, ?) ON CONFLICT(singleton) DO UPDATE SET phase_id = excluded.phase_id").run(t)}function es(e,t){let n=e.prepare("SELECT parent_phase_id, status FROM main.roadmap_phases WHERE id = ?"),s=e.prepare("UPDATE main.roadmap_phases SET status = 'in_progress' WHERE id = ? AND status = 'open'"),o=n.get(t);for(;o&&o.parent_phase_id!=null;)s.run(o.parent_phase_id),o=n.get(o.parent_phase_id)}function ft(e,t){Qn(e,t),e.prepare("UPDATE main.roadmap_phases SET status = 'in_progress' WHERE id = ? AND status = 'open'").run(t),es(e,t)}function ts(e){let t=new Map,n;try{n=e.prepare("SELECT blocker_id, blocked_id FROM main.roadmap_phase_deps").all()}catch{return t}for(let s of n)t.has(s.blocked_id)||t.set(s.blocked_id,new Set),t.get(s.blocked_id).add(s.blocker_id);return t}function ns(e,t,n){let s=t.get(e);if(!s||s.size===0)return[];let o=[];for(let i of s)n.get(i)!=="done"&&o.push(i);return o}function mt(e,t){let n=e.prepare("SELECT id, decision_name, phase_number, parent_phase_id, status, position, text FROM main.roadmap_phases ORDER BY decision_name, phase_number, position, id").all();if(n.length===0)return{id:null,reason:"No phases \u2014 add a ## Phases section to .cairn/roadmap.md."};let s=new Map(n.map(p=>[p.id,p.status])),o=ts(e),i=new Map;for(let p of n){let m=p.parent_phase_id??`root:${p.decision_name}`;i.has(m)||i.set(m,[]),i.get(m).push(p)}for(let p of i.values())p.sort((m,d)=>m.phase_number!=null&&d.phase_number!=null?m.phase_number-d.phase_number:m.position-d.position||m.id-d.id);let a=[],l=new Set;for(let p of n){if(p.parent_phase_id!=null||l.has(p.decision_name))continue;l.add(p.decision_name);let m=[],d=i.get(`root:${p.decision_name}`)||[];for(let h=d.length-1;h>=0;h--)m.push(d[h]);for(;m.length;){let h=m.pop();a.push(h);let f=i.get(h.id)||[];for(let g=f.length-1;g>=0;g--)m.push(f[g])}}let u=p=>Zn.has(p.status),r=[],c=0;if(t!=null){let p=a.findIndex(m=>m.id===t);c=p===-1?0:p+1}for(let p=c;p<a.length;p++){let m=a[p];if(!u(m))continue;let d=ns(m.id,o,s);if(d.length>0){r.push({id:m.id,blockers:d});continue}let h=t!=null?n.find(_=>_.id===t):null,f=h&&h.decision_name!==m.decision_name,g;return t==null?g=`First actionable phase: ${m.decision_name??"(sub-task)"} / Phase ${m.phase_number??"\xB7"}.`:f?g=`Previous decision "${h.decision_name}" is complete \u2014 advanced to "${m.decision_name}".`:g=`Next open phase in "${m.decision_name??"(sub-task)"}" after id ${t}.`,r.length>0&&(g+=` Skipped ${r.length} dep-blocked phase(s): ${r.map(_=>`id ${_.id} (blocked by ${_.blockers.join(",")})`).join("; ")}.`),{id:m.id,reason:g,skipped_blocked:r}}return r.length>0?{id:null,reason:`All remaining open phases are blocked by unfinished dependencies: ${r.map(p=>`id ${p.id} (blocked by ${p.blockers.join(",")})`).join("; ")}.`,skipped_blocked:r}:{id:null,reason:"No actionable phases remaining."}}var os=new Set(["java","typescript","javascript","vue","python","sql"]);function Pe(e,t){let n={hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:e};t&&(n.additionalContext=t),process.stdout.write(JSON.stringify({decision:"block",reason:e,hookSpecificOutput:n})),process.exit(0)}var _t={name:"cairn",contract:1,title:"cairn",slots:["roadmap"],provides:{roadmap:{path:"ROADMAP.md",format:X}},spec:"Project memory and roadmap. Publishes the plan as ROADMAP.md in the roadmap/1 slot format."};function rs(){let e=S.join(Z.homedir(),".claude.json");if(!y.existsSync(e))return!1;let t;try{t=JSON.parse(y.readFileSync(e,"utf8"))}catch{return!1}let n=t?.mcpServers?.cairn;if(!n||typeof n!="object")return!1;let s=n.terminalConnect,o=s?.provides?.roadmap?.format;if(s&&o===X)return!1;n.terminalConnect=_t;try{return y.writeFileSync(e,JSON.stringify(t,null,2),"utf8"),!0}catch{return!1}}function St(e){let t=S.join(Z.homedir(),".claude","projects",fe(e));return y.existsSync(t)?S.join(t,"memory"):null}function xt(e){try{return y.readdirSync(e).filter(t=>t.endsWith(".md"))}catch{return[]}}function is(e){let t=St(e);if(!t||!y.existsSync(t))return 0;let n=S.join(e,".cairn","memory");y.mkdirSync(n,{recursive:!0});let s=0;for(let o of xt(t)){let i=S.join(t,o),a;try{a=y.statSync(i)}catch{continue}if(!a.isFile())continue;let l=S.join(n,o),u=!y.existsSync(l);if(!u)try{u=a.mtimeMs>y.statSync(l).mtimeMs}catch{u=!0}if(u){y.copyFileSync(i,l);try{y.utimesSync(l,a.atime,a.mtime)}catch{}s++}}return s}function as(e){let t=St(e);if(!t||(y.mkdirSync(t,{recursive:!0}),xt(t).some(o=>o!=="MEMORY.md")))return 0;let n=S.join(e,".cairn","memory");if(!y.existsSync(n))return 0;let s=0;for(let o of y.readdirSync(n)){let i=S.join(n,o),a;try{a=y.statSync(i)}catch{continue}if(a.isFile()){y.copyFileSync(i,S.join(t,o));try{y.utimesSync(S.join(t,o),a.atime,a.mtime)}catch{}s++}}return s}function ht(e,t){let n=new Date(t.checkpoint_at).toLocaleString(),i=[` message: ${/^Auto-checkpoint\b/.test(t.message||"")&&t.last_manual_message||t.message}`];i.push(` would_lose: ${_e(re(e,t))}`),i.push(` checkpoint: ${n}`),t.active_files?.length>0&&i.push(` active_files: ${t.active_files.join(", ")}`),t.notes?.length>0&&i.push(` notes: ${t.notes.join(" | ")}`);let a=r=>{try{return ss(r,{cwd:e,encoding:"utf8",timeout:5e3,stdio:["ignore","pipe","ignore"]}).trim()}catch{return null}};try{let r=new Date(t.checkpoint_at).toISOString(),c=a(`git log --oneline --after="${r}"`);c!==null&&i.push(` commits_since: ${c?c.split(`
69
+ `}function Xn(e){let t=T.join(e,"ROADMAP.bak.md");if(!U.existsSync(t))return t;for(let n=2;n<=1e3;n++){let s=T.join(e,`ROADMAP.bak.${n}.md`);if(!U.existsSync(s))return s}return null}function Zn(e,t){let n=T.join(T.dirname(e),`.${T.basename(e)}.${process.pid}.tmp`);try{U.writeFileSync(n,t,"utf8"),U.renameSync(n,e)}catch(s){try{U.unlinkSync(n)}catch{}throw s}}function V(e,t,n={}){return{written:!1,reason:e,warning:`[cairn] ROADMAP.md was not updated \u2014 ${e}`,path:t,...n}}function ht(e){let t=pt(),n=null;if(U.existsSync(t))try{n=U.readFileSync(t,"utf8")}catch(f){return V(`the existing file could not be read (${f.message})`,t)}let s=Pn(n),o=n!=null&&(s?.provider===le||n.startsWith($n));if(n!=null&&s&&!o)return{written:!1,reason:`ROADMAP.md is generated by another provider (${s.provider}) \u2014 refusing to overwrite`,conflict_provider:s.provider,warning:`[cairn] ROADMAP.md is published by "${s.provider}", so cairn left it untouched and the file may not reflect cairn's plan. Two roadmap providers are claiming the same file \u2014 decide which one owns it.`,path:t};let i=o?ct(n):null,a=o?An(n):null,l=o&&typeof i?.migrated=="string"?i.migrated:null,u=typeof i?.generated=="string"?i.generated:null,r=!1,c=Pe(e,{generated:u||"1970-01-01T00:00:00Z",migratedFrom:a,migrated:l,previous:i});if(c==null){if(!o)return{written:!1,reason:"empty roadmap \u2014 no file written",path:t};r=!0,c=Pe(e,{generated:u||"1970-01-01T00:00:00Z",migratedFrom:a,migrated:l,previous:i,allowEmpty:!0})}if(o&&n===c)return{written:!1,unchanged:!0,path:t,bytes:Buffer.byteLength(c)};let p=dt(),m=null,d=null;if(n!=null&&!o){let f=Xn(T.dirname(t));if(f==null)return V("no free ROADMAP.bak name was available to preserve the existing file",t);try{U.renameSync(t,f)}catch(g){return V(`the existing file could not be preserved (${g.message})`,t)}m=T.basename(f),d=f,a=m,l=p}c=Pe(e,{generated:p,migratedFrom:a,migrated:l,previous:i,allowEmpty:r});try{Zn(t,c)}catch(f){if(d)try{return U.renameSync(d,t),V(`write failed (${f.message}); the original ROADMAP.md was restored`,t)}catch{return V(`write failed (${f.message}); the original ROADMAP.md is at ${m} and must be restored by hand`,t,{backed_up_to:m})}return V(`write failed (${f.message})`,t)}let h={written:!0,path:t,bytes:Buffer.byteLength(c),format:X,generated:p};return m&&(h.backed_up_to=m,h.warning=`[cairn] The existing ROADMAP.md was not cairn-generated. It has been preserved as ${m}, and ROADMAP.md is now generated from cairn's roadmap.`),h}function Qn(e){try{return ht(e)}catch(t){process.stderr.write(`[cairn] ROADMAP.md publish skipped: ${t.message}
70
+ `);let n=null;try{n=pt()}catch{}return V(`publish failed (${t.message})`,n)}}function ue(e){let t=Qn(e);return t&&t.warning?t.warning:null}var es=new Set(["open","in_progress"]);function ts(e,t){e.prepare("INSERT INTO main.roadmap_cursor (singleton, phase_id) VALUES (1, ?) ON CONFLICT(singleton) DO UPDATE SET phase_id = excluded.phase_id").run(t)}function ns(e,t){let n=e.prepare("SELECT parent_phase_id, status FROM main.roadmap_phases WHERE id = ?"),s=e.prepare("UPDATE main.roadmap_phases SET status = 'in_progress' WHERE id = ? AND status = 'open'"),o=n.get(t);for(;o&&o.parent_phase_id!=null;)s.run(o.parent_phase_id),o=n.get(o.parent_phase_id)}function yt(e,t){ts(e,t),e.prepare("UPDATE main.roadmap_phases SET status = 'in_progress' WHERE id = ? AND status = 'open'").run(t),ns(e,t)}function ss(e){let t=new Map,n;try{n=e.prepare("SELECT blocker_id, blocked_id FROM main.roadmap_phase_deps").all()}catch{return t}for(let s of n)t.has(s.blocked_id)||t.set(s.blocked_id,new Set),t.get(s.blocked_id).add(s.blocker_id);return t}function os(e,t,n){let s=t.get(e);if(!s||s.size===0)return[];let o=[];for(let i of s)n.get(i)!=="done"&&o.push(i);return o}function gt(e,t){let n=e.prepare("SELECT id, decision_name, phase_number, parent_phase_id, status, position, text FROM main.roadmap_phases ORDER BY decision_name, phase_number, position, id").all();if(n.length===0)return{id:null,reason:"No phases \u2014 add a ## Phases section to .cairn/roadmap.md."};let s=new Map(n.map(p=>[p.id,p.status])),o=ss(e),i=new Map;for(let p of n){let m=p.parent_phase_id??`root:${p.decision_name}`;i.has(m)||i.set(m,[]),i.get(m).push(p)}for(let p of i.values())p.sort((m,d)=>m.phase_number!=null&&d.phase_number!=null?m.phase_number-d.phase_number:m.position-d.position||m.id-d.id);let a=[],l=new Set;for(let p of n){if(p.parent_phase_id!=null||l.has(p.decision_name))continue;l.add(p.decision_name);let m=[],d=i.get(`root:${p.decision_name}`)||[];for(let h=d.length-1;h>=0;h--)m.push(d[h]);for(;m.length;){let h=m.pop();a.push(h);let f=i.get(h.id)||[];for(let g=f.length-1;g>=0;g--)m.push(f[g])}}let u=p=>es.has(p.status),r=[],c=0;if(t!=null){let p=a.findIndex(m=>m.id===t);c=p===-1?0:p+1}for(let p=c;p<a.length;p++){let m=a[p];if(!u(m))continue;let d=os(m.id,o,s);if(d.length>0){r.push({id:m.id,blockers:d});continue}let h=t!=null?n.find(_=>_.id===t):null,f=h&&h.decision_name!==m.decision_name,g;return t==null?g=`First actionable phase: ${m.decision_name??"(sub-task)"} / Phase ${m.phase_number??"\xB7"}.`:f?g=`Previous decision "${h.decision_name}" is complete \u2014 advanced to "${m.decision_name}".`:g=`Next open phase in "${m.decision_name??"(sub-task)"}" after id ${t}.`,r.length>0&&(g+=` Skipped ${r.length} dep-blocked phase(s): ${r.map(_=>`id ${_.id} (blocked by ${_.blockers.join(",")})`).join("; ")}.`),{id:m.id,reason:g,skipped_blocked:r}}return r.length>0?{id:null,reason:`All remaining open phases are blocked by unfinished dependencies: ${r.map(p=>`id ${p.id} (blocked by ${p.blockers.join(",")})`).join("; ")}.`,skipped_blocked:r}:{id:null,reason:"No actionable phases remaining."}}var is=new Set(["java","typescript","javascript","vue","python","sql"]);function Te(e,t){let n={hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:e};t&&(n.additionalContext=t),process.stdout.write(JSON.stringify({decision:"block",reason:e,hookSpecificOutput:n})),process.exit(0)}var wt={name:"cairn",contract:1,title:"cairn",slots:["roadmap"],provides:{roadmap:{path:"ROADMAP.md",format:X}},spec:"Project memory and roadmap. Publishes the plan as ROADMAP.md in the roadmap/1 slot format."};function as(){let e=S.join(Z.homedir(),".claude.json");if(!y.existsSync(e))return!1;let t;try{t=JSON.parse(y.readFileSync(e,"utf8"))}catch{return!1}let n=t?.mcpServers?.cairn;if(!n||typeof n!="object")return!1;let s=n.terminalConnect,o=s?.provides?.roadmap?.format;if(s&&o===X)return!1;n.terminalConnect=wt;try{return y.writeFileSync(e,JSON.stringify(t,null,2),"utf8"),!0}catch{return!1}}function bt(e){let t=S.join(Z.homedir(),".claude","projects",me(e));return y.existsSync(t)?S.join(t,"memory"):null}function kt(e){try{return y.readdirSync(e).filter(t=>t.endsWith(".md"))}catch{return[]}}function cs(e){let t=bt(e);if(!t||!y.existsSync(t))return 0;let n=S.join(e,".cairn","memory");y.mkdirSync(n,{recursive:!0});let s=0;for(let o of kt(t)){let i=S.join(t,o),a;try{a=y.statSync(i)}catch{continue}if(!a.isFile())continue;let l=S.join(n,o),u=!y.existsSync(l);if(!u)try{u=a.mtimeMs>y.statSync(l).mtimeMs}catch{u=!0}if(u){y.copyFileSync(i,l);try{y.utimesSync(l,a.atime,a.mtime)}catch{}s++}}return s}function ls(e){let t=bt(e);if(!t||(y.mkdirSync(t,{recursive:!0}),kt(t).some(o=>o!=="MEMORY.md")))return 0;let n=S.join(e,".cairn","memory");if(!y.existsSync(n))return 0;let s=0;for(let o of y.readdirSync(n)){let i=S.join(n,o),a;try{a=y.statSync(i)}catch{continue}if(a.isFile()){y.copyFileSync(i,S.join(t,o));try{y.utimesSync(S.join(t,o),a.atime,a.mtime)}catch{}s++}}return s}function _t(e,t){let n=new Date(t.checkpoint_at).toLocaleString(),i=[` message: ${/^Auto-checkpoint\b/.test(t.message||"")&&t.last_manual_message||t.message}`];i.push(` would_lose: ${Se(ie(e,t))}`),i.push(` checkpoint: ${n}`),t.active_files?.length>0&&i.push(` active_files: ${t.active_files.join(", ")}`),t.notes?.length>0&&i.push(` notes: ${t.notes.join(" | ")}`);let a=r=>{try{return rs(r,{cwd:e,encoding:"utf8",timeout:5e3,stdio:["ignore","pipe","ignore"]}).trim()}catch{return null}};try{let r=new Date(t.checkpoint_at).toISOString(),c=a(`git log --oneline --after="${r}"`);c!==null&&i.push(` commits_since: ${c?c.split(`
71
71
  `).join("; "):"(none)"}`)}catch{}let l=a("git diff --name-only HEAD");l&&i.push(` uncommitted: ${l.split(`
72
72
  `).join(", ")}`);let u=a("git status --short");return u&&i.push(` git_status:
73
73
  ${u.split(`
74
74
  `).map(r=>" "+r).join(`
75
- `)}`),i.push(" \u2192 Call cairn_resume for full index status and open todos."),i}async function yt(e){let t={active:!1},n=S.join(e,"index.db");if(!y.existsSync(n))return t;let s;try{({DatabaseSync:s}=await import("node:sqlite"))}catch{return t}let o;try{o=new s(n,{readonly:!0})}catch{return t}let i=l=>l.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_|_$/g,"");function a(l,u){if(!y.existsSync(l))return null;let r=y.readFileSync(l,"utf8"),c=!1,p=!1,m=[];for(let d of r.split(/\r?\n/)){if(/^##\s+Phases\s*$/i.test(d)){c=!0;continue}if(c&&/^##\s+/.test(d)&&!/^###/.test(d))break;if(!c)continue;let h=/^###\s+Phase\s+(\d+)\s*:\s*(.+?)\s*$/.exec(d);if(h){if(p)break;parseInt(h[1],10)===u&&(m.push(`### Phase ${h[1]}: ${h[2]}`),p=!0);continue}p&&m.push(d)}return m.join(`
76
- `).trim()||null}try{let u=o.prepare("SELECT phase_id FROM roadmap_cursor WHERE singleton = 1").get()?.phase_id??null;if(u==null)return o.close(),t;let r=o.prepare("SELECT id, decision_name, phase_number, parent_phase_id, text, status, position FROM roadmap_phases ORDER BY decision_name, phase_number, position, id").all(),c={open:"[ ]",in_progress:"[~]",done:"[x]",blocked:"[!]",stale:"[s]"},p=new Map;for(let R of r){let E=R.parent_phase_id??"root";p.has(E)||p.set(E,[]),p.get(E).push(R)}for(let R of p.values())R.sort((E,$)=>E.decision_name&&$.decision_name&&E.decision_name!==$.decision_name?E.decision_name.localeCompare($.decision_name):E.phase_number!=null&&$.phase_number!=null?E.phase_number-$.phase_number:E.position-$.position||E.id-$.id);let m=p.get("root")||[],d=new Map;for(let R of m)R.decision_name!=null&&(d.has(R.decision_name)||d.set(R.decision_name,[]),d.get(R.decision_name).push(R));let h=[],f=(R,E)=>{for(let $ of p.get(R)||[]){if($.status==="stale")continue;let ue=u===$.id?" \u2192":"",pe=$.phase_number!=null?`Phase ${$.phase_number}: ${$.text}`:$.text;h.push(`${" ".repeat(E)}${c[$.status]||"[?]"}${ue} ${pe}`),f($.id,E+1)}};for(let[R,E]of d){let $=E.filter(j=>j.status!=="stale");if($.length===0)continue;let ue=$.every(j=>j.status==="done"),pe=$.some(j=>j.status==="in_progress"),wt=ue?"done":pe?"in_progress":"open",bt=$.filter(j=>j.status==="done").length,kt=R===P?"Roadmap":`Decision: ${R}`;h.push(`${c[wt]} ${kt} (${bt}/${$.length} done)`);for(let j of E){if(j.status==="stale"||R===P&&j.status==="done")continue;let Et=u===j.id?" \u2192":"";h.push(` ${c[j.status]||"[?]"}${Et} Phase ${j.phase_number}: ${j.text}`),f(j.id,2)}}let g=R=>r.find(E=>E.id===R),_=g(u),w=_?.decision_name??null,b=_?.phase_number??null,x=_;for(;x&&x.parent_phase_id!=null&&(x=g(x.parent_phase_id),!!x);)w=x.decision_name??w,b=x.phase_number??b;o.close();let k=_?_.phase_number!=null?`${_.decision_name} / Phase ${_.phase_number}: ${_.text}`:`${w} / Phase ${b} \u2192 ${_.text}`:"(unknown)",L=S.join(e,".last-roadmap-cursor"),Te=null;try{Te=parseInt(y.readFileSync(L,"utf8").trim(),10)}catch{}let Me=Te!==u;try{y.writeFileSync(L,String(u),"utf8")}catch{}let Ne=null;if(Me&&w!=null&&b!=null){let R=w===P?S.join(e,"roadmap.md"):S.join(e,"memory",`decision_${i(w)}.md`),E=a(R,b);E&&(Ne=E.split(`
77
- `))}return{active:!0,currentLabel:k,treeLines:h,advanced:Me,memoLines:Ne}}catch{try{o?.close()}catch{}return t}}process.on("uncaughtException",e=>{process.stderr.write(`cairn: ${e.message}
78
- `),process.exit(0)});var M=process.argv[2];if(M==="--version"||M==="-v"){let e=JSON.parse(y.readFileSync(new URL("../package.json",import.meta.url),"utf8"));console.log(e.version),process.exit(0)}else if(M==="minify"){let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{e+=t}),process.stdin.on("end",()=>{let t,n;try{n=JSON.parse(e)?.tool_input||{},t=n.file_path}catch{process.exit(0)}t||process.exit(0),(n.offset!=null||n.limit!=null)&&process.exit(0),t=S.resolve(t);let s=xe(t);s||process.exit(0);let o=S.join(s,"views"),i=S.relative(o,t);!i.startsWith("..")&&!S.isAbsolute(i)&&process.exit(0);let a=K(s),u=ie(s,a)>0,r=a[t];r?.state==="edit-ready"&&(u&&C(s,a),process.exit(0)),r?.state==="compressed"&&(r.state="edit-ready",C(s,a),process.exit(0));let c=S.extname(t).toLowerCase(),p=Ae[c];(!p||!os.has(p))&&(u&&C(s,a),process.exit(0));let m;try{m=y.readFileSync(t,"utf8")}catch{u&&C(s,a),process.exit(0)}let d=m.split(`
79
- `).length,h=p==="vue"?Fe(m):oe(m,p),f=h.split(`
80
- `).length,g=d>0?Math.round((1-f/d)*100):0,_=Be(s,t);y.mkdirSync(S.dirname(_),{recursive:!0}),y.writeFileSync(_,h,"utf8");let w=r?.readCount??0;a[t]={tempPath:_,state:"compressed",minifiedAt:y.statSync(t).mtimeMs,readCount:w+1},C(s,a);let b=f>Je,x=w>0;if(x||b){let k=Ye(h,p),L=x?`[cairn: ${d} \u2192 ${f} lines | seen \xD7${w+1} this session]`:`[cairn: ${d} \u2192 outline (${f} minified lines)]`;process.stderr.write(`${L}
75
+ `)}`),i.push(" \u2192 Call cairn_resume for full index status and open todos."),i}async function St(e){let t={active:!1},n=S.join(e,"index.db");if(!y.existsSync(n))return t;let s;try{({DatabaseSync:s}=await import("node:sqlite"))}catch{return t}let o;try{o=new s(n,{readonly:!0})}catch{return t}let i=l=>l.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_|_$/g,"");function a(l,u){if(!y.existsSync(l))return null;let r=y.readFileSync(l,"utf8"),c=!1,p=!1,m=[];for(let d of r.split(/\r?\n/)){if(/^##\s+Phases\s*$/i.test(d)){c=!0;continue}if(c&&/^##\s+/.test(d)&&!/^###/.test(d))break;if(!c)continue;let h=/^###\s+Phase\s+(\d+)\s*:\s*(.+?)\s*$/.exec(d);if(h){if(p)break;parseInt(h[1],10)===u&&(m.push(`### Phase ${h[1]}: ${h[2]}`),p=!0);continue}p&&m.push(d)}return m.join(`
76
+ `).trim()||null}try{let u=o.prepare("SELECT phase_id FROM roadmap_cursor WHERE singleton = 1").get()?.phase_id??null;if(u==null)return o.close(),t;let r=o.prepare("SELECT id, decision_name, phase_number, parent_phase_id, text, status, position FROM roadmap_phases ORDER BY decision_name, phase_number, position, id").all(),c={open:"[ ]",in_progress:"[~]",done:"[x]",blocked:"[!]",stale:"[s]"},p=new Map;for(let R of r){let E=R.parent_phase_id??"root";p.has(E)||p.set(E,[]),p.get(E).push(R)}for(let R of p.values())R.sort((E,$)=>E.decision_name&&$.decision_name&&E.decision_name!==$.decision_name?E.decision_name.localeCompare($.decision_name):E.phase_number!=null&&$.phase_number!=null?E.phase_number-$.phase_number:E.position-$.position||E.id-$.id);let m=p.get("root")||[],d=new Map;for(let R of m)R.decision_name!=null&&(d.has(R.decision_name)||d.set(R.decision_name,[]),d.get(R.decision_name).push(R));let h=[],f=(R,E)=>{for(let $ of p.get(R)||[]){if($.status==="stale")continue;let pe=u===$.id?" \u2192":"",de=$.phase_number!=null?`Phase ${$.phase_number}: ${$.text}`:$.text;h.push(`${" ".repeat(E)}${c[$.status]||"[?]"}${pe} ${de}`),f($.id,E+1)}};for(let[R,E]of d){let $=E.filter(j=>j.status!=="stale");if($.length===0)continue;let pe=$.every(j=>j.status==="done"),de=$.some(j=>j.status==="in_progress"),Et=pe?"done":de?"in_progress":"open",$t=$.filter(j=>j.status==="done").length,Rt=R===P?"Roadmap":`Decision: ${R}`;h.push(`${c[Et]} ${Rt} (${$t}/${$.length} done)`);for(let j of E){if(j.status==="stale"||R===P&&j.status==="done")continue;let vt=u===j.id?" \u2192":"";h.push(` ${c[j.status]||"[?]"}${vt} Phase ${j.phase_number}: ${j.text}`),f(j.id,2)}}let g=R=>r.find(E=>E.id===R),_=g(u),w=_?.decision_name??null,b=_?.phase_number??null,x=_;for(;x&&x.parent_phase_id!=null&&(x=g(x.parent_phase_id),!!x);)w=x.decision_name??w,b=x.phase_number??b;o.close();let k=_?_.phase_number!=null?`${_.decision_name} / Phase ${_.phase_number}: ${_.text}`:`${w} / Phase ${b} \u2192 ${_.text}`:"(unknown)",L=S.join(e,".last-roadmap-cursor"),Ne=null;try{Ne=parseInt(y.readFileSync(L,"utf8").trim(),10)}catch{}let Me=Ne!==u;try{y.writeFileSync(L,String(u),"utf8")}catch{}let Ae=null;if(Me&&w!=null&&b!=null){let R=w===P?S.join(e,"roadmap.md"):S.join(e,"memory",`decision_${i(w)}.md`),E=a(R,b);E&&(Ae=E.split(`
77
+ `))}return{active:!0,currentLabel:k,treeLines:h,advanced:Me,memoLines:Ae}}catch{try{o?.close()}catch{}return t}}process.on("uncaughtException",e=>{process.stderr.write(`cairn: ${e.message}
78
+ `),process.exit(0)});var N=process.argv[2];if(N==="--version"||N==="-v"){let e=JSON.parse(y.readFileSync(new URL("../package.json",import.meta.url),"utf8"));console.log(e.version),process.exit(0)}else if(N==="minify"){let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{e+=t}),process.stdin.on("end",()=>{let t,n;try{n=JSON.parse(e)?.tool_input||{},t=n.file_path}catch{process.exit(0)}t||process.exit(0),(n.offset!=null||n.limit!=null)&&process.exit(0),t=S.resolve(t);let s=we(t);s||process.exit(0);let o=S.join(s,"views"),i=S.relative(o,t);!i.startsWith("..")&&!S.isAbsolute(i)&&process.exit(0);let a=K(s),u=ae(s,a)>0,r=a[t];r?.state==="edit-ready"&&(u&&D(s,a),process.exit(0)),r?.state==="compressed"&&(r.state="edit-ready",D(s,a),process.exit(0));let c=S.extname(t).toLowerCase(),p=Fe[c];(!p||!is.has(p))&&(u&&D(s,a),process.exit(0));let m;try{m=y.readFileSync(t,"utf8")}catch{u&&D(s,a),process.exit(0)}let d=m.split(`
79
+ `).length,h=p==="vue"?Le(m):oe(m,p),f=h.split(`
80
+ `).length,g=d>0?Math.round((1-f/d)*100):0,_=Ge(s,t);y.mkdirSync(S.dirname(_),{recursive:!0}),y.writeFileSync(_,h,"utf8");let w=r?.readCount??0;a[t]={tempPath:_,state:"compressed",minifiedAt:y.statSync(t).mtimeMs,readCount:w+1},D(s,a);let b=f>ze,x=w>0;if(x||b){let k=Ke(h,p),L=x?`[cairn: ${d} \u2192 ${f} lines | seen \xD7${w+1} this session]`:`[cairn: ${d} \u2192 outline (${f} minified lines)]`;process.stderr.write(`${L}
81
81
  ${k}
82
82
  Full minified view: Read("${_}")
83
83
  `)}else process.stderr.write(`[cairn: ${d} \u2192 ${f} lines (-${g}%)]
84
84
  ${h}
85
85
  Cached view: Read("${_}")
86
- `);process.exit(2)})}else if(M==="edit-guard"){let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{e+=t}),process.stdin.on("end",()=>{let t="(unknown)",n="Edit";try{let p=JSON.parse(e);t=p?.tool_input?.file_path||"(unknown)",n=p?.tool_name||"Edit"}catch{}let s=S.resolve(t),o=xe(s);o||process.exit(0);let i=S.join(o,"views"),a=S.relative(i,s);if(!a.startsWith("..")&&!S.isAbsolute(a)){let p=K(o),m=Object.entries(p).find(([,h])=>h.tempPath===s),d=m?`Edit the source file instead: Edit("${m[0]}")`:"Edit the original source file, not the .cairn/views/ cache.";Pe(`[cairn] That path is a cached view, not the source \u2014 it is read-only and is overwritten on the next read. ${d}`)}let l=K(o),u=ie(o,l),r=l[s];r||(u>0&&C(o,l),process.exit(0)),n==="Write"&&(delete l[s],C(o,l),process.exit(0)),r.state==="edit-ready"&&(delete l[s],C(o,l),process.exit(0)),r.state="edit-ready",C(o,l);let c=null;try{let p=y.readFileSync(s,"utf8");p.split(`
87
- `).length<=300&&(c=p)}catch{}c!==null?Pe(`[cairn] Expected step, not a failure: this file was shown compressed, so the edit was built from partial text. The full source has been provided in context \u2014 re-apply the same edit, or Read("${t}") first if you do not see it.`,`[cairn] Full source of ${t} (the compressed view you saw is not editable text \u2014 use the exact content below, then re-apply your Edit; the guard is now cleared):
86
+ `);process.exit(2)})}else if(N==="edit-guard"){let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{e+=t}),process.stdin.on("end",()=>{let t="(unknown)",n="Edit";try{let p=JSON.parse(e);t=p?.tool_input?.file_path||"(unknown)",n=p?.tool_name||"Edit"}catch{}let s=S.resolve(t),o=we(s);o||process.exit(0);let i=S.join(o,"views"),a=S.relative(i,s);if(!a.startsWith("..")&&!S.isAbsolute(a)){let p=K(o),m=Object.entries(p).find(([,h])=>h.tempPath===s),d=m?`Edit the source file instead: Edit("${m[0]}")`:"Edit the original source file, not the .cairn/views/ cache.";Te(`[cairn] That path is a cached view, not the source \u2014 it is read-only and is overwritten on the next read. ${d}`)}let l=K(o),u=ae(o,l),r=l[s];r||(u>0&&D(o,l),process.exit(0)),n==="Write"&&(delete l[s],D(o,l),process.exit(0)),r.state==="edit-ready"&&(delete l[s],D(o,l),process.exit(0)),r.state="edit-ready",D(o,l);let c=null;try{let p=y.readFileSync(s,"utf8");p.split(`
87
+ `).length<=300&&(c=p)}catch{}c!==null?Te(`[cairn] Expected step, not a failure: this file was shown compressed, so the edit was built from partial text. The full source has been provided in context \u2014 re-apply the same edit, or Read("${t}") first if you do not see it.`,`[cairn] Full source of ${t} (the compressed view you saw is not editable text \u2014 use the exact content below, then re-apply your Edit; the guard is now cleared):
88
88
 
89
- ${c}`):Pe(`[cairn] Expected step, not a failure: this file was shown compressed. Read("${t}") to load the real source (it comes through in full now), then re-apply the same edit.`)})}else if(M==="roadmap-capture"){let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{e+=t}),process.stdin.on("end",async()=>{let t;try{t=JSON.parse(e)}catch{process.exit(0)}let n=t?.tool_name||"",s=t?.tool_input||{},o=process.cwd(),i=S.join(o,".cairn");y.existsSync(i)||process.exit(0);let a=S.join(i,"index.db");y.existsSync(a)||process.exit(0);let l;try{({DatabaseSync:l}=await import("node:sqlite"))}catch{process.exit(0)}let u=/exit.?plan.?mode/i.test(n),r=/todo.?write/i.test(n);!u&&!r&&process.exit(0);let c=null,p;try{if(u){let d=s.plan;(typeof d!="string"||!d.trim())&&process.exit(0);let h=et(d);if(p=new l(a),te(p),(p.prepare("SELECT phase_id FROM main.roadmap_cursor WHERE singleton = 1").get()?.phase_id??null)==null){let g=mt(p,null);g.id!=null&&ft(p,g.id)}h.added.length>0&&(c=`[cairn] roadmap: captured ${h.added.length} phase(s) from plan${h.skipped.length?` (${h.skipped.length} already present)`:""}.`)}else if(r){p=new l(a);let d=tt(p,s.todos);d.seeded?c=`[cairn] roadmap: seeded ${d.seeded} phase(s) from todos.`:d.inserted>0&&(c=`[cairn] roadmap: linked ${d.inserted} new sub-task(s) to the current phase.`)}}catch(d){process.stderr.write(`[cairn] roadmap-capture skipped: ${d.message}
90
- `);try{p?.close()}catch{}process.exit(0)}let m=p?le(p):null;try{p?.close()}catch{}m&&(c=c?`${c} ${m}`:m),c&&process.stdout.write(JSON.stringify({systemMessage:c})),process.exit(0)})}else if(M==="validate-map"){let e=S.join(process.cwd(),".cairn");y.existsSync(e)||process.exit(0);let t=K(e),n=ie(e,t);for(let s of Object.values(t))s.readCount=0;C(e,t),n>0&&process.stdout.write(`[cairn] Cleaned minify map: ${n} stale entr${n===1?"y":"ies"} removed
91
- `),process.exit(0)}else if(M==="checkpoint"&&process.argv[3]==="--auto"){let e=`Auto-checkpoint at ${new Date().toISOString()}`,t=[],n=[];try{let s=S.join(process.cwd(),".cairn"),o=S.join(s,"session.json");y.existsSync(o)&&(t=JSON.parse(y.readFileSync(o,"utf8")).notes||[]);let i=K(s);n=Object.entries(i).filter(([,a])=>(a.readCount??0)>0).map(([a])=>a)}catch{}Se(null,{message:e,active_files:n,notes:t,auto:!0}),process.exit(0)}else if(M==="session-tick"){let e=process.cwd(),t=S.join(e,".cairn");y.existsSync(t)||process.exit(0);let n={};if(!process.stdin.isTTY)try{n=JSON.parse(y.readFileSync(0,"utf8"))||{}}catch{}let s=[];try{let r=`Auto-checkpoint at ${new Date().toISOString()}`,c=[],p=S.join(t,"session.json");if(y.existsSync(p))try{c=JSON.parse(y.readFileSync(p,"utf8")).notes||[]}catch{}let m=K(t),d=Object.entries(m).filter(([,h])=>(h.readCount??0)>0).map(([h])=>h);Se(null,{message:r,active_files:d,notes:c,auto:!0}),s.push("\u2713 checkpoint saved")}catch{}try{let r=is(e);r>0&&s.push(`memory backed up (${r})`)}catch{}let o=[],i=null;try{let r=S.join(t,"index.db");if(y.existsSync(r)){let{DatabaseSync:c}=await import("node:sqlite"),p=new c(r);try{te(p),i=le(p);try{o=nt(p,e).candidates}catch{}}finally{p.close()}}}catch{}let a="";try{let r=await yt(t);if(r.active){s.push(`roadmap ${r.advanced?"updated":"on track"}: ${r.currentLabel}`);let c=["[cairn] Production roadmap (\u2192 = cursor / current phase):",...r.treeLines.map(p=>" "+p)];r.memoLines&&c.push("","\u2500\u2500 Current phase detail (from roadmap.md) \u2500\u2500",...r.memoLines,"\u2500\u2500 (re-read after each phase to confirm direction) \u2500\u2500"),c.push("","Keep this current via cairn_roadmap on MATERIAL change (phase done / blocked / new) \u2014 not every turn."),a=c.join(`
89
+ ${c}`):Te(`[cairn] Expected step, not a failure: this file was shown compressed. Read("${t}") to load the real source (it comes through in full now), then re-apply the same edit.`)})}else if(N==="roadmap-capture"){let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>{e+=t}),process.stdin.on("end",async()=>{let t;try{t=JSON.parse(e)}catch{process.exit(0)}let n=t?.tool_name||"",s=t?.tool_input||{},o=process.cwd(),i=S.join(o,".cairn");y.existsSync(i)||process.exit(0);let a=S.join(i,"index.db");y.existsSync(a)||process.exit(0);let l;try{({DatabaseSync:l}=await import("node:sqlite"))}catch{process.exit(0)}let u=/exit.?plan.?mode/i.test(n),r=/todo.?write/i.test(n);!u&&!r&&process.exit(0);let c=null,p;try{if(u){let d=s.plan;(typeof d!="string"||!d.trim())&&process.exit(0);let h=st(d);if(p=new l(a),te(p),(p.prepare("SELECT phase_id FROM main.roadmap_cursor WHERE singleton = 1").get()?.phase_id??null)==null){let g=gt(p,null);g.id!=null&&yt(p,g.id)}h.added.length>0&&(c=`[cairn] roadmap: captured ${h.added.length} phase(s) from plan${h.skipped.length?` (${h.skipped.length} already present)`:""}.`)}else if(r){p=new l(a);let d=ot(p,s.todos);d.seeded?c=`[cairn] roadmap: seeded ${d.seeded} phase(s) from todos.`:d.inserted>0&&(c=`[cairn] roadmap: linked ${d.inserted} new sub-task(s) to the current phase.`)}}catch(d){process.stderr.write(`[cairn] roadmap-capture skipped: ${d.message}
90
+ `);try{p?.close()}catch{}process.exit(0)}let m=p?ue(p):null;try{p?.close()}catch{}m&&(c=c?`${c} ${m}`:m),c&&process.stdout.write(JSON.stringify({systemMessage:c})),process.exit(0)})}else if(N==="validate-map"){let e=S.join(process.cwd(),".cairn");y.existsSync(e)||process.exit(0);let t=K(e),n=ae(e,t);for(let s of Object.values(t))s.readCount=0;D(e,t),n>0&&process.stdout.write(`[cairn] Cleaned minify map: ${n} stale entr${n===1?"y":"ies"} removed
91
+ `),process.exit(0)}else if(N==="checkpoint"&&process.argv[3]==="--auto"){let e=`Auto-checkpoint at ${new Date().toISOString()}`,t=[],n=[];try{let s=S.join(process.cwd(),".cairn"),o=S.join(s,"session.json");y.existsSync(o)&&(t=JSON.parse(y.readFileSync(o,"utf8")).notes||[]);let i=K(s);n=Object.entries(i).filter(([,a])=>(a.readCount??0)>0).map(([a])=>a)}catch{}xe(null,{message:e,active_files:n,notes:t,auto:!0}),process.exit(0)}else if(N==="session-tick"){let e=process.cwd(),t=S.join(e,".cairn");y.existsSync(t)||process.exit(0);let n={};if(!process.stdin.isTTY)try{n=JSON.parse(y.readFileSync(0,"utf8"))||{}}catch{}let s=[];try{let r=`Auto-checkpoint at ${new Date().toISOString()}`,c=[],p=S.join(t,"session.json");if(y.existsSync(p))try{c=JSON.parse(y.readFileSync(p,"utf8")).notes||[]}catch{}let m=K(t),d=Object.entries(m).filter(([,h])=>(h.readCount??0)>0).map(([h])=>h);xe(null,{message:r,active_files:d,notes:c,auto:!0}),s.push("\u2713 checkpoint saved")}catch{}try{let r=cs(e);r>0&&s.push(`memory backed up (${r})`)}catch{}let o=[],i=null;try{let r=S.join(t,"index.db");if(y.existsSync(r)){let{DatabaseSync:c}=await import("node:sqlite"),p=new c(r);try{te(p),i=ue(p);try{o=rt(p,e).candidates}catch{}}finally{p.close()}}}catch{}let a="";try{let r=await St(t);if(r.active){s.push(`roadmap ${r.advanced?"updated":"on track"}: ${r.currentLabel}`);let c=["[cairn] Production roadmap (\u2192 = cursor / current phase):",...r.treeLines.map(p=>" "+p)];r.memoLines&&c.push("","\u2500\u2500 Current phase detail (from roadmap.md) \u2500\u2500",...r.memoLines,"\u2500\u2500 (re-read after each phase to confirm direction) \u2500\u2500"),c.push("","Keep this current via cairn_roadmap on MATERIAL change (phase done / blocked / new) \u2014 not every turn."),a=c.join(`
92
92
  `)}}catch{}if(i&&(s.push("ROADMAP.md not updated \u2014 see warning"),a=a?`${a}
93
93
 
94
94
  ${i}`:i),o.length>0){s.push(`${o.length} phase(s) may be done \u2014 confirm with user`);let r=["","[cairn] Possible COMPLETED phase(s) detected from recent commits \u2014","CONFIRM WITH THE USER before marking done; do NOT mark done unilaterally:"];for(let c of o)r.push(` \u2022 Phase ${c.phase_number}: ${c.text} (commit ${c.commit}, ${c.confidence}) \u2192 if confirmed: cairn_roadmap set_status ${c.phase_id} done`);a=a?a+`
95
95
  `+r.join(`
96
96
  `):r.join(`
97
- `).trimStart()}let l=null;try{let r=JSON.parse(y.readFileSync(S.join(t,"session.json"),"utf8")),c=We({cwd:e,session:r,transcriptPath:n.transcript_path});if(c&&Ue(t,c.signature)){l=c;let p=`
97
+ `).trimStart()}let l=null;try{let r=JSON.parse(y.readFileSync(S.join(t,"session.json"),"utf8")),c=Je({cwd:e,session:r,transcriptPath:n.transcript_path});if(c&&Be(t,c.signature)){l=c;let p=`
98
98
  ${c.line}
99
99
  ${c.guidance}`;a=a?a+`
100
100
  `+p:p.trimStart()}}catch{}s.length===0&&!l&&process.exit(0);let u={systemMessage:s.length>0?`[cairn] ${s.join(" \xB7 ")}${l?`
101
- ${l.line}`:""}`:l.line};a&&(u.additionalContext=a),process.stdout.write(JSON.stringify(u)),process.exit(0)}else if(M==="repair-memory"){let e=process.argv.includes("--dry-run"),t=process.cwd();y.existsSync(S.join(t,".cairn"))||(console.error("[cairn] Not a cairn project (no .cairn/ here). Run this from the project root."),process.exit(1)),Ce(t);let n=Ve(he(),{dryRun:e});if(n.reason&&(console.error(`[cairn] ${n.reason}: ${n.dir}`),process.exit(1)),console.log(`Memory index: ${n.dir}`),console.log(` files on disk : ${n.files_on_disk}`),console.log(` already listed: ${n.already_indexed}`),console.log(` missing : ${n.restored}`),n.restored>0){for(let s of n.restored_entries.slice(0,20))console.log(` + ${s.name}${s.type?" ["+s.type+"]":""} (${s.file})`);n.restored_entries.length>20&&console.log(` \u2026 and ${n.restored_entries.length-20} more`)}if(n.dangling.length>0){console.log(` dangling : ${n.dangling.length} pointer(s) whose file is gone (left alone)`);for(let s of n.dangling.slice(0,10))console.log(` ? ${s.name} (${s.file})`)}n.unreadable.length>0&&console.log(` unreadable : ${n.unreadable.length} file(s) skipped`),e?console.log(n.restored>0?`
101
+ ${l.line}`:""}`:l.line};a&&(u.additionalContext=a),process.stdout.write(JSON.stringify(u)),process.exit(0)}else if(N==="repair-memory"){let e=process.argv.includes("--dry-run"),t=process.cwd();y.existsSync(S.join(t,".cairn"))||(console.error("[cairn] Not a cairn project (no .cairn/ here). Run this from the project root."),process.exit(1)),Ce(t);let n=Qe(ye(),{dryRun:e});if(n.reason&&(console.error(`[cairn] ${n.reason}: ${n.dir}`),process.exit(1)),console.log(`Memory index: ${n.dir}`),console.log(` files on disk : ${n.files_on_disk}`),console.log(` already listed: ${n.already_indexed}`),console.log(` missing : ${n.restored}`),n.restored>0){for(let s of n.restored_entries.slice(0,20))console.log(` + ${s.name}${s.type?" ["+s.type+"]":""} (${s.file})`);n.restored_entries.length>20&&console.log(` \u2026 and ${n.restored_entries.length-20} more`)}if(n.dangling.length>0){console.log(` dangling : ${n.dangling.length} pointer(s) whose file is gone (left alone)`);for(let s of n.dangling.slice(0,10))console.log(` ? ${s.name} (${s.file})`)}n.unreadable.length>0&&console.log(` unreadable : ${n.unreadable.length} file(s) skipped`),e?console.log(n.restored>0?`
102
102
  Dry run \u2014 nothing written. Re-run without --dry-run to restore them.`:`
103
103
  Dry run \u2014 nothing to restore.`):n.repaired?console.log(`
104
104
  Restored ${n.restored} pointer(s). Previous index kept as ${n.backup}.`):console.log(`
105
- Index already lists every memory file. Nothing to do.`),process.exit(0)}else if(M==="memory-restore"){let e=process.cwd();y.existsSync(S.join(e,".cairn"))||process.exit(0);try{let t=as(e);t>0&&process.stdout.write(`[cairn] Restored ${t} memory file(s) from .cairn/memory backup (Claude store was empty)
106
- `)}catch{}process.exit(0)}else if(M==="resume-hint"){let e=S.join(process.cwd(),".cairn"),t=S.join(e,"session.json"),n=S.join(e,"index.db"),s=S.join(e,".hint-lock"),o=1800*1e3,i=process.cwd()===Z.homedir();if(!i&&y.existsSync(e)&&(y.existsSync(n)||y.existsSync(t))){let f=S.join(e,".cairn-project");if(!y.existsSync(f))try{y.writeFileSync(f,"","utf8"),process.stdout.write(`[cairn] Repaired: wrote .cairn-project sentinel for hook compatibility.
107
- `)}catch{}}if(!i&&y.existsSync(e))try{rs()&&process.stdout.write(`[cairn] Repaired: declared the roadmap slot (${X}) in ~/.claude.json.
108
- `)}catch{}if(!i&&y.existsSync(n))try{let{DatabaseSync:f}=await import("node:sqlite"),g=new f(n,{readonly:!0}),_=null;try{_=le(g)}finally{g.close()}_&&process.stdout.write(`${_}
105
+ Index already lists every memory file. Nothing to do.`),process.exit(0)}else if(N==="memory-restore"){let e=process.cwd();y.existsSync(S.join(e,".cairn"))||process.exit(0);try{let t=ls(e);t>0&&process.stdout.write(`[cairn] Restored ${t} memory file(s) from .cairn/memory backup (Claude store was empty)
106
+ `)}catch{}process.exit(0)}else if(N==="resume-hint"){let e=S.join(process.cwd(),".cairn"),t=S.join(e,"session.json"),n=S.join(e,"index.db"),s=S.join(e,".hint-lock"),o=1800*1e3,i=process.cwd()===Z.homedir();if(!i&&y.existsSync(e)&&(y.existsSync(n)||y.existsSync(t))){let f=S.join(e,".cairn-project");if(!y.existsSync(f))try{y.writeFileSync(f,"","utf8"),process.stdout.write(`[cairn] Repaired: wrote .cairn-project sentinel for hook compatibility.
107
+ `)}catch{}}if(!i&&y.existsSync(e))try{as()&&process.stdout.write(`[cairn] Repaired: declared the roadmap slot (${X}) in ~/.claude.json.
108
+ `)}catch{}if(!i&&y.existsSync(n))try{let{DatabaseSync:f}=await import("node:sqlite"),g=new f(n,{readonly:!0}),_=null;try{_=ue(g)}finally{g.close()}_&&process.stdout.write(`${_}
109
109
  `)}catch{}{let f=[S.join(process.cwd(),".claude","settings.json"),S.join(Z.homedir(),".claude","settings.json")];for(let g of f)try{if(!y.existsSync(g))continue;let _=y.readFileSync(g,"utf8"),w=JSON.parse(_),b=w?.hooks?.PreToolUse;if(!Array.isArray(b))continue;let x=!1;for(let k of b)if(!(k.matcher!=="Edit"||!Array.isArray(k.hooks)))for(let L of k.hooks)L.command==="cairn set-mode edit"&&(L.command="cairn edit-guard",x=!0);x&&(y.writeFileSync(g,JSON.stringify(w,null,2),"utf8"),process.stdout.write(`[cairn] Repaired: migrated cairn set-mode \u2192 cairn edit-guard in ${g}
110
110
  `))}catch{}}let a="";if(!process.stdin.isTTY)try{a=(JSON.parse(y.readFileSync(0,"utf8"))?.prompt||"").toLowerCase()}catch{}let u=/\b(resume|continue|pick up|where (were|did) (we|i)|last session|carry on|what('s| is) next|what (were|was) (we|i)|start|begin)\b/.test(a);y.existsSync(t)||(y.existsSync(n)||(y.existsSync(s)&&Date.now()-y.statSync(s).mtimeMs<o&&process.exit(0),y.mkdirSync(e,{recursive:!0}),y.writeFileSync(s,new Date().toISOString(),"utf8"),process.stdout.write(`[cairn] No index found. Run cairn_maintain to index this project.
111
- `)),process.exit(0)),y.existsSync(s)&&Date.now()-y.statSync(s).mtimeMs<o&&process.exit(0);let r=JSON.parse(y.readFileSync(t,"utf8")),c=new Date(r.checkpoint_at).toLocaleString(),p=/^Auto-checkpoint\b/.test(r.message||""),m=p&&r.last_manual_message||r.message,d=p&&r.last_manual_checkpoint_at?new Date(r.last_manual_checkpoint_at).toLocaleString():c,h=[`[cairn] Prior session: "${m}" (${d}).`,"[cairn] SESSION: ALWAYS call cairn_resume FIRST when asked to resume, continue, or recall prior work \u2014 do NOT substitute git log or file reads. cairn_resume includes commits_since_checkpoint so you can cross-reference checkpoint notes against what was shipped. At end of session, and before any /clear or /compact, call cairn_checkpoint with message + active_files + notes + would_be_lost (what a fresh session would NOT know).","[cairn] TOOLS: NEVER use Bash to run cat, grep, head, tail, find, or ls for file inspection \u2014 always use the Read, Grep, or Glob tools instead. Prefer cairn_search / cairn_bundle / cairn_describe over manual file browsing. Use cairn_outline for structural overview, cairn_todos for backlog, cairn_security before PRs.","[cairn] MEMORY: IMPORTANT \u2014 call cairn_memo IMMEDIATELY and SILENTLY (do NOT ask the user, do NOT list what you are about to save, do NOT confirm afterward) when you observe \u2014 preference: user corrects/confirms your behaviour; experience: non-trivial problem solved; decision: architectural choice made; knowledge: non-obvious codebase fact discovered. cairn_employ_memory is explicit only \u2014 never call it unless asked."];if(!u){let f=re(process.cwd(),r);f.recorded&&h.push(`[cairn] ${_e(f)}`)}u&&h.push(["[cairn] RESUME \u2014 checkpoint loaded inline:",...ht(process.cwd(),r)].join(`
111
+ `)),process.exit(0)),y.existsSync(s)&&Date.now()-y.statSync(s).mtimeMs<o&&process.exit(0);let r=JSON.parse(y.readFileSync(t,"utf8")),c=new Date(r.checkpoint_at).toLocaleString(),p=/^Auto-checkpoint\b/.test(r.message||""),m=p&&r.last_manual_message||r.message,d=p&&r.last_manual_checkpoint_at?new Date(r.last_manual_checkpoint_at).toLocaleString():c,h=[`[cairn] Prior session: "${m}" (${d}).`,"[cairn] SESSION: ALWAYS call cairn_resume FIRST when asked to resume, continue, or recall prior work \u2014 do NOT substitute git log or file reads. cairn_resume includes commits_since_checkpoint so you can cross-reference checkpoint notes against what was shipped. At end of session, and before any /clear or /compact, call cairn_checkpoint with message + active_files + notes + would_be_lost (what a fresh session would NOT know).","[cairn] TOOLS: NEVER use Bash to run cat, grep, head, tail, find, or ls for file inspection \u2014 always use the Read, Grep, or Glob tools instead. Prefer cairn_search / cairn_bundle / cairn_describe over manual file browsing. Use cairn_outline for structural overview, cairn_todos for backlog, cairn_security before PRs.","[cairn] MEMORY: IMPORTANT \u2014 call cairn_memo IMMEDIATELY and SILENTLY (do NOT ask the user, do NOT list what you are about to save, do NOT confirm afterward) when you observe \u2014 preference: user corrects/confirms your behaviour; experience: non-trivial problem solved; decision: architectural choice made; knowledge: non-obvious codebase fact discovered. cairn_employ_memory is explicit only \u2014 never call it unless asked."];if(!u){let f=ie(process.cwd(),r);f.recorded&&h.push(`[cairn] ${Se(f)}`)}u&&h.push(["[cairn] RESUME \u2014 checkpoint loaded inline:",..._t(process.cwd(),r)].join(`
112
112
  `)),process.stdout.write(h.join(`
113
113
  `)+`
114
- `),y.writeFileSync(s,new Date().toISOString(),"utf8"),process.exit(0)}else if(M==="session-start"){let e=process.cwd(),t=S.join(e,".cairn"),n=S.join(t,"session.json");y.existsSync(n)||process.exit(0);let s={};if(!process.stdin.isTTY)try{s=JSON.parse(y.readFileSync(0,"utf8"))||{}}catch{}let o=typeof s.source=="string"?s.source:"",i;try{i=JSON.parse(y.readFileSync(n,"utf8"))}catch{process.exit(0)}He(t);let l=[`[cairn] Session restored automatically after ${o==="compact"?"a /compact":o==="clear"?"a /clear":o==="fork"?"a fork":o==="resume"?"a resume":"a restart"} \u2014 the user did not have to ask:`,...ht(e,i),"","This IS the prior session state. Do not re-derive it from git log or file reads, and do not","ask the user what they were doing \u2014 it is above. If their first message continues this work,",'carry on from would_lose without making them say "resume" first.',"","If would_lose says nothing was recorded, treat that as a gap rather than an all-clear: cairn","cannot see what the previous session never wrote down."].join(`
115
- `);process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:l}})),process.exit(0)}else if(M==="roadmap-hint"){let e=S.join(process.cwd(),".cairn"),t=await yt(e);t.active||process.exit(0);let n=["[cairn] ROADMAP \u2014 cursor is active.",...t.treeLines.map(s=>"[cairn] "+s),`[cairn] Current: ${t.currentLabel}`];if(t.memoLines){n.push("[cairn] \u2500\u2500\u2500\u2500\u2500 Decision memo (current phase) \u2500\u2500\u2500\u2500\u2500");for(let s of t.memoLines)n.push("[cairn] "+s);n.push("[cairn] \u2500\u2500\u2500\u2500\u2500 (re-read after each phase to confirm direction) \u2500\u2500\u2500\u2500\u2500")}n.push("[cairn] Use cairn_roadmap set_status <id> done (auto-advances), focus <id>, or next to move cursor."),process.stdout.write(n.join(`
114
+ `),y.writeFileSync(s,new Date().toISOString(),"utf8"),process.exit(0)}else if(N==="session-start"){let e=process.cwd(),t=S.join(e,".cairn"),n=S.join(t,"session.json");y.existsSync(n)||process.exit(0);let s={};if(!process.stdin.isTTY)try{s=JSON.parse(y.readFileSync(0,"utf8"))||{}}catch{}let o=typeof s.source=="string"?s.source:"",i;try{i=JSON.parse(y.readFileSync(n,"utf8"))}catch{process.exit(0)}Ye(t);let l=[`[cairn] Session restored automatically after ${o==="compact"?"a /compact":o==="clear"?"a /clear":o==="fork"?"a fork":o==="resume"?"a resume":"a restart"} \u2014 the user did not have to ask:`,..._t(e,i),"","This IS the prior session state. Do not re-derive it from git log or file reads, and do not","ask the user what they were doing \u2014 it is above. If their first message continues this work,",'carry on from would_lose without making them say "resume" first.',"","If would_lose says nothing was recorded, treat that as a gap rather than an all-clear: cairn","cannot see what the previous session never wrote down."].join(`
115
+ `);process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:l}})),process.exit(0)}else if(N==="roadmap-hint"){let e=S.join(process.cwd(),".cairn"),t=await St(e);t.active||process.exit(0);let n=["[cairn] ROADMAP \u2014 cursor is active.",...t.treeLines.map(s=>"[cairn] "+s),`[cairn] Current: ${t.currentLabel}`];if(t.memoLines){n.push("[cairn] \u2500\u2500\u2500\u2500\u2500 Decision memo (current phase) \u2500\u2500\u2500\u2500\u2500");for(let s of t.memoLines)n.push("[cairn] "+s);n.push("[cairn] \u2500\u2500\u2500\u2500\u2500 (re-read after each phase to confirm direction) \u2500\u2500\u2500\u2500\u2500")}n.push("[cairn] Use cairn_roadmap set_status <id> done (auto-advances), focus <id>, or next to move cursor."),process.stdout.write(n.join(`
116
116
  `)+`
117
- `),process.exit(0)}else if(M==="install"){let e=process.platform,t=S.join(Z.homedir(),".claude.json"),n={};if(y.existsSync(t))try{n=JSON.parse(y.readFileSync(t,"utf8"))}catch{}n.mcpServers=n.mcpServers||{};let s=!!n.mcpServers.cairn;n.mcpServers.cairn={...s&&typeof n.mcpServers.cairn=="object"?n.mcpServers.cairn:{},command:"cairn-mcp",terminalConnect:_t},y.writeFileSync(t,JSON.stringify(n,null,2),"utf8");let o=S.join(Z.homedir(),".claude");gt(o,!0),console.log("Cairn installed successfully!"),console.log(""),console.log(` Platform : ${e}`),console.log(` MCP : cairn-mcp registered in ${t}${s?" (updated)":""}`),console.log(` Hooks : installed in ${S.join(o,"settings.json")}`),console.log(""),console.log("Active hooks:"),console.log(" PreToolUse[Read] -> cairn minify (compress source files for reading)"),console.log(" PreToolUse[Edit] -> cairn edit-guard (block Edit until re-read with full content)"),console.log(" PreToolUse[Write] -> cairn edit-guard (clear minify state before full file overwrite)"),console.log(" PostToolUse[ExitPlanMode] -> cairn roadmap-capture (approved plan -> roadmap phases)"),console.log(" PostToolUse[TodoWrite] -> cairn roadmap-capture (todos -> roadmap sub-tasks)"),console.log(" Stop -> cairn session-tick (checkpoint + memory backup + roadmap, surfaced)"),console.log(" UserPromptSubmit -> cairn resume-hint (remind Claude of prior session)"),console.log(" UserPromptSubmit -> cairn memory-restore (restore .cairn/memory -> Claude store if empty)"),console.log(" SessionStart -> cairn session-start (re-inject checkpoint on restart / clear / compact / fork)"),console.log(""),console.log("Restart Claude Code to activate the MCP server, then open any project and start working."),console.log("Upgrade any time with: npm install -g @misterhuydo/cairn-mcp"),process.exit(0)}else if(M==="install-hooks"){let e=process.argv.includes("--global"),t=e?S.join(Z.homedir(),".claude"):S.join(process.cwd(),".claude");gt(t,e),console.log(`Cairn hooks installed in ${e?"global (~/.claude/settings.json)":"project (.claude/settings.json)"}`),console.log(""),console.log("Active hooks:"),console.log(" PreToolUse[Read] -> cairn minify (compress source files for reading)"),console.log(" PreToolUse[Edit] -> cairn edit-guard (block Edit until re-read with full content)"),console.log(" PreToolUse[Write] -> cairn edit-guard (clear minify state before full file overwrite)"),console.log(" PostToolUse[ExitPlanMode] -> cairn roadmap-capture (approved plan -> roadmap phases)"),console.log(" PostToolUse[TodoWrite] -> cairn roadmap-capture (todos -> roadmap sub-tasks)"),console.log(" Stop -> cairn session-tick (checkpoint + memory backup + roadmap, surfaced)"),console.log(" UserPromptSubmit -> cairn resume-hint (remind Claude of prior session)"),console.log(" UserPromptSubmit -> cairn memory-restore (restore .cairn/memory -> Claude store if empty)"),console.log(" SessionStart -> cairn session-start (re-inject checkpoint on restart / clear / compact / fork)"),process.exit(0)}else console.error("Usage: cairn <--version | install | install-hooks [--global] | minify | edit-guard | validate-map | checkpoint --auto | session-tick | session-start | resume-hint | roadmap-hint | roadmap-capture | memory-restore | repair-memory [--dry-run]>"),process.exit(1);function gt(e,t){let n=S.join(e,"settings.json"),s={};if(y.existsSync(n))try{s=JSON.parse(y.readFileSync(n,"utf8"))}catch{}s.hooks=s.hooks||{};let o=s.hooks.PreToolUse||[];o.some(d=>d.matcher==="Read"&&d.hooks?.some(h=>h.command==="cairn minify"))||o.push({matcher:"Read",hooks:[{type:"command",command:"cairn minify"}]});let i=o.filter(d=>!(d.matcher==="Edit"&&d.hooks?.some(h=>h.command==="cairn set-mode edit")));i.some(d=>d.matcher==="Edit"&&d.hooks?.some(h=>h.command==="cairn edit-guard"))||i.push({matcher:"Edit",hooks:[{type:"command",command:"cairn edit-guard"}]}),i.some(d=>d.matcher==="Write"&&d.hooks?.some(h=>h.command==="cairn edit-guard"))||i.push({matcher:"Write",hooks:[{type:"command",command:"cairn edit-guard"}]}),s.hooks.PreToolUse=i;let a=(d,h)=>(d||[]).map(f=>({...f,hooks:(f.hooks||[]).filter(g=>!h(g.command))})).filter(f=>(f.hooks||[]).length>0),l=d=>typeof d=="string"&&/sync-memory-(forward|restore)\.(mjs|py)/.test(d),u=a(s.hooks.Stop,d=>d==="cairn checkpoint --auto"||d==="cairn roadmap-hint");u.some(d=>d.hooks?.some(h=>h.command==="cairn session-tick"))||u.push({hooks:[{type:"command",command:"cairn session-tick"}]}),s.hooks.Stop=u;let r=a(s.hooks.UserPromptSubmit,l);r.some(d=>d.hooks?.some(h=>h.command==="cairn resume-hint"))||r.push({hooks:[{type:"command",command:"cairn resume-hint"}]}),r.some(d=>d.hooks?.some(h=>h.command==="cairn memory-restore"))||r.push({hooks:[{type:"command",command:"cairn memory-restore"}]}),s.hooks.UserPromptSubmit=r;let c=a(s.hooks.SessionStart,d=>d==="cairn session-start");c.push({hooks:[{type:"command",command:"cairn session-start"}]}),s.hooks.SessionStart=c;let p=a(s.hooks.PostToolUse,l),m=d=>{p.some(h=>h.matcher===d&&h.hooks?.some(f=>f.command==="cairn roadmap-capture"))||p.push({matcher:d,hooks:[{type:"command",command:"cairn roadmap-capture"}]})};if(m("ExitPlanMode"),m("TodoWrite"),s.hooks.PostToolUse=p,t){let d=S.join(e,"scripts");for(let h of["sync-memory-forward.mjs","sync-memory-restore.mjs","sync-memory-forward.py","sync-memory-restore.py"]){let f=S.join(d,h);if(y.existsSync(f))try{y.unlinkSync(f)}catch{}}}if(!t){let d=S.join(process.cwd(),".cairn");y.mkdirSync(d,{recursive:!0});let h=S.join(d,".cairn-project");y.existsSync(h)||y.writeFileSync(h,"","utf8")}y.mkdirSync(e,{recursive:!0}),y.writeFileSync(n,JSON.stringify(s,null,2),"utf8")}
117
+ `),process.exit(0)}else if(N==="install"){let e=process.platform,t=S.join(Z.homedir(),".claude.json"),n={};if(y.existsSync(t))try{n=JSON.parse(y.readFileSync(t,"utf8"))}catch{}n.mcpServers=n.mcpServers||{};let s=!!n.mcpServers.cairn;n.mcpServers.cairn={...s&&typeof n.mcpServers.cairn=="object"?n.mcpServers.cairn:{},command:"cairn-mcp",terminalConnect:wt},y.writeFileSync(t,JSON.stringify(n,null,2),"utf8");let o=S.join(Z.homedir(),".claude");xt(o,!0),console.log("Cairn installed successfully!"),console.log(""),console.log(` Platform : ${e}`),console.log(` MCP : cairn-mcp registered in ${t}${s?" (updated)":""}`),console.log(` Hooks : installed in ${S.join(o,"settings.json")}`),console.log(""),console.log("Active hooks:"),console.log(" PreToolUse[Read] -> cairn minify (compress source files for reading)"),console.log(" PreToolUse[Edit] -> cairn edit-guard (block Edit until re-read with full content)"),console.log(" PreToolUse[Write] -> cairn edit-guard (clear minify state before full file overwrite)"),console.log(" PostToolUse[ExitPlanMode] -> cairn roadmap-capture (approved plan -> roadmap phases)"),console.log(" PostToolUse[TodoWrite] -> cairn roadmap-capture (todos -> roadmap sub-tasks)"),console.log(" Stop -> cairn session-tick (checkpoint + memory backup + roadmap, surfaced)"),console.log(" UserPromptSubmit -> cairn resume-hint (remind Claude of prior session)"),console.log(" UserPromptSubmit -> cairn memory-restore (restore .cairn/memory -> Claude store if empty)"),console.log(" SessionStart -> cairn session-start (re-inject checkpoint on restart / clear / compact / fork)"),console.log(""),console.log("Restart Claude Code to activate the MCP server, then open any project and start working."),console.log("Upgrade any time with: npm install -g @misterhuydo/cairn-mcp"),process.exit(0)}else if(N==="install-hooks"){let e=process.argv.includes("--global"),t=e?S.join(Z.homedir(),".claude"):S.join(process.cwd(),".claude");xt(t,e),console.log(`Cairn hooks installed in ${e?"global (~/.claude/settings.json)":"project (.claude/settings.json)"}`),console.log(""),console.log("Active hooks:"),console.log(" PreToolUse[Read] -> cairn minify (compress source files for reading)"),console.log(" PreToolUse[Edit] -> cairn edit-guard (block Edit until re-read with full content)"),console.log(" PreToolUse[Write] -> cairn edit-guard (clear minify state before full file overwrite)"),console.log(" PostToolUse[ExitPlanMode] -> cairn roadmap-capture (approved plan -> roadmap phases)"),console.log(" PostToolUse[TodoWrite] -> cairn roadmap-capture (todos -> roadmap sub-tasks)"),console.log(" Stop -> cairn session-tick (checkpoint + memory backup + roadmap, surfaced)"),console.log(" UserPromptSubmit -> cairn resume-hint (remind Claude of prior session)"),console.log(" UserPromptSubmit -> cairn memory-restore (restore .cairn/memory -> Claude store if empty)"),console.log(" SessionStart -> cairn session-start (re-inject checkpoint on restart / clear / compact / fork)"),process.exit(0)}else console.error("Usage: cairn <--version | install | install-hooks [--global] | minify | edit-guard | validate-map | checkpoint --auto | session-tick | session-start | resume-hint | roadmap-hint | roadmap-capture | memory-restore | repair-memory [--dry-run]>"),process.exit(1);function xt(e,t){let n=S.join(e,"settings.json"),s={};if(y.existsSync(n))try{s=JSON.parse(y.readFileSync(n,"utf8"))}catch{}s.hooks=s.hooks||{};let o=s.hooks.PreToolUse||[];o.some(d=>d.matcher==="Read"&&d.hooks?.some(h=>h.command==="cairn minify"))||o.push({matcher:"Read",hooks:[{type:"command",command:"cairn minify"}]});let i=o.filter(d=>!(d.matcher==="Edit"&&d.hooks?.some(h=>h.command==="cairn set-mode edit")));i.some(d=>d.matcher==="Edit"&&d.hooks?.some(h=>h.command==="cairn edit-guard"))||i.push({matcher:"Edit",hooks:[{type:"command",command:"cairn edit-guard"}]}),i.some(d=>d.matcher==="Write"&&d.hooks?.some(h=>h.command==="cairn edit-guard"))||i.push({matcher:"Write",hooks:[{type:"command",command:"cairn edit-guard"}]}),s.hooks.PreToolUse=i;let a=(d,h)=>(d||[]).map(f=>({...f,hooks:(f.hooks||[]).filter(g=>!h(g.command))})).filter(f=>(f.hooks||[]).length>0),l=d=>typeof d=="string"&&/sync-memory-(forward|restore)\.(mjs|py)/.test(d),u=a(s.hooks.Stop,d=>d==="cairn checkpoint --auto"||d==="cairn roadmap-hint");u.some(d=>d.hooks?.some(h=>h.command==="cairn session-tick"))||u.push({hooks:[{type:"command",command:"cairn session-tick"}]}),s.hooks.Stop=u;let r=a(s.hooks.UserPromptSubmit,l);r.some(d=>d.hooks?.some(h=>h.command==="cairn resume-hint"))||r.push({hooks:[{type:"command",command:"cairn resume-hint"}]}),r.some(d=>d.hooks?.some(h=>h.command==="cairn memory-restore"))||r.push({hooks:[{type:"command",command:"cairn memory-restore"}]}),s.hooks.UserPromptSubmit=r;let c=a(s.hooks.SessionStart,d=>d==="cairn session-start");c.push({hooks:[{type:"command",command:"cairn session-start"}]}),s.hooks.SessionStart=c;let p=a(s.hooks.PostToolUse,l),m=d=>{p.some(h=>h.matcher===d&&h.hooks?.some(f=>f.command==="cairn roadmap-capture"))||p.push({matcher:d,hooks:[{type:"command",command:"cairn roadmap-capture"}]})};if(m("ExitPlanMode"),m("TodoWrite"),s.hooks.PostToolUse=p,t){let d=S.join(e,"scripts");for(let h of["sync-memory-forward.mjs","sync-memory-restore.mjs","sync-memory-forward.py","sync-memory-restore.py"]){let f=S.join(d,h);if(y.existsSync(f))try{y.unlinkSync(f)}catch{}}}if(!t){let d=S.join(process.cwd(),".cairn");y.mkdirSync(d,{recursive:!0});let h=S.join(d,".cairn-project");y.existsSync(h)||y.writeFileSync(h,"","utf8")}y.mkdirSync(e,{recursive:!0}),y.writeFileSync(n,JSON.stringify(s,null,2),"utf8")}