infernoflow 0.40.5 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # Changelog — infernoflow
2
2
 
3
+ ## 0.42.0 — 2026-05-03
4
+
5
+ ### Added
6
+ - **AMP-compliant on-disk format** — infernoflow now speaks the [AI Memory Protocol v1.0](docs/protocol/PROTOCOL.md) natively. New projects get `.ai-memory/sessions.jsonl` (the AMP canonical layout) instead of `inferno/sessions.jsonl`. Entries on disk use the AMP wire format: `msg` instead of `summary`, Unix-ms integer `ts`, ULID `id` on every entry, AMP type enum (gotcha/decision/attempt/note/detection/pattern), `meta` for tool-specific extras.
7
+ - **Lossless round-trip for infernoflow-specific fields** — `result`, `agent: "human"`, `auto: true`, and the extra entry types (`preference`, `theme`, `handoff`, `error`) are preserved via `meta.subtype` / `meta.result` / `meta.agent` / `confidence`. Read paths translate AMP shape back to infernoflow's familiar internal shape so the rest of the codebase doesn't need to change.
8
+ - **AMP injection markers** — auto-update of `CLAUDE.md`, `.cursorrules`, and `.github/copilot-instructions.md` now wraps the generated section with `<!-- AMP:START -->` / `<!-- AMP:END -->` so other AMP-compliant tools can edit-in-place without trampling each other.
9
+ - **Backward compat** — projects with the legacy `inferno/sessions.jsonl` keep working unchanged. Both layouts are read transparently; writes always target `.ai-memory/`.
10
+
11
+ ### Internal
12
+ - `lib/amp/io.mjs` is the single source of truth for file paths, entry shape, ULID generation, and translation. ~270 lines, zero external dependencies. Plumbing for the upcoming `@amp/core` npm publish (Phase B) and `amp_*` MCP tool aliasing (Phase C).
13
+ - `infernoflow amp migrate` (coming in next release) will copy legacy `inferno/sessions.jsonl` → `.ai-memory/sessions.jsonl` with shape translation. Until then, projects can stay on the legacy layout indefinitely.
14
+
15
+ ## 0.41.0 — 2026-05-03
16
+
17
+ ### Changed
18
+ - **Command surface culled from 51 visible to 12.** `--help` now shows only the 5-command memory core (log/ask/switch/recap/status), 3 setup commands (init/watch/doctor), and 3 subsystem dispatchers (contract/cloud/dev). All 53 legacy command names remain callable as top-level aliases — backward compatible. K.I.S.S. first-impression for new users; full discoverability via `infernoflow commands`.
19
+ - **New namespace dispatchers** — `infernoflow contract scan` routes to the same handler as `infernoflow scan`. Same for `infernoflow dev publish`, etc. Run `infernoflow contract` or `infernoflow dev` with no verb to see the verbs in that namespace. The existing `cloud` dispatcher (with init/push/pull/status/dashboard subcommands) is untouched.
20
+ - **`infernoflow commands` regrouped** to advertise the new namespace structure: Memory / Watch / Setup at top level, then Contract / Cloud / Dev grouped with their verbs.
21
+
22
+ ### Internal
23
+ - This is the first half of the move toward the AI Memory Protocol (AMP) — see docs/protocol/PROTOCOL.md. Phase A.2 (folder rename to `.ai-memory/`, AMP-compliant entry shape with ULIDs and `meta.subtype` for infernoflow extras) is up next.
24
+
25
+ ## 0.40.6 — 2026-05-02
26
+
27
+ ### Fixed
28
+ - **`infernoflow init` first-gotcha prompt is now bulletproof.** The original prompt accepted whatever you typed verbatim, so a confused user (Ron earlier today, after Ctrl+C'ing a stuck `--browser` login) could paste a multi-line shell command and end up with `node ../infernoflow-pkg/bin/infernoflow.mjs log "..."` saved as their first memory. The first interaction every new user has now:
29
+ - **Detects shell-command-shaped input** (starts with node/npm/npx/git/cd/python/etc., or contains `&&`/`||`/`>`/`|` operators, or contains a Windows drive path) and re-prompts with a hint.
30
+ - **Detects multi-line paste** and re-prompts asking for a single short sentence.
31
+ - **Trims accidental leading prompt characters** (`> `, `$ `, `# `) — common when copy/pasting from terminal output.
32
+ - **Treats input shorter than 3 chars as too short** (single keystrokes, accidental Enter).
33
+ - **Handles Ctrl+C cleanly** — exits the prompt without leaving inferno/ in a half-state.
34
+ - **Two-strike rule** — after two bad inputs we silently skip rather than block the install.
35
+ - Verified via 18-case unit test of the classifier covering all the failure modes.
36
+
3
37
  ## 0.40.5 — 2026-05-02
4
38
 
5
39
  ### Fixed
@@ -1,34 +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 e={"\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]"},t=new RegExp(Object.keys(e).join("|"),"g");function c(m){const g=m.write.bind(m);m.write=function(i,...w){if(typeof i=="string")i=i.replace(t,l=>e[l]);else if(Buffer.isBuffer(i)){const l=i.toString("utf8").replace(t,C=>e[C]);i=Buffer.from(l,"utf8")}return g(i,...w)}}c(process.stdout),c(process.stderr)})();import{readFileSync as b}from"node:fs";import{dirname as v,join as f}from"node:path";import{fileURLToPath as k}from"node:url";import{bold as r,gray as o,cyan as n,red as h}from"../lib/ui/output.mjs";const $=v(k(import.meta.url));function S(a){for(const e of[f(a,"..","..","package.json"),f(a,"..","package.json")])try{return JSON.parse(b(e,"utf8"))}catch{}return{version:"0.0.0-source"}}const x=S($),d=x.version||"0.0.0",u={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)"},p={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),dashboard:async a=>(await import("../lib/commands/dashboard.mjs")).dashboardCommand(a),login:async a=>(await import("../lib/commands/login.mjs")).loginCommand(a),logout:async()=>(await import("../lib/commands/login.mjs")).logoutCommand(),whoami:async()=>(await import("../lib/commands/login.mjs")).whoamiCommand(),cloud:async a=>(await import("../lib/commands/cloud.mjs")).cloudCommand(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),monorepo:async a=>(await import("../lib/commands/monorepo.mjs")).monorepoCommand(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 L(){const a=Object.keys(u),e=Math.max(...a.map(t=>t.length),8)+1;return Object.entries(u).map(([t,c])=>` ${t.padEnd(e," ")}${c}`).join(`
3
- `)}const A={"Session Memory":["log","ask","switch","recap","stats","theme"],Context:["context","scan","suggest","check","status"],"Code Analysis":["graph","impact","why","coverage","stability","freeze","thaw"],Workflow:["run","sync","watch","implement","doc-gate"],Publishing:["publish","changelog","diff"],Cloud:["login","logout","whoami","cloud","notify","pr-impact"],Quality:["review"],Integration:["ai","ci","coverage","dashboard"],Setup:["init","setup","demo","doctor","generate-skills","upgrade","uninstall"],Advanced:["scaffold","explain","test","monorepo","feedback","telemetry"]};function I(){return Object.entries(A).map(([e,t])=>` ${r(e+":")}
4
- ${t.join(" ")}`).join(`
2
+ (function(){if(process.platform!=="win32"||process.env.WT_SESSION||process.env.ConEmuPID||process.env.TERM_PROGRAM==="vscode")return;const a={"\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(a).join("|"),"g");function i(c){const d=c.write.bind(c);c.write=function(s,...p){if(typeof s=="string")s=s.replace(n,f=>a[f]);else if(Buffer.isBuffer(s)){const f=s.toString("utf8").replace(n,k=>a[k]);s=Buffer.from(f,"utf8")}return d(s,...p)}}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 t,red as u}from"../lib/ui/output.mjs";const O=S(x(import.meta.url));function A(o){for(const a of[g(o,"..","..","package.json"),g(o,"..","package.json")])try{return JSON.parse($(a,"utf8"))}catch{}return{version:"0.0.0-source"}}const R=A(O),h=R.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)"},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)};async function w(o,a,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 d=Math.max(...Object.keys(a).map(s=>s.length))+2;for(const s of Object.keys(a)){const p=y[a[s]]||"";console.log(` ${t(s.padEnd(d))} ${e(p)}`)}console.log(),console.log(` ${e("Run")} ${t(`infernoflow ${o} <verb> --help`)} ${e("for verb-specific options.")}`),console.log();return}const c=a[i];return(!c||!l[c])&&(console.error(u(`
3
+ Unknown ${o} verb: ${i}`)),console.error(e(` Run: infernoflow ${o} (see all verbs)
4
+ `)),process.exit(1)),l[c]([c,...n.slice(2)])}function U(){const o=Object.keys(y),a=Math.max(...o.map(n=>n.length),8)+1;return Object.entries(y).map(([n,i])=>` ${n.padEnd(a," ")}${i}`).join(`
5
+ `)}const b={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"},v={publish:"publish",changelog:"changelog",diff:"diff",dashboard:"dashboard",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"},I={"Memory (top-level)":["log","ask","switch","recap","status"],"Watch (top-level)":["watch"],"Setup (top-level)":["init","doctor"],"Contract (use: infernoflow contract <verb>)":Object.keys(b),"Cloud (use: infernoflow cloud <verb>)":["login","logout","whoami","cloud","notify"],"Dev (use: infernoflow dev <verb>)":Object.keys(v)};function j(){return Object.entries(I).map(([a,n])=>` ${m(a+":")}
6
+ ${n.join(" ")}`).join(`
5
7
 
6
- `)}const y=Object.keys(p).length,O=`
7
- ${r("\u{1F525} infernoflow")} ${o("v"+d)}
8
- ${o("Persistent memory for AI coding sessions")}
8
+ `)}const C=Object.keys(l).length,M=`
9
+ ${m("\u{1F525} infernoflow")} ${e("v"+h)}
10
+ ${e("Persistent memory for AI coding sessions")}
9
11
 
10
- ${r("Usage:")}
12
+ ${m("Usage:")}
11
13
  infernoflow [command] [options]
12
14
 
13
- ${r("Core Commands:")}
14
- ${n("log")} ${o('"..."')} Add to session memory ${o("(--type gotcha|decision|attempt|preference)")}
15
- ${n("ask")} ${o('"..."')} Search your memory by keyword ${o("(gotchas surface first)")}
16
- ${n("switch")} Generate handoff for next AI agent
17
- ${n("recap")} End-of-session health score + unlogged changes
18
- ${n("status")} Contract health at a glance
15
+ ${m("Memory")} ${e("\u2014 the 5-command core")}
16
+ ${t("log")} ${e('"..."')} Add to session memory ${e("(--type gotcha|decision|attempt)")}
17
+ ${t("ask")} ${e('"..."')} Search your memory by keyword ${e("(gotchas surface first)")}
18
+ ${t("switch")} Generate handoff for next AI agent
19
+ ${t("recap")} End-of-session health score + unlogged changes
20
+ ${t("status")} Quick health check
19
21
 
20
- ${r("Getting Started:")}
21
- ${n("setup")} One command to get fully operational
22
- ${n("demo")} Interactive walkthrough ${o("(5 minutes)")}
23
- ${n("doctor")} Diagnose your setup
22
+ ${m("Setup")}
23
+ ${t("init")} 60-second setup ${e("(memory mode by default)")}
24
+ ${t("watch")} Auto-capture mode ${e("(stuck-loops, dep changes, test removals)")}
25
+ ${t("doctor")} Diagnose your setup
24
26
 
25
- ${o("Run")} ${n("infernoflow commands")} ${o("to see all "+y+" commands.")}
26
- ${o("Run")} ${n("infernoflow <command> --help")} ${o("for command-specific options.")}
27
- `;import*as M from"node:fs";import*as R from"node:path";try{const a=R.join(process.cwd(),"inferno");if(M.existsSync(a)){const{observeCommandStart:e}=await import("../lib/learning/observe.mjs"),t=process.argv[2];t&&!t.startsWith("-")&&e(a,t)}}catch{}const[,,s,...j]=process.argv;(!s||s==="--help"||s==="-h")&&(console.log(O),process.exit(0)),(s==="--version"||s==="-v")&&(console.log(d),process.exit(0)),s==="commands"&&(console.log(`
28
- ${r("\u{1F525} infernoflow")} ${o("v"+d)} ${o("\u2014 all "+y+" commands")}
29
- `),console.log(I()),console.log(`
30
- ${o("Run")} ${n("infernoflow <command> --help")} ${o("for options.")}
31
- `),process.exit(0));const E=Object.keys(p);E.includes(s)||(console.error(h(`
32
- Unknown command: ${s}`)),console.error(o("Run: infernoflow commands (see all commands)")),console.error(o(`Run: infernoflow --help (quick start)
33
- `)),process.exit(1));const P=[s,...j];p[s](P).catch(a=>{console.error(h(`
34
- Error: `)+a.message),process.exit(1)});
27
+ ${m("Subsystems")} ${e("\u2014 grouped, run for verbs:")}
28
+ ${t("contract")} Capability contracts ${e("(scan, freeze, impact, scaffold, \u2026)")}
29
+ ${t("cloud")} Cloud sync + accounts ${e("(login, push, pull, status, \u2026)")}
30
+ ${t("dev")} Publishing, dashboards, AI providers ${e("(publish, ai, ci, \u2026)")}
31
+
32
+ ${e("Run")} ${t("infernoflow commands")} ${e("to see all "+C+" commands grouped.")}
33
+ ${e("Run")} ${t("infernoflow <command> --help")} ${e("for command-specific options.")}
34
+ `;import*as E from"node:fs";import*as D from"node:path";try{const o=D.join(process.cwd(),"inferno");if(E.existsSync(o)){const{observeCommandStart:a}=await import("../lib/learning/observe.mjs"),n=process.argv[2];n&&!n.startsWith("-")&&a(o,n)}}catch{}const[,,r,...P]=process.argv;(!r||r==="--help"||r==="-h")&&(console.log(M),process.exit(0)),(r==="--version"||r==="-v")&&(console.log(h),process.exit(0)),r==="commands"&&(console.log(`
35
+ ${m("\u{1F525} infernoflow")} ${e("v"+h)} ${e("\u2014 all "+C+" commands")}
36
+ `),console.log(j()),console.log(`
37
+ ${e("Run")} ${t("infernoflow <command> --help")} ${e("for options.")}
38
+ `),process.exit(0));const G=Object.keys(l);G.includes(r)||(console.error(u(`
39
+ Unknown command: ${r}`)),console.error(e("Run: infernoflow commands (see all commands)")),console.error(e(`Run: infernoflow --help (quick start)
40
+ `)),process.exit(1));const T=[r,...P];l[r](T).catch(o=>{console.error(u(`
41
+ Error: `)+o.message),process.exit(1)});
@@ -0,0 +1,11 @@
1
+ import*as i from"node:fs";import*as c from"node:path";const p="1.0",A={start:"<!-- AMP:START -->",end:"<!-- AMP:END -->"},S=new Set(["gotcha","decision","attempt","note","detection","pattern"]),j=new Set(["copilot","cursor","claude","windsurf","other"]);function a(e,t={}){const o=c.join(e,".ai-memory"),n=c.join(e,"inferno"),r=t.forWrite||i.existsSync(o)||!i.existsSync(n),u=r?o:n,f=r;return{root:u,isAmp:f,sessions:c.join(u,"sessions.jsonl"),config:c.join(u,f?"amp.json":"config.json"),handoff:c.join(u,f?"handoff.md":"HANDOFF.md")}}function l(e){const t=c.join(e,".ai-memory");return i.existsSync(t)||i.mkdirSync(t,{recursive:!0}),t}const y="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function h(){let e=Date.now(),t="";for(let n=0;n<10;n++)t=y[e%32]+t,e=Math.floor(e/32);let o="";for(let n=0;n<16;n++)o+=y[Math.floor(Math.random()*32)];return t+o}function g(e){const t={...e.meta||{}};let o=e.type||"note";S.has(o)||(t.subtype=o,o="note"),e.result&&(t.result=e.result);let n;const r=e.agent;r&&j.has(r)?n=r:r&&(t.agent=r);const u=typeof e.ts=="number"?e.ts:e.ts?Date.parse(e.ts):Date.now(),f=e.confidence!=null?e.confidence:e.auto?.7:void 0,s={type:o,msg:e.summary||e.msg||"",ts:u,id:e.id||`amp_${h()}`};return e.file&&(s.file=e.file),e.line&&(s.line=e.line),e.function&&(s.function=e.function),e.tags&&e.tags.length&&(s.tags=e.tags),e.source&&(s.source=e.source),n&&(s.tool=n),e.session&&(s.session=e.session),f!=null&&(s.confidence=f),Object.keys(t).length&&(s.meta=t),s}function x(e){if(e.summary&&!e.msg)return e;const t=e.meta||{},o=t.subtype||e.type||"note",n={ts:e.ts,type:o,summary:e.msg||""};e.id&&(n.id=e.id),e.file&&(n.file=e.file),e.line&&(n.line=e.line),e.function&&(n.function=e.function),e.tags&&(n.tags=e.tags),e.source&&(n.source=e.source),e.tool&&(n.agent=e.tool),t.agent&&(n.agent=t.agent),t.result&&(n.result=t.result),e.confidence!=null&&(n.confidence=e.confidence,e.confidence<1&&(n.auto=!0));const{subtype:r,agent:u,result:f,...s}=t;return Object.keys(s).length&&(n.meta=s),n}function D(e){const{sessions:t}=a(e);return i.existsSync(t)?i.readFileSync(t,"utf8").split(`
2
+ `).filter(Boolean).map(o=>{try{return JSON.parse(o)}catch{return null}}).filter(Boolean).map(x):[]}function M(e,t){l(e);const{sessions:o}=a(e,{forWrite:!0}),n=g(t);return i.appendFileSync(o,JSON.stringify(n)+`
3
+ `,"utf8"),n}function O(e){const{config:t}=a(e);try{return JSON.parse(i.readFileSync(t,"utf8"))}catch{return null}}function N(e,t={}){l(e);const{config:o}=a(e,{forWrite:!0});if(i.existsSync(o))return!1;const n={amp:p,project:t.project||c.basename(e),stack:t.stack||{},config:{autoCapture:!0,maxEntries:1e3,rotationStrategy:"archive",inject:["all"],...t.config||{}}};return i.writeFileSync(o,JSON.stringify(n,null,2)+`
4
+ `,"utf8"),!0}function E(e){const t=c.join(e,"inferno"),o=c.join(t,"sessions.jsonl");if(!i.existsSync(o))return{migrated:0,reason:"no legacy sessions.jsonl"};const n=c.join(e,".ai-memory"),r=c.join(n,"sessions.jsonl");if(i.existsSync(r))return{migrated:0,reason:".ai-memory/sessions.jsonl already exists"};l(e);const u=i.readFileSync(o,"utf8").split(`
5
+ `).filter(Boolean);let f=0;for(const s of u)try{const d=JSON.parse(s),m=g(d);i.appendFileSync(r,JSON.stringify(m)+`
6
+ `,"utf8"),f++}catch{}return i.writeFileSync(c.join(n,"MIGRATED.md"),`# Migrated from inferno/
7
+
8
+ Copied ${f} entries from inferno/sessions.jsonl on ${new Date().toISOString()}.
9
+
10
+ The original inferno/sessions.jsonl is untouched. You can delete it once you're confident the new layout works.
11
+ `,"utf8"),{migrated:f,reason:"ok"}}export{A as AMP_MARKERS,p as AMP_VERSION,a as ampPaths,M as appendEntry,l as ensureAmpDir,x as fromAmp,h as generateULID,E as migrateLegacy,O as readConfig,D as readEntries,g as toAmp,N as writeDefaultConfig};
@@ -1,4 +1,3 @@
1
- import*as x from"node:fs";import*as E from"node:path";import{bold as S,cyan as O,gray as s,green as C,yellow as L,red as W}from"../ui/output.mjs";const q="inferno",F=E.join(q,"sessions.jsonl"),y={gotcha:0,decision:1,attempt:2,preference:3,theme:4,note:5,error:5,handoff:6},I={gotcha:"\u26A0",decision:"\u2713",attempt:"\u21BA",preference:"\u2666",theme:"\u{1F3A8}",note:"\xB7",error:"\u2717",handoff:"\u2192"},D={gotcha:L,decision:C,attempt:O,preference:O,theme:O,note:s,error:W,handoff:s};function j(t){return t.toLowerCase().replace(/[^a-z0-9\s]/g," ").split(/\s+/).filter(e=>e.length>1)}function M(t,e){const r=[t.summary||"",t.type||""].join(" ").toLowerCase(),n=j(r);let i=0;for(const c of e)(t.summary||"").toLowerCase().includes(c)&&(i+=3),n.includes(c)&&(i+=1),n.some(o=>o.startsWith(c)||c.startsWith(o))&&(i+=.5);return i}function _(t){if(!t)return"";const e=new Date(t),r=Date.now()-e.getTime(),n=Math.floor(r/864e5);return n===0?"today":n===1?"yesterday":n<7?`${n}d ago`:n<30?`${Math.floor(n/7)}w ago`:e.toLocaleDateString("en-GB",{day:"2-digit",month:"short"})}function P(t,e){const r=t.type||"note",n=I[r]||"\xB7",i=D[r]||s,c=t.result?s(` [${t.result}]`):"",o=t.agent?s(` \u2014 ${t.agent}`):"",f=s(` (${_(t.ts)})`);let a=t.summary||"";if(e)for(const p of e){const g=new RegExp(`(${p})`,"gi");a=a.replace(g,$=>S($))}console.log(` ${i(n+" "+r.padEnd(11))}${c}${o}${f}`),console.log(` ${a}`)}function Y(t){const e=E.join(t,F);return x.existsSync(e)?x.readFileSync(e,"utf8").split(`
2
- `).filter(Boolean).map(r=>{try{return JSON.parse(r)}catch{return null}}).filter(Boolean):[]}function v(t,e,r,n){let i=t;r&&(i=i.filter(o=>(o.type||"note")===r));let c;return e.length>0?c=i.map(o=>({entry:o,score:M(o,e)})).filter(({score:o})=>o>0).sort((o,f)=>{if(f.score!==o.score)return f.score-o.score;const a=y[o.entry.type]??9,p=y[f.entry.type]??9;return a!==p?a-p:new Date(f.entry.ts||0)-new Date(o.entry.ts||0)}):c=i.map(o=>({entry:o,score:1})).sort((o,f)=>{const a=y[o.entry.type]??9,p=y[f.entry.type]??9;return a!==p?a-p:new Date(f.entry.ts||0)-new Date(o.entry.ts||0)}),c.slice(0,n||20)}async function J(t=[]){const e=t,r=e.indexOf("--type"),n=r!==-1?e[r+1]:null,i=e.indexOf("--limit")!==-1?e.indexOf("--limit"):e.indexOf("-n"),c=i!==-1?parseInt(e[i+1]||"20",10):15,o=e.includes("--json"),f=e.includes("--recent")||e.includes("-r"),a=e.slice(1),g=a.filter((l,u)=>!(l.startsWith("--")||u>0&&a[u-1].startsWith("--"))).join(" ").trim(),$=f?[]:j(g),R=process.cwd(),d=Y(R);if(d.length===0){if(o){console.log(JSON.stringify({results:[],total:0}));return}console.log(s(`
1
+ import"node:fs";import*as j from"node:path";import{bold as O,cyan as S,gray as s,green as k,yellow as C,red as L}from"../ui/output.mjs";import{readEntries as W}from"../amp/io.mjs";const q="inferno",b=j.join(q,"sessions.jsonl"),d={gotcha:0,decision:1,attempt:2,preference:3,theme:4,note:5,error:5,handoff:6},I={gotcha:"\u26A0",decision:"\u2713",attempt:"\u21BA",preference:"\u2666",theme:"\u{1F3A8}",note:"\xB7",error:"\u2717",handoff:"\u2192"},N={gotcha:C,decision:k,attempt:S,preference:S,theme:S,note:s,error:L,handoff:s};function x(t){return t.toLowerCase().replace(/[^a-z0-9\s]/g," ").split(/\s+/).filter(o=>o.length>1)}function M(t,o){const i=[t.summary||"",t.type||""].join(" ").toLowerCase(),n=x(i);let l=0;for(const r of o)(t.summary||"").toLowerCase().includes(r)&&(l+=3),n.includes(r)&&(l+=1),n.some(e=>e.startsWith(r)||r.startsWith(e))&&(l+=.5);return l}function _(t){if(!t)return"";const o=new Date(t),i=Date.now()-o.getTime(),n=Math.floor(i/864e5);return n===0?"today":n===1?"yesterday":n<7?`${n}d ago`:n<30?`${Math.floor(n/7)}w ago`:o.toLocaleDateString("en-GB",{day:"2-digit",month:"short"})}function F(t,o){const i=t.type||"note",n=I[i]||"\xB7",l=N[i]||s,r=t.result?s(` [${t.result}]`):"",e=t.agent?s(` \u2014 ${t.agent}`):"",f=s(` (${_(t.ts)})`);let a=t.summary||"";if(o)for(const u of o){const p=new RegExp(`(${u})`,"gi");a=a.replace(p,$=>O($))}console.log(` ${l(n+" "+i.padEnd(11))}${r}${e}${f}`),console.log(` ${a}`)}function P(t){return W(t)}function Y(t,o,i,n){let l=t;i&&(l=l.filter(e=>(e.type||"note")===i));let r;return o.length>0?r=l.map(e=>({entry:e,score:M(e,o)})).filter(({score:e})=>e>0).sort((e,f)=>{if(f.score!==e.score)return f.score-e.score;const a=d[e.entry.type]??9,u=d[f.entry.type]??9;return a!==u?a-u:new Date(f.entry.ts||0)-new Date(e.entry.ts||0)}):r=l.map(e=>({entry:e,score:1})).sort((e,f)=>{const a=d[e.entry.type]??9,u=d[f.entry.type]??9;return a!==u?a-u:new Date(f.entry.ts||0)-new Date(e.entry.ts||0)}),r.slice(0,n||20)}async function z(t=[]){const o=t,i=o.indexOf("--type"),n=i!==-1?o[i+1]:null,l=o.indexOf("--limit")!==-1?o.indexOf("--limit"):o.indexOf("-n"),r=l!==-1?parseInt(o[l+1]||"20",10):15,e=o.includes("--json"),f=o.includes("--recent")||o.includes("-r"),a=o.slice(1),p=a.filter((c,g)=>!(c.startsWith("--")||g>0&&a[g-1].startsWith("--"))).join(" ").trim(),$=f?[]:x(p),D=process.cwd(),y=P(D);if(y.length===0){if(e){console.log(JSON.stringify({results:[],total:0}));return}console.log(s(`
3
2
  No session memory yet.`)),console.log(s(` Run: infernoflow log "<what happened>" --type gotcha
4
- `));return}const m=f?d.slice(-c).reverse().map(l=>({entry:l,score:1})):v(d,$,n,c);if(o){console.log(JSON.stringify({query:g,type:n,total:d.length,matched:m.length,results:m.map(({entry:l,score:u})=>({...l,relevanceScore:u}))},null,2));return}if(console.log(),console.log(g?` ${S("\u{1F525} infernoflow ask")} ${O(`"${g}"`)}${n?s(` [${n}]`):""}`:f?` ${S("\u{1F525} infernoflow ask")} ${s("\u2014 recent entries")}`:` ${S("\u{1F525} infernoflow ask")} ${s("\u2014 all entries")}${n?s(` [${n}]`):""}`),console.log(s(` ${"\u2500".repeat(52)}`)),m.length===0){console.log(),g?(console.log(s(` No entries found for "${g}"`)),n&&console.log(s(` Try removing --type ${n} to widen the search`))):console.log(s(" No entries found.")),console.log();return}const w=new Map;for(const{entry:l,score:u}of m){const h=l.type||"note";w.has(h)||w.set(h,[]),w.get(h).push({entry:l,score:u})}const T=Object.keys(y).sort((l,u)=>y[l]-y[u]);let N=0;for(const l of T){const u=w.get(l);if(!u?.length)continue;console.log();const h=D[l]||s;console.log(h(` ${I[l]} ${l.toUpperCase()}S (${u.length})`)),console.log(s(" "+"\u2500".repeat(50)));for(const{entry:k}of u)console.log(),P(k,$),N++}console.log(),console.log(s(` ${N} result${N!==1?"s":""} from ${d.length} total entries`)),m.length===c&&d.length>c&&console.log(s(" Use --limit N to see more")),console.log()}export{J as askCommand};
3
+ `));return}const m=f?y.slice(-r).reverse().map(c=>({entry:c,score:1})):Y(y,$,n,r);if(e){console.log(JSON.stringify({query:p,type:n,total:y.length,matched:m.length,results:m.map(({entry:c,score:g})=>({...c,relevanceScore:g}))},null,2));return}if(console.log(),console.log(p?` ${O("\u{1F525} infernoflow ask")} ${S(`"${p}"`)}${n?s(` [${n}]`):""}`:f?` ${O("\u{1F525} infernoflow ask")} ${s("\u2014 recent entries")}`:` ${O("\u{1F525} infernoflow ask")} ${s("\u2014 all entries")}${n?s(` [${n}]`):""}`),console.log(s(` ${"\u2500".repeat(52)}`)),m.length===0){console.log(),p?(console.log(s(` No entries found for "${p}"`)),n&&console.log(s(` Try removing --type ${n} to widen the search`))):console.log(s(" No entries found.")),console.log();return}const w=new Map;for(const{entry:c,score:g}of m){const h=c.type||"note";w.has(h)||w.set(h,[]),w.get(h).push({entry:c,score:g})}const R=Object.keys(d).sort((c,g)=>d[c]-d[g]);let E=0;for(const c of R){const g=w.get(c);if(!g?.length)continue;console.log();const h=N[c]||s;console.log(h(` ${I[c]} ${c.toUpperCase()}S (${g.length})`)),console.log(s(" "+"\u2500".repeat(50)));for(const{entry:T}of g)console.log(),F(T,$),E++}console.log(),console.log(s(` ${E} result${E!==1?"s":""} from ${y.length} total entries`)),m.length===r&&y.length>r&&console.log(s(" Use --limit N to see more")),console.log()}export{z as askCommand};
@@ -1,8 +1,8 @@
1
- import*as i from"node:fs";import*as t from"node:path";import*as L from"node:readline";import{fileURLToPath as se}from"node:url";import{header as re,ok as y,warn as _,done as z,nextSteps as ce,bold as H,cyan as f,yellow as J,gray as F}from"../ui/output.mjs";import{discoverProjectSignals as D,reviewCapabilitiesInteractive as le,writeAdoptionBaseline as ae,buildAdoptionReport as pe,summarizeCapabilities as fe,buildSignalsReport as de}from"./adopt.mjs";import{installCursorHooksArtifacts as ue}from"../cursorHooksInstall.mjs";import{installVsCodeCopilotHooksArtifacts as me}from"../vsCodeCopilotHooksInstall.mjs";const ye=t.dirname(se(import.meta.url));function ge(){return t.resolve(ye,"../../templates")}function R(e,o,s=""){return new Promise(n=>{const c=s?F(` (${s})`):"";e.question(` ${o}${c}: `,a=>{n(a.trim()||s)})})}function q(e,...o){for(const s of o){const n=e.indexOf(s);if(n!==-1&&e[n+1]&&!e[n+1].startsWith("-"))return e[n+1]}return null}function G(e,o,s,n=!1){return i.existsSync(o)&&!s?(n||_("Skipped (exists): "+t.relative(process.cwd(),o)),!1):(i.mkdirSync(t.dirname(o),{recursive:!0}),i.copyFileSync(e,o),n||y("Created: "+f(t.relative(process.cwd(),o))),!0)}function we(e,o,s){i.mkdirSync(o,{recursive:!0});for(const n of i.readdirSync(e,{withFileTypes:!0})){const c=t.join(e,n.name),a=t.join(o,n.name);n.isDirectory()?we(c,a,s):G(c,a,s)}}function je(e,o=!1){const s=t.join(e,"package.json");if(!i.existsSync(s))return;const n=JSON.parse(i.readFileSync(s,"utf8"));n.scripts=n.scripts||{};let c=!1;const a={"inferno:check":"infernoflow check","inferno:status":"infernoflow status","inferno:gate":"infernoflow doc-gate","inferno:impact":"infernoflow pr-impact --json","inferno:sync":"infernoflow sync --auto --json","inferno:run":'infernoflow run "sync check" --provider auto --json',"inferno:hooks":"node scripts/inferno-install-hooks.mjs"};for(const[h,A]of Object.entries(a))n.scripts[h]||(n.scripts[h]=A,c=!0);c&&(i.writeFileSync(s,JSON.stringify(n,null,2)+`
2
- `,"utf8"),o||y("Updated "+f("package.json")+" scripts"))}function U(e){const o=t.join(e,"package.json");if(i.existsSync(o))try{const s=JSON.parse(i.readFileSync(o,"utf8"));if(s.name)return s.name.replace(/[^a-z0-9_-]/gi,"_")}catch{}return t.basename(e)}function he(e,o,s){const n={policyId:o,policyVersion:1,capabilities:s,rules:{docsRequiredOnCapabilityChange:!0,requireScenarioForEachCapability:!0,requireChangelogOnCapabilityChange:!0}};i.writeFileSync(e,JSON.stringify(n,null,2)+`
3
- `)}function ke(e,o){const s={schemaVersion:1,capabilities:o.map(n=>({id:n,title:n.replace(/([A-Z])/g," $1").trim(),since:"0.1.0"}))};i.writeFileSync(e,JSON.stringify(s,null,2)+`
4
- `)}function Se(e,o){i.mkdirSync(e,{recursive:!0});const s={scenarioId:"happy_path",description:"Basic happy-path flow covering all capabilities",capabilitiesCovered:o,steps:o.map(n=>({action:n,expect:`${n} works as expected`}))};i.writeFileSync(t.join(e,"happy_path.json"),JSON.stringify(s,null,2)+`
5
- `)}function V(e,o){const s=`# Changelog \u2014 ${o}
1
+ import*as i from"node:fs";import*as s from"node:path";import*as L from"node:readline";import{fileURLToPath as se}from"node:url";import{header as re,ok as g,warn as $,done as q,nextSteps as ce,bold as J,cyan as d,yellow as R,gray as A}from"../ui/output.mjs";import{discoverProjectSignals as H,reviewCapabilitiesInteractive as le,writeAdoptionBaseline as ae,buildAdoptionReport as pe,summarizeCapabilities as fe,buildSignalsReport as de}from"./adopt.mjs";import{installCursorHooksArtifacts as ue}from"../cursorHooksInstall.mjs";import{ensureAmpDir as me,appendEntry as ye,writeDefaultConfig as ge}from"../amp/io.mjs";import{installVsCodeCopilotHooksArtifacts as we}from"../vsCodeCopilotHooksInstall.mjs";const he=s.dirname(se(import.meta.url));function ke(){return s.resolve(he,"../../templates")}function _(e,o,n=""){return new Promise(t=>{const r=n?A(` (${n})`):"";e.question(` ${o}${r}: `,l=>{t(l.trim()||n)})})}function M(e,...o){for(const n of o){const t=e.indexOf(n);if(t!==-1&&e[t+1]&&!e[t+1].startsWith("-"))return e[t+1]}return null}function D(e,o,n,t=!1){return i.existsSync(o)&&!n?(t||$("Skipped (exists): "+s.relative(process.cwd(),o)),!1):(i.mkdirSync(s.dirname(o),{recursive:!0}),i.copyFileSync(e,o),t||g("Created: "+d(s.relative(process.cwd(),o))),!0)}function je(e,o,n){i.mkdirSync(o,{recursive:!0});for(const t of i.readdirSync(e,{withFileTypes:!0})){const r=s.join(e,t.name),l=s.join(o,t.name);t.isDirectory()?je(r,l,n):D(r,l,n)}}function Se(e,o=!1){const n=s.join(e,"package.json");if(!i.existsSync(n))return;const t=JSON.parse(i.readFileSync(n,"utf8"));t.scripts=t.scripts||{};let r=!1;const l={"inferno:check":"infernoflow check","inferno:status":"infernoflow status","inferno:gate":"infernoflow doc-gate","inferno:impact":"infernoflow pr-impact --json","inferno:sync":"infernoflow sync --auto --json","inferno:run":'infernoflow run "sync check" --provider auto --json',"inferno:hooks":"node scripts/inferno-install-hooks.mjs"};for(const[y,k]of Object.entries(l))t.scripts[y]||(t.scripts[y]=k,r=!0);r&&(i.writeFileSync(n,JSON.stringify(t,null,2)+`
2
+ `,"utf8"),o||g("Updated "+d("package.json")+" scripts"))}function G(e){const o=s.join(e,"package.json");if(i.existsSync(o))try{const n=JSON.parse(i.readFileSync(o,"utf8"));if(n.name)return n.name.replace(/[^a-z0-9_-]/gi,"_")}catch{}return s.basename(e)}function ve(e,o,n){const t={policyId:o,policyVersion:1,capabilities:n,rules:{docsRequiredOnCapabilityChange:!0,requireScenarioForEachCapability:!0,requireChangelogOnCapabilityChange:!0}};i.writeFileSync(e,JSON.stringify(t,null,2)+`
3
+ `)}function be(e,o){const n={schemaVersion:1,capabilities:o.map(t=>({id:t,title:t.replace(/([A-Z])/g," $1").trim(),since:"0.1.0"}))};i.writeFileSync(e,JSON.stringify(n,null,2)+`
4
+ `)}function Ce(e,o){i.mkdirSync(e,{recursive:!0});const n={scenarioId:"happy_path",description:"Basic happy-path flow covering all capabilities",capabilitiesCovered:o,steps:o.map(t=>({action:t,expect:`${t} works as expected`}))};i.writeFileSync(s.join(e,"happy_path.json"),JSON.stringify(n,null,2)+`
5
+ `)}function z(e,o){const n=`# Changelog \u2014 ${o}
6
6
 
7
7
  ## Unreleased
8
8
 
@@ -11,42 +11,41 @@ import*as i from"node:fs";import*as t from"node:path";import*as L from"node:read
11
11
  ## 0.1.0 \u2014 Initial release
12
12
 
13
13
  - Project initialized with infernoflow
14
- `;i.writeFileSync(e,s)}async function ve(e,o){const{bold:s,cyan:n,gray:c,green:a,yellow:h,red:A}=await import("../ui/output.mjs");console.log(`
15
- `+s("\u{1F525} infernoflow init --lite")),console.log(" "+"\u2500".repeat(50)+`
16
- `),console.log(c(" Lite mode: 3 files, no scripts, no workflows, no hooks.")),console.log(c(" Use `infernoflow upgrade` later to expand to the full setup.\n"));const m=t.join(e,"inferno");i.existsSync(m)&&!o&&(console.log(h(` \u26A0 inferno/ already exists. Use --force to overwrite.
17
- `)),process.exit(0)),i.mkdirSync(m,{recursive:!0});const w=U(e);let S="";if(!process.argv.includes("--yes")&&!process.argv.includes("-y")&&process.stdin.isTTY){const C=L.createInterface({input:process.stdin,output:process.stdout});S=await new Promise(j=>{C.question(c(" What does this project do? (one line, Enter to skip): "),O=>{C.close(),j(O.trim())})})}const v={policyId:w,policyVersion:1,lite:!0,capabilities:[],intent:S||void 0};i.writeFileSync(t.join(m,"contract.json"),JSON.stringify(v,null,2)+`
18
- `),i.writeFileSync(t.join(m,"capabilities.json"),JSON.stringify([],null,2)+`
19
- `),i.writeFileSync(t.join(m,"sessions.jsonl"),"","utf8"),i.writeFileSync(t.join(m,".lite"),"1","utf8"),console.log(a(" \u2714 Created inferno/contract.json")),console.log(a(" \u2714 Created inferno/capabilities.json")),console.log(a(" \u2714 Created inferno/sessions.jsonl")),console.log(),console.log(" "+s("Ready. Start using it:")),console.log(" "+n("infernoflow log")+c(` "what you're building" --type note`)),console.log(" "+n("infernoflow theme")+c(" \u2014 scan your fonts + colors")),console.log(" "+n("infernoflow context")+c(" \u2014 generate AI context to paste")),console.log(" "+n("infernoflow upgrade")+c(" \u2014 expand to full setup when you need it")),console.log()}async function Ce(e,o,s){const{bold:n,cyan:c,gray:a,green:h,yellow:A}=await import("../ui/output.mjs"),m=t.join(e,"inferno"),w=t.join(m,"sessions.jsonl"),S=t.join(m,"config.json");if(i.existsSync(m)&&!o){console.log(`
20
- `+n("\u{1F525} infernoflow")+a(` \u2014 already set up
21
- `)),console.log(" "+h("\u2714")+` inferno/ found
22
- `),console.log(" Quick commands:"),console.log(" "+c('infernoflow log "..."')+a(" \u2014 remember something")),console.log(" "+c("infernoflow switch")+a(" \u2014 handoff to next AI")),console.log(" "+c("infernoflow recap")+a(` \u2014 session summary
23
- `)),console.log(a(` For contracts & CI gates: infernoflow init --mode full
24
- `));return}const v=U(e);console.log(`
25
- `+n("\u{1F525} infernoflow")+a(` \u2014 let's get you set up (30 seconds)
26
- `)),console.log(" Detected: "+c(v)+`
27
- `),i.mkdirSync(m,{recursive:!0}),i.writeFileSync(S,JSON.stringify({project:v,version:"1",mode:"memory",created:new Date().toISOString()},null,2)+`
28
- `,"utf8"),i.existsSync(w)||i.writeFileSync(w,"","utf8");let C="";if(!s&&process.stdin.isTTY){const j=L.createInterface({input:process.stdin,output:process.stdout});C=await new Promise(O=>{j.question(" "+a(`What should the next AI agent know about this project?
29
- > `),N=>{j.close(),O(N.trim())})})}if(C){const j={ts:new Date().toISOString(),agent:"user",type:"gotcha",summary:C,source:"init"};i.appendFileSync(w,JSON.stringify(j)+`
30
- `,"utf8"),console.log(`
31
- `+h("\u2714")+" First gotcha logged!")}console.log(`
32
- `+h("\u2714")+` You're set up. Quick commands:
33
- `),console.log(" "+c('infernoflow log "..."')+a(" \u2014 remember something")),console.log(" "+c("infernoflow switch")+a(" \u2014 generate handoff for next AI")),console.log(" "+c("infernoflow recap")+a(` \u2014 session summary
34
- `)),console.log(a(` Tip: infernoflow switch --copy puts the handoff on your clipboard.
35
- `)),console.log(a(` Want contracts & CI gates? Run: infernoflow init --mode full
36
- `))}async function Te(e){const o=process.cwd(),s=e.includes("--force")||e.includes("-f"),n=e.includes("--yes")||e.includes("-y"),c=e.includes("--adopt"),a=e.find(l=>l.startsWith("--mode="))?.split("=")[1]||(e.indexOf("--mode")!==-1?e[e.indexOf("--mode")+1]:null),h=a==="full"||a==="contract",A=c||e.includes("--template")||e.includes("--cursor-hooks")||e.includes("--vscode-copilot-hooks")||e.includes("--lite");if(!h&&!A){await Ce(o,s,n);return}if(e.includes("--lite")){await ve(o,s);return}const m=e.indexOf("--template"),w=m!==-1?e[m+1]:null;if(w){let l;try{l=await import("../templates/index.mjs")}catch{}const r=l?.getTemplate(w);if(!r){const p=l?l.listTemplates().map(b=>b.name).join(", "):"rest-api, nextjs, cli, graphql, monorepo";_(`Unknown template: ${w}. Available: ${p}`),process.exit(1)}const u=t.join(o,"inferno"),I=t.join(u,"scenarios");i.existsSync(u)||i.mkdirSync(u,{recursive:!0}),i.existsSync(I)||i.mkdirSync(I,{recursive:!0});const E=U(o),g=r.capabilities;i.writeFileSync(t.join(u,"contract.json"),JSON.stringify({policyId:E,policyVersion:1,capabilities:g.map(p=>p.id)},null,2)+`
37
- `),i.writeFileSync(t.join(u,"capabilities.json"),JSON.stringify({capabilities:g.map(p=>({id:p.id,description:p.description,since:new Date().toISOString().slice(0,10),source:`template:${w}`}))},null,2)+`
38
- `);for(const p of g)i.writeFileSync(t.join(I,`${p.id}.json`),JSON.stringify({id:`${p.id}-happy-path`,capability:p.id,description:`Happy path for ${p.description||p.id}`,steps:[{action:"invoke",target:p.id,input:{}},{action:"assert",field:"status",value:"success"}],capabilitiesCovered:[p.id]},null,2)+`
39
- `);V(t.join(u,"CHANGELOG.md"),E),i.writeFileSync(t.join(u,"CONTEXT.md"),`# ${E} \u2014 infernoflow context
14
+ `;i.writeFileSync(e,n)}async function Oe(e,o){const{bold:n,cyan:t,gray:r,green:l,yellow:y,red:k}=await import("../ui/output.mjs");console.log(`
15
+ `+n("\u{1F525} infernoflow init --lite")),console.log(" "+"\u2500".repeat(50)+`
16
+ `),console.log(r(" Lite mode: 3 files, no scripts, no workflows, no hooks.")),console.log(r(" Use `infernoflow upgrade` later to expand to the full setup.\n"));const p=s.join(e,"inferno");i.existsSync(p)&&!o&&(console.log(y(` \u26A0 inferno/ already exists. Use --force to overwrite.
17
+ `)),process.exit(0)),i.mkdirSync(p,{recursive:!0});const w=G(e);let j="";if(!process.argv.includes("--yes")&&!process.argv.includes("-y")&&process.stdin.isTTY){const b=L.createInterface({input:process.stdin,output:process.stdout});j=await new Promise(S=>{b.question(r(" What does this project do? (one line, Enter to skip): "),x=>{b.close(),S(x.trim())})})}const O={policyId:w,policyVersion:1,lite:!0,capabilities:[],intent:j||void 0};i.writeFileSync(s.join(p,"contract.json"),JSON.stringify(O,null,2)+`
18
+ `),i.writeFileSync(s.join(p,"capabilities.json"),JSON.stringify([],null,2)+`
19
+ `),i.writeFileSync(s.join(p,"sessions.jsonl"),"","utf8"),i.writeFileSync(s.join(p,".lite"),"1","utf8"),console.log(l(" \u2714 Created inferno/contract.json")),console.log(l(" \u2714 Created inferno/capabilities.json")),console.log(l(" \u2714 Created inferno/sessions.jsonl")),console.log(),console.log(" "+n("Ready. Start using it:")),console.log(" "+t("infernoflow log")+r(` "what you're building" --type note`)),console.log(" "+t("infernoflow theme")+r(" \u2014 scan your fonts + colors")),console.log(" "+t("infernoflow context")+r(" \u2014 generate AI context to paste")),console.log(" "+t("infernoflow upgrade")+r(" \u2014 expand to full setup when you need it")),console.log()}const xe=/^(?:node|npm|npx|yarn|pnpm|bun|git|cd|mkdir|rm|ls|cat|echo|type|dir|copy|del|move|python|python3|pip|go|cargo|java|gradle|mvn|docker|kubectl|curl|wget|ssh|scp|chmod|chown|sudo|brew|apt|yum)\b/i,Ie=/\s(?:&&|\|\||>>|>|<<|<|\|)\s/,Te=/(?:^|\s)[A-Za-z]:\\|\.\.[\\\/]|[\\\/]bin[\\\/]/;function Ae(e){if(!e)return{kind:"empty"};const o=e.replace(/^\s*[>$#]\s+/,"").trim();return o?/[\r\n]/.test(o)?{kind:"multiline",value:o}:xe.test(o)?{kind:"command",value:o}:Ie.test(o)?{kind:"command",value:o}:Te.test(o)?{kind:"command",value:o}:o.length<3?{kind:"tooShort",value:o}:{kind:"ok",value:o}:{kind:"empty"}}async function Pe({yes:e}){if(e||!process.stdin.isTTY)return"";const{gray:o,yellow:n,cyan:t}=await import("../ui/output.mjs"),r=" "+o(`What should the next AI agent know about this project?
20
+ > `),l=y=>new Promise(k=>{const p=L.createInterface({input:process.stdin,output:process.stdout});let w=!1;p.on("SIGINT",()=>{w=!0,p.close(),k(null)}),p.on("close",()=>{w&&k(null)}),p.question(y,j=>{p.close(),k(j)})});for(let y=0;y<2;y++){const k=await l(y===0?r:" "+o("> "));if(k===null)return console.log(),"";const p=Ae(k);if(p.kind==="ok")return p.value;if(p.kind==="empty")return"";if(p.kind==="command"){console.log(" "+n("\u26A0")+" That looks like a shell command, not a memory."),console.log(" "+o(" Try a short note like: ")+t('"API returns 202 not 200 on async upload"'));continue}if(p.kind==="multiline"){console.log(" "+n("\u26A0")+" Multi-line paste detected \u2014 log a single gotcha at a time."),console.log(" "+o(" Try one short sentence:"));continue}if(p.kind==="tooShort"){console.log(" "+n("\u26A0")+" Too short to be useful as a memory. Skip with Enter, or try again:");continue}}return""}async function Fe(e,o,n){const{bold:t,cyan:r,gray:l,green:y,yellow:k}=await import("../ui/output.mjs"),p=s.join(e,".ai-memory"),w=s.join(e,"inferno"),j=s.join(p,"sessions.jsonl");if((i.existsSync(p)||i.existsSync(w))&&!o){const x=i.existsSync(p)?".ai-memory/":"inferno/ (legacy)";console.log(`
21
+ `+t("\u{1F525} infernoflow")+l(` \u2014 already set up
22
+ `)),console.log(" "+y("\u2714")+" "+x+` found
23
+ `),console.log(" Quick commands:"),console.log(" "+r('infernoflow log "..."')+l(" \u2014 remember something")),console.log(" "+r("infernoflow switch")+l(" \u2014 handoff to next AI")),console.log(" "+r("infernoflow recap")+l(` \u2014 session summary
24
+ `)),i.existsSync(p)||console.log(l(" Tip: run ")+r("infernoflow amp migrate")+l(` to move legacy memory into .ai-memory/
25
+ `)),console.log(l(` For contracts & CI gates: infernoflow init --mode full
26
+ `));return}const b=G(e);console.log(`
27
+ `+t("\u{1F525} infernoflow")+l(` \u2014 let's get you set up (30 seconds)
28
+ `)),console.log(" Detected: "+r(b)+`
29
+ `),me(e),i.existsSync(j)||i.writeFileSync(j,"","utf8"),ge(e,{project:b,config:{autoCapture:!0}});const S=await Pe({yes:n});S&&(ye(e,{ts:new Date().toISOString(),agent:"user",type:"gotcha",summary:S,source:"init"}),console.log(`
30
+ `+y("\u2714")+" First gotcha logged!")),console.log(`
31
+ `+y("\u2714")+` You're set up. Quick commands:
32
+ `),console.log(" "+r('infernoflow log "..."')+l(" \u2014 remember something")),console.log(" "+r("infernoflow switch")+l(" \u2014 generate handoff for next AI")),console.log(" "+r("infernoflow recap")+l(` \u2014 session summary
33
+ `)),console.log(l(` Tip: infernoflow switch --copy puts the handoff on your clipboard.
34
+ `)),console.log(l(` Want contracts & CI gates? Run: infernoflow init --mode full
35
+ `))}async function Ge(e){const o=process.cwd(),n=e.includes("--force")||e.includes("-f"),t=e.includes("--yes")||e.includes("-y"),r=e.includes("--adopt"),l=e.find(a=>a.startsWith("--mode="))?.split("=")[1]||(e.indexOf("--mode")!==-1?e[e.indexOf("--mode")+1]:null),y=l==="full"||l==="contract",k=r||e.includes("--template")||e.includes("--cursor-hooks")||e.includes("--vscode-copilot-hooks")||e.includes("--lite");if(!y&&!k){await Fe(o,n,t);return}if(e.includes("--lite")){await Oe(o,n);return}const p=e.indexOf("--template"),w=p!==-1?e[p+1]:null;if(w){let a;try{a=await import("../templates/index.mjs")}catch{}const c=a?.getTemplate(w);if(!c){const f=a?a.listTemplates().map(C=>C.name).join(", "):"rest-api, nextjs, cli, graphql, monorepo";$(`Unknown template: ${w}. Available: ${f}`),process.exit(1)}const m=s.join(o,"inferno"),T=s.join(m,"scenarios");i.existsSync(m)||i.mkdirSync(m,{recursive:!0}),i.existsSync(T)||i.mkdirSync(T,{recursive:!0});const N=G(o),h=c.capabilities;i.writeFileSync(s.join(m,"contract.json"),JSON.stringify({policyId:N,policyVersion:1,capabilities:h.map(f=>f.id)},null,2)+`
36
+ `),i.writeFileSync(s.join(m,"capabilities.json"),JSON.stringify({capabilities:h.map(f=>({id:f.id,description:f.description,since:new Date().toISOString().slice(0,10),source:`template:${w}`}))},null,2)+`
37
+ `);for(const f of h)i.writeFileSync(s.join(T,`${f.id}.json`),JSON.stringify({id:`${f.id}-happy-path`,capability:f.id,description:`Happy path for ${f.description||f.id}`,steps:[{action:"invoke",target:f.id,input:{}},{action:"assert",field:"status",value:"success"}],capabilitiesCovered:[f.id]},null,2)+`
38
+ `);z(s.join(m,"CHANGELOG.md"),N),i.writeFileSync(s.join(m,"CONTEXT.md"),`# ${N} \u2014 infernoflow context
40
39
 
41
- > Template: ${w} \u2014 ${r.description}
40
+ > Template: ${w} \u2014 ${c.description}
42
41
 
43
42
  ## Hint
44
- ${r.contextHint}
43
+ ${c.contextHint}
45
44
 
46
- ## Capabilities (${g.length})
47
- ${g.map(p=>`- \`${p.id}\`: ${p.description}`).join(`
45
+ ## Capabilities (${h.length})
46
+ ${h.map(f=>`- \`${f.id}\`: ${f.description}`).join(`
48
47
  `)}
49
- `),r.scripts&&(info("Suggested package.json scripts for this template:"),Object.entries(r.scripts).forEach(([p,b])=>console.log(` ${H(p)}: ${F(b)}`)),console.log()),z(`Initialised from template ${H(f(w))} \u2014 ${H(String(g.length))} capabilities`),console.log(),info(`Run ${f("infernoflow vibe")} to start vibe coding mode`),console.log();return}const S=e.includes("--cursor-hooks"),v=e.includes("--vscode-copilot-hooks"),C=e.includes("--report-json"),j=e.includes("--report-json-only"),O=e.includes("--report-human-only"),N=q(e,"--lang"),W=q(e,"--framework"),Y=q(e,"--project-type"),d=j;j&&O&&(console.error("Error: --report-json-only and --report-human-only cannot be used together."),process.exit(1)),d||re("init");const k=t.join(o,"inferno"),M=t.join(o,".github","workflows");i.existsSync(k)&&!s&&(d&&(console.log(JSON.stringify({ok:!1,error:"inferno_exists",hint:"Use --force to overwrite"},null,2)),process.exit(1)),_("inferno/ already exists. Use --force to overwrite."),console.log(),process.exit(0));const T=U(o),K="CreateTask, ReadTasks, UpdateTask, ToggleComplete, DeleteTask";let P=T,x=K.split(",").map(l=>l.trim());if(c){let r=D(o,{language:N||void 0,framework:W||void 0,projectType:Y||void 0});if(!n&&!j){const g=L.createInterface({input:process.stdin,output:process.stdout}),p=r.developmentProfile||{},b=p.detected||{};console.log(F(` Review inferred development stack (press Enter to accept detected values)
50
- `));const ne=await R(g,"Language",p.language||b.language||"unknown"),ie=await R(g,"Framework",p.framework||b.framework||"unknown"),te=await R(g,"Project type",p.projectType||b.projectType||"unknown");g.close(),r=D(o,{language:ne,framework:ie,projectType:te})}const u=r.capabilities,I=fe(u);j?console.log(JSON.stringify({mode:"adopt",policyId:T,inferredCapabilities:I,components:r.components,displayFields:r.displayFields,externalLibraries:r.externalLibraries,uiLayout:r.uiLayout,styling:r.styling,developmentProfile:r.developmentProfile,apiCalls:r.apiCalls},null,2)):(console.log(),console.log(F(pe(u))),console.log(),console.log(F(de(r))),console.log(),C&&!O&&(console.log(JSON.stringify({mode:"adopt",policyId:T,inferredCapabilities:I,components:r.components,displayFields:r.displayFields,externalLibraries:r.externalLibraries,uiLayout:r.uiLayout,styling:r.styling,developmentProfile:r.developmentProfile,apiCalls:r.apiCalls},null,2)),console.log()));const E=await le(u,n);P=T,x=E.map(g=>g.id)}else if(!n){const l=L.createInterface({input:process.stdin,output:process.stdout});console.log(F(` Press Enter to accept defaults
51
- `)),P=await R(l,"Project / policy name",T),x=(await R(l,"Capabilities (comma-separated)",K)).split(",").map(u=>u.trim()).filter(Boolean),l.close(),console.log()}if(i.mkdirSync(k,{recursive:!0}),c){const l=x.map(u=>({id:u,title:u.replace(/([A-Z])/g," $1").trim()})),r=D(o,{language:N||void 0,framework:W||void 0,projectType:Y||void 0});ae(k,P,l,r),d||(y("Created: "+f("inferno/contract.json")),y("Created: "+f("inferno/capabilities.json")),y("Created: "+f("inferno/scenarios/adoption_baseline.json")),y("Created: "+f("inferno/adoption_profile.json")),y("Created: "+f("inferno/CHANGELOG.md")))}else he(t.join(k,"contract.json"),P,x),d||y("Created: "+f("inferno/contract.json")),ke(t.join(k,"capabilities.json"),x),d||y("Created: "+f("inferno/capabilities.json")),Se(t.join(k,"scenarios"),x),d||y("Created: "+f("inferno/scenarios/happy_path.json")),V(t.join(k,"CHANGELOG.md"),P),d||y("Created: "+f("inferno/CHANGELOG.md"));const $=ge(),B=t.join($,"scripts","inferno-doc-gate.mjs"),X=t.join(o,"scripts","inferno-doc-gate.mjs");G(B,X,s,d);const Q=t.join($,"scripts","inferno-install-hooks.mjs"),Z=t.join(o,"scripts","inferno-install-hooks.mjs");G(Q,Z,s,d);const ee=t.join($,"ci","github-inferno-check.yml"),oe=t.join(M,"infernoflow-check.yml");if(G(ee,oe,s,d),je(o,d),S&&ue({cwd:o,templatesRoot:$,force:s,silent:d,logOk:l=>{d||y(l)},logWarn:l=>{d||_(l)}}),v&&me({cwd:o,templatesRoot:$,force:s,silent:d,logOk:l=>{d||y(l)},logWarn:l=>{d||_(l)}}),c){const l=t.join(k,"context-state.json");let r={};try{r=JSON.parse(i.readFileSync(l,"utf8"))}catch{}const u=D(o,{language:N||void 0,framework:W||void 0,projectType:Y||void 0});r.stack=u.developmentProfile,i.writeFileSync(l,JSON.stringify(r,null,2)+`
52
- `,"utf8"),d||y("Created: "+f("inferno/context-state.json"))}if(!d){z("infernoflow initialized!");const l=t.join(k,"integrations.json");!(process.env.ANTHROPIC_API_KEY||process.env.OPENAI_API_KEY||process.env.GOOGLE_AI_API_KEY||process.env.OPENROUTER_API_KEY||process.env.GEMINI_API_KEY)&&!i.existsSync(l)&&(console.log(),console.log(` ${J("\u{1F4A1}")} ${H("Tip:")} connect an AI provider for explain, why, review, and changelog AI.`),console.log(` ${f("infernoflow ai setup")} \u2014 takes 60 seconds`)),ce([f("infernoflow status")+" \u2014 see your contract at a glance",f("infernoflow check")+" \u2014 validate everything",(c?"Review inferred baseline in ":"Edit ")+J("inferno/capabilities.json")+(c?" and refine IDs/titles":" to describe each capability in detail"),"Add more "+J("inferno/scenarios/*.json")+" files for edge cases","Add "+f("inferno:check")+" to your CI pipeline",...S?["Restart Cursor \u2014 hooks write assistant text to "+J("inferno/CONTEXT.draft.md"),"Promote when ready: "+f("npm run inferno:promote-draft -- --append-notes")]:[],...v?["Restart VS Code \u2014 Copilot hooks append prompts + assistant (from transcript) to "+J("inferno/CONTEXT.draft.md"),"Promote when ready: "+f("npm run inferno:promote-draft -- --append-notes")]:[],...!S&&!v?["Optional: "+f("infernoflow install-cursor-hooks")+" or "+f("infernoflow install-vscode-copilot-hooks")]:[]])}}export{Te as initCommand};
48
+ `),c.scripts&&(info("Suggested package.json scripts for this template:"),Object.entries(c.scripts).forEach(([f,C])=>console.log(` ${J(f)}: ${A(C)}`)),console.log()),q(`Initialised from template ${J(d(w))} \u2014 ${J(String(h.length))} capabilities`),console.log(),info(`Run ${d("infernoflow vibe")} to start vibe coding mode`),console.log();return}const j=e.includes("--cursor-hooks"),O=e.includes("--vscode-copilot-hooks"),b=e.includes("--report-json"),S=e.includes("--report-json-only"),x=e.includes("--report-human-only"),U=M(e,"--lang"),W=M(e,"--framework"),K=M(e,"--project-type"),u=S;S&&x&&(console.error("Error: --report-json-only and --report-human-only cannot be used together."),process.exit(1)),u||re("init");const v=s.join(o,"inferno"),V=s.join(o,".github","workflows");i.existsSync(v)&&!n&&(u&&(console.log(JSON.stringify({ok:!1,error:"inferno_exists",hint:"Use --force to overwrite"},null,2)),process.exit(1)),$("inferno/ already exists. Use --force to overwrite."),console.log(),process.exit(0));const P=G(o),Y="CreateTask, ReadTasks, UpdateTask, ToggleComplete, DeleteTask";let F=P,I=Y.split(",").map(a=>a.trim());if(r){let c=H(o,{language:U||void 0,framework:W||void 0,projectType:K||void 0});if(!t&&!S){const h=L.createInterface({input:process.stdin,output:process.stdout}),f=c.developmentProfile||{},C=f.detected||{};console.log(A(` Review inferred development stack (press Enter to accept detected values)
49
+ `));const ne=await _(h,"Language",f.language||C.language||"unknown"),te=await _(h,"Framework",f.framework||C.framework||"unknown"),ie=await _(h,"Project type",f.projectType||C.projectType||"unknown");h.close(),c=H(o,{language:ne,framework:te,projectType:ie})}const m=c.capabilities,T=fe(m);S?console.log(JSON.stringify({mode:"adopt",policyId:P,inferredCapabilities:T,components:c.components,displayFields:c.displayFields,externalLibraries:c.externalLibraries,uiLayout:c.uiLayout,styling:c.styling,developmentProfile:c.developmentProfile,apiCalls:c.apiCalls},null,2)):(console.log(),console.log(A(pe(m))),console.log(),console.log(A(de(c))),console.log(),b&&!x&&(console.log(JSON.stringify({mode:"adopt",policyId:P,inferredCapabilities:T,components:c.components,displayFields:c.displayFields,externalLibraries:c.externalLibraries,uiLayout:c.uiLayout,styling:c.styling,developmentProfile:c.developmentProfile,apiCalls:c.apiCalls},null,2)),console.log()));const N=await le(m,t);F=P,I=N.map(h=>h.id)}else if(!t){const a=L.createInterface({input:process.stdin,output:process.stdout});console.log(A(` Press Enter to accept defaults
50
+ `)),F=await _(a,"Project / policy name",P),I=(await _(a,"Capabilities (comma-separated)",Y)).split(",").map(m=>m.trim()).filter(Boolean),a.close(),console.log()}if(i.mkdirSync(v,{recursive:!0}),r){const a=I.map(m=>({id:m,title:m.replace(/([A-Z])/g," $1").trim()})),c=H(o,{language:U||void 0,framework:W||void 0,projectType:K||void 0});ae(v,F,a,c),u||(g("Created: "+d("inferno/contract.json")),g("Created: "+d("inferno/capabilities.json")),g("Created: "+d("inferno/scenarios/adoption_baseline.json")),g("Created: "+d("inferno/adoption_profile.json")),g("Created: "+d("inferno/CHANGELOG.md")))}else ve(s.join(v,"contract.json"),F,I),u||g("Created: "+d("inferno/contract.json")),be(s.join(v,"capabilities.json"),I),u||g("Created: "+d("inferno/capabilities.json")),Ce(s.join(v,"scenarios"),I),u||g("Created: "+d("inferno/scenarios/happy_path.json")),z(s.join(v,"CHANGELOG.md"),F),u||g("Created: "+d("inferno/CHANGELOG.md"));const E=ke(),X=s.join(E,"scripts","inferno-doc-gate.mjs"),B=s.join(o,"scripts","inferno-doc-gate.mjs");D(X,B,n,u);const Z=s.join(E,"scripts","inferno-install-hooks.mjs"),Q=s.join(o,"scripts","inferno-install-hooks.mjs");D(Z,Q,n,u);const ee=s.join(E,"ci","github-inferno-check.yml"),oe=s.join(V,"infernoflow-check.yml");if(D(ee,oe,n,u),Se(o,u),j&&ue({cwd:o,templatesRoot:E,force:n,silent:u,logOk:a=>{u||g(a)},logWarn:a=>{u||$(a)}}),O&&we({cwd:o,templatesRoot:E,force:n,silent:u,logOk:a=>{u||g(a)},logWarn:a=>{u||$(a)}}),r){const a=s.join(v,"context-state.json");let c={};try{c=JSON.parse(i.readFileSync(a,"utf8"))}catch{}const m=H(o,{language:U||void 0,framework:W||void 0,projectType:K||void 0});c.stack=m.developmentProfile,i.writeFileSync(a,JSON.stringify(c,null,2)+`
51
+ `,"utf8"),u||g("Created: "+d("inferno/context-state.json"))}if(!u){q("infernoflow initialized!");const a=s.join(v,"integrations.json");!(process.env.ANTHROPIC_API_KEY||process.env.OPENAI_API_KEY||process.env.GOOGLE_AI_API_KEY||process.env.OPENROUTER_API_KEY||process.env.GEMINI_API_KEY)&&!i.existsSync(a)&&(console.log(),console.log(` ${R("\u{1F4A1}")} ${J("Tip:")} connect an AI provider for explain, why, review, and changelog AI.`),console.log(` ${d("infernoflow ai setup")} \u2014 takes 60 seconds`)),ce([d("infernoflow status")+" \u2014 see your contract at a glance",d("infernoflow check")+" \u2014 validate everything",(r?"Review inferred baseline in ":"Edit ")+R("inferno/capabilities.json")+(r?" and refine IDs/titles":" to describe each capability in detail"),"Add more "+R("inferno/scenarios/*.json")+" files for edge cases","Add "+d("inferno:check")+" to your CI pipeline",...j?["Restart Cursor \u2014 hooks write assistant text to "+R("inferno/CONTEXT.draft.md"),"Promote when ready: "+d("npm run inferno:promote-draft -- --append-notes")]:[],...O?["Restart VS Code \u2014 Copilot hooks append prompts + assistant (from transcript) to "+R("inferno/CONTEXT.draft.md"),"Promote when ready: "+d("npm run inferno:promote-draft -- --append-notes")]:[],...!j&&!O?["Optional: "+d("infernoflow install-cursor-hooks")+" or "+d("infernoflow install-vscode-copilot-hooks")]:[]])}}export{Ge as initCommand};
@@ -1,18 +1,20 @@
1
- import*as s from"node:fs";import*as u from"node:path";import"node:os";import{bold as N,cyan as v,gray as o,green as j,red as I}from"../ui/output.mjs";import{readCredentials as _,isLoggedIn as k}from"../cloud/credentials.mjs";import{pushEntry as R}from"../cloud/supabase.mjs";const O="inferno",h=u.join(O,"sessions.jsonl");function A(){try{const e=s.existsSync(h)?s.readFileSync(h,"utf8").split(`
2
- `).filter(Boolean).map(t=>{try{return JSON.parse(t)}catch{return null}}).filter(Boolean):[],c=e.filter(t=>t.type==="gotcha"),r=e.filter(t=>t.type==="decision"),l=e.filter(t=>t.type==="attempt"&&(t.result==="failed"||t.result==="partial")),i=["# Project Context (auto-generated by infernoflow)","",`> Last updated: ${new Date().toISOString()}`,""];if(c.length){i.push("## \u26A0\uFE0F Known Gotchas (Read These First)","");for(const t of c)i.push(`- ${t.summary}`);i.push("")}if(r.length){i.push("## \u2713 Decisions In Effect","");for(const t of r)i.push(`- ${t.summary}`);i.push("")}if(l.length){i.push("## \u274C Things That Don't Work (Don't Try These)","");for(const t of l)i.push(`- ${t.summary}`);i.push("")}const d=i.join(`
3
- `),a=process.cwd(),f=u.join(a,"CLAUDE.md");s.existsSync(f)&&s.writeFileSync(f,d,"utf8");const m=u.join(a,".cursorrules");(s.existsSync(m)||s.existsSync(u.join(a,".cursor")))&&s.writeFileSync(m,d,"utf8");const $=u.join(a,".github","copilot-instructions.md");s.existsSync(u.join(a,".github"))&&s.writeFileSync($,d,"utf8")}catch{}}const F=["note","attempt","decision","gotcha","preference","theme","handoff","error"],b=["worked","failed","partial","unknown"];function C(){return s.existsSync(h)?s.readFileSync(h,"utf8").split(`
4
- `).filter(Boolean).map(e=>{try{return JSON.parse(e)}catch{return null}}).filter(Boolean):[]}function U(e,{auto:c=!1,quiet:r=!1}={}){if(!s.existsSync(O)){if(c)return!1;r||console.error(I(` \u2718 inferno/ not found \u2014 run: infernoflow init
5
- `)),process.exit(1)}return s.appendFileSync(h,JSON.stringify(e)+`
6
- `,"utf8"),!0}function B(){return process.env.CURSOR_SESSION?"cursor":process.env.COPILOT_SESSION?"copilot":process.env.CLAUDE_CODE_SESSION?"claude":process.env.WINDSURF_SESSION?"windsurf":process.env.INFERNOFLOW_AGENT?process.env.INFERNOFLOW_AGENT:"human"}function J(e,c){const r=new Date(e.ts).toLocaleString("en-GB",{day:"2-digit",month:"short",hour:"2-digit",minute:"2-digit"}),l=e.type||"note",i=l==="gotcha"?"\x1B[33m":l==="decision"?"\x1B[36m":l==="theme"?"\x1B[35m":l==="preference"?"\x1B[34m":l==="attempt"?"\x1B[90m":l==="error"?"\x1B[31m":"\x1B[0m",d="\x1B[0m",a=e.result?` [${e.result}]`:"",f=e.agent&&e.agent!=="human"?o(` (${e.agent})`):"";return` ${o(String(c+1).padStart(3))} ${o(r)} ${i}${l}${d}${a} ${e.summary}${f}`}async function K(e){const c=n=>e.includes(n),r=(n,g)=>{const p=e.indexOf(n);return p!==-1&&e[p+1]?e[p+1]:g},l=c("--show"),i=c("--clear"),d=c("--json"),a=c("--auto"),f=c("--quiet"),m=r("--source",null);if(l||d){const n=C(),g=e[e.indexOf("--show")+1],p=g&&/^\d+$/.test(g)?parseInt(g):20,S=n.slice(-p);if(d){console.log(JSON.stringify(S,null,2));return}if(console.log(`
7
- `+N("\u{1F525} infernoflow \u2014 session memory")),console.log(" "+"\u2500".repeat(50)),!S.length){console.log(o(`
1
+ import*as l from"node:fs";import*as p from"node:path";import"node:os";import{bold as N,cyan as A,gray as t,green as D,red as b}from"../ui/output.mjs";import{readCredentials as R,isLoggedIn as _}from"../cloud/credentials.mjs";import{pushEntry as C}from"../cloud/supabase.mjs";import{ampPaths as P,appendEntry as U,readEntries as F,AMP_MARKERS as E}from"../amp/io.mjs";function L(){return P(process.cwd())}function W(){try{const e=F(process.cwd()),a=e.filter(n=>n.type==="gotcha"),c=e.filter(n=>n.type==="decision"),r=e.filter(n=>n.type==="attempt"&&(n.result==="failed"||n.result==="partial")),s=["# Project Context (auto-generated by infernoflow)","",`> Last updated: ${new Date().toISOString()}`,""];if(a.length){s.push("## \u26A0\uFE0F Known Gotchas (Read These First)","");for(const n of a)s.push(`- ${n.summary}`);s.push("")}if(c.length){s.push("## \u2713 Decisions In Effect","");for(const n of c)s.push(`- ${n.summary}`);s.push("")}if(r.length){s.push("## \u274C Things That Don't Work (Don't Try These)","");for(const n of r)s.push(`- ${n.summary}`);s.push("")}const d=s.join(`
2
+ `),m=`${E.start}
3
+ ${d}
4
+ ${E.end}
5
+ `,u=process.cwd(),h=n=>{let i="";try{i=l.readFileSync(n,"utf8")}catch{}const $=i.indexOf(E.start),w=i.indexOf(E.end);let x;$!==-1&&w!==-1&&w>$?x=i.slice(0,$)+m+i.slice(w+E.end.length).replace(/^\n+/,""):i?x=i.replace(/\s+$/,"")+`
6
+
7
+ `+m:x=m,l.writeFileSync(n,x,"utf8")},I=p.join(u,"CLAUDE.md");l.existsSync(I)&&h(I);const j=p.join(u,".cursorrules");(l.existsSync(j)||l.existsSync(p.join(u,".cursor")))&&h(j);const S=p.join(u,".github","copilot-instructions.md");l.existsSync(p.join(u,".github"))&&h(S)}catch{}}const T=["note","attempt","decision","gotcha","preference","theme","handoff","error"],v=["worked","failed","partial","unknown"];function V(){return F(process.cwd())}function q(e,{auto:a=!1,quiet:c=!1}={}){const r=process.cwd(),s=p.join(r,".ai-memory"),d=p.join(r,"inferno");return a&&!l.existsSync(s)&&!l.existsSync(d)?!1:(!l.existsSync(s)&&!l.existsSync(d)&&(c||console.error(b(` \u2718 no .ai-memory/ or inferno/ \u2014 run: infernoflow init
8
+ `)),process.exit(1)),U(r,e),!0)}function G(){return process.env.CURSOR_SESSION?"cursor":process.env.COPILOT_SESSION?"copilot":process.env.CLAUDE_CODE_SESSION?"claude":process.env.WINDSURF_SESSION?"windsurf":process.env.INFERNOFLOW_AGENT?process.env.INFERNOFLOW_AGENT:"human"}function M(e,a){const c=new Date(e.ts).toLocaleString("en-GB",{day:"2-digit",month:"short",hour:"2-digit",minute:"2-digit"}),r=e.type||"note",s=r==="gotcha"?"\x1B[33m":r==="decision"?"\x1B[36m":r==="theme"?"\x1B[35m":r==="preference"?"\x1B[34m":r==="attempt"?"\x1B[90m":r==="error"?"\x1B[31m":"\x1B[0m",d="\x1B[0m",m=e.result?` [${e.result}]`:"",u=e.agent&&e.agent!=="human"?t(` (${e.agent})`):"";return` ${t(String(a+1).padStart(3))} ${t(c)} ${s}${r}${d}${m} ${e.summary}${u}`}async function H(e){const a=o=>e.includes(o),c=(o,f)=>{const g=e.indexOf(o);return g!==-1&&e[g+1]?e[g+1]:f},r=a("--show"),s=a("--clear"),d=a("--json"),m=a("--auto"),u=a("--quiet"),h=c("--source",null);if(r||d){const o=V(),f=e[e.indexOf("--show")+1],g=f&&/^\d+$/.test(f)?parseInt(f):20,y=o.slice(-g);if(d){console.log(JSON.stringify(y,null,2));return}if(console.log(`
9
+ `+N("\u{1F525} infernoflow \u2014 session memory")),console.log(" "+"\u2500".repeat(50)),!y.length){console.log(t(`
8
10
  No entries yet. Start logging with: infernoflow log "<what happened>"
9
- `));return}console.log(o(` Showing last ${S.length} of ${n.length} entries
10
- `)),S.forEach((D,T)=>console.log(J(D,n.length-S.length+T))),console.log();return}if(i){if(!s.existsSync(h)){console.log(o(` Nothing to clear.
11
- `));return}const n=h.replace(".jsonl",`-archive-${Date.now()}.jsonl`);s.renameSync(h,n),console.log(j(` \u2714 Session log archived \u2192 ${u.basename(n)}
12
- `));return}const $=new Set([r("--type",""),r("--result",""),r("--agent",""),r("--source","")].filter(Boolean)),x=e.slice(1).filter(n=>!n.startsWith("--")&&!$.has(n)).join(" ").trim();if(!x){console.log(`
11
+ `));return}console.log(t(` Showing last ${y.length} of ${o.length} entries
12
+ `)),y.forEach((O,k)=>console.log(M(O,o.length-y.length+k))),console.log();return}if(s){const{sessions:o}=L();if(!l.existsSync(o)){console.log(t(` Nothing to clear.
13
+ `));return}const f=o.replace(".jsonl",`-archive-${Date.now()}.jsonl`);l.renameSync(o,f),console.log(D(` \u2714 Session log archived \u2192 ${p.basename(f)}
14
+ `));return}const I=new Set([c("--type",""),c("--result",""),c("--agent",""),c("--source","")].filter(Boolean)),S=e.slice(1).filter(o=>!o.startsWith("--")&&!I.has(o)).join(" ").trim();if(!S){console.log(`
13
15
  `+N("\u{1F525} infernoflow log")+` \u2014 append to session memory
14
- `),console.log(o(" Usage:")),console.log(o(' infernoflow log "what happened"')),console.log(o(' infernoflow log "tried X, failed because Y" --type attempt --result failed')),console.log(o(' infernoflow log "always use multipart/form-data" --type gotcha')),console.log(o(' infernoflow log "switched to dark mode" --type theme')),console.log(o(" infernoflow log --show Print last 20 entries")),console.log(o(" infernoflow log --json Print as JSON")),console.log(),console.log(o(" Types: note \xB7 attempt \xB7 decision \xB7 gotcha \xB7 preference \xB7 theme \xB7 handoff \xB7 error")),console.log(o(" Results: worked \xB7 failed \xB7 partial \xB7 unknown")),console.log(o(` Auto-capture: --auto (silent skip if no inferno/) \xB7 --quiet \xB7 --source <name>
15
- `));return}const w=r("--type","note"),y=r("--result",null),L=r("--agent",B());F.includes(w)||(f||console.error(I(` \u2718 Invalid type: ${w}. Valid: ${F.join(", ")}
16
- `)),process.exit(1)),y&&!b.includes(y)&&(f||console.error(I(` \u2718 Invalid result: ${y}. Valid: ${b.join(", ")}
17
- `)),process.exit(1));const E={ts:new Date().toISOString(),agent:L,type:w,summary:x,...y?{result:y}:{},...m?{source:m}:{},...a?{auto:!0}:{}};if(U(E,{auto:a,quiet:f})){A();try{if(k()){const n=_(),g=n.user_token||n.user?.id||n.user?.login||"anonymous",p=(()=>{try{return JSON.parse(s.readFileSync(u.join(O,"config.json"),"utf8")).projectId||u.basename(process.cwd())}catch{return u.basename(process.cwd())}})();await R(E,g,p)}}catch{}if(!f){const n=w!=="note"?v(` [${w}]`):"",g=y?o(` \u2192 ${y}`):"",p=m?o(` (via ${m})`):"";console.log(j(` \u2714 Logged${n}${g}${p}: `)+x+`
18
- `)}}}export{K as logCommand};
16
+ `),console.log(t(" Usage:")),console.log(t(' infernoflow log "what happened"')),console.log(t(' infernoflow log "tried X, failed because Y" --type attempt --result failed')),console.log(t(' infernoflow log "always use multipart/form-data" --type gotcha')),console.log(t(' infernoflow log "switched to dark mode" --type theme')),console.log(t(" infernoflow log --show Print last 20 entries")),console.log(t(" infernoflow log --json Print as JSON")),console.log(),console.log(t(" Types: note \xB7 attempt \xB7 decision \xB7 gotcha \xB7 preference \xB7 theme \xB7 handoff \xB7 error")),console.log(t(" Results: worked \xB7 failed \xB7 partial \xB7 unknown")),console.log(t(` Auto-capture: --auto (silent skip if no inferno/) \xB7 --quiet \xB7 --source <name>
17
+ `));return}const n=c("--type","note"),i=c("--result",null),$=c("--agent",G());T.includes(n)||(u||console.error(b(` \u2718 Invalid type: ${n}. Valid: ${T.join(", ")}
18
+ `)),process.exit(1)),i&&!v.includes(i)&&(u||console.error(b(` \u2718 Invalid result: ${i}. Valid: ${v.join(", ")}
19
+ `)),process.exit(1));const w={ts:new Date().toISOString(),agent:$,type:n,summary:S,...i?{result:i}:{},...h?{source:h}:{},...m?{auto:!0}:{}};if(q(w,{auto:m,quiet:u})){W();try{if(_()){const o=R(),f=o.user_token||o.user?.id||o.user?.login||"anonymous",g=(()=>{try{const{config:y}=L(),O=JSON.parse(l.readFileSync(y,"utf8"));return O.project||O.projectId||p.basename(process.cwd())}catch{return p.basename(process.cwd())}})();await C(w,f,g)}}catch{}if(!u){const o=n!=="note"?A(` [${n}]`):"",f=i?t(` \u2192 ${i}`):"",g=h?t(` (via ${h})`):"";console.log(D(` \u2714 Logged${o}${f}${g}: `)+S+`
20
+ `)}}}export{H as logCommand};
@@ -1,6 +1,5 @@
1
- import*as O from"node:fs";import*as b from"node:path";import{execSync as M}from"node:child_process";import{bold as I,cyan as u,gray as n,green as S,yellow as k,red as C}from"../ui/output.mjs";const j="inferno",v=b.join(j,"sessions.jsonl"),B=b.join(j,"contract.json");function P(e){try{return JSON.parse(O.readFileSync(e,"utf8"))}catch{return null}}function R(e,s){if(s){const o=s.match(/^(\d+)h$/i),t=s.match(/^(\d+)d$/i);if(o)return new Date(Date.now()-parseInt(o[1])*36e5);if(t)return new Date(Date.now()-parseInt(t[1])*864e5);const c=new Date(s);if(!isNaN(c))return c}for(let o=e.length-1;o>=0;o--)if(e[o].type==="handoff"){const t=new Date(e[o].ts||0),c=new Date(Date.now()-864e5);return t>c?t:c}return new Date(Date.now()-864e5)}function A(e){const s=process.cwd(),o=m=>{try{return M(m,{cwd:s,encoding:"utf8",timeout:5e3,stdio:["pipe","pipe","pipe"]}).trim()}catch{return""}},t=e.toISOString().slice(0,19),c=o("git diff --cached --name-only"),w=o("git diff --name-only"),a=o(`git log --since="${t}" --name-only --pretty=format:""`);return[...new Set([...c.split(`
2
- `),...w.split(`
3
- `),...a.split(`
4
- `)].map(m=>m.trim()).filter(Boolean))]}function _(e,s){const o=[{keywords:["auth","login","logout","session","jwt","token","password"],topic:"authentication"},{keywords:["stripe","payment","checkout","billing","subscription"],topic:"payments"},{keywords:["upload","file","s3","storage","bucket","cdn"],topic:"file handling"},{keywords:["email","sendgrid","ses","smtp","nodemailer","twilio"],topic:"notifications"},{keywords:["db","database","prisma","mongoose","postgres","mysql","migration"],topic:"database"},{keywords:["deploy","docker","ci","workflow","action","kubernetes"],topic:"deployment"},{keywords:["cache","redis","memcache"],topic:"caching"},{keywords:["test","spec","jest","vitest","cypress","playwright"],topic:"testing"},{keywords:["config","env",".env","environment","secret"],topic:"configuration"},{keywords:["api","route","endpoint","controller","handler"],topic:"API routes"},{keywords:["ui","component","style","css","tailwind","theme"],topic:"UI/styles"}],t=s.map(a=>(a.summary||"").toLowerCase()).join(" "),c=[],w=new Set;for(const a of o){if(w.has(a.topic))continue;const y=e.filter(p=>a.keywords.some(d=>p.toLowerCase().includes(d)));!y.length||a.keywords.some(p=>t.includes(p))||(w.add(a.topic),c.push({topic:a.topic,files:y.slice(0,3),suggestedType:"gotcha"}))}return c}function J(e){const s=new Set(e.map(c=>c.type));let o=0;const t=[];return e.length>0?(o+=20,t.push({ok:!0,label:`${e.length} entr${e.length!==1?"ies":"y"} logged`})):t.push({ok:!1,label:"nothing logged this session"}),s.has("gotcha")?(o+=35,t.push({ok:!0,label:"gotchas captured"})):t.push({ok:!1,label:"no gotchas (most valuable \u2014 log landmines!)"}),s.has("decision")?(o+=25,t.push({ok:!0,label:"decisions recorded"})):t.push({ok:!1,label:"no decisions recorded"}),s.has("attempt")&&(o+=10,t.push({ok:!0,label:"attempts tracked"})),s.has("preference")&&(o+=10,t.push({ok:!0,label:"preferences noted"})),{score:Math.min(o,100),checks:t}}function U(e){if(!e)return"";const s=new Date(e),o=Date.now()-s.getTime(),t=Math.floor(o/6e4);if(t<60)return`${t}m ago`;const c=Math.floor(o/36e5);return c<24?`${c}h ago`:`${Math.floor(o/864e5)}d ago`}const G={gotcha:"\u26A0",decision:"\u2713",attempt:"\u21BA",preference:"\u2666",theme:"\u25C8",note:"\xB7",error:"\u2717",handoff:"\u2192"},H={gotcha:k,decision:S,attempt:u,preference:u,theme:u,note:n,error:C,handoff:n};function Y(e){const s=H[e.type]||n,o=G[e.type]||"\xB7",t=e.result?n(` [${e.result}]`):"",c=n(` (${U(e.ts)})`);console.log(` ${s(o+" "+(e.type||"note").padEnd(11))}${t}${c}`),console.log(` ${e.summary}`)}async function K(e=[]){const s=e,o=s.includes("--json"),t=s.includes("--brief"),c=s.indexOf("--since"),w=c!==-1?s[c+1]:null,a=process.cwd();O.existsSync(b.join(a,j))||(o||console.error(C(` \u2718 inferno/ not found \u2014 run: infernoflow init
5
- `)),process.exit(1));const y=[],m=b.join(a,v);O.existsSync(m)&&O.readFileSync(m,"utf8").split(`
6
- `).filter(Boolean).forEach(r=>{try{y.push(JSON.parse(r))}catch{}});const p=R(y,w),d=y.filter(r=>new Date(r.ts||0)>p),D=A(p),h=_(D,d),{score:i,checks:E}=J(d),N=P(b.join(a,B));if(o){console.log(JSON.stringify({sessionStart:p.toISOString(),entries:d,changedFiles:D,unloggedTopics:h,health:{score:i,checks:E}},null,2));return}if(t){const r=i>=80?"A":i>=60?"B":i>=40?"C":"D",g=i>=60?S:i>=40?k:C;console.log(g(`Session health: ${r} (${i}/100)`)+n(` \u2014 ${d.length} entries logged`)),h.length&&console.log(k(` ${h.length} topic${h.length!==1?"s":""} changed but not logged: `)+h.map(l=>l.topic).join(", "));return}const $=n(" "+"\u2500".repeat(52));console.log(),console.log(" "+I("\u{1F525} infernoflow recap")),N?.policyId&&console.log(n(` Project: ${N.policyId}`));const F=p.toLocaleString("en-GB",{day:"2-digit",month:"short",hour:"2-digit",minute:"2-digit"});if(console.log(n(` Session since: ${F}`)),console.log($),console.log(),console.log(" "+I("Captured this session")),console.log(),d.length===0)console.log(n(" Nothing logged yet this session."));else{const r=["gotcha","decision","attempt","preference","theme","note","error"],g=new Map;for(const l of d){const f=l.type||"note";g.has(f)||g.set(f,[]),g.get(f).push(l)}for(const l of r){const f=g.get(l);if(f?.length)for(const L of f)console.log(),Y(L)}}if(h.length>0){console.log(),console.log($),console.log(),console.log(" "+I("Changed but not logged")+n(" (git diff since session start)")),console.log();for(const{topic:r,files:g}of h){console.log(k(` ? ${r}`));for(const l of g)console.log(n(` ${l}`))}console.log(),console.log(n(" Any gotchas or decisions from these areas worth capturing?")),console.log(n(" Run: ")+u('infernoflow log "<what happened>" --type gotcha'))}else D.length>0&&(console.log(),console.log($),console.log(),console.log(S(" \u2714 ")+n(`${D.length} changed files \u2014 all topics appear to be logged`)));console.log(),console.log($),console.log(),console.log(" "+I("Session health")),console.log();const T=i>=80?"A":i>=60?"B":i>=40?"C":"D",x=i>=60?S:i>=40?k:C;console.log(` ${x(I(`${T}`))} ${x(`${i}/100`)}`),console.log();for(const{ok:r,label:g}of E){const l=r?S(" \u2714"):k(" \xB7");console.log(`${l} ${r?g:n(g)}`)}{const r=d.filter(f=>f.type==="gotcha").length,g=d.filter(f=>f.type==="decision").length,l=[];if(r===0?l.push(u('infernoflow log "..." --type gotcha')+n(" \u2014 adds 35 pts")):r<3&&i<80&&l.push(n(` ${3-r} more gotcha(s) would push you higher`)),g===0&&l.push(u('infernoflow log "..." --type decision')+n(" \u2014 adds 25 pts")),i>=60&&i<80&&l.push(n(" Almost B! One more entry gets you there.")),i>=80&&l.push(S(" Great session \u2014 your handoff will be excellent.")),l.length){console.log(),console.log(n(" How to improve:"));for(const f of l)console.log(" "+f)}}(d.length>0||h.length>0)&&(console.log(),console.log($),console.log(),console.log(n(" Before your next session:")),console.log(n(" ")+u("infernoflow switch")+n(" \u2014 generate a handoff summary for the next AI agent")),console.log(n(" ")+u("infernoflow ask --recent")+n(" \u2014 review what's in memory before starting"))),console.log()}export{K as recapCommand};
1
+ import*as C from"node:fs";import*as D from"node:path";import{execSync as M}from"node:child_process";import{bold as b,cyan as u,gray as n,green as k,yellow as $,red as I}from"../ui/output.mjs";import{ampPaths as L,readEntries as P}from"../amp/io.mjs";const E="inferno";function Q(){return L(process.cwd()).sessions}const R=D.join(E,"contract.json");function v(e){try{return JSON.parse(C.readFileSync(e,"utf8"))}catch{return null}}function A(e,s){if(s){const o=s.match(/^(\d+)h$/i),t=s.match(/^(\d+)d$/i);if(o)return new Date(Date.now()-parseInt(o[1])*36e5);if(t)return new Date(Date.now()-parseInt(t[1])*864e5);const c=new Date(s);if(!isNaN(c))return c}for(let o=e.length-1;o>=0;o--)if(e[o].type==="handoff"){const t=new Date(e[o].ts||0),c=new Date(Date.now()-864e5);return t>c?t:c}return new Date(Date.now()-864e5)}function B(e){const s=process.cwd(),o=p=>{try{return M(p,{cwd:s,encoding:"utf8",timeout:5e3,stdio:["pipe","pipe","pipe"]}).trim()}catch{return""}},t=e.toISOString().slice(0,19),c=o("git diff --cached --name-only"),m=o("git diff --name-only"),r=o(`git log --since="${t}" --name-only --pretty=format:""`);return[...new Set([...c.split(`
2
+ `),...m.split(`
3
+ `),...r.split(`
4
+ `)].map(p=>p.trim()).filter(Boolean))]}function _(e,s){const o=[{keywords:["auth","login","logout","session","jwt","token","password"],topic:"authentication"},{keywords:["stripe","payment","checkout","billing","subscription"],topic:"payments"},{keywords:["upload","file","s3","storage","bucket","cdn"],topic:"file handling"},{keywords:["email","sendgrid","ses","smtp","nodemailer","twilio"],topic:"notifications"},{keywords:["db","database","prisma","mongoose","postgres","mysql","migration"],topic:"database"},{keywords:["deploy","docker","ci","workflow","action","kubernetes"],topic:"deployment"},{keywords:["cache","redis","memcache"],topic:"caching"},{keywords:["test","spec","jest","vitest","cypress","playwright"],topic:"testing"},{keywords:["config","env",".env","environment","secret"],topic:"configuration"},{keywords:["api","route","endpoint","controller","handler"],topic:"API routes"},{keywords:["ui","component","style","css","tailwind","theme"],topic:"UI/styles"}],t=s.map(r=>(r.summary||"").toLowerCase()).join(" "),c=[],m=new Set;for(const r of o){if(m.has(r.topic))continue;const y=e.filter(g=>r.keywords.some(w=>g.toLowerCase().includes(w)));!y.length||r.keywords.some(g=>t.includes(g))||(m.add(r.topic),c.push({topic:r.topic,files:y.slice(0,3),suggestedType:"gotcha"}))}return c}function J(e){const s=new Set(e.map(c=>c.type));let o=0;const t=[];return e.length>0?(o+=20,t.push({ok:!0,label:`${e.length} entr${e.length!==1?"ies":"y"} logged`})):t.push({ok:!1,label:"nothing logged this session"}),s.has("gotcha")?(o+=35,t.push({ok:!0,label:"gotchas captured"})):t.push({ok:!1,label:"no gotchas (most valuable \u2014 log landmines!)"}),s.has("decision")?(o+=25,t.push({ok:!0,label:"decisions recorded"})):t.push({ok:!1,label:"no decisions recorded"}),s.has("attempt")&&(o+=10,t.push({ok:!0,label:"attempts tracked"})),s.has("preference")&&(o+=10,t.push({ok:!0,label:"preferences noted"})),{score:Math.min(o,100),checks:t}}function U(e){if(!e)return"";const s=new Date(e),o=Date.now()-s.getTime(),t=Math.floor(o/6e4);if(t<60)return`${t}m ago`;const c=Math.floor(o/36e5);return c<24?`${c}h ago`:`${Math.floor(o/864e5)}d ago`}const G={gotcha:"\u26A0",decision:"\u2713",attempt:"\u21BA",preference:"\u2666",theme:"\u25C8",note:"\xB7",error:"\u2717",handoff:"\u2192"},H={gotcha:$,decision:k,attempt:u,preference:u,theme:u,note:n,error:I,handoff:n};function Y(e){const s=H[e.type]||n,o=G[e.type]||"\xB7",t=e.result?n(` [${e.result}]`):"",c=n(` (${U(e.ts)})`);console.log(` ${s(o+" "+(e.type||"note").padEnd(11))}${t}${c}`),console.log(` ${e.summary}`)}async function V(e=[]){const s=e,o=s.includes("--json"),t=s.includes("--brief"),c=s.indexOf("--since"),m=c!==-1?s[c+1]:null,r=process.cwd();!C.existsSync(D.join(r,E))&&!C.existsSync(D.join(r,".ai-memory"))&&(o||console.error(I(` \u2718 not initialized \u2014 run: infernoflow init
5
+ `)),process.exit(1));const y=P(r),p=A(y,m),g=y.filter(a=>new Date(a.ts||0)>p),w=B(p),h=_(w,g),{score:l,checks:O}=J(g),j=v(D.join(r,R));if(o){console.log(JSON.stringify({sessionStart:p.toISOString(),entries:g,changedFiles:w,unloggedTopics:h,health:{score:l,checks:O}},null,2));return}if(t){const a=l>=80?"A":l>=60?"B":l>=40?"C":"D",f=l>=60?k:l>=40?$:I;console.log(f(`Session health: ${a} (${l}/100)`)+n(` \u2014 ${g.length} entries logged`)),h.length&&console.log($(` ${h.length} topic${h.length!==1?"s":""} changed but not logged: `)+h.map(i=>i.topic).join(", "));return}const S=n(" "+"\u2500".repeat(52));console.log(),console.log(" "+b("\u{1F525} infernoflow recap")),j?.policyId&&console.log(n(` Project: ${j.policyId}`));const T=p.toLocaleString("en-GB",{day:"2-digit",month:"short",hour:"2-digit",minute:"2-digit"});if(console.log(n(` Session since: ${T}`)),console.log(S),console.log(),console.log(" "+b("Captured this session")),console.log(),g.length===0)console.log(n(" Nothing logged yet this session."));else{const a=["gotcha","decision","attempt","preference","theme","note","error"],f=new Map;for(const i of g){const d=i.type||"note";f.has(d)||f.set(d,[]),f.get(d).push(i)}for(const i of a){const d=f.get(i);if(d?.length)for(const F of d)console.log(),Y(F)}}if(h.length>0){console.log(),console.log(S),console.log(),console.log(" "+b("Changed but not logged")+n(" (git diff since session start)")),console.log();for(const{topic:a,files:f}of h){console.log($(` ? ${a}`));for(const i of f)console.log(n(` ${i}`))}console.log(),console.log(n(" Any gotchas or decisions from these areas worth capturing?")),console.log(n(" Run: ")+u('infernoflow log "<what happened>" --type gotcha'))}else w.length>0&&(console.log(),console.log(S),console.log(),console.log(k(" \u2714 ")+n(`${w.length} changed files \u2014 all topics appear to be logged`)));console.log(),console.log(S),console.log(),console.log(" "+b("Session health")),console.log();const N=l>=80?"A":l>=60?"B":l>=40?"C":"D",x=l>=60?k:l>=40?$:I;console.log(` ${x(b(`${N}`))} ${x(`${l}/100`)}`),console.log();for(const{ok:a,label:f}of O){const i=a?k(" \u2714"):$(" \xB7");console.log(`${i} ${a?f:n(f)}`)}{const a=g.filter(d=>d.type==="gotcha").length,f=g.filter(d=>d.type==="decision").length,i=[];if(a===0?i.push(u('infernoflow log "..." --type gotcha')+n(" \u2014 adds 35 pts")):a<3&&l<80&&i.push(n(` ${3-a} more gotcha(s) would push you higher`)),f===0&&i.push(u('infernoflow log "..." --type decision')+n(" \u2014 adds 25 pts")),l>=60&&l<80&&i.push(n(" Almost B! One more entry gets you there.")),l>=80&&i.push(k(" Great session \u2014 your handoff will be excellent.")),i.length){console.log(),console.log(n(" How to improve:"));for(const d of i)console.log(" "+d)}}(g.length>0||h.length>0)&&(console.log(),console.log(S),console.log(),console.log(n(" Before your next session:")),console.log(n(" ")+u("infernoflow switch")+n(" \u2014 generate a handoff summary for the next AI agent")),console.log(n(" ")+u("infernoflow ask --recent")+n(" \u2014 review what's in memory before starting"))),console.log()}export{V as recapCommand};
@@ -1,5 +1,4 @@
1
- import*as y from"node:fs";import*as f from"node:path";import{bold as g,cyan as w,gray as t,green as v,yellow as E,red as j}from"../ui/output.mjs";const m="inferno",I=f.join(m,"sessions.jsonl"),N=f.join(m,"CONTEXT.md"),O=f.join(m,"theme.json"),A=f.join(m,"scan.json"),C=f.join(m,"contract.json"),M=f.join(m,"capabilities.json");function k(o){try{return JSON.parse(y.readFileSync(o,"utf8"))}catch{return null}}function F(o){return Math.ceil((o||"").length/4)}const $={gotcha:400,decision:200,attempt:250,preference:150,note:100,theme:300,handoff:500,error:200};function L(o){const e={ok:!1,memory:{total:0,byType:{},oldestEntry:null,newestEntry:null,sessionsTracked:0},context:{sizeBytes:0,estimatedTokens:0,hasIntent:!1,hasWorking:!1},theme:{captured:!1,fonts:0,colors:0,cssVars:0,framework:null},coverage:{total:0,withAnalysis:0,pct:0},chains:{total:0,resolved:0},contract:{policyId:null,capabilities:0,isLite:!1},savings:{estimatedTokens:0,breakdown:{}}},a=k(f.join(o,C));a&&(e.contract.policyId=a.policyId,e.contract.capabilities=(a.capabilities||[]).length,e.contract.isLite=!!a.lite,e.ok=!0);const c=k(f.join(o,M));if(c){const s=Array.isArray(c)?c:c.capabilities||[];e.coverage.total=s.length,e.coverage.withAnalysis=s.filter(i=>i.codeAnalysis).length,e.coverage.pct=e.coverage.total?Math.round(e.coverage.withAnalysis/e.coverage.total*100):0}const n=f.join(o,I);if(y.existsSync(n)){const i=y.readFileSync(n,"utf8").split(`
2
- `).filter(Boolean).map(u=>{try{return JSON.parse(u)}catch{return null}}).filter(Boolean);e.memory.total=i.length;for(const u of i){const p=u.type||"note";e.memory.byType[p]=(e.memory.byType[p]||0)+1;const b=$[p]||100;e.savings.estimatedTokens+=b,e.savings.breakdown[p]=(e.savings.breakdown[p]||0)+b}i.length&&(e.memory.oldestEntry=i[0].ts,e.memory.newestEntry=i[i.length-1].ts);const x=new Set(i.map(u=>(u.ts||"").slice(0,10)));e.memory.sessionsTracked=x.size}const l=f.join(o,N);if(y.existsSync(l)){const s=y.readFileSync(l,"utf8");e.context.sizeBytes=Buffer.byteLength(s,"utf8"),e.context.estimatedTokens=F(s),e.context.hasIntent=s.includes("## Intent"),e.context.hasWorking=s.includes("## Working on")}const r=k(f.join(o,O));r&&(e.theme.captured=!0,e.theme.fonts=Object.keys(r.fonts||{}).filter(s=>r.fonts[s]).length,e.theme.colors=Object.keys(r.colors?.palette||{}).length,e.theme.cssVars=Object.keys(r.cssVars||{}).length,e.theme.framework=r.framework||null);const h=k(f.join(o,A));if(h?.httpChains){const s=Object.values(h.httpChains).flat();e.chains.total=s.length,e.chains.resolved=s.filter(i=>i.resolved).length}return e}function T(o,e,a=20){const c=e>0?Math.round(o/e*a):0;return"\u2588".repeat(c)+"\u2591".repeat(a-c)}function S(o){return o>=80?v:o>=40?E:j}function B(o){if(!o)return"never";const e=new Date(o),c=Date.now()-e.getTime(),n=Math.floor(c/864e5);return n===0?"today":n===1?"yesterday":n<7?`${n}d ago`:n<30?`${Math.floor(n/7)}w ago`:e.toLocaleDateString("en-GB",{day:"2-digit",month:"short"})}function d(o){return o>=1e3?`~${Math.round(o/100)/10}k`:`~${o}`}function P(o){const e=t(" "+"\u2500".repeat(52));console.log(),console.log(" "+g("\u{1F525} infernoflow stats")),o.contract.policyId&&console.log(t(` Project: ${o.contract.policyId}${o.contract.isLite?" (lite)":""}`)),console.log(e),console.log(),console.log(" "+g("Session memory")+t(" (inferno/sessions.jsonl)")),console.log();const a=o.memory.total;if(a===0)console.log(t(' No entries yet \u2014 run: infernoflow log "<what happened>" --type gotcha'));else{const n=["gotcha","decision","attempt","preference","theme","note","handoff","error"],l=Math.max(...Object.values(o.memory.byType));for(const r of n){const h=o.memory.byType[r]||0;if(h===0)continue;const s=T(h,l,16),i=r.padEnd(12);console.log(` ${t(i)} ${w(s)} ${h}`)}console.log(),console.log(t(" Total entries: ")+g(a)),console.log(t(" Sessions tracked: ")+o.memory.sessionsTracked),o.memory.newestEntry&&console.log(t(" Last entry: ")+B(o.memory.newestEntry))}if(console.log(),console.log(e),console.log(),console.log(" "+g("Context injection")+t(" (per session start)")),console.log(),o.context.sizeBytes===0?console.log(t(" No CONTEXT.md yet \u2014 run: infernoflow context")):(console.log(t(" Size: ")+`${Math.round(o.context.sizeBytes/1024*10)/10} KB`),console.log(t(" Tokens: ")+g(d(o.context.estimatedTokens))+t(" injected into every session")),o.context.hasIntent&&console.log(t(" ")+v("\u2714")+t(" Intent captured")),o.context.hasWorking&&console.log(t(" ")+v("\u2714")+t(" Working state captured"))),console.log(),console.log(e),console.log(),console.log(" "+g("Capability coverage")+t(" (code analysis via infernoflow scan)")),console.log(),o.coverage.total===0)console.log(t(" No capabilities yet \u2014 run: infernoflow init"));else{const n=S(o.coverage.pct),l=T(o.coverage.withAnalysis,o.coverage.total,24);if(console.log(` ${n(l)} ${g(o.coverage.pct+"%")} (${o.coverage.withAnalysis}/${o.coverage.total})`),o.coverage.pct<100){const r=o.coverage.total-o.coverage.withAnalysis;console.log(t(`
3
- ${r} capabilities without code analysis`)),console.log(t(" Run: infernoflow scan to enrich them"))}}if(o.chains.total>0){console.log(),console.log(e),console.log(),console.log(" "+g("HTTP call chains")+t(" (end-to-end resolution)")),console.log();const n=Math.round(o.chains.resolved/o.chains.total*100),l=S(n),r=T(o.chains.resolved,o.chains.total,20);console.log(` ${l(r)} ${g(n+"%")} resolved (${o.chains.resolved}/${o.chains.total} call chains)`),o.chains.resolved<o.chains.total&&console.log(t(`
4
- Unresolved calls may be to external services or missing route files`))}if(console.log(),console.log(e),console.log(),console.log(" "+g("Design system")+t(" (inferno/theme.json)")),console.log(),!o.theme.captured)console.log(t(" Not captured yet \u2014 run: infernoflow theme"));else{const n=[];o.theme.fonts&&n.push(`${o.theme.fonts} font${o.theme.fonts!==1?"s":""}`),o.theme.colors&&n.push(`${o.theme.colors} colors`),o.theme.cssVars&&n.push(`${o.theme.cssVars} CSS vars`),o.theme.framework&&n.push(`${o.theme.framework}`),console.log(t(" ")+v("\u2714")+" "+n.join(" \xB7 ")),console.log(t(" AI agents always use the correct fonts and colors for this project"))}console.log(),console.log(e),console.log(),console.log(" "+g("Estimated token savings")+t(" (vs re-discovering from scratch)")),console.log();const c=o.savings.estimatedTokens;if(c===0)console.log(t(" No session entries yet \u2014 start logging to track savings"));else{const n=Math.max(o.memory.sessionsTracked,1),l=Math.round(c/n);console.log(" Total saved: "+g(v(d(c)+" tokens"))),console.log(" Per session: "+g(d(l)+" tokens")),console.log(),console.log(t(" Breakdown:"));const r=["gotcha","handoff","attempt","decision","theme","preference","note","error"];for(const h of r){const s=o.savings.breakdown[h];if(!s)continue;const i=o.memory.byType[h]||0;console.log(t(` ${h.padEnd(12)} ${i}\xD7 \xD7 ${$[h]||100} = `)+w(d(s)))}console.log(),console.log(t(" * Estimates based on typical back-and-forth cost per entry type.")),console.log(t(" Actual savings vary with model, project complexity, and session length."))}console.log(),console.log(e),console.log()}async function D(o=[]){const e=o.includes("--json"),a=o.includes("--brief"),c=process.cwd();y.existsSync(f.join(c,m))||(console.error(j(` \u2718 inferno/ not found \u2014 run: infernoflow init
5
- `)),process.exit(1));const n=L(c);if(e){console.log(JSON.stringify(n,null,2));return}if(a){const l=[];n.memory.total&&l.push(`${n.memory.total} memory entries`),n.context.estimatedTokens&&l.push(`${d(n.context.estimatedTokens)} tokens/session`),n.coverage.total&&l.push(`${n.coverage.pct}% capability coverage`),n.savings.estimatedTokens&&l.push(`${d(n.savings.estimatedTokens)} tokens saved`),console.log(l.join(" \xB7 ")||"No data yet \u2014 run infernoflow init + infernoflow log");return}P(n)}export{D as statsCommand};
1
+ import*as d from"node:fs";import*as a from"node:path";import{bold as g,cyan as b,gray as t,green as u,yellow as E,red as j}from"../ui/output.mjs";import{ampPaths as I,readEntries as N}from"../amp/io.mjs";const y="inferno";function $(){return I(process.cwd()).sessions}const O=a.join(y,"CONTEXT.md"),A=a.join(y,"theme.json"),C=a.join(y,"scan.json"),M=a.join(y,"contract.json"),P=a.join(y,"capabilities.json");function v(o){try{return JSON.parse(d.readFileSync(o,"utf8"))}catch{return null}}function L(o){return Math.ceil((o||"").length/4)}const x={gotcha:400,decision:200,attempt:250,preference:150,note:100,theme:300,handoff:500,error:200};function F(o){const e={ok:!1,memory:{total:0,byType:{},oldestEntry:null,newestEntry:null,sessionsTracked:0},context:{sizeBytes:0,estimatedTokens:0,hasIntent:!1,hasWorking:!1},theme:{captured:!1,fonts:0,colors:0,cssVars:0,framework:null},coverage:{total:0,withAnalysis:0,pct:0},chains:{total:0,resolved:0},contract:{policyId:null,capabilities:0,isLite:!1},savings:{estimatedTokens:0,breakdown:{}}},i=v(a.join(o,M));i&&(e.contract.policyId=i.policyId,e.contract.capabilities=(i.capabilities||[]).length,e.contract.isLite=!!i.lite,e.ok=!0);const c=v(a.join(o,P));if(c){const s=Array.isArray(c)?c:c.capabilities||[];e.coverage.total=s.length,e.coverage.withAnalysis=s.filter(m=>m.codeAnalysis).length,e.coverage.pct=e.coverage.total?Math.round(e.coverage.withAnalysis/e.coverage.total*100):0}const n=$();if(d.existsSync(n)){const s=N(process.cwd());e.memory.total=s.length;for(const k of s){const h=k.type||"note";e.memory.byType[h]=(e.memory.byType[h]||0)+1;const T=x[h]||100;e.savings.estimatedTokens+=T,e.savings.breakdown[h]=(e.savings.breakdown[h]||0)+T}s.length&&(e.memory.oldestEntry=s[0].ts,e.memory.newestEntry=s[s.length-1].ts);const m=new Set(s.map(k=>{const h=k.ts;return typeof h=="number"?new Date(h).toISOString().slice(0,10):(h||"").slice(0,10)}));e.memory.sessionsTracked=m.size}const l=a.join(o,O);if(d.existsSync(l)){const s=d.readFileSync(l,"utf8");e.context.sizeBytes=Buffer.byteLength(s,"utf8"),e.context.estimatedTokens=L(s),e.context.hasIntent=s.includes("## Intent"),e.context.hasWorking=s.includes("## Working on")}const r=v(a.join(o,A));r&&(e.theme.captured=!0,e.theme.fonts=Object.keys(r.fonts||{}).filter(s=>r.fonts[s]).length,e.theme.colors=Object.keys(r.colors?.palette||{}).length,e.theme.cssVars=Object.keys(r.cssVars||{}).length,e.theme.framework=r.framework||null);const f=v(a.join(o,C));if(f?.httpChains){const s=Object.values(f.httpChains).flat();e.chains.total=s.length,e.chains.resolved=s.filter(m=>m.resolved).length}return e}function w(o,e,i=20){const c=e>0?Math.round(o/e*i):0;return"\u2588".repeat(c)+"\u2591".repeat(i-c)}function S(o){return o>=80?u:o>=40?E:j}function B(o){if(!o)return"never";const e=new Date(o),c=Date.now()-e.getTime(),n=Math.floor(c/864e5);return n===0?"today":n===1?"yesterday":n<7?`${n}d ago`:n<30?`${Math.floor(n/7)}w ago`:e.toLocaleDateString("en-GB",{day:"2-digit",month:"short"})}function p(o){return o>=1e3?`~${Math.round(o/100)/10}k`:`~${o}`}function D(o){const e=t(" "+"\u2500".repeat(52));console.log(),console.log(" "+g("\u{1F525} infernoflow stats")),o.contract.policyId&&console.log(t(` Project: ${o.contract.policyId}${o.contract.isLite?" (lite)":""}`)),console.log(e),console.log(),console.log(" "+g("Session memory")+t(" ("+a.relative(process.cwd(),$())+")")),console.log();const i=o.memory.total;if(i===0)console.log(t(' No entries yet \u2014 run: infernoflow log "<what happened>" --type gotcha'));else{const n=["gotcha","decision","attempt","preference","theme","note","handoff","error"],l=Math.max(...Object.values(o.memory.byType));for(const r of n){const f=o.memory.byType[r]||0;if(f===0)continue;const s=w(f,l,16),m=r.padEnd(12);console.log(` ${t(m)} ${b(s)} ${f}`)}console.log(),console.log(t(" Total entries: ")+g(i)),console.log(t(" Sessions tracked: ")+o.memory.sessionsTracked),o.memory.newestEntry&&console.log(t(" Last entry: ")+B(o.memory.newestEntry))}if(console.log(),console.log(e),console.log(),console.log(" "+g("Context injection")+t(" (per session start)")),console.log(),o.context.sizeBytes===0?console.log(t(" No CONTEXT.md yet \u2014 run: infernoflow context")):(console.log(t(" Size: ")+`${Math.round(o.context.sizeBytes/1024*10)/10} KB`),console.log(t(" Tokens: ")+g(p(o.context.estimatedTokens))+t(" injected into every session")),o.context.hasIntent&&console.log(t(" ")+u("\u2714")+t(" Intent captured")),o.context.hasWorking&&console.log(t(" ")+u("\u2714")+t(" Working state captured"))),console.log(),console.log(e),console.log(),console.log(" "+g("Capability coverage")+t(" (code analysis via infernoflow scan)")),console.log(),o.coverage.total===0)console.log(t(" No capabilities yet \u2014 run: infernoflow init"));else{const n=S(o.coverage.pct),l=w(o.coverage.withAnalysis,o.coverage.total,24);if(console.log(` ${n(l)} ${g(o.coverage.pct+"%")} (${o.coverage.withAnalysis}/${o.coverage.total})`),o.coverage.pct<100){const r=o.coverage.total-o.coverage.withAnalysis;console.log(t(`
2
+ ${r} capabilities without code analysis`)),console.log(t(" Run: infernoflow scan to enrich them"))}}if(o.chains.total>0){console.log(),console.log(e),console.log(),console.log(" "+g("HTTP call chains")+t(" (end-to-end resolution)")),console.log();const n=Math.round(o.chains.resolved/o.chains.total*100),l=S(n),r=w(o.chains.resolved,o.chains.total,20);console.log(` ${l(r)} ${g(n+"%")} resolved (${o.chains.resolved}/${o.chains.total} call chains)`),o.chains.resolved<o.chains.total&&console.log(t(`
3
+ Unresolved calls may be to external services or missing route files`))}if(console.log(),console.log(e),console.log(),console.log(" "+g("Design system")+t(" (inferno/theme.json)")),console.log(),!o.theme.captured)console.log(t(" Not captured yet \u2014 run: infernoflow theme"));else{const n=[];o.theme.fonts&&n.push(`${o.theme.fonts} font${o.theme.fonts!==1?"s":""}`),o.theme.colors&&n.push(`${o.theme.colors} colors`),o.theme.cssVars&&n.push(`${o.theme.cssVars} CSS vars`),o.theme.framework&&n.push(`${o.theme.framework}`),console.log(t(" ")+u("\u2714")+" "+n.join(" \xB7 ")),console.log(t(" AI agents always use the correct fonts and colors for this project"))}console.log(),console.log(e),console.log(),console.log(" "+g("Estimated token savings")+t(" (vs re-discovering from scratch)")),console.log();const c=o.savings.estimatedTokens;if(c===0)console.log(t(" No session entries yet \u2014 start logging to track savings"));else{const n=Math.max(o.memory.sessionsTracked,1),l=Math.round(c/n);console.log(" Total saved: "+g(u(p(c)+" tokens"))),console.log(" Per session: "+g(p(l)+" tokens")),console.log(),console.log(t(" Breakdown:"));const r=["gotcha","handoff","attempt","decision","theme","preference","note","error"];for(const f of r){const s=o.savings.breakdown[f];if(!s)continue;const m=o.memory.byType[f]||0;console.log(t(` ${f.padEnd(12)} ${m}\xD7 \xD7 ${x[f]||100} = `)+b(p(s)))}console.log(),console.log(t(" * Estimates based on typical back-and-forth cost per entry type.")),console.log(t(" Actual savings vary with model, project complexity, and session length."))}console.log(),console.log(e),console.log()}async function R(o=[]){const e=o.includes("--json"),i=o.includes("--brief"),c=process.cwd();!d.existsSync(a.join(c,y))&&!d.existsSync(a.join(c,".ai-memory"))&&(console.error(j(` \u2718 not initialized \u2014 run: infernoflow init
4
+ `)),process.exit(1));const n=F(c);if(e){console.log(JSON.stringify(n,null,2));return}if(i){const l=[];n.memory.total&&l.push(`${n.memory.total} memory entries`),n.context.estimatedTokens&&l.push(`${p(n.context.estimatedTokens)} tokens/session`),n.coverage.total&&l.push(`${n.coverage.pct}% capability coverage`),n.savings.estimatedTokens&&l.push(`${p(n.savings.estimatedTokens)} tokens saved`),console.log(l.join(" \xB7 ")||"No data yet \u2014 run infernoflow init + infernoflow log");return}D(n)}export{R as statsCommand};
@@ -1,7 +1,7 @@
1
- import*as n from"node:fs";import*as r from"node:path";import{header as R,ok as H,fail as J,warn as P,section as $,bold as c,cyan as x,yellow as p,gray as e,green as g,red as U,white as I}from"../ui/output.mjs";function E(a){const s=Math.floor((Date.now()-a)/1e3);return s<60?"just now":s<3600?`${Math.floor(s/60)}m ago`:s<86400?`${Math.floor(s/3600)}h ago`:`${Math.floor(s/86400)}d ago`}function _(a,s){const S=new Set;if(n.existsSync(a))for(const i of n.readdirSync(a).filter(m=>m.endsWith(".json")))try{(JSON.parse(n.readFileSync(r.join(a,i),"utf8")).capabilitiesCovered||[]).forEach(d=>S.add(d))}catch{}return{covered:s.filter(i=>S.has(i)),uncovered:s.filter(i=>!S.has(i))}}async function W(a=[]){const s=a.includes("--json"),S=process.cwd(),i=r.join(S,"inferno");s||R("status"),n.existsSync(i)||(s&&(console.log(JSON.stringify({ok:!1,error:"inferno_not_found",hint:"Run: infernoflow init"},null,2)),process.exit(1)),J("inferno/ not found","Run: infernoflow init"),console.log(),process.exit(1));const m=r.join(i,"config.json"),d=r.join(i,"contract.json");if((()=>{try{return JSON.parse(n.readFileSync(m,"utf8")).mode||null}catch{return null}})()==="memory"||!n.existsSync(d)&&n.existsSync(m)){const t=r.join(i,"sessions.jsonl"),o=n.existsSync(t)?n.readFileSync(t,"utf8").split(`
2
- `).filter(Boolean).map(f=>{try{return JSON.parse(f)}catch{return null}}).filter(Boolean):[],l=o.filter(f=>f.type==="gotcha").length,h=o.filter(f=>f.type==="decision").length,u=o.filter(f=>f.type==="attempt").length,y=o[o.length-1];if(s){console.log(JSON.stringify({ok:!0,mode:"memory",entries:o.length,gotchas:l,decisions:h,attempts:u,lastEntry:y?y.ts:null},null,2));return}$("Session memory"),console.log(` ${e("entries")} ${c(String(o.length))}`),console.log(` ${e("gotchas")} ${c(String(l))}`),console.log(` ${e("decisions")} ${c(String(h))}`),console.log(` ${e("attempts")} ${c(String(u))}`),y&&console.log(` ${e("last entry")} ${e(E(new Date(y.ts).getTime()))}`),console.log(),o.length===0?console.log(` ${p("\u25CF")} ${c(p("empty"))} ${e("\u2014 log your first gotcha:")} ${x('infernoflow log "..." --type gotcha')}`):console.log(` ${g("\u25CF")} ${c(g("ready"))} ${e("\u2014 run")} ${x("infernoflow recap")} ${e("for the full session summary")}`),console.log(`
3
- ${e("Want capability contracts + CI gates? Run:")} ${x("infernoflow init --mode full")}
4
- `);return}n.existsSync(d)||(s&&(console.log(JSON.stringify({ok:!1,error:"contract_not_found"},null,2)),process.exit(1)),J("contract.json not found"),console.log(),process.exit(1));const j=JSON.parse(n.readFileSync(d,"utf8")),C=j.capabilities||[],M=n.statSync(d),N=r.join(i,"scenarios"),O=r.join(i,"CHANGELOG.md"),k=r.join(i,"capabilities.json"),{covered:A,uncovered:v}=_(N,C),F=n.existsSync(O)&&/##\s+Unreleased/i.test(n.readFileSync(O,"utf8")),w=[];v.length>0&&w.push(`${v.length} capabilities without scenario coverage`),F||w.push("CHANGELOG missing ## Unreleased section");const b=w.length===0;if(s){const t={ok:b,driftReasons:w,project:{policyId:j.policyId||null,policyVersion:j.policyVersion||null,lastChange:E(M.mtimeMs)},capabilities:{total:C.length,uncovered:v},changelog:{hasUnreleased:F}};console.log(JSON.stringify(t,null,2)),process.exit(b?0:1)}b||($("Drift"),w.forEach(t=>console.log(` ${p("\u26A0")} ${t}`))),$("Project"),console.log(` ${e("policy")} ${c(j.policyId||"\u2014")}`),console.log(` ${e("version")} ${c("v"+(j.policyVersion||"?"))}`),console.log(` ${e("last change")} ${e(E(M.mtimeMs))}`),$(`Capabilities ${e("("+C.length+")")}`);let G={};if(n.existsSync(k))try{(JSON.parse(n.readFileSync(k,"utf8")).capabilities||[]).forEach(o=>{G[o.id]=o})}catch{}if(C.forEach(t=>{const o=G[t],h=A.includes(t)?g("\u2714"):U("\u2718"),u=o?.title?e(` \u2014 ${o.title}`):"",y=o?.since?e(` [${o.since}]`):"";console.log(` ${h} ${I(t)}${u}${y}`)}),v.length>0?console.log(`
5
- ${p("\u26A0")} ${v.length} capability(ies) lack scenario coverage`):console.log(`
6
- ${g("\u2714")} All capabilities have scenario coverage`),$("Scenarios"),n.existsSync(N)){const t=n.readdirSync(N).filter(o=>o.endsWith(".json"));t.length===0?P("No scenario files \u2014 add .json files to inferno/scenarios/"):t.forEach(o=>{try{const l=JSON.parse(n.readFileSync(r.join(N,o),"utf8")),h=l.steps?.length||0,u=(l.capabilitiesCovered||[]).length;console.log(` ${g("\u2714")} ${x(o)} ${e(`\u2014 ${h} steps, ${u} caps covered`)}`)}catch{console.log(` ${U("\u2718")} ${x(o)} ${e("\u2014 invalid JSON")}`)}})}else P("scenarios/ directory not found");if($("Changelog"),n.existsSync(O)){const t=n.readFileSync(O,"utf8");/##\s+Unreleased/i.test(t)?H("Has ## Unreleased section"):J("Missing ## Unreleased section"),t.split(`
7
- `).filter(l=>/^##\s/.test(l)).slice(0,3).forEach(l=>console.log(` ${e(l)}`))}else J("inferno/CHANGELOG.md not found");console.log(),console.log(b?` ${g("\u25CF")} ${c(g("ready"))} ${e("\u2014 run infernoflow check for full validation")}`:` ${p("\u25CF")} ${c(p("needs attention"))} ${e("\u2014 run infernoflow check for details")}`),console.log()}export{W as statusCommand};
1
+ import*as o from"node:fs";import*as l from"node:path";import{header as L,ok as V,fail as J,warn as U,section as $,bold as r,cyan as v,yellow as m,gray as n,green as d,red as R,white as W}from"../ui/output.mjs";import{ampPaths as _}from"../amp/io.mjs";function E(a){const s=Math.floor((Date.now()-a)/1e3);return s<60?"just now":s<3600?`${Math.floor(s/60)}m ago`:s<86400?`${Math.floor(s/3600)}h ago`:`${Math.floor(s/86400)}d ago`}function z(a,s){const f=new Set;if(o.existsSync(a))for(const i of o.readdirSync(a).filter(h=>h.endsWith(".json")))try{(JSON.parse(o.readFileSync(l.join(a,i),"utf8")).capabilitiesCovered||[]).forEach(M=>f.add(M))}catch{}return{covered:s.filter(i=>f.has(i)),uncovered:s.filter(i=>!f.has(i))}}async function K(a=[]){const s=a.includes("--json"),f=process.cwd(),i=l.join(f,"inferno"),h=l.join(f,".ai-memory");s||L("status"),!o.existsSync(i)&&!o.existsSync(h)&&(s&&(console.log(JSON.stringify({ok:!1,error:"not_initialized",hint:"Run: infernoflow init"},null,2)),process.exit(1)),J("not initialized \u2014 neither .ai-memory/ nor inferno/ found","Run: infernoflow init"),console.log(),process.exit(1));const M=l.join(h,"amp.json"),k=l.join(i,"config.json"),w=l.join(i,"contract.json"),D=o.existsSync(M)||o.existsSync(h),H=(()=>{try{return JSON.parse(o.readFileSync(k,"utf8")).mode||null}catch{return null}})();if(D||H==="memory"||!o.existsSync(w)&&o.existsSync(k)){const t=_(f).sessions,e=o.existsSync(t)?o.readFileSync(t,"utf8").split(`
2
+ `).filter(Boolean).map(g=>{try{return JSON.parse(g)}catch{return null}}).filter(Boolean):[],c=e.filter(g=>g.type==="gotcha").length,y=e.filter(g=>g.type==="decision").length,u=e.filter(g=>g.type==="attempt").length,p=e[e.length-1];if(s){console.log(JSON.stringify({ok:!0,mode:"memory",entries:e.length,gotchas:c,decisions:y,attempts:u,lastEntry:p?p.ts:null},null,2));return}$("Session memory"),console.log(` ${n("entries")} ${r(String(e.length))}`),console.log(` ${n("gotchas")} ${r(String(c))}`),console.log(` ${n("decisions")} ${r(String(y))}`),console.log(` ${n("attempts")} ${r(String(u))}`),p&&console.log(` ${n("last entry")} ${n(E(new Date(p.ts).getTime()))}`),console.log(),e.length===0?console.log(` ${m("\u25CF")} ${r(m("empty"))} ${n("\u2014 log your first gotcha:")} ${v('infernoflow log "..." --type gotcha')}`):console.log(` ${d("\u25CF")} ${r(d("ready"))} ${n("\u2014 run")} ${v("infernoflow recap")} ${n("for the full session summary")}`),console.log(`
3
+ ${n("Want capability contracts + CI gates? Run:")} ${v("infernoflow init --mode full")}
4
+ `);return}o.existsSync(w)||(s&&(console.log(JSON.stringify({ok:!1,error:"contract_not_found"},null,2)),process.exit(1)),J("contract.json not found"),console.log(),process.exit(1));const S=JSON.parse(o.readFileSync(w,"utf8")),C=S.capabilities||[],F=o.statSync(w),N=l.join(i,"scenarios"),O=l.join(i,"CHANGELOG.md"),P=l.join(i,"capabilities.json"),{covered:I,uncovered:j}=z(N,C),G=o.existsSync(O)&&/##\s+Unreleased/i.test(o.readFileSync(O,"utf8")),x=[];j.length>0&&x.push(`${j.length} capabilities without scenario coverage`),G||x.push("CHANGELOG missing ## Unreleased section");const b=x.length===0;if(s){const t={ok:b,driftReasons:x,project:{policyId:S.policyId||null,policyVersion:S.policyVersion||null,lastChange:E(F.mtimeMs)},capabilities:{total:C.length,uncovered:j},changelog:{hasUnreleased:G}};console.log(JSON.stringify(t,null,2)),process.exit(b?0:1)}b||($("Drift"),x.forEach(t=>console.log(` ${m("\u26A0")} ${t}`))),$("Project"),console.log(` ${n("policy")} ${r(S.policyId||"\u2014")}`),console.log(` ${n("version")} ${r("v"+(S.policyVersion||"?"))}`),console.log(` ${n("last change")} ${n(E(F.mtimeMs))}`),$(`Capabilities ${n("("+C.length+")")}`);let A={};if(o.existsSync(P))try{(JSON.parse(o.readFileSync(P,"utf8")).capabilities||[]).forEach(e=>{A[e.id]=e})}catch{}if(C.forEach(t=>{const e=A[t],y=I.includes(t)?d("\u2714"):R("\u2718"),u=e?.title?n(` \u2014 ${e.title}`):"",p=e?.since?n(` [${e.since}]`):"";console.log(` ${y} ${W(t)}${u}${p}`)}),j.length>0?console.log(`
5
+ ${m("\u26A0")} ${j.length} capability(ies) lack scenario coverage`):console.log(`
6
+ ${d("\u2714")} All capabilities have scenario coverage`),$("Scenarios"),o.existsSync(N)){const t=o.readdirSync(N).filter(e=>e.endsWith(".json"));t.length===0?U("No scenario files \u2014 add .json files to inferno/scenarios/"):t.forEach(e=>{try{const c=JSON.parse(o.readFileSync(l.join(N,e),"utf8")),y=c.steps?.length||0,u=(c.capabilitiesCovered||[]).length;console.log(` ${d("\u2714")} ${v(e)} ${n(`\u2014 ${y} steps, ${u} caps covered`)}`)}catch{console.log(` ${R("\u2718")} ${v(e)} ${n("\u2014 invalid JSON")}`)}})}else U("scenarios/ directory not found");if($("Changelog"),o.existsSync(O)){const t=o.readFileSync(O,"utf8");/##\s+Unreleased/i.test(t)?V("Has ## Unreleased section"):J("Missing ## Unreleased section"),t.split(`
7
+ `).filter(c=>/^##\s/.test(c)).slice(0,3).forEach(c=>console.log(` ${n(c)}`))}else J("inferno/CHANGELOG.md not found");console.log(),console.log(b?` ${d("\u25CF")} ${r(d("ready"))} ${n("\u2014 run infernoflow check for full validation")}`:` ${m("\u25CF")} ${r(m("needs attention"))} ${n("\u2014 run infernoflow check for details")}`),console.log()}export{K as statusCommand};
@@ -1,13 +1,11 @@
1
- import*as m from"node:fs";import*as O from"node:path";import"node:os";import{execSync as y}from"node:child_process";import{bold as P,cyan as S,gray as et,green as ot,yellow as V,red as rt}from"../ui/output.mjs";const I="inferno",nt=O.join(I,"HANDOFF.md"),U=O.join(I,"sessions.jsonl"),J=O.join(I,"context-state.json"),K=O.join(I,"contract.json"),X=O.join(I,"theme.json"),q=O.join(I,"adoption_profile.json");function u(e){try{return JSON.parse(m.readFileSync(e,"utf8"))}catch{return null}}function ct(e){try{return m.readFileSync(e,"utf8")}catch{return null}}function F(e){return e?new Date(e).toLocaleString("en-GB",{day:"2-digit",month:"short",hour:"2-digit",minute:"2-digit"}):"unknown"}function z(e){if(e<0)return"unknown";const i=Math.floor(e/36e5),l=Math.floor(e%36e5/6e4);return i>0?`${i}h ${l}m`:`${l}m`}function Q(){return m.existsSync(U)?m.readFileSync(U,"utf8").split(`
2
- `).filter(Boolean).map(e=>{try{return JSON.parse(e)}catch{return null}}).filter(Boolean):[]}function Y(e,i,l){if(l)return new Date(0);if(i){const n=i.match(/^(\d+)h$/i),c=i.match(/^(\d+)d$/i);if(n)return new Date(Date.now()-parseInt(n[1])*36e5);if(c)return new Date(Date.now()-parseInt(c[1])*864e5);const s=new Date(i);if(!isNaN(s))return s}for(let n=e.length-1;n>=0;n--)if(e[n].type==="handoff"){const c=new Date(e[n].ts||0),s=new Date(Date.now()-864e5);return c>s?c:s}return new Date(Date.now()-864e5)}function lt(e){try{const i=process.platform;if(i==="win32")y("clip",{input:e});else if(i==="darwin")y("pbcopy",{input:e});else try{y("xclip -selection clipboard",{input:e})}catch{y("xsel --clipboard --input",{input:e})}return!0}catch{return!1}}function Z(){if(process.env.CURSOR_SESSION)return"Cursor";if(process.env.COPILOT_SESSION)return"GitHub Copilot";if(process.env.CLAUDE_CODE_SESSION)return"Claude Code";if(process.env.WINDSURF_SESSION)return"Windsurf";if(process.env.TERM_PROGRAM==="vscode")return"VS Code";const e=u(q);return e?.ide?e.ide:null}function at(){try{const e=y("git diff --stat HEAD 2>/dev/null || git diff --cached --stat 2>/dev/null",{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim();return e||y("git log --stat -1 --pretty= 2>/dev/null",{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim()||null}catch{return null}}function st(e){try{const i=e&&e.getTime()>0?`--after="${e.toISOString()}"`:"-10",l=y(`git log ${i} --name-only --pretty=format: 2>/dev/null`,{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim();if(!l)return[];const n={};for(const c of l.split(`
3
- `)){const s=c.trim();s&&(n[s]=(n[s]||0)+1)}return Object.entries(n).sort((c,s)=>s[1]-c[1]).slice(0,5).map(([c,s])=>({file:c,edits:s}))}catch{return[]}}function tt(e){try{const i=e?`--after="${e.toISOString()}"`:"-5",l=y(`git log ${i} --pretty=format:"%h %s" 2>/dev/null`,{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim();return l?l.split(`
4
- `).filter(Boolean):[]}catch{return[]}}function it(e){const i=[],l=e.filter(n=>n.type==="attempt"&&(n.result==="failed"||n.result==="partial"||!n.result));for(const n of l)e.find(s=>s.type==="attempt"&&s.result==="worked"&&new Date(s.ts)>new Date(n.ts)&&s.summary.toLowerCase().includes(n.summary.split(" ")[0].toLowerCase()))||i.push({text:n.summary,ts:n.ts,kind:"unresolved-attempt"});for(const n of e)/\b(TODO|WIP|FIXME|BLOCKED|pending)\b/i.test(n.summary)&&(i.find(c=>c.text===n.summary)||i.push({text:n.summary,ts:n.ts,kind:"flagged"}));return i.slice(0,8)}function ft(e,i,l){const n=u(J)||{},c=u(K)||{},s=u(X),C=u(q),w=Q(),a=Y(w,i,l),_=w.filter(o=>new Date(o.ts||0)>a),T=w.slice(-5),f=new Date,N=f.toLocaleString("en-GB",{day:"2-digit",month:"short",year:"numeric",hour:"2-digit",minute:"2-digit"}),$=c.policyId||O.basename(process.cwd()),j=c.policyVersion||"?",v=(c.capabilities||[]).slice(0,20),b=Z(),H=a.getTime()>0?f-a:-1,k=z(H),x=a.getTime()>0?a.getTime().toString(16).slice(-6).toUpperCase():"ALL",D=tt(a.getTime()>0?a:null),E=at(),G=st(a.getTime()>0?a:null),r=_.length>0?_:T,g=r.filter(o=>o.type==="gotcha"),R=r.filter(o=>o.type==="decision"),M=r.filter(o=>o.type==="attempt").filter(o=>o.result==="failed"||o.result==="partial"),A=r.filter(o=>o.type==="preference"),d=r.slice(-8),B=it(r),W=a.getTime()===0?"all time":a.toLocaleString("en-GB",{day:"2-digit",month:"short",hour:"2-digit",minute:"2-digit"}),h=["sessions.jsonl"];(n.working||n.intent)&&h.push("context-state.json"),s&&h.push("theme.json"),c.capabilities?.length&&h.push("contract.json"),C&&h.push("adoption_profile.json"),D.length&&h.push("git log");const t=[`# \u{1F525} infernoflow Handoff \u2014 ${$}`,`> Generated: ${N}${e?` | Handing off to: **${e}**`:""}`,`> Session: **#${x}** \xB7 ${k} \xB7 **${_.length} entries** since ${W}`,`> Sources: ${h.join(" \xB7 ")}${b?` \xB7 IDE: ${b}`:""}`,"","---","","## Pick up here",""];if(n.working||n.intent?(n.working&&t.push(`**Working on:** ${n.working} _(${F(n.workingUpdated)})_`),n.intent&&t.push(`**Intent:** ${n.intent} _(${F(n.intentUpdated)})_`),t.push("")):t.push('_No working state set. Run: `infernoflow context --working "..."` to set it._',""),B.length){t.push("## \u{1F513} Open threads \u2014 not yet resolved","");for(const o of B){const p=o.kind==="flagged"?"[flagged]":"[unresolved]";t.push(`- ${p} ${o.text} _(${F(o.ts)})_`)}t.push("")}if(g.length){t.push("## \u26A0 Gotchas \u2014 read these first","");for(const o of g)t.push(`- ${o.summary} _(${F(o.ts)})_`);t.push("")}if(R.length){t.push("## Decisions made","");for(const o of R){const p=o.result?` \u2192 **${o.result}**`:"";t.push(`- ${o.summary}${p} _(${F(o.ts)})_`)}t.push("")}if(M.length){t.push("## \u2717 Already tried \u2014 don't repeat","");for(const o of M)t.push(`- ${o.summary} _(${F(o.ts)})_`);t.push("")}if(G.length){t.push("## \u{1F4C1} Hot Files This Session","");for(const{file:o,edits:p}of G)t.push(`- \`${o}\` \u2014 ${p} edit${p!==1?"s":""}`);t.push("")}if(A.length){t.push("## Developer preferences","");for(const o of A)t.push(`- ${o.summary}`);t.push("")}if(D.length||E){if(t.push("## Git activity this session",""),D.length){t.push("**Commits:**");for(const o of D)t.push(`- \`${o}\``);t.push("")}E&&(t.push("**Uncommitted changes:**"),t.push("```"),t.push(E.split(`
1
+ import*as E from"node:fs";import*as g from"node:path";import"node:os";import{execSync as w}from"node:child_process";import{bold as W,cyan as S,gray as et,green as ot,yellow as B,red as it}from"../ui/output.mjs";import{ampPaths as rt,readEntries as ct,appendEntry as lt}from"../amp/io.mjs";const I="inferno";function V(){return rt(process.cwd())}const at=g.join(I,"HANDOFF.md"),wt=g.join(I,"sessions.jsonl"),J=g.join(I,"context-state.json"),z=g.join(I,"contract.json"),K=g.join(I,"theme.json"),X=g.join(I,"adoption_profile.json");function f(o){try{return JSON.parse(E.readFileSync(o,"utf8"))}catch{return null}}function pt(o){try{return E.readFileSync(o,"utf8")}catch{return null}}function F(o){return o?new Date(o).toLocaleString("en-GB",{day:"2-digit",month:"short",hour:"2-digit",minute:"2-digit"}):"unknown"}function q(o){if(o<0)return"unknown";const i=Math.floor(o/36e5),l=Math.floor(o%36e5/6e4);return i>0?`${i}h ${l}m`:`${l}m`}function Q(){return ct(process.cwd())}function Y(o,i,l){if(l)return new Date(0);if(i){const n=i.match(/^(\d+)h$/i),c=i.match(/^(\d+)d$/i);if(n)return new Date(Date.now()-parseInt(n[1])*36e5);if(c)return new Date(Date.now()-parseInt(c[1])*864e5);const s=new Date(i);if(!isNaN(s))return s}for(let n=o.length-1;n>=0;n--)if(o[n].type==="handoff"){const c=new Date(o[n].ts||0),s=new Date(Date.now()-864e5);return c>s?c:s}return new Date(Date.now()-864e5)}function ft(o){try{const i=process.platform;if(i==="win32")w("clip",{input:o});else if(i==="darwin")w("pbcopy",{input:o});else try{w("xclip -selection clipboard",{input:o})}catch{w("xsel --clipboard --input",{input:o})}return!0}catch{return!1}}function Z(){if(process.env.CURSOR_SESSION)return"Cursor";if(process.env.COPILOT_SESSION)return"GitHub Copilot";if(process.env.CLAUDE_CODE_SESSION)return"Claude Code";if(process.env.WINDSURF_SESSION)return"Windsurf";if(process.env.TERM_PROGRAM==="vscode")return"VS Code";const o=f(X);return o?.ide?o.ide:null}function ut(){try{const o=w("git diff --stat HEAD 2>/dev/null || git diff --cached --stat 2>/dev/null",{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim();return o||w("git log --stat -1 --pretty= 2>/dev/null",{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim()||null}catch{return null}}function nt(o){try{const i=o&&o.getTime()>0?`--after="${o.toISOString()}"`:"-10",l=w(`git log ${i} --name-only --pretty=format: 2>/dev/null`,{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim();if(!l)return[];const n={};for(const c of l.split(`
2
+ `)){const s=c.trim();s&&(n[s]=(n[s]||0)+1)}return Object.entries(n).sort((c,s)=>s[1]-c[1]).slice(0,5).map(([c,s])=>({file:c,edits:s}))}catch{return[]}}function tt(o){try{const i=o?`--after="${o.toISOString()}"`:"-5",l=w(`git log ${i} --pretty=format:"%h %s" 2>/dev/null`,{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim();return l?l.split(`
3
+ `).filter(Boolean):[]}catch{return[]}}function st(o){const i=[],l=o.filter(n=>n.type==="attempt"&&(n.result==="failed"||n.result==="partial"||!n.result));for(const n of l)o.find(s=>s.type==="attempt"&&s.result==="worked"&&new Date(s.ts)>new Date(n.ts)&&s.summary.toLowerCase().includes(n.summary.split(" ")[0].toLowerCase()))||i.push({text:n.summary,ts:n.ts,kind:"unresolved-attempt"});for(const n of o)/\b(TODO|WIP|FIXME|BLOCKED|pending)\b/i.test(n.summary)&&(i.find(c=>c.text===n.summary)||i.push({text:n.summary,ts:n.ts,kind:"flagged"}));return i.slice(0,8)}function dt(o,i,l){const n=f(J)||{},c=f(z)||{},s=f(K),b=f(X),$=Q(),a=Y($,i,l),k=$.filter(e=>new Date(e.ts||0)>a),H=$.slice(-5),D=new Date,u=D.toLocaleString("en-GB",{day:"2-digit",month:"short",year:"numeric",hour:"2-digit",minute:"2-digit"}),_=c.policyId||g.basename(process.cwd()),j=c.policyVersion||"?",O=(c.capabilities||[]).slice(0,20),x=Z(),R=a.getTime()>0?D-a:-1,N=q(R),A=a.getTime()>0?a.getTime().toString(16).slice(-6).toUpperCase():"ALL",T=tt(a.getTime()>0?a:null),C=ut(),G=nt(a.getTime()>0?a:null),m=k.length>0?k:H,r=m.filter(e=>e.type==="gotcha"),h=m.filter(e=>e.type==="decision"),M=m.filter(e=>e.type==="attempt").filter(e=>e.result==="failed"||e.result==="partial"),P=m.filter(e=>e.type==="preference"),L=m.slice(-8),d=st(m),U=a.getTime()===0?"all time":a.toLocaleString("en-GB",{day:"2-digit",month:"short",hour:"2-digit",minute:"2-digit"}),y=["sessions.jsonl"];(n.working||n.intent)&&y.push("context-state.json"),s&&y.push("theme.json"),c.capabilities?.length&&y.push("contract.json"),b&&y.push("adoption_profile.json"),T.length&&y.push("git log");const t=[`# \u{1F525} infernoflow Handoff \u2014 ${_}`,`> Generated: ${u}${o?` | Handing off to: **${o}**`:""}`,`> Session: **#${A}** \xB7 ${N} \xB7 **${k.length} entries** since ${U}`,`> Sources: ${y.join(" \xB7 ")}${x?` \xB7 IDE: ${x}`:""}`,"","---","","## Pick up here",""];if(n.working||n.intent?(n.working&&t.push(`**Working on:** ${n.working} _(${F(n.workingUpdated)})_`),n.intent&&t.push(`**Intent:** ${n.intent} _(${F(n.intentUpdated)})_`),t.push("")):t.push('_No working state set. Run: `infernoflow context --working "..."` to set it._',""),d.length){t.push("## \u{1F513} Open threads \u2014 not yet resolved","");for(const e of d){const p=e.kind==="flagged"?"[flagged]":"[unresolved]";t.push(`- ${p} ${e.text} _(${F(e.ts)})_`)}t.push("")}if(r.length){t.push("## \u26A0 Gotchas \u2014 read these first","");for(const e of r)t.push(`- ${e.summary} _(${F(e.ts)})_`);t.push("")}if(h.length){t.push("## Decisions made","");for(const e of h){const p=e.result?` \u2192 **${e.result}**`:"";t.push(`- ${e.summary}${p} _(${F(e.ts)})_`)}t.push("")}if(M.length){t.push("## \u2717 Already tried \u2014 don't repeat","");for(const e of M)t.push(`- ${e.summary} _(${F(e.ts)})_`);t.push("")}if(G.length){t.push("## \u{1F4C1} Hot Files This Session","");for(const{file:e,edits:p}of G)t.push(`- \`${e}\` \u2014 ${p} edit${p!==1?"s":""}`);t.push("")}if(P.length){t.push("## Developer preferences","");for(const e of P)t.push(`- ${e.summary}`);t.push("")}if(T.length||C){if(t.push("## Git activity this session",""),T.length){t.push("**Commits:**");for(const e of T)t.push(`- \`${e}\``);t.push("")}C&&(t.push("**Uncommitted changes:**"),t.push("```"),t.push(C.split(`
5
4
  `).slice(0,15).join(`
6
- `)),t.push("```"),t.push(""))}if(s){if(t.push("## Design system",""),s.fonts?.primary&&t.push(`- **Font:** ${s.fonts.primary}${s.fonts.mono?` \xB7 mono: ${s.fonts.mono}`:""}`),s.colors?.mode&&t.push(`- **Mode:** ${s.colors.mode}`),s.colors?.palette){const o=Object.entries(s.colors.palette).map(([p,L])=>`${p}=${L}`).join(" ");t.push(`- **Palette:** ${o}`)}if(s.cssVars&&Object.keys(s.cssVars).length){const o=Object.entries(s.cssVars).slice(0,6).map(([p,L])=>`${p}: ${L}`).join(" | ");t.push(`- **CSS vars:** ${o}`)}s.framework&&t.push(`- **Framework:** ${s.framework}`),t.push("","> \u26A0 Always match these exactly. Do not introduce new colors or fonts.","")}if(v.length&&(t.push("## Capability contract",""),t.push(`Project: **${$}** v${j}`),t.push(`Capabilities: ${v.join(", ")}`),t.push("")),d.length){t.push("## Recent session log","");for(const o of d){const p=o.result?` [${o.result}]`:"",L=o.source?` {${o.source}}`:"";t.push(`- **${o.type}**${p}${L}: ${o.summary} _(${F(o.ts)})_`)}t.push("")}return t.push("---"),t.push(`_Session #${x} \xB7 ${k} \xB7 Generated by infernoflow._`),t.join(`
7
- `)}async function gt(e){const i=r=>e.includes(r),l=r=>{const g=e.indexOf(r);return g!==-1&&e[g+1]?e[g+1]:null},n=i("--show")||i("-s"),c=i("--copy")||i("-c"),s=i("--json"),C=i("--all"),w=l("--since"),a=l("--to")||e.find(r=>!r.startsWith("-")&&!["switch"].includes(r))||null;if(console.log(`
8
- `+P("\u{1F525} infernoflow \u2014 switch")),console.log(" "+"\u2500".repeat(50)+`
9
- `),m.existsSync(I)||(console.error(rt(` \u2718 inferno/ not found \u2014 run: infernoflow init
10
- `)),process.exit(1)),n){const r=ct(nt);if(!r){console.log(V(` \u26A0 No HANDOFF.md yet \u2014 run: infernoflow switch
11
- `));return}console.log(r);return}const _=ft(a,w,C);if(s){const r=u(J)||{},g=u(K)||{},R=u(X),M=u(q),A=Q(),d=Y(A,w,C),B=A.filter(t=>new Date(t.ts||0)>d),W=tt(d.getTime()>0?d:null),h=Z();console.log(JSON.stringify({state:r,contract:{policyId:g.policyId,policyVersion:g.policyVersion,capabilities:g.capabilities},theme:R,adoption:M,sessions:B,commits:W,ide:h,sessionStart:d.toISOString(),sessionId:d.getTime()>0?d.getTime().toString(16).slice(-6).toUpperCase():"ALL",sessionDuration:z(d.getTime()>0?Date.now()-d.getTime():-1),generatedAt:new Date().toISOString()},null,2));return}m.writeFileSync(nt,_,"utf8"),console.log(ot(` \u2714 Written \u2192 inferno/HANDOFF.md
12
- `));const T=Q(),f=Y(T,w,C),N=T.filter(r=>new Date(r.ts||0)>f),$=u(J)||{},j=u(X),v=u(K)||{},b=tt(f.getTime()>0?f:null),H=st(f.getTime()>0?f:null),k=Z(),x=N.length>0?N:T.slice(-5),D=it(x),E=z(f.getTime()>0?Date.now()-f.getTime():-1),G=f.getTime()>0?f.getTime().toString(16).slice(-6).toUpperCase():"ALL";if(console.log(" "+P("Handoff ready")),console.log(" "+"\u2500".repeat(50)),console.log(" "+et("Session #"+G+" \xB7 "+E)),$.working&&console.log(" Working on "+S($.working)),$.intent&&console.log(" Intent "+S($.intent)),console.log(" Memory "+N.length+" entries this session (total: "+T.length+")"),D.length&&console.log(" Open threads "+V(D.length+" unresolved")),b.length&&console.log(" Git commits "+b.length+" this session"),H.length&&console.log(" Hot files "+H.map(r=>S(r.file)).join(", ")),console.log(" Capabilities "+(v.capabilities||[]).length+" registered"),j?.fonts?.primary&&console.log(" Font "+j.fonts.primary),j?.colors?.mode&&console.log(" Color mode "+j.colors.mode),k&&console.log(" IDE "+k),a&&console.log(" Handing off \u2192 "+S(a)),console.log(),c){const r=lt(_);console.log(r?ot(" \u2714 Copied to clipboard \u2014 paste at the start of your next AI session"):V(" \u26A0 Clipboard failed \u2014 open inferno/HANDOFF.md manually"))}else console.log(" "+P("Ready to use:")),console.log(" "+S("1.")+" Open "+S("inferno/HANDOFF.md")),console.log(" "+S("2.")+" Copy all"),console.log(" "+S("3.")+" Paste at the start of your next AI session"),console.log(" "+et(" tip: use --copy to skip steps 1-2 automatically"));if(console.log(),m.existsSync(U)){const r={ts:new Date().toISOString(),agent:"infernoflow",type:"handoff",summary:a?`Handed off to ${a}`:"Handoff generated"};m.appendFileSync(U,JSON.stringify(r)+`
13
- `,"utf8")}}export{gt as switchCommand};
5
+ `)),t.push("```"),t.push(""))}if(s){if(t.push("## Design system",""),s.fonts?.primary&&t.push(`- **Font:** ${s.fonts.primary}${s.fonts.mono?` \xB7 mono: ${s.fonts.mono}`:""}`),s.colors?.mode&&t.push(`- **Mode:** ${s.colors.mode}`),s.colors?.palette){const e=Object.entries(s.colors.palette).map(([p,v])=>`${p}=${v}`).join(" ");t.push(`- **Palette:** ${e}`)}if(s.cssVars&&Object.keys(s.cssVars).length){const e=Object.entries(s.cssVars).slice(0,6).map(([p,v])=>`${p}: ${v}`).join(" | ");t.push(`- **CSS vars:** ${e}`)}s.framework&&t.push(`- **Framework:** ${s.framework}`),t.push("","> \u26A0 Always match these exactly. Do not introduce new colors or fonts.","")}if(O.length&&(t.push("## Capability contract",""),t.push(`Project: **${_}** v${j}`),t.push(`Capabilities: ${O.join(", ")}`),t.push("")),L.length){t.push("## Recent session log","");for(const e of L){const p=e.result?` [${e.result}]`:"",v=e.source?` {${e.source}}`:"";t.push(`- **${e.type}**${p}${v}: ${e.summary} _(${F(e.ts)})_`)}t.push("")}return t.push("---"),t.push(`_Session #${A} \xB7 ${N} \xB7 Generated by infernoflow._`),t.join(`
6
+ `)}async function St(o){const i=r=>o.includes(r),l=r=>{const h=o.indexOf(r);return h!==-1&&o[h+1]?o[h+1]:null},n=i("--show")||i("-s"),c=i("--copy")||i("-c"),s=i("--json"),b=i("--all"),$=l("--since"),a=l("--to")||o.find(r=>!r.startsWith("-")&&!["switch"].includes(r))||null;console.log(`
7
+ `+W("\u{1F525} infernoflow \u2014 switch")),console.log(" "+"\u2500".repeat(50)+`
8
+ `);const k=g.join(process.cwd(),".ai-memory");if(!E.existsSync(I)&&!E.existsSync(k)&&(console.error(it(` \u2718 not initialized \u2014 run: infernoflow init
9
+ `)),process.exit(1)),n){const r=pt(at);if(!r){console.log(B(` \u26A0 No HANDOFF.md yet \u2014 run: infernoflow switch
10
+ `));return}console.log(r);return}const H=dt(a,$,b);if(s){const r=f(J)||{},h=f(z)||{},M=f(K),P=f(X),L=Q(),d=Y(L,$,b),U=L.filter(e=>new Date(e.ts||0)>d),y=tt(d.getTime()>0?d:null),t=Z();console.log(JSON.stringify({state:r,contract:{policyId:h.policyId,policyVersion:h.policyVersion,capabilities:h.capabilities},theme:M,adoption:P,sessions:U,commits:y,ide:t,sessionStart:d.toISOString(),sessionId:d.getTime()>0?d.getTime().toString(16).slice(-6).toUpperCase():"ALL",sessionDuration:q(d.getTime()>0?Date.now()-d.getTime():-1),generatedAt:new Date().toISOString()},null,2));return}E.writeFileSync(V().handoff,H,"utf8"),console.log(ot(" \u2714 Written \u2192 "+g.relative(process.cwd(),V().handoff)+`
11
+ `));const D=Q(),u=Y(D,$,b),_=D.filter(r=>new Date(r.ts||0)>u),j=f(J)||{},O=f(K),x=f(z)||{},R=tt(u.getTime()>0?u:null),N=nt(u.getTime()>0?u:null),A=Z(),T=_.length>0?_:D.slice(-5),C=st(T),G=q(u.getTime()>0?Date.now()-u.getTime():-1),m=u.getTime()>0?u.getTime().toString(16).slice(-6).toUpperCase():"ALL";if(console.log(" "+W("Handoff ready")),console.log(" "+"\u2500".repeat(50)),console.log(" "+et("Session #"+m+" \xB7 "+G)),j.working&&console.log(" Working on "+S(j.working)),j.intent&&console.log(" Intent "+S(j.intent)),console.log(" Memory "+_.length+" entries this session (total: "+D.length+")"),C.length&&console.log(" Open threads "+B(C.length+" unresolved")),R.length&&console.log(" Git commits "+R.length+" this session"),N.length&&console.log(" Hot files "+N.map(r=>S(r.file)).join(", ")),console.log(" Capabilities "+(x.capabilities||[]).length+" registered"),O?.fonts?.primary&&console.log(" Font "+O.fonts.primary),O?.colors?.mode&&console.log(" Color mode "+O.colors.mode),A&&console.log(" IDE "+A),a&&console.log(" Handing off \u2192 "+S(a)),console.log(),c){const r=ft(H);console.log(r?ot(" \u2714 Copied to clipboard \u2014 paste at the start of your next AI session"):B(" \u26A0 Clipboard failed \u2014 open inferno/HANDOFF.md manually"))}else console.log(" "+W("Ready to use:")),console.log(" "+S("1.")+" Open "+S("inferno/HANDOFF.md")),console.log(" "+S("2.")+" Copy all"),console.log(" "+S("3.")+" Paste at the start of your next AI session"),console.log(" "+et(" tip: use --copy to skip steps 1-2 automatically"));if(console.log(),E.existsSync(V().sessions)){const r={ts:new Date().toISOString(),agent:"infernoflow",type:"handoff",summary:a?`Handed off to ${a}`:"Handoff generated"};lt(process.cwd(),r)}}export{St as switchCommand};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "infernoflow",
3
- "version": "0.40.5",
3
+ "version": "0.42.0",
4
4
  "description": "Persistent memory for AI coding sessions \u2014 captures what agents can't infer from code alone. Works with Copilot, Cursor, Claude, and Windsurf.",
5
5
  "type": "module",
6
6
  "bin": {