infernoflow 0.33.1 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +208 -120
  2. package/dist/bin/infernoflow.mjs +271 -85
  3. package/dist/lib/adopters/angular.mjs +128 -1
  4. package/dist/lib/adopters/css.mjs +111 -1
  5. package/dist/lib/adopters/react.mjs +104 -1
  6. package/dist/lib/ai/ideDetection.mjs +31 -1
  7. package/dist/lib/ai/localProvider.mjs +88 -1
  8. package/dist/lib/ai/providerRouter.mjs +295 -2
  9. package/dist/lib/commands/adopt.mjs +869 -20
  10. package/dist/lib/commands/adoptWizard.mjs +320 -9
  11. package/dist/lib/commands/agent.mjs +191 -5
  12. package/dist/lib/commands/ai.mjs +407 -2
  13. package/dist/lib/commands/ask.mjs +299 -0
  14. package/dist/lib/commands/audit.mjs +300 -13
  15. package/dist/lib/commands/changelog.mjs +594 -26
  16. package/dist/lib/commands/check.mjs +184 -3
  17. package/dist/lib/commands/ci.mjs +208 -3
  18. package/dist/lib/commands/claudeMd.mjs +139 -28
  19. package/dist/lib/commands/cloud.mjs +521 -5
  20. package/dist/lib/commands/context.mjs +346 -34
  21. package/dist/lib/commands/coverage.mjs +282 -2
  22. package/dist/lib/commands/dashboard.mjs +635 -123
  23. package/dist/lib/commands/demo.mjs +465 -8
  24. package/dist/lib/commands/diff.mjs +274 -5
  25. package/dist/lib/commands/docGate.mjs +81 -2
  26. package/dist/lib/commands/doctor.mjs +321 -3
  27. package/dist/lib/commands/explain.mjs +438 -8
  28. package/dist/lib/commands/export.mjs +239 -10
  29. package/dist/lib/commands/generateSkills.mjs +163 -38
  30. package/dist/lib/commands/graph.mjs +378 -11
  31. package/dist/lib/commands/health.mjs +309 -2
  32. package/dist/lib/commands/impact.mjs +325 -2
  33. package/dist/lib/commands/implement.mjs +103 -7
  34. package/dist/lib/commands/init.mjs +545 -23
  35. package/dist/lib/commands/installCursorHooks.mjs +36 -1
  36. package/dist/lib/commands/installVsCodeCopilotHooks.mjs +37 -1
  37. package/dist/lib/commands/link.mjs +342 -2
  38. package/dist/lib/commands/log.mjs +164 -16
  39. package/dist/lib/commands/monorepo.mjs +428 -4
  40. package/dist/lib/commands/notify.mjs +258 -4
  41. package/dist/lib/commands/onboard.mjs +296 -4
  42. package/dist/lib/commands/prComment.mjs +361 -2
  43. package/dist/lib/commands/prImpact.mjs +157 -2
  44. package/dist/lib/commands/publish.mjs +316 -15
  45. package/dist/lib/commands/recap.mjs +359 -0
  46. package/dist/lib/commands/report.mjs +272 -28
  47. package/dist/lib/commands/review.mjs +223 -9
  48. package/dist/lib/commands/run.mjs +336 -8
  49. package/dist/lib/commands/scaffold.mjs +419 -54
  50. package/dist/lib/commands/scan.mjs +1118 -5
  51. package/dist/lib/commands/scout.mjs +291 -2
  52. package/dist/lib/commands/setup.mjs +310 -5
  53. package/dist/lib/commands/share.mjs +196 -13
  54. package/dist/lib/commands/snapshot.mjs +383 -3
  55. package/dist/lib/commands/stability.mjs +293 -2
  56. package/dist/lib/commands/stats.mjs +402 -0
  57. package/dist/lib/commands/status.mjs +172 -4
  58. package/dist/lib/commands/suggest.mjs +563 -21
  59. package/dist/lib/commands/switch.mjs +310 -9
  60. package/dist/lib/commands/syncAuto.mjs +96 -1
  61. package/dist/lib/commands/synthesize.mjs +228 -10
  62. package/dist/lib/commands/teamSync.mjs +388 -2
  63. package/dist/lib/commands/test.mjs +363 -6
  64. package/dist/lib/commands/theme.mjs +195 -18
  65. package/dist/lib/commands/upgrade.mjs +153 -0
  66. package/dist/lib/commands/version.mjs +282 -2
  67. package/dist/lib/commands/vibe.mjs +357 -7
  68. package/dist/lib/commands/watch.mjs +203 -4
  69. package/dist/lib/commands/why.mjs +358 -4
  70. package/dist/lib/cursorHooksInstall.mjs +60 -1
  71. package/dist/lib/draftToolingInstall.mjs +68 -7
  72. package/dist/lib/git/detect-drift.mjs +208 -4
  73. package/dist/lib/learning/adapt.mjs +101 -6
  74. package/dist/lib/learning/observe.mjs +119 -1
  75. package/dist/lib/learning/patternDetector.mjs +298 -1
  76. package/dist/lib/learning/profile.mjs +279 -2
  77. package/dist/lib/learning/skillSynthesizer.mjs +145 -24
  78. package/dist/lib/templates/index.mjs +131 -1
  79. package/dist/lib/theme/scanner.mjs +343 -4
  80. package/dist/lib/ui/errors.mjs +142 -1
  81. package/dist/lib/ui/output.mjs +72 -6
  82. package/dist/lib/ui/prompts.mjs +147 -6
  83. package/dist/lib/vsCodeCopilotHooksInstall.mjs +42 -1
  84. package/package.json +1 -1
@@ -1,21 +1,170 @@
1
1
  #!/usr/bin/env node
