infernoflow 0.43.12 → 0.44.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +87 -149
  2. package/dist/bin/infernoflow.mjs +30 -33
  3. package/dist/lib/amp/io.mjs +8 -8
  4. package/dist/lib/cleanTree.mjs +12 -0
  5. package/dist/lib/commands/ai.mjs +2 -2
  6. package/dist/lib/commands/amp.mjs +4 -4
  7. package/dist/lib/commands/ask.mjs +2 -2
  8. package/dist/lib/commands/context.mjs +18 -18
  9. package/dist/lib/commands/doctor.mjs +2 -3
  10. package/dist/lib/commands/init.mjs +31 -32
  11. package/dist/lib/commands/log.mjs +13 -19
  12. package/dist/lib/commands/recap.mjs +3 -3
  13. package/dist/lib/commands/refresh.mjs +5 -0
  14. package/dist/lib/commands/setup.mjs +6 -6
  15. package/dist/lib/commands/status.mjs +6 -7
  16. package/dist/lib/commands/switch.mjs +5 -5
  17. package/dist/lib/commands/sync.mjs +41 -0
  18. package/dist/lib/git/branch.mjs +2 -0
  19. package/dist/lib/mcpRuntime.mjs +1 -0
  20. package/dist/lib/projectRoot.mjs +1 -0
  21. package/dist/lib/ruleFiles.mjs +9 -8
  22. package/dist/lib/upgradeCheck.mjs +1 -1
  23. package/dist/templates/cursor/inferno-mcp-server.mjs +200 -325
  24. package/package.json +13 -5
  25. package/dist/lib/commands/changelog.mjs +0 -21
  26. package/dist/lib/commands/ci.mjs +0 -3
  27. package/dist/lib/commands/claudeMd.mjs +0 -116
  28. package/dist/lib/commands/coverage.mjs +0 -2
  29. package/dist/lib/commands/demo.mjs +0 -113
  30. package/dist/lib/commands/diff.mjs +0 -5
  31. package/dist/lib/commands/explain.mjs +0 -8
  32. package/dist/lib/commands/feedback.mjs +0 -12
  33. package/dist/lib/commands/graph.mjs +0 -76
  34. package/dist/lib/commands/impact.mjs +0 -2
  35. package/dist/lib/commands/implement.mjs +0 -7
  36. package/dist/lib/commands/monorepo.mjs +0 -4
  37. package/dist/lib/commands/notify.mjs +0 -4
  38. package/dist/lib/commands/prImpact.mjs +0 -2
  39. package/dist/lib/commands/publish.mjs +0 -21
  40. package/dist/lib/commands/review.mjs +0 -24
  41. package/dist/lib/commands/run.mjs +0 -10
  42. package/dist/lib/commands/scaffold.mjs +0 -124
  43. package/dist/lib/commands/scan.mjs +0 -42
  44. package/dist/lib/commands/stability.mjs +0 -2
  45. package/dist/lib/commands/stats.mjs +0 -4
  46. package/dist/lib/commands/suggest.mjs +0 -62
  47. package/dist/lib/commands/syncAuto.mjs +0 -1
  48. package/dist/lib/commands/test.mjs +0 -6
  49. package/dist/lib/commands/theme.mjs +0 -18
  50. package/dist/lib/commands/upgrade.mjs +0 -20
  51. package/dist/lib/commands/watch.mjs +0 -7
  52. package/dist/lib/commands/why.mjs +0 -4
