infernoflow 0.44.18 → 0.44.19

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
@@ -143,7 +143,7 @@ infernoflow prune --apply --max-age-days 14 # one-off cleanup
143
143
 
144
144
  **Rotation** archives stale `note` / `attempt` / `detection` entries to `.ai-memory/archive/sessions-YYYY-MM.jsonl` — invisible to the merged read (so the AI, sidebar, `ask`, and `refresh` stop surfacing them) but still on disk if you want them back. `gotcha`, `decision`, `pattern`, and `bookmark` entries are **never auto-pruned** — that's the knowledge you logged infernoflow FOR.
145
145
 
146
- **Two-tier bodies (new in 0.44.10):** any entry can carry a rich `detail` — stored in `.ai-memory/details/<id>.md`, loaded on demand via `readDetail()`, and **never injected into rule files**. The lean index stays lean; you pay for the body only when you open it. `log --detail`, `--detail-file`, MCP `amp_write` `detail`, and the new `amp_bookmark` tool all feed it.
146
+ **Two-tier bodies:** any entry can carry a rich `detail` — stored in a single `.ai-memory/details.jsonl` (consolidated in 0.44.18; older per-entry `details/<id>.md` sidecars auto-migrate on the next write), loaded on demand via `readDetail()`, and **never injected into rule files**. The lean index stays lean; you pay for the body only when you open it. `log --detail`, `--detail-file`, MCP `amp_write` `detail`, and the new `amp_bookmark` tool all feed it.
147
147
 
148
148
  ---
149
149
 