2
- import{readFileSync as d}from"node:fs";import{dirname as m,join as u}from"node:path";import{fileURLToPath as h}from"node:url";import{bold as t,gray as a,red as s}from"../lib/ui/output.mjs";const f=m(h(import.meta.url)),y=JSON.parse(d(u(f,"..","..","package.json"),"utf8")),r=y.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)",changelog:"Draft a changelog entry from commits since the last tag",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",synthesize:"Auto-detect workflow patterns and synthesize reusable skills + agents",agent:"Manage and run auto-synthesized agents (list | run | show | delete)",version:"Smart semver bump recommendation based on capability changes (--apply to write)","pr-comment":"Post capability drift analysis as a GitHub PR comment (works in CI automatically)",dashboard:"Launch local web dashboard on localhost:7337 \u2014 live contract health, capabilities, agents","team-sync":"Sync capability contract across a team via a shared git branch (push | pull | status | init)",onboard:"Interactive onboarding wizard for new developers \u2014 explains infernoflow in 5 minutes",cloud:"Sync capability contracts via infernoflow cloud (init | push | pull | status | dashboard)",share:"Generate a public read-only HTML snapshot of your capability contract",watch:"Watch source files and run suggest automatically on save",ci:"CI-native check: GitHub Actions annotations, GitLab code quality, exit codes",notify:"Post capability drift summary to Slack or Discord",report:"Generate a weekly/monthly HTML or Markdown report of capability activity",monorepo:"Manage infernoflow across monorepo packages (init | list | status | diff | sync)",link:"Link capabilities to Jira, Linear, or GitHub Issues tickets",audit:"Classify capabilities by sensitivity (auth, payment, PII, admin) and generate security surface map",scout:"Scan source files for undocumented capabilities not yet in the contract",export:"Export contract to OpenAPI, Backstage catalog-info.yaml, CSV, or Markdown",snapshot:"Save/diff/restore named snapshots of the capability contract",health:"Compute a 0\u2013100 health score across coverage, docs, freshness, completeness, drift",vibe:"Vibe coding mode \u2014 watches files, auto-syncs contract, regenerates context on every save",adopt:"Interactive wizard to adopt infernoflow in an existing project (detect \u2192 review \u2192 wire up)",doctor:"Diagnose your infernoflow setup \u2014 checks Node, git, contract, AI providers, MCP, hooks",coverage:"Map test files to capabilities \u2014 show which caps have test coverage and which don't",review:"AI-powered capability impact review for staged or recent git changes",scan:"Deep AST scan \u2014 reads actual function bodies, extracts calls, DB ops, external services",graph:"Build capability dependency graph \u2014 shows which caps call which, detects breaking changes",stability:"Show solid/liquid stability level for every capability (frozen/stable/experimental)",freeze:"Mark a capability as frozen (solid) \u2014 AI will not modify it without explicit instruction",thaw:"Reset a capability to experimental (liquid) \u2014 free to evolve",why:"Given a file or function name \u2014 show which capability it serves, scenarios, stability, and git history",impact:"Blast radius analysis \u2014 see every cap, scenario, and risk level affected before you change anything",scaffold:"Generate a new capability \u2014 source skeleton, contract registration, and placeholder scenario in one command",explain:"AI narrative about a capability \u2014 what it does, why it exists, what's risky, and what to test",test:"Run registered scenarios for a capability \u2014 auto-generates a smoke harness if no test runner is configured",ai:"Manage AI providers \u2014 setup, status, test connection (subcommands: setup | status | test | clear)",demo:"Interactive walkthrough \u2014 scaffolds a sample project and runs the full capability chain end-to-end",log:"Append to session memory (decisions, gotchas, failed attempts, theme changes) \u2014 what AI can't infer from code",theme:"Scan fonts, colors, and CSS variables \u2014 write inferno/theme.json so AI always matches the design system",switch:"Generate a handoff summary when switching AI agents \u2014 paste into the next session so nothing is lost"},l={publish:async e=>(await import("../lib/commands/publish.mjs")).publishCommand(e),diff:async e=>(await import("../lib/commands/diff.mjs")).diffCommand(e),changelog:async e=>(await import("../lib/commands/changelog.mjs")).changelogCommand(e),setup:async e=>(await import("../lib/commands/setup.mjs")).setupCommand(e),init:async e=>(await import("../lib/commands/init.mjs")).initCommand(e),"install-cursor-hooks":async e=>(await import("../lib/commands/installCursorHooks.mjs")).installCursorHooksCommand(e),"install-vscode-copilot-hooks":async e=>(await import("../lib/commands/installVsCodeCopilotHooks.mjs")).installVsCodeCopilotHooksCommand(e),check:async e=>(await import("../lib/commands/check.mjs")).checkCommand(e),status:async e=>(await import("../lib/commands/status.mjs")).statusCommand(e),"pr-impact":async e=>(await import("../lib/commands/prImpact.mjs")).prImpactCommand(e),sync:async e=>(await import("../lib/commands/syncAuto.mjs")).syncCommand(e),run:async e=>(await import("../lib/commands/run.mjs")).runCommand(e),suggest:async e=>(await import("../lib/commands/suggest.mjs")).suggestCommand(e),implement:async e=>(await import("../lib/commands/implement.mjs")).implementCommand(e),context:async e=>(await import("../lib/commands/context.mjs")).contextCommand(e),"doc-gate":async e=>(await import("../lib/commands/docGate.mjs")).docGateCommand(e),"generate-skills":async e=>(await import("../lib/commands/generateSkills.mjs")).generateSkillsCommand(e),synthesize:async e=>(await import("../lib/commands/synthesize.mjs")).synthesizeCommand(e),agent:async e=>(await import("../lib/commands/agent.mjs")).agentCommand(e),version:async e=>(await import("../lib/commands/version.mjs")).versionCommand(e),"pr-comment":async e=>(await import("../lib/commands/prComment.mjs")).prCommentCommand(e),dashboard:async e=>(await import("../lib/commands/dashboard.mjs")).dashboardCommand(e),"team-sync":async e=>(await import("../lib/commands/teamSync.mjs")).teamSyncCommand(e),onboard:async e=>(await import("../lib/commands/onboard.mjs")).onboardCommand(e),cloud:async e=>(await import("../lib/commands/cloud.mjs")).cloudCommand(e),share:async e=>(await import("../lib/commands/share.mjs")).shareCommand(e),watch:async e=>(await import("../lib/commands/watch.mjs")).watchCommand(e),ci:async e=>(await import("../lib/commands/ci.mjs")).ciCommand(e),notify:async e=>(await import("../lib/commands/notify.mjs")).notifyCommand(e),report:async e=>(await import("../lib/commands/report.mjs")).reportCommand(e),monorepo:async e=>(await import("../lib/commands/monorepo.mjs")).monorepoCommand(e),link:async e=>(await import("../lib/commands/link.mjs")).linkCommand(e),audit:async e=>(await import("../lib/commands/audit.mjs")).auditCommand(e),scout:async e=>(await import("../lib/commands/scout.mjs")).scoutCommand(e),export:async e=>(await import("../lib/commands/export.mjs")).exportCommand(e),snapshot:async e=>(await import("../lib/commands/snapshot.mjs")).snapshotCommand(e),health:async e=>(await import("../lib/commands/health.mjs")).healthCommand(e),vibe:async e=>(await import("../lib/commands/vibe.mjs")).vibeCommand(e),adopt:async e=>(await import("../lib/commands/adoptWizard.mjs")).adoptWizardCommand(e),doctor:async e=>(await import("../lib/commands/doctor.mjs")).doctorCommand(e),coverage:async e=>(await import("../lib/commands/coverage.mjs")).coverageCommand(e),review:async e=>(await import("../lib/commands/review.mjs")).reviewCommand(e),scan:async e=>(await import("../lib/commands/scan.mjs")).scanCommand(e),graph:async e=>(await import("../lib/commands/graph.mjs")).graphCommand(e),stability:async e=>(await import("../lib/commands/stability.mjs")).stabilityCommand(e),freeze:async e=>(await import("../lib/commands/stability.mjs")).freezeCommand(e),thaw:async e=>(await import("../lib/commands/stability.mjs")).thawCommand(e),why:async e=>(await import("../lib/commands/why.mjs")).whyCommand(e),impact:async e=>(await import("../lib/commands/impact.mjs")).impactCommand(e),scaffold:async e=>(await import("../lib/commands/scaffold.mjs")).scaffoldCommand(e),explain:async e=>(await import("../lib/commands/explain.mjs")).explainCommand(e),test:async e=>(await import("../lib/commands/test.mjs")).testCommand(e),ai:async e=>(await import("../lib/commands/ai.mjs")).aiCommand(e),demo:async e=>(await import("../lib/commands/demo.mjs")).demoCommand(e),log:async e=>(await import("../lib/commands/log.mjs")).logCommand(e),theme:async e=>(await import("../lib/commands/theme.mjs")).themeCommand(e),switch:async e=>(await import("../lib/commands/switch.mjs")).switchCommand(e)};function w(){const e=Object.keys(c),n=Math.max(...e.map(i=>i.length),8)+1;return Object.entries(c).map(([i,p])=>` ${i.padEnd(n," ")}${p}`).join(`
3
- `)}const g=`
4
- ${t("\u{1F525} infernoflow")} ${a("v"+r)}
5
- ${a("The forge for liquid code \u2014 keep every AI session in sync")}
6
-
7
- ${t("Usage:")}
2
+ import { readFileSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { bold, gray, cyan, red } from "../lib/ui/output.mjs";
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ // When installed globally: __dirname = .../infernoflow/dist/bin
9
+ // Root package.json lives two levels up at .../infernoflow/package.json
10
+ // npm always includes the root package.json, so this path is always reliable.
11
+ const pkg = JSON.parse(readFileSync(join(__dirname, "..", "..", "package.json"), "utf8"));
12
+ const VERSION = pkg.version || "0.0.0";
13
+ const COMMAND_DESCRIPTIONS = {
14
+ publish: "Bump version, update changelog, build, npm publish, git commit + push in one shot",
15
+ diff: "Show what capabilities changed since the last git tag (or any ref)",
16
+ changelog: "Draft a changelog entry from commits since the last tag",
17
+ setup: "One command to get fully operational — detects IDE, inits, installs hooks + MCP",
18
+ init: "Scaffold inferno/ in your project (or adopt existing project)",
19
+ "install-cursor-hooks": "Install Cursor hooks: draft agent replies to inferno/CONTEXT.draft.md",
20
+ "install-vscode-copilot-hooks":
21
+ "Install VS Code + Copilot agent hooks (Preview): draft to inferno/CONTEXT.draft.md",
22
+ check: "Validate contract, capabilities, scenarios, changelog",
23
+ status: "Show contract health at a glance",
24
+ "pr-impact": "Summarize PR impact on capabilities and docs",
25
+ sync: "Run deterministic inferno sync flow",
26
+ run: "One-command detect/propose/apply/validate flow",
27
+ "doc-gate": "Fail if code changed but docs were not updated",
28
+ suggest: "Generate AI prompt + apply capability updates",
29
+ implement: "Generate code-agent implementation prompt(s)",
30
+ context: "Generate AI-ready context for new sessions",
31
+ "generate-skills": "Generate personalised Cursor rules + skill files from your developer profile",
32
+ synthesize: "Auto-detect workflow patterns and synthesize reusable skills + agents",
33
+ agent: "Manage and run auto-synthesized agents (list | run | show | delete)",
34
+ version: "Smart semver bump recommendation based on capability changes (--apply to write)",
35
+ "pr-comment": "Post capability drift analysis as a GitHub PR comment (works in CI automatically)",
36
+ dashboard: "Launch local web dashboard on localhost:7337 — live contract health, capabilities, agents",
37
+ "team-sync": "Sync capability contract across a team via a shared git branch (push | pull | status | init)",
38
+ onboard: "Interactive onboarding wizard for new developers — explains infernoflow in 5 minutes",
39
+ cloud: "Sync capability contracts via infernoflow cloud (init | push | pull | status | dashboard)",
40
+ share: "Generate a public read-only HTML snapshot of your capability contract",
41
+ watch: "Watch source files and run suggest automatically on save",
42
+ ci: "CI-native check: GitHub Actions annotations, GitLab code quality, exit codes",
43
+ notify: "Post capability drift summary to Slack or Discord",
44
+ report: "Generate a weekly/monthly HTML or Markdown report of capability activity",
45
+ monorepo: "Manage infernoflow across monorepo packages (init | list | status | diff | sync)",
46
+ link: "Link capabilities to Jira, Linear, or GitHub Issues tickets",
47
+ audit: "Classify capabilities by sensitivity (auth, payment, PII, admin) and generate security surface map",
48
+ scout: "Scan source files for undocumented capabilities not yet in the contract",
49
+ export: "Export contract to OpenAPI, Backstage catalog-info.yaml, CSV, or Markdown",
50
+ snapshot: "Save/diff/restore named snapshots of the capability contract",
51
+ health: "Compute a 0–100 health score across coverage, docs, freshness, completeness, drift",
52
+ vibe: "Vibe coding mode — watches files, auto-syncs contract, regenerates context on every save",
53
+ adopt: "Interactive wizard to adopt infernoflow in an existing project (detect → review → wire up)",
54
+ doctor: "Diagnose your infernoflow setup — checks Node, git, contract, AI providers, MCP, hooks",
55
+ coverage: "Map test files to capabilities — show which caps have test coverage and which don't",
56
+ review: "AI-powered capability impact review for staged or recent git changes",
57
+ scan: "Deep AST scan — route discovery, entry point detection, HTTP URL extraction, capability suggestions",
58
+ graph: "Build capability dependency graph — shows which caps call which, detects breaking changes",
59
+ stability: "Show solid/liquid stability level for every capability (frozen/stable/experimental)",
60
+ freeze: "Mark a capability as frozen (solid) — AI will not modify it without explicit instruction",
61
+ thaw: "Reset a capability to experimental (liquid) — free to evolve",
62
+ why: "Given a file or function name — show which capability it serves, scenarios, stability, and git history",
63
+ impact: "Blast radius analysis — see every cap, scenario, and risk level affected before you change anything",
64
+ scaffold: "Generate a new capability — source skeleton, contract registration, and placeholder scenario in one command",
65
+ explain: "AI narrative about a capability — what it does, why it exists, what's risky, and what to test",
66
+ test: "Run registered scenarios for a capability — auto-generates a smoke harness if no test runner is configured",
67
+ ai: "Manage AI providers — setup, status, test connection (subcommands: setup | status | test | clear)",
68
+ demo: "Interactive walkthrough — scaffolds a sample project and runs the full capability chain end-to-end",
69
+ log: "Append to session memory (decisions, gotchas, failed attempts, theme changes) — what AI can't infer from code",
70
+ theme: "Scan fonts, colors, and CSS variables — write inferno/theme.json so AI always matches the design system",
71
+ switch: "Generate a handoff summary when switching AI agents — paste into the next session so nothing is lost",
72
+ upgrade: "Upgrade a lite infernoflow setup to the full structure (scenarios, changelog, scripts)",
73
+ stats: "Value dashboard — session memory, tokens injected per session, coverage %, estimated savings",
74
+ ask: "Query session memory — search gotchas, decisions, and failed attempts by keyword or type",
75
+ recap: "End-of-session summary — what was captured, what git changes weren't logged, session health score",
76
+ };
77
+
78
+ const COMMAND_HANDLERS = {
79
+ publish: async (args) => (await import("../lib/commands/publish.mjs")).publishCommand(args),
80
+ diff: async (args) => (await import("../lib/commands/diff.mjs")).diffCommand(args),
81
+ changelog: async (args) => (await import("../lib/commands/changelog.mjs")).changelogCommand(args),
82
+ setup: async (args) => (await import("../lib/commands/setup.mjs")).setupCommand(args),
83
+ init: async (args) => (await import("../lib/commands/init.mjs")).initCommand(args),
84
+ "install-cursor-hooks": async (args) =>
85
+ (await import("../lib/commands/installCursorHooks.mjs")).installCursorHooksCommand(args),
86
+ "install-vscode-copilot-hooks": async (args) =>
87
+ (await import("../lib/commands/installVsCodeCopilotHooks.mjs")).installVsCodeCopilotHooksCommand(args),
88
+ check: async (args) => (await import("../lib/commands/check.mjs")).checkCommand(args),
89
+ status: async (args) => (await import("../lib/commands/status.mjs")).statusCommand(args),
90
+ "pr-impact": async (args) => (await import("../lib/commands/prImpact.mjs")).prImpactCommand(args),
91
+ sync: async (args) => (await import("../lib/commands/syncAuto.mjs")).syncCommand(args),
92
+ run: async (args) => (await import("../lib/commands/run.mjs")).runCommand(args),
93
+ suggest: async (args) => (await import("../lib/commands/suggest.mjs")).suggestCommand(args),
94
+ implement: async (args) => (await import("../lib/commands/implement.mjs")).implementCommand(args),
95
+ context: async (args) => (await import("../lib/commands/context.mjs")).contextCommand(args),
96
+ "doc-gate": async (args) => (await import("../lib/commands/docGate.mjs")).docGateCommand(args),
97
+ "generate-skills": async (args) => (await import("../lib/commands/generateSkills.mjs")).generateSkillsCommand(args),
98
+ synthesize: async (args) => (await import("../lib/commands/synthesize.mjs")).synthesizeCommand(args),
99
+ agent: async (args) => (await import("../lib/commands/agent.mjs")).agentCommand(args),
100
+ version: async (args) => (await import("../lib/commands/version.mjs")).versionCommand(args),
101
+ "pr-comment": async (args) => (await import("../lib/commands/prComment.mjs")).prCommentCommand(args),
102
+ dashboard: async (args) => (await import("../lib/commands/dashboard.mjs")).dashboardCommand(args),
103
+ "team-sync": async (args) => (await import("../lib/commands/teamSync.mjs")).teamSyncCommand(args),
104
+ onboard: async (args) => (await import("../lib/commands/onboard.mjs")).onboardCommand(args),
105
+ cloud: async (args) => (await import("../lib/commands/cloud.mjs")).cloudCommand(args),
106
+ share: async (args) => (await import("../lib/commands/share.mjs")).shareCommand(args),
107
+ watch: async (args) => (await import("../lib/commands/watch.mjs")).watchCommand(args),
108
+ ci: async (args) => (await import("../lib/commands/ci.mjs")).ciCommand(args),
109
+ notify: async (args) => (await import("../lib/commands/notify.mjs")).notifyCommand(args),
110
+ report: async (args) => (await import("../lib/commands/report.mjs")).reportCommand(args),
111
+ monorepo: async (args) => (await import("../lib/commands/monorepo.mjs")).monorepoCommand(args),
112
+ link: async (args) => (await import("../lib/commands/link.mjs")).linkCommand(args),
113
+ audit: async (args) => (await import("../lib/commands/audit.mjs")).auditCommand(args),
114
+ scout: async (args) => (await import("../lib/commands/scout.mjs")).scoutCommand(args),
115
+ export: async (args) => (await import("../lib/commands/export.mjs")).exportCommand(args),
116
+ snapshot: async (args) => (await import("../lib/commands/snapshot.mjs")).snapshotCommand(args),
117
+ health: async (args) => (await import("../lib/commands/health.mjs")).healthCommand(args),
118
+ vibe: async (args) => (await import("../lib/commands/vibe.mjs")).vibeCommand(args),
119
+ adopt: async (args) => (await import("../lib/commands/adoptWizard.mjs")).adoptWizardCommand(args),
120
+ doctor: async (args) => (await import("../lib/commands/doctor.mjs")).doctorCommand(args),
121
+ coverage: async (args) => (await import("../lib/commands/coverage.mjs")).coverageCommand(args),
122
+ review: async (args) => (await import("../lib/commands/review.mjs")).reviewCommand(args),
123
+ scan: async (args) => (await import("../lib/commands/scan.mjs")).scanCommand(args),
124
+ graph: async (args) => (await import("../lib/commands/graph.mjs")).graphCommand(args),
125
+ stability: async (args) => (await import("../lib/commands/stability.mjs")).stabilityCommand(args),
126
+ freeze: async (args) => (await import("../lib/commands/stability.mjs")).freezeCommand(args),
127
+ thaw: async (args) => (await import("../lib/commands/stability.mjs")).thawCommand(args),
128
+ why: async (args) => (await import("../lib/commands/why.mjs")).whyCommand(args),
129
+ impact: async (args) => (await import("../lib/commands/impact.mjs")).impactCommand(args),
130
+ scaffold: async (args) => (await import("../lib/commands/scaffold.mjs")).scaffoldCommand(args),
131
+ explain: async (args) => (await import("../lib/commands/explain.mjs")).explainCommand(args),
132
+ test: async (args) => (await import("../lib/commands/test.mjs")).testCommand(args),
133
+ ai: async (args) => (await import("../lib/commands/ai.mjs")).aiCommand(args),
134
+ demo: async (args) => (await import("../lib/commands/demo.mjs")).demoCommand(args),
135
+ log: async (args) => (await import("../lib/commands/log.mjs")).logCommand(args),
136
+ theme: async (args) => (await import("../lib/commands/theme.mjs")).themeCommand(args),
137
+ switch: async (args) => (await import("../lib/commands/switch.mjs")).switchCommand(args),
138
+ upgrade: async (args) => (await import("../lib/commands/upgrade.mjs")).upgradeCommand(args),
139
+ stats: async (args) => (await import("../lib/commands/stats.mjs")).statsCommand(args),
140
+ ask: async (args) => (await import("../lib/commands/ask.mjs")).askCommand(args),
141
+ recap: async (args) => (await import("../lib/commands/recap.mjs")).recapCommand(args),
142
+ };
143
+
144
+ function formatCommandsHelp() {
145
+ const names = Object.keys(COMMAND_DESCRIPTIONS);
146
+ const w = Math.max(...names.map((n) => n.length), 8) + 1;
147
+ return Object.entries(COMMAND_DESCRIPTIONS)
148
+ .map(([name, desc]) => ` ${name.padEnd(w, " ")}${desc}`)
149
+ .join("\n");
150
+ }
151
+
152
+ const HELP = `
153
+ ${bold("🔥 infernoflow")} ${gray("v" + VERSION)}
154
+ ${gray("The forge for liquid code — keep every AI session in sync")}
155
+
156
+ ${bold("Usage:")}
8
157
  infernoflow <command> [options]
