infernoflow 0.43.6 → 0.43.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,11 @@
1
- import*as i from"node:fs";import*as s from"node:path";import*as L from"node:readline";import{fileURLToPath as se}from"node:url";import{header as re,ok as g,warn as $,done as q,nextSteps as ce,bold as J,cyan as d,yellow as R,gray as A}from"../ui/output.mjs";import{discoverProjectSignals as H,reviewCapabilitiesInteractive as le,writeAdoptionBaseline as ae,buildAdoptionReport as pe,summarizeCapabilities as fe,buildSignalsReport as de}from"./adopt.mjs";import{installCursorHooksArtifacts as ue}from"../cursorHooksInstall.mjs";import{ensureAmpDir as me,appendEntry as ye,writeDefaultConfig as ge}from"../amp/io.mjs";import{installVsCodeCopilotHooksArtifacts as we}from"../vsCodeCopilotHooksInstall.mjs";const he=s.dirname(se(import.meta.url));function ke(){return s.resolve(he,"../../templates")}function _(e,o,n=""){return new Promise(t=>{const r=n?A(` (${n})`):"";e.question(` ${o}${r}: `,l=>{t(l.trim()||n)})})}function M(e,...o){for(const n of o){const t=e.indexOf(n);if(t!==-1&&e[t+1]&&!e[t+1].startsWith("-"))return e[t+1]}return null}function D(e,o,n,t=!1){return i.existsSync(o)&&!n?(t||$("Skipped (exists): "+s.relative(process.cwd(),o)),!1):(i.mkdirSync(s.dirname(o),{recursive:!0}),i.copyFileSync(e,o),t||g("Created: "+d(s.relative(process.cwd(),o))),!0)}function je(e,o,n){i.mkdirSync(o,{recursive:!0});for(const t of i.readdirSync(e,{withFileTypes:!0})){const r=s.join(e,t.name),l=s.join(o,t.name);t.isDirectory()?je(r,l,n):D(r,l,n)}}function Se(e,o=!1){const n=s.join(e,"package.json");if(!i.existsSync(n))return;const t=JSON.parse(i.readFileSync(n,"utf8"));t.scripts=t.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[y,k]of Object.entries(l))t.scripts[y]||(t.scripts[y]=k,r=!0);r&&(i.writeFileSync(n,JSON.stringify(t,null,2)+`
2
- `,"utf8"),o||g("Updated "+d("package.json")+" scripts"))}function G(e){const o=s.join(e,"package.json");if(i.existsSync(o))try{const n=JSON.parse(i.readFileSync(o,"utf8"));if(n.name)return n.name.replace(/[^a-z0-9_-]/gi,"_")}catch{}return s.basename(e)}function ve(e,o,n){const t={policyId:o,policyVersion:1,capabilities:n,rules:{docsRequiredOnCapabilityChange:!0,requireScenarioForEachCapability:!0,requireChangelogOnCapabilityChange:!0}};i.writeFileSync(e,JSON.stringify(t,null,2)+`
3
- `)}function be(e,o){const n={schemaVersion:1,capabilities:o.map(t=>({id:t,title:t.replace(/([A-Z])/g," $1").trim(),since:"0.1.0"}))};i.writeFileSync(e,JSON.stringify(n,null,2)+`
4
- `)}function Ce(e,o){i.mkdirSync(e,{recursive:!0});const n={scenarioId:"happy_path",description:"Basic happy-path flow covering all capabilities",capabilitiesCovered:o,steps:o.map(t=>({action:t,expect:`${t} works as expected`}))};i.writeFileSync(s.join(e,"happy_path.json"),JSON.stringify(n,null,2)+`
5
- `)}function z(e,o){const n=`# Changelog \u2014 ${o}
1
+ import*as i from"node:fs";import*as s from"node:path";import*as G from"node:readline";import{fileURLToPath as ce}from"node:url";import{header as le,ok as g,warn as R,done as q,nextSteps as ae,bold as L,cyan as d,yellow as _,gray as O}from"../ui/output.mjs";import{discoverProjectSignals as D,reviewCapabilitiesInteractive as pe,writeAdoptionBaseline as fe,buildAdoptionReport as de,summarizeCapabilities as ue,buildSignalsReport as me}from"./adopt.mjs";import{installCursorHooksArtifacts as ye}from"../cursorHooksInstall.mjs";import{ensureAmpDir as ge,appendEntry as we,writeDefaultConfig as he}from"../amp/io.mjs";import{installVsCodeCopilotHooksArtifacts as ke}from"../vsCodeCopilotHooksInstall.mjs";const je=s.dirname(ce(import.meta.url));function Se(){return s.resolve(je,"../../templates")}function $(e,o,t=""){return new Promise(n=>{const r=t?O(` (${t})`):"";e.question(` ${o}${r}: `,l=>{n(l.trim()||t)})})}function W(e,...o){for(const t of o){const n=e.indexOf(t);if(n!==-1&&e[n+1]&&!e[n+1].startsWith("-"))return e[n+1]}return null}function J(e,o,t,n=!1){return i.existsSync(o)&&!t?(n||R("Skipped (exists): "+s.relative(process.cwd(),o)),!1):(i.mkdirSync(s.dirname(o),{recursive:!0}),i.copyFileSync(e,o),n||g("Created: "+d(s.relative(process.cwd(),o))),!0)}function ve(e,o,t){i.mkdirSync(o,{recursive:!0});for(const n of i.readdirSync(e,{withFileTypes:!0})){const r=s.join(e,n.name),l=s.join(o,n.name);n.isDirectory()?ve(r,l,t):J(r,l,t)}}function be(e,o=!1){const t=s.join(e,"package.json");if(!i.existsSync(t))return;const n=JSON.parse(i.readFileSync(t,"utf8"));n.scripts=n.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[y,k]of Object.entries(l))n.scripts[y]||(n.scripts[y]=k,r=!0);r&&(i.writeFileSync(t,JSON.stringify(n,null,2)+`
2
+ `,"utf8"),o||g("Updated "+d("package.json")+" scripts"))}const z="# --- infernoflow (developer-local AI memory; do not commit) ---",Ce="# --- /infernoflow ---",Oe=[".ai-memory/",".cursorrules","CLAUDE.md",".github/copilot-instructions.md"];function V(e,{silent:o=!1}={}){const t=s.join(e,".gitignore");let n="";if(i.existsSync(t)&&(n=i.readFileSync(t,"utf8")),n.includes(z))return!1;const r=["",z,"# Memory is per-developer, not per-branch. These files travel with you, not with git.",...Oe,Ce,""].join(`
3
+ `),l=n.length===0||n.endsWith(`
4
+ `)?"":`
5
+ `;return i.writeFileSync(t,n+l+r,"utf8"),o||g("Updated "+d(".gitignore")+O(" (memory stays local across branch switches)")),!0}function H(e){const o=s.join(e,"package.json");if(i.existsSync(o))try{const t=JSON.parse(i.readFileSync(o,"utf8"));if(t.name)return t.name.replace(/[^a-z0-9_-]/gi,"_")}catch{}return s.basename(e)}function xe(e,o,t){const n={policyId:o,policyVersion:1,capabilities:t,rules:{docsRequiredOnCapabilityChange:!0,requireScenarioForEachCapability:!0,requireChangelogOnCapabilityChange:!0}};i.writeFileSync(e,JSON.stringify(n,null,2)+`
6
+ `)}function Ie(e,o){const t={schemaVersion:1,capabilities:o.map(n=>({id:n,title:n.replace(/([A-Z])/g," $1").trim(),since:"0.1.0"}))};i.writeFileSync(e,JSON.stringify(t,null,2)+`
7
+ `)}function Te(e,o){i.mkdirSync(e,{recursive:!0});const t={scenarioId:"happy_path",description:"Basic happy-path flow covering all capabilities",capabilitiesCovered:o,steps:o.map(n=>({action:n,expect:`${n} works as expected`}))};i.writeFileSync(s.join(e,"happy_path.json"),JSON.stringify(t,null,2)+`
8
+ `)}function X(e,o){const t=`# Changelog \u2014 ${o}
6
9
 
7
10
  ## Unreleased
8
11
 
@@ -11,31 +14,31 @@ import*as i from"node:fs";import*as s from"node:path";import*as L from"node:read
11
14
  ## 0.1.0 \u2014 Initial release
12
15
 
13
16
  - Project initialized with infernoflow
14
- `;i.writeFileSync(e,n)}async function Oe(e,o){const{bold:n,cyan:t,gray:r,green:l,yellow:y,red:k}=await import("../ui/output.mjs");console.log(`
15
- `+n("\u{1F525} infernoflow init --lite")),console.log(" "+"\u2500".repeat(50)+`
17
+ `;i.writeFileSync(e,t)}async function Ae(e,o){const{bold:t,cyan:n,gray:r,green:l,yellow:y,red:k}=await import("../ui/output.mjs");console.log(`
18
+ `+t("\u{1F525} infernoflow init --lite")),console.log(" "+"\u2500".repeat(50)+`
16
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 p=s.join(e,"inferno");i.existsSync(p)&&!o&&(console.log(y(` \u26A0 inferno/ already exists. Use --force to overwrite.
17
- `)),process.exit(0)),i.mkdirSync(p,{recursive:!0});const w=G(e);let j="";if(!process.argv.includes("--yes")&&!process.argv.includes("-y")&&process.stdin.isTTY){const b=L.createInterface({input:process.stdin,output:process.stdout});j=await new Promise(S=>{b.question(r(" What does this project do? (one line, Enter to skip): "),x=>{b.close(),S(x.trim())})})}const O={policyId:w,policyVersion:1,lite:!0,capabilities:[],intent:j||void 0};i.writeFileSync(s.join(p,"contract.json"),JSON.stringify(O,null,2)+`
20
+ `)),process.exit(0)),i.mkdirSync(p,{recursive:!0});const w=H(e);let j="";if(!process.argv.includes("--yes")&&!process.argv.includes("-y")&&process.stdin.isTTY){const b=G.createInterface({input:process.stdin,output:process.stdout});j=await new Promise(S=>{b.question(r(" What does this project do? (one line, Enter to skip): "),I=>{b.close(),S(I.trim())})})}const x={policyId:w,policyVersion:1,lite:!0,capabilities:[],intent:j||void 0};i.writeFileSync(s.join(p,"contract.json"),JSON.stringify(x,null,2)+`
18
21
  `),i.writeFileSync(s.join(p,"capabilities.json"),JSON.stringify([],null,2)+`
19
- `),i.writeFileSync(s.join(p,"sessions.jsonl"),"","utf8"),i.writeFileSync(s.join(p,".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(" "+n("Ready. Start using it:")),console.log(" "+t("infernoflow log")+r(` "what you're building" --type note`)),console.log(" "+t("infernoflow theme")+r(" \u2014 scan your fonts + colors")),console.log(" "+t("infernoflow context")+r(" \u2014 generate AI context to paste")),console.log(" "+t("infernoflow upgrade")+r(" \u2014 expand to full setup when you need it")),console.log()}const xe=/^(?: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,Ie=/\s(?:&&|\|\||>>|>|<<|<|\|)\s/,Te=/(?:^|\s)[A-Za-z]:\\|\.\.[\\\/]|[\\\/]bin[\\\/]/;function Ae(e){if(!e)return{kind:"empty"};const o=e.replace(/^\s*[>$#]\s+/,"").trim();return o?/[\r\n]/.test(o)?{kind:"multiline",value:o}:xe.test(o)?{kind:"command",value:o}:Ie.test(o)?{kind:"command",value:o}:Te.test(o)?{kind:"command",value:o}:o.length<3?{kind:"tooShort",value:o}:{kind:"ok",value:o}:{kind:"empty"}}async function Pe({yes:e}){if(e||!process.stdin.isTTY)return"";const{gray:o,yellow:n,cyan:t}=await import("../ui/output.mjs"),r=" "+o(`What should the next AI agent know about this project?
20
- > `),l=y=>new Promise(k=>{const p=L.createInterface({input:process.stdin,output:process.stdout});let w=!1;p.on("SIGINT",()=>{w=!0,p.close(),k(null)}),p.on("close",()=>{w&&k(null)}),p.question(y,j=>{p.close(),k(j)})});for(let y=0;y<2;y++){const k=await l(y===0?r:" "+o("> "));if(k===null)return console.log(),"";const p=Ae(k);if(p.kind==="ok")return p.value;if(p.kind==="empty")return"";if(p.kind==="command"){console.log(" "+n("\u26A0")+" That looks like a shell command, not a memory."),console.log(" "+o(" Try a short note like: ")+t('"API returns 202 not 200 on async upload"'));continue}if(p.kind==="multiline"){console.log(" "+n("\u26A0")+" Multi-line paste detected \u2014 log a single gotcha at a time."),console.log(" "+o(" Try one short sentence:"));continue}if(p.kind==="tooShort"){console.log(" "+n("\u26A0")+" Too short to be useful as a memory. Skip with Enter, or try again:");continue}}return""}async function Fe(e,o,n){const{bold:t,cyan:r,gray:l,green:y,yellow:k}=await import("../ui/output.mjs"),p=s.join(e,".ai-memory"),w=s.join(e,"inferno"),j=s.join(p,"sessions.jsonl");if((i.existsSync(p)||i.existsSync(w))&&!o){const x=i.existsSync(p)?".ai-memory/":"inferno/ (legacy)";console.log(`
21
- `+t("\u{1F525} infernoflow")+l(` \u2014 already set up
22
- `)),console.log(" "+y("\u2714")+" "+x+` found
23
- `),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
22
+ `),i.writeFileSync(s.join(p,"sessions.jsonl"),"","utf8"),i.writeFileSync(s.join(p,".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(" "+n("infernoflow log")+r(` "what you're building" --type note`)),console.log(" "+n("infernoflow theme")+r(" \u2014 scan your fonts + colors")),console.log(" "+n("infernoflow context")+r(" \u2014 generate AI context to paste")),console.log(" "+n("infernoflow upgrade")+r(" \u2014 expand to full setup when you need it")),console.log()}const Ee=/^(?: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,Pe=/\s(?:&&|\|\||>>|>|<<|<|\|)\s/,Ne=/(?:^|\s)[A-Za-z]:\\|\.\.[\\\/]|[\\\/]bin[\\\/]/;function Fe(e){if(!e)return{kind:"empty"};const o=e.replace(/^\s*[>$#]\s+/,"").trim();return o?/[\r\n]/.test(o)?{kind:"multiline",value:o}:Ee.test(o)?{kind:"command",value:o}:Pe.test(o)?{kind:"command",value:o}:Ne.test(o)?{kind:"command",value:o}:o.length<3?{kind:"tooShort",value:o}:{kind:"ok",value:o}:{kind:"empty"}}async function Re({yes:e}){if(e||!process.stdin.isTTY)return"";const{gray:o,yellow:t,cyan:n}=await import("../ui/output.mjs"),r=" "+o(`What should the next AI agent know about this project?
23
+ > `),l=y=>new Promise(k=>{const p=G.createInterface({input:process.stdin,output:process.stdout});let w=!1;p.on("SIGINT",()=>{w=!0,p.close(),k(null)}),p.on("close",()=>{w&&k(null)}),p.question(y,j=>{p.close(),k(j)})});for(let y=0;y<2;y++){const k=await l(y===0?r:" "+o("> "));if(k===null)return console.log(),"";const p=Fe(k);if(p.kind==="ok")return p.value;if(p.kind==="empty")return"";if(p.kind==="command"){console.log(" "+t("\u26A0")+" That looks like a shell command, not a memory."),console.log(" "+o(" Try a short note like: ")+n('"API returns 202 not 200 on async upload"'));continue}if(p.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(p.kind==="tooShort"){console.log(" "+t("\u26A0")+" Too short to be useful as a memory. Skip with Enter, or try again:");continue}}return""}async function _e(e,o,t){const{bold:n,cyan:r,gray:l,green:y,yellow:k}=await import("../ui/output.mjs"),p=s.join(e,".ai-memory"),w=s.join(e,"inferno"),j=s.join(p,"sessions.jsonl");if((i.existsSync(p)||i.existsSync(w))&&!o){const I=i.existsSync(p)?".ai-memory/":"inferno/ (legacy)";console.log(`
24
+ `+n("\u{1F525} infernoflow")+l(` \u2014 already set up
25
+ `)),console.log(" "+y("\u2714")+" "+I+` found
26
+ `),V(e),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
24
27
  `)),i.existsSync(p)||console.log(l(" Tip: run ")+r("infernoflow amp migrate")+l(` to move legacy memory into .ai-memory/
25
28
  `)),console.log(l(` For contracts & CI gates: infernoflow init --mode full
26
- `));return}const b=G(e);console.log(`
27
- `+t("\u{1F525} infernoflow")+l(` \u2014 let's get you set up (30 seconds)
29
+ `));return}const b=H(e);console.log(`
30
+ `+n("\u{1F525} infernoflow")+l(` \u2014 let's get you set up (30 seconds)
28
31
  `)),console.log(" Detected: "+r(b)+`
29
- `),me(e),i.existsSync(j)||i.writeFileSync(j,"","utf8"),ge(e,{project:b,config:{autoCapture:!0}});const S=await Pe({yes:n});S&&(ye(e,{ts:new Date().toISOString(),agent:"user",type:"gotcha",summary:S,source:"init"}),console.log(`
32
+ `),ge(e),i.existsSync(j)||i.writeFileSync(j,"","utf8"),he(e,{project:b,config:{autoCapture:!0}}),V(e);const S=await Re({yes:t});S&&(we(e,{ts:new Date().toISOString(),agent:"user",type:"gotcha",summary:S,source:"init"}),console.log(`
30
33
  `+y("\u2714")+" First gotcha logged!")),console.log(`
31
34
  `+y("\u2714")+` You're set up. Quick commands:
32
35
  `),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
33
36
  `)),console.log(l(` Tip: infernoflow switch --copy puts the handoff on your clipboard.
34
37
  `)),console.log(l(` Want contracts & CI gates? Run: infernoflow init --mode full
35
- `))}async function Ge(e){const o=process.cwd(),n=e.includes("--force")||e.includes("-f"),t=e.includes("--yes")||e.includes("-y"),r=e.includes("--adopt"),l=e.find(a=>a.startsWith("--mode="))?.split("=")[1]||(e.indexOf("--mode")!==-1?e[e.indexOf("--mode")+1]:null),y=l==="full"||l==="contract",k=r||e.includes("--template")||e.includes("--cursor-hooks")||e.includes("--vscode-copilot-hooks")||e.includes("--lite");if(!y&&!k){await Fe(o,n,t);return}if(e.includes("--lite")){await Oe(o,n);return}const p=e.indexOf("--template"),w=p!==-1?e[p+1]:null;if(w){let a;try{a=await import("../templates/index.mjs")}catch{}const c=a?.getTemplate(w);if(!c){const f=a?a.listTemplates().map(C=>C.name).join(", "):"rest-api, nextjs, cli, graphql, monorepo";$(`Unknown template: ${w}. Available: ${f}`),process.exit(1)}const m=s.join(o,"inferno"),T=s.join(m,"scenarios");i.existsSync(m)||i.mkdirSync(m,{recursive:!0}),i.existsSync(T)||i.mkdirSync(T,{recursive:!0});const N=G(o),h=c.capabilities;i.writeFileSync(s.join(m,"contract.json"),JSON.stringify({policyId:N,policyVersion:1,capabilities:h.map(f=>f.id)},null,2)+`
38
+ `))}async function We(e){const o=process.cwd(),t=e.includes("--force")||e.includes("-f"),n=e.includes("--yes")||e.includes("-y"),r=e.includes("--adopt"),l=e.find(a=>a.startsWith("--mode="))?.split("=")[1]||(e.indexOf("--mode")!==-1?e[e.indexOf("--mode")+1]:null),y=l==="full"||l==="contract",k=r||e.includes("--template")||e.includes("--cursor-hooks")||e.includes("--vscode-copilot-hooks")||e.includes("--lite");if(!y&&!k){await _e(o,t,n);return}if(e.includes("--lite")){await Ae(o,t);return}const p=e.indexOf("--template"),w=p!==-1?e[p+1]:null;if(w){let a;try{a=await import("../templates/index.mjs")}catch{}const c=a?.getTemplate(w);if(!c){const f=a?a.listTemplates().map(C=>C.name).join(", "):"rest-api, nextjs, cli, graphql, monorepo";R(`Unknown template: ${w}. Available: ${f}`),process.exit(1)}const m=s.join(o,"inferno"),A=s.join(m,"scenarios");i.existsSync(m)||i.mkdirSync(m,{recursive:!0}),i.existsSync(A)||i.mkdirSync(A,{recursive:!0});const F=H(o),h=c.capabilities;i.writeFileSync(s.join(m,"contract.json"),JSON.stringify({policyId:F,policyVersion:1,capabilities:h.map(f=>f.id)},null,2)+`
36
39
  `),i.writeFileSync(s.join(m,"capabilities.json"),JSON.stringify({capabilities:h.map(f=>({id:f.id,description:f.description,since:new Date().toISOString().slice(0,10),source:`template:${w}`}))},null,2)+`
37
- `);for(const f of h)i.writeFileSync(s.join(T,`${f.id}.json`),JSON.stringify({id:`${f.id}-happy-path`,capability:f.id,description:`Happy path for ${f.description||f.id}`,steps:[{action:"invoke",target:f.id,input:{}},{action:"assert",field:"status",value:"success"}],capabilitiesCovered:[f.id]},null,2)+`
38
- `);z(s.join(m,"CHANGELOG.md"),N),i.writeFileSync(s.join(m,"CONTEXT.md"),`# ${N} \u2014 infernoflow context
40
+ `);for(const f of h)i.writeFileSync(s.join(A,`${f.id}.json`),JSON.stringify({id:`${f.id}-happy-path`,capability:f.id,description:`Happy path for ${f.description||f.id}`,steps:[{action:"invoke",target:f.id,input:{}},{action:"assert",field:"status",value:"success"}],capabilitiesCovered:[f.id]},null,2)+`
41
+ `);X(s.join(m,"CHANGELOG.md"),F),i.writeFileSync(s.join(m,"CONTEXT.md"),`# ${F} \u2014 infernoflow context
39
42
 
40
43
  > Template: ${w} \u2014 ${c.description}
41
44
 
@@ -45,7 +48,7 @@ ${c.contextHint}
45
48
  ## Capabilities (${h.length})
46
49
  ${h.map(f=>`- \`${f.id}\`: ${f.description}`).join(`
47
50
  `)}
48
- `),c.scripts&&(info("Suggested package.json scripts for this template:"),Object.entries(c.scripts).forEach(([f,C])=>console.log(` ${J(f)}: ${A(C)}`)),console.log()),q(`Initialised from template ${J(d(w))} \u2014 ${J(String(h.length))} capabilities`),console.log(),info(`Run ${d("infernoflow vibe")} to start vibe coding mode`),console.log();return}const j=e.includes("--cursor-hooks"),O=e.includes("--vscode-copilot-hooks"),b=e.includes("--report-json"),S=e.includes("--report-json-only"),x=e.includes("--report-human-only"),U=M(e,"--lang"),W=M(e,"--framework"),K=M(e,"--project-type"),u=S;S&&x&&(console.error("Error: --report-json-only and --report-human-only cannot be used together."),process.exit(1)),u||re("init");const v=s.join(o,"inferno"),V=s.join(o,".github","workflows");i.existsSync(v)&&!n&&(u&&(console.log(JSON.stringify({ok:!1,error:"inferno_exists",hint:"Use --force to overwrite"},null,2)),process.exit(1)),$("inferno/ already exists. Use --force to overwrite."),console.log(),process.exit(0));const P=G(o),Y="CreateTask, ReadTasks, UpdateTask, ToggleComplete, DeleteTask";let F=P,I=Y.split(",").map(a=>a.trim());if(r){let c=H(o,{language:U||void 0,framework:W||void 0,projectType:K||void 0});if(!t&&!S){const h=L.createInterface({input:process.stdin,output:process.stdout}),f=c.developmentProfile||{},C=f.detected||{};console.log(A(` Review inferred development stack (press Enter to accept detected values)
49
- `));const ne=await _(h,"Language",f.language||C.language||"unknown"),te=await _(h,"Framework",f.framework||C.framework||"unknown"),ie=await _(h,"Project type",f.projectType||C.projectType||"unknown");h.close(),c=H(o,{language:ne,framework:te,projectType:ie})}const m=c.capabilities,T=fe(m);S?console.log(JSON.stringify({mode:"adopt",policyId:P,inferredCapabilities:T,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(A(pe(m))),console.log(),console.log(A(de(c))),console.log(),b&&!x&&(console.log(JSON.stringify({mode:"adopt",policyId:P,inferredCapabilities:T,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 N=await le(m,t);F=P,I=N.map(h=>h.id)}else if(!t){const a=L.createInterface({input:process.stdin,output:process.stdout});console.log(A(` Press Enter to accept defaults
50
- `)),F=await _(a,"Project / policy name",P),I=(await _(a,"Capabilities (comma-separated)",Y)).split(",").map(m=>m.trim()).filter(Boolean),a.close(),console.log()}if(i.mkdirSync(v,{recursive:!0}),r){const a=I.map(m=>({id:m,title:m.replace(/([A-Z])/g," $1").trim()})),c=H(o,{language:U||void 0,framework:W||void 0,projectType:K||void 0});ae(v,F,a,c),u||(g("Created: "+d("inferno/contract.json")),g("Created: "+d("inferno/capabilities.json")),g("Created: "+d("inferno/scenarios/adoption_baseline.json")),g("Created: "+d("inferno/adoption_profile.json")),g("Created: "+d("inferno/CHANGELOG.md")))}else ve(s.join(v,"contract.json"),F,I),u||g("Created: "+d("inferno/contract.json")),be(s.join(v,"capabilities.json"),I),u||g("Created: "+d("inferno/capabilities.json")),Ce(s.join(v,"scenarios"),I),u||g("Created: "+d("inferno/scenarios/happy_path.json")),z(s.join(v,"CHANGELOG.md"),F),u||g("Created: "+d("inferno/CHANGELOG.md"));const E=ke(),X=s.join(E,"scripts","inferno-doc-gate.mjs"),B=s.join(o,"scripts","inferno-doc-gate.mjs");D(X,B,n,u);const Z=s.join(E,"scripts","inferno-install-hooks.mjs"),Q=s.join(o,"scripts","inferno-install-hooks.mjs");D(Z,Q,n,u);const ee=s.join(E,"ci","github-inferno-check.yml"),oe=s.join(V,"infernoflow-check.yml");if(D(ee,oe,n,u),Se(o,u),j&&ue({cwd:o,templatesRoot:E,force:n,silent:u,logOk:a=>{u||g(a)},logWarn:a=>{u||$(a)}}),O&&we({cwd:o,templatesRoot:E,force:n,silent:u,logOk:a=>{u||g(a)},logWarn:a=>{u||$(a)}}),r){const a=s.join(v,"context-state.json");let c={};try{c=JSON.parse(i.readFileSync(a,"utf8"))}catch{}const m=H(o,{language:U||void 0,framework:W||void 0,projectType:K||void 0});c.stack=m.developmentProfile,i.writeFileSync(a,JSON.stringify(c,null,2)+`
51
- `,"utf8"),u||g("Created: "+d("inferno/context-state.json"))}if(!u){q("infernoflow initialized!");const a=s.join(v,"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)&&!i.existsSync(a)&&(console.log(),console.log(` ${R("\u{1F4A1}")} ${J("Tip:")} connect an AI provider for explain, why, review, and changelog AI.`),console.log(` ${d("infernoflow ai setup")} \u2014 takes 60 seconds`)),ce([d("infernoflow status")+" \u2014 see your contract at a glance",d("infernoflow check")+" \u2014 validate everything",(r?"Review inferred baseline in ":"Edit ")+R("inferno/capabilities.json")+(r?" and refine IDs/titles":" to describe each capability in detail"),"Add more "+R("inferno/scenarios/*.json")+" files for edge cases","Add "+d("inferno:check")+" to your CI pipeline",...j?["Restart Cursor \u2014 hooks write assistant text to "+R("inferno/CONTEXT.draft.md"),"Promote when ready: "+d("npm run inferno:promote-draft -- --append-notes")]:[],...O?["Restart VS Code \u2014 Copilot hooks append prompts + assistant (from transcript) to "+R("inferno/CONTEXT.draft.md"),"Promote when ready: "+d("npm run inferno:promote-draft -- --append-notes")]:[],...!j&&!O?["Optional: "+d("infernoflow install-cursor-hooks")+" or "+d("infernoflow install-vscode-copilot-hooks")]:[]])}}export{Ge as initCommand};
51
+ `),c.scripts&&(info("Suggested package.json scripts for this template:"),Object.entries(c.scripts).forEach(([f,C])=>console.log(` ${L(f)}: ${O(C)}`)),console.log()),q(`Initialised from template ${L(d(w))} \u2014 ${L(String(h.length))} capabilities`),console.log(),info(`Run ${d("infernoflow vibe")} to start vibe coding mode`),console.log();return}const j=e.includes("--cursor-hooks"),x=e.includes("--vscode-copilot-hooks"),b=e.includes("--report-json"),S=e.includes("--report-json-only"),I=e.includes("--report-human-only"),U=W(e,"--lang"),M=W(e,"--framework"),K=W(e,"--project-type"),u=S;S&&I&&(console.error("Error: --report-json-only and --report-human-only cannot be used together."),process.exit(1)),u||le("init");const v=s.join(o,"inferno"),B=s.join(o,".github","workflows");i.existsSync(v)&&!t&&(u&&(console.log(JSON.stringify({ok:!1,error:"inferno_exists",hint:"Use --force to overwrite"},null,2)),process.exit(1)),R("inferno/ already exists. Use --force to overwrite."),console.log(),process.exit(0));const E=H(o),Y="CreateTask, ReadTasks, UpdateTask, ToggleComplete, DeleteTask";let P=E,T=Y.split(",").map(a=>a.trim());if(r){let c=D(o,{language:U||void 0,framework:M||void 0,projectType:K||void 0});if(!n&&!S){const h=G.createInterface({input:process.stdin,output:process.stdout}),f=c.developmentProfile||{},C=f.detected||{};console.log(O(` Review inferred development stack (press Enter to accept detected values)
52
+ `));const ie=await $(h,"Language",f.language||C.language||"unknown"),se=await $(h,"Framework",f.framework||C.framework||"unknown"),re=await $(h,"Project type",f.projectType||C.projectType||"unknown");h.close(),c=D(o,{language:ie,framework:se,projectType:re})}const m=c.capabilities,A=ue(m);S?console.log(JSON.stringify({mode:"adopt",policyId:E,inferredCapabilities:A,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(O(de(m))),console.log(),console.log(O(me(c))),console.log(),b&&!I&&(console.log(JSON.stringify({mode:"adopt",policyId:E,inferredCapabilities:A,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 F=await pe(m,n);P=E,T=F.map(h=>h.id)}else if(!n){const a=G.createInterface({input:process.stdin,output:process.stdout});console.log(O(` Press Enter to accept defaults
53
+ `)),P=await $(a,"Project / policy name",E),T=(await $(a,"Capabilities (comma-separated)",Y)).split(",").map(m=>m.trim()).filter(Boolean),a.close(),console.log()}if(i.mkdirSync(v,{recursive:!0}),r){const a=T.map(m=>({id:m,title:m.replace(/([A-Z])/g," $1").trim()})),c=D(o,{language:U||void 0,framework:M||void 0,projectType:K||void 0});fe(v,P,a,c),u||(g("Created: "+d("inferno/contract.json")),g("Created: "+d("inferno/capabilities.json")),g("Created: "+d("inferno/scenarios/adoption_baseline.json")),g("Created: "+d("inferno/adoption_profile.json")),g("Created: "+d("inferno/CHANGELOG.md")))}else xe(s.join(v,"contract.json"),P,T),u||g("Created: "+d("inferno/contract.json")),Ie(s.join(v,"capabilities.json"),T),u||g("Created: "+d("inferno/capabilities.json")),Te(s.join(v,"scenarios"),T),u||g("Created: "+d("inferno/scenarios/happy_path.json")),X(s.join(v,"CHANGELOG.md"),P),u||g("Created: "+d("inferno/CHANGELOG.md"));const N=Se(),Z=s.join(N,"scripts","inferno-doc-gate.mjs"),Q=s.join(o,"scripts","inferno-doc-gate.mjs");J(Z,Q,t,u);const ee=s.join(N,"scripts","inferno-install-hooks.mjs"),oe=s.join(o,"scripts","inferno-install-hooks.mjs");J(ee,oe,t,u);const ne=s.join(N,"ci","github-inferno-check.yml"),te=s.join(B,"infernoflow-check.yml");if(J(ne,te,t,u),be(o,u),j&&ye({cwd:o,templatesRoot:N,force:t,silent:u,logOk:a=>{u||g(a)},logWarn:a=>{u||R(a)}}),x&&ke({cwd:o,templatesRoot:N,force:t,silent:u,logOk:a=>{u||g(a)},logWarn:a=>{u||R(a)}}),r){const a=s.join(v,"context-state.json");let c={};try{c=JSON.parse(i.readFileSync(a,"utf8"))}catch{}const m=D(o,{language:U||void 0,framework:M||void 0,projectType:K||void 0});c.stack=m.developmentProfile,i.writeFileSync(a,JSON.stringify(c,null,2)+`
54
+ `,"utf8"),u||g("Created: "+d("inferno/context-state.json"))}if(!u){q("infernoflow initialized!");const a=s.join(v,"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)&&!i.existsSync(a)&&(console.log(),console.log(` ${_("\u{1F4A1}")} ${L("Tip:")} connect an AI provider for explain, why, review, and changelog AI.`),console.log(` ${d("infernoflow ai setup")} \u2014 takes 60 seconds`)),ae([d("infernoflow status")+" \u2014 see your contract at a glance",d("infernoflow check")+" \u2014 validate everything",(r?"Review inferred baseline in ":"Edit ")+_("inferno/capabilities.json")+(r?" and refine IDs/titles":" to describe each capability in detail"),"Add more "+_("inferno/scenarios/*.json")+" files for edge cases","Add "+d("inferno:check")+" to your CI pipeline",...j?["Restart Cursor \u2014 hooks write assistant text to "+_("inferno/CONTEXT.draft.md"),"Promote when ready: "+d("npm run inferno:promote-draft -- --append-notes")]:[],...x?["Restart VS Code \u2014 Copilot hooks append prompts + assistant (from transcript) to "+_("inferno/CONTEXT.draft.md"),"Promote when ready: "+d("npm run inferno:promote-draft -- --append-notes")]:[],...!j&&!x?["Optional: "+d("infernoflow install-cursor-hooks")+" or "+d("infernoflow install-vscode-copilot-hooks")]:[]])}}export{We as initCommand};
package/package.json CHANGED
@@ -1,61 +1,61 @@
1
- {
2
- "name": "infernoflow",
3
- "version": "0.43.6",
4
- "description": "Persistent memory for AI coding sessions \u2014 captures what agents can't infer from code alone. Works with Copilot, Cursor, Claude, and Windsurf.",
5
- "type": "module",
6
- "bin": {
7
- "infernoflow": "dist/bin/infernoflow.mjs"
8
- },
9
- "engines": {
10
- "node": ">=18"
11
- },
12
- "files": [
13
- "dist/bin",
14
- "dist/lib",
15
- "dist/templates",
16
- "README.md"
17
- ],
18
- "scripts": {
19
- "test": "node scripts/smoke.mjs && node scripts/json-smoke.mjs && node scripts/json-negative-smoke.mjs && node scripts/implement-smoke.mjs && node scripts/adopt-smoke.mjs && node scripts/pr-impact-smoke.mjs && node scripts/sync-smoke.mjs && node scripts/run-smoke.mjs",
20
- "test:help": "node bin/infernoflow.mjs --help",
21
- "build": "node build.mjs",
22
- "prepublishOnly": "echo skipping build",
23
- "inferno:promote-draft": "node scripts/inferno-promote-draft.mjs"
24
- },
25
- "dependencies": {},
26
- "keywords": [
27
- "ai",
28
- "ai-memory",
29
- "ai-context",
30
- "ai-coding",
31
- "session-memory",
32
- "persistent-memory",
33
- "agent-memory",
34
- "agent-handoff",
35
- "copilot",
36
- "cursor",
37
- "claude",
38
- "windsurf",
39
- "mcp",
40
- "mcp-server",
41
- "context-switching",
42
- "developer-tools",
43
- "cli",
44
- "infernoflow",
45
- "gotchas",
46
- "coding-assistant"
47
- ],
48
- "author": "infernoflow",
49
- "license": "MIT",
50
- "repository": {
51
- "type": "git",
52
- "url": "git+https://github.com/ronmiz/infernoflow.git"
53
- },
54
- "homepage": "https://infernoflow.dev",
55
- "bugs": {
56
- "url": "https://github.com/ronmiz/infernoflow/issues"
57
- },
58
- "devDependencies": {
59
- "esbuild": "^0.28.0"
60
- }
61
- }
1
+ {
2
+ "name": "infernoflow",
3
+ "version": "0.43.8",
4
+ "description": "Persistent memory for AI coding sessions \u2014 captures what agents can't infer from code alone. Works with Copilot, Cursor, Claude, and Windsurf.",
5
+ "type": "module",
6
+ "bin": {
7
+ "infernoflow": "dist/bin/infernoflow.mjs"
8
+ },
9
+ "engines": {
10
+ "node": ">=18"
11
+ },
12
+ "files": [
13
+ "dist/bin",
14
+ "dist/lib",
15
+ "dist/templates",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "test": "node scripts/smoke.mjs && node scripts/json-smoke.mjs && node scripts/json-negative-smoke.mjs && node scripts/implement-smoke.mjs && node scripts/adopt-smoke.mjs && node scripts/pr-impact-smoke.mjs && node scripts/sync-smoke.mjs && node scripts/run-smoke.mjs",
20
+ "test:help": "node bin/infernoflow.mjs --help",
21
+ "build": "node build.mjs",
22
+ "prepublishOnly": "echo skipping build",
23
+ "inferno:promote-draft": "node scripts/inferno-promote-draft.mjs"
24
+ },
25
+ "dependencies": {},
26
+ "keywords": [
27
+ "ai",
28
+ "ai-memory",
29
+ "ai-context",
30
+ "ai-coding",
31
+ "session-memory",
32
+ "persistent-memory",
33
+ "agent-memory",
34
+ "agent-handoff",
35
+ "copilot",
36
+ "cursor",
37
+ "claude",
38
+ "windsurf",
39
+ "mcp",
40
+ "mcp-server",
41
+ "context-switching",
42
+ "developer-tools",
43
+ "cli",
44
+ "infernoflow",
45
+ "gotchas",
46
+ "coding-assistant"
47
+ ],
48
+ "author": "infernoflow",
49
+ "license": "MIT",
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/ronmiz/infernoflow.git"
53
+ },
54
+ "homepage": "https://infernoflow.dev",
55
+ "bugs": {
56
+ "url": "https://github.com/ronmiz/infernoflow/issues"
57
+ },
58
+ "devDependencies": {
59
+ "esbuild": "^0.28.0"
60
+ }
61
+ }