infernoflow 0.43.4 → 0.43.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -19
- package/dist/bin/infernoflow.mjs +24 -25
- package/dist/lib/commands/adopt.mjs +10 -10
- package/dist/lib/commands/graph.mjs +55 -94
- package/dist/lib/commands/log.mjs +17 -17
- package/package.json +3 -5
- package/dist/lib/cloud/credentials.mjs +0 -2
- package/dist/lib/cloud/supabase.mjs +0 -1
- package/dist/lib/commands/cloud.mjs +0 -10
- package/dist/lib/commands/dashboard.mjs +0 -442
- package/dist/lib/commands/login.mjs +0 -35
- package/scripts/postinstall.js +0 -2
package/README.md
CHANGED
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
[](./package.json)
|
|
10
10
|
[](https://docs.npmjs.com/cli/v10/commands/npm-audit)
|
|
11
11
|
[](https://marketplace.visualstudio.com/items?itemName=infernoflow.infernoflow)
|
|
12
|
+
[](#)
|
|
13
|
+
[](./LICENSE)
|
|
14
|
+
|
|
15
|
+
> **⚠ Alpha — actively iterating.** Local memory operations (logging, search, handoff, AI rule-file injection) are stable. The product does ONE thing: persistent memory that any AI tool can read. No network calls in default command paths. See [SECURITY.md](./SECURITY.md) for the full data-flow disclosure.
|
|
12
16
|
|
|
13
17
|
## The 60-second pitch
|
|
14
18
|
|
|
@@ -48,7 +52,7 @@ These five cover 90% of usage:
|
|
|
48
52
|
| `infernoflow recap` | End-of-session summary with health score and unlogged-change detection. |
|
|
49
53
|
| `infernoflow status` | Quick session-memory health check. |
|
|
50
54
|
|
|
51
|
-
Run `infernoflow commands` for the full grouped list (
|
|
55
|
+
Run `infernoflow commands` for the full grouped list (12 visible commands across Session Memory, Code Analysis, Workflow, Cloud, Setup; ~40 more available as aliases for backward compat).
|
|
52
56
|
|
|
53
57
|
## The AI Memory Protocol (AMP)
|
|
54
58
|
|
|
@@ -103,15 +107,29 @@ ext install infernoflow.infernoflow
|
|
|
103
107
|
|
|
104
108
|
Or browse it [on the Marketplace](https://marketplace.visualstudio.com/items?itemName=infernoflow.infernoflow). Activates automatically on any project containing `.ai-memory/sessions.jsonl` or `inferno/`.
|
|
105
109
|
|
|
106
|
-
**What you get (v0.7.
|
|
110
|
+
**What you get (v0.7.4):**
|
|
111
|
+
|
|
112
|
+
The extension closes a real loop: **capture** the right thing → **rank** it for the file you're editing → **inject** it into the AI's context automatically.
|
|
113
|
+
|
|
114
|
+
#### Capture (zero-typing memory)
|
|
115
|
+
|
|
116
|
+
- **Auto-capture popup** — when you edit the same file 5+ times in 10 minutes, a popup asks "Stuck on something?" with [Log Gotcha] [Log Attempt] [Dismiss] buttons. Click once → entry is auto-written with timestamp, file:line, the enclosing function name, a 5-line code-context window, nearby TypeScript/ESLint diagnostics, AND failure/success-keyword excerpts from the recent agent conversation.
|
|
117
|
+
- **Agent conversation harvest** — if you've installed Cursor / Copilot hooks (`infernoflow install-cursor-hooks`), every agent exchange writes to `CONTEXT.draft.md`. Auto-capture pulls failure-signal lines (`error`, `cannot`, `still failing`) AND resolution-signal lines (`got it`, `the fix was`, `turns out`) so the gotcha tells the full arc — breakdown and breakthrough.
|
|
118
|
+
- **AI session summarize** — new "Summarize session with AI" action runs the recent conversation through Copilot (via VS Code LM API) or your configured AI provider, proposes 1–6 structured memory entries, shows them in a multi-select picker. Tick which to keep.
|
|
119
|
+
- **Bulk delete + orphan cleanup** — "Manage entries…" picker grouped by date for cleanups. "Cleanup orphaned entries…" finds entries whose source files were deleted (e.g., after refactor) for bulk removal.
|
|
107
120
|
|
|
108
|
-
|
|
109
|
-
|
|
121
|
+
#### Inject (closing the loop)
|
|
122
|
+
|
|
123
|
+
- **AI Context for [current file]** sidebar section — shows top 5 most-relevant entries for whatever file you're editing, scored by: same file (+100), same directory (+40), same extension (+10), recent (+20), type-weighted. Updates as you switch editors.
|
|
124
|
+
- **Auto-sync rule files** (default on, debounced 1.5s) — on every memory change OR editor switch, the extension rewrites `.cursorrules` / `CLAUDE.md` / `.github/copilot-instructions.md` with the current ranking. Recent git commits go at the top, top-5 most-relevant memory entries next, older context collapsed under `<details>`. Idempotent — uses delimiter comments, doesn't trample your manual edits. **Result: when you open a NEW AI chat in the same VS Code session, the AI tool reads the updated rule files at chat start and sees the right gotchas first — no manual "rebuild" step required.**
|
|
110
125
|
- **Inline CodeLens** — `🔥 ⚠ 2 gotchas · 1 failed · 1 decision` above any file with logged entries. Per-line annotations at gotcha locations. Click for full entry detail.
|
|
111
|
-
- **Editor diagnostics** — gotchas as yellow Warnings, failed attempts as blue Information squiggles.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
126
|
+
- **Editor diagnostics** — gotchas as yellow Warnings, failed attempts as blue Information squiggles. Copilot reads them.
|
|
127
|
+
|
|
128
|
+
#### Surface
|
|
129
|
+
|
|
130
|
+
- **Sidebar memory panel** — Session Health (A–F), AI Context for [current file], Gotchas, Decisions, Failed Attempts, Quick Actions, CLI Tools (one-click access to 11 CLI commands).
|
|
131
|
+
- **Status bar** — `🔥 B 65 · ⚠3 · ✓2 · ❌1 · 📋 Switch` with grade-based color coding.
|
|
132
|
+
- **Help tooltips everywhere** — hover any sidebar item for a description of what it does, when to use it, what happens when clicked.
|
|
115
133
|
- **Keyboard shortcuts** — `Ctrl+Alt+G/D/A/S/R` for log gotcha / log decision / ask memory / generate handoff / show recap.
|
|
116
134
|
|
|
117
135
|
## Cursor / VS Code MCP integration
|
|
@@ -145,17 +163,6 @@ After install-cursor-hooks, your AI agent can call infernoflow directly in chat:
|
|
|
145
163
|
|
|
146
164
|
The `amp_*` tools are vendor-neutral aliases following the [AMP MCP spec §7.3](docs/protocol/PROTOCOL.md#73-mcp-tool-interface). Any AMP-Full client only needs to know these names — the `infernoflow_*` set stays for backward compat.
|
|
147
165
|
|
|
148
|
-
## Cloud sync (optional)
|
|
149
|
-
|
|
150
|
-
```bash
|
|
151
|
-
infernoflow login # GitHub Device Flow — no PKCE, no callback server
|
|
152
|
-
infernoflow whoami
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
Once logged in, every `infernoflow log` quietly mirrors the entry to a Supabase project so your memory survives across machines. Push is fire-and-forget; local always succeeds even if cloud is down.
|
|
156
|
-
|
|
157
|
-
> **Auth model (v0.38.x):** the cloud currently uses anonymous-key writes with a per-user `user_token` column. Anyone with the public anon key can write rows — fine for solo dev, not yet a security boundary. The schema is forward-compatible with authenticated mode; see `scripts/supabase-schema.sql` for the migration path.
|
|
158
|
-
|
|
159
166
|
## Capability contracts (advanced)
|
|
160
167
|
|
|
161
168
|
The "memory" track above (Tier 1) is what most users want. infernoflow also ships a heavier "contracts" track for teams that want machine-checked guarantees about what their codebase *does*:
|
package/dist/bin/infernoflow.mjs
CHANGED
|
@@ -1,42 +1,41 @@
|
|
|
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 t={"\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]"},n=new RegExp(Object.keys(t).join("|"),"g");function i(c){const p=c.write.bind(c);c.write=function(s,...d){if(typeof s=="string")s=s.replace(n,f=>t[f]);else if(Buffer.isBuffer(s)){const f=s.toString("utf8").replace(n,k=>t[k]);s=Buffer.from(f,"utf8")}return p(s,...d)}}i(process.stdout),i(process.stderr)})();import{readFileSync as $}from"node:fs";import{dirname as S,join as g}from"node:path";import{fileURLToPath as x}from"node:url";import{bold as m,gray as e,cyan as a,red as u}from"../lib/ui/output.mjs";const A=S(x(import.meta.url));function O(o){for(const t of[g(o,"..","..","package.json"),g(o,"..","package.json")])try{return JSON.parse($(t,"utf8"))}catch{}return{version:"0.0.0-source"}}const I=O(A),h=I.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",dashboard:"Launch local web dashboard on localhost:7337 \u2014 live contract health, capabilities, agents",login:"Sign in with GitHub \u2014 syncs session memory to the cloud on every log",logout:"Sign out and remove local credentials",whoami:"Show currently logged-in user",cloud:"Sync capability contracts via infernoflow cloud (init | push | pull | status | dashboard)",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",monorepo:"Manage infernoflow across monorepo packages (init | list | status | diff | sync)",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",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",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)",contract:"Capability contracts \u2014 scan, freeze, impact, graph, scaffold, etc. (run: infernoflow contract)",dev:"Maintenance & integration \u2014 publish, changelog, dashboard, ai, ci, sync, etc. (run: infernoflow dev)",amp:"AI Memory Protocol \u2014 status, migrate from legacy, validate (run: infernoflow amp)"},l={publish:async o=>(await import("../lib/commands/publish.mjs")).publishCommand(o),diff:async o=>(await import("../lib/commands/diff.mjs")).diffCommand(o),changelog:async o=>(await import("../lib/commands/changelog.mjs")).changelogCommand(o),setup:async o=>(await import("../lib/commands/setup.mjs")).setupCommand(o),init:async o=>(await import("../lib/commands/init.mjs")).initCommand(o),"install-cursor-hooks":async o=>(await import("../lib/commands/installCursorHooks.mjs")).installCursorHooksCommand(o),"install-vscode-copilot-hooks":async o=>(await import("../lib/commands/installVsCodeCopilotHooks.mjs")).installVsCodeCopilotHooksCommand(o),check:async o=>(await import("../lib/commands/check.mjs")).checkCommand(o),status:async o=>(await import("../lib/commands/status.mjs")).statusCommand(o),"pr-impact":async o=>(await import("../lib/commands/prImpact.mjs")).prImpactCommand(o),sync:async o=>(await import("../lib/commands/syncAuto.mjs")).syncCommand(o),run:async o=>(await import("../lib/commands/run.mjs")).runCommand(o),suggest:async o=>(await import("../lib/commands/suggest.mjs")).suggestCommand(o),implement:async o=>(await import("../lib/commands/implement.mjs")).implementCommand(o),context:async o=>(await import("../lib/commands/context.mjs")).contextCommand(o),"doc-gate":async o=>(await import("../lib/commands/docGate.mjs")).docGateCommand(o),"generate-skills":async o=>(await import("../lib/commands/generateSkills.mjs")).generateSkillsCommand(o),dashboard:async o=>(await import("../lib/commands/dashboard.mjs")).dashboardCommand(o),login:async o=>(await import("../lib/commands/login.mjs")).loginCommand(o),logout:async()=>(await import("../lib/commands/login.mjs")).logoutCommand(),whoami:async()=>(await import("../lib/commands/login.mjs")).whoamiCommand(),cloud:async o=>(await import("../lib/commands/cloud.mjs")).cloudCommand(o),watch:async o=>(await import("../lib/commands/watch.mjs")).watchCommand(o),ci:async o=>(await import("../lib/commands/ci.mjs")).ciCommand(o),notify:async o=>(await import("../lib/commands/notify.mjs")).notifyCommand(o),monorepo:async o=>(await import("../lib/commands/monorepo.mjs")).monorepoCommand(o),doctor:async o=>(await import("../lib/commands/doctor.mjs")).doctorCommand(o),coverage:async o=>(await import("../lib/commands/coverage.mjs")).coverageCommand(o),review:async o=>(await import("../lib/commands/review.mjs")).reviewCommand(o),scan:async o=>(await import("../lib/commands/scan.mjs")).scanCommand(o),graph:async o=>(await import("../lib/commands/graph.mjs")).graphCommand(o),stability:async o=>(await import("../lib/commands/stability.mjs")).stabilityCommand(o),freeze:async o=>(await import("../lib/commands/stability.mjs")).freezeCommand(o),thaw:async o=>(await import("../lib/commands/stability.mjs")).thawCommand(o),why:async o=>(await import("../lib/commands/why.mjs")).whyCommand(o),impact:async o=>(await import("../lib/commands/impact.mjs")).impactCommand(o),scaffold:async o=>(await import("../lib/commands/scaffold.mjs")).scaffoldCommand(o),explain:async o=>(await import("../lib/commands/explain.mjs")).explainCommand(o),test:async o=>(await import("../lib/commands/test.mjs")).testCommand(o),ai:async o=>(await import("../lib/commands/ai.mjs")).aiCommand(o),demo:async o=>(await import("../lib/commands/demo.mjs")).demoCommand(o),log:async o=>(await import("../lib/commands/log.mjs")).logCommand(o),theme:async o=>(await import("../lib/commands/theme.mjs")).themeCommand(o),switch:async o=>(await import("../lib/commands/switch.mjs")).switchCommand(o),upgrade:async o=>(await import("../lib/commands/upgrade.mjs")).upgradeCommand(o),stats:async o=>(await import("../lib/commands/stats.mjs")).statsCommand(o),ask:async o=>(await import("../lib/commands/ask.mjs")).askCommand(o),recap:async o=>(await import("../lib/commands/recap.mjs")).recapCommand(o),uninstall:async o=>(await import("../lib/commands/uninstall.mjs")).uninstallCommand(o),feedback:async o=>(await import("../lib/commands/feedback.mjs")).feedbackCommand(o),telemetry:async o=>(await import("../lib/telemetry.mjs")).telemetryCommand(o),contract:async o=>w("contract",b,o),dev:async o=>w("dev",v,o),amp:async o=>(await import("../lib/commands/amp.mjs")).ampCommand(o)};async function w(o,t,n){const i=n[1];if(!i||i==="--help"||i==="-h"){console.log(),console.log(` ${m("\u{1F525} infernoflow "+o)} ${e("\u2014 available verbs:")}`),console.log();const p=Math.max(...Object.keys(t).map(s=>s.length))+2;for(const s of Object.keys(t)){const d=y[t[s]]||"";console.log(` ${a(s.padEnd(p))} ${e(d)}`)}console.log(),console.log(` ${e("Run")} ${a(`infernoflow ${o} <verb> --help`)} ${e("for verb-specific options.")}`),console.log();return}const c=t[i];return(!c||!l[c])&&(console.error(u(`
|
|
3
|
-
Unknown ${
|
|
4
|
-
`)),process.exit(1)),l[c]([c,...n.slice(2)])}function
|
|
5
|
-
`)}const
|
|
2
|
+
(function(){if(process.platform!=="win32"||process.env.WT_SESSION||process.env.ConEmuPID||process.env.TERM_PROGRAM==="vscode")return;const t={"\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]"},n=new RegExp(Object.keys(t).join("|"),"g");function i(c){const p=c.write.bind(c);c.write=function(s,...d){if(typeof s=="string")s=s.replace(n,f=>t[f]);else if(Buffer.isBuffer(s)){const f=s.toString("utf8").replace(n,k=>t[k]);s=Buffer.from(f,"utf8")}return p(s,...d)}}i(process.stdout),i(process.stderr)})();import{readFileSync as $}from"node:fs";import{dirname as S,join as g}from"node:path";import{fileURLToPath as x}from"node:url";import{bold as m,gray as o,cyan as a,red as u}from"../lib/ui/output.mjs";const A=S(x(import.meta.url));function O(e){for(const t of[g(e,"..","..","package.json"),g(e,"..","package.json")])try{return JSON.parse($(t,"utf8"))}catch{}return{version:"0.0.0-source"}}const I=O(A),h=I.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",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",monorepo:"Manage infernoflow across monorepo packages (init | list | status | diff | sync)",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",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",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)",contract:"Capability contracts \u2014 scan, freeze, impact, graph, scaffold, etc. (run: infernoflow contract)",dev:"Maintenance & integration \u2014 publish, changelog, dashboard, ai, ci, sync, etc. (run: infernoflow dev)",amp:"AI Memory Protocol \u2014 status, migrate from legacy, validate (run: infernoflow amp)"},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),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),monorepo:async e=>(await import("../lib/commands/monorepo.mjs")).monorepoCommand(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),upgrade:async e=>(await import("../lib/commands/upgrade.mjs")).upgradeCommand(e),stats:async e=>(await import("../lib/commands/stats.mjs")).statsCommand(e),ask:async e=>(await import("../lib/commands/ask.mjs")).askCommand(e),recap:async e=>(await import("../lib/commands/recap.mjs")).recapCommand(e),uninstall:async e=>(await import("../lib/commands/uninstall.mjs")).uninstallCommand(e),feedback:async e=>(await import("../lib/commands/feedback.mjs")).feedbackCommand(e),telemetry:async e=>(await import("../lib/telemetry.mjs")).telemetryCommand(e),contract:async e=>w("contract",v,e),dev:async e=>w("dev",b,e),amp:async e=>(await import("../lib/commands/amp.mjs")).ampCommand(e)};async function w(e,t,n){const i=n[1];if(!i||i==="--help"||i==="-h"){console.log(),console.log(` ${m("\u{1F525} infernoflow "+e)} ${o("\u2014 available verbs:")}`),console.log();const p=Math.max(...Object.keys(t).map(s=>s.length))+2;for(const s of Object.keys(t)){const d=y[t[s]]||"";console.log(` ${a(s.padEnd(p))} ${o(d)}`)}console.log(),console.log(` ${o("Run")} ${a(`infernoflow ${e} <verb> --help`)} ${o("for verb-specific options.")}`),console.log();return}const c=t[i];return(!c||!l[c])&&(console.error(u(`
|
|
3
|
+
Unknown ${e} verb: ${i}`)),console.error(o(` Run: infernoflow ${e} (see all verbs)
|
|
4
|
+
`)),process.exit(1)),l[c]([c,...n.slice(2)])}function B(){const e=Object.keys(y),t=Math.max(...e.map(n=>n.length),8)+1;return Object.entries(y).map(([n,i])=>` ${n.padEnd(t," ")}${i}`).join(`
|
|
5
|
+
`)}const v={scan:"scan",check:"check",status:"status",freeze:"freeze",thaw:"thaw",why:"why",impact:"impact",graph:"graph",stability:"stability",scaffold:"scaffold",explain:"explain",test:"test",coverage:"coverage",suggest:"suggest",run:"run",implement:"implement","doc-gate":"doc-gate","pr-impact":"pr-impact",review:"review",demo:"demo",upgrade:"upgrade",context:"context",sync:"sync"},b={publish:"publish",changelog:"changelog",diff:"diff",monorepo:"monorepo",ci:"ci",ai:"ai",theme:"theme",stats:"stats",feedback:"feedback",telemetry:"telemetry",uninstall:"uninstall","generate-skills":"generate-skills","install-cursor-hooks":"install-cursor-hooks","install-vscode-copilot-hooks":"install-vscode-copilot-hooks",setup:"setup"},M={"Memory (top-level)":["log","ask","switch","recap","status"],"Watch (top-level)":["watch"],"Setup (top-level)":["init","doctor"],"AMP (use: infernoflow amp <verb>)":["status","migrate","validate","version"],"Contract (use: infernoflow contract <verb>)":Object.keys(v),"Dev (use: infernoflow dev <verb>)":Object.keys(b)};function R(){return Object.entries(M).map(([t,n])=>` ${m(t+":")}
|
|
6
6
|
${n.join(" ")}`).join(`
|
|
7
7
|
|
|
8
8
|
`)}const C=Object.keys(l).length,j=`
|
|
9
|
-
${m("\u{1F525} infernoflow")} ${
|
|
10
|
-
${
|
|
9
|
+
${m("\u{1F525} infernoflow")} ${o("v"+h)}
|
|
10
|
+
${o("Persistent memory for AI coding sessions")}
|
|
11
11
|
|
|
12
12
|
${m("Usage:")}
|
|
13
13
|
infernoflow [command] [options]
|
|
14
14
|
|
|
15
|
-
${m("Memory")} ${
|
|
16
|
-
${a("log")} ${
|
|
17
|
-
${a("ask")} ${
|
|
15
|
+
${m("Memory")} ${o("\u2014 the 5-command core")}
|
|
16
|
+
${a("log")} ${o('"..."')} Add to session memory ${o("(--type gotcha|decision|attempt)")}
|
|
17
|
+
${a("ask")} ${o('"..."')} Search your memory by keyword ${o("(gotchas surface first)")}
|
|
18
18
|
${a("switch")} Generate handoff for next AI agent
|
|
19
19
|
${a("recap")} End-of-session health score + unlogged changes
|
|
20
20
|
${a("status")} Quick health check
|
|
21
21
|
|
|
22
22
|
${m("Setup")}
|
|
23
|
-
${a("init")} 60-second setup ${
|
|
24
|
-
${a("watch")} Auto-capture mode ${
|
|
23
|
+
${a("init")} 60-second setup ${o("(memory mode by default)")}
|
|
24
|
+
${a("watch")} Auto-capture mode ${o("(stuck-loops, dep changes, test removals)")}
|
|
25
25
|
${a("doctor")} Diagnose your setup
|
|
26
26
|
|
|
27
|
-
${m("Subsystems")} ${
|
|
28
|
-
${a("amp")} AI Memory Protocol ${
|
|
29
|
-
${a("contract")} Capability contracts ${
|
|
30
|
-
${a("
|
|
31
|
-
${a("dev")} Publishing, dashboards, AI providers ${e("(publish, ai, ci, \u2026)")}
|
|
27
|
+
${m("Subsystems")} ${o("\u2014 grouped, run for verbs:")}
|
|
28
|
+
${a("amp")} AI Memory Protocol ${o("(status, migrate, validate)")}
|
|
29
|
+
${a("contract")} Capability contracts ${o("(scan, freeze, impact, scaffold, \u2026)")}
|
|
30
|
+
${a("dev")} Publishing, AI providers, hooks ${o("(publish, ai, ci, \u2026)")}
|
|
32
31
|
|
|
33
|
-
${
|
|
34
|
-
${
|
|
35
|
-
`;import*as E from"node:fs";import*as P from"node:path";try{const
|
|
36
|
-
${m("\u{1F525} infernoflow")} ${
|
|
32
|
+
${o("Run")} ${a("infernoflow commands")} ${o("to see all "+C+" commands grouped.")}
|
|
33
|
+
${o("Run")} ${a("infernoflow <command> --help")} ${o("for command-specific options.")}
|
|
34
|
+
`;import*as E from"node:fs";import*as P from"node:path";try{const e=P.join(process.cwd(),"inferno");if(E.existsSync(e)){const{observeCommandStart:t}=await import("../lib/learning/observe.mjs"),n=process.argv[2];n&&!n.startsWith("-")&&t(e,n)}}catch{}const[,,r,...D]=process.argv;(!r||r==="--help"||r==="-h")&&(console.log(j),process.exit(0)),(r==="--version"||r==="-v")&&(console.log(h),process.exit(0)),r==="commands"&&(console.log(`
|
|
35
|
+
${m("\u{1F525} infernoflow")} ${o("v"+h)} ${o("\u2014 all "+C+" commands")}
|
|
37
36
|
`),console.log(R()),console.log(`
|
|
38
|
-
${
|
|
39
|
-
`),process.exit(0));const
|
|
40
|
-
Unknown command: ${r}`)),console.error(
|
|
41
|
-
`)),process.exit(1));const
|
|
42
|
-
Error: `)+
|
|
37
|
+
${o("Run")} ${a("infernoflow <command> --help")} ${o("for options.")}
|
|
38
|
+
`),process.exit(0));const T=Object.keys(l);T.includes(r)||(console.error(u(`
|
|
39
|
+
Unknown command: ${r}`)),console.error(o("Run: infernoflow commands (see all commands)")),console.error(o(`Run: infernoflow --help (quick start)
|
|
40
|
+
`)),process.exit(1));const G=[r,...D];l[r](G).catch(e=>{console.error(u(`
|
|
41
|
+
Error: `)+e.message),process.exit(1)});
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import*as
|
|
2
|
-
`),
|
|
3
|
-
`)}function ce(e){const r=(i,o,n=10)=>{const
|
|
4
|
-
`);for(const
|
|
1
|
+
import*as x from"node:fs";import*as A from"node:path";import"node:readline";import{seedProfileFromAdoption as L}from"../learning/profile.mjs";import{scanAngular as D}from"../adopters/angular.mjs";import{scanReact as R}from"../adopters/react.mjs";import{scanCSS as M}from"../adopters/css.mjs";function U(e){return e.replace(/[^a-zA-Z0-9]+/g," ").trim().split(/\s+/).filter(Boolean).map(r=>r[0].toUpperCase()+r.slice(1)).join("")}function O(e){return e.replace(/([A-Z])/g," $1").trim()}function k(e){try{return x.readFileSync(e,"utf8")}catch{return""}}const H=[{id:"CreateItem",title:"Create Item",regex:/\b(post|create|add)\b/i},{id:"ReadItems",title:"Read Items",regex:/\b(get|read|list|fetch)\b/i},{id:"UpdateItem",title:"Update Item",regex:/\b(put|patch|update|edit)\b/i},{id:"DeleteItem",title:"Delete Item",regex:/\b(delete|remove)\b/i},{id:"SearchItems",title:"Search Items",regex:/\bsearch\b/i},{id:"FilterItems",title:"Filter Items",regex:/\bfilter\b/i},{id:"SetDueDate",title:"Set Due Date",regex:/\bdueDate|deadline|due\b/i},{id:"SetPriority",title:"Set Priority",regex:/\bpriority\b/i},{id:"ToggleComplete",title:"Toggle Complete",regex:/\bcomplete|completed|toggle\b/i},{id:"ClearCompleted",title:"Clear Completed",regex:/\bclearCompleted|clear completed\b/i}];function ie(e){return Q(e).capabilities}function W(e){const r=[],i=["src","server","app","backend","frontend","api","Controllers"];for(const o of i){const n=A.join(e,o);if(!x.existsSync(n))continue;const s=[n];for(;s.length;){const f=s.pop();for(const t of x.readdirSync(f,{withFileTypes:!0})){const a=A.join(f,t.name);if(t.isDirectory()){if(new Set(["node_modules",".git","dist","build","out","www","tmp",".tmp","vendor","assets","public","static","coverage",".nyc_output",".angular",".vite",".cache",".parcel-cache",".next",".nuxt","__pycache__","e2e","test","tests","spec","__tests__","fixtures","mocks",".turbo","storybook-static"]).has(t.name))continue;s.push(a)}else if(/\.(js|jsx|ts|tsx|mjs|cjs|json|md|html|htm|cs|csproj)$/.test(t.name)){if(/\.(min|bundle)\.(js|css)$/.test(t.name)||/\.map$/.test(t.name)||/\.(spec|test)\.(ts|js|tsx|jsx)$/.test(t.name))continue;r.push(a)}}}}for(const o of x.readdirSync(e,{withFileTypes:!0}))o.isFile()&&/^(Program\.cs|.+\.csproj)$/i.test(o.name)&&r.push(A.join(e,o.name));return r}function G(e,r){const i=new Set;for(const o of e){const n=A.relative(r,o),s=k(o),f=s.matchAll(/\bclass\s+([A-Z][A-Za-z0-9_]*?(?:Component|Page|View|Widget|Card))\b/g);for(const m of f)i.add(m[1]);const t=s.matchAll(/\bselector\s*:\s*["']([^"']+)["']/g);for(const m of t)i.add(m[1]);const a=s.matchAll(/\bfunction\s+([A-Z][A-Za-z0-9_]*)\s*\(/g);for(const m of a)/component|page|view|card|chart|dashboard/i.test(m[1])&&i.add(m[1]);const d=n.match(/([^/\\]+)\.(component|page|view|widget|card)\.(ts|tsx|js|jsx)$/i);d&&i.add(d[1])}return Array.from(i).sort()}function N(e){const r=new Set,i=new Set,o=new Set(["if","for","while","const","let","var","return","function","class","import","export","null","undefined","true","false","string","number","boolean","any","unknown","never","selector","templateUrl","styleUrl","standalone","imports","providers","providedIn","options","scales","responsive","display","title","type","label","component","service","routes","appConfig","ApplicationConfig"]),n=s=>{s&&/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&(s.length<=1||o.has(s)||/^[A-Z0-9_]+$/.test(s)||r.add(s))};for(const s of e){const f=k(s);if(/\.(html|htm)$/i.test(s)){const t=f.matchAll(/\{\{\s*(?:this\.)?([a-zA-Z_][a-zA-Z0-9_]*)/g);for(const c of t)n(c[1]);const a=f.matchAll(/\[\(ngModel\)\]\s*=\s*["']([a-zA-Z_][a-zA-Z0-9_]*)["']/g);for(const c of a)n(c[1]);const d=f.matchAll(/\[[a-zA-Z0-9_-]+\]\s*=\s*["'](?:this\.)?([a-zA-Z_][a-zA-Z0-9_]*)["']/g);for(const c of d)n(c[1]);const m=f.matchAll(/\*ngIf\s*=\s*["'](?:this\.)?([a-zA-Z_][a-zA-Z0-9_]*)/g);for(const c of m)n(c[1])}if(/\.(ts|tsx|js|jsx|mjs|cjs)$/i.test(s)){const t=f.matchAll(/(?:^|\n)\s*(?:public|private|protected)?\s*(?:async\s+)?([a-zA-Z_][a-zA-Z0-9_]*)\s*\([^)]*\)\s*\{/g);for(const p of t)i.add(p[1]);const a=f.matchAll(/\bthis\.([a-zA-Z_][a-zA-Z0-9_]*)\b/g);for(const p of a)n(p[1]);const d=f.matchAll(/(?:^|\n)\s*(?:public|private|protected)?\s*(?:readonly\s+)?([a-zA-Z_][a-zA-Z0-9_]*)\s*(?::|=)/g);for(const p of d)n(p[1]);const m=f.matchAll(/@Input\([^)]*\)\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*[:=]/g);for(const p of m)n(p[1]);const c=f.matchAll(/forEach\(\((\w+)\)\s*=>/g);for(const p of c){const C=p[1],S=new RegExp(`\\b${C}\\.([a-zA-Z_][a-zA-Z0-9_]*)\\b`,"g");for(const y of f.matchAll(S))n(y[1])}}}return Array.from(r).filter(s=>!i.has(s)).sort().slice(0,80)}function B(e){const r=new Set,i=A.join(e,"package.json");if(!x.existsSync(i))return[];try{const o=JSON.parse(k(i)||"{}"),n={...o.dependencies||{},...o.devDependencies||{}};for(const s of Object.keys(n))r.add(s)}catch{}return Array.from(r).sort()}function V(e,r,i){const o=r.filter(t=>/\.(css|scss|sass|less|styl)$/i.test(t)).map(t=>A.relative(e,t)).sort(),n=[],s=t=>i.includes(t);s("tailwindcss")&&n.push("Tailwind CSS"),s("bootstrap")&&n.push("Bootstrap"),i.some(t=>t.startsWith("@angular/material"))&&n.push("Angular Material"),s("antd")&&n.push("Ant Design"),s("styled-components")&&n.push("styled-components"),(s("emotion")||s("@emotion/react"))&&n.push("Emotion");const f=new Set;for(const t of r){if(!/\.(css|scss|sass|less|styl|html|htm|ts|tsx|js|jsx|mjs|cjs)$/i.test(t))continue;const a=k(t);for(const d of a.matchAll(/--([a-zA-Z][a-zA-Z0-9_-]*)/g))f.add(`--${d[1]}`)}return{cssFrameworks:n,styleFileCount:o.length,styleFilesSample:o.slice(0,12),designTokens:Array.from(f).sort().slice(0,24)}}function q(e){let r=!1,i=!1;const o=new Set;for(const s of e){if(!/\.(html|htm|tsx|jsx|ts|js|mjs|cjs)$/i.test(s))continue;const f=k(s);/\bgrid\b|grid-template|grid-cols-|display:\s*grid/i.test(f)&&(r=!0),/\bflex\b|display:\s*flex|flex-row|flex-col|justify-|items-/i.test(f)&&(i=!0);for(const t of f.matchAll(/<(main|header|footer|section|aside|nav)\b/gi))o.add(t[1].toLowerCase());for(const t of f.matchAll(/class(?:Name)?\s*=\s*["'`][^"'`]*(dashboard|chart|card|sidebar|content|toolbar|filter|panel|table)[^"'`]*["'`]/gi)){const a=t[1].toLowerCase();o.add(a==="filter"?"filters":a)}}return{layoutType:r&&i?"mixed":r?"grid":i?"flex":"unknown",usesGrid:r,usesFlex:i,sections:Array.from(o).sort()}}function J(e,r,i,o={}){const n={ts:0,js:0,py:0,java:0,go:0,rb:0,rs:0,cs:0,php:0};for(const y of r){const b=A.extname(y).toLowerCase();(b===".ts"||b===".tsx")&&(n.ts+=1),(b===".js"||b===".jsx"||b===".mjs"||b===".cjs")&&(n.js+=1),b===".py"&&(n.py+=1),b===".java"&&(n.java+=1),b===".go"&&(n.go+=1),b===".rb"&&(n.rb+=1),b===".rs"&&(n.rs+=1),b===".cs"&&(n.cs+=1),b===".php"&&(n.php+=1)}const s=Object.entries(n).sort((y,b)=>b[1]-y[1]),f=s[0]?.[1]>0?s[0][0]:"unknown";let t="unknown",a=!1,d=!1,m=!1;for(const y of r){const b=A.basename(y).toLowerCase();if(b.endsWith(".csproj")){const P=k(y);/Microsoft\.NET\.Sdk\.Web/i.test(P)&&(a=!0),(/Blazor/i.test(P)||/Microsoft\.AspNetCore\.Components/i.test(P))&&(m=!0)}if(b==="program.cs"){const P=k(y);/app\.Map(Get|Post|Put|Delete|Patch)\s*\(/i.test(P)&&(d=!0)}}const c=y=>i.includes(y);i.some(y=>y.startsWith("@angular/"))?t="angular":c("react")?t="react":c("vue")?t="vue":c("svelte")?t="svelte":c("next")?t="nextjs":c("nuxt")?t="nuxt":c("express")?t="express":c("@nestjs/core")?t="nestjs":c("fastify")?t="fastify":c("flask")?t="flask":c("django")?t="django":c("spring-boot")?t="spring":m?t="blazor":d?t="minimalapi":(a||n.cs>0)&&(t="aspnet");let p="fullstack";const C=["src","frontend","app"].some(y=>x.existsSync(A.join(e,y))),S=["server","backend","api"].some(y=>x.existsSync(A.join(e,y)));return["react","angular","vue","svelte","nextjs","nuxt"].includes(t)&&(p="frontend"),["express","nestjs","fastify","flask","django","spring","aspnet","minimalapi"].includes(t)&&(p="backend"),C&&S&&(p="fullstack"),!C&&!S&&(p="library"),t==="blazor"&&(p="frontend"),{language:o.language||f,framework:o.framework||t,projectType:o.projectType||p,detected:{language:f,framework:t,projectType:p}}}function K(e,r){const i=[],o=new Set,n=t=>{let a=String(t||"").trim();return a?(a=a.replace(/https?:\/\/[^/]+/gi,""),a=a.replace(/\$\{[^}]+\}/g,"{var}"),a=a.replace(/\{[A-Za-z_][A-Za-z0-9_]*\}/g,"{var}"),a=a.replace(/:[A-Za-z_][A-Za-z0-9_]*/g,"{var}"),a=a.replace(/\/\d+(?=\/|$)/g,"/{id}"),a=a.replace(/=[^&\s]+/g,"={value}"),a=a.replace(/\/+/g,"/"),a):""},s=t=>{const a=n(t.endpointPattern);if(!a)return;const d=`${t.method}|${a}|${t.sourceFile}|${t.style}`;o.has(d)||(o.add(d),i.push({...t,endpointPattern:a}))};for(const t of r){if(!/\.(ts|tsx|js|jsx|mjs|cjs|cs)$/i.test(t))continue;const a=A.relative(e,t),d=k(t);if(!(/service|api|client|controller|program\.cs/i.test(a)||/HttpClient|fetch\(|app\.Map(Get|Post|Put|Delete|Patch)\(/i.test(d)))continue;const c=d.replace(/\r\n/g,`
|
|
2
|
+
`),p={},C=u=>{let l=String(u||"").trim();if(!l)return"";for(l=l.replace(/;+$/,"").trim(),l=l.replace(/\(\s*$/,"");l.startsWith("'")&&l.endsWith("'")||l.startsWith('"')&&l.endsWith('"')||l.startsWith("`")&&l.endsWith("`");)l=l.slice(1,-1).trim();return l},S=u=>{const l=String(u||"").trim();return l?!!(/^https?:\/\//i.test(l)||l.startsWith("/")||/\bapi\b/i.test(l)||/\$\{[^}]+\}/.test(l)||/\?[^=\s]+=?/.test(l)||/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_./${}-]+$/.test(l)):!1},y=(u,l)=>{!u||!l||(p[u]=C(l))},b=/(?:const|let|var)\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*([\s\S]*?);/g;for(const u of c.matchAll(b))y(u[1],u[2]);const P=/(?:public|private|protected)?\s*(?:readonly\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*([\s\S]*?);/g;for(const u of c.matchAll(P))y(u[1],u[2]);const F=u=>{const l=C(u);if(!l)return"";if(/^['"`][\s\S]*['"`]$/.test(l))return l.replace(/^['"`]|['"`]$/g,"");if(p[l]&&S(p[l]))return p[l];const h=l.match(/^this\.([A-Za-z_][A-Za-z0-9_]*)$/);if(h&&p[h[1]]&&S(p[h[1]]))return p[h[1]];const w=l.split("+").map(g=>g.trim()).filter(Boolean);if(w.length>1){const g=w.map(j=>{if(/^['"`][\s\S]*['"`]$/.test(j))return j.replace(/^['"`]|['"`]$/g,"");if(p[j]&&S(p[j]))return p[j];const v=j.match(/^this\.([A-Za-z_][A-Za-z0-9_]*)$/);return v&&p[v[1]]&&S(p[v[1]])?p[v[1]]:`{${j}}`}).join("");if(g)return g}const $=l.match(/^(.+?)\?(.+?):(.+)$/s);if($){const g=F($[2]),j=F($[3]);if(g||j)return`${g||"{optionA}"} | ${j||"{optionB}"}`}return l},_=/\.\s*(get|post|put|patch|delete)\s*(?:<[\s\S]*?>)?\s*\(\s*([\s\S]*?)(?:,|\))/gi;for(const u of c.matchAll(_)){const l=u[1].toUpperCase(),h=F(u[2]);!h||!S(h)||s({method:l,endpointPattern:h,style:"httpClient",sourceFile:a})}const T=/\bfetch\s*\(\s*([\s\S]*?)(?:,|\))/gi;for(const u of c.matchAll(T)){const l=F(u[1]);if(!l||!S(l))continue;const h=u.index||0,w=c.slice(h,h+260),g=(/method\s*:\s*["'](GET|POST|PUT|PATCH|DELETE)["']/i.exec(w)?.[1]||"GET").toUpperCase();s({method:g,endpointPattern:l,style:"fetch",sourceFile:a})}const z=/\baxios\.(get|post|put|patch|delete)\s*\(\s*([\s\S]*?)(?:,|\))/gi;for(const u of c.matchAll(z)){const l=u[1].toUpperCase(),h=F(u[2]);!h||!S(h)||s({method:l,endpointPattern:h,style:"axios",sourceFile:a})}const I=/\baxios\s*\(\s*\{([\s\S]*?)\}\s*\)/gi;for(const u of c.matchAll(I)){const l=u[1],h=/\bmethod\s*:\s*["']?(get|post|put|patch|delete)["']?/i.exec(l),w=/\burl\s*:\s*([^,\n]+)/i.exec(l),$=(h?.[1]||"get").toUpperCase(),g=F(w?.[1]||"");!g||!S(g)||s({method:$,endpointPattern:g,style:"axios-config",sourceFile:a})}const Z=/\.\s*request\s*\(\s*["'](GET|POST|PUT|PATCH|DELETE)["']\s*,\s*([\s\S]*?)(?:,|\))/gi;for(const u of c.matchAll(Z)){const l=u[1].toUpperCase(),h=F(u[2]);!h||!S(h)||s({method:l,endpointPattern:h,style:"request",sourceFile:a})}if(/\.cs$/i.test(t)){const u=/\bapp\.Map(Get|Post|Put|Delete|Patch)\s*\(\s*"([^"]+)"/gi;for(const g of c.matchAll(u))s({method:g[1].toUpperCase(),endpointPattern:g[2],style:"csharp-map",sourceFile:a});const l=/\[Route\("([^"]+)"\)\][\s\S]*?class\s+\w+/i.exec(c),h=l?l[1]:"",w=/\[(HttpGet|HttpPost|HttpPut|HttpDelete|HttpPatch)(?:\("([^"]*)"\))?\]/gi;for(const g of c.matchAll(w)){const j=g[1].replace("Http","").toUpperCase(),v=g[2]||"",E=[h,v].filter(Boolean).join("/").replace(/\/+/g,"/").replace(/\[controller\]/gi,"{controller}");s({method:j,endpointPattern:E||h||"{controller-route}",style:"csharp-controller",sourceFile:a})}const $=/\b(GetAsync|PostAsync|PutAsync|DeleteAsync|SendAsync)\s*\(\s*"([^"]+)"/gi;for(const g of c.matchAll($)){const j=g[1].replace("Async","").replace("Send","SEND").toUpperCase();s({method:j,endpointPattern:g[2],style:"csharp-httpclient",sourceFile:a})}}}const f=i.reduce((t,a)=>(t[a.method]=(t[a.method]||0)+1,t),{});return{totalCalls:i.length,byMethod:f,calls:i.slice(0,80)}}function Q(e,r={}){const i=W(e),o=new Map,n=(c,p)=>{o.has(c.id)||o.set(c.id,{id:c.id,title:c.title,reason:"Detected from code signals",sourceFiles:new Set}),o.get(c.id).sourceFiles.add(A.relative(e,p))};for(const c of i){const p=k(c);for(const C of H)C.regex.test(p)&&n(C,c)}const s=A.join(e,"package.json");if(x.existsSync(s)){const c=JSON.parse(k(s)||"{}"),p=typeof c.name=="string"?c.name:A.basename(e);U(p)&&!o.size&&(o.set("ReadItems",{id:"ReadItems",title:"Read Items",reason:`Fallback default for ${p}`,sourceFiles:new Set}),o.set("CreateItem",{id:"CreateItem",title:"Create Item",reason:`Fallback default for ${p}`,sourceFiles:new Set}))}o.size||(o.set("CreateItem",{id:"CreateItem",title:"Create Item",reason:"Fallback default",sourceFiles:new Set}),o.set("ReadItems",{id:"ReadItems",title:"Read Items",reason:"Fallback default",sourceFiles:new Set}));const f=B(e),t=J(e,i,f,r);let a=[];try{if(t.framework==="angular"){const c=D(e,i);for(const p of c.capabilities)o.has(p.id)||o.set(p.id,{...p,sourceFiles:new Set(p.sourceFiles)})}else if(t.framework==="react"||t.framework==="nextjs"){const c=R(e,i);for(const p of c.capabilities)o.has(p.id)||o.set(p.id,{...p,sourceFiles:new Set(p.sourceFiles)})}}catch{}let d={designTokens:[],colorTokens:[],spacingTokens:[],componentClasses:[],themeVars:[]};try{d=M(e,i)}catch{}return{capabilities:Array.from(o.values()).map(c=>({...c,sourceFiles:Array.from(c.sourceFiles||[])})),components:G(i,e),displayFields:N(i),externalLibraries:f,uiLayout:q(i),styling:{...V(e,i,f),designTokens:d.designTokens.length>0?d.designTokens:void 0,colorTokens:d.colorTokens,spacingTokens:d.spacingTokens,componentClasses:d.componentClasses,themeVars:d.themeVars},developmentProfile:t,apiCalls:K(e,i)}}async function re(e,r=!1){if(e&&e.length>0){const i=e.map(o=>o.id).join(", ");console.log(` Inferred capabilities: ${i}`),console.log(" (edit inferno/capabilities.json later if you want to refine)")}return e}function ae(e){if(!e.length)return"No capabilities inferred.";const r=X(e),i=r.reduce((s,f)=>s+f.signalCount,0),o={high:r.filter(s=>s.confidence==="high").length,medium:r.filter(s=>s.confidence==="medium").length,low:r.filter(s=>s.confidence==="low").length},n=[];n.push("Adoption Analysis"),n.push("=".repeat(56)),n.push(`Capabilities detected : ${r.length}`),n.push(`Signal hits total : ${i}`),n.push(`Confidence mix : high=${o.high}, medium=${o.medium}, low=${o.low}`),n.push("-".repeat(56)),n.push("Capability Breakdown"),n.push("-".repeat(56)),n.push("Confidence Signals Capability"),n.push("-".repeat(56));for(const s of r){const f=s.confidence.toUpperCase().padEnd(10," "),t=String(s.signalCount).padEnd(7," ");if(n.push(`${f} ${t} ${s.id} (${s.title})`),s.signalCount>0){const a=s.sourceFiles.slice(0,3).join(", ");n.push(` sources: ${a}`),s.sourceFiles.length>3&&n.push(` more : +${s.sourceFiles.length-3} additional files`)}else n.push(" sources: inferred fallback (no strong code signal)")}return n.push("=".repeat(56)),n.join(`
|
|
3
|
+
`)}function ce(e){const r=(i,o,n=10)=>{const s=[`${i} (${o.length})`];if(s.push("-".repeat(56)),!o.length)return s.push(" - none"),s.join(`
|
|
4
|
+
`);for(const f of o.slice(0,n))s.push(` - ${f}`);return o.length>n&&s.push(` - ... +${o.length-n} more`),s.join(`
|
|
5
5
|
`)};return["Project Structure Signals","=".repeat(56),r("Components",e.components||[]),r("Display fields",e.displayFields||[]),r("External libraries",e.externalLibraries||[]),"UI layout","-".repeat(56),` - layout type: ${e.uiLayout?.layoutType||"unknown"}`,` - uses grid : ${e.uiLayout?.usesGrid?"yes":"no"}`,` - uses flex : ${e.uiLayout?.usesFlex?"yes":"no"}`,` - sections : ${(e.uiLayout?.sections||[]).slice(0,10).join(", ")||"none"}`,"Styling","-".repeat(56),` - frameworks : ${(e.styling?.cssFrameworks||[]).join(", ")||"none detected"}`,` - style files: ${e.styling?.styleFileCount??0}`,` - tokens : ${(e.styling?.designTokens||[]).slice(0,8).join(", ")||"none detected"}`,"Development profile","-".repeat(56),` - language : ${e.developmentProfile?.language||"unknown"} (auto: ${e.developmentProfile?.detected?.language||"unknown"})`,` - framework : ${e.developmentProfile?.framework||"unknown"} (auto: ${e.developmentProfile?.detected?.framework||"unknown"})`,` - project type: ${e.developmentProfile?.projectType||"unknown"} (auto: ${e.developmentProfile?.detected?.projectType||"unknown"})`,"API calls","-".repeat(56),` - total calls : ${e.apiCalls?.totalCalls??0}`,` - by method : ${Object.entries(e.apiCalls?.byMethod||{}).map(([i,o])=>`${i}:${o}`).join(", ")||"none"}`,...(e.apiCalls?.calls||[]).slice(0,6).map(i=>` - ${i.method} ${i.endpointPattern} [${i.style}] (${i.sourceFile})`),...(e.apiCalls?.calls||[]).length>6?[` - ... +${(e.apiCalls?.calls||[]).length-6} more`]:[],"=".repeat(56)].join(`
|
|
6
|
-
`)}function
|
|
7
|
-
`);const
|
|
8
|
-
`);const a={scenarioId:"adoption_baseline",description:"Baseline inferred from existing codebase during adoption",capabilitiesCovered:n,steps:n.map(m=>({action:m,expect:`${m} behavior exists in the current project`}))};if(
|
|
9
|
-
`),o){const m={profileId:"adoption_profile",generatedAt:new Date().toISOString(),components:o.components||[],displayFields:o.displayFields||[],externalLibraries:o.externalLibraries||[],uiLayout:o.uiLayout||{layoutType:"unknown",usesGrid:!1,usesFlex:!1,sections:[]},styling:o.styling||{cssFrameworks:[],styleFileCount:0,styleFilesSample:[],designTokens:[]},developmentProfile:o.developmentProfile||{language:"unknown",framework:"unknown",projectType:"unknown",detected:{language:"unknown",framework:"unknown",projectType:"unknown"}},apiCalls:o.apiCalls||{totalCalls:0,byMethod:{},calls:[]}};
|
|
10
|
-
`);try{
|
|
6
|
+
`)}function X(e){return e.map(r=>{const i=r.sourceFiles?.length||0,o=i>=3?"high":i>=1?"medium":"low";return{id:r.id,title:r.title,reason:r.reason,confidence:o,sourceFiles:r.sourceFiles||[],signalCount:i}})}function Y(e){if(!e)return null;const r=[...e.components||[]].filter(Boolean).slice(0,40),i=(e.styling?.designTokens||[]).slice(0,20),o=e.uiLayout?.layoutType||"unknown",n=(e.uiLayout?.sections||[]).slice(0,12),s=e.styling?.cssFrameworks||[],f=(e.styling?.colorTokens||[]).slice(0,10),t=(e.styling?.themeVars||[]).slice(0,10);return{components:r,designTokens:i,colorTokens:f,themeVars:t,cssFrameworks:s,layout:o,sections:n,lastScanned:new Date().toISOString()}}function le(e,r){return r?e?{...r,components:[...new Set([...r.components||[],...e.components||[]])].slice(0,50),designTokens:[...new Set([...r.designTokens||[],...e.designTokens||[]])].slice(0,30)}:r:e}function pe(e,r,i,o=null){const n=i.map(m=>m.id),s=Y(o),f={policyId:r,policyVersion:1,capabilities:n,rules:{docsRequiredOnCapabilityChange:!0,requireScenarioForEachCapability:!0,requireChangelogOnCapabilityChange:!0},...s?{ui:s}:{}};x.mkdirSync(A.join(e,"scenarios"),{recursive:!0}),x.writeFileSync(A.join(e,"contract.json"),JSON.stringify(f,null,2)+`
|
|
7
|
+
`);const t={schemaVersion:1,capabilities:i.map(m=>({id:m.id,title:m.title||O(m.id),since:"0.1.0"}))};x.writeFileSync(A.join(e,"capabilities.json"),JSON.stringify(t,null,2)+`
|
|
8
|
+
`);const a={scenarioId:"adoption_baseline",description:"Baseline inferred from existing codebase during adoption",capabilitiesCovered:n,steps:n.map(m=>({action:m,expect:`${m} behavior exists in the current project`}))};if(x.writeFileSync(A.join(e,"scenarios","adoption_baseline.json"),JSON.stringify(a,null,2)+`
|
|
9
|
+
`),o){const m={profileId:"adoption_profile",generatedAt:new Date().toISOString(),components:o.components||[],displayFields:o.displayFields||[],externalLibraries:o.externalLibraries||[],uiLayout:o.uiLayout||{layoutType:"unknown",usesGrid:!1,usesFlex:!1,sections:[]},styling:o.styling||{cssFrameworks:[],styleFileCount:0,styleFilesSample:[],designTokens:[]},developmentProfile:o.developmentProfile||{language:"unknown",framework:"unknown",projectType:"unknown",detected:{language:"unknown",framework:"unknown",projectType:"unknown"}},apiCalls:o.apiCalls||{totalCalls:0,byMethod:{},calls:[]}};x.writeFileSync(A.join(e,"adoption_profile.json"),JSON.stringify(m,null,2)+`
|
|
10
|
+
`);try{L(e,o,i)}catch{}}const d=`# Changelog \u2014 ${r}
|
|
11
11
|
|
|
12
12
|
## Unreleased
|
|
13
13
|
|
|
@@ -17,4 +17,4 @@ import*as j from"node:fs";import*as A from"node:path";import*as D from"node:read
|
|
|
17
17
|
## 0.1.0 \u2014 Adoption baseline
|
|
18
18
|
|
|
19
19
|
- Initial baseline generated by infernoflow init --adopt
|
|
20
|
-
`;
|
|
20
|
+
`;x.writeFileSync(A.join(e,"CHANGELOG.md"),d,"utf8")}export{ae as buildAdoptionReport,ce as buildSignalsReport,Y as buildUiContractSection,ie as discoverCapabilities,Q as discoverProjectSignals,le as mergeUiSection,re as reviewCapabilitiesInteractive,X as summarizeCapabilities,pe as writeAdoptionBaseline};
|