9
158
 
10
- ${t("Commands:")}
11
- ${w()}
159
+ ${bold("Commands:")}
160
+ ${formatCommandsHelp()}
12
161
 
13
- ${t("diff options:")}
162
+ ${bold("diff options:")}
14
163
  --ref <tag|commit> Compare against a specific ref (default: last git tag)
15
164
  --summary One-liner count only
16
165
  --json Machine-readable output
17
166
 
18
- ${t("changelog options:")}
167
+ ${bold("changelog options:")}
19
168
  update Draft ## Unreleased from commits (default sub-command)
20
169
  show Print the current ## Unreleased block
21
170
  list List commits since last tag
@@ -26,7 +175,7 @@ ${w()}
26
175
  --append Append to existing ## Unreleased instead of replacing
27
176
  --json Machine-readable output
28
177
 
29
- ${t("publish options:")}
178
+ ${bold("publish options:")}
30
179
  --bump patch|minor|major Version bump type (default: patch)
31
180
  --skip-build Skip the build step
32
181
  --skip-tests Skip smoke tests
@@ -35,13 +184,13 @@ ${w()}
35
184
  --dry-run Print all steps without executing
36
185
  --yes, -y Non-interactive (skip confirmation prompt)
37
186
 
38
- ${t("setup options:")}
187
+ ${bold("setup options:")}
39
188
  --yes, -y Skip prompts (non-interactive)
