infernoflow 0.37.1 → 0.37.3
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 +64 -0
- package/dist/bin/infernoflow.mjs +29 -277
- package/dist/lib/adopters/angular.mjs +1 -128
- package/dist/lib/adopters/css.mjs +1 -111
- package/dist/lib/adopters/react.mjs +1 -104
- package/dist/lib/ai/ideDetection.mjs +1 -31
- package/dist/lib/ai/localProvider.mjs +1 -88
- package/dist/lib/ai/providerRouter.mjs +2 -295
- package/dist/lib/commands/adopt.mjs +20 -869
- package/dist/lib/commands/adoptWizard.mjs +9 -320
- package/dist/lib/commands/agent.mjs +5 -191
- package/dist/lib/commands/ai.mjs +2 -407
- package/dist/lib/commands/ask.mjs +4 -299
- package/dist/lib/commands/audit.mjs +13 -300
- package/dist/lib/commands/changelog.mjs +26 -594
- package/dist/lib/commands/check.mjs +3 -184
- package/dist/lib/commands/ci.mjs +3 -208
- package/dist/lib/commands/claudeMd.mjs +30 -135
- package/dist/lib/commands/cloud.mjs +10 -773
- package/dist/lib/commands/context.mjs +34 -346
- package/dist/lib/commands/coverage.mjs +2 -282
- package/dist/lib/commands/dashboard.mjs +123 -635
- package/dist/lib/commands/demo.mjs +8 -465
- package/dist/lib/commands/diff.mjs +5 -274
- package/dist/lib/commands/docGate.mjs +2 -81
- package/dist/lib/commands/doctor.mjs +3 -321
- package/dist/lib/commands/explain.mjs +8 -438
- package/dist/lib/commands/export.mjs +10 -239
- package/dist/lib/commands/feedback.mjs +12 -216
- package/dist/lib/commands/generateSkills.mjs +38 -163
- package/dist/lib/commands/graph.mjs +11 -378
- package/dist/lib/commands/health.mjs +2 -309
- package/dist/lib/commands/impact.mjs +2 -325
- package/dist/lib/commands/implement.mjs +7 -103
- package/dist/lib/commands/init.mjs +45 -631
- package/dist/lib/commands/installCursorHooks.mjs +1 -36
- package/dist/lib/commands/installVsCodeCopilotHooks.mjs +1 -37
- package/dist/lib/commands/link.mjs +2 -342
- package/dist/lib/commands/log.mjs +18 -248
- package/dist/lib/commands/monorepo.mjs +4 -428
- package/dist/lib/commands/notify.mjs +4 -258
- package/dist/lib/commands/onboard.mjs +4 -296
- package/dist/lib/commands/prComment.mjs +2 -361
- package/dist/lib/commands/prImpact.mjs +2 -157
- package/dist/lib/commands/publish.mjs +15 -316
- package/dist/lib/commands/recap.mjs +6 -380
- package/dist/lib/commands/report.mjs +28 -272
- package/dist/lib/commands/review.mjs +9 -223
- package/dist/lib/commands/run.mjs +8 -336
- package/dist/lib/commands/scaffold.mjs +54 -419
- package/dist/lib/commands/scan.mjs +11 -1118
- package/dist/lib/commands/scout.mjs +2 -291
- package/dist/lib/commands/setup.mjs +5 -310
- package/dist/lib/commands/share.mjs +13 -196
- package/dist/lib/commands/snapshot.mjs +3 -383
- package/dist/lib/commands/stability.mjs +2 -293
- package/dist/lib/commands/stats.mjs +5 -402
- package/dist/lib/commands/status.mjs +4 -172
- package/dist/lib/commands/suggest.mjs +21 -563
- package/dist/lib/commands/switch.mjs +13 -520
- package/dist/lib/commands/syncAuto.mjs +1 -96
- package/dist/lib/commands/synthesize.mjs +10 -228
- package/dist/lib/commands/teamSync.mjs +2 -388
- package/dist/lib/commands/test.mjs +6 -363
- package/dist/lib/commands/theme.mjs +18 -195
- package/dist/lib/commands/uninstall.mjs +13 -406
- package/dist/lib/commands/upgrade.mjs +20 -153
- package/dist/lib/commands/version.mjs +2 -282
- package/dist/lib/commands/vibe.mjs +7 -357
- package/dist/lib/commands/watch.mjs +4 -203
- package/dist/lib/commands/why.mjs +4 -358
- package/dist/lib/cursorHooksInstall.mjs +1 -60
- package/dist/lib/draftToolingInstall.mjs +7 -68
- package/dist/lib/git/detect-drift.mjs +4 -208
- package/dist/lib/learning/adapt.mjs +6 -101
- package/dist/lib/learning/observe.mjs +1 -119
- package/dist/lib/learning/patternDetector.mjs +1 -298
- package/dist/lib/learning/profile.mjs +2 -279
- package/dist/lib/learning/skillSynthesizer.mjs +24 -145
- package/dist/lib/telemetry.mjs +19 -269
- package/dist/lib/templates/index.mjs +1 -131
- package/dist/lib/theme/scanner.mjs +4 -343
- package/dist/lib/ui/errors.mjs +1 -142
- package/dist/lib/ui/output.mjs +6 -95
- package/dist/lib/ui/prompts.mjs +6 -147
- package/dist/lib/vsCodeCopilotHooksInstall.mjs +1 -42
- package/package.json +2 -4
- package/scripts/postinstall.js +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -132,6 +132,70 @@
|
|
|
132
132
|
- release 0.10.20
|
|
133
133
|
- release 0.10.19
|
|
134
134
|
|
|
135
|
+
- Scarf analytics + telemetry UUID/timezone + feedback Formspree (v0.35.6)
|
|
136
|
+
- cloud session memory sync (v0.35.4)
|
|
137
|
+
- opt-in telemetry system (v0.35.3)
|
|
138
|
+
- stunning switch output (v0.35.2)
|
|
139
|
+
- infernoflow feedback command (v0.35.1)
|
|
140
|
+
- auto-capture git hooks + log --auto/--quiet/--source (v0.35.0)
|
|
141
|
+
- progressive disclosure — 5 core commands in --help, infernoflow commands shows full grouped list (v0.34.4)
|
|
142
|
+
- npm metadata — AI-optimized description + 20 keywords for AI-native discovery (v0.34.3)
|
|
143
|
+
- infernoflow uninstall — remove all infernoflow artifacts from a project (v0.34.1)
|
|
144
|
+
- v0.34.0 — persistent memory layer: switch session-boundary-aware, README rewrite, build fix (Sprint 8+9+10)
|
|
145
|
+
- infernoflow recap — end-of-session summary with session health score, unlogged git topics, and nudges (v0.33.7)
|
|
146
|
+
- infernoflow ask — query session memory by keyword/type, gotchas surface first (v0.33.6)
|
|
147
|
+
- infernoflow stats — value dashboard (memory, tokens injected, coverage, savings estimate) (v0.33.5)
|
|
148
|
+
- end-to-end HTTP chain tracing — resolves outbound calls to route handlers (v0.33.4)
|
|
149
|
+
- Sprint 4 — route discovery, HTTP URL extraction, entry point detection, --suggest (v0.33.3)
|
|
150
|
+
- infernoflow init --lite (3 files) + infernoflow upgrade (lite → full) (v0.33.2)
|
|
151
|
+
- infernoflow switch — AI agent handoff summary (v0.33.1)
|
|
152
|
+
- session memory (infernoflow log) + design system tracker (infernoflow theme) — capture what AI can't infer from code (v0.33.0)
|
|
153
|
+
- graph --html interactive SVG output; VS Code extension v0.5.0 with Capability Graph panel (v0.32.9)
|
|
154
|
+
- VS Code extension v0.4.0 — save-triggered contract sync
|
|
155
|
+
- ai setup numbered menu with env-var auto-detection (v0.32.3)
|
|
156
|
+
- Sprint 18C/D — dogfood capabilities.json, doctor action list, init AI nudge (v0.32.0)
|
|
157
|
+
- Sprint 18 — demo command, AI fallback nudges, test fix (v0.31.0)
|
|
158
|
+
- Sprint 17 — infernoflow test + ai commands (v0.30.0)
|
|
159
|
+
- Sprint 16C — infernoflow explain command (v0.29.0)
|
|
160
|
+
- Sprint 16B — infernoflow scaffold command (v0.28.0)
|
|
161
|
+
- Sprint 15 Liquid Layer + Sprint 16A/16D (v0.26-0.27)
|
|
162
|
+
- auto-configure Claude Code MCP + allowedTools (v0.10.25)
|
|
163
|
+
- add changelog and diff commands for enhanced version tracking
|
|
164
|
+
- add VS Code + Cursor extension
|
|
165
|
+
- add infernoflow publish command
|
|
166
|
+
- bump to 0.10.18, fix duplicate infernoDir in suggest
|
|
167
|
+
- add React-specific scanner for --adopt
|
|
168
|
+
- uninstall now removes all infernoflow artifacts completely (v0.34.2)
|
|
169
|
+
- extension sidebar icon badge — switch to createTreeView so uncovered count shows on activity bar icon
|
|
170
|
+
- graph crashes on toString() — use Map instead of plain object for funcIndex (v0.32.8)
|
|
171
|
+
- check handles bare-array capabilities.json; scenario coverage is a warning not error (v0.32.7)
|
|
172
|
+
- typo 'capabilityy' in explain file-path output
|
|
173
|
+
- explain command now accepts file paths in addition to capability IDs (v0.32.6)
|
|
174
|
+
- await import in non-async loadCapsAtRef causes syntax error (v0.32.5)
|
|
175
|
+
- add missing resolveProvider export to providerRouter.mjs (v0.32.4)
|
|
176
|
+
- Windows path bug in demo/watch/ci/monorepo/notify (v0.32.2)
|
|
177
|
+
- doctor CLI check false positive on Windows (.cmd PATH resolution)
|
|
178
|
+
- v0.37.1 — GitHub Action v1, fix switch session boundary bug
|
|
179
|
+
- v0.37.1 — fix switch showing 0 session entries (handoff append was poisoning session boundary)
|
|
180
|
+
- v0.37.0 — Windows unicode fix, memory-first init, hot files in switch, CLAUDE.md auto-update, health score tips, icon fix, cross-platform postinstall
|
|
181
|
+
- wire Polar.sh Pro checkout — real payments live
|
|
182
|
+
- point homepage to infernoflow.dev (v0.35.9)
|
|
183
|
+
- activate Formspree feedback endpoint (v0.35.8)
|
|
184
|
+
- activate PostHog EU telemetry (v0.35.7)
|
|
185
|
+
- bump to 0.35.5 (0.35.4 already published)
|
|
186
|
+
- rewrite README to reflect all capabilities (v0.32.5)
|
|
187
|
+
- v0.32.1
|
|
188
|
+
- bump to v0.32.0
|
|
189
|
+
- bump version to 0.31.0
|
|
190
|
+
- release 0.10.24
|
|
191
|
+
- release 0.10.23
|
|
192
|
+
- release 0.10.22
|
|
193
|
+
- release 0.10.21
|
|
194
|
+
- release 0.10.20
|
|
195
|
+
- release 0.10.19
|
|
196
|
+
|
|
197
|
+
- v0.37.3 — remove @scarf/scarf to fix Windows install hang
|
|
198
|
+
|
|
135
199
|
## 0.10.25 — 2026-04-22
|
|
136
200
|
|
|
137
201
|
### Added
|
package/dist/bin/infernoflow.mjs
CHANGED
|
@@ -1,282 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
(function(){if(process.platform!=="win32"||process.env.WT_SESSION||process.env.ConEmuPID||process.env.TERM_PROGRAM==="vscode")return;const s={"\u2500":"-","\u2501":"-","\u2550":"=","\u2502":"|","\u2503":"|","\u2551":"|","\u250C":"+","\u2510":"+","\u2514":"+","\u2518":"+","\u251C":"+","\u2524":"+","\u252C":"+","\u2534":"+","\u253C":"+","\xB7":"*","\u2192":"->","\u2190":"<-","\u2714":"[OK]","\u2713":"[OK]","\u2718":"[X]","\u2717":"[X]","\u26A0":"[!]",\u2139:"[i]"},e=new RegExp(Object.keys(s).join("|"),"g");function c(m){const h=m.write.bind(m);m.write=function(i,...u){if(typeof i=="string")i=i.replace(e,p=>s[p]);else if(Buffer.isBuffer(i)){const p=i.toString("utf8").replace(e,w=>s[w]);i=Buffer.from(p,"utf8")}return h(i,...u)}}c(process.stdout),c(process.stderr)})();import{readFileSync as g}from"node:fs";import{dirname as C,join as b}from"node:path";import{fileURLToPath as v}from"node:url";import{bold as r,gray as t,cyan as o,red as l}from"../lib/ui/output.mjs";const k=C(v(import.meta.url)),S=JSON.parse(g(b(k,"..","..","package.json"),"utf8")),d=S.version||"0.0.0",y={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 route discovery, entry point detection, HTTP URL extraction, capability suggestions",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",upgrade:"Upgrade a lite infernoflow setup to the full structure (scenarios, changelog, scripts)",stats:"Value dashboard \u2014 session memory, tokens injected per session, coverage %, estimated savings",ask:"Query session memory \u2014 search gotchas, decisions, and failed attempts by keyword or type",recap:"End-of-session summary \u2014 what was captured, what git changes weren't logged, session health score",uninstall:"Remove infernoflow from a project \u2014 inferno/, CLAUDE.md, MCP server, git hooks (--dry-run to preview)",feedback:"60-second CLI survey about how you use infernoflow (--form to open web form)",telemetry:"Manage anonymous usage telemetry (on | off | status) \u2014 opt-in, command names only"},f={publish:async a=>(await import("../lib/commands/publish.mjs")).publishCommand(a),diff:async a=>(await import("../lib/commands/diff.mjs")).diffCommand(a),changelog:async a=>(await import("../lib/commands/changelog.mjs")).changelogCommand(a),setup:async a=>(await import("../lib/commands/setup.mjs")).setupCommand(a),init:async a=>(await import("../lib/commands/init.mjs")).initCommand(a),"install-cursor-hooks":async a=>(await import("../lib/commands/installCursorHooks.mjs")).installCursorHooksCommand(a),"install-vscode-copilot-hooks":async a=>(await import("../lib/commands/installVsCodeCopilotHooks.mjs")).installVsCodeCopilotHooksCommand(a),check:async a=>(await import("../lib/commands/check.mjs")).checkCommand(a),status:async a=>(await import("../lib/commands/status.mjs")).statusCommand(a),"pr-impact":async a=>(await import("../lib/commands/prImpact.mjs")).prImpactCommand(a),sync:async a=>(await import("../lib/commands/syncAuto.mjs")).syncCommand(a),run:async a=>(await import("../lib/commands/run.mjs")).runCommand(a),suggest:async a=>(await import("../lib/commands/suggest.mjs")).suggestCommand(a),implement:async a=>(await import("../lib/commands/implement.mjs")).implementCommand(a),context:async a=>(await import("../lib/commands/context.mjs")).contextCommand(a),"doc-gate":async a=>(await import("../lib/commands/docGate.mjs")).docGateCommand(a),"generate-skills":async a=>(await import("../lib/commands/generateSkills.mjs")).generateSkillsCommand(a),synthesize:async a=>(await import("../lib/commands/synthesize.mjs")).synthesizeCommand(a),agent:async a=>(await import("../lib/commands/agent.mjs")).agentCommand(a),version:async a=>(await import("../lib/commands/version.mjs")).versionCommand(a),"pr-comment":async a=>(await import("../lib/commands/prComment.mjs")).prCommentCommand(a),dashboard:async a=>(await import("../lib/commands/dashboard.mjs")).dashboardCommand(a),"team-sync":async a=>(await import("../lib/commands/teamSync.mjs")).teamSyncCommand(a),onboard:async a=>(await import("../lib/commands/onboard.mjs")).onboardCommand(a),cloud:async a=>(await import("../lib/commands/cloud.mjs")).cloudCommand(a),share:async a=>(await import("../lib/commands/share.mjs")).shareCommand(a),watch:async a=>(await import("../lib/commands/watch.mjs")).watchCommand(a),ci:async a=>(await import("../lib/commands/ci.mjs")).ciCommand(a),notify:async a=>(await import("../lib/commands/notify.mjs")).notifyCommand(a),report:async a=>(await import("../lib/commands/report.mjs")).reportCommand(a),monorepo:async a=>(await import("../lib/commands/monorepo.mjs")).monorepoCommand(a),link:async a=>(await import("../lib/commands/link.mjs")).linkCommand(a),audit:async a=>(await import("../lib/commands/audit.mjs")).auditCommand(a),scout:async a=>(await import("../lib/commands/scout.mjs")).scoutCommand(a),export:async a=>(await import("../lib/commands/export.mjs")).exportCommand(a),snapshot:async a=>(await import("../lib/commands/snapshot.mjs")).snapshotCommand(a),health:async a=>(await import("../lib/commands/health.mjs")).healthCommand(a),vibe:async a=>(await import("../lib/commands/vibe.mjs")).vibeCommand(a),adopt:async a=>(await import("../lib/commands/adoptWizard.mjs")).adoptWizardCommand(a),doctor:async a=>(await import("../lib/commands/doctor.mjs")).doctorCommand(a),coverage:async a=>(await import("../lib/commands/coverage.mjs")).coverageCommand(a),review:async a=>(await import("../lib/commands/review.mjs")).reviewCommand(a),scan:async a=>(await import("../lib/commands/scan.mjs")).scanCommand(a),graph:async a=>(await import("../lib/commands/graph.mjs")).graphCommand(a),stability:async a=>(await import("../lib/commands/stability.mjs")).stabilityCommand(a),freeze:async a=>(await import("../lib/commands/stability.mjs")).freezeCommand(a),thaw:async a=>(await import("../lib/commands/stability.mjs")).thawCommand(a),why:async a=>(await import("../lib/commands/why.mjs")).whyCommand(a),impact:async a=>(await import("../lib/commands/impact.mjs")).impactCommand(a),scaffold:async a=>(await import("../lib/commands/scaffold.mjs")).scaffoldCommand(a),explain:async a=>(await import("../lib/commands/explain.mjs")).explainCommand(a),test:async a=>(await import("../lib/commands/test.mjs")).testCommand(a),ai:async a=>(await import("../lib/commands/ai.mjs")).aiCommand(a),demo:async a=>(await import("../lib/commands/demo.mjs")).demoCommand(a),log:async a=>(await import("../lib/commands/log.mjs")).logCommand(a),theme:async a=>(await import("../lib/commands/theme.mjs")).themeCommand(a),switch:async a=>(await import("../lib/commands/switch.mjs")).switchCommand(a),upgrade:async a=>(await import("../lib/commands/upgrade.mjs")).upgradeCommand(a),stats:async a=>(await import("../lib/commands/stats.mjs")).statsCommand(a),ask:async a=>(await import("../lib/commands/ask.mjs")).askCommand(a),recap:async a=>(await import("../lib/commands/recap.mjs")).recapCommand(a),uninstall:async a=>(await import("../lib/commands/uninstall.mjs")).uninstallCommand(a),feedback:async a=>(await import("../lib/commands/feedback.mjs")).feedbackCommand(a),telemetry:async a=>(await import("../lib/telemetry.mjs")).telemetryCommand(a)};function D(){const a=Object.keys(y),s=Math.max(...a.map(e=>e.length),8)+1;return Object.entries(y).map(([e,c])=>` ${e.padEnd(s," ")}${c}`).join(`
|
|
3
|
+
`)}const $={"Session Memory":["log","ask","switch","recap","stats","theme"],Context:["context","scan","suggest","check","status"],"Code Analysis":["graph","impact","why","coverage","stability","freeze","thaw","scout"],Workflow:["run","sync","watch","vibe","implement","doc-gate","synthesize","agent"],Publishing:["publish","version","changelog","diff"],Team:["team-sync","cloud","share","notify","pr-comment","pr-impact"],Quality:["health","audit","review","snapshot","export","link"],Integration:["ai","ci","coverage"],Setup:["init","setup","adopt","demo","doctor","onboard","generate-skills","upgrade","uninstall"],Advanced:["scaffold","explain","test","report","monorepo","feedback","telemetry"]};function x(){return Object.entries($).map(([s,e])=>` ${r(s+":")}
|
|
4
|
+
${e.join(" ")}`).join(`
|
|
2
5
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
(function patchUnicodeForWindows() {
|
|
7
|
-
if (process.platform !== "win32") return;
|
|
8
|
-
if (process.env.WT_SESSION) return; // Windows Terminal — supports unicode
|
|
9
|
-
if (process.env.ConEmuPID) return; // ConEmu/Cmder
|
|
10
|
-
if (process.env.TERM_PROGRAM === "vscode") return; // VS Code terminal
|
|
6
|
+
`)}const I=`
|
|
7
|
+
${r("\u{1F525} infernoflow")} ${t("v"+d)}
|
|
8
|
+
${t("Persistent memory for AI coding sessions")}
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
"─": "-", "━": "-", "═": "=",
|
|
14
|
-
"│": "|", "┃": "|", "║": "|",
|
|
15
|
-
"┌": "+", "┐": "+", "└": "+", "┘": "+",
|
|
16
|
-
"├": "+", "┤": "+", "┬": "+", "┴": "+", "┼": "+",
|
|
17
|
-
"·": "*", "→": "->", "←": "<-", "✔": "[OK]", "✓": "[OK]",
|
|
18
|
-
"✘": "[X]", "✗": "[X]", "⚠": "[!]", "ℹ": "[i]",
|
|
19
|
-
};
|
|
20
|
-
const RE = new RegExp(Object.keys(MAP).join("|"), "g");
|
|
21
|
-
|
|
22
|
-
function patch(stream) {
|
|
23
|
-
const orig = stream.write.bind(stream);
|
|
24
|
-
stream.write = function(chunk, ...args) {
|
|
25
|
-
if (typeof chunk === "string") chunk = chunk.replace(RE, c => MAP[c]);
|
|
26
|
-
else if (Buffer.isBuffer(chunk)) {
|
|
27
|
-
const s = chunk.toString("utf8").replace(RE, c => MAP[c]);
|
|
28
|
-
chunk = Buffer.from(s, "utf8");
|
|
29
|
-
}
|
|
30
|
-
return orig(chunk, ...args);
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
patch(process.stdout);
|
|
34
|
-
patch(process.stderr);
|
|
35
|
-
})();
|
|
36
|
-
|
|
37
|
-
import { readFileSync } from "node:fs";
|
|
38
|
-
import { dirname, join } from "node:path";
|
|
39
|
-
import { fileURLToPath } from "node:url";
|
|
40
|
-
import { bold, gray, cyan, red } from "../lib/ui/output.mjs";
|
|
41
|
-
|
|
42
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
43
|
-
// When installed globally: __dirname = .../infernoflow/dist/bin
|
|
44
|
-
// Root package.json lives two levels up at .../infernoflow/package.json
|
|
45
|
-
// npm always includes the root package.json, so this path is always reliable.
|
|
46
|
-
const pkg = JSON.parse(readFileSync(join(__dirname, "..", "..", "package.json"), "utf8"));
|
|
47
|
-
const VERSION = pkg.version || "0.0.0";
|
|
48
|
-
const COMMAND_DESCRIPTIONS = {
|
|
49
|
-
publish: "Bump version, update changelog, build, npm publish, git commit + push in one shot",
|
|
50
|
-
diff: "Show what capabilities changed since the last git tag (or any ref)",
|
|
51
|
-
changelog: "Draft a changelog entry from commits since the last tag",
|
|
52
|
-
setup: "One command to get fully operational — detects IDE, inits, installs hooks + MCP",
|
|
53
|
-
init: "Scaffold inferno/ in your project (or adopt existing project)",
|
|
54
|
-
"install-cursor-hooks": "Install Cursor hooks: draft agent replies to inferno/CONTEXT.draft.md",
|
|
55
|
-
"install-vscode-copilot-hooks":
|
|
56
|
-
"Install VS Code + Copilot agent hooks (Preview): draft to inferno/CONTEXT.draft.md",
|
|
57
|
-
check: "Validate contract, capabilities, scenarios, changelog",
|
|
58
|
-
status: "Show contract health at a glance",
|
|
59
|
-
"pr-impact": "Summarize PR impact on capabilities and docs",
|
|
60
|
-
sync: "Run deterministic inferno sync flow",
|
|
61
|
-
run: "One-command detect/propose/apply/validate flow",
|
|
62
|
-
"doc-gate": "Fail if code changed but docs were not updated",
|
|
63
|
-
suggest: "Generate AI prompt + apply capability updates",
|
|
64
|
-
implement: "Generate code-agent implementation prompt(s)",
|
|
65
|
-
context: "Generate AI-ready context for new sessions",
|
|
66
|
-
"generate-skills": "Generate personalised Cursor rules + skill files from your developer profile",
|
|
67
|
-
synthesize: "Auto-detect workflow patterns and synthesize reusable skills + agents",
|
|
68
|
-
agent: "Manage and run auto-synthesized agents (list | run | show | delete)",
|
|
69
|
-
version: "Smart semver bump recommendation based on capability changes (--apply to write)",
|
|
70
|
-
"pr-comment": "Post capability drift analysis as a GitHub PR comment (works in CI automatically)",
|
|
71
|
-
dashboard: "Launch local web dashboard on localhost:7337 — live contract health, capabilities, agents",
|
|
72
|
-
"team-sync": "Sync capability contract across a team via a shared git branch (push | pull | status | init)",
|
|
73
|
-
onboard: "Interactive onboarding wizard for new developers — explains infernoflow in 5 minutes",
|
|
74
|
-
cloud: "Sync capability contracts via infernoflow cloud (init | push | pull | status | dashboard)",
|
|
75
|
-
share: "Generate a public read-only HTML snapshot of your capability contract",
|
|
76
|
-
watch: "Watch source files and run suggest automatically on save",
|
|
77
|
-
ci: "CI-native check: GitHub Actions annotations, GitLab code quality, exit codes",
|
|
78
|
-
notify: "Post capability drift summary to Slack or Discord",
|
|
79
|
-
report: "Generate a weekly/monthly HTML or Markdown report of capability activity",
|
|
80
|
-
monorepo: "Manage infernoflow across monorepo packages (init | list | status | diff | sync)",
|
|
81
|
-
link: "Link capabilities to Jira, Linear, or GitHub Issues tickets",
|
|
82
|
-
audit: "Classify capabilities by sensitivity (auth, payment, PII, admin) and generate security surface map",
|
|
83
|
-
scout: "Scan source files for undocumented capabilities not yet in the contract",
|
|
84
|
-
export: "Export contract to OpenAPI, Backstage catalog-info.yaml, CSV, or Markdown",
|
|
85
|
-
snapshot: "Save/diff/restore named snapshots of the capability contract",
|
|
86
|
-
health: "Compute a 0–100 health score across coverage, docs, freshness, completeness, drift",
|
|
87
|
-
vibe: "Vibe coding mode — watches files, auto-syncs contract, regenerates context on every save",
|
|
88
|
-
adopt: "Interactive wizard to adopt infernoflow in an existing project (detect → review → wire up)",
|
|
89
|
-
doctor: "Diagnose your infernoflow setup — checks Node, git, contract, AI providers, MCP, hooks",
|
|
90
|
-
coverage: "Map test files to capabilities — show which caps have test coverage and which don't",
|
|
91
|
-
review: "AI-powered capability impact review for staged or recent git changes",
|
|
92
|
-
scan: "Deep AST scan — route discovery, entry point detection, HTTP URL extraction, capability suggestions",
|
|
93
|
-
graph: "Build capability dependency graph — shows which caps call which, detects breaking changes",
|
|
94
|
-
stability: "Show solid/liquid stability level for every capability (frozen/stable/experimental)",
|
|
95
|
-
freeze: "Mark a capability as frozen (solid) — AI will not modify it without explicit instruction",
|
|
96
|
-
thaw: "Reset a capability to experimental (liquid) — free to evolve",
|
|
97
|
-
why: "Given a file or function name — show which capability it serves, scenarios, stability, and git history",
|
|
98
|
-
impact: "Blast radius analysis — see every cap, scenario, and risk level affected before you change anything",
|
|
99
|
-
scaffold: "Generate a new capability — source skeleton, contract registration, and placeholder scenario in one command",
|
|
100
|
-
explain: "AI narrative about a capability — what it does, why it exists, what's risky, and what to test",
|
|
101
|
-
test: "Run registered scenarios for a capability — auto-generates a smoke harness if no test runner is configured",
|
|
102
|
-
ai: "Manage AI providers — setup, status, test connection (subcommands: setup | status | test | clear)",
|
|
103
|
-
demo: "Interactive walkthrough — scaffolds a sample project and runs the full capability chain end-to-end",
|
|
104
|
-
log: "Append to session memory (decisions, gotchas, failed attempts, theme changes) — what AI can't infer from code",
|
|
105
|
-
theme: "Scan fonts, colors, and CSS variables — write inferno/theme.json so AI always matches the design system",
|
|
106
|
-
switch: "Generate a handoff summary when switching AI agents — paste into the next session so nothing is lost",
|
|
107
|
-
upgrade: "Upgrade a lite infernoflow setup to the full structure (scenarios, changelog, scripts)",
|
|
108
|
-
stats: "Value dashboard — session memory, tokens injected per session, coverage %, estimated savings",
|
|
109
|
-
ask: "Query session memory — search gotchas, decisions, and failed attempts by keyword or type",
|
|
110
|
-
recap: "End-of-session summary — what was captured, what git changes weren't logged, session health score",
|
|
111
|
-
uninstall: "Remove infernoflow from a project — inferno/, CLAUDE.md, MCP server, git hooks (--dry-run to preview)",
|
|
112
|
-
feedback: "60-second CLI survey about how you use infernoflow (--form to open web form)",
|
|
113
|
-
telemetry: "Manage anonymous usage telemetry (on | off | status) — opt-in, command names only",
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
const COMMAND_HANDLERS = {
|
|
117
|
-
publish: async (args) => (await import("../lib/commands/publish.mjs")).publishCommand(args),
|
|
118
|
-
diff: async (args) => (await import("../lib/commands/diff.mjs")).diffCommand(args),
|
|
119
|
-
changelog: async (args) => (await import("../lib/commands/changelog.mjs")).changelogCommand(args),
|
|
120
|
-
setup: async (args) => (await import("../lib/commands/setup.mjs")).setupCommand(args),
|
|
121
|
-
init: async (args) => (await import("../lib/commands/init.mjs")).initCommand(args),
|
|
122
|
-
"install-cursor-hooks": async (args) =>
|
|
123
|
-
(await import("../lib/commands/installCursorHooks.mjs")).installCursorHooksCommand(args),
|
|
124
|
-
"install-vscode-copilot-hooks": async (args) =>
|
|
125
|
-
(await import("../lib/commands/installVsCodeCopilotHooks.mjs")).installVsCodeCopilotHooksCommand(args),
|
|
126
|
-
check: async (args) => (await import("../lib/commands/check.mjs")).checkCommand(args),
|
|
127
|
-
status: async (args) => (await import("../lib/commands/status.mjs")).statusCommand(args),
|
|
128
|
-
"pr-impact": async (args) => (await import("../lib/commands/prImpact.mjs")).prImpactCommand(args),
|
|
129
|
-
sync: async (args) => (await import("../lib/commands/syncAuto.mjs")).syncCommand(args),
|
|
130
|
-
run: async (args) => (await import("../lib/commands/run.mjs")).runCommand(args),
|
|
131
|
-
suggest: async (args) => (await import("../lib/commands/suggest.mjs")).suggestCommand(args),
|
|
132
|
-
implement: async (args) => (await import("../lib/commands/implement.mjs")).implementCommand(args),
|
|
133
|
-
context: async (args) => (await import("../lib/commands/context.mjs")).contextCommand(args),
|
|
134
|
-
"doc-gate": async (args) => (await import("../lib/commands/docGate.mjs")).docGateCommand(args),
|
|
135
|
-
"generate-skills": async (args) => (await import("../lib/commands/generateSkills.mjs")).generateSkillsCommand(args),
|
|
136
|
-
synthesize: async (args) => (await import("../lib/commands/synthesize.mjs")).synthesizeCommand(args),
|
|
137
|
-
agent: async (args) => (await import("../lib/commands/agent.mjs")).agentCommand(args),
|
|
138
|
-
version: async (args) => (await import("../lib/commands/version.mjs")).versionCommand(args),
|
|
139
|
-
"pr-comment": async (args) => (await import("../lib/commands/prComment.mjs")).prCommentCommand(args),
|
|
140
|
-
dashboard: async (args) => (await import("../lib/commands/dashboard.mjs")).dashboardCommand(args),
|
|
141
|
-
"team-sync": async (args) => (await import("../lib/commands/teamSync.mjs")).teamSyncCommand(args),
|
|
142
|
-
onboard: async (args) => (await import("../lib/commands/onboard.mjs")).onboardCommand(args),
|
|
143
|
-
cloud: async (args) => (await import("../lib/commands/cloud.mjs")).cloudCommand(args),
|
|
144
|
-
share: async (args) => (await import("../lib/commands/share.mjs")).shareCommand(args),
|
|
145
|
-
watch: async (args) => (await import("../lib/commands/watch.mjs")).watchCommand(args),
|
|
146
|
-
ci: async (args) => (await import("../lib/commands/ci.mjs")).ciCommand(args),
|
|
147
|
-
notify: async (args) => (await import("../lib/commands/notify.mjs")).notifyCommand(args),
|
|
148
|
-
report: async (args) => (await import("../lib/commands/report.mjs")).reportCommand(args),
|
|
149
|
-
monorepo: async (args) => (await import("../lib/commands/monorepo.mjs")).monorepoCommand(args),
|
|
150
|
-
link: async (args) => (await import("../lib/commands/link.mjs")).linkCommand(args),
|
|
151
|
-
audit: async (args) => (await import("../lib/commands/audit.mjs")).auditCommand(args),
|
|
152
|
-
scout: async (args) => (await import("../lib/commands/scout.mjs")).scoutCommand(args),
|
|
153
|
-
export: async (args) => (await import("../lib/commands/export.mjs")).exportCommand(args),
|
|
154
|
-
snapshot: async (args) => (await import("../lib/commands/snapshot.mjs")).snapshotCommand(args),
|
|
155
|
-
health: async (args) => (await import("../lib/commands/health.mjs")).healthCommand(args),
|
|
156
|
-
vibe: async (args) => (await import("../lib/commands/vibe.mjs")).vibeCommand(args),
|
|
157
|
-
adopt: async (args) => (await import("../lib/commands/adoptWizard.mjs")).adoptWizardCommand(args),
|
|
158
|
-
doctor: async (args) => (await import("../lib/commands/doctor.mjs")).doctorCommand(args),
|
|
159
|
-
coverage: async (args) => (await import("../lib/commands/coverage.mjs")).coverageCommand(args),
|
|
160
|
-
review: async (args) => (await import("../lib/commands/review.mjs")).reviewCommand(args),
|
|
161
|
-
scan: async (args) => (await import("../lib/commands/scan.mjs")).scanCommand(args),
|
|
162
|
-
graph: async (args) => (await import("../lib/commands/graph.mjs")).graphCommand(args),
|
|
163
|
-
stability: async (args) => (await import("../lib/commands/stability.mjs")).stabilityCommand(args),
|
|
164
|
-
freeze: async (args) => (await import("../lib/commands/stability.mjs")).freezeCommand(args),
|
|
165
|
-
thaw: async (args) => (await import("../lib/commands/stability.mjs")).thawCommand(args),
|
|
166
|
-
why: async (args) => (await import("../lib/commands/why.mjs")).whyCommand(args),
|
|
167
|
-
impact: async (args) => (await import("../lib/commands/impact.mjs")).impactCommand(args),
|
|
168
|
-
scaffold: async (args) => (await import("../lib/commands/scaffold.mjs")).scaffoldCommand(args),
|
|
169
|
-
explain: async (args) => (await import("../lib/commands/explain.mjs")).explainCommand(args),
|
|
170
|
-
test: async (args) => (await import("../lib/commands/test.mjs")).testCommand(args),
|
|
171
|
-
ai: async (args) => (await import("../lib/commands/ai.mjs")).aiCommand(args),
|
|
172
|
-
demo: async (args) => (await import("../lib/commands/demo.mjs")).demoCommand(args),
|
|
173
|
-
log: async (args) => (await import("../lib/commands/log.mjs")).logCommand(args),
|
|
174
|
-
theme: async (args) => (await import("../lib/commands/theme.mjs")).themeCommand(args),
|
|
175
|
-
switch: async (args) => (await import("../lib/commands/switch.mjs")).switchCommand(args),
|
|
176
|
-
upgrade: async (args) => (await import("../lib/commands/upgrade.mjs")).upgradeCommand(args),
|
|
177
|
-
stats: async (args) => (await import("../lib/commands/stats.mjs")).statsCommand(args),
|
|
178
|
-
ask: async (args) => (await import("../lib/commands/ask.mjs")).askCommand(args),
|
|
179
|
-
recap: async (args) => (await import("../lib/commands/recap.mjs")).recapCommand(args),
|
|
180
|
-
uninstall: async (args) => (await import("../lib/commands/uninstall.mjs")).uninstallCommand(args),
|
|
181
|
-
feedback: async (args) => (await import("../lib/commands/feedback.mjs")).feedbackCommand(args),
|
|
182
|
-
telemetry: async (args) => (await import("../lib/telemetry.mjs")).telemetryCommand(args),
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
function formatCommandsHelp() {
|
|
186
|
-
const names = Object.keys(COMMAND_DESCRIPTIONS);
|
|
187
|
-
const w = Math.max(...names.map((n) => n.length), 8) + 1;
|
|
188
|
-
return Object.entries(COMMAND_DESCRIPTIONS)
|
|
189
|
-
.map(([name, desc]) => ` ${name.padEnd(w, " ")}${desc}`)
|
|
190
|
-
.join("\n");
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// ── Full grouped command list (infernoflow commands) ──────────────────────────
|
|
194
|
-
const COMMAND_GROUPS = {
|
|
195
|
-
"Session Memory": ["log", "ask", "switch", "recap", "stats", "theme"],
|
|
196
|
-
"Context": ["context", "scan", "suggest", "check", "status"],
|
|
197
|
-
"Code Analysis": ["graph", "impact", "why", "coverage", "stability", "freeze", "thaw", "scout"],
|
|
198
|
-
"Workflow": ["run", "sync", "watch", "vibe", "implement", "doc-gate", "synthesize", "agent"],
|
|
199
|
-
"Publishing": ["publish", "version", "changelog", "diff"],
|
|
200
|
-
"Team": ["team-sync", "cloud", "share", "notify", "pr-comment", "pr-impact"],
|
|
201
|
-
"Quality": ["health", "audit", "review", "snapshot", "export", "link"],
|
|
202
|
-
"Integration": ["ai", "ci", "coverage"],
|
|
203
|
-
"Setup": ["init", "setup", "adopt", "demo", "doctor", "onboard", "generate-skills", "upgrade", "uninstall"],
|
|
204
|
-
"Advanced": ["scaffold", "explain", "test", "report", "monorepo", "feedback", "telemetry"],
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
function formatCommandGroups() {
|
|
208
|
-
const w = 18;
|
|
209
|
-
return Object.entries(COMMAND_GROUPS).map(([group, cmds]) =>
|
|
210
|
-
` ${bold(group + ":")}
|
|
211
|
-
${cmds.join(" ")}`
|
|
212
|
-
).join("\n\n");
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const HELP = `
|
|
216
|
-
${bold("🔥 infernoflow")} ${gray("v" + VERSION)}
|
|
217
|
-
${gray("Persistent memory for AI coding sessions")}
|
|
218
|
-
|
|
219
|
-
${bold("Usage:")}
|
|
10
|
+
${r("Usage:")}
|
|
220
11
|
infernoflow [command] [options]
|
|
221
12
|
|
|
222
|
-
${
|
|
223
|
-
${
|
|
224
|
-
${
|
|
225
|
-
${
|
|
226
|
-
${
|
|
227
|
-
${
|
|
228
|
-
|
|
229
|
-
${
|
|
230
|
-
${
|
|
231
|
-
${
|
|
232
|
-
${
|
|
233
|
-
|
|
234
|
-
${
|
|
235
|
-
${
|
|
236
|
-
`;
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const { observeCommandStart } = await import("../lib/learning/observe.mjs");
|
|
245
|
-
const cmdForObserve = process.argv[2];
|
|
246
|
-
if (cmdForObserve && !cmdForObserve.startsWith("-")) {
|
|
247
|
-
observeCommandStart(infernoDir, cmdForObserve);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
} catch {}
|
|
251
|
-
|
|
252
|
-
const [, , cmd, ...rest] = process.argv;
|
|
253
|
-
|
|
254
|
-
if (!cmd || cmd === "--help" || cmd === "-h") {
|
|
255
|
-
console.log(HELP);
|
|
256
|
-
process.exit(0);
|
|
257
|
-
}
|
|
258
|
-
if (cmd === "--version" || cmd === "-v") {
|
|
259
|
-
console.log(VERSION);
|
|
260
|
-
process.exit(0);
|
|
261
|
-
}
|
|
262
|
-
if (cmd === "commands") {
|
|
263
|
-
console.log(`\n ${bold("🔥 infernoflow")} ${gray("v" + VERSION)} ${gray("— all commands")}\n`);
|
|
264
|
-
console.log(formatCommandGroups());
|
|
265
|
-
console.log(`\n ${gray("Run")} ${cyan("infernoflow <command> --help")} ${gray("for options.")}\n`);
|
|
266
|
-
process.exit(0);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const commands = Object.keys(COMMAND_HANDLERS);
|
|
270
|
-
|
|
271
|
-
if (!commands.includes(cmd)) {
|
|
272
|
-
console.error(red(`\nUnknown command: ${cmd}`));
|
|
273
|
-
console.error(gray(`Run: infernoflow commands (see all commands)`));
|
|
274
|
-
console.error(gray("Run: infernoflow --help (quick start)\n"));
|
|
275
|
-
process.exit(1);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const args = [cmd, ...rest];
|
|
279
|
-
COMMAND_HANDLERS[cmd](args).catch((err) => {
|
|
280
|
-
console.error(red("\nError: ") + err.message);
|
|
281
|
-
process.exit(1);
|
|
282
|
-
});
|
|
13
|
+
${r("Core Commands:")}
|
|
14
|
+
${o("log")} ${t('"..."')} Add to session memory ${t("(--type gotcha|decision|attempt|preference)")}
|
|
15
|
+
${o("ask")} ${t('"..."')} Search your memory by keyword ${t("(gotchas surface first)")}
|
|
16
|
+
${o("switch")} Generate handoff for next AI agent
|
|
17
|
+
${o("recap")} End-of-session health score + unlogged changes
|
|
18
|
+
${o("status")} Contract health at a glance
|
|
19
|
+
|
|
20
|
+
${r("Getting Started:")}
|
|
21
|
+
${o("setup")} One command to get fully operational
|
|
22
|
+
${o("demo")} Interactive walkthrough ${t("(5 minutes)")}
|
|
23
|
+
${o("doctor")} Diagnose your setup
|
|
24
|
+
|
|
25
|
+
${t("Run")} ${o("infernoflow commands")} ${t("to see all 50+ commands.")}
|
|
26
|
+
${t("Run")} ${o("infernoflow <command> --help")} ${t("for command-specific options.")}
|
|
27
|
+
`;import*as A from"node:fs";import*as M from"node:path";try{const a=M.join(process.cwd(),"inferno");if(A.existsSync(a)){const{observeCommandStart:s}=await import("../lib/learning/observe.mjs"),e=process.argv[2];e&&!e.startsWith("-")&&s(a,e)}}catch{}const[,,n,...O]=process.argv;(!n||n==="--help"||n==="-h")&&(console.log(I),process.exit(0)),(n==="--version"||n==="-v")&&(console.log(d),process.exit(0)),n==="commands"&&(console.log(`
|
|
28
|
+
${r("\u{1F525} infernoflow")} ${t("v"+d)} ${t("\u2014 all commands")}
|
|
29
|
+
`),console.log(x()),console.log(`
|
|
30
|
+
${t("Run")} ${o("infernoflow <command> --help")} ${t("for options.")}
|
|
31
|
+
`),process.exit(0));const R=Object.keys(f);R.includes(n)||(console.error(l(`
|
|
32
|
+
Unknown command: ${n}`)),console.error(t("Run: infernoflow commands (see all commands)")),console.error(t(`Run: infernoflow --help (quick start)
|
|
33
|
+
`)),process.exit(1));const P=[n,...O];f[n](P).catch(a=>{console.error(l(`
|
|
34
|
+
Error: `)+a.message),process.exit(1)});
|
|
@@ -1,128 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* lib/adopters/angular.mjs
|
|
3
|
-
* Angular-specific scanner for --adopt.
|
|
4
|
-
* Detects components, routes, services, and UI capabilities from Angular projects.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import * as fs from "node:fs";
|
|
8
|
-
import * as path from "node:path";
|
|
9
|
-
|
|
10
|
-
function safeRead(filePath) {
|
|
11
|
-
try { return fs.readFileSync(filePath, "utf8"); } catch { return ""; }
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Scan an Angular project's source files and return detected signals.
|
|
16
|
-
*
|
|
17
|
-
* Returns:
|
|
18
|
-
* {
|
|
19
|
-
* components: string[], // component class names
|
|
20
|
-
* routes: string[], // route paths detected
|
|
21
|
-
* services: string[], // service class names
|
|
22
|
-
* lazyModules: string[], // lazy-loaded module paths
|
|
23
|
-
* formFields: string[], // reactive form control names
|
|
24
|
-
* capabilities: { id, title, reason, sourceFiles }[]
|
|
25
|
-
* }
|
|
26
|
-
*/
|
|
27
|
-
export function scanAngular(cwd, files) {
|
|
28
|
-
const components = new Set();
|
|
29
|
-
const routes = new Set();
|
|
30
|
-
const services = new Set();
|
|
31
|
-
const lazyModules = new Set();
|
|
32
|
-
const formFields = new Set();
|
|
33
|
-
const capabilityMap = new Map(); // capId → { id, title, reason, sourceFiles: Set }
|
|
34
|
-
|
|
35
|
-
const addCap = (id, title, reason, filePath) => {
|
|
36
|
-
if (!capabilityMap.has(id)) {
|
|
37
|
-
capabilityMap.set(id, { id, title, reason, sourceFiles: new Set() });
|
|
38
|
-
}
|
|
39
|
-
capabilityMap.get(id).sourceFiles.add(path.relative(cwd, filePath));
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
for (const filePath of files) {
|
|
43
|
-
const rel = path.relative(cwd, filePath).replace(/\\/g, "/");
|
|
44
|
-
const text = safeRead(filePath);
|
|
45
|
-
if (!text) continue;
|
|
46
|
-
|
|
47
|
-
// ── Component detection ───────────────────────────────────────────────
|
|
48
|
-
if (/\.(ts)$/.test(filePath)) {
|
|
49
|
-
// @Component decorated classes
|
|
50
|
-
const compMatches = text.matchAll(/@Component\s*\([^)]*\)[\s\S]*?class\s+([A-Z][A-Za-z0-9_]*Component)/g);
|
|
51
|
-
for (const m of compMatches) {
|
|
52
|
-
const name = m[1].replace(/Component$/, "");
|
|
53
|
-
components.add(name);
|
|
54
|
-
// Derive a capability from the component name
|
|
55
|
-
const capId = name.endsWith("Page") || name.endsWith("View")
|
|
56
|
-
? `View${name.replace(/(Page|View)$/, "")}`
|
|
57
|
-
: `View${name}`;
|
|
58
|
-
addCap(capId, `View ${name.replace(/([A-Z])/g, " $1").trim()}`, `@Component class detected: ${m[1]}`, filePath);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Service classes → often wrap API capabilities
|
|
62
|
-
const svcMatches = text.matchAll(/@Injectable[\s\S]*?class\s+([A-Z][A-Za-z0-9_]*Service)/g);
|
|
63
|
-
for (const m of svcMatches) {
|
|
64
|
-
services.add(m[1]);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Reactive form controls → hint at form-based capabilities
|
|
68
|
-
const formGroups = text.matchAll(/FormBuilder|FormGroup|FormControl/g);
|
|
69
|
-
if ([...formGroups].length > 0) {
|
|
70
|
-
const controlNames = text.matchAll(/['"]([a-zA-Z][a-zA-Z0-9_]*)['"]:\s*(?:this\.\w+\.control|new FormControl|\[)/g);
|
|
71
|
-
for (const m of controlNames) formFields.add(m[1]);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// ── Route detection ───────────────────────────────────────────────────
|
|
76
|
-
if (rel.includes("routing") || rel.includes("routes") || rel.endsWith("app.routes.ts")) {
|
|
77
|
-
// path: 'some/route'
|
|
78
|
-
const pathMatches = text.matchAll(/\bpath\s*:\s*['"`]([^'"`]+)['"`]/g);
|
|
79
|
-
for (const m of pathMatches) {
|
|
80
|
-
const routePath = m[1].trim();
|
|
81
|
-
if (routePath && routePath !== "**" && !routePath.startsWith(":")) {
|
|
82
|
-
routes.add(routePath);
|
|
83
|
-
// Each top-level route = likely a view capability
|
|
84
|
-
const parts = routePath.split("/").filter(Boolean);
|
|
85
|
-
if (parts.length >= 1) {
|
|
86
|
-
const name = parts[parts.length - 1];
|
|
87
|
-
const capId = "View" + name.charAt(0).toUpperCase() + name.slice(1).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
88
|
-
const title = "View " + name.replace(/-/g, " ").replace(/\b\w/g, c => c.toUpperCase());
|
|
89
|
-
addCap(capId, title, `Route detected: /${routePath}`, filePath);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Lazy-loaded modules
|
|
95
|
-
const lazyMatches = text.matchAll(/loadChildren\s*:\s*\(\s*\)\s*=>\s*import\s*\(['"`]([^'"`]+)['"`]\)/g);
|
|
96
|
-
for (const m of lazyMatches) lazyModules.add(m[1]);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// ── Template-based capability detection (.html) ───────────────────────
|
|
100
|
-
if (/\.html$/.test(filePath)) {
|
|
101
|
-
// Router links hint at navigation capabilities
|
|
102
|
-
const routerLinks = text.matchAll(/routerLink\s*=\s*['"`]([^'"`]+)['"`]/g);
|
|
103
|
-
for (const m of routerLinks) routes.add(m[1].replace(/^\//, ""));
|
|
104
|
-
|
|
105
|
-
// (click) event bindings → actions
|
|
106
|
-
const clickHandlers = text.matchAll(/\(click\)\s*=\s*["']([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/g);
|
|
107
|
-
for (const m of clickHandlers) {
|
|
108
|
-
const handler = m[1];
|
|
109
|
-
// Heuristic: delete/remove/create handlers → capabilities
|
|
110
|
-
if (/delete|remove/i.test(handler)) addCap("DeleteItem", "Delete Item", `(click) handler: ${handler}`, filePath);
|
|
111
|
-
if (/create|add|new/i.test(handler)) addCap("CreateItem", "Create Item", `(click) handler: ${handler}`, filePath);
|
|
112
|
-
if (/submit|save/i.test(handler)) addCap("UpdateItem", "Update Item", `(click) handler: ${handler}`, filePath);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
components: Array.from(components).sort(),
|
|
119
|
-
routes: Array.from(routes).sort(),
|
|
120
|
-
services: Array.from(services).sort(),
|
|
121
|
-
lazyModules: Array.from(lazyModules).sort(),
|
|
122
|
-
formFields: Array.from(formFields).sort(),
|
|
123
|
-
capabilities: Array.from(capabilityMap.values()).map(c => ({
|
|
124
|
-
...c,
|
|
125
|
-
sourceFiles: Array.from(c.sourceFiles),
|
|
126
|
-
})),
|
|
127
|
-
};
|
|
128
|
-
}
|
|
1
|
+
import*as z from"node:fs";import*as w from"node:path";function S(m){try{return z.readFileSync(m,"utf8")}catch{return""}}function v(m,y){const d=new Set,p=new Set,h=new Set,u=new Set,A=new Set,f=new Map,c=(t,n,o,a)=>{f.has(t)||f.set(t,{id:t,title:n,reason:o,sourceFiles:new Set}),f.get(t).sourceFiles.add(w.relative(m,a))};for(const t of y){const n=w.relative(m,t).replace(/\\/g,"/"),o=S(t);if(o){if(/\.(ts)$/.test(t)){const a=o.matchAll(/@Component\s*\([^)]*\)[\s\S]*?class\s+([A-Z][A-Za-z0-9_]*Component)/g);for(const e of a){const s=e[1].replace(/Component$/,"");d.add(s);const i=s.endsWith("Page")||s.endsWith("View")?`View${s.replace(/(Page|View)$/,"")}`:`View${s}`;c(i,`View ${s.replace(/([A-Z])/g," $1").trim()}`,`@Component class detected: ${e[1]}`,t)}const l=o.matchAll(/@Injectable[\s\S]*?class\s+([A-Z][A-Za-z0-9_]*Service)/g);for(const e of l)h.add(e[1]);if([...o.matchAll(/FormBuilder|FormGroup|FormControl/g)].length>0){const e=o.matchAll(/['"]([a-zA-Z][a-zA-Z0-9_]*)['"]:\s*(?:this\.\w+\.control|new FormControl|\[)/g);for(const s of e)A.add(s[1])}}if(n.includes("routing")||n.includes("routes")||n.endsWith("app.routes.ts")){const a=o.matchAll(/\bpath\s*:\s*['"`]([^'"`]+)['"`]/g);for(const r of a){const e=r[1].trim();if(e&&e!=="**"&&!e.startsWith(":")){p.add(e);const s=e.split("/").filter(Boolean);if(s.length>=1){const i=s[s.length-1],C="View"+i.charAt(0).toUpperCase()+i.slice(1).replace(/-([a-z])/g,(g,F)=>F.toUpperCase()),$="View "+i.replace(/-/g," ").replace(/\b\w/g,g=>g.toUpperCase());c(C,$,`Route detected: /${e}`,t)}}}const l=o.matchAll(/loadChildren\s*:\s*\(\s*\)\s*=>\s*import\s*\(['"`]([^'"`]+)['"`]\)/g);for(const r of l)u.add(r[1])}if(/\.html$/.test(t)){const a=o.matchAll(/routerLink\s*=\s*['"`]([^'"`]+)['"`]/g);for(const r of a)p.add(r[1].replace(/^\//,""));const l=o.matchAll(/\(click\)\s*=\s*["']([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/g);for(const r of l){const e=r[1];/delete|remove/i.test(e)&&c("DeleteItem","Delete Item",`(click) handler: ${e}`,t),/create|add|new/i.test(e)&&c("CreateItem","Create Item",`(click) handler: ${e}`,t),/submit|save/i.test(e)&&c("UpdateItem","Update Item",`(click) handler: ${e}`,t)}}}}return{components:Array.from(d).sort(),routes:Array.from(p).sort(),services:Array.from(h).sort(),lazyModules:Array.from(u).sort(),formFields:Array.from(A).sort(),capabilities:Array.from(f.values()).map(t=>({...t,sourceFiles:Array.from(t.sourceFiles)}))}}export{v as scanAngular};
|