@@ -1,20 +1,27 @@
1
- import*as s from"node:fs";import*as J from"node:os";import*as r from"node:path";import{findProjectRoot as O}from"../projectRoot.mjs";import{getBranchInfo as M}from"../git/branch.mjs";const T="1.0",_=new Set(["gotcha","decision","attempt","note","detection","pattern"]),P=new Set(["copilot","cursor","claude","windsurf","other"]);function L(e){let t=r.basename(r.resolve(e));try{const o=r.join(e,".ai-memory","amp.json");if(s.existsSync(o)){const n=JSON.parse(s.readFileSync(o,"utf8"));n&&typeof n.project=="string"&&n.project.trim()&&(t=n.project.trim())}}catch{}return t.toLowerCase().replace(/[^a-z0-9_.\-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,64)||"unnamed-project"}function W(e,t){let o=process.env.INFERNOFLOW_GLOBAL_DIR;if(!o)try{const l=JSON.parse(s.readFileSync(r.join(t,"amp.json"),"utf8"));l&&typeof l.globalDir=="string"&&l.globalDir.trim()&&(o=l.globalDir.trim())}catch{}if(!o)return r.join(t,"global.jsonl");let n=o;n.startsWith("~")&&(n=r.join(J.homedir(),n.slice(1).replace(/^[\/\\]/,""))),r.isAbsolute(n)||(n=r.resolve(e,n));const c=L(e);return r.join(n,c,"global.jsonl")}function m(e,t={}){const o=t.literal?r.resolve(e):O(e),n=r.join(o,".ai-memory"),c=r.join(o,"inferno"),l=t.forWrite||s.existsSync(n)||!s.existsSync(c),a=l?n:c,i=l,u=r.join(a,"branches"),f=M(o),h=r.join(u,`${f.currentSlug}.jsonl`),g=f.defaultSlug?r.join(u,`${f.defaultSlug}.jsonl`):null,S=Y(o,a);return{root:a,projectRoot:o,isAmp:i,sessions:r.join(a,"sessions.jsonl"),config:r.join(a,i?"amp.json":"config.json"),handoff:r.join(a,i?"handoff.md":"HANDOFF.md"),globalFile:S,branchesDir:u,currentBranchFile:h,defaultBranchFile:g,branch:f}}function Y(e,t){try{return W(e,t)}catch{return r.join(t,"global.jsonl")}}function x(e){const t=O(e),o=r.join(t,".ai-memory");return s.existsSync(o)||s.mkdirSync(o,{recursive:!0}),o}const B="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function E(){let e=Date.now(),t="";for(let n=0;n<10;n++)t=B[e%32]+t,e=Math.floor(e/32);let o="";for(let n=0;n<16;n++)o+=B[Math.floor(Math.random()*32)];return t+o}function N(e){const t={...e.meta||{}};let o=e.type||"note";_.has(o)||(t.subtype=o,o="note"),e.result&&(t.result=e.result),e.detailRef&&(t.detailRef=e.detailRef);let n;const c=e.agent;c&&P.has(c)?n=c:c&&(t.agent=c);const l=typeof e.ts=="number"?e.ts:e.ts?Date.parse(e.ts):Date.now(),a=e.confidence!=null?e.confidence:e.auto?.7:void 0,i={type:o,msg:e.summary||e.msg||"",ts:l,id:e.id||`amp_${E()}`};return e.file&&(i.file=e.file),e.line&&(i.line=e.line),e.function&&(i.function=e.function),e.tags&&e.tags.length&&(i.tags=e.tags),e.source&&(i.source=e.source),n&&(i.tool=n),e.session&&(i.session=e.session),a!=null&&(i.confidence=a),Object.keys(t).length&&(i.meta=t),i}function G(e){if(e.summary&&!e.msg)return e;const t=e.meta||{},o=t.subtype||e.type||"note",n={ts:e.ts,type:o,summary:e.msg||""};e.id&&(n.id=e.id),e.file&&(n.file=e.file),e.line&&(n.line=e.line),e.function&&(n.function=e.function),e.tags&&(n.tags=e.tags),e.source&&(n.source=e.source),e.tool&&(n.agent=e.tool),t.agent&&(n.agent=t.agent),t.result&&(n.result=t.result),t.detailRef&&(n.detailRef=t.detailRef),e.confidence!=null&&(n.confidence=e.confidence,e.confidence<1&&(n.auto=!0));const{subtype:c,agent:l,result:a,detailRef:i,...u}=t;return Object.keys(u).length&&(n.meta=u),n}function U(e){if(!e||!s.existsSync(e))return[];try{return s.readFileSync(e,"utf8").split(`
2
- `).filter(Boolean).map(t=>{try{return JSON.parse(t)}catch{return null}}).filter(Boolean).map(G)}catch{return[]}}function ne(e){const t=m(e),o=new Set,n=[],c=[t.sessions,t.globalFile,t.defaultBranchFile,t.currentBranchFile],l=[...new Set(c.filter(Boolean))];for(const a of l)for(const i of U(a)){const u=i.id||`${i.ts}|${i.summary}`;o.has(u)||(o.add(u),n.push(i))}return n.sort((a,i)=>{const u=typeof a.ts=="number"?a.ts:Date.parse(a.ts||0),f=typeof i.ts=="number"?i.ts:Date.parse(i.ts||0);return u-f})}function H(e,t){return t.target==="global"?e.globalFile:t.target==="legacy"?e.sessions:t.target==="branch"?e.currentBranchFile:t.type==="preference"?e.globalFile:e.currentBranchFile}function oe(e,t){x(e);const o=m(e,{forWrite:!0}),n=t.id||`amp_${E()}`,c={...t,id:n};if(typeof t.detail=="string"&&t.detail.trim()){const u=`details/${n}.md`,f=r.join(o.root,"details",`${n}.md`);try{s.mkdirSync(r.dirname(f),{recursive:!0}),s.writeFileSync(f,t.detail,"utf8"),c.detailRef=u}catch{}}delete c.detail;const l=H(o,c),a=N(c),i=JSON.stringify(a)+`
3
- `;if(s.mkdirSync(r.dirname(l),{recursive:!0}),s.appendFileSync(l,i,"utf8"),l!==o.sessions)try{s.mkdirSync(r.dirname(o.sessions),{recursive:!0}),s.appendFileSync(o.sessions,i,"utf8")}catch{}return a}function ie(e,t){const o=m(e);return r.join(o.root,"details",`${t}.md`)}function se(e,t){if(!t)return null;const o=t.detailRef||t.meta&&t.meta.detailRef;if(!o||typeof o!="string")return null;const n=m(e),c=r.isAbsolute(o)?o:r.join(n.root,o);try{return s.readFileSync(c,"utf8")}catch{return null}}function re(e,t){if(!t)return{removed:0,files:[]};const o=m(e),n=[o.sessions,o.globalFile,o.currentBranchFile,o.defaultBranchFile].filter(Boolean);try{if(s.existsSync(o.branchesDir))for(const i of s.readdirSync(o.branchesDir))i.endsWith(".jsonl")&&n.push(r.join(o.branchesDir,i))}catch{}const c=[...new Set(n)];let l=0;const a=[];for(const i of c){if(!s.existsSync(i))continue;let u;try{u=s.readFileSync(i,"utf8").split(`
4
- `)}catch{continue}const f=[];let h=0;for(const g of u){if(!g.trim())continue;let S;try{S=JSON.parse(g).id}catch{f.push(g);continue}if(S===t){h++,l++;continue}f.push(g)}h>0&&(s.writeFileSync(i,f.length?f.join(`
1
+ import*as i from"node:fs";import*as M from"node:os";import*as a from"node:path";import{findProjectRoot as R}from"../projectRoot.mjs";import{getBranchInfo as P}from"../git/branch.mjs";const T="1.0",L=new Set(["gotcha","decision","attempt","note","detection","pattern"]),W=new Set(["copilot","cursor","claude","windsurf","other"]);function Y(e){let t=a.basename(a.resolve(e));try{const o=a.join(e,".ai-memory","amp.json");if(i.existsSync(o)){const n=JSON.parse(i.readFileSync(o,"utf8"));n&&typeof n.project=="string"&&n.project.trim()&&(t=n.project.trim())}}catch{}return t.toLowerCase().replace(/[^a-z0-9_.\-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,64)||"unnamed-project"}function G(e,t){let o=process.env.INFERNOFLOW_GLOBAL_DIR;if(!o)try{const f=JSON.parse(i.readFileSync(a.join(t,"amp.json"),"utf8"));f&&typeof f.globalDir=="string"&&f.globalDir.trim()&&(o=f.globalDir.trim())}catch{}if(!o)return a.join(t,"global.jsonl");let n=o;n.startsWith("~")&&(n=a.join(M.homedir(),n.slice(1).replace(/^[\/\\]/,""))),a.isAbsolute(n)||(n=a.resolve(e,n));const c=Y(e);return a.join(n,c,"global.jsonl")}function m(e,t={}){const o=t.literal?a.resolve(e):R(e),n=a.join(o,".ai-memory"),c=a.join(o,"inferno"),f=t.forWrite||i.existsSync(n)||!i.existsSync(c),s=f?n:c,r=f,u=a.join(s,"branches"),l=P(o),h=a.join(u,`${l.currentSlug}.jsonl`),g=l.defaultSlug?a.join(u,`${l.defaultSlug}.jsonl`):null,S=U(o,s);return{root:s,projectRoot:o,isAmp:r,sessions:a.join(s,"sessions.jsonl"),config:a.join(s,r?"amp.json":"config.json"),handoff:a.join(s,r?"handoff.md":"HANDOFF.md"),globalFile:S,branchesDir:u,currentBranchFile:h,defaultBranchFile:g,branch:l}}function U(e,t){try{return G(e,t)}catch{return a.join(t,"global.jsonl")}}function v(e){const t=R(e),o=a.join(t,".ai-memory");return i.existsSync(o)||i.mkdirSync(o,{recursive:!0}),o}const B="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function w(){let e=Date.now(),t="";for(let n=0;n<10;n++)t=B[e%32]+t,e=Math.floor(e/32);let o="";for(let n=0;n<16;n++)o+=B[Math.floor(Math.random()*32)];return t+o}function E(e){const t={...e.meta||{}};let o=e.type||"note";L.has(o)||(t.subtype=o,o="note"),e.result&&(t.result=e.result),e.detailRef&&(t.detailRef=e.detailRef);let n;const c=e.agent;c&&W.has(c)?n=c:c&&(t.agent=c);const f=typeof e.ts=="number"?e.ts:e.ts?Date.parse(e.ts):Date.now(),s=e.confidence!=null?e.confidence:e.auto?.7:void 0,r={type:o,msg:e.summary||e.msg||"",ts:f,id:e.id||`amp_${w()}`};return e.file&&(r.file=e.file),e.line&&(r.line=e.line),e.function&&(r.function=e.function),e.tags&&e.tags.length&&(r.tags=e.tags),e.source&&(r.source=e.source),n&&(r.tool=n),e.session&&(r.session=e.session),s!=null&&(r.confidence=s),Object.keys(t).length&&(r.meta=t),r}function H(e){if(e.summary&&!e.msg)return e;const t=e.meta||{},o=t.subtype||e.type||"note",n={ts:e.ts,type:o,summary:e.msg||""};e.id&&(n.id=e.id),e.file&&(n.file=e.file),e.line&&(n.line=e.line),e.function&&(n.function=e.function),e.tags&&(n.tags=e.tags),e.source&&(n.source=e.source),e.tool&&(n.agent=e.tool),t.agent&&(n.agent=t.agent),t.result&&(n.result=t.result),t.detailRef&&(n.detailRef=t.detailRef),e.confidence!=null&&(n.confidence=e.confidence,e.confidence<1&&(n.auto=!0));const{subtype:c,agent:f,result:s,detailRef:r,...u}=t;return Object.keys(u).length&&(n.meta=u),n}function V(e){if(!e||!i.existsSync(e))return[];try{return i.readFileSync(e,"utf8").split(`
2
+ `).filter(Boolean).map(t=>{try{return JSON.parse(t)}catch{return null}}).filter(Boolean).map(H)}catch{return[]}}function ae(e){const t=m(e),o=new Set,n=[],c=[t.sessions,t.globalFile,t.defaultBranchFile,t.currentBranchFile],f=[...new Set(c.filter(Boolean))];for(const s of f)for(const r of V(s)){const u=r.id||`${r.ts}|${r.summary}`;o.has(u)||(o.add(u),n.push(r))}return n.sort((s,r)=>{const u=typeof s.ts=="number"?s.ts:Date.parse(s.ts||0),l=typeof r.ts=="number"?r.ts:Date.parse(r.ts||0);return u-l})}function z(e,t){return t.target==="global"?e.globalFile:t.target==="legacy"?e.sessions:t.target==="branch"?e.currentBranchFile:t.type==="preference"?e.globalFile:e.currentBranchFile}const J=new Set;function b(e){return a.join(e,"details.jsonl")}function q(e,t,o){const n=b(e);i.mkdirSync(a.dirname(n),{recursive:!0}),i.appendFileSync(n,JSON.stringify({id:t,body:o})+`
3
+ `,"utf8")}function K(e,t){if(!t)return null;let o;try{o=i.readFileSync(b(e),"utf8")}catch{return null}let n=null;for(const c of o.split(`
4
+ `))if(c.trim())try{const f=JSON.parse(c);f.id===t&&typeof f.body=="string"&&(n=f.body)}catch{}return n}function Q(e,t){const o=b(e);let n;try{n=i.readFileSync(o,"utf8")}catch{return!1}const c=[];let f=!1;for(const s of n.split(`
5
+ `))if(s.trim()){try{if(JSON.parse(s).id===t){f=!0;continue}}catch{}c.push(s)}return f&&i.writeFileSync(o,c.length?c.join(`
5
6
  `)+`
6
- `:"","utf8"),a.push(i))}try{const i=r.join(o.root,"details",`${t}.md`);s.existsSync(i)&&(s.unlinkSync(i),a.push(i))}catch{}return{removed:l,files:a}}const V=30,z=["note","attempt","detection"],w=new Set(["gotcha","decision","pattern"]);function q(e){const t=e&&e.config&&e.config.rotation&&typeof e.config.rotation=="object"?e.config.rotation:{};return{archiveAfterDays:Number.isInteger(t.archiveAfterDays)&&t.archiveAfterDays>0?t.archiveAfterDays:V,archivableTypes:Array.isArray(t.archivableTypes)&&t.archivableTypes.length?t.archivableTypes:z,auto:t.auto===!0}}function K(e){const t=[e.sessions,e.currentBranchFile,e.defaultBranchFile].filter(Boolean);try{if(s.existsSync(e.branchesDir))for(const o of s.readdirSync(e.branchesDir))o.endsWith(".jsonl")&&t.push(r.join(e.branchesDir,o))}catch{}return[...new Set(t)]}function Q(e,t){const o=new Date(t),n=o.getUTCFullYear(),c=String(o.getUTCMonth()+1).padStart(2,"0");return r.join(e,"archive",`sessions-${n}-${c}.jsonl`)}function ce(e,t={}){const o=m(e,{forWrite:!t.dryRun}),n=q(X(e)),c=Number.isFinite(t.maxAgeDays)?t.maxAgeDays:n.archiveAfterDays,l=t.archive!==!1,a=new Set((Array.isArray(t.types)&&t.types.length?t.types:n.archivableTypes).filter(p=>!w.has(p))),i=Date.now()-c*24*60*60*1e3,u=K(o),f={scanned:0,archived:0,kept:0,files:[],byType:{},dryRun:!!t.dryRun},h=new Map,g=new Set,S=new Set,R=new Set;for(const p of u){if(!s.existsSync(p))continue;let b;try{b=s.readFileSync(p,"utf8").split(`
7
- `)}catch{continue}const F=[];let v=0;for(const j of b){if(!j.trim())continue;let y;try{y=JSON.parse(j)}catch{F.push(j);continue}const d=y&&y.id,D=y&&typeof y.type=="string"?y.type:"note",A=typeof y.ts=="number"?y.ts:Date.parse(y.ts||0),I=Number.isFinite(A)&&A<i,$=Array.isArray(y.tags)&&y.tags.includes("bookmark"),C=a.has(D)&&!w.has(D)&&!$;if(d&&!S.has(d)&&(S.add(d),f.scanned++),d||f.scanned++,I&&C){if(v++,(!d||!g.has(d))&&(d&&g.add(d),f.archived++,f.byType[D]=(f.byType[D]||0)+1,l)){const k=Q(o.root,A);h.has(k)||h.set(k,[]),h.get(k).push(j)}}else d&&!R.has(d)&&(R.add(d),f.kept++),d||f.kept++,F.push(j)}v>0&&!t.dryRun?(s.writeFileSync(p,F.length?F.join(`
7
+ `:"","utf8"),f}function X(e){const t=a.join(e,"details");if(!i.existsSync(t))return 0;let o;try{o=i.readdirSync(t).filter(s=>s.endsWith(".md"))}catch{return 0}if(!o.length)return 0;const n=new Set;try{const s=i.readFileSync(b(e),"utf8");for(const r of s.split(`
8
+ `))if(r.trim())try{n.add(JSON.parse(r).id)}catch{}}catch{}let c="",f=0;for(const s of o){const r=s.replace(/\.md$/,"");if(n.has(r))continue;let u;try{u=i.readFileSync(a.join(t,s),"utf8")}catch{continue}c+=JSON.stringify({id:r,body:u})+`
9
+ `,f++}if(c)try{i.appendFileSync(b(e),c,"utf8")}catch{return 0}for(const s of o)try{i.unlinkSync(a.join(t,s))}catch{}try{i.rmdirSync(t)}catch{}return f}function fe(e,t){v(e);const o=m(e,{forWrite:!0});if(!J.has(o.root)){J.add(o.root);try{X(o.root)}catch{}}const n=t.id||`amp_${w()}`,c={...t,id:n};if(typeof t.detail=="string"&&t.detail.trim())try{q(o.root,n,t.detail),c.detailRef="details.jsonl"}catch{}delete c.detail;const f=z(o,c),s=E(c),r=JSON.stringify(s)+`
10
+ `;if(i.mkdirSync(a.dirname(f),{recursive:!0}),i.appendFileSync(f,r,"utf8"),f!==o.sessions)try{i.mkdirSync(a.dirname(o.sessions),{recursive:!0}),i.appendFileSync(o.sessions,r,"utf8")}catch{}return s}function le(e,t){const o=m(e);return a.join(o.root,"details",`${t}.md`)}function ue(e,t){if(!t)return null;const o=t.detailRef||t.meta&&t.meta.detailRef;if(!o)return null;const n=m(e),c=t.id||t.meta&&t.meta.id,f=K(n.root,c);if(f!=null)return f;if(typeof o=="string"&&o!=="details.jsonl"){const s=a.isAbsolute(o)?o:a.join(n.root,o);try{return i.readFileSync(s,"utf8")}catch{return null}}return null}function de(e,t){if(!t)return{removed:0,files:[]};const o=m(e),n=[o.sessions,o.globalFile,o.currentBranchFile,o.defaultBranchFile].filter(Boolean);try{if(i.existsSync(o.branchesDir))for(const r of i.readdirSync(o.branchesDir))r.endsWith(".jsonl")&&n.push(a.join(o.branchesDir,r))}catch{}const c=[...new Set(n)];let f=0;const s=[];for(const r of c){if(!i.existsSync(r))continue;let u;try{u=i.readFileSync(r,"utf8").split(`
11
+ `)}catch{continue}const l=[];let h=0;for(const g of u){if(!g.trim())continue;let S;try{S=JSON.parse(g).id}catch{l.push(g);continue}if(S===t){h++,f++;continue}l.push(g)}h>0&&(i.writeFileSync(r,l.length?l.join(`
8
12
  `)+`
9
- `:"","utf8"),f.files.push(p)):v>0&&f.files.push(p)}if(!t.dryRun&&l&&h.size>0)for(const[p,b]of h)try{s.mkdirSync(r.dirname(p),{recursive:!0}),s.appendFileSync(p,b.join(`
13
+ `:"","utf8"),s.push(r))}try{Q(o.root,t)}catch{}try{const r=a.join(o.root,"details",`${t}.md`);i.existsSync(r)&&(i.unlinkSync(r),s.push(r))}catch{}return{removed:f,files:s}}const Z=30,ee=["note","attempt","detection"],I=new Set(["gotcha","decision","pattern"]);function te(e){const t=e&&e.config&&e.config.rotation&&typeof e.config.rotation=="object"?e.config.rotation:{};return{archiveAfterDays:Number.isInteger(t.archiveAfterDays)&&t.archiveAfterDays>0?t.archiveAfterDays:Z,archivableTypes:Array.isArray(t.archivableTypes)&&t.archivableTypes.length?t.archivableTypes:ee,auto:t.auto===!0}}function ne(e){const t=[e.sessions,e.currentBranchFile,e.defaultBranchFile].filter(Boolean);try{if(i.existsSync(e.branchesDir))for(const o of i.readdirSync(e.branchesDir))o.endsWith(".jsonl")&&t.push(a.join(e.branchesDir,o))}catch{}return[...new Set(t)]}function oe(e,t){const o=new Date(t),n=o.getUTCFullYear(),c=String(o.getUTCMonth()+1).padStart(2,"0");return a.join(e,"archive",`sessions-${n}-${c}.jsonl`)}function ye(e,t={}){const o=m(e,{forWrite:!t.dryRun}),n=te(ie(e)),c=Number.isFinite(t.maxAgeDays)?t.maxAgeDays:n.archiveAfterDays,f=t.archive!==!1,s=new Set((Array.isArray(t.types)&&t.types.length?t.types:n.archivableTypes).filter(p=>!I.has(p))),r=Date.now()-c*24*60*60*1e3,u=ne(o),l={scanned:0,archived:0,kept:0,files:[],byType:{},dryRun:!!t.dryRun},h=new Map,g=new Set,S=new Set,N=new Set;for(const p of u){if(!i.existsSync(p))continue;let F;try{F=i.readFileSync(p,"utf8").split(`
14
+ `)}catch{continue}const D=[];let A=0;for(const j of F){if(!j.trim())continue;let y;try{y=JSON.parse(j)}catch{D.push(j);continue}const d=y&&y.id,x=y&&typeof y.type=="string"?y.type:"note",O=typeof y.ts=="number"?y.ts:Date.parse(y.ts||0),C=Number.isFinite(O)&&O<r,_=Array.isArray(y.tags)&&y.tags.includes("bookmark"),$=s.has(x)&&!I.has(x)&&!_;if(d&&!S.has(d)&&(S.add(d),l.scanned++),d||l.scanned++,C&&$){if(A++,(!d||!g.has(d))&&(d&&g.add(d),l.archived++,l.byType[x]=(l.byType[x]||0)+1,f)){const k=oe(o.root,O);h.has(k)||h.set(k,[]),h.get(k).push(j)}}else d&&!N.has(d)&&(N.add(d),l.kept++),d||l.kept++,D.push(j)}A>0&&!t.dryRun?(i.writeFileSync(p,D.length?D.join(`
10
15
  `)+`
11
- `,"utf8")}catch{}return f}function X(e){const{config:t}=m(e);try{return JSON.parse(s.readFileSync(t,"utf8"))}catch{return null}}function ae(e,t={}){x(e);const{config:o}=m(e,{forWrite:!0});if(s.existsSync(o))return!1;const n={amp:T,project:t.project||r.basename(e),stack:t.stack||{},config:{autoCapture:!0,maxEntries:1e3,rotationStrategy:"archive",inject:["all"],injection:{maxEntries:4,maxCommits:5,maxEntryChars:200,protocolStyle:"compact"},rotation:{archiveAfterDays:30,archivableTypes:["note","attempt","detection"],auto:!1},...t.config||{}}};return s.writeFileSync(o,JSON.stringify(n,null,2)+`
12
- `,"utf8"),!0}function fe(e,t={}){x(e);const{config:o}=m(e,{forWrite:!0});let n={};try{n=JSON.parse(s.readFileSync(o,"utf8"))}catch{n={}}(!n||typeof n!="object")&&(n={}),n.amp||(n.amp=T),n.project||(n.project=r.basename(e)),(!n.config||typeof n.config!="object")&&(n.config={});const c=n.config.injection&&typeof n.config.injection=="object"?n.config.injection:{};return n.config.injection={...c,...t},s.writeFileSync(o,JSON.stringify(n,null,2)+`
13
- `,"utf8"),n.config.injection}function le(e){const t=r.join(e,"inferno"),o=r.join(t,"sessions.jsonl");if(!s.existsSync(o))return{migrated:0,reason:"no legacy sessions.jsonl"};const n=r.join(e,".ai-memory"),c=r.join(n,"sessions.jsonl");if(s.existsSync(c))return{migrated:0,reason:".ai-memory/sessions.jsonl already exists"};x(e);const l=s.readFileSync(o,"utf8").split(`
14
- `).filter(Boolean);let a=0;for(const i of l)try{const u=JSON.parse(i),f=N(u);s.appendFileSync(c,JSON.stringify(f)+`
15
- `,"utf8"),a++}catch{}return s.writeFileSync(r.join(n,"MIGRATED.md"),`# Migrated from inferno/
16
+ `:"","utf8"),l.files.push(p)):A>0&&l.files.push(p)}if(!t.dryRun&&f&&h.size>0)for(const[p,F]of h)try{i.mkdirSync(a.dirname(p),{recursive:!0}),i.appendFileSync(p,F.join(`
17
+ `)+`
18
+ `,"utf8")}catch{}return l}function ie(e){const{config:t}=m(e);try{return JSON.parse(i.readFileSync(t,"utf8"))}catch{return null}}function he(e,t={}){v(e);const{config:o}=m(e,{forWrite:!0});if(i.existsSync(o))return!1;const n={amp:T,project:t.project||a.basename(e),stack:t.stack||{},config:{autoCapture:!0,maxEntries:1e3,rotationStrategy:"archive",inject:["all"],injection:{maxEntries:4,maxCommits:5,maxEntryChars:200,protocolStyle:"compact"},rotation:{archiveAfterDays:30,archivableTypes:["note","attempt","detection"],auto:!1},...t.config||{}}};return i.writeFileSync(o,JSON.stringify(n,null,2)+`
19
+ `,"utf8"),!0}function pe(e,t={}){v(e);const{config:o}=m(e,{forWrite:!0});let n={};try{n=JSON.parse(i.readFileSync(o,"utf8"))}catch{n={}}(!n||typeof n!="object")&&(n={}),n.amp||(n.amp=T),n.project||(n.project=a.basename(e)),(!n.config||typeof n.config!="object")&&(n.config={});const c=n.config.injection&&typeof n.config.injection=="object"?n.config.injection:{};return n.config.injection={...c,...t},i.writeFileSync(o,JSON.stringify(n,null,2)+`
20
+ `,"utf8"),n.config.injection}function ge(e){const t=a.join(e,"inferno"),o=a.join(t,"sessions.jsonl");if(!i.existsSync(o))return{migrated:0,reason:"no legacy sessions.jsonl"};const n=a.join(e,".ai-memory"),c=a.join(n,"sessions.jsonl");if(i.existsSync(c))return{migrated:0,reason:".ai-memory/sessions.jsonl already exists"};v(e);const f=i.readFileSync(o,"utf8").split(`
21
+ `).filter(Boolean);let s=0;for(const r of f)try{const u=JSON.parse(r),l=E(u);i.appendFileSync(c,JSON.stringify(l)+`
22
+ `,"utf8"),s++}catch{}return i.writeFileSync(a.join(n,"MIGRATED.md"),`# Migrated from inferno/
16
23
 
17
- Copied ${a} entries from inferno/sessions.jsonl on ${new Date().toISOString()}.
24
+ Copied ${s} entries from inferno/sessions.jsonl on ${new Date().toISOString()}.
18
25
 
19
26
  The original inferno/sessions.jsonl is untouched. You can delete it once you're confident the new layout works.
20
- `,"utf8"),{migrated:a,reason:"ok"}}export{T as AMP_VERSION,m as ampPaths,oe as appendEntry,re as deleteEntry,ie as detailPath,x as ensureAmpDir,G as fromAmp,E as generateULID,le as migrateLegacy,L as projectSlug,ce as pruneEntries,X as readConfig,se as readDetail,ne as readEntries,W as resolveGlobalFile,q as resolveRotationSettings,N as toAmp,fe as updateInjectionConfig,ae as writeDefaultConfig};
27
+ `,"utf8"),{migrated:s,reason:"ok"}}export{T as AMP_VERSION,m as ampPaths,fe as appendEntry,de as deleteEntry,le as detailPath,v as ensureAmpDir,H as fromAmp,w as generateULID,ge as migrateLegacy,Y as projectSlug,ye as pruneEntries,ie as readConfig,ue as readDetail,ae as readEntries,G as resolveGlobalFile,te as resolveRotationSettings,E as toAmp,pe as updateInjectionConfig,he as writeDefaultConfig};
@@ -1,11 +1,11 @@
1
- import*as n from"node:fs";import*as i from"node:path";import*as H from"node:readline";import{fileURLToPath as ye}from"node:url";import{header as ge,ok as w,warn as T,info as z,done as V,nextSteps as he,bold as J,cyan as f,yellow as G,gray as v}from"../ui/output.mjs";import{discoverProjectSignals as M,reviewCapabilitiesInteractive as we,writeAdoptionBaseline as ke,buildAdoptionReport as je,summarizeCapabilities as Se,buildSignalsReport as ve}from"./adopt.mjs";import{installCursorHooksArtifacts as be}from"../cursorHooksInstall.mjs";import{ensureAmpDir as Ce,appendEntry as X,writeDefaultConfig as xe}from"../amp/io.mjs";import{applyCleanTreePolicy as B}from"../cleanTree.mjs";import{installVsCodeCopilotHooksArtifacts as Oe}from"../vsCodeCopilotHooksInstall.mjs";import{writeInitRuleFiles as Z}from"../ruleFiles.mjs";import{autoSetupMcp as Q}from"./setup.mjs";const Ie=i.dirname(ye(import.meta.url));function W(){return i.resolve(Ie,"../../templates")}function L(e,o,t=""){return new Promise(s=>{const r=t?v(` (${t})`):"";e.question(` ${o}${r}: `,l=>{s(l.trim()||t)})})}function Y(e,...o){for(const t of o){const s=e.indexOf(t);if(s!==-1&&e[s+1]&&!e[s+1].startsWith("-"))return e[s+1]}return null}function U(e,o,t,s=!1){return n.existsSync(o)&&!t?(s||T("Skipped (exists): "+i.relative(process.cwd(),o)),!1):(n.mkdirSync(i.dirname(o),{recursive:!0}),n.copyFileSync(e,o),s||w("Created: "+f(i.relative(process.cwd(),o))),!0)}function Ae(e,o,t){n.mkdirSync(o,{recursive:!0});for(const s of n.readdirSync(e,{withFileTypes:!0})){const r=i.join(e,s.name),l=i.join(o,s.name);s.isDirectory()?Ae(r,l,t):U(r,l,t)}}function ee(e,o){const t=i.join(W(),"skills");if(!n.existsSync(t))return 0;let s=0;const r=(l,d)=>{n.mkdirSync(d,{recursive:!0});for(const y of n.readdirSync(l,{withFileTypes:!0})){const a=i.join(l,y.name),u=i.join(d,y.name);if(y.isDirectory()){r(a,u);continue}n.existsSync(u)&&!o||(n.mkdirSync(i.dirname(u),{recursive:!0}),n.copyFileSync(a,u),s++,w("Created: "+f(i.relative(e,u))))}};return r(t,i.join(e,".claude","skills")),s}function oe(e,o){const t=i.join(W(),"agents");if(!n.existsSync(t))return 0;let s=0;const r=(l,d)=>{n.mkdirSync(d,{recursive:!0});for(const y of n.readdirSync(l,{withFileTypes:!0})){const a=i.join(l,y.name),u=i.join(d,y.name);if(y.isDirectory()){r(a,u);continue}n.existsSync(u)&&!o||(n.mkdirSync(i.dirname(u),{recursive:!0}),n.copyFileSync(a,u),s++,w("Created: "+f(i.relative(e,u))))}};return r(t,i.join(e,".claude","agents")),s}function Te(e,o=!1){const t=i.join(e,"package.json");if(!n.existsSync(t))return;const s=JSON.parse(n.readFileSync(t,"utf8"));s.scripts=s.scripts||{};let r=!1;const l={"inferno:check":"infernoflow check","inferno:status":"infernoflow status","inferno:gate":"infernoflow doc-gate","inferno:impact":"infernoflow pr-impact --json","inferno:sync":"infernoflow sync --auto --json","inferno:run":'infernoflow run "sync check" --provider auto --json',"inferno:hooks":"node scripts/inferno-install-hooks.mjs"};for(const[d,y]of Object.entries(l))s.scripts[d]||(s.scripts[d]=y,r=!0);r&&(n.writeFileSync(t,JSON.stringify(s,null,2)+`
2
- `,"utf8"),o||w("Updated "+f("package.json")+" scripts"))}const ne="# --- infernoflow (developer-local AI memory; do not commit) ---",Ee="# --- /infernoflow ---",te=[".ai-memory/",".cursorrules","CLAUDE.md",".github/copilot-instructions.md"];function oo(e,{silent:o=!1}={}){const t=i.join(e,".gitignore");let s="";if(n.existsSync(t)&&(s=n.readFileSync(t,"utf8")),s.includes(ne))return!1;const r=["",ne,"# Memory is per-developer, not per-branch. These files travel with you, not with git.",...te,Ee,""].join(`
1
+ import*as n from"node:fs";import*as i from"node:path";import*as H from"node:readline";import{fileURLToPath as ge}from"node:url";import{header as he,ok as k,warn as T,info as z,done as X,nextSteps as we,bold as J,cyan as p,yellow as F,gray as j}from"../ui/output.mjs";import{discoverProjectSignals as M,reviewCapabilitiesInteractive as ke,writeAdoptionBaseline as je,buildAdoptionReport as Se,summarizeCapabilities as ve,buildSignalsReport as Ce}from"./adopt.mjs";import{installCursorHooksArtifacts as be}from"../cursorHooksInstall.mjs";import{ensureAmpDir as xe,appendEntry as B,writeDefaultConfig as Oe}from"../amp/io.mjs";import{applyCleanTreePolicy as Z}from"../cleanTree.mjs";import{installVsCodeCopilotHooksArtifacts as Ie}from"../vsCodeCopilotHooksInstall.mjs";import{writeInitRuleFiles as Q}from"../ruleFiles.mjs";import{autoSetupMcp as ee}from"./setup.mjs";const Ae=i.dirname(ge(import.meta.url));function Y(){return i.resolve(Ae,"../../templates")}function L(e,o,t=""){return new Promise(s=>{const r=t?j(` (${t})`):"";e.question(` ${o}${r}: `,l=>{s(l.trim()||t)})})}function q(e,...o){for(const t of o){const s=e.indexOf(t);if(s!==-1&&e[s+1]&&!e[s+1].startsWith("-"))return e[s+1]}return null}function U(e,o,t,s=!1){return n.existsSync(o)&&!t?(s||T("Skipped (exists): "+i.relative(process.cwd(),o)),!1):(n.mkdirSync(i.dirname(o),{recursive:!0}),n.copyFileSync(e,o),s||k("Created: "+p(i.relative(process.cwd(),o))),!0)}function Te(e,o,t){n.mkdirSync(o,{recursive:!0});for(const s of n.readdirSync(e,{withFileTypes:!0})){const r=i.join(e,s.name),l=i.join(o,s.name);s.isDirectory()?Te(r,l,t):U(r,l,t)}}function oe(e,o){const t=i.join(Y(),"skills");if(!n.existsSync(t))return 0;let s=0;const r=(l,d)=>{n.mkdirSync(d,{recursive:!0});for(const g of n.readdirSync(l,{withFileTypes:!0})){const a=i.join(l,g.name),u=i.join(d,g.name);if(g.isDirectory()){r(a,u);continue}n.existsSync(u)&&!o||(n.mkdirSync(i.dirname(u),{recursive:!0}),n.copyFileSync(a,u),s++,k("Created: "+p(i.relative(e,u))))}};return r(t,i.join(e,".claude","skills")),s}function ne(e,o){const t=i.join(Y(),"agents");if(!n.existsSync(t))return 0;let s=0;const r=(l,d)=>{n.mkdirSync(d,{recursive:!0});for(const g of n.readdirSync(l,{withFileTypes:!0})){const a=i.join(l,g.name),u=i.join(d,g.name);if(g.isDirectory()){r(a,u);continue}n.existsSync(u)&&!o||(n.mkdirSync(i.dirname(u),{recursive:!0}),n.copyFileSync(a,u),s++,k("Created: "+p(i.relative(e,u))))}};return r(t,i.join(e,".claude","agents")),s}function te(){console.log(""),console.log(" "+F("\u21BB Restart Claude Code / Cursor")+j(" to activate the infernoflow skill + memory-keeper agent.")),console.log(" "+j("Editors only load newly-added .claude/ skills & agents on restart. Verify with ")+p("/skills")+j(" and ")+p("@agent-")+j("."))}function Ee(e,o=!1){const t=i.join(e,"package.json");if(!n.existsSync(t))return;const s=JSON.parse(n.readFileSync(t,"utf8"));s.scripts=s.scripts||{};let r=!1;const l={"inferno:check":"infernoflow check","inferno:status":"infernoflow status","inferno:gate":"infernoflow doc-gate","inferno:impact":"infernoflow pr-impact --json","inferno:sync":"infernoflow sync --auto --json","inferno:run":'infernoflow run "sync check" --provider auto --json',"inferno:hooks":"node scripts/inferno-install-hooks.mjs"};for(const[d,g]of Object.entries(l))s.scripts[d]||(s.scripts[d]=g,r=!0);r&&(n.writeFileSync(t,JSON.stringify(s,null,2)+`
2
+ `,"utf8"),o||k("Updated "+p("package.json")+" scripts"))}const ie="# --- infernoflow (developer-local AI memory; do not commit) ---",Pe="# --- /infernoflow ---",se=[".ai-memory/",".cursorrules","CLAUDE.md",".github/copilot-instructions.md"];function no(e,{silent:o=!1}={}){const t=i.join(e,".gitignore");let s="";if(n.existsSync(t)&&(s=n.readFileSync(t,"utf8")),s.includes(ie))return!1;const r=["",ie,"# Memory is per-developer, not per-branch. These files travel with you, not with git.",...se,Pe,""].join(`
3
3
  `),l=s.length===0||s.endsWith(`
4
4
  `)?"":`
5
- `;if(n.writeFileSync(t,s+l+r,"utf8"),!o){w("Updated "+f(".gitignore")+v(" \u2014 added entries:"));for(const d of te)console.log(v(" + ")+f(d));console.log(v(" Memory + AI rule files are now per-developer (won't move with branches).")),console.log(v(" To commit them instead: delete the `")+f("# --- infernoflow ---")+v("` block in .gitignore."))}return!0}function K(e){const o=i.join(e,"package.json");if(n.existsSync(o))try{const t=JSON.parse(n.readFileSync(o,"utf8"));if(t.name)return t.name.replace(/[^a-z0-9_-]/gi,"_")}catch{}return i.basename(e)}function Pe(e,o,t){const s={policyId:o,policyVersion:1,capabilities:t,rules:{docsRequiredOnCapabilityChange:!0,requireScenarioForEachCapability:!0,requireChangelogOnCapabilityChange:!0}};n.writeFileSync(e,JSON.stringify(s,null,2)+`
6
- `)}function Fe(e,o){const t={schemaVersion:1,capabilities:o.map(s=>({id:s,title:s.replace(/([A-Z])/g," $1").trim(),since:"0.1.0"}))};n.writeFileSync(e,JSON.stringify(t,null,2)+`
7
- `)}function Ne(e,o){n.mkdirSync(e,{recursive:!0});const t={scenarioId:"happy_path",description:"Basic happy-path flow covering all capabilities",capabilitiesCovered:o,steps:o.map(s=>({action:s,expect:`${s} works as expected`}))};n.writeFileSync(i.join(e,"happy_path.json"),JSON.stringify(t,null,2)+`
8
- `)}function ie(e,o){const t=`# Changelog \u2014 ${o}
5
+ `;if(n.writeFileSync(t,s+l+r,"utf8"),!o){k("Updated "+p(".gitignore")+j(" \u2014 added entries:"));for(const d of se)console.log(j(" + ")+p(d));console.log(j(" Memory + AI rule files are now per-developer (won't move with branches).")),console.log(j(" To commit them instead: delete the `")+p("# --- infernoflow ---")+j("` block in .gitignore."))}return!0}function K(e){const o=i.join(e,"package.json");if(n.existsSync(o))try{const t=JSON.parse(n.readFileSync(o,"utf8"));if(t.name)return t.name.replace(/[^a-z0-9_-]/gi,"_")}catch{}return i.basename(e)}function Fe(e,o,t){const s={policyId:o,policyVersion:1,capabilities:t,rules:{docsRequiredOnCapabilityChange:!0,requireScenarioForEachCapability:!0,requireChangelogOnCapabilityChange:!0}};n.writeFileSync(e,JSON.stringify(s,null,2)+`
6
+ `)}function Ne(e,o){const t={schemaVersion:1,capabilities:o.map(s=>({id:s,title:s.replace(/([A-Z])/g," $1").trim(),since:"0.1.0"}))};n.writeFileSync(e,JSON.stringify(t,null,2)+`
7
+ `)}function Re(e,o){n.mkdirSync(e,{recursive:!0});const t={scenarioId:"happy_path",description:"Basic happy-path flow covering all capabilities",capabilitiesCovered:o,steps:o.map(s=>({action:s,expect:`${s} works as expected`}))};n.writeFileSync(i.join(e,"happy_path.json"),JSON.stringify(t,null,2)+`
8
+ `)}function re(e,o){const t=`# Changelog \u2014 ${o}
9
9
 
10
10
  ## Unreleased
11
11
 
@@ -14,41 +14,41 @@ import*as n from"node:fs";import*as i from"node:path";import*as H from"node:read
14
14
  ## 0.1.0 \u2014 Initial release
15
15
 
16
16
  - Project initialized with infernoflow
17
- `;n.writeFileSync(e,t)}async function Re(e,o){const{bold:t,cyan:s,gray:r,green:l,yellow:d,red:y}=await import("../ui/output.mjs");console.log(`
17
+ `;n.writeFileSync(e,t)}async function _e(e,o){const{bold:t,cyan:s,gray:r,green:l,yellow:d,red:g}=await import("../ui/output.mjs");console.log(`
18
18
  `+t("\u{1F525} infernoflow init --lite")),console.log(" "+"\u2500".repeat(50)+`
19
19
  `),console.log(r(" Lite mode: 3 files, no scripts, no workflows, no hooks.")),console.log(r(" Use `infernoflow upgrade` later to expand to the full setup.\n"));const a=i.join(e,"inferno");n.existsSync(a)&&!o&&(console.log(d(` \u26A0 inferno/ already exists. Use --force to overwrite.
20
- `)),process.exit(0)),n.mkdirSync(a,{recursive:!0});const u=K(e);let S="";if(!process.argv.includes("--yes")&&!process.argv.includes("-y")&&process.stdin.isTTY){const E=H.createInterface({input:process.stdin,output:process.stdout});S=await new Promise(O=>{E.question(r(" What does this project do? (one line, Enter to skip): "),I=>{E.close(),O(I.trim())})})}const x={policyId:u,policyVersion:1,lite:!0,capabilities:[],intent:S||void 0};n.writeFileSync(i.join(a,"contract.json"),JSON.stringify(x,null,2)+`
20
+ `)),process.exit(0)),n.mkdirSync(a,{recursive:!0});const u=K(e);let v="";if(!process.argv.includes("--yes")&&!process.argv.includes("-y")&&process.stdin.isTTY){const E=H.createInterface({input:process.stdin,output:process.stdout});v=await new Promise(x=>{E.question(r(" What does this project do? (one line, Enter to skip): "),O=>{E.close(),x(O.trim())})})}const b={policyId:u,policyVersion:1,lite:!0,capabilities:[],intent:v||void 0};n.writeFileSync(i.join(a,"contract.json"),JSON.stringify(b,null,2)+`
21
21
  `),n.writeFileSync(i.join(a,"capabilities.json"),JSON.stringify([],null,2)+`
22
- `),n.writeFileSync(i.join(a,"sessions.jsonl"),"","utf8"),n.writeFileSync(i.join(a,".lite"),"1","utf8"),console.log(l(" \u2714 Created inferno/contract.json")),console.log(l(" \u2714 Created inferno/capabilities.json")),console.log(l(" \u2714 Created inferno/sessions.jsonl")),console.log(),console.log(" "+t("Ready. Start using it:")),console.log(" "+s("infernoflow log")+r(` "what you're building" --type note`)),console.log(" "+s("infernoflow context")+r(" \u2014 generate AI context to paste")),console.log(" "+s("infernoflow init --mode full")+r(" \u2014 expand to full setup when you need it")),console.log()}const _e=/^(?:node|npm|npx|yarn|pnpm|bun|git|cd|mkdir|rm|ls|cat|echo|type|dir|copy|del|move|python|python3|pip|go|cargo|java|gradle|mvn|docker|kubectl|curl|wget|ssh|scp|chmod|chown|sudo|brew|apt|yum)\b/i,$e=/\s(?:&&|\|\||>>|>|<<|<|\|)\s/,De=/(?:^|\s)[A-Za-z]:\\|\.\.[\\\/]|[\\\/]bin[\\\/]/;function Ge(e){if(!e)return{kind:"empty"};const o=e.replace(/^\s*[>$#]\s+/,"").trim();return o?/[\r\n]/.test(o)?{kind:"multiline",value:o}:_e.test(o)?{kind:"command",value:o}:$e.test(o)?{kind:"command",value:o}:De.test(o)?{kind:"command",value:o}:o.length<3?{kind:"tooShort",value:o}:{kind:"ok",value:o}:{kind:"empty"}}async function Le({yes:e}){if(e||!process.stdin.isTTY)return"";const{gray:o,yellow:t,cyan:s}=await import("../ui/output.mjs"),r=" "+o(`What should the next AI agent know about this project?
23
- > `),l=d=>new Promise(y=>{const a=H.createInterface({input:process.stdin,output:process.stdout});let u=!1;a.on("SIGINT",()=>{u=!0,a.close(),y(null)}),a.on("close",()=>{u&&y(null)}),a.question(d,S=>{a.close(),y(S)})});for(let d=0;d<2;d++){const y=await l(d===0?r:" "+o("> "));if(y===null)return console.log(),"";const a=Ge(y);if(a.kind==="ok")return a.value;if(a.kind==="empty")return"";if(a.kind==="command"){console.log(" "+t("\u26A0")+" That looks like a shell command, not a memory."),console.log(" "+o(" Try a short note like: ")+s('"API returns 202 not 200 on async upload"'));continue}if(a.kind==="multiline"){console.log(" "+t("\u26A0")+" Multi-line paste detected \u2014 log a single gotcha at a time."),console.log(" "+o(" Try one short sentence:"));continue}if(a.kind==="tooShort"){console.log(" "+t("\u26A0")+" Too short to be useful as a memory. Skip with Enter, or try again:");continue}}return""}async function He(e,o,t){const{bold:s,cyan:r,gray:l,green:d,yellow:y}=await import("../ui/output.mjs"),a=i.join(e,".ai-memory"),u=i.join(e,"inferno"),S=i.join(a,"sessions.jsonl"),x=n.existsSync(S),E=n.existsSync(i.join(u,"sessions.jsonl"));if((x||E)&&!o){const k=n.existsSync(a)?".ai-memory/":"inferno/ (legacy)";console.log(`
22
+ `),n.writeFileSync(i.join(a,"sessions.jsonl"),"","utf8"),n.writeFileSync(i.join(a,".lite"),"1","utf8"),console.log(l(" \u2714 Created inferno/contract.json")),console.log(l(" \u2714 Created inferno/capabilities.json")),console.log(l(" \u2714 Created inferno/sessions.jsonl")),console.log(),console.log(" "+t("Ready. Start using it:")),console.log(" "+s("infernoflow log")+r(` "what you're building" --type note`)),console.log(" "+s("infernoflow context")+r(" \u2014 generate AI context to paste")),console.log(" "+s("infernoflow init --mode full")+r(" \u2014 expand to full setup when you need it")),console.log()}const $e=/^(?:node|npm|npx|yarn|pnpm|bun|git|cd|mkdir|rm|ls|cat|echo|type|dir|copy|del|move|python|python3|pip|go|cargo|java|gradle|mvn|docker|kubectl|curl|wget|ssh|scp|chmod|chown|sudo|brew|apt|yum)\b/i,De=/\s(?:&&|\|\||>>|>|<<|<|\|)\s/,Ge=/(?:^|\s)[A-Za-z]:\\|\.\.[\\\/]|[\\\/]bin[\\\/]/;function Le(e){if(!e)return{kind:"empty"};const o=e.replace(/^\s*[>$#]\s+/,"").trim();return o?/[\r\n]/.test(o)?{kind:"multiline",value:o}:$e.test(o)?{kind:"command",value:o}:De.test(o)?{kind:"command",value:o}:Ge.test(o)?{kind:"command",value:o}:o.length<3?{kind:"tooShort",value:o}:{kind:"ok",value:o}:{kind:"empty"}}async function He({yes:e}){if(e||!process.stdin.isTTY)return"";const{gray:o,yellow:t,cyan:s}=await import("../ui/output.mjs"),r=" "+o(`What should the next AI agent know about this project?
23
+ > `),l=d=>new Promise(g=>{const a=H.createInterface({input:process.stdin,output:process.stdout});let u=!1;a.on("SIGINT",()=>{u=!0,a.close(),g(null)}),a.on("close",()=>{u&&g(null)}),a.question(d,v=>{a.close(),g(v)})});for(let d=0;d<2;d++){const g=await l(d===0?r:" "+o("> "));if(g===null)return console.log(),"";const a=Le(g);if(a.kind==="ok")return a.value;if(a.kind==="empty")return"";if(a.kind==="command"){console.log(" "+t("\u26A0")+" That looks like a shell command, not a memory."),console.log(" "+o(" Try a short note like: ")+s('"API returns 202 not 200 on async upload"'));continue}if(a.kind==="multiline"){console.log(" "+t("\u26A0")+" Multi-line paste detected \u2014 log a single gotcha at a time."),console.log(" "+o(" Try one short sentence:"));continue}if(a.kind==="tooShort"){console.log(" "+t("\u26A0")+" Too short to be useful as a memory. Skip with Enter, or try again:");continue}}return""}async function Je(e,o,t){const{bold:s,cyan:r,gray:l,green:d,yellow:g}=await import("../ui/output.mjs"),a=i.join(e,".ai-memory"),u=i.join(e,"inferno"),v=i.join(a,"sessions.jsonl"),b=n.existsSync(v),E=n.existsSync(i.join(u,"sessions.jsonl"));if((b||E)&&!o){const h=n.existsSync(a)?".ai-memory/":"inferno/ (legacy)";console.log(`
24
24
  `+s("\u{1F525} infernoflow")+l(` \u2014 already set up
25
- `)),console.log(" "+d("\u2714")+" "+k+` found
26
- `),B(e),Z(e);try{Q(e,{silent:!0})}catch{}ee(e,o),oe(e,o),console.log(" Quick commands:"),console.log(" "+r('infernoflow log "..."')+l(" \u2014 remember something")),console.log(" "+r("infernoflow switch")+l(" \u2014 handoff to next AI")),console.log(" "+r("infernoflow recap")+l(` \u2014 session summary
25
+ `)),console.log(" "+d("\u2714")+" "+h+` found
26
+ `),Z(e),Q(e);try{ee(e,{silent:!0})}catch{}const W=oe(e,o),I=ne(e,o);W+I>0&&te(),console.log(" Quick commands:"),console.log(" "+r('infernoflow log "..."')+l(" \u2014 remember something")),console.log(" "+r("infernoflow switch")+l(" \u2014 handoff to next AI")),console.log(" "+r("infernoflow recap")+l(` \u2014 session summary
27
27
  `)),n.existsSync(a)||console.log(l(" Tip: run ")+r("infernoflow amp migrate")+l(` to move legacy memory into .ai-memory/
28
28
  `)),console.log(l(` For contracts & CI gates: infernoflow init --mode full
29
- `));return}const I=K(e);console.log(`
29
+ `));return}const O=K(e);console.log(`
30
30
  `+s("\u{1F525} infernoflow")+l(` \u2014 let's get you set up (30 seconds)
31
- `)),console.log(" Detected: "+r(I)+`
32
- `),Ce(e),n.existsSync(S)||n.writeFileSync(S,"","utf8"),xe(e,{project:I,config:{autoCapture:!0}}),B(e);const N=Z(e);for(const k of N)"created"in k&&(k.created||k.updated)&&w((k.created?"Created: ":"Updated: ")+r(k.rel));ee(e,o),oe(e,o);try{Q(e)}catch(k){T("MCP auto-setup skipped: "+k.message)}const P=await Le({yes:t});P?(X(e,{ts:new Date().toISOString(),agent:"user",type:"gotcha",summary:P,source:"init"}),console.log(`
33
- `+d("\u2714")+" First gotcha logged!")):t&&X(e,{ts:new Date().toISOString(),agent:"infernoflow",type:"note",summary:"infernoflow init complete \u2014 memory loop is live. Run `infernoflow status` to verify.",source:"init"}),console.log(`
31
+ `)),console.log(" Detected: "+r(O)+`
32
+ `),xe(e),n.existsSync(v)||n.writeFileSync(v,"","utf8"),Oe(e,{project:O,config:{autoCapture:!0}}),Z(e);const N=Q(e);for(const h of N)"created"in h&&(h.created||h.updated)&&k((h.created?"Created: ":"Updated: ")+r(h.rel));const R=oe(e,o),_=ne(e,o);R+_>0&&te();try{ee(e)}catch(h){T("MCP auto-setup skipped: "+h.message)}const y=await He({yes:t});y?(B(e,{ts:new Date().toISOString(),agent:"user",type:"gotcha",summary:y,source:"init"}),console.log(`
33
+ `+d("\u2714")+" First gotcha logged!")):t&&B(e,{ts:new Date().toISOString(),agent:"infernoflow",type:"note",summary:"infernoflow init complete \u2014 memory loop is live. Run `infernoflow status` to verify.",source:"init"}),console.log(`
34
34
  `+d("\u2714")+` You're set up. Quick commands:
35
35
  `),console.log(" "+r("infernoflow status")+l(" \u2014 verify the loop end-to-end")),console.log(" "+r('infernoflow log "..."')+l(" \u2014 remember something")),console.log(" "+r("infernoflow switch")+l(" \u2014 generate handoff for next AI")),console.log(" "+r("infernoflow recap")+l(` \u2014 session summary
36
36
  `)),console.log(l(` Tip: infernoflow switch --copy puts the handoff on your clipboard.
37
- `))}function Je(){console.log(["infernoflow init \u2014 set up persistent AI memory in this project","","Usage:"," infernoflow init Memory-first setup (default, 60 sec)"," infernoflow init --lite Minimal: 3 files, no scripts, no workflows"," infernoflow init --mode full Contracts + CI gates (advanced)"," infernoflow init --adopt Detect existing capabilities from code"," infernoflow init --template <name> Start from a project template","","Options:"," -y, --yes Non-interactive; accept all defaults"," -f, --force Overwrite existing inferno/ or .ai-memory/"," --cursor-hooks Also install Cursor IDE hooks (advanced)"," --vscode-copilot-hooks Also install VS Code Copilot hooks (advanced)"," --lang <lang> Override language detection (with --adopt)"," --framework <name> Override framework detection (with --adopt)"," --project-type <type> Override project-type detection (with --adopt)"," -h, --help Show this help","","Default flow writes .ai-memory/ + AI rule files + MCP server config, then asks","for one first gotcha. Press Enter to skip \u2014 never blocks."].join(`
38
- `))}async function no(e){if(e.includes("--help")||e.includes("-h")){Je();return}const o=process.cwd(),t=e.includes("--force")||e.includes("-f"),s=e.includes("--yes")||e.includes("-y"),r=e.includes("--adopt"),l=e.find(p=>p.startsWith("--mode="))?.split("=")[1]||(e.indexOf("--mode")!==-1?e[e.indexOf("--mode")+1]:null),d=l==="full"||l==="contract",y=r||e.includes("--template")||e.includes("--cursor-hooks")||e.includes("--vscode-copilot-hooks")||e.includes("--lite");if(!d&&!y){await He(o,t,s);return}if(e.includes("--lite")){await Re(o,t);return}const a=e.indexOf("--template"),u=a!==-1?e[a+1]:null;if(u){let p;try{p=await import("../templates/index.mjs")}catch{}const c=p?.getTemplate(u);if(!c){const m=p?p.listTemplates().map(A=>A.name).join(", "):"rest-api, nextjs, cli, graphql, monorepo";T(`Unknown template: ${u}. Available: ${m}`),process.exit(1)}const h=i.join(o,"inferno"),F=i.join(h,"scenarios");n.existsSync(h)||n.mkdirSync(h,{recursive:!0}),n.existsSync(F)||n.mkdirSync(F,{recursive:!0});const D=K(o),j=c.capabilities;n.writeFileSync(i.join(h,"contract.json"),JSON.stringify({policyId:D,policyVersion:1,capabilities:j.map(m=>m.id)},null,2)+`
39
- `),n.writeFileSync(i.join(h,"capabilities.json"),JSON.stringify({capabilities:j.map(m=>({id:m.id,description:m.description,since:new Date().toISOString().slice(0,10),source:`template:${u}`}))},null,2)+`
40
- `);for(const m of j)n.writeFileSync(i.join(F,`${m.id}.json`),JSON.stringify({id:`${m.id}-happy-path`,capability:m.id,description:`Happy path for ${m.description||m.id}`,steps:[{action:"invoke",target:m.id,input:{}},{action:"assert",field:"status",value:"success"}],capabilitiesCovered:[m.id]},null,2)+`
41
- `);ie(i.join(h,"CHANGELOG.md"),D),n.writeFileSync(i.join(h,"CONTEXT.md"),`# ${D} \u2014 infernoflow context
37
+ `))}function Me(){console.log(["infernoflow init \u2014 set up persistent AI memory in this project","","Usage:"," infernoflow init Memory-first setup (default, 60 sec)"," infernoflow init --lite Minimal: 3 files, no scripts, no workflows"," infernoflow init --mode full Contracts + CI gates (advanced)"," infernoflow init --adopt Detect existing capabilities from code"," infernoflow init --template <name> Start from a project template","","Options:"," -y, --yes Non-interactive; accept all defaults"," -f, --force Overwrite existing inferno/ or .ai-memory/"," --cursor-hooks Also install Cursor IDE hooks (advanced)"," --vscode-copilot-hooks Also install VS Code Copilot hooks (advanced)"," --lang <lang> Override language detection (with --adopt)"," --framework <name> Override framework detection (with --adopt)"," --project-type <type> Override project-type detection (with --adopt)"," -h, --help Show this help","","Default flow writes .ai-memory/ + AI rule files + MCP server config, then asks","for one first gotcha. Press Enter to skip \u2014 never blocks."].join(`
38
+ `))}async function to(e){if(e.includes("--help")||e.includes("-h")){Me();return}const o=process.cwd(),t=e.includes("--force")||e.includes("-f"),s=e.includes("--yes")||e.includes("-y"),r=e.includes("--adopt"),l=e.find(f=>f.startsWith("--mode="))?.split("=")[1]||(e.indexOf("--mode")!==-1?e[e.indexOf("--mode")+1]:null),d=l==="full"||l==="contract",g=r||e.includes("--template")||e.includes("--cursor-hooks")||e.includes("--vscode-copilot-hooks")||e.includes("--lite");if(!d&&!g){await Je(o,t,s);return}if(e.includes("--lite")){await _e(o,t);return}const a=e.indexOf("--template"),u=a!==-1?e[a+1]:null;if(u){let f;try{f=await import("../templates/index.mjs")}catch{}const c=f?.getTemplate(u);if(!c){const m=f?f.listTemplates().map(A=>A.name).join(", "):"rest-api, nextjs, cli, graphql, monorepo";T(`Unknown template: ${u}. Available: ${m}`),process.exit(1)}const w=i.join(o,"inferno"),P=i.join(w,"scenarios");n.existsSync(w)||n.mkdirSync(w,{recursive:!0}),n.existsSync(P)||n.mkdirSync(P,{recursive:!0});const G=K(o),S=c.capabilities;n.writeFileSync(i.join(w,"contract.json"),JSON.stringify({policyId:G,policyVersion:1,capabilities:S.map(m=>m.id)},null,2)+`
39
+ `),n.writeFileSync(i.join(w,"capabilities.json"),JSON.stringify({capabilities:S.map(m=>({id:m.id,description:m.description,since:new Date().toISOString().slice(0,10),source:`template:${u}`}))},null,2)+`
40
+ `);for(const m of S)n.writeFileSync(i.join(P,`${m.id}.json`),JSON.stringify({id:`${m.id}-happy-path`,capability:m.id,description:`Happy path for ${m.description||m.id}`,steps:[{action:"invoke",target:m.id,input:{}},{action:"assert",field:"status",value:"success"}],capabilitiesCovered:[m.id]},null,2)+`
41
+ `);re(i.join(w,"CHANGELOG.md"),G),n.writeFileSync(i.join(w,"CONTEXT.md"),`# ${G} \u2014 infernoflow context
42
42
 
43
43
  > Template: ${u} \u2014 ${c.description}
44
44
 
45
45
  ## Hint
46
46
  ${c.contextHint}
47
47
 
48
- ## Capabilities (${j.length})
49
- ${j.map(m=>`- \`${m.id}\`: ${m.description}`).join(`
48
+ ## Capabilities (${S.length})
49
+ ${S.map(m=>`- \`${m.id}\`: ${m.description}`).join(`
50
50
  `)}
51
- `),c.scripts&&(z("Suggested package.json scripts for this template:"),Object.entries(c.scripts).forEach(([m,A])=>console.log(` ${J(m)}: ${v(A)}`)),console.log()),V(`Initialised from template ${J(f(u))} \u2014 ${J(String(j.length))} capabilities`),console.log(),z(`Run ${f("infernoflow vibe")} to start vibe coding mode`),console.log();return}const S=e.includes("--cursor-hooks"),x=e.includes("--vscode-copilot-hooks"),E=e.includes("--report-json"),O=e.includes("--report-json-only"),I=e.includes("--report-human-only"),N=Y(e,"--lang"),P=Y(e,"--framework"),k=Y(e,"--project-type"),g=O;O&&I&&(console.error("Error: --report-json-only and --report-human-only cannot be used together."),process.exit(1)),g||ge("init");const b=i.join(o,"inferno"),se=i.join(o,".github","workflows");n.existsSync(b)&&!t&&(g&&(console.log(JSON.stringify({ok:!1,error:"inferno_exists",hint:"Use --force to overwrite"},null,2)),process.exit(1)),T("inferno/ already exists. Use --force to overwrite."),console.log(),process.exit(0));const R=K(o),q="CreateTask, ReadTasks, UpdateTask, ToggleComplete, DeleteTask";let _=R,C=q.split(",").map(p=>p.trim());if(r||d){let c=M(o,{language:N||void 0,framework:P||void 0,projectType:k||void 0});if(!s&&!O){const j=H.createInterface({input:process.stdin,output:process.stdout}),m=c.developmentProfile||{},A=m.detected||{};console.log(v(` Review inferred development stack (press Enter to accept detected values)
52
- `));const de=await L(j,"Language",m.language||A.language||"unknown"),ue=await L(j,"Framework",m.framework||A.framework||"unknown"),me=await L(j,"Project type",m.projectType||A.projectType||"unknown");j.close(),c=M(o,{language:de,framework:ue,projectType:me})}const h=c.capabilities,F=Se(h);O?console.log(JSON.stringify({mode:"adopt",policyId:R,inferredCapabilities:F,components:c.components,displayFields:c.displayFields,externalLibraries:c.externalLibraries,uiLayout:c.uiLayout,styling:c.styling,developmentProfile:c.developmentProfile,apiCalls:c.apiCalls},null,2)):(console.log(),console.log(v(je(h))),console.log(),console.log(v(ve(c))),console.log(),E&&!I&&(console.log(JSON.stringify({mode:"adopt",policyId:R,inferredCapabilities:F,components:c.components,displayFields:c.displayFields,externalLibraries:c.externalLibraries,uiLayout:c.uiLayout,styling:c.styling,developmentProfile:c.developmentProfile,apiCalls:c.apiCalls},null,2)),console.log()));const D=await we(h,s);_=R,C=D.map(j=>j.id),C.length===0&&(C=["ExampleCapability"],g||T("No capabilities detected from the code \u2014 wrote a placeholder. Edit "+f("inferno/capabilities.json")+" to describe what this project does."))}else if(!s){const p=H.createInterface({input:process.stdin,output:process.stdout});console.log(v(` Press Enter to accept defaults
53
- `)),_=await L(p,"Project / policy name",R),C=(await L(p,"Capabilities (comma-separated)",q)).split(",").map(h=>h.trim()).filter(Boolean),p.close(),console.log()}if(n.mkdirSync(b,{recursive:!0}),r){const p=C.map(h=>({id:h,title:h.replace(/([A-Z])/g," $1").trim()})),c=M(o,{language:N||void 0,framework:P||void 0,projectType:k||void 0});ke(b,_,p,c),g||(w("Created: "+f("inferno/contract.json")),w("Created: "+f("inferno/capabilities.json")),w("Created: "+f("inferno/scenarios/adoption_baseline.json")),w("Created: "+f("inferno/adoption_profile.json")),w("Created: "+f("inferno/CHANGELOG.md")))}else Pe(i.join(b,"contract.json"),_,C),g||w("Created: "+f("inferno/contract.json")),Fe(i.join(b,"capabilities.json"),C),g||w("Created: "+f("inferno/capabilities.json")),Ne(i.join(b,"scenarios"),C),g||w("Created: "+f("inferno/scenarios/happy_path.json")),ie(i.join(b,"CHANGELOG.md"),_),g||w("Created: "+f("inferno/CHANGELOG.md"));const $=W(),re=i.join($,"scripts","inferno-doc-gate.mjs"),le=i.join(o,"scripts","inferno-doc-gate.mjs");U(re,le,t,g);const ce=i.join($,"scripts","inferno-install-hooks.mjs"),ae=i.join(o,"scripts","inferno-install-hooks.mjs");U(ce,ae,t,g);const pe=i.join($,"ci","github-inferno-check.yml"),fe=i.join(se,"infernoflow-check.yml");if(U(pe,fe,t,g),Te(o,g),S&&be({cwd:o,templatesRoot:$,force:t,silent:g,logOk:p=>{g||w(p)},logWarn:p=>{g||T(p)}}),x&&Oe({cwd:o,templatesRoot:$,force:t,silent:g,logOk:p=>{g||w(p)},logWarn:p=>{g||T(p)}}),r){const p=i.join(b,"context-state.json");let c={};try{c=JSON.parse(n.readFileSync(p,"utf8"))}catch{}const h=M(o,{language:N||void 0,framework:P||void 0,projectType:k||void 0});c.stack=h.developmentProfile,n.writeFileSync(p,JSON.stringify(c,null,2)+`
54
- `,"utf8"),g||w("Created: "+f("inferno/context-state.json"))}if(!g){V("infernoflow initialized!");const p=i.join(b,"integrations.json");!(process.env.ANTHROPIC_API_KEY||process.env.OPENAI_API_KEY||process.env.GOOGLE_AI_API_KEY||process.env.OPENROUTER_API_KEY||process.env.GEMINI_API_KEY)&&!n.existsSync(p)&&(console.log(),console.log(` ${G("\u{1F4A1}")} ${J("Tip:")} connect an AI provider for explain, why, review, and changelog AI.`),console.log(` ${f("infernoflow ai setup")} \u2014 takes 60 seconds`)),he([f("infernoflow status")+" \u2014 see your contract at a glance",f("infernoflow check")+" \u2014 validate everything",(r?"Review inferred baseline in ":"Edit ")+G("inferno/capabilities.json")+(r?" and refine IDs/titles":" to describe each capability in detail"),"Add more "+G("inferno/scenarios/*.json")+" files for edge cases","Add "+f("inferno:check")+" to your CI pipeline",...S?["Restart Cursor \u2014 hooks write assistant text to "+G("inferno/CONTEXT.draft.md"),"Promote when ready: "+f("npm run inferno:promote-draft -- --append-notes")]:[],...x?["Restart VS Code \u2014 Copilot hooks append prompts + assistant (from transcript) to "+G("inferno/CONTEXT.draft.md"),"Promote when ready: "+f("npm run inferno:promote-draft -- --append-notes")]:[],...!S&&!x?["Optional: "+f("infernoflow install-cursor-hooks")+" or "+f("infernoflow install-vscode-copilot-hooks")]:[]])}}export{oo as ensureGitignoreEntries,no as initCommand};
51
+ `),c.scripts&&(z("Suggested package.json scripts for this template:"),Object.entries(c.scripts).forEach(([m,A])=>console.log(` ${J(m)}: ${j(A)}`)),console.log()),X(`Initialised from template ${J(p(u))} \u2014 ${J(String(S.length))} capabilities`),console.log(),z(`Run ${p("infernoflow vibe")} to start vibe coding mode`),console.log();return}const v=e.includes("--cursor-hooks"),b=e.includes("--vscode-copilot-hooks"),E=e.includes("--report-json"),x=e.includes("--report-json-only"),O=e.includes("--report-human-only"),N=q(e,"--lang"),R=q(e,"--framework"),_=q(e,"--project-type"),y=x;x&&O&&(console.error("Error: --report-json-only and --report-human-only cannot be used together."),process.exit(1)),y||he("init");const h=i.join(o,"inferno"),W=i.join(o,".github","workflows");n.existsSync(h)&&!t&&(y&&(console.log(JSON.stringify({ok:!1,error:"inferno_exists",hint:"Use --force to overwrite"},null,2)),process.exit(1)),T("inferno/ already exists. Use --force to overwrite."),console.log(),process.exit(0));const I=K(o),V="CreateTask, ReadTasks, UpdateTask, ToggleComplete, DeleteTask";let $=I,C=V.split(",").map(f=>f.trim());if(r||d){let c=M(o,{language:N||void 0,framework:R||void 0,projectType:_||void 0});if(!s&&!x){const S=H.createInterface({input:process.stdin,output:process.stdout}),m=c.developmentProfile||{},A=m.detected||{};console.log(j(` Review inferred development stack (press Enter to accept detected values)
52
+ `));const ue=await L(S,"Language",m.language||A.language||"unknown"),me=await L(S,"Framework",m.framework||A.framework||"unknown"),ye=await L(S,"Project type",m.projectType||A.projectType||"unknown");S.close(),c=M(o,{language:ue,framework:me,projectType:ye})}const w=c.capabilities,P=ve(w);x?console.log(JSON.stringify({mode:"adopt",policyId:I,inferredCapabilities:P,components:c.components,displayFields:c.displayFields,externalLibraries:c.externalLibraries,uiLayout:c.uiLayout,styling:c.styling,developmentProfile:c.developmentProfile,apiCalls:c.apiCalls},null,2)):(console.log(),console.log(j(Se(w))),console.log(),console.log(j(Ce(c))),console.log(),E&&!O&&(console.log(JSON.stringify({mode:"adopt",policyId:I,inferredCapabilities:P,components:c.components,displayFields:c.displayFields,externalLibraries:c.externalLibraries,uiLayout:c.uiLayout,styling:c.styling,developmentProfile:c.developmentProfile,apiCalls:c.apiCalls},null,2)),console.log()));const G=await ke(w,s);$=I,C=G.map(S=>S.id),C.length===0&&(C=["ExampleCapability"],y||T("No capabilities detected from the code \u2014 wrote a placeholder. Edit "+p("inferno/capabilities.json")+" to describe what this project does."))}else if(!s){const f=H.createInterface({input:process.stdin,output:process.stdout});console.log(j(` Press Enter to accept defaults
53
+ `)),$=await L(f,"Project / policy name",I),C=(await L(f,"Capabilities (comma-separated)",V)).split(",").map(w=>w.trim()).filter(Boolean),f.close(),console.log()}if(n.mkdirSync(h,{recursive:!0}),r){const f=C.map(w=>({id:w,title:w.replace(/([A-Z])/g," $1").trim()})),c=M(o,{language:N||void 0,framework:R||void 0,projectType:_||void 0});je(h,$,f,c),y||(k("Created: "+p("inferno/contract.json")),k("Created: "+p("inferno/capabilities.json")),k("Created: "+p("inferno/scenarios/adoption_baseline.json")),k("Created: "+p("inferno/adoption_profile.json")),k("Created: "+p("inferno/CHANGELOG.md")))}else Fe(i.join(h,"contract.json"),$,C),y||k("Created: "+p("inferno/contract.json")),Ne(i.join(h,"capabilities.json"),C),y||k("Created: "+p("inferno/capabilities.json")),Re(i.join(h,"scenarios"),C),y||k("Created: "+p("inferno/scenarios/happy_path.json")),re(i.join(h,"CHANGELOG.md"),$),y||k("Created: "+p("inferno/CHANGELOG.md"));const D=Y(),le=i.join(D,"scripts","inferno-doc-gate.mjs"),ce=i.join(o,"scripts","inferno-doc-gate.mjs");U(le,ce,t,y);const ae=i.join(D,"scripts","inferno-install-hooks.mjs"),pe=i.join(o,"scripts","inferno-install-hooks.mjs");U(ae,pe,t,y);const fe=i.join(D,"ci","github-inferno-check.yml"),de=i.join(W,"infernoflow-check.yml");if(U(fe,de,t,y),Ee(o,y),v&&be({cwd:o,templatesRoot:D,force:t,silent:y,logOk:f=>{y||k(f)},logWarn:f=>{y||T(f)}}),b&&Ie({cwd:o,templatesRoot:D,force:t,silent:y,logOk:f=>{y||k(f)},logWarn:f=>{y||T(f)}}),r){const f=i.join(h,"context-state.json");let c={};try{c=JSON.parse(n.readFileSync(f,"utf8"))}catch{}const w=M(o,{language:N||void 0,framework:R||void 0,projectType:_||void 0});c.stack=w.developmentProfile,n.writeFileSync(f,JSON.stringify(c,null,2)+`
54
+ `,"utf8"),y||k("Created: "+p("inferno/context-state.json"))}if(!y){X("infernoflow initialized!");const f=i.join(h,"integrations.json");!(process.env.ANTHROPIC_API_KEY||process.env.OPENAI_API_KEY||process.env.GOOGLE_AI_API_KEY||process.env.OPENROUTER_API_KEY||process.env.GEMINI_API_KEY)&&!n.existsSync(f)&&(console.log(),console.log(` ${F("\u{1F4A1}")} ${J("Tip:")} connect an AI provider for explain, why, review, and changelog AI.`),console.log(` ${p("infernoflow ai setup")} \u2014 takes 60 seconds`)),we([p("infernoflow status")+" \u2014 see your contract at a glance",p("infernoflow check")+" \u2014 validate everything",(r?"Review inferred baseline in ":"Edit ")+F("inferno/capabilities.json")+(r?" and refine IDs/titles":" to describe each capability in detail"),"Add more "+F("inferno/scenarios/*.json")+" files for edge cases","Add "+p("inferno:check")+" to your CI pipeline",...v?["Restart Cursor \u2014 hooks write assistant text to "+F("inferno/CONTEXT.draft.md"),"Promote when ready: "+p("npm run inferno:promote-draft -- --append-notes")]:[],...b?["Restart VS Code \u2014 Copilot hooks append prompts + assistant (from transcript) to "+F("inferno/CONTEXT.draft.md"),"Promote when ready: "+p("npm run inferno:promote-draft -- --append-notes")]:[],...!v&&!b?["Optional: "+p("infernoflow install-cursor-hooks")+" or "+p("infernoflow install-vscode-copilot-hooks")]:[]])}}export{no as ensureGitignoreEntries,to as initCommand};
@@ -260,7 +260,7 @@ function isCmdError(result) {
260
260
  const TOOLS = [
261
261
  // ── AMP-spec memory tools (the product) ──────────────────────────────────
262
262
  { name: "amp_read", description: "AMP: read session memory entries with optional filters.", inputSchema: { type: "object", properties: { file: { type: "string" }, type: { type: "string", enum: ["gotcha","decision","attempt","note","detection","pattern"] }, query: { type: "string" }, limit: { type: "number" } } } },
263
- { name: "amp_write", description: "AMP: log a new entry. Required: type + msg (one sentence). Optional: file, line, tags, detail. Use 'detail' for a rich multi-paragraph body (repro steps, code, full reasoning, or a session snapshot) — it's stored in a sidecar and loaded on demand, so it never bloats the always-on memory index.", inputSchema: { type: "object", properties: { type: { type: "string", enum: ["gotcha","decision","attempt","note","detection","pattern"] }, msg: { type: "string" }, file: { type: "string" }, line: { type: "number" }, tags: { type: "array", items: { type: "string" } }, detail: { type: "string", description: "Optional rich body (Tier-2). Stored in details/<id>.md; NOT injected into rule files. Put the long-form context here; keep 'msg' to one summary sentence." } }, required: ["type","msg"] } },
263
+ { name: "amp_write", description: "AMP: log a new entry. Required: type + msg (one sentence). Optional: file, line, tags, detail. Use 'detail' for a rich multi-paragraph body (repro steps, code, full reasoning, or a session snapshot) — it's stored in a sidecar and loaded on demand, so it never bloats the always-on memory index.", inputSchema: { type: "object", properties: { type: { type: "string", enum: ["gotcha","decision","attempt","note","detection","pattern"] }, msg: { type: "string" }, file: { type: "string" }, line: { type: "number" }, tags: { type: "array", items: { type: "string" } }, detail: { type: "string", description: "Optional rich body (Tier-2). Stored in the consolidated details store; NOT injected into rule files. Put the long-form context here; keep 'msg' to one summary sentence." } }, required: ["type","msg"] } },
264
264
  { name: "amp_search", description: "AMP: search entries by keyword. Optional type filter.", inputSchema: { type: "object", properties: { query: { type: "string" }, type: { type: "string", enum: ["gotcha","decision","attempt","note","detection","pattern"] } }, required: ["query"] } },
265
265
  { name: "amp_bookmark", description: "AMP: drop a named session bookmark — a resume point. Required: label (short name). Optional: note. If note is OMITTED, the current session transcript is auto-captured as the bookmark's context (the 'save everything here' resume point). Use when the user says 'bookmark this' / 'mark this point', or before a risky change / when the context window is filling up, so the exact state can be recalled later and appears in the next session's handoff. Bookmarks are never auto-pruned.", inputSchema: { type: "object", properties: { label: { type: "string" }, note: { type: "string", description: "Optional explicit context. Omit to auto-capture the session transcript instead. Stored in a sidecar; not injected into rule files." } }, required: ["label"] } },
266
266
  { name: "amp_handoff", description: "AMP: generate the handoff document for the next AI session. format=markdown|json (default: markdown).", inputSchema: { type: "object", properties: { format: { type: "string", enum: ["markdown","json"] } } } },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "infernoflow",
3
- "version": "0.44.18",
3
+ "version": "0.44.19",
4
4
  "description": "Persistent memory for AI coding sessions — captures what agents can't infer from code alone. Works with Copilot, Cursor, Claude, and Windsurf.",
5
5
  "type": "module",
6
6
  "bin": {