40
189
  --force, -f Overwrite existing hook files
41
190
 
42
- ${t("init options:")}
43
- --cursor-hooks Also install Cursor hooks (draft \u2192 inferno/CONTEXT.draft.md)
44
- --vscode-copilot-hooks Also install VS Code + Copilot hooks (.github/hooks \u2014 Preview)
191
+ ${bold("init options:")}
192
+ --cursor-hooks Also install Cursor hooks (draft inferno/CONTEXT.draft.md)
193
+ --vscode-copilot-hooks Also install VS Code + Copilot hooks (.github/hooks Preview)
45
194
  --adopt Infer capabilities from an existing codebase
46
195
  --lang <name> Override detected language (e.g. ts, js, py)
47
196
  --framework <name> Override detected framework (e.g. react, angular, express)
@@ -52,13 +201,13 @@ ${w()}
52
201
  --yes, -y Skip prompts and accept inferred/default values
53
202
  --force, -f Overwrite existing inferno/ files
54
203
 
55
- ${t("install-cursor-hooks options:")}
204
+ ${bold("install-cursor-hooks options:")}
56
205
  --force, -f Overwrite .cursor/hooks.json and hook scripts if they exist
57
206
 
58
- ${t("install-vscode-copilot-hooks options:")}
207
+ ${bold("install-vscode-copilot-hooks options:")}
59
208
  --force, -f Overwrite .github/hooks/infernoflow-drafts.json and scripts if they exist
60
209
 
61
- ${t("context options:")}
210
+ ${bold("context options:")}
62
211
  --intent "..." What you plan to build next
63
212
  --working "..." What you are building right now
64
213
  --decision "..." Record a decision or note
@@ -70,39 +219,39 @@ ${w()}
70
219
  --auto-commit Watch mode: commit CONTEXT.md to git on every change
71
220
  --auto-push Watch mode: commit + push CONTEXT.md on every change
72
221
 
73
- ${t("generate-skills options:")}
222
+ ${bold("generate-skills options:")}
74
223
  --cursor Also install rules to .cursor/rules/infernoflow.md
75
224
  --force, -f Overwrite existing generated skill files
76
225
 
77
- ${t("implement options:")}
226
+ ${bold("implement options:")}
78
227
  --mode <type> cursor | generic | both (default: both)
79
228
  --copy, -c Copy generated prompt(s) to clipboard
80
229
 
81
- ${t("run options:")}
230
+ ${bold("run options:")}
82
231
  --dry-run Execute full flow without writing files
83
232
  --json Emit machine-readable events and result payload
84
233
  --no-rollback Keep changes even if validation fails
85
234
  --provider <type> auto | agent | local | prompt (default: auto)
86
235
  --ide <name> auto | cursor | vscode | windsurf (default: auto)
