infernoflow 0.42.6 → 0.42.8
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 +19 -0
- package/README.md +13 -2
- package/dist/lib/commands/doctor.mjs +3 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog — infernoflow
|
|
2
2
|
|
|
3
|
+
## 0.42.8 — 2026-05-06
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **`infernoflow doctor` no longer reports a false-positive "CLI not found on PATH" on Windows**. `npm install -g` creates an `infernoflow.cmd` shim on Windows, not an `.exe`, and `spawnSync` won't resolve `.cmd` files without `shell: true`. The PATH-check now passes the right flag on Windows. Belt-and-suspenders: if doctor is running, the CLI is by definition reachable, so the check now defaults to pass even if the spawn probe fails on exotic shells.
|
|
7
|
+
|
|
8
|
+
## 0.42.7 — 2026-05-06
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
Same content as 0.42.6 — that version got registered on npm during a flaky publish attempt but the dist contents were stale. 0.42.7 is the actual usable build with these fixes:
|
|
12
|
+
|
|
3
13
|
## 0.42.6 — 2026-05-06
|
|
4
14
|
|
|
5
15
|
### Fixed
|
|
@@ -86,6 +96,15 @@
|
|
|
86
96
|
- extension v0.7.2 + CLI hotfixes: auto-capture, CodeLens, bulk + orphan delete, MCP setup tools fix, graph crash guard
|
|
87
97
|
- VS Code Marketplace badge + extension install section
|
|
88
98
|
|
|
99
|
+
- infernoflow CLI v0.42.6: graph crash fix + MCP setup/error-handling hotfixes
|
|
100
|
+
- extension v0.7.2 + CLI hotfixes: auto-capture, CodeLens, bulk + orphan delete, MCP setup tools fix, graph crash guard
|
|
101
|
+
- VS Code Marketplace badge + extension install section
|
|
102
|
+
|
|
103
|
+
- infernoflow CLI v0.42.7: graph crash fix + MCP setup/error-handling hotfixes; README v0.7.2 extension features
|
|
104
|
+
- infernoflow CLI v0.42.6: graph crash fix + MCP setup/error-handling hotfixes
|
|
105
|
+
- extension v0.7.2 + CLI hotfixes: auto-capture, CodeLens, bulk + orphan delete, MCP setup tools fix, graph crash guard
|
|
106
|
+
- VS Code Marketplace badge + extension install section
|
|
107
|
+
|
|
89
108
|
## 0.42.1 — 2026-05-03
|
|
90
109
|
|
|
91
110
|
### Added
|
package/README.md
CHANGED
|
@@ -95,13 +95,24 @@ You don't have to paste anything. Set up once, every future session is better.
|
|
|
95
95
|
|
|
96
96
|
## VS Code extension
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
The companion extension turns the CLI into a live visual surface inside VS Code. Install it from the Marketplace:
|
|
99
99
|
|
|
100
100
|
```
|
|
101
101
|
ext install infernoflow.infernoflow
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
Or browse it on the
|
|
104
|
+
Or browse it [on the Marketplace](https://marketplace.visualstudio.com/items?itemName=infernoflow.infernoflow). Activates automatically on any project containing `.ai-memory/sessions.jsonl` or `inferno/`.
|
|
105
|
+
|
|
106
|
+
**What you get (v0.7.2):**
|
|
107
|
+
|
|
108
|
+
- **Auto-capture popup** — when you edit the same file 5+ times in 10 minutes, a popup asks "Stuck on something?" with [Log Gotcha] [Log Attempt] [Dismiss] buttons. Click once → entry is auto-written with timestamp, file:line, the enclosing function name, a 5-line code-context window, and any nearby TypeScript/ESLint diagnostics. Zero typing.
|
|
109
|
+
- **Sidebar memory panel** — Session Health (A–F grade), Gotchas, Decisions, Failed Attempts, Quick Actions, and a CLI Tools section with one-click access to 11 CLI commands (status, check, doctor, scan, init --adopt, setup, ai setup, install-cursor-hooks, watch, cloud status, show context).
|
|
110
|
+
- **Inline CodeLens** — `🔥 ⚠ 2 gotchas · 1 failed · 1 decision` above any file with logged entries. Per-line annotations at gotcha locations. Click for full entry detail.
|
|
111
|
+
- **Editor diagnostics** — gotchas as yellow Warnings, failed attempts as blue Information squiggles. Visible in the Problems panel and as inline squiggles. Copilot reads them too.
|
|
112
|
+
- **Status bar** — `🔥 B 65 · ⚠3 · ✓2 · ❌1 · 📋 Switch` with grade-based color coding. Click to open the panel; click "Switch" to copy a handoff.
|
|
113
|
+
- **Bulk delete + orphan cleanup** — "Manage entries…" picker grouped by date for cleaning up old entries. "Cleanup orphaned entries…" finds entries whose source files were deleted (e.g., after a refactor) and lets you bulk-remove them.
|
|
114
|
+
- **Help tooltips on everything** — hover any sidebar item or section header for a description of what it does, when to use it, and what happens when clicked.
|
|
115
|
+
- **Keyboard shortcuts** — `Ctrl+Alt+G/D/A/S/R` for log gotcha / log decision / ask memory / generate handoff / show recap.
|
|
105
116
|
|
|
106
117
|
## Cursor / VS Code MCP integration
|
|
107
118
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import*as s from"node:fs";import*as l from"node:path";import*as h from"node:os";import*as
|
|
2
|
-
`).filter(Boolean).length}catch{}return a(`Memory mode \u2014 ${t} session entr${t===1?"y":"ies"}`)}for(const i of["contract.json","capabilities.json"]){const t=l.join(o,i);if(s.existsSync(t))try{const u=(JSON.parse(s.readFileSync(t,"utf8")).capabilities||[]).length;return a(`${i} valid \u2014 ${u} capabilities`)}catch{return
|
|
1
|
+
import*as s from"node:fs";import*as l from"node:path";import*as h from"node:os";import*as N from"node:http";import{execSync as O,spawnSync as k}from"node:child_process";import{fileURLToPath as v}from"node:url";import{bold as b,cyan as C,gray as w,green as y,yellow as x,red as j}from"../ui/output.mjs";import{detectAvailableProviders as A}from"../ai/providerRouter.mjs";function c(n,o){try{const e=o();return{label:n,...e}}catch(e){return{label:n,status:"error",message:e.message,fix:null}}}function a(n,o){return{status:"pass",message:n,detail:o||null,fix:null}}function f(n,o){return{status:"warn",message:n,detail:null,fix:o||null}}function g(n,o){return{status:"fail",message:n,detail:null,fix:o||null}}function _(){const n=process.version,o=parseInt(n.slice(1).split(".")[0],10);return o>=20?a(`Node.js ${n}`,"Node 20+ recommended"):o>=18?a(`Node.js ${n}`):g(`Node.js ${n} \u2014 infernoflow requires Node 18+`,"Install Node 20 from nodejs.org")}function P(){try{const n=k("infernoflow",["--version"],{encoding:"utf8",timeout:5e3,shell:process.platform==="win32"});return n.status===0?a(`infernoflow v${n.stdout.trim()} installed`):a("infernoflow CLI on PATH (version probe failed but doctor itself ran)")}catch{return a("infernoflow CLI on PATH (version probe threw but doctor itself ran)")}}function E(n){try{return O("git rev-parse --git-dir",{cwd:n,stdio:"ignore"}),a("Git repository detected")}catch{return g("Not a git repository","git init && git add . && git commit -m 'init'")}}function I(n){const o=l.join(n,"inferno");return s.existsSync(o)?a("inferno/ directory exists"):g("inferno/ not found","infernoflow init")}function T(n){const o=l.join(n,"inferno");if((()=>{try{return JSON.parse(s.readFileSync(l.join(o,"config.json"),"utf8"))}catch{return{}}})().mode==="memory"){const i=l.join(o,"sessions.jsonl");if(!s.existsSync(i))return a("Memory mode \u2014 sessions.jsonl will be created on first log");let t=0;try{t=s.readFileSync(i,"utf8").split(`
|
|
2
|
+
`).filter(Boolean).length}catch{}return a(`Memory mode \u2014 ${t} session entr${t===1?"y":"ies"}`)}for(const i of["contract.json","capabilities.json"]){const t=l.join(o,i);if(s.existsSync(t))try{const u=(JSON.parse(s.readFileSync(t,"utf8")).capabilities||[]).length;return a(`${i} valid \u2014 ${u} capabilities`)}catch{return g(`${i} contains invalid JSON`,`Fix the JSON syntax in inferno/${i}`)}}return g("No contract.json/capabilities.json (and not in memory mode)","infernoflow init or infernoflow init --mode full")}function S(n){try{return JSON.parse(s.readFileSync(l.join(n,"inferno","config.json"),"utf8")).mode==="memory"}catch{return!1}}function G(n){if(S(n))return{status:"info",message:"n/a in memory mode",detail:null,fix:null};const o=l.join(n,"inferno","scenarios");if(!s.existsSync(o))return f("No scenarios/ directory","infernoflow init");const e=s.readdirSync(o).filter(i=>i.endsWith(".json"));return e.length?a(`${e.length} scenario file${e.length!==1?"s":""} found`):f("scenarios/ is empty","Add scenario files or run infernoflow suggest")}function F(n){if(S(n))return{status:"info",message:"n/a in memory mode",detail:null,fix:null};const o=l.join(n,"inferno","CHANGELOG.md");return s.existsSync(o)?a("inferno/CHANGELOG.md exists"):f("No inferno/CHANGELOG.md","infernoflow init")}function M(n){if(S(n))return{status:"info",message:"n/a in memory mode (CLAUDE.md is auto-maintained)",detail:null,fix:null};const o=l.join(n,"inferno","CONTEXT.md");if(!s.existsSync(o))return f("No CONTEXT.md generated","infernoflow context");const e=(Date.now()-s.statSync(o).mtimeMs)/(1e3*60*60*24);return e>7?f(`CONTEXT.md is ${Math.round(e)} days old \u2014 may be stale`,"infernoflow context"):a(`CONTEXT.md present (${Math.round(e)}d old)`)}function D(n){const o=l.join(n,".git","hooks"),e=l.join(o,"post-commit"),i=l.join(o,"pre-push"),t=s.existsSync(e)&&s.readFileSync(e,"utf8").includes("infernoflow"),r=s.existsSync(i)&&s.readFileSync(i,"utf8").includes("infernoflow");return t&&r?a("Git hooks installed (post-commit + pre-push)"):f(t||r?"Partial git hooks installed":"Git hooks not installed","infernoflow setup --yes")}function L(n){const o=[l.join(n,".cursor","mcp.json"),l.join(n,".mcp.json"),l.join(h.homedir(),".cursor","mcp.json"),l.join(h.homedir(),"Library","Application Support","Claude","claude_desktop_config.json"),l.join(h.homedir(),"AppData","Roaming","Claude","claude_desktop_config.json")];for(const e of o)if(s.existsSync(e))try{const i=JSON.parse(s.readFileSync(e,"utf8")),t=i.mcpServers||i.mcp_servers||{};if(Object.keys(t).some(r=>r.toLowerCase().includes("inferno")))return a(`MCP server configured in ${l.basename(e)}`)}catch{}return f("MCP server not configured","infernoflow setup --yes (adds to Cursor/Claude config)")}function R(n){const o=A(n),e=Object.entries(o).filter(([,i])=>i).map(([i])=>i);return e.length?a(`AI provider${e.length!==1?"s":""}: ${e.join(", ")}`):f("No AI provider configured",`Set ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_AI_API_KEY, or OPENROUTER_API_KEY
|
|
3
3
|
Or install Ollama (ollama.com) for free local AI
|
|
4
|
-
Or use VS Code with GitHub Copilot (zero config)`)}async function J(){return new Promise(n=>{const o=
|
|
4
|
+
Or use VS Code with GitHub Copilot (zero config)`)}async function J(){return new Promise(n=>{const o=N.get({hostname:"localhost",port:11434,path:"/api/tags",timeout:1500},e=>{n(a("Ollama running on localhost:11434"))});o.on("error",()=>n({status:"info",message:"Ollama not running (optional)",fix:"ollama serve",detail:null})),o.on("timeout",()=>{o.destroy(),n({status:"info",message:"Ollama not running (optional)",fix:null,detail:null})})})}function H(){const n=l.join(h.homedir(),".infernoflow","credentials.json");if(!s.existsSync(n))return{status:"info",message:"Not logged in to cloud (optional)",fix:"infernoflow login",detail:null};try{const o=JSON.parse(s.readFileSync(n,"utf8")),e=o.user?.login||o.user?.name||o.user?.email||"unknown";if(o.mode==="supabase"&&o.access_token){if(o.expires_at){const i=new Date(o.expires_at).getTime();if(Date.now()>i)return f(`JWT expired for ${e} \u2014 refresh on next log will retry`,"infernoflow login")}return a(`Authenticated as ${e} (Supabase JWT \u2014 auth.uid() writes)`)}return o.mode==="device-flow"&&o.github_access_token?{status:"info",message:`Identity-only as ${e} (device flow \u2014 anon-mode writes)`,fix:"infernoflow login (without --device-flow, for full auth)",detail:null}:o.access_token?f(`Legacy login for ${e} \u2014 re-run for authenticated cloud writes`,"infernoflow logout && infernoflow login"):{status:"info",message:"Credentials file present but no recognised token",fix:"infernoflow logout && infernoflow login",detail:null}}catch{return f("Credentials file unreadable","infernoflow logout && infernoflow login")}}function X(){try{const n=v(import.meta.url),o=l.resolve(l.dirname(n),"..","..","bin","infernoflow.mjs");if(!s.existsSync(o))return{status:"info",message:"bin/infernoflow.mjs not found from doctor location",fix:null,detail:null};const i=[...s.readFileSync(o,"utf8").matchAll(/import\("\.\.\/lib\/(commands\/[^"]+|telemetry\.mjs)"\)/g)],t=[],r=l.resolve(l.dirname(o),"..");for(const u of i){const m=u[1],$=l.join(r,"lib",m);s.existsSync($)||t.push(m)}return t.length?g(`${t.length} routed command(s) missing module files: ${t.slice(0,3).join(", ")}${t.length>3?"\u2026":""}`,"Restore the missing files or remove their entries from bin/infernoflow.mjs"):a(`All ${i.length} routed commands resolve to real files`)}catch(n){return{status:"info",message:`Router integrity check skipped: ${n.message}`,fix:null,detail:null}}}function K(n){const o=l.join(n,".gitignore");if(!s.existsSync(o))return{status:"info",message:".gitignore not found",fix:null,detail:null};const e=s.readFileSync(o,"utf8");return/^(?:\*\*\/)?node_modules\/?$/m.test(e)?a(".gitignore excludes node_modules"):f(".gitignore does not exclude node_modules","Add 'node_modules/' (and '**/node_modules/') to .gitignore")}function W(n,o){const e=n.filter(t=>t.status==="warn"&&t.fix),i=[];for(const t of e){const r=t.fix;if(r.startsWith("infernoflow ")){const u=r.slice(12).split(" ");k("infernoflow",u,{cwd:o,encoding:"utf8",timeout:3e4}).status===0&&i.push(t.label)}}return i}function Y(n){return n==="pass"?y("\u2714"):n==="warn"?x("\u26A0"):n==="fail"?j("\u2717"):w("\xB7")}function U(n,o){const e={pass:0,warn:0,fail:0,info:0,error:0};for(const r of n)e[r.status]=(e[r.status]||0)+1;console.log(),console.log(` ${b("\u{1F525} infernoflow doctor")}`),console.log();const i=Math.max(...n.map(r=>r.label.length))+2;for(const r of n)console.log(` ${Y(r.status)} ${b(r.label.padEnd(i))} ${r.message}`),r.detail&&console.log(` ${" ".repeat(i)} ${w(r.detail)}`),r.fix&&(r.status==="warn"||r.status==="fail")&&console.log(` ${" ".repeat(i)} ${C("fix:")} ${w(r.fix)}`);console.log();const t=e.fail>0?j("issues found"):e.warn>0?x("warnings"):y("all good");console.log(` ${t} \u2014 ${y(String(e.pass))} pass \xB7 ${x(String(e.warn))} warn \xB7 ${j(String(e.fail))} fail (${o}ms)`),console.log(),(e.warn>0||e.fail>0)&&(console.log(` Run ${C("infernoflow doctor --fix")} to auto-fix warnings`),console.log())}async function q(n){const o=n.slice(1),e=o.includes("--json"),i=o.includes("--fix"),t=process.cwd(),r=Date.now(),u=[c("Node.js version",()=>_()),c("infernoflow CLI",()=>P()),c("Git repository",()=>E(t)),c("inferno/ directory",()=>I(t)),c("Contract / mode",()=>T(t)),c("Scenarios",()=>G(t)),c("Changelog",()=>F(t)),c("CONTEXT.md",()=>M(t)),c("Git hooks",()=>D(t)),c("MCP server",()=>L(t)),c("AI providers",()=>R(t)),c("Cloud sync",()=>H()),c(".gitignore",()=>K(t)),c("Router integrity",()=>X()),await J().then(d=>({label:"Ollama (local AI)",...d}))],m=Date.now()-r;if(i){const d=W(u,t);if(d.length)return e||(console.log(),d.forEach(p=>console.log(` ${y("\u2714")} Fixed: ${p}`)),console.log()),q(["doctor","--json"])}if(e){const d={pass:0,warn:0,fail:0,info:0};u.forEach(p=>d[p.status]=(d[p.status]||0)+1),console.log(JSON.stringify({ok:d.fail===0,counts:d,results:u,elapsed:m}));return}U(u,m),u.some(d=>d.status==="fail")&&process.exit(1)}export{q as doctorCommand};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "infernoflow",
|
|
3
|
-
"version": "0.42.
|
|
3
|
+
"version": "0.42.8",
|
|
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": {
|