infernoflow 0.10.18 → 0.10.20

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/CHANGELOG.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Changelog — infernoflow
2
2
 
3
- ## Unreleased
3
+ ## 0.10.20 — 2026-04-21
4
+
5
+ ### Added
6
+ - Release 0.10.20
7
+
8
+
9
+ ## 0.10.19 — 2026-04-21
4
10
 
5
11
  ## 0.10.12 — 2026-04-12
6
12
 
@@ -1,14 +1,28 @@
1
1
  #!/usr/bin/env node
2
- import{readFileSync as d}from"node:fs";import{dirname as m,join as f}from"node:path";import{fileURLToPath as u}from"node:url";import{bold as t,gray as e,red as a}from"../lib/ui/output.mjs";const h=m(u(import.meta.url)),y=JSON.parse(d(f(h,"..","package.json"),"utf8")),i=y.version||"0.0.0",c={setup:"One command to get fully operational \u2014 detects IDE, inits, installs hooks + MCP",init:"Scaffold inferno/ in your project (or adopt existing project)","install-cursor-hooks":"Install Cursor hooks: draft agent replies to inferno/CONTEXT.draft.md","install-vscode-copilot-hooks":"Install VS Code + Copilot agent hooks (Preview): draft to inferno/CONTEXT.draft.md",check:"Validate contract, capabilities, scenarios, changelog",status:"Show contract health at a glance","pr-impact":"Summarize PR impact on capabilities and docs",sync:"Run deterministic inferno sync flow",run:"One-command detect/propose/apply/validate flow","doc-gate":"Fail if code changed but docs were not updated",suggest:"Generate AI prompt + apply capability updates",implement:"Generate code-agent implementation prompt(s)",context:"Generate AI-ready context for new sessions","generate-skills":"Generate personalised Cursor rules + skill files from your developer profile"},l={setup:async o=>(await import("../lib/commands/setup.mjs")).setupCommand(o),init:async o=>(await import("../lib/commands/init.mjs")).initCommand(o),"install-cursor-hooks":async o=>(await import("../lib/commands/installCursorHooks.mjs")).installCursorHooksCommand(o),"install-vscode-copilot-hooks":async o=>(await import("../lib/commands/installVsCodeCopilotHooks.mjs")).installVsCodeCopilotHooksCommand(o),check:async o=>(await import("../lib/commands/check.mjs")).checkCommand(o),status:async o=>(await import("../lib/commands/status.mjs")).statusCommand(o),"pr-impact":async o=>(await import("../lib/commands/prImpact.mjs")).prImpactCommand(o),sync:async o=>(await import("../lib/commands/syncAuto.mjs")).syncCommand(o),run:async o=>(await import("../lib/commands/run.mjs")).runCommand(o),suggest:async o=>(await import("../lib/commands/suggest.mjs")).suggestCommand(o),implement:async o=>(await import("../lib/commands/implement.mjs")).implementCommand(o),context:async o=>(await import("../lib/commands/context.mjs")).contextCommand(o),"doc-gate":async o=>(await import("../lib/commands/docGate.mjs")).docGateCommand(o),"generate-skills":async o=>(await import("../lib/commands/generateSkills.mjs")).generateSkillsCommand(o)};function g(){const o=Object.keys(c),s=Math.max(...o.map(r=>r.length),8)+1;return Object.entries(c).map(([r,p])=>` ${r.padEnd(s," ")}${p}`).join(`
2
+ import{readFileSync as m}from"node:fs";import{dirname as d,join as f}from"node:path";import{fileURLToPath as u}from"node:url";import{bold as t,gray as e,red as s}from"../lib/ui/output.mjs";const h=d(u(import.meta.url)),g=JSON.parse(m(f(h,"..","package.json"),"utf8")),r=g.version||"0.0.0",c={publish:"Bump version, update changelog, build, npm publish, git commit + push in one shot",diff:"Show what capabilities changed since the last git tag (or any ref)",setup:"One command to get fully operational \u2014 detects IDE, inits, installs hooks + MCP",init:"Scaffold inferno/ in your project (or adopt existing project)","install-cursor-hooks":"Install Cursor hooks: draft agent replies to inferno/CONTEXT.draft.md","install-vscode-copilot-hooks":"Install VS Code + Copilot agent hooks (Preview): draft to inferno/CONTEXT.draft.md",check:"Validate contract, capabilities, scenarios, changelog",status:"Show contract health at a glance","pr-impact":"Summarize PR impact on capabilities and docs",sync:"Run deterministic inferno sync flow",run:"One-command detect/propose/apply/validate flow","doc-gate":"Fail if code changed but docs were not updated",suggest:"Generate AI prompt + apply capability updates",implement:"Generate code-agent implementation prompt(s)",context:"Generate AI-ready context for new sessions","generate-skills":"Generate personalised Cursor rules + skill files from your developer profile"},l={publish:async o=>(await import("../lib/commands/publish.mjs")).publishCommand(o),diff:async o=>(await import("../lib/commands/diff.mjs")).diffCommand(o),setup:async o=>(await import("../lib/commands/setup.mjs")).setupCommand(o),init:async o=>(await import("../lib/commands/init.mjs")).initCommand(o),"install-cursor-hooks":async o=>(await import("../lib/commands/installCursorHooks.mjs")).installCursorHooksCommand(o),"install-vscode-copilot-hooks":async o=>(await import("../lib/commands/installVsCodeCopilotHooks.mjs")).installVsCodeCopilotHooksCommand(o),check:async o=>(await import("../lib/commands/check.mjs")).checkCommand(o),status:async o=>(await import("../lib/commands/status.mjs")).statusCommand(o),"pr-impact":async o=>(await import("../lib/commands/prImpact.mjs")).prImpactCommand(o),sync:async o=>(await import("../lib/commands/syncAuto.mjs")).syncCommand(o),run:async o=>(await import("../lib/commands/run.mjs")).runCommand(o),suggest:async o=>(await import("../lib/commands/suggest.mjs")).suggestCommand(o),implement:async o=>(await import("../lib/commands/implement.mjs")).implementCommand(o),context:async o=>(await import("../lib/commands/context.mjs")).contextCommand(o),"doc-gate":async o=>(await import("../lib/commands/docGate.mjs")).docGateCommand(o),"generate-skills":async o=>(await import("../lib/commands/generateSkills.mjs")).generateSkillsCommand(o)};function y(){const o=Object.keys(c),a=Math.max(...o.map(i=>i.length),8)+1;return Object.entries(c).map(([i,p])=>` ${i.padEnd(a," ")}${p}`).join(`
3
3
  `)}const w=`
4
- ${t("\u{1F525} infernoflow")} ${e("v"+i)}
4
+ ${t("\u{1F525} infernoflow")} ${e("v"+r)}
5
5
  ${e("The forge for liquid code \u2014 keep every AI session in sync")}
6
6
 
7
7
  ${t("Usage:")}
8
8
  infernoflow <command> [options]
9
9
 
10
10
  ${t("Commands:")}
11
- ${g()}
11
+ ${y()}
12
+
13
+ ${t("diff options:")}
14
+ --ref <tag|commit> Compare against a specific ref (default: last git tag)
15
+ --summary One-liner count only
16
+ --json Machine-readable output
17
+
18
+ ${t("publish options:")}
19
+ --bump patch|minor|major Version bump type (default: patch)
20
+ --skip-build Skip the build step
21
+ --skip-tests Skip smoke tests
22
+ --skip-push Commit but don't git push
23
+ --tag Also create a git tag vX.Y.Z
24
+ --dry-run Print all steps without executing
25
+ --yes, -y Non-interactive (skip confirmation prompt)
12
26
 
13
27
  ${t("setup options:")}
14
28
  --yes, -y Skip prompts (non-interactive)
@@ -72,7 +86,7 @@ ${g()}
72
86
  ${e("pr-impact --json")}
73
87
  ${e("sync --auto --json")}
74
88
  ${e('run "task" --json')}
75
- `;import*as k from"node:fs";import*as C from"node:path";try{const o=C.join(process.cwd(),"inferno");if(k.existsSync(o)){const{observeCommandStart:s}=await import("../lib/learning/observe.mjs"),r=process.argv[2];r&&!r.startsWith("-")&&s(o,r)}}catch{}const[,,n,...v]=process.argv;(!n||n==="--help"||n==="-h")&&(console.log(w),process.exit(0)),(n==="--version"||n==="-v")&&(console.log(i),process.exit(0));const b=Object.keys(l);b.includes(n)||(console.error(a(`
89
+ `;import*as k from"node:fs";import*as C from"node:path";try{const o=C.join(process.cwd(),"inferno");if(k.existsSync(o)){const{observeCommandStart:a}=await import("../lib/learning/observe.mjs"),i=process.argv[2];i&&!i.startsWith("-")&&a(o,i)}}catch{}const[,,n,...b]=process.argv;(!n||n==="--help"||n==="-h")&&(console.log(w),process.exit(0)),(n==="--version"||n==="-v")&&(console.log(r),process.exit(0));const v=Object.keys(l);v.includes(n)||(console.error(s(`
76
90
  Unknown command: ${n}`)),console.error(e(`Run: infernoflow --help
77
- `)),process.exit(1));const $=[n,...v];l[n]($).catch(o=>{console.error(a(`
91
+ `)),process.exit(1));const $=[n,...b];l[n]($).catch(o=>{console.error(s(`
78
92
  Error: `)+o.message),process.exit(1)});
@@ -0,0 +1,5 @@
1
+ import*as u from"node:fs";import*as v from"node:path";import{execSync as C}from"node:child_process";import{header as N,ok as J,fail as m,bold as d,cyan as k,gray as c,green as $,red as y,yellow as x}from"../ui/output.mjs";function h(n,e){try{return C(n,{cwd:e,encoding:"utf8",stdio:["ignore","pipe","pipe"]}).trim()}catch{return null}}function D(n){const e=h("git describe --tags --abbrev=0",n);return e||null}function j(n,e,o){return h(`git show "${n}:${e}"`,o)}function z(n){return h("git rev-parse --abbrev-ref HEAD",n)||"HEAD"}function O(n,e){const o=h(`git log -1 --format=%ci "${e}"`,n),t=o?o.slice(0,10):null;return t?`${e} ${c("("+t+")")}`:e}function b(n){if(!n)return null;try{return(JSON.parse(n).capabilities||[]).map(t=>typeof t=="string"?{id:t,title:t}:{id:t.id||t,title:t.title||t.id||String(t),since:t.since,status:t.status})}catch{return null}}function _(n,e,o){const t=j(n,`${e}/capabilities.json`,o);if(t)return b(t);const g=j(n,`${e}/contract.json`,o);return b(g)}function E(n){const e=v.join(n,"capabilities.json"),o=v.join(n,"contract.json");return u.existsSync(e)?b(u.readFileSync(e,"utf8")):u.existsSync(o)?b(u.readFileSync(o,"utf8")):null}function R(n,e){const o=new Map(n.map(i=>[i.id,i])),t=new Map(e.map(i=>[i.id,i])),g=e.filter(i=>!o.has(i.id)),s=n.filter(i=>!t.has(i.id)),l=[];for(const i of e){const f=o.get(i.id);if(!f)continue;const a=[];f.title!==i.title&&a.push({field:"title",from:f.title,to:i.title}),f.status!==i.status&&(f.status||i.status)&&a.push({field:"status",from:f.status||"\u2014",to:i.status||"\u2014"}),a.length&&l.push({id:i.id,changes:a})}return{added:g,removed:s,changed:l}}function H(n){if(n.length){console.log(`
2
+ ${d($("+ Added"))} ${c("("+n.length+")")}`);for(const e of n){const o=e.since?c(" since "+e.since):"";console.log(` ${$("+")} ${d(e.id)} ${c(e.title)}${o}`)}}}function M(n){if(n.length){console.log(`
3
+ ${d(y("- Removed"))} ${c("("+n.length+")")}`);for(const e of n)console.log(` ${y("-")} ${d(e.id)} ${c(e.title)}`)}}function F(n){if(n.length){console.log(`
4
+ ${d(x("~ Changed"))} ${c("("+n.length+")")}`);for(const e of n){console.log(` ${x("~")} ${d(e.id)}`);for(const o of e.changes)console.log(` ${c(o.field+":")} ${y(o.from)} \u2192 ${$(o.to)}`)}}}function U(n){n!==0&&console.log(`
5
+ ${c(" Unchanged "+n)}`)}function w(n,e){const o=[];n.added.length&&o.push($("+"+n.added.length+" added")),n.removed.length&&o.push(y("-"+n.removed.length+" removed")),n.changed.length&&o.push(x("~"+n.changed.length+" changed")),o.length||o.push(c("no changes")),console.log(` ${o.join(" ")} ${c("vs "+e)}`)}async function G(n){const e=n.slice(1),o=e.includes("--json"),t=e.includes("--summary"),g=e.indexOf("--ref");let s=g!==-1?e[g+1]:null;const l=process.cwd(),i=v.join(l,"inferno"),f="inferno";o||N("diff"),u.existsSync(i)||(o&&(console.log(JSON.stringify({ok:!1,error:"inferno_not_found"})),process.exit(1)),m("inferno/ not found","Run: infernoflow init"),process.exit(1)),s||(s=D(l),s||(h("git rev-parse HEAD~1",l)?s="HEAD~1":(o&&(console.log(JSON.stringify({ok:!1,error:"no_ref",hint:"No git tags found and no parent commit. Use --ref <commit>"})),process.exit(1)),m("No git tags found","Create a tag first: git tag v0.1.0 or use --ref <commit>"),process.exit(1))));const a=E(i);a||(o&&(console.log(JSON.stringify({ok:!1,error:"no_capabilities_found"})),process.exit(1)),m("No capabilities.json or contract.json found in inferno/"),process.exit(1));const p=_(s,f,l);p||(o&&(console.log(JSON.stringify({ok:!1,error:"ref_not_found",ref:s,hint:"Does inferno/capabilities.json exist at that ref?"})),process.exit(1)),m(`Could not read capabilities at ${s}`,"The inferno/ directory may not exist at that ref"),process.exit(1));const r=R(p,a),S=a.length-r.added.length-r.changed.length;if(o){console.log(JSON.stringify({ok:!0,ref:s,current:a.length,previous:p.length,added:r.added,removed:r.removed,changed:r.changed,unchanged:S},null,2));return}const A=O(l,s);if(console.log(),console.log(` Comparing ${d(k("current"))} vs ${d(A)}`),console.log(` ${c(a.length+" capabilities now / "+p.length+" before")}`),t){w(r,s),console.log();return}if(!(r.added.length||r.removed.length||r.changed.length)){console.log(),J("No capability changes since "+s),console.log();return}H(r.added),M(r.removed),F(r.changed),U(S),console.log(),w(r,s),console.log()}export{G as diffCommand};
@@ -0,0 +1,21 @@
1
+ import*as c from"node:fs";import*as f from"node:path";import{execSync as b}from"node:child_process";import{fileURLToPath as F}from"node:url";import{header as L,ok as o,fail as k,warn as l,info as u,done as O,bold as S,cyan as C,gray as n,green as T}from"../ui/output.mjs";const U=f.dirname(F(import.meta.url)),g=f.resolve(U,"../..");function a(r,i={}){return b(r,{cwd:g,encoding:"utf8",stdio:i.silent?["ignore","pipe","pipe"]:["inherit","inherit","inherit"],...i})}function x(r){return b(r,{cwd:g,encoding:"utf8",stdio:["ignore","pipe","pipe"]}).trim()}function E(r,i){const e=r.split(".").map(Number);return i==="major"?(e[0]++,e[1]=0,e[2]=0):i==="minor"?(e[1]++,e[2]=0):e[2]++,e.join(".")}function h(){return new Date().toISOString().slice(0,10)}function H(r,i){if(!c.existsSync(r))return c.writeFileSync(r,`# Changelog \u2014 infernoflow
2
+
3
+ ## ${i} \u2014 ${h()}
4
+
5
+ ### Added
6
+ - Release ${i}
7
+ `),!0;let e=c.readFileSync(r,"utf8");if(/^## Unreleased/im.test(e))return e=e.replace(/^## Unreleased.*$/im,`## ${i} \u2014 ${h()}`),c.writeFileSync(r,e),!0;const p=/^# .+$/im;return p.test(e)?(e=e.replace(p,y=>`${y}
8
+
9
+ ## ${i} \u2014 ${h()}
10
+
11
+ ### Added
12
+ - Release ${i}
13
+ `),c.writeFileSync(r,e),!0):(c.writeFileSync(r,`## ${i} \u2014 ${h()}
14
+
15
+ ### Added
16
+ - Release ${i}
17
+
18
+ ${e}`),!0)}function _(){try{return x("git status --porcelain").length>0}catch{return!1}}function J(){try{return x("git config user.email"),!0}catch{return!1}}async function M(r){const i=r.slice(1),e=i.includes("--dry-run"),p=i.includes("--skip-build"),y=i.includes("--skip-tests"),A=i.includes("--skip-push"),j=i.includes("--tag"),N=i.includes("--yes")||i.includes("-y"),G=i.indexOf("--bump"),m=G!==-1&&i[G+1]||"patch";["patch","minor","major"].includes(m)||(console.error(` Invalid --bump value: ${m}. Must be patch, minor, or major.`),process.exit(1)),L("infernoflow publish"),e&&l("DRY RUN \u2014 no files will be written, no commands executed");const v=f.join(g,"package.json"),$=JSON.parse(c.readFileSync(v,"utf8")),w=$.version,t=E(w,m);if(console.log(),console.log(` ${n("current")} ${S(w)}`),console.log(` ${n("new ")} ${S(T(t))} ${n("("+m+" bump)")}`),console.log(),!N&&!e){process.stdout.write(` Publish ${S(C("infernoflow@"+t))} to npm? [y/N] `);let s=!1;try{const d=b("bash -c 'read -r ans </dev/tty; echo $ans'",{encoding:"utf8",stdio:["inherit","pipe","inherit"]}).trim().toLowerCase();s=d==="y"||d==="yes"}catch{s=!1}console.log(),s||(console.log(n(` Aborted.
19
+ `)),process.exit(0))}u(`Bumping package.json ${n(w+" \u2192 "+t)}`),e?o(n("[dry] would write package.json")):($.version=t,c.writeFileSync(v,JSON.stringify($,null,4)+`
20
+ `),o("package.json updated"));const R=f.join(g,"CHANGELOG.md");if(u("Updating CHANGELOG.md"),e?o(n("[dry] would update CHANGELOG.md")):(H(R,t),o("CHANGELOG.md updated")),p)l("Skipping build (--skip-build)");else if(u("Running build "+n("node build.mjs")),e)o(n("[dry] would run: node build.mjs"));else try{a("node build.mjs",{silent:!1}),o("Build succeeded")}catch(s){k("Build failed",s.message),process.exit(1)}if(y)l("Skipping tests (--skip-tests)");else if(u("Running smoke tests"),e)o(n("[dry] would run: npm test"));else try{a("npm test",{silent:!1}),o("All smoke tests passed")}catch{k("Smoke tests failed","Fix tests or re-run with --skip-tests"),process.exit(1)}if(u(`Publishing to npm ${n("infernoflow@"+t)}`),e)o(n("[dry] would run: npm publish"));else try{a("npm publish",{silent:!1}),o(`Published infernoflow@${t}`)}catch(s){k("npm publish failed",s.message||"Check npm credentials"),l("Continuing to git commit despite publish failure")}if(u("Committing version bump"),e)o(n(`[dry] would commit: chore: release ${t}`));else try{a(`git add ${["package.json","CHANGELOG.md"].join(" ")}`,{silent:!1});const d=`chore: release ${t}`;a(`git commit -m "${d}"`,{silent:!1}),o(`Committed: ${n(d)}`)}catch(s){l(`Git commit failed: ${s.message}`),l('You can commit manually: git add package.json CHANGELOG.md && git commit -m "chore: release '+t+'"')}if(j)if(u(`Creating git tag ${n("v"+t)}`),e)o(n(`[dry] would tag: v${t}`));else try{a(`git tag v${t}`,{silent:!1}),o(`Tagged v${t}`)}catch(s){l(`Git tag failed: ${s.message}`)}if(A)l("Skipping push (--skip-push)");else if(u("Pushing to origin"),e)o(n("[dry] would run: git push"));else try{const s=j?`git push && git push origin v${t}`:"git push";a(s,{silent:!1}),o("Pushed to origin")}catch(s){l(`Git push failed: ${s.message}`),l("Push manually: git push")}console.log(),e?O(`Dry run complete \u2014 would have published infernoflow@${t}`):(O(`infernoflow@${t} published, committed, and pushed`),console.log(` ${C("npm:")} https://www.npmjs.com/package/infernoflow`),console.log(` ${C("git:")} ${n("chore: release "+t)}
21
+ `))}export{M as publishCommand};
package/package.json CHANGED
@@ -1,48 +1,48 @@
1
- {
2
- "name": "infernoflow",
3
- "version": "0.10.18",
4
- "description": "The forge for liquid code — keep capabilities, contracts, and docs in sync.",
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
- "CHANGELOG.md"
18
- ],
19
- "scripts": {
20
- "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",
21
- "test:help": "node bin/infernoflow.mjs --help",
22
- "build": "node build.mjs",
23
- "prepublishOnly": "node build.mjs"
24
- },
25
- "keywords": [
26
- "cli",
27
- "capabilities",
28
- "contract",
29
- "documentation",
30
- "ai",
31
- "liquid-code",
32
- "dx",
33
- "developer-tools"
34
- ],
35
- "author": "infernoflow",
36
- "license": "MIT",
37
- "repository": {
38
- "type": "git",
39
- "url": "git+https://github.com/ronmiz/infernoflow.git"
40
- },
41
- "homepage": "https://github.com/ronmiz/infernoflow#readme",
42
- "bugs": {
43
- "url": "https://github.com/ronmiz/infernoflow/issues"
44
- },
45
- "devDependencies": {
46
- "esbuild": "^0.28.0"
47
- }
48
- }
1
+ {
2
+ "name": "infernoflow",
3
+ "version": "0.10.20",
4
+ "description": "The forge for liquid code — keep capabilities, contracts, and docs in sync.",
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
+ "CHANGELOG.md"
18
+ ],
19
+ "scripts": {
20
+ "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",
21
+ "test:help": "node bin/infernoflow.mjs --help",
22
+ "build": "node build.mjs",
23
+ "prepublishOnly": "node build.mjs"
24
+ },
25
+ "keywords": [
26
+ "cli",
27
+ "capabilities",
28
+ "contract",
29
+ "documentation",
30
+ "ai",
31
+ "liquid-code",
32
+ "dx",
33
+ "developer-tools"
34
+ ],
35
+ "author": "infernoflow",
36
+ "license": "MIT",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/ronmiz/infernoflow.git"
40
+ },
41
+ "homepage": "https://github.com/ronmiz/infernoflow#readme",
42
+ "bugs": {
43
+ "url": "https://github.com/ronmiz/infernoflow/issues"
44
+ },
45
+ "devDependencies": {
46
+ "esbuild": "^0.28.0"
47
+ }
48
+ }