87
236
 
88
- ${t("Typical workflow:")}
89
- ${a('1. infernoflow context --intent "what I want to build"')}
90
- ${a("2. [paste inferno/CONTEXT.md into Claude / Cursor / Copilot]")}
91
- ${a("3. [build the feature]")}
92
- ${a('4. infernoflow suggest "what I built"')}
93
- ${a("5. infernoflow check")}
237
+ ${bold("Typical workflow:")}
238
+ ${gray('1. infernoflow context --intent "what I want to build"')}
239
+ ${gray("2. [paste inferno/CONTEXT.md into Claude / Cursor / Copilot]")}
240
+ ${gray("3. [build the feature]")}
241
+ ${gray('4. infernoflow suggest "what I built"')}
242
+ ${gray("5. infernoflow check")}
94
243
 
95
- ${t("suggest options:")}
244
+ ${bold("suggest options:")}
96
245
  --json Non-interactive: emit prompt as JSON, no readline prompts
97
246
  --response <json|@file> Provide AI response directly (use with --json)
98
247
  --apply Apply the response changes when using --json --response
99
248
 
100
- ${t("version options:")}
249
+ ${bold("version options:")}
101
250
  --ref <tag|commit> Compare against a specific ref (default: last git tag)
102
251
  --apply Write recommended version bump to package.json
103
252
  --json Machine-readable output
104
253
 
105
- ${t("pr-comment options:")}
254
+ ${bold("pr-comment options:")}
106
255
  --pr <number> PR number to comment on (auto-detected in GitHub Actions)
107
256
  --repo <owner/repo> GitHub repository (auto-detected in GitHub Actions)
108
257
  --token <ghp_...> GitHub token (auto-detected from GITHUB_TOKEN env var)
@@ -110,83 +259,83 @@ ${w()}
110
259
  --dry-run Print the comment without posting it
111
260
  --json Machine-readable output
112
261
 
113
- ${t("cloud sub-commands:")}
262
+ ${bold("cloud sub-commands:")}
114
263
  init Generate a project token and configure cloud sync
115
264
  push Upload local capability contract to cloud
116
265
  pull Download latest contract from cloud (conflict detection)
117
266
  status Compare local vs cloud (hashes, capability counts)
118
267
  dashboard Print hosted dashboard URL and open in browser
119
268
 
120
- ${t("cloud options:")}
269
+ ${bold("cloud options:")}
121
270
  --token <tok> Override token (or set INFERNOFLOW_TOKEN env var)