@@ -1,6 +1,6 @@
1
- import*as n from"node:fs";import*as l from"node:path";import*as w from"node:os";import{fileURLToPath as C}from"node:url";import{execSync as j}from"node:child_process";import{detectIdeContext as h}from"../ai/ideDetection.mjs";import{header as _,ok as y,warn as x,info as S,done as M,cyan as f,yellow as $,bold as g,green as d,gray as k}from"../ui/output.mjs";import"../cursorHooksInstall.mjs";import"../vsCodeCopilotHooksInstall.mjs";const J=l.dirname(C(import.meta.url));function v(){return l.resolve(J,"../../templates")}function P(s){try{return j(`npx infernoflow ${s}`,{encoding:"utf8",cwd:process.cwd(),timeout:6e4,stdio:["inherit","pipe","pipe"]})}catch(t){return t.stdout||t.stderr||t.message}}const O=["infernoflow_status","infernoflow_run","infernoflow_apply","infernoflow_check","infernoflow_context","infernoflow_implement","infernoflow_git_drift","infernoflow_scan_ui","infernoflow_review","amp_read","amp_write","amp_search","amp_handoff","amp_health"];function F(s){const t=l.join(w.homedir(),".claude.json");let c={};if(n.existsSync(t))try{c=JSON.parse(n.readFileSync(t,"utf8"))}catch{c={}}c.mcpServers||(c.mcpServers={});const o=c.mcpServers.infernoflow;if(o&&o.args&&o.args[0]===s)return{updated:!1};c.mcpServers.infernoflow={command:"node",args:[s]};const r=JSON.stringify(c,null,2).replace(/\u0000+/g,"");return n.writeFileSync(t,r,"utf8"),{updated:!0,path:t}}function N(s,t){const c=l.join(s,".vscode"),o=l.join(c,"mcp.json");let r={};if(n.existsSync(o))try{r=JSON.parse(n.readFileSync(o,"utf8"))}catch{r={}}r.servers||(r.servers={});const i=r.servers.infernoflow;return i&&i.args&&i.args[0]===t?{updated:!1}:(r.servers.infernoflow={type:"stdio",command:"node",args:[t]},n.mkdirSync(c,{recursive:!0}),n.writeFileSync(o,JSON.stringify(r,null,2)+`
2
- `,"utf8"),{updated:!0,path:o})}function R(s,t){const c=l.join(s,".claude"),o=l.join(c,"settings.json");let r={};if(n.existsSync(o))try{r=JSON.parse(n.readFileSync(o,"utf8"))}catch{r={}}const i=new Set(r.allowedTools||[]);for(const a of O)i.add(`mcp__infernoflow__${a}`);const p={...r,allowedTools:[...i]};return n.mkdirSync(c,{recursive:!0}),n.writeFileSync(o,JSON.stringify(p,null,2),"utf8"),o}function T(s,{silent:t=!1}={}){const c=v(),o=t?()=>{}:e=>y(e),r=t?()=>{}:e=>x(e),i={mcpServer:!1,claudeJson:!1,claudeSettings:!1},p=l.join(c,"cursor","inferno-mcp-server.mjs"),a=l.join(s,".cursor","inferno-mcp-server.mjs");try{n.existsSync(a)||(n.mkdirSync(l.dirname(a),{recursive:!0}),n.copyFileSync(p,a),i.mcpServer=!0,o("Copied MCP server \u2192 "+f(".cursor/inferno-mcp-server.mjs")))}catch(e){r("MCP server copy skipped: "+e.message)}try{F(a).updated&&(i.claudeJson=!0,o("Registered MCP server in "+f("~/.claude.json")))}catch(e){r("~/.claude.json update skipped: "+e.message)}try{N(s,a).updated&&(i.vscodeMcp=!0,o("Registered MCP server in "+f(".vscode/mcp.json")+k(" (Copilot Chat)")))}catch(e){r(".vscode/mcp.json update skipped: "+e.message)}try{const e=l.join(s,".cursor","mcp.json");let u={};if(n.existsSync(e))try{u=JSON.parse(n.readFileSync(e,"utf8"))}catch{u={}}u.mcpServers||(u.mcpServers={});const m=u.mcpServers.infernoflow;(!m||!m.args||m.args[0]!==a)&&(u.mcpServers.infernoflow={command:"node",args:[a],env:{}},n.mkdirSync(l.dirname(e),{recursive:!0}),n.writeFileSync(e,JSON.stringify(u,null,2)+`
3
- `,"utf8"),i.cursorMcp=!0,o("Registered MCP server in "+f(".cursor/mcp.json")))}catch(e){r(".cursor/mcp.json update skipped: "+e.message)}try{R(s,!1),i.claudeSettings=!0,o("Pre-approved infernoflow tools in "+f(".claude/settings.json"))}catch(e){r(".claude/settings.json skipped: "+e.message)}return i}async function E(s){const t=process.cwd(),c=s.includes("--force")||s.includes("-f"),o=s.includes("--yes")||s.includes("-y"),r=v();_("infernoflow setup");const{ideDetected:i}=h("auto");S(`IDE detected: ${g(i==="cursor"?"Cursor":i==="vscode"?"VS Code":i==="windsurf"?"Windsurf":"unknown")}`);const a=l.join(t,".ai-memory");n.existsSync(a)?y(".ai-memory/ already exists \u2014 skipping init"):(console.log(`
4
- ${$(".ai-memory/")} not found \u2014 running init ...
5
- `),P(o?"init --yes":"init")),console.log(),S("Wiring up MCP servers for Cursor / VS Code Copilot / Claude Code ...");const e=T(t,{silent:!1});console.log(),M("infernoflow ready"),console.log(`
6
- ${g("What was set up:")}`),console.log(` ${d("\u2714")} MCP server installed \u2192 ${f(".cursor/inferno-mcp-server.mjs")}`),e.cursorMcp&&console.log(` ${d("\u2714")} Cursor MCP config \u2192 ${f(".cursor/mcp.json")}`),e.vscodeMcp&&console.log(` ${d("\u2714")} VS Code Copilot MCP config \u2192 ${f(".vscode/mcp.json")}`),e.claudeJson&&console.log(` ${d("\u2714")} Claude Code MCP config \u2192 ${f("~/.claude.json")}`),e.claudeSettings&&console.log(` ${d("\u2714")} Auto-approved tools \u2192 ${f(".claude/settings.json")}`),console.log(),console.log(` ${g("Next step:")} Restart your AI tool. Test by asking:`),console.log(` ${f('"call the amp_write tool with a test note"')}`),console.log()}export{O as MCP_TOOLS,T as autoSetupMcp,E as setupCommand,F as updateClaudeJson,N as updateVscodeMcpJson,R as writeClaudeSettings};
1
+ import*as n from"node:fs";import*as a from"node:path";import*as k from"node:os";import{fileURLToPath as P}from"node:url";import{execSync as J}from"node:child_process";import{detectIdeContext as O}from"../ai/ideDetection.mjs";import{header as R,ok as S,warn as F,info as w,done as N,cyan as f,yellow as v,bold as g,green as d,gray as T}from"../ui/output.mjs";import"../cursorHooksInstall.mjs";import"../vsCodeCopilotHooksInstall.mjs";const V=a.dirname(P(import.meta.url));function C(){return a.resolve(V,"../../templates")}function L(s){try{return J(`npx infernoflow ${s}`,{encoding:"utf8",cwd:process.cwd(),timeout:6e4,stdio:["inherit","pipe","pipe"]})}catch(t){return t.stdout||t.stderr||t.message}}const D=["infernoflow_status","infernoflow_run","infernoflow_apply","infernoflow_check","infernoflow_context","infernoflow_implement","infernoflow_git_drift","infernoflow_scan_ui","infernoflow_review","amp_read","amp_write","amp_search","amp_handoff","amp_health"];function I(s){const t=a.join(k.homedir(),".claude.json");let c={};if(n.existsSync(t))try{c=JSON.parse(n.readFileSync(t,"utf8"))}catch{c={}}c.mcpServers||(c.mcpServers={});const o=c.mcpServers.infernoflow;if(o&&o.args&&o.args[0]===s)return{updated:!1};c.mcpServers.infernoflow={command:"node",args:[s]};const r=JSON.stringify(c,null,2).replace(/\u0000+/g,"");return n.writeFileSync(t,r,"utf8"),{updated:!0,path:t}}function W(s,t){const c=a.join(s,".vscode"),o=a.join(c,"mcp.json");let r={};if(n.existsSync(o))try{r=JSON.parse(n.readFileSync(o,"utf8"))}catch{r={}}r.servers||(r.servers={});const i=r.servers.infernoflow;return i&&i.args&&i.args[0]===t?{updated:!1}:(r.servers.infernoflow={type:"stdio",command:"node",args:[t]},n.mkdirSync(c,{recursive:!0}),n.writeFileSync(o,JSON.stringify(r,null,2)+`
2
+ `,"utf8"),{updated:!0,path:o})}function A(s,t){const c=a.join(s,".claude"),o=a.join(c,"settings.json");let r={};if(n.existsSync(o))try{r=JSON.parse(n.readFileSync(o,"utf8"))}catch{r={}}const i=new Set(r.allowedTools||[]);for(const l of D)i.add(`mcp__infernoflow__${l}`);const m={...r,allowedTools:[...i]};return n.mkdirSync(c,{recursive:!0}),n.writeFileSync(o,JSON.stringify(m,null,2),"utf8"),o}function b(s,{silent:t=!1}={}){const c=C(),o=t?()=>{}:e=>S(e),r=t?()=>{}:e=>F(e),i={mcpServer:!1,claudeJson:!1,claudeSettings:!1},m=a.join(c,"cursor","inferno-mcp-server.mjs"),l=a.join(s,".cursor","inferno-mcp-server.mjs");try{n.existsSync(l)||(n.mkdirSync(a.dirname(l),{recursive:!0}),n.copyFileSync(m,l),i.mcpServer=!0,o("Copied MCP server \u2192 "+f(".cursor/inferno-mcp-server.mjs")))}catch(e){r("MCP server copy skipped: "+e.message)}try{I(l).updated&&(i.claudeJson=!0,o("Registered MCP server in "+f("~/.claude.json")))}catch(e){r("~/.claude.json update skipped: "+e.message)}try{W(s,l).updated&&(i.vscodeMcp=!0,o("Registered MCP server in "+f(".vscode/mcp.json")+T(" (Copilot Chat)")))}catch(e){r(".vscode/mcp.json update skipped: "+e.message)}try{const e=a.join(s,".cursor","mcp.json");let p={};if(n.existsSync(e))try{p=JSON.parse(n.readFileSync(e,"utf8"))}catch{p={}}p.mcpServers||(p.mcpServers={});const u=p.mcpServers.infernoflow;(!u||!u.args||u.args[0]!==l)&&(p.mcpServers.infernoflow={command:"node",args:[l],env:{}},n.mkdirSync(a.dirname(e),{recursive:!0}),n.writeFileSync(e,JSON.stringify(p,null,2)+`
3
+ `,"utf8"),i.cursorMcp=!0,o("Registered MCP server in "+f(".cursor/mcp.json")))}catch(e){r(".cursor/mcp.json update skipped: "+e.message)}try{A(s,!1),i.claudeSettings=!0,o("Pre-approved infernoflow tools in "+f(".claude/settings.json"))}catch(e){r(".claude/settings.json skipped: "+e.message)}return i}async function Q(s){const t=process.cwd(),c=s.includes("--force")||s.includes("-f"),o=s.includes("--yes")||s.includes("-y"),r=C();R("infernoflow setup");const{ideDetected:i}=O("auto");w(`IDE detected: ${g(i==="cursor"?"Cursor":i==="vscode"?"VS Code":i==="windsurf"?"Windsurf":"unknown")}`);const l=a.join(t,".ai-memory");n.existsSync(l)?S(".ai-memory/ already exists \u2014 skipping init"):(console.log(`
4
+ ${v(".ai-memory/")} not found \u2014 running init ...
5
+ `),L(o?"init --yes":"init")),console.log(),w("Wiring up MCP servers for Cursor / VS Code Copilot / Claude Code ...");const e=b(t,{silent:!1});console.log(),N("infernoflow ready"),console.log(`
6
+ ${g("What was set up:")}`),console.log(` ${d("\u2714")} MCP server installed \u2192 ${f(".cursor/inferno-mcp-server.mjs")}`),e.cursorMcp&&console.log(` ${d("\u2714")} Cursor MCP config \u2192 ${f(".cursor/mcp.json")}`),e.vscodeMcp&&console.log(` ${d("\u2714")} VS Code Copilot MCP config \u2192 ${f(".vscode/mcp.json")}`),e.claudeJson&&console.log(` ${d("\u2714")} Claude Code MCP config \u2192 ${f("~/.claude.json")}`),e.claudeSettings&&console.log(` ${d("\u2714")} Auto-approved tools \u2192 ${f(".claude/settings.json")}`);try{const{detectStaleMcpRuntime:p}=await import("../mcpRuntime.mjs"),{readFileSync:u}=await import("node:fs"),{dirname:j,join:h}=await import("node:path"),{fileURLToPath:_}=await import("node:url"),x=j(_(import.meta.url)),M=h(x,"..","..","package.json"),$=JSON.parse(u(M,"utf8")).version,y=p(t,$);y&&(console.log(),console.log(` ${v("\u26A0")} ${g("Restart required:")} ${y.message}`))}catch{}console.log(),console.log(` ${g("Next step:")} Restart your AI tool. Test by asking:`),console.log(` ${f('"call the amp_write tool with a test note"')}`),console.log()}export{D as MCP_TOOLS,b as autoSetupMcp,Q as setupCommand,I as updateClaudeJson,W as updateVscodeMcpJson,A as writeClaudeSettings};
@@ -1,7 +1,6 @@
1
- import*as o from"node:fs";import*as l from"node:path";import{header as L,ok as V,fail as J,warn as U,section as $,bold as r,cyan as w,yellow as m,gray as n,green as d,red as R,white as W}from"../ui/output.mjs";import{ampPaths as _}from"../amp/io.mjs";function E(a){const s=Math.floor((Date.now()-a)/1e3);return s<60?"just now":s<3600?`${Math.floor(s/60)}m ago`:s<86400?`${Math.floor(s/3600)}h ago`:`${Math.floor(s/86400)}d ago`}function z(a,s){const f=new Set;if(o.existsSync(a))for(const i of o.readdirSync(a).filter(y=>y.endsWith(".json")))try{(JSON.parse(o.readFileSync(l.join(a,i),"utf8")).capabilitiesCovered||[]).forEach(M=>f.add(M))}catch{}return{covered:s.filter(i=>f.has(i)),uncovered:s.filter(i=>!f.has(i))}}async function K(a=[]){const s=a.includes("--json"),f=process.cwd(),i=l.join(f,"inferno"),y=l.join(f,".ai-memory");s||L("status"),!o.existsSync(i)&&!o.existsSync(y)&&(s&&(console.log(JSON.stringify({ok:!1,error:"not_initialized",hint:"Run: infernoflow init"},null,2)),process.exit(1)),J("not initialized \u2014 neither .ai-memory/ nor inferno/ found","Run: infernoflow init"),console.log(),process.exit(1));const M=l.join(y,"amp.json"),k=l.join(i,"config.json"),S=l.join(i,"contract.json"),D=o.existsSync(M)||o.existsSync(y),H=(()=>{try{return JSON.parse(o.readFileSync(k,"utf8")).mode||null}catch{return null}})();if(D||H==="memory"||!o.existsSync(S)&&o.existsSync(k)){const t=_(f).sessions,e=o.existsSync(t)?o.readFileSync(t,"utf8").split(`
2
- `).filter(Boolean).map(g=>{try{return JSON.parse(g)}catch{return null}}).filter(Boolean):[],c=e.filter(g=>g.type==="gotcha").length,h=e.filter(g=>g.type==="decision").length,u=e.filter(g=>g.type==="attempt").length,p=e[e.length-1];if(s){console.log(JSON.stringify({ok:!0,mode:"memory",entries:e.length,gotchas:c,decisions:h,attempts:u,lastEntry:p?p.ts:null},null,2));return}$("Session memory"),console.log(` ${n("entries")} ${r(String(e.length))}`),console.log(` ${n("gotchas")} ${r(String(c))}`),console.log(` ${n("decisions")} ${r(String(h))}`),console.log(` ${n("attempts")} ${r(String(u))}`),p&&console.log(` ${n("last entry")} ${n(E(new Date(p.ts).getTime()))}`),console.log(),e.length===0?console.log(` ${m("\u25CF")} ${r(m("empty"))} ${n("\u2014 log your first gotcha:")} ${w('infernoflow log "..." --type gotcha')}`):console.log(` ${d("\u25CF")} ${r(d("ready"))} ${n("\u2014 run")} ${w("infernoflow recap")} ${n("for the full session summary")}`),o.existsSync(S)?console.log():console.log(`
3
- ${n("Want capability contracts + CI gates? Run:")} ${w("infernoflow init --mode full")}
4
- `);return}o.existsSync(S)||(s&&(console.log(JSON.stringify({ok:!1,error:"contract_not_found"},null,2)),process.exit(1)),J("contract.json not found"),console.log(),process.exit(1));const j=JSON.parse(o.readFileSync(S,"utf8")),C=j.capabilities||[],F=o.statSync(S),N=l.join(i,"scenarios"),O=l.join(i,"CHANGELOG.md"),P=l.join(i,"capabilities.json"),{covered:I,uncovered:x}=z(N,C),G=o.existsSync(O)&&/##\s+Unreleased/i.test(o.readFileSync(O,"utf8")),v=[];x.length>0&&v.push(`${x.length} capabilities without scenario coverage`),G||v.push("CHANGELOG missing ## Unreleased section");const b=v.length===0;if(s){const t={ok:b,driftReasons:v,project:{policyId:j.policyId||null,policyVersion:j.policyVersion||null,lastChange:E(F.mtimeMs)},capabilities:{total:C.length,uncovered:x},changelog:{hasUnreleased:G}};console.log(JSON.stringify(t,null,2)),process.exit(b?0:1)}b||($("Drift"),v.forEach(t=>console.log(` ${m("\u26A0")} ${t}`))),$("Project"),console.log(` ${n("policy")} ${r(j.policyId||"\u2014")}`),console.log(` ${n("version")} ${r("v"+(j.policyVersion||"?"))}`),console.log(` ${n("last change")} ${n(E(F.mtimeMs))}`),$(`Capabilities ${n("("+C.length+")")}`);let A={};if(o.existsSync(P))try{(JSON.parse(o.readFileSync(P,"utf8")).capabilities||[]).forEach(e=>{A[e.id]=e})}catch{}if(C.forEach(t=>{const e=A[t],h=I.includes(t)?d("\u2714"):R("\u2718"),u=e?.title?n(` \u2014 ${e.title}`):"",p=e?.since?n(` [${e.since}]`):"";console.log(` ${h} ${W(t)}${u}${p}`)}),x.length>0?console.log(`
5
- ${m("\u26A0")} ${x.length} capability(ies) lack scenario coverage`):console.log(`
6
- ${d("\u2714")} All capabilities have scenario coverage`),$("Scenarios"),o.existsSync(N)){const t=o.readdirSync(N).filter(e=>e.endsWith(".json"));t.length===0?U("No scenario files \u2014 add .json files to inferno/scenarios/"):t.forEach(e=>{try{const c=JSON.parse(o.readFileSync(l.join(N,e),"utf8")),h=c.steps?.length||0,u=(c.capabilitiesCovered||[]).length;console.log(` ${d("\u2714")} ${w(e)} ${n(`\u2014 ${h} steps, ${u} caps covered`)}`)}catch{console.log(` ${R("\u2718")} ${w(e)} ${n("\u2014 invalid JSON")}`)}})}else U("scenarios/ directory not found");if($("Changelog"),o.existsSync(O)){const t=o.readFileSync(O,"utf8");/##\s+Unreleased/i.test(t)?V("Has ## Unreleased section"):J("Missing ## Unreleased section"),t.split(`
7
- `).filter(c=>/^##\s/.test(c)).slice(0,3).forEach(c=>console.log(` ${n(c)}`))}else J("inferno/CHANGELOG.md not found");console.log(),console.log(b?` ${d("\u25CF")} ${r(d("ready"))} ${n("\u2014 run infernoflow check for full validation")}`:` ${m("\u25CF")} ${r(m("needs attention"))} ${n("\u2014 run infernoflow check for details")}`),console.log()}export{K as statusCommand};
1
+ import*as e from"node:fs";import*as l from"node:path";import{header as I,ok as L,fail as b,warn as A,section as $,bold as r,cyan as v,yellow as u,gray as n,green as h,red as R,white as V}from"../ui/output.mjs";import{readEntries as W}from"../amp/io.mjs";function M(f){const t=Math.floor((Date.now()-f)/1e3);return t<60?"just now":t<3600?`${Math.floor(t/60)}m ago`:t<86400?`${Math.floor(t/3600)}h ago`:`${Math.floor(t/86400)}d ago`}function _(f,t){const g=new Set;if(e.existsSync(f))for(const i of e.readdirSync(f).filter(y=>y.endsWith(".json")))try{(JSON.parse(e.readFileSync(l.join(f,i),"utf8")).capabilitiesCovered||[]).forEach(E=>g.add(E))}catch{}return{covered:t.filter(i=>g.has(i)),uncovered:t.filter(i=>!g.has(i))}}async function K(f=[]){const t=f.includes("--json"),g=process.cwd(),i=l.join(g,"inferno"),y=l.join(g,".ai-memory");t||I("status"),!e.existsSync(i)&&!e.existsSync(y)&&(t&&(console.log(JSON.stringify({ok:!1,error:"not_initialized",hint:"Run: infernoflow init"},null,2)),process.exit(1)),b("not initialized \u2014 neither .ai-memory/ nor inferno/ found","Run: infernoflow init"),console.log(),process.exit(1));const E=l.join(y,"amp.json"),J=l.join(i,"config.json"),m=l.join(i,"contract.json"),U=e.existsSync(E)||e.existsSync(y),D=(()=>{try{return JSON.parse(e.readFileSync(J,"utf8")).mode||null}catch{return null}})();if(U||D==="memory"||!e.existsSync(m)&&e.existsSync(J)){const o=W(g),s=o.filter(d=>d.type==="gotcha").length,c=o.filter(d=>d.type==="decision").length,p=o.filter(d=>d.type==="attempt").length,a=o[o.length-1];if(t){console.log(JSON.stringify({ok:!0,mode:"memory",entries:o.length,gotchas:s,decisions:c,attempts:p,lastEntry:a?a.ts:null},null,2));return}$("Session memory"),console.log(` ${n("entries")} ${r(String(o.length))}`),console.log(` ${n("gotchas")} ${r(String(s))}`),console.log(` ${n("decisions")} ${r(String(c))}`),console.log(` ${n("attempts")} ${r(String(p))}`),a&&console.log(` ${n("last entry")} ${n(M(new Date(a.ts).getTime()))}`),console.log(),o.length===0?console.log(` ${u("\u25CF")} ${r(u("empty"))} ${n("\u2014 log your first gotcha:")} ${v('infernoflow log "..." --type gotcha')}`):console.log(` ${h("\u25CF")} ${r(h("ready"))} ${n("\u2014 run")} ${v("infernoflow recap")} ${n("for the full session summary")}`),e.existsSync(m)?console.log():console.log(`
2
+ ${n("Want capability contracts + CI gates? Run:")} ${v("infernoflow init --mode full")}
3
+ `);return}e.existsSync(m)||(t&&(console.log(JSON.stringify({ok:!1,error:"contract_not_found"},null,2)),process.exit(1)),b("contract.json not found"),console.log(),process.exit(1));const S=JSON.parse(e.readFileSync(m,"utf8")),w=S.capabilities||[],k=e.statSync(m),C=l.join(i,"scenarios"),N=l.join(i,"CHANGELOG.md"),F=l.join(i,"capabilities.json"),{covered:H,uncovered:j}=_(C,w),G=e.existsSync(N)&&/##\s+Unreleased/i.test(e.readFileSync(N,"utf8")),x=[];j.length>0&&x.push(`${j.length} capabilities without scenario coverage`),G||x.push("CHANGELOG missing ## Unreleased section");const O=x.length===0;if(t){const o={ok:O,driftReasons:x,project:{policyId:S.policyId||null,policyVersion:S.policyVersion||null,lastChange:M(k.mtimeMs)},capabilities:{total:w.length,uncovered:j},changelog:{hasUnreleased:G}};console.log(JSON.stringify(o,null,2)),process.exit(O?0:1)}O||($("Drift"),x.forEach(o=>console.log(` ${u("\u26A0")} ${o}`))),$("Project"),console.log(` ${n("policy")} ${r(S.policyId||"\u2014")}`),console.log(` ${n("version")} ${r("v"+(S.policyVersion||"?"))}`),console.log(` ${n("last change")} ${n(M(k.mtimeMs))}`),$(`Capabilities ${n("("+w.length+")")}`);let P={};if(e.existsSync(F))try{(JSON.parse(e.readFileSync(F,"utf8")).capabilities||[]).forEach(s=>{P[s.id]=s})}catch{}if(w.forEach(o=>{const s=P[o],p=H.includes(o)?h("\u2714"):R("\u2718"),a=s?.title?n(` \u2014 ${s.title}`):"",d=s?.since?n(` [${s.since}]`):"";console.log(` ${p} ${V(o)}${a}${d}`)}),j.length>0?console.log(`
4
+ ${u("\u26A0")} ${j.length} capability(ies) lack scenario coverage`):console.log(`
5
+ ${h("\u2714")} All capabilities have scenario coverage`),$("Scenarios"),e.existsSync(C)){const o=e.readdirSync(C).filter(s=>s.endsWith(".json"));o.length===0?A("No scenario files \u2014 add .json files to inferno/scenarios/"):o.forEach(s=>{try{const c=JSON.parse(e.readFileSync(l.join(C,s),"utf8")),p=c.steps?.length||0,a=(c.capabilitiesCovered||[]).length;console.log(` ${h("\u2714")} ${v(s)} ${n(`\u2014 ${p} steps, ${a} caps covered`)}`)}catch{console.log(` ${R("\u2718")} ${v(s)} ${n("\u2014 invalid JSON")}`)}})}else A("scenarios/ directory not found");if($("Changelog"),e.existsSync(N)){const o=e.readFileSync(N,"utf8");/##\s+Unreleased/i.test(o)?L("Has ## Unreleased section"):b("Missing ## Unreleased section"),o.split(`
6
+ `).filter(c=>/^##\s/.test(c)).slice(0,3).forEach(c=>console.log(` ${n(c)}`))}else b("inferno/CHANGELOG.md not found");console.log(),console.log(O?` ${h("\u25CF")} ${r(h("ready"))} ${n("\u2014 run infernoflow check for full validation")}`:` ${u("\u25CF")} ${r(u("needs attention"))} ${n("\u2014 run infernoflow check for details")}`),console.log()}export{K as statusCommand};
@@ -1,11 +1,11 @@
1
- import*as b from"node:fs";import*as m from"node:path";import"node:os";import{execSync as S}from"node:child_process";import{bold as V,cyan as $,gray as it,green as rt,yellow as J,red as pt}from"../ui/output.mjs";import{ampPaths as ut,readEntries as dt,appendEntry as ht}from"../amp/io.mjs";const T="inferno";function z(){return ut(process.cwd())}const gt=m.join(T,"HANDOFF.md"),Ft=m.join(T,"sessions.jsonl"),K=m.join(T,"context-state.json"),X=m.join(T,"contract.json"),q=m.join(T,"theme.json"),Q=m.join(T,"adoption_profile.json");function u(t){try{return JSON.parse(b.readFileSync(t,"utf8"))}catch{return null}}function mt(t){try{return b.readFileSync(t,"utf8")}catch{return null}}function Y(t){return t?new Date(t).toLocaleString("en-GB",{day:"2-digit",month:"short",hour:"2-digit",minute:"2-digit"}):"unknown"}function Z(t){if(t<0)return"unknown";const i=Math.floor(t/36e5),l=Math.floor(t%36e5/6e4);return i>0?`${i}h ${l}m`:`${l}m`}function tt(){return dt(process.cwd())}function et(t,i,l){if(l)return new Date(0);if(i){const o=i.match(/^(\d+)h$/i),c=i.match(/^(\d+)d$/i);if(o)return new Date(Date.now()-parseInt(o[1])*36e5);if(c)return new Date(Date.now()-parseInt(c[1])*864e5);const s=new Date(i);if(!isNaN(s))return s}for(let o=t.length-1;o>=0;o--)if(t[o].type==="handoff"){const c=new Date(t[o].ts||0),s=new Date(Date.now()-864e5);return c>s?c:s}return new Date(Date.now()-864e5)}function yt(t){try{const i=process.platform;if(i==="win32")S("clip",{input:t});else if(i==="darwin")S("pbcopy",{input:t});else try{S("xclip -selection clipboard",{input:t})}catch{S("xsel --clipboard --input",{input:t})}return!0}catch{return!1}}function ot(){if(process.env.CURSOR_SESSION)return"Cursor";if(process.env.COPILOT_SESSION)return"GitHub Copilot";if(process.env.CLAUDE_CODE_SESSION)return"Claude Code";if(process.env.WINDSURF_SESSION)return"Windsurf";if(process.env.TERM_PROGRAM==="vscode")return"VS Code";const t=u(Q);return t?.ide?t.ide:null}function wt(){try{const t=S("git diff --stat HEAD 2>/dev/null || git diff --cached --stat 2>/dev/null",{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim();return t||S("git log --stat -1 --pretty= 2>/dev/null",{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim()||null}catch{return null}}function ct(t){try{const i=t&&t.getTime()>0?`--after="${t.toISOString()}"`:"-10",l=S(`git log ${i} --name-only --pretty=format: 2>/dev/null`,{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim();if(!l)return[];const o={};for(const c of l.split(`
1
+ import*as b from"node:fs";import*as m from"node:path";import"node:os";import{execSync as S}from"node:child_process";import{bold as V,cyan as $,gray as it,green as rt,yellow as J,red as pt}from"../ui/output.mjs";import{ampPaths as ut,readEntries as dt,appendEntry as ht}from"../amp/io.mjs";const F="inferno";function z(){return ut(process.cwd())}const gt=m.join(F,"HANDOFF.md"),Tt=m.join(F,"sessions.jsonl"),K=m.join(F,"context-state.json"),X=m.join(F,"contract.json"),q=m.join(F,"theme.json"),Q=m.join(F,"adoption_profile.json");function u(t){try{return JSON.parse(b.readFileSync(t,"utf8"))}catch{return null}}function mt(t){try{return b.readFileSync(t,"utf8")}catch{return null}}function Y(t){return t?new Date(t).toLocaleString("en-GB",{day:"2-digit",month:"short",hour:"2-digit",minute:"2-digit"}):"unknown"}function Z(t){if(t<0)return"unknown";const i=Math.floor(t/36e5),l=Math.floor(t%36e5/6e4);return i>0?`${i}h ${l}m`:`${l}m`}function tt(){return dt(process.cwd())}function et(t,i,l){if(l)return new Date(0);if(i){const o=i.match(/^(\d+)h$/i),c=i.match(/^(\d+)d$/i);if(o)return new Date(Date.now()-parseInt(o[1])*36e5);if(c)return new Date(Date.now()-parseInt(c[1])*864e5);const s=new Date(i);if(!isNaN(s.getTime()))return s}for(let o=t.length-1;o>=0;o--)if(t[o].type==="handoff"){const c=new Date(t[o].ts||0),s=new Date(Date.now()-864e5);return c>s?c:s}return new Date(Date.now()-864e5)}function yt(t){try{const i=process.platform;if(i==="win32")S("clip",{input:t});else if(i==="darwin")S("pbcopy",{input:t});else try{S("xclip -selection clipboard",{input:t})}catch{S("xsel --clipboard --input",{input:t})}return!0}catch{return!1}}function ot(){if(process.env.CURSOR_SESSION)return"Cursor";if(process.env.COPILOT_SESSION)return"GitHub Copilot";if(process.env.CLAUDE_CODE_SESSION)return"Claude Code";if(process.env.WINDSURF_SESSION)return"Windsurf";if(process.env.TERM_PROGRAM==="vscode")return"VS Code";const t=u(Q);return t?.ide?t.ide:null}function wt(){try{const t=S("git diff --stat HEAD 2>/dev/null || git diff --cached --stat 2>/dev/null",{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim();return t||S("git log --stat -1 --pretty= 2>/dev/null",{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim()||null}catch{return null}}function ct(t){try{const i=t&&t.getTime()>0?`--after="${t.toISOString()}"`:"-10",l=S(`git log ${i} --name-only --pretty=format: 2>/dev/null`,{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim();if(!l)return[];const o={};for(const c of l.split(`
2
2
  `)){const s=c.trim();s&&(o[s]=(o[s]||0)+1)}return Object.entries(o).sort((c,s)=>s[1]-c[1]).slice(0,5).map(([c,s])=>({file:c,edits:s}))}catch{return[]}}function nt(t){try{const i=t?`--after="${t.toISOString()}"`:"-5",l=S(`git log ${i} --pretty=format:"%h %s" 2>/dev/null`,{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim();return l?l.split(`
3
- `).filter(Boolean):[]}catch{return[]}}function lt(t){const i=[],l=t.filter(o=>o.type==="attempt"&&(o.result==="failed"||o.result==="partial"||!o.result));for(const o of l)t.find(s=>s.type==="attempt"&&s.result==="worked"&&new Date(s.ts)>new Date(o.ts)&&s.summary.toLowerCase().includes(o.summary.split(" ")[0].toLowerCase()))||i.push({text:o.summary,ts:o.ts,kind:"unresolved-attempt"});for(const o of t)/\b(TODO|WIP|FIXME|BLOCKED|pending)\b/i.test(o.summary)&&(i.find(c=>c.text===o.summary)||i.push({text:o.summary,ts:o.ts,kind:"flagged"}));return i.slice(0,8)}function St(t,i,l){const o=u(K)||{},c=u(X)||{},s=u(q),A=u(Q),D=tt(),a=et(D,i,l),N=D.filter(n=>new Date(n.ts||0)>a),H=D.slice(-5),I=new Date,d=I.toLocaleString("en-GB",{day:"2-digit",month:"short",year:"numeric",hour:"2-digit",minute:"2-digit"}),j=c.policyId||m.basename(process.cwd()),E=c.policyVersion||"?",O=(c.capabilities||[]).slice(0,20),x=ot(),M=a.getTime()>0?I-a:-1,k=Z(M),L=a.getTime()>0?a.getTime().toString(16).slice(-6).toUpperCase():"ALL",C=nt(a.getTime()>0?a:null),_=wt(),R=ct(a.getTime()>0?a:null),y=N.length>0?N:H,r=y.filter(n=>n.type==="gotcha"),h=y.filter(n=>n.type==="decision"),v=y.filter(n=>n.type==="attempt").filter(n=>n.result==="failed"||n.result==="partial"),G=y.filter(n=>n.type==="preference"),W=y.slice(-8),g=lt(y),st=a.getTime()===0?"all time":a.toLocaleString("en-GB",{day:"2-digit",month:"short",hour:"2-digit",minute:"2-digit"}),w=["sessions.jsonl"];(o.working||o.intent)&&w.push("context-state.json"),s&&w.push("theme.json"),c.capabilities?.length&&w.push("contract.json"),A&&w.push("adoption_profile.json"),C.length&&w.push("git log");const B=r.length,P=h.length,at=v.length;let F=Math.min(B*20,40)+Math.min(P*15,30)+Math.min(at*15,20);F=Math.min(F,100);const ft=F>=80?"A":F>=60?"B":F>=40?"C":F>=20?"D":"F",e=[`# \u{1F525} infernoflow Handoff \u2014 ${j}`,`> Generated: ${d}${t?` | Handing off to: **${t}**`:""}`,`> Session: **#${L}** \xB7 ${k} \xB7 **${N.length} entries** \xB7 Health: **${ft}** (${F}/100)`,`> Sources: ${w.join(" \xB7 ")}${x?` \xB7 IDE: ${x}`:""}`,"","---",""];if((o.working||o.intent)&&(e.push("## \u{1F3AF} Working on",""),o.working&&e.push(`**${o.working}** _(${Y(o.workingUpdated)})_`),o.intent&&e.push(`Intent: ${o.intent} _(${Y(o.intentUpdated)})_`),e.push("")),r.length&&(e.push(`## \u26A0\uFE0F STOP \u2014 Read These Before Doing Anything (${r.length} gotcha${r.length===1?"":"s"})`,""),r.forEach((n,p)=>{e.push(`${p+1}. **${n.summary}**`);const f=n.file||n.source;if(f&&/[\\/.]/.test(f)){const U=n.line?`${f}:${n.line}`:f;e.push(` \u2192 File: \`${U}\``)}}),e.push("")),h.length&&(e.push("## \u2713 Decisions In Effect \u2014 Follow These",""),h.forEach((n,p)=>{const f=n.result?` \u2192 **${n.result}**`:"";e.push(`${p+1}. ${n.summary}${f}`)}),e.push("")),v.length&&(e.push("## \u274C Already Tried \u2014 Don't Repeat",""),v.forEach((n,p)=>{const f=n.file||n.source,U=f&&/[\\/.]/.test(f)?` (\`${f}\`)`:"";e.push(`${p+1}. ${n.summary}${U} _(${Y(n.ts)})_`)}),e.push("")),R.length){e.push("## \u{1F4C1} Hot Files This Session","");for(const{file:n,edits:p}of R)e.push(`- \`${n}\` \u2014 ${p} edit${p!==1?"s":""}`);e.push("")}if(G.length){e.push("## Developer preferences","");for(const n of G)e.push(`- ${n.summary}`);e.push("")}if(C.length||_){if(e.push("## Git activity this session",""),C.length){e.push("**Commits:**");for(const n of C)e.push(`- \`${n}\``);e.push("")}_&&(e.push("**Uncommitted changes:**"),e.push("```"),e.push(_.split(`
3
+ `).filter(Boolean):[]}catch{return[]}}function lt(t){const i=[],l=t.filter(o=>o.type==="attempt"&&(o.result==="failed"||o.result==="partial"||!o.result));for(const o of l)t.find(s=>s.type==="attempt"&&s.result==="worked"&&new Date(s.ts)>new Date(o.ts)&&s.summary.toLowerCase().includes(o.summary.split(" ")[0].toLowerCase()))||i.push({text:o.summary,ts:o.ts,kind:"unresolved-attempt"});for(const o of t)/\b(TODO|WIP|FIXME|BLOCKED|pending)\b/i.test(o.summary)&&(i.find(c=>c.text===o.summary)||i.push({text:o.summary,ts:o.ts,kind:"flagged"}));return i.slice(0,8)}function St(t,i,l){const o=u(K)||{},c=u(X)||{},s=u(q),A=u(Q),D=tt(),a=et(D,i,l),N=D.filter(n=>new Date(n.ts||0)>a),H=D.slice(-5),I=new Date,d=I.toLocaleString("en-GB",{day:"2-digit",month:"short",year:"numeric",hour:"2-digit",minute:"2-digit"}),j=c.policyId||m.basename(process.cwd()),E=c.policyVersion||"?",O=(c.capabilities||[]).slice(0,20),x=ot(),M=a.getTime()>0?I.getTime()-a.getTime():-1,k=Z(M),L=a.getTime()>0?a.getTime().toString(16).slice(-6).toUpperCase():"ALL",C=nt(a.getTime()>0?a:null),_=wt(),R=ct(a.getTime()>0?a:null),y=N.length>0?N:H,r=y.filter(n=>n.type==="gotcha"),h=y.filter(n=>n.type==="decision"),v=y.filter(n=>n.type==="attempt").filter(n=>n.result==="failed"||n.result==="partial"),G=y.filter(n=>n.type==="preference"),W=y.slice(-8),g=lt(y),st=a.getTime()===0?"all time":a.toLocaleString("en-GB",{day:"2-digit",month:"short",hour:"2-digit",minute:"2-digit"}),w=["sessions.jsonl"];(o.working||o.intent)&&w.push("context-state.json"),s&&w.push("theme.json"),c.capabilities?.length&&w.push("contract.json"),A&&w.push("adoption_profile.json"),C.length&&w.push("git log");const B=r.length,P=h.length,at=v.length;let T=Math.min(B*20,40)+Math.min(P*15,30)+Math.min(at*15,20);T=Math.min(T,100);const ft=T>=80?"A":T>=60?"B":T>=40?"C":T>=20?"D":"F",e=[`# \u{1F525} infernoflow Handoff \u2014 ${j}`,`> Generated: ${d}${t?` | Handing off to: **${t}**`:""}`,`> Session: **#${L}** \xB7 ${k} \xB7 **${N.length} entries** \xB7 Health: **${ft}** (${T}/100)`,`> Sources: ${w.join(" \xB7 ")}${x?` \xB7 IDE: ${x}`:""}`,"","---",""];if((o.working||o.intent)&&(e.push("## \u{1F3AF} Working on",""),o.working&&e.push(`**${o.working}** _(${Y(o.workingUpdated)})_`),o.intent&&e.push(`Intent: ${o.intent} _(${Y(o.intentUpdated)})_`),e.push("")),r.length&&(e.push(`## \u26A0\uFE0F STOP \u2014 Read These Before Doing Anything (${r.length} gotcha${r.length===1?"":"s"})`,""),r.forEach((n,p)=>{e.push(`${p+1}. **${n.summary}**`);const f=n.file||n.source;if(f&&/[\\/.]/.test(f)){const U=n.line?`${f}:${n.line}`:f;e.push(` \u2192 File: \`${U}\``)}}),e.push("")),h.length&&(e.push("## \u2713 Decisions In Effect \u2014 Follow These",""),h.forEach((n,p)=>{const f=n.result?` \u2192 **${n.result}**`:"";e.push(`${p+1}. ${n.summary}${f}`)}),e.push("")),v.length&&(e.push("## \u274C Already Tried \u2014 Don't Repeat",""),v.forEach((n,p)=>{const f=n.file||n.source,U=f&&/[\\/.]/.test(f)?` (\`${f}\`)`:"";e.push(`${p+1}. ${n.summary}${U} _(${Y(n.ts)})_`)}),e.push("")),R.length){e.push("## \u{1F4C1} Hot Files This Session","");for(const{file:n,edits:p}of R)e.push(`- \`${n}\` \u2014 ${p} edit${p!==1?"s":""}`);e.push("")}if(G.length){e.push("## Developer preferences","");for(const n of G)e.push(`- ${n.summary}`);e.push("")}if(C.length||_){if(e.push("## Git activity this session",""),C.length){e.push("**Commits:**");for(const n of C)e.push(`- \`${n}\``);e.push("")}_&&(e.push("**Uncommitted changes:**"),e.push("```"),e.push(_.split(`
4
4
  `).slice(0,15).join(`
5
5
  `)),e.push("```"),e.push(""))}if(s){if(e.push("## Design system",""),s.fonts?.primary&&e.push(`- **Font:** ${s.fonts.primary}${s.fonts.mono?` \xB7 mono: ${s.fonts.mono}`:""}`),s.colors?.mode&&e.push(`- **Mode:** ${s.colors.mode}`),s.colors?.palette){const n=Object.entries(s.colors.palette).map(([p,f])=>`${p}=${f}`).join(" ");e.push(`- **Palette:** ${n}`)}if(s.cssVars&&Object.keys(s.cssVars).length){const n=Object.entries(s.cssVars).slice(0,6).map(([p,f])=>`${p}: ${f}`).join(" | ");e.push(`- **CSS vars:** ${n}`)}s.framework&&e.push(`- **Framework:** ${s.framework}`),e.push("","> \u26A0 Always match these exactly. Do not introduce new colors or fonts.","")}return O.length&&(e.push("## Capability contract",""),e.push(`Project: **${j}** v${E}`),e.push(`Capabilities: ${O.join(", ")}`),e.push("")),e.push("---"),e.push(`_Session #${L} \xB7 ${k} \xB7 Generated by infernoflow._`),e.join(`
6
- `)}async function Tt(t){const i=r=>t.includes(r),l=r=>{const h=t.indexOf(r);return h!==-1&&t[h+1]?t[h+1]:null},o=i("--show")||i("-s"),c=i("--copy")||i("-c"),s=i("--json"),A=i("--all"),D=l("--since"),a=l("--to")||t.find(r=>!r.startsWith("-")&&!["switch"].includes(r))||null;console.log(`
6
+ `)}async function Ft(t){const i=r=>t.includes(r),l=r=>{const h=t.indexOf(r);return h!==-1&&t[h+1]?t[h+1]:null},o=i("--show")||i("-s"),c=i("--copy")||i("-c"),s=i("--json"),A=i("--all"),D=l("--since"),a=l("--to")||t.find(r=>!r.startsWith("-")&&!["switch"].includes(r))||null;console.log(`
7
7
  `+V("\u{1F525} infernoflow \u2014 switch")),console.log(" "+"\u2500".repeat(50)+`
8
- `);const N=m.join(process.cwd(),".ai-memory");if(!b.existsSync(T)&&!b.existsSync(N)&&(console.error(pt(` \u2718 not initialized \u2014 run: infernoflow init
8
+ `);const N=m.join(process.cwd(),".ai-memory");if(!b.existsSync(F)&&!b.existsSync(N)&&(console.error(pt(` \u2718 not initialized \u2014 run: infernoflow init
9
9
  `)),process.exit(1)),o){const r=mt(gt);if(!r){console.log(J(` \u26A0 No HANDOFF.md yet \u2014 run: infernoflow switch
10
10
  `));return}console.log(r);return}const H=St(a,D,A);if(s){const r=u(K)||{},h=u(X)||{},v=u(q),G=u(Q),W=tt(),g=et(W,D,A),st=W.filter(P=>new Date(P.ts||0)>g),w=nt(g.getTime()>0?g:null),B=ot();console.log(JSON.stringify({state:r,contract:{policyId:h.policyId,policyVersion:h.policyVersion,capabilities:h.capabilities},theme:v,adoption:G,sessions:st,commits:w,ide:B,sessionStart:g.toISOString(),sessionId:g.getTime()>0?g.getTime().toString(16).slice(-6).toUpperCase():"ALL",sessionDuration:Z(g.getTime()>0?Date.now()-g.getTime():-1),generatedAt:new Date().toISOString()},null,2));return}b.writeFileSync(z().handoff,H,"utf8"),console.log(rt(" \u2714 Written \u2192 "+m.relative(process.cwd(),z().handoff)+`
11
- `));const I=tt(),d=et(I,D,A),j=I.filter(r=>new Date(r.ts||0)>d),E=u(K)||{},O=u(q),x=u(X)||{},M=nt(d.getTime()>0?d:null),k=ct(d.getTime()>0?d:null),L=ot(),C=j.length>0?j:I.slice(-5),_=lt(C),R=Z(d.getTime()>0?Date.now()-d.getTime():-1),y=d.getTime()>0?d.getTime().toString(16).slice(-6).toUpperCase():"ALL";if(console.log(" "+V("Handoff ready")),console.log(" "+"\u2500".repeat(50)),console.log(" "+it("Session #"+y+" \xB7 "+R)),E.working&&console.log(" Working on "+$(E.working)),E.intent&&console.log(" Intent "+$(E.intent)),console.log(" Memory "+j.length+" entries this session (total: "+I.length+")"),_.length&&console.log(" Open threads "+J(_.length+" unresolved")),M.length&&console.log(" Git commits "+M.length+" this session"),k.length&&console.log(" Hot files "+k.map(r=>$(r.file)).join(", ")),console.log(" Capabilities "+(x.capabilities||[]).length+" registered"),O?.fonts?.primary&&console.log(" Font "+O.fonts.primary),O?.colors?.mode&&console.log(" Color mode "+O.colors.mode),L&&console.log(" IDE "+L),a&&console.log(" Handing off \u2192 "+$(a)),console.log(),c){const r=yt(H);console.log(r?rt(" \u2714 Copied to clipboard \u2014 paste at the start of your next AI session"):J(" \u26A0 Clipboard failed \u2014 open inferno/HANDOFF.md manually"))}else console.log(" "+V("Ready to use:")),console.log(" "+$("1.")+" Open "+$("inferno/HANDOFF.md")),console.log(" "+$("2.")+" Copy all"),console.log(" "+$("3.")+" Paste at the start of your next AI session"),console.log(" "+it(" tip: use --copy to skip steps 1-2 automatically"));if(console.log(),b.existsSync(z().sessions)){const r={ts:new Date().toISOString(),agent:"infernoflow",type:"handoff",summary:a?`Handed off to ${a}`:"Handoff generated"};ht(process.cwd(),r)}}export{Tt as switchCommand};
11
+ `));const I=tt(),d=et(I,D,A),j=I.filter(r=>new Date(r.ts||0)>d),E=u(K)||{},O=u(q),x=u(X)||{},M=nt(d.getTime()>0?d:null),k=ct(d.getTime()>0?d:null),L=ot(),C=j.length>0?j:I.slice(-5),_=lt(C),R=Z(d.getTime()>0?Date.now()-d.getTime():-1),y=d.getTime()>0?d.getTime().toString(16).slice(-6).toUpperCase():"ALL";if(console.log(" "+V("Handoff ready")),console.log(" "+"\u2500".repeat(50)),console.log(" "+it("Session #"+y+" \xB7 "+R)),E.working&&console.log(" Working on "+$(E.working)),E.intent&&console.log(" Intent "+$(E.intent)),console.log(" Memory "+j.length+" entries this session (total: "+I.length+")"),_.length&&console.log(" Open threads "+J(_.length+" unresolved")),M.length&&console.log(" Git commits "+M.length+" this session"),k.length&&console.log(" Hot files "+k.map(r=>$(r.file)).join(", ")),console.log(" Capabilities "+(x.capabilities||[]).length+" registered"),O?.fonts?.primary&&console.log(" Font "+O.fonts.primary),O?.colors?.mode&&console.log(" Color mode "+O.colors.mode),L&&console.log(" IDE "+L),a&&console.log(" Handing off \u2192 "+$(a)),console.log(),c){const r=yt(H);console.log(r?rt(" \u2714 Copied to clipboard \u2014 paste at the start of your next AI session"):J(" \u26A0 Clipboard failed \u2014 open inferno/HANDOFF.md manually"))}else console.log(" "+V("Ready to use:")),console.log(" "+$("1.")+" Open "+$("inferno/HANDOFF.md")),console.log(" "+$("2.")+" Copy all"),console.log(" "+$("3.")+" Paste at the start of your next AI session"),console.log(" "+it(" tip: use --copy to skip steps 1-2 automatically"));if(console.log(),b.existsSync(z().sessions)){const r={ts:new Date().toISOString(),agent:"infernoflow",type:"handoff",summary:a?`Handed off to ${a}`:"Handoff generated"};ht(process.cwd(),r)}}export{Ft as switchCommand};
@@ -0,0 +1,41 @@
1
+ import*as s from"node:fs";import*as u from"node:path";import{ampPaths as h,projectSlug as O}from"../amp/io.mjs";import{findProjectRoot as x}from"../projectRoot.mjs";import{bold as y,cyan as a,gray as e,green as p,yellow as D,red as v}from"../ui/output.mjs";function S(o){try{return JSON.parse(s.readFileSync(u.join(o,"amp.json"),"utf8"))}catch{return null}}function j(o,t){s.mkdirSync(o,{recursive:!0}),s.writeFileSync(u.join(o,"amp.json"),JSON.stringify(t,null,2)+`
2
+ `,"utf8")}function F(o){try{return s.readFileSync(o,"utf8").split(`
3
+ `).filter(r=>r.trim().length>0).length}catch{return 0}}function N(o,t){return o?{source:"env (INFERNOFLOW_GLOBAL_DIR)",value:o}:t?{source:"amp.json (globalDir)",value:t}:{source:"default (in-project)",value:null}}function R({jsonOut:o}={}){const t=x(process.cwd()),r=h(process.cwd()),n=S(r.root)||{},l=N(process.env.INFERNOFLOW_GLOBAL_DIR,n.globalDir),i=O(t),m=s.existsSync(r.globalFile),g=m?F(r.globalFile):0,f=u.join(r.root,"global.jsonl"),c=l.value&&f!==r.globalFile&&s.existsSync(f),d=c?F(f):0;if(o){console.log(JSON.stringify({projectRoot:t,projectSlug:i,configured:!!l.value,source:l.source,configuredPath:l.value,resolvedFile:r.globalFile,exists:m,entries:g,orphan:c,orphanLines:d},null,2));return}console.log(`
4
+ `+y("\u{1F525} infernoflow sync \u2014 status")),console.log(" "+"\u2500".repeat(58)),console.log(" "+e("Project ")+a(i)),console.log(" "+e("Source ")+l.source),l.value&&console.log(" "+e("Configured path ")+a(l.value)),console.log(" "+e("Resolved file ")+a(r.globalFile)),console.log(" "+e("Status ")+(m?p(`${g} entries`):e("not yet created"))),c&&(console.log(""),console.log(" "+D("\u26A0 Orphan local file detected")),console.log(" "+e(" "+f+" ("+d+" entries)")),console.log(" "+e(" Run ")+a("infernoflow sync migrate")+e(" to merge it into the synced location."))),console.log(""),l.value||(console.log(" "+e("Tip: point at a synced folder (iCloud/Dropbox/etc.) to share personal")),console.log(" "+e(" preferences across your own machines:")),console.log(" "+a(" infernoflow sync set ~/Dropbox/infernoflow-memory")),console.log(""))}function J(o,{jsonOut:t}={}){o||(console.error(v(`
5
+ \u2718 usage: infernoflow sync set <path>
6
+ `)),process.exit(1));const r=h(process.cwd(),{forWrite:!0}),n=S(r.root)||{},l=n.globalDir||null;if(n.globalDir=o,j(r.root,n),t){console.log(JSON.stringify({ok:!0,previous:l,current:o,file:u.join(r.root,"amp.json")},null,2));return}console.log(`
7
+ `+p("\u2714 ")+"globalDir set to "+a(o)),l&&l!==o&&console.log(" "+e(" (was: "+l+")")),console.log(" "+e(" Run ")+a("infernoflow sync migrate")+e(` to move existing entries.
8
+ `))}function L({jsonOut:o}={}){const t=h(process.cwd(),{forWrite:!0}),r=S(t.root)||{},n=r.globalDir||null;if(!n){if(o){console.log(JSON.stringify({ok:!0,changed:!1},null,2));return}console.log(`
9
+ `+e(`globalDir was not set \u2014 nothing to clear.
10
+ `));return}if(delete r.globalDir,j(t.root,r),o){console.log(JSON.stringify({ok:!0,changed:!0,previous:n},null,2));return}console.log(`
11
+ `+p("\u2714 ")+"globalDir cleared "+e("(was "+n+")")),console.log(" "+e(` global.jsonl is now in-project again. Old synced file is left in place.
12
+ `))}function k({jsonOut:o,dryRun:t}={}){const r=h(process.cwd()),n=u.join(r.root,"global.jsonl"),l=r.globalFile;if(n===l){if(o){console.log(JSON.stringify({ok:!0,migrated:0,reason:"sync not configured"},null,2));return}console.log(`
13
+ `+e(`Sync not configured \u2014 nothing to migrate.
14
+ `));return}if(!s.existsSync(n)){if(o){console.log(JSON.stringify({ok:!0,migrated:0,reason:"no local global.jsonl"},null,2));return}console.log(`
15
+ `+e(`No local global.jsonl to migrate.
16
+ `));return}const i=s.readFileSync(n,"utf8").split(`
17
+ `).filter(Boolean),g=s.existsSync(l)?s.readFileSync(l,"utf8").split(`
18
+ `).filter(Boolean):[],f=new Set,c=[];for(const w of[...g,...i])try{const b=JSON.parse(w).id||w;if(f.has(b))continue;f.add(b),c.push(w)}catch{}if(t){if(o){console.log(JSON.stringify({ok:!0,dryRun:!0,wouldWrite:l,fromLocal:i.length,existingTarget:g.length,afterMerge:c.length},null,2));return}console.log(`
19
+ `+y("\u{1F525} infernoflow sync migrate")+e(" \u2014 dry run")),console.log(" "+e("From ")+a(n)+e(" ("+i.length+" entries)")),console.log(" "+e("To ")+a(l)+e(" ("+g.length+" existing)")),console.log(" "+e("After ")+p(c.length+" entries (deduped by id)")),console.log("");return}s.mkdirSync(u.dirname(l),{recursive:!0}),s.writeFileSync(l,c.join(`
20
+ `)+(c.length?`
21
+ `:""),"utf8");const d=n.replace(/\.jsonl$/,`-archive-${Date.now()}.jsonl`);if(s.renameSync(n,d),o){console.log(JSON.stringify({ok:!0,migrated:i.length,afterMerge:c.length,target:l,archivedAs:d},null,2));return}console.log(`
22
+ `+p("\u2714 ")+"Migrated "+c.length+" entries to "+a(l)),console.log(" "+e(" Local file archived \u2192 "+u.basename(d)+`
23
+ `))}async function B(o){const t=o.includes("--json"),r=o.includes("--dry-run")||o.includes("-n"),n=o.slice(1).find(i=>!i.startsWith("-"));if(!n||n==="status"||n==="--help"||n==="-h"){if(n==="--help"||n==="-h"){console.log(`
24
+ ${y("\u{1F525} infernoflow sync")} ${e("\u2014 cross-machine personal memory")}
25
+
26
+ ${y("Usage:")}
27
+ infernoflow sync Show current setup
28
+ infernoflow sync status Same as bare invocation
29
+ infernoflow sync set <path> Configure synced directory
30
+ infernoflow sync clear Remove configuration
31
+ infernoflow sync migrate [--dry-run] Move local global.jsonl into sync
32
+
33
+ ${y("Recommended:")}
34
+ point at an OS-synced folder (iCloud / Dropbox / OneDrive / Syncthing).
35
+ Zero new infrastructure; the OS handles sync.
36
+
37
+ ${a("infernoflow sync set ~/Dropbox/infernoflow-memory")}
38
+ `);return}return R({jsonOut:t})}const l=o.slice(1).filter(i=>!i.startsWith("-"));if(n==="set")return J(l[1],{jsonOut:t});if(n==="clear")return L({jsonOut:t});if(n==="migrate")return k({jsonOut:t,dryRun:r});console.error(v(`
39
+ \u2718 Unknown sync verb: ${n}
40
+ `)),console.error(e(` Run: infernoflow sync --help
41
+ `)),process.exit(1)}export{B as syncCommand};
@@ -0,0 +1,2 @@
1
+ import{execSync as h}from"node:child_process";import*as o from"node:fs";import*as n from"node:path";function l(r){return r&&r.replace(/\//g,"__").replace(/[^A-Za-z0-9_.\-]+/g,"-").replace(/^-+|-+$/g,"").toLowerCase()||"no-branch"}function y(r,t){try{return h(`git ${t}`,{cwd:r,encoding:"utf8",timeout:1500,stdio:["ignore","pipe","ignore"]}).trim()||null}catch{return null}}const f=new Map;function S(){f.clear()}function x(r){let t=n.resolve(r);for(;;){if(o.existsSync(n.join(t,".git")))return!0;const e=n.dirname(t);if(e===t)return!1;t=e}}function d(r){let t=n.resolve(r);for(;;){const e=n.join(t,".git");if(o.existsSync(e))return e;const i=n.dirname(t);if(i===t)return null;t=i}}function m(r=process.cwd()){const t=d(r);if(!t)return"no-git";let e=t;try{if(o.statSync(t).isFile()){const u=o.readFileSync(t,"utf8").trim().match(/^gitdir:\s*(.+)$/);u&&(e=n.resolve(n.dirname(t),u[1].trim()))}}catch{}let i;try{i=o.readFileSync(n.join(e,"HEAD"),"utf8").trim()}catch{return"no-branch"}const c=i.match(/^ref:\s+refs\/heads\/(.+)$/);return c?c[1]:"no-branch"}function g(r=process.cwd()){const t=d(r);if(!t)return null;let e=t;try{if(o.statSync(t).isFile()){const a=o.readFileSync(t,"utf8").trim().match(/^gitdir:\s*(.+)$/);a&&(e=n.resolve(n.dirname(t),a[1].trim()))}}catch{}try{const s=o.readFileSync(n.join(e,"refs","remotes","origin","HEAD"),"utf8").trim().match(/^ref:\s+refs\/remotes\/origin\/(.+)$/);if(s)return s[1]}catch{}let i="";try{i=o.readFileSync(n.join(e,"packed-refs"),"utf8")}catch{}for(const c of["main","master","trunk","develop","dev"])if(o.existsSync(n.join(e,"refs","heads",c))||i.includes(`refs/heads/${c}
2
+ `))return c;return null}function v(r=process.cwd()){const t=n.resolve(r),e=f.get(t);if(e)return e;const i=m(r),c=g(r),s={current:i,currentSlug:l(i),default:c,defaultSlug:c?l(c):null,isSynthetic:i==="no-git"||i==="no-branch"};return f.set(t,s),s}export{S as _resetBranchCache,v as getBranchInfo,m as getCurrentBranch,g as getDefaultBranch,x as hasGit,l as slugifyBranch};
@@ -0,0 +1 @@
1
+ import*as o from"node:fs";import*as i from"node:path";function u(e){const r=i.join(e,".ai-memory",".mcp-runtime.json");try{const t=o.readFileSync(r,"utf8"),n=JSON.parse(t);return!n||typeof n.version!="string"?null:n}catch{return null}}function l(e,r){return!e||!e.version||e.version===r||r.startsWith("0.0.0")||e.version.startsWith("0.0.0")?null:{severity:"warn",message:`Your AI tool is running an older infernoflow MCP server: MCP=${e.version} (booted ${e.bootedAt}) vs CLI=${r}. Quit and reopen Cursor / Claude Code / VS Code to pick up the new code. Until you do, amp_write calls will use the cached old wrapper.`}}function s(e,r){return l(u(e),r)}export{l as compareMcpRuntime,s as detectStaleMcpRuntime,u as readMcpRuntimeStamp};
@@ -0,0 +1 @@
1
+ import*as a from"node:fs";import*as t from"node:path";const d=["package.json","Cargo.toml","pyproject.toml","go.mod","Gemfile","composer.json","deno.json","deno.jsonc","build.gradle","build.gradle.kts","pom.xml","mix.exs"],j=[/\.sln$/i,/\.csproj$/i,/\.fsproj$/i,/\.vbproj$/i],f=new Map;function h(r){for(const n of d)if(a.existsSync(t.join(r,n)))return!0;return!1}function y(r){try{for(const n of a.readdirSync(r))for(const e of j)if(e.test(n))return!0}catch{}return!1}function x(r=process.cwd()){const n=t.resolve(r);if(f.has(n))return f.get(n);let e=n;for(;;){if(a.existsSync(t.join(e,".ai-memory"))||a.existsSync(t.join(e,"inferno")))return c(n,e);const o=t.dirname(e);if(o===e)break;e=o}for(e=n;;){if(a.existsSync(t.join(e,".git"))||h(e)||y(e))return c(n,e);const o=t.dirname(e);if(o===e)break;e=o}return c(n,n)}function c(r,n){return f.set(r,n),n}function S(){f.clear()}function g(r,n=6){const e=[],o=new Set,p=new Set(["node_modules",".git","dist","build","out","bin","obj",".next",".nuxt",".angular",".svelte-kit","coverage","vendor","target",".venv","venv","__pycache__",".pytest_cache"]);function u(s,m){if(m>n||o.has(s))return;o.add(s);let l;try{l=a.readdirSync(s,{withFileTypes:!0})}catch{return}for(const i of l)i.isDirectory()&&(p.has(i.name)||(i.name===".ai-memory"&&e.push({kind:"amp",path:t.join(s,i.name)}),i.name==="inferno"&&e.push({kind:"legacy",path:t.join(s,i.name)}),u(t.join(s,i.name),m+1)))}return u(t.resolve(r),0),e}export{S as _resetProjectRootCache,g as findAllMemoryDirs,x as findProjectRoot};
@@ -1,9 +1,10 @@
1
- import*as c from"node:fs";import*as l from"node:path";import{execSync as w}from"node:child_process";const m="<!-- infernoflow:start -->",h="<!-- infernoflow:end -->",d=[".cursorrules","CLAUDE.md",l.join(".github","copilot-instructions.md")];function y(){const e=[];return e.push("### Memory protocol \u2014 capture as you go"),e.push(""),e.push("You have the `amp_write` MCP tool. **Use it proactively** (without the user asking) whenever you detect any of these moments. Capture the lesson in one sentence so the next AI session inherits it."),e.push(""),e.push("| When you see\u2026 | Call `amp_write` with\u2026 |"),e.push("|---|---|"),e.push("| User types `!!!`, `not working`, `still broken`, `no change`, `same error`, repeated frustration | `{type: 'attempt', msg: '<one-line: what was tried and how it failed>', file: '<active file>'}` |"),e.push("| You generate a plan, numbered steps, or `Plan:` / `Approach:` block | `{type: 'note', msg: 'Plan: <one-line summary>', file: '<focus file>'}` |"),e.push("| User picks X over Y, decides an architectural approach, or you both agree on a direction | `{type: 'decision', msg: '<X> chosen over <Y> because <reason>', file: '<file>'}` |"),e.push("| Discovered non-obvious behavior, API quirk, or `aha \u2014 turns out\u2026` moment | `{type: 'gotcha', msg: '<one-line>', file: '<file>', line: <line if known>}` |"),e.push("| User says `switching` / `back to X`, or ~30 min passed without logging | `{type: 'note', msg: 'Session snapshot \u2014 worked on: <X>. Open threads: <Y>.'}` |"),e.push(""),e.push("**Do NOT log:** typos, syntax errors, obvious bugs visible in the code, anything the next AI can infer by re-reading the file."),e.push("**Do log:** anything that taught you something non-derivable from code."),e.push("**Be concise:** entries should be one sentence. Future-you will skim them."),e.join(`
2
- `)}function b(){return[m,"<!-- Auto-managed by infernoflow. Don't edit between these markers. -->","## Project memory (infernoflow)","",y(),"",'_No entries yet. They\'ll appear here as you and your AI tools log them \u2014 run `infernoflow log "..."` or call `amp_write` from any MCP-aware AI._',h].join(`
3
- `)}function g(e,u){const s=l.dirname(e);if(c.existsSync(s)||c.mkdirSync(s,{recursive:!0}),!c.existsSync(e))return c.writeFileSync(e,u+`
4
- `,"utf8"),{created:!0,updated:!1};const t=c.readFileSync(e,"utf8"),i=t.indexOf(m),r=t.indexOf(h);if(i===-1||r===-1){const a=u+`
1
+ import*as a from"node:fs";import*as p from"node:path";import{execSync as w}from"node:child_process";import{readEntries as b}from"./amp/io.mjs";const m="<!-- infernoflow:start -->",h="<!-- infernoflow:end -->",d=[".cursorrules","CLAUDE.md",p.join(".github","copilot-instructions.md")];function y(){const e=[];return e.push("### Memory protocol \u2014 capture as you go"),e.push(""),e.push("You have the `amp_write` MCP tool. **Use it proactively** (without the user asking) whenever you detect any of these moments. Capture the lesson in one sentence so the next AI session inherits it."),e.push(""),e.push("| When you see\u2026 | Call `amp_write` with\u2026 |"),e.push("|---|---|"),e.push("| User types `!!!`, `not working`, `still broken`, `no change`, `same error`, repeated frustration | `{type: 'attempt', msg: '<one-line: what was tried and how it failed>', file: '<active file>'}` |"),e.push("| You generate a plan, numbered steps, or `Plan:` / `Approach:` block | `{type: 'note', msg: 'Plan: <one-line summary>', file: '<focus file>'}` |"),e.push("| User picks X over Y, decides an architectural approach, or you both agree on a direction | `{type: 'decision', msg: '<X> chosen over <Y> because <reason>', file: '<file>'}` |"),e.push("| Discovered non-obvious behavior, API quirk, or `aha \u2014 turns out\u2026` moment | `{type: 'gotcha', msg: '<one-line>', file: '<file>', line: <line if known>}` |"),e.push("| User says `switching` / `back to X`, or ~30 min passed without logging | `{type: 'note', msg: 'Session snapshot \u2014 worked on: <X>. Open threads: <Y>.'}` |"),e.push(""),e.push("**Do NOT log:** typos, syntax errors, obvious bugs visible in the code, anything the next AI can infer by re-reading the file."),e.push("**Do log:** anything that taught you something non-derivable from code."),e.push("**Be concise:** entries should be one sentence. Future-you will skim them."),e.join(`
2
+ `)}function A(){return[m,"<!-- Auto-managed by infernoflow. Don't edit between these markers. -->","## Project memory (infernoflow)","",y(),"",'_No entries yet. They\'ll appear here as you and your AI tools log them \u2014 run `infernoflow log "..."` or call `amp_write` from any MCP-aware AI._',h].join(`
3
+ `)}function x(e){const r=e.indexOf("<!-- AMP:START -->"),o=e.indexOf("<!-- AMP:END -->");if(r===-1||o===-1||o<=r)return e;const t=e.slice(0,r).replace(/\s+$/,""),i=e.slice(o+16).replace(/^\s+/,"");return(t?t+(i?`
5
4
 
6
- `+t;return c.writeFileSync(e,a,"utf8"),{created:!1,updated:!0}}const p=t.slice(0,i),o=t.slice(r+h.length),n=p+u+o;return n===t?{created:!1,updated:!1}:(c.writeFileSync(e,n,"utf8"),{created:!1,updated:!0})}function v(e){const u=b(),s=[];for(const t of d){const i=l.join(e,t);try{const r=g(i,u);s.push({rel:t,...r})}catch(r){s.push({rel:t,error:r.message})}}return s}function S(e){const u=[l.join(e,".ai-memory","sessions.jsonl"),l.join(e,"inferno","sessions.jsonl")];for(const s of u)if(c.existsSync(s))try{return c.readFileSync(s,"utf8").split(`
7
- `).filter(Boolean).map(i=>{try{return JSON.parse(i)}catch{return null}}).filter(Boolean)}catch{}return[]}function x(e,u=10){try{return w(`git log --pretty=format:"%h%x09%ad%x09%s" --date=short -n ${u}`,{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).split(`
8
- `).filter(Boolean).map(t=>{const[i,r,p]=t.split(" ");return{hash:(i||"").slice(0,7),date:r||"",subject:p||""}})}catch{return[]}}function j(e,u=10,s=10){const t=S(e),i=x(e,s);t.sort((n,a)=>{const f=typeof n.ts=="number"?n.ts:Date.parse(n.ts||0);return(typeof a.ts=="number"?a.ts:Date.parse(a.ts||0))-f});const r=t.slice(0,u),p={gotcha:"\u26A0",decision:"\u2713",attempt:"\u2717",note:"\xB7",detection:"\u25CB",pattern:"\u25C7"},o=[];if(o.push(m),o.push("<!-- Auto-managed by infernoflow. Don't edit between these markers. -->"),o.push("## Project memory (infernoflow)"),o.push(""),o.push(y()),o.push(""),i.length>0){o.push("### Recent commits");for(const n of i)o.push(`- \`${n.hash}\` _${n.date}_ ${n.subject}`);o.push("")}if(r.length>0){o.push("### Recent memory");for(const n of r){const a=n.file?` (\`${n.file}${n.line?":"+n.line:""}\`)`:"",f=(n.msg||n.summary||"").replace(/\n/g," ");o.push(`- ${p[n.type]||"\xB7"} **${n.type||"note"}**${a}: ${f}`)}o.push("")}return t.length===0&&i.length===0&&o.push('_No entries yet. They\'ll appear here as you and your AI tools log them \u2014 run `infernoflow log "..."` or call `amp_write` from any MCP-aware AI._'),o.push(h),o.join(`
9
- `)}function A(e){const u=j(e),s=[];for(const t of d){const i=l.join(e,t);try{const r=g(i,u);s.push({rel:t,...r})}catch(r){s.push({rel:t,error:r.message})}}return s}export{A as refreshRuleFilesFromMemory,v as writeInitRuleFiles};
5
+ `:""):"")+i}function g(e,r){const o=p.dirname(e);if(a.existsSync(o)||a.mkdirSync(o,{recursive:!0}),!a.existsSync(e))return a.writeFileSync(e,r+`
6
+ `,"utf8"),{created:!0,updated:!1};let t=a.readFileSync(e,"utf8");t=x(t);const i=t.indexOf(m),c=t.indexOf(h);if(i===-1||c===-1){const u=r+`
7
+
8
+ `+t;return a.writeFileSync(e,u,"utf8"),{created:!1,updated:!0}}const l=t.slice(0,i),s=t.slice(c+h.length),n=l+r+s;return n===t?{created:!1,updated:!1}:(a.writeFileSync(e,n,"utf8"),{created:!1,updated:!0})}function D(e){const r=A(),o=[];for(const t of d){const i=p.join(e,t);try{const c=g(i,r);o.push({rel:t,...c})}catch(c){o.push({rel:t,error:c.message})}}return o}function I(e){return b(e)}function S(e,r=10){try{return w(`git log --pretty=format:"%h%x09%ad%x09%s" --date=short -n ${r}`,{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).split(`
9
+ `).filter(Boolean).map(t=>{const[i,c,l]=t.split(" ");return{hash:(i||"").slice(0,7),date:c||"",subject:l||""}})}catch{return[]}}function k(e,r=10,o=10){const t=I(e),i=S(e,o);t.sort((n,u)=>{const f=typeof n.ts=="number"?n.ts:Date.parse(n.ts||0);return(typeof u.ts=="number"?u.ts:Date.parse(u.ts||0))-f});const c=t.slice(0,r),l={gotcha:"\u26A0",decision:"\u2713",attempt:"\u2717",note:"\xB7",detection:"\u25CB",pattern:"\u25C7"},s=[];if(s.push(m),s.push("<!-- Auto-managed by infernoflow. Don't edit between these markers. -->"),s.push("## Project memory (infernoflow)"),s.push(""),s.push(y()),s.push(""),i.length>0){s.push("### Recent commits");for(const n of i)s.push(`- \`${n.hash}\` _${n.date}_ ${n.subject}`);s.push("")}if(c.length>0){s.push("### Recent memory");for(const n of c){const u=n.file?` (\`${n.file}${n.line?":"+n.line:""}\`)`:"",f=(n.msg||n.summary||"").replace(/\n/g," ");s.push(`- ${l[n.type]||"\xB7"} **${n.type||"note"}**${u}: ${f}`)}s.push("")}return t.length===0&&i.length===0&&s.push('_No entries yet. They\'ll appear here as you and your AI tools log them \u2014 run `infernoflow log "..."` or call `amp_write` from any MCP-aware AI._'),s.push(h),s.join(`
10
+ `)}function R(e){const r=k(e),o=[];for(const t of d){const i=p.join(e,t);try{const c=g(i,r);o.push({rel:t,...c})}catch(c){o.push({rel:t,error:c.message})}}return o}export{R as refreshRuleFilesFromMemory,D as writeInitRuleFiles};
@@ -1,3 +1,3 @@
1
- import*as o from"node:fs";import*as a from"node:path";const u=new Set(["--help","-h","--version","-v","commands","doctor","setup","init","uninstall"]);function p(t){const n=a.join(t,".ai-memory"),e=a.join(t,"inferno");return o.existsSync(n)?a.join(n,".last-cli-version"):o.existsSync(e)?a.join(e,".last-cli-version"):null}function d(t){try{return o.readFileSync(t,"utf8").trim()}catch{return""}}async function m(t,n){if(!t||n&&u.has(n))return;const e=process.cwd(),c=p(e);if(!c)return;const s=d(c);if(s===t)return;let f=!1,l=s==="";try{const{refreshRuleFilesFromMemory:i}=await import("./ruleFiles.mjs");i(e)}catch{}try{const{ensureGitignoreEntries:i}=await import("./commands/init.mjs");i(e,{silent:!0})}catch{}try{const{autoSetupMcp:i}=await import("./commands/setup.mjs"),r=i(e,{silent:!0});f=!!(r&&(r.mcpServer||r.claudeJson||r.cursorMcp||r.vscodeMcp||r.claudeSettings))}catch{}try{o.writeFileSync(c,t,"utf8")}catch{}if(f||l)try{const{gray:i}=await import("./ui/output.mjs"),r=s?`upgraded ${s} \u2192 ${t}`:`initialized for ${t}`;process.stderr.write(i(` infernoflow: ${r}, wired MCP servers + rule files
1
+ import*as o from"node:fs";import*as a from"node:path";const p=new Set(["--help","-h","--version","-v","commands","doctor","setup","init","uninstall"]);function u(t){const n=a.join(t,".ai-memory"),e=a.join(t,"inferno");return o.existsSync(n)?a.join(n,".last-cli-version"):o.existsSync(e)?a.join(e,".last-cli-version"):null}function d(t){try{return o.readFileSync(t,"utf8").trim()}catch{return""}}async function m(t,n){if(!t||n&&p.has(n))return;const e=process.cwd(),c=u(e);if(!c)return;const s=d(c);if(s===t)return;let l=!1,f=s==="";try{const{refreshRuleFilesFromMemory:i}=await import("./ruleFiles.mjs");i(e)}catch{}try{const{applyCleanTreePolicy:i}=await import("./cleanTree.mjs");i(e)}catch{}try{const{autoSetupMcp:i}=await import("./commands/setup.mjs"),r=i(e,{silent:!0});l=!!(r&&(r.mcpServer||r.claudeJson||r.cursorMcp||r.vscodeMcp||r.claudeSettings))}catch{}try{o.writeFileSync(c,t,"utf8")}catch{}if(l||f)try{const{gray:i}=await import("./ui/output.mjs"),r=s?`upgraded ${s} \u2192 ${t}`:`initialized for ${t}`;process.stderr.write(i(` infernoflow: ${r}, wired MCP servers + rule files
2
2
  `))}catch{process.stderr.write(` infernoflow: refreshed for v${t}
3
3
  `)}}export{m as runUpgradeBackfillIfNeeded};