122
271
  --endpoint <url> Override default endpoint (https://cloud.infernoflow.dev)
123
272
  --force, -f Overwrite on init; overwrite local on conflicted pull
124
273
  --dry-run Print what would happen without sending
125
274
  --json Machine-readable output
126
275
 
127
- ${t("share options:")}
276
+ ${bold("share options:")}
128
277
  --upload Upload to dpaste.com and print a public URL
129
278
  --open Open the snapshot in your browser immediately
130
279
  --copy Copy HTML to clipboard
131
280
  --out <path> Custom output path (default: inferno/share.html)
132
281
  --json Machine-readable: { ok, file, url }
133
282
 
134
- ${t("watch options:")}
283
+ ${bold("watch options:")}
135
284
  [dirs...] Directories to watch (default: src/, lib/, app/)
136
285
  --interval <secs> Debounce interval in seconds (default: 3)
137
286
  --dry-run Print what would run without executing
138
287
  --silent No output (for git hook use)
139
288
 
140
- ${t("notify options:")}
289
+ ${bold("notify options:")}
141
290
  --slack <url> Slack incoming webhook URL
142
291
  --discord <url> Discord webhook URL
143
292
  --on-change Only notify if capabilities actually changed
144
293
  --dry-run Print message without sending
145
294
  --json Machine-readable result
146
295
 
147
- ${t("report options:")}
296
+ ${bold("report options:")}
148
297
  --format html|md Output format (default: html)
149
298
  --since <period> 7d, 30d, 90d, or YYYY-MM-DD (default: 30d)
150
299
  --out <path> Output file path (default: inferno/report.html)
151
300
  --open Open HTML report in browser after generating
152
301
  --json Machine-readable summary
153
302
 
154
- ${t("ci options:")}
303
+ ${bold("ci options:")}
155
304
  --platform <name> github | gitlab | bitbucket | generic (auto-detected)
156
305
  --fail-on <level> error | warning (default: error)
157
306
  --json Machine-readable result + exit code
158
307
 
159
- ${t("monorepo sub-commands:")}
308
+ ${bold("monorepo sub-commands:")}
160
309
  init Run infernoflow init --adopt in every package
161
310
  list List detected packages with their capability counts
162
311
  status Show contract health across all packages
163
312
  diff Show capability changes across packages (--package to filter)
164
313
  sync Aggregate all contracts into inferno-monorepo.json
165
314
 
166
- ${t("monorepo options:")}
315
+ ${bold("monorepo options:")}
167
316
  --package <name> Filter to a specific package
168
317
  --json Machine-readable output
169
318
 
170
- ${t("link sub-commands:")}
319
+ ${bold("link sub-commands:")}
171
320
  (default) Link a capability to a ticket
172
- list Show all capability\u2192ticket links
321
+ list Show all capability→ticket links
173
322
  status Show linked and unlinked capabilities
174
323
  remove Remove a link by capability ID
175
324
 
176
- ${t("link options:")}
325
+ ${bold("link options:")}
177
326
  --capability <id> Capability to link
178
327
  --jira <TICKET> Jira ticket ID (e.g. PROJ-123)
179
328
  --linear <ID> Linear issue ID
180
329
  --github <NUM> GitHub issue number
181
330
  --json Machine-readable output
182
331
 
183
- ${t("audit options:")}
332
+ ${bold("audit options:")}
184
333
  --format text|json|html Output format (default: text)
185
334
  --out <path> Save to file (default: prints to stdout)
186
335
  --fail-on high|medium Exit 1 if unreviewed caps at given severity exist
187
336
  --json Machine-readable output
188
337
 
189
- ${t("vibe options:")}
338
+ ${bold("vibe options:")}
190
339
  --dir <dirs> Comma-separated directories to watch (default: auto-detected)
191
340
  --no-suggest Disable automatic contract sync on file save
192
341
  --no-context Disable CONTEXT.md regeneration
@@ -194,30 +343,30 @@ ${w()}
194
343
  --port <n> Also run a mini status dashboard on localhost:<n>
195
344
  --silent Suppress all terminal output (pure background mode)
196
345
 
197
- ${t("adopt options:")}
346
+ ${bold("adopt options:")}
198
347
  --dir <dirs> Source directories to scan (default: src,lib,app,api,routes,controllers)
199
348
  --yes, -y Auto-approve all candidates (non-interactive)
200
349
  --json Machine-readable output, implies --yes
201
350
 
202
- ${t("init --template options:")}
351
+ ${bold("init --template options:")}
203
352
  --template rest-api REST API (Express/Fastify/Hono) starter
204
353
  --template nextjs Next.js fullstack app starter
205
354
  --template cli CLI tool (Node.js/Python) starter
206
355
  --template graphql GraphQL API (Apollo/Pothos) starter
207
356
  --template monorepo Monorepo workspace starter
208
357
 
209
- ${t("scout options:")}
358
+ ${bold("scout options:")}
210
359
  --dir <dirs> Comma-separated directories to scan (default: src,lib,app,api,routes)
211
360
  --apply Write discovered capabilities to the contract file
212
361
  --min-confidence <0-1> Minimum confidence threshold (default: 0.6)
213
362
  --json Machine-readable output
214
363
 
215
- ${t("export options:")}
364
+ ${bold("export options:")}
216
365
  --format openapi|backstage|csv|markdown|json Output format (required)
217
366
  --out <path> Output file path (default: project root, auto-named)
218
367
  --json Machine-readable summary
219
368
 
220
- ${t("snapshot sub-commands:")}
369
+ ${bold("snapshot sub-commands:")}
221
370
  save <name> Save current contract as a named snapshot
222
371
  list List all snapshots
223
372
  show <name> Print a snapshot's capabilities
@@ -225,62 +374,63 @@ ${w()}
225
374
  restore <name> Overwrite contract with snapshot contents
226
375
  delete <name> Delete a snapshot
227
376
 
228
- ${t("snapshot options:")}
377
+ ${bold("snapshot options:")}
229
378
  --json Machine-readable output
230
379
 
231
- ${t("health options:")}
380
+ ${bold("health options:")}
232
381
  --fail-below <score> Exit 1 if health score is below this threshold (CI gate)
233
382
  --watch Re-run every 30s (live terminal view)
234
383
  --interval <secs> Watch interval in seconds (default: 30)
235
384
  --json Machine-readable score + breakdown
236
385
 
237
- ${t("doctor options:")}
386
+ ${bold("doctor options:")}
238
387
  --fix Auto-fix common issues (installs hooks, runs init, etc.)
239
388
  --json Machine-readable list of pass/warn/fail results
240
389
 
241
- ${t("coverage options:")}
390
+ ${bold("coverage options:")}
242
391
  --dir <path> Extra directory to scan for test files (repeatable)
243
392
  --threshold <0-1> Minimum fuzzy-match score to count a test (default: 0.25)
244
393
  --fail-below <pct> Exit 1 if coverage percentage is below this value (CI gate)
245
394
  --json Machine-readable coverage breakdown
246
395
 
247
- ${t("scan options:")}
396
+ ${bold("scan options:")}
248
397
  --dir <path> Extra directory to scan (repeatable)
249
398
  --capability <id> Scan and enrich a single capability only
399
+ --suggest, -s Show untracked entry points as new capability candidates
250
400
  --dry-run Print results without writing files
251
- --json Machine-readable scan output
401
+ --json Machine-readable scan output (includes discovered routes)
252
402
 
253
- ${t("graph options:")}
403
+ ${bold("graph options:")}
254
404
  --cap <id> Show dependency view for a single capability
255
405
  --check Exit 1 if breaking dependency changes detected (CI gate)
256
406
  --json Machine-readable graph output
257
407
 
258
- ${t("stability / freeze / thaw options:")}
408
+ ${bold("stability / freeze / thaw options:")}
259
409
  infernoflow stability List all capabilities with their stability level
260
410
  infernoflow freeze <id> Mark capability as frozen (AI won't touch it)
261
411
  infernoflow freeze <id> --stable Mark as stable (careful, not forbidden)
262
- infernoflow thaw <id> Reset to experimental (liquid \u2014 free to change)
412
+ infernoflow thaw <id> Reset to experimental (liquid free to change)
263
413
  --json Machine-readable stability list
264
414
 
265
- ${t("review options:")}
415
+ ${bold("review options:")}
266
416
  --unstaged Review all working-tree changes (not just staged)
267
417
  --last Review last commit (git diff HEAD~1)
268
- --dry-run Print the AI prompt only \u2014 no API call made
418
+ --dry-run Print the AI prompt only no API call made
269
419
  --json Machine-readable output (affectedCaps, summary, provider)
270
420
 
271
- ${t("why options:")}
421
+ ${bold("why options:")}
272
422
  infernoflow why <file> Show capability for a source file
273
423
  infernoflow why <functionName> Show capability for a function name
274
424
  --function <name> Filter to a specific function when multiple caps match
275
425
  --json Machine-readable output
276
426
 
277
- ${t("impact options:")}
427
+ ${bold("impact options:")}
278
428
  infernoflow impact <cap-id> Show blast radius for a capability
279
429
  --depth <n> Max transitive depth to traverse (default: 10)
280
430
  --check Exit 1 if risk level is HIGH or CRITICAL (CI gate)
281
431
  --json Machine-readable output
282
432
 
283
- ${t("scaffold options:")}
433
+ ${bold("scaffold options:")}
284
434
  infernoflow scaffold <cap-id> Generate a new capability skeleton
285
435
  --dir <path> Output directory for the source file (default: auto-detected)
286
436
  --lang ts|js|py|go Language override (default: auto-detected from project)
@@ -288,12 +438,12 @@ ${w()}
288
438
  --dry-run Preview what would be generated without writing files
289
439
  --json Machine-readable output including generated code
290
440
 
291
- ${t("explain options:")}
441
+ ${bold("explain options:")}
292
442
  infernoflow explain <cap-id> AI narrative: what it does, risk, what to test
293
- --dry-run Print the AI prompt only \u2014 no API call made
443
+ --dry-run Print the AI prompt only no API call made
294
444
  --json Machine-readable output (narrative, stability, scenarios)
295
445
 
296
- ${t("test options:")}
446
+ ${bold("test options:")}
297
447
  infernoflow test Run all caps that have registered scenarios
298
448
  infernoflow test <cap-id> Run scenarios for a specific capability
299
449
  infernoflow test --all Run every capability (including those without scenarios)
@@ -302,30 +452,66 @@ ${w()}
302
452
  --verbose, -v Show runner output for each scenario
303
453
  --json Machine-readable output (passed/failed/skipped counts)
304
454
 
305
- ${t("ai options:")}
306
- infernoflow ai setup Interactive wizard \u2014 pick provider, enter API key, verify
455
+ ${bold("ai options:")}
456
+ infernoflow ai setup Interactive wizard pick provider, enter API key, verify
307
457
  infernoflow ai status Show all providers and which are configured
308
458
  infernoflow ai test [provider] Send a test prompt and verify the connection
309
459
  infernoflow ai clear <provider> Remove a provider's config from integrations.json
310
460
  Supported providers: anthropic openai gemini openrouter ollama
311
461
 
312
- ${t("demo options:")}
462
+ ${bold("demo options:")}
313
463
  infernoflow demo Full interactive walkthrough (sample e-commerce project)
314
- infernoflow demo --fast Skip pauses \u2014 good for CI or screen recording
464
+ infernoflow demo --fast Skip pauses good for CI or screen recording
315
465
  infernoflow demo --no-cleanup Keep the temp demo project after the run
316
466
 
317
- ${t("Machine output:")}
318
- ${a("status --json")}
319
- ${a("check --json")}
320
- ${a("doc-gate --json")}
321
- ${a("pr-impact --json")}
322
- ${a("sync --auto --json")}
323
- ${a('run "task" --json')}
324
- ${a('suggest "what changed" --json')}
325
- ${a(`suggest "what changed" --json --response '{"newCapabilities":[...]}' --apply`)}
326
- ${a("version --json")}
327
- ${a("version --apply")}
328
- `;import*as b from"node:fs";import*as v from"node:path";try{const e=v.join(process.cwd(),"inferno");if(b.existsSync(e)){const{observeCommandStart:n}=await import("../lib/learning/observe.mjs"),i=process.argv[2];i&&!i.startsWith("-")&&n(e,i)}}catch{}const[,,o,...k]=process.argv;(!o||o==="--help"||o==="-h")&&(console.log(g),process.exit(0)),(o==="--version"||o==="-v")&&(console.log(r),process.exit(0));const C=Object.keys(l);C.includes(o)||(console.error(s(`
329
- Unknown command: ${o}`)),console.error(a(`Run: infernoflow --help
330
- `)),process.exit(1));const j=[o,...k];l[o](j).catch(e=>{console.error(s(`
331
- Error: `)+e.message),process.exit(1)});
467
+ ${bold("Machine output:")}
468
+ ${gray("status --json")}
469
+ ${gray("check --json")}
470
+ ${gray("doc-gate --json")}
471
+ ${gray("pr-impact --json")}
472
+ ${gray("sync --auto --json")}
473
+ ${gray('run "task" --json')}
474
+ ${gray('suggest "what changed" --json')}
475
+ ${gray('suggest "what changed" --json --response \'{"newCapabilities":[...]}\' --apply')}
476
+ ${gray("version --json")}
477
+ ${gray("version --apply")}
478
+ `;
479
+
480
+ // ── Silent behavior observation ───────────────────────────────────────────
481
+ import * as fs from "node:fs";
482
+ import * as path from "node:path";
483
+ try {
484
+ const infernoDir = path.join(process.cwd(), "inferno");
485
+ if (fs.existsSync(infernoDir)) {
486
+ const { observeCommandStart } = await import("../lib/learning/observe.mjs");
487
+ const cmdForObserve = process.argv[2];
488
+ if (cmdForObserve && !cmdForObserve.startsWith("-")) {
489
+ observeCommandStart(infernoDir, cmdForObserve);
490
+ }
491
+ }
492
+ } catch {}
493
+
494
+ const [, , cmd, ...rest] = process.argv;
495
+
496
+ if (!cmd || cmd === "--help" || cmd === "-h") {
497
+ console.log(HELP);
498
+ process.exit(0);
499
+ }
500
+ if (cmd === "--version" || cmd === "-v") {
501
+ console.log(VERSION);
502
+ process.exit(0);
503
+ }
504
+
505
+ const commands = Object.keys(COMMAND_HANDLERS);
506
+
507
+ if (!commands.includes(cmd)) {
508
+ console.error(red(`\nUnknown command: ${cmd}`));
509
+ console.error(gray("Run: infernoflow --help\n"));
510
+ process.exit(1);
511
+ }
512
+
513
+ const args = [cmd, ...rest];
514
+ COMMAND_HANDLERS[cmd](args).catch((err) => {
515
+ console.error(red("\nError: ") + err.message);
516
+ process.exit(1);
517
+ });