infernoflow 0.40.1 → 0.40.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/lib/commands/watch.mjs +6 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog — infernoflow
|
|
2
2
|
|
|
3
|
+
## 0.40.3 — 2026-05-02
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **`infernoflow watch` heuristic prompts (Plan Part 4 Level 2)** — the watcher now surfaces "log this?" tips when it spots patterns that usually indicate gotchas, on top of the existing debounced auto-suggest:
|
|
7
|
+
- Same file edited 5/12/25 times in a session → "stuck on something?" with a gotcha-log hint
|
|
8
|
+
- Dependency manifest changed (package.json, Cargo.toml, go.mod, requirements.txt, Pipfile, Gemfile, composer.json, lockfiles, etc. — 18 files total) → suggests logging the decision
|
|
9
|
+
- Test file deleted (under `tests/`, `test/`, `__tests__/`, `spec/`, or matching `*.test.*` / `*.spec.*`) → suggests logging why
|
|
10
|
+
- **`--no-tips` flag** — disables the heuristic prompts while keeping the auto-suggest behaviour. `--silent` continues to disable all output.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- **`watch` default directories** include `tests`, `test`, `__tests__`, `spec` so test-file removal is detected without manual `infernoflow watch tests` invocations.
|
|
14
|
+
- **Non-recursive project-root watcher** added under the hood so dependency-manifest changes are caught regardless of which subdirectory was passed to `watch`.
|
|
15
|
+
- **Per-file 250ms debounce** on event handling — fs.watch fires multiple events per save on most platforms; the watcher now collapses them so edit-count thresholds and dependency-tip prompts don't double-fire.
|
|
16
|
+
|
|
3
17
|
## 0.40.1 — 2026-05-02
|
|
4
18
|
|
|
5
19
|
### Fixed
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import*as
|
|
2
|
-
|
|
3
|
-
${d(new Date().
|
|
1
|
+
import*as p from"node:fs";import*as t from"node:path";import{fileURLToPath as D}from"node:url";import{spawnSync as I}from"node:child_process";import{warn as x,bold as y,cyan as N,gray as d,green as L,yellow as O}from"../ui/output.mjs";const H=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".py",".go",".java",".cs",".rb",".swift"]),ie=new Set(["node_modules",".git","dist","build","out",".next",".angular","vendor","coverage","__pycache__"]);function M(e){const i=["src","lib","app","pages","components","server","api","tests","test","__tests__","spec"].filter(o=>p.existsSync(t.join(e,o)));return i.length?i.map(o=>t.join(e,o)):[e]}function U(e){return H.has(t.extname(e).toLowerCase())}const G=new Set(["package.json","package-lock.json","yarn.lock","pnpm-lock.yaml","bun.lockb","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","poetry.lock","Cargo.toml","Cargo.lock","go.mod","go.sum","Gemfile","Gemfile.lock","composer.json","composer.lock"]),J=/(?:^|[\\/])(?:test|tests|spec|__tests__)[\\/].+\.(?:mjs|cjs|js|jsx|ts|tsx|py|go|rb|java|cs)$|\.(?:test|spec)\.(?:mjs|cjs|js|jsx|ts|tsx|py|go|rb|java|cs)$/i;function X(e){return G.has(t.basename(e))}function Y(e){return J.test(e)}function k(e,n){return`
|
|
2
|
+
${O("\u26A0")} ${e}
|
|
3
|
+
${d("\u2192")} ${N(n)}`}const R=new Map,j=new Map,C=250,q=[5,12,25];function B(e){const n=Date.now(),i=j.get(e)||0;if(n-i<C)return null;j.set(e,n);const o=(R.get(e)||0)+1;return R.set(e,o),o}function K(e,n,i){if(!q.includes(n))return null;const o=t.relative(i,e);return k(`${y(o)} edited ${n}\xD7 this session \u2014 stuck on something?`,`infernoflow log "<what's tripping you up>" --type gotcha`)}function Z(e,n){const i=t.relative(n,e);return k(`dependency manifest changed: ${y(i)}`,'infernoflow log "<switched from X to Y because Z>" --type decision')}function z(e,n){const i=t.relative(n,e);return k(`test file removed: ${y(i)} \u2014 was it failing?`,'infernoflow log "<why the test was removed>" --type attempt --result failed')}function Q(e,n){const i=t.join(n,"capability-map.json");if(!p.existsSync(i))return{relevant:!0,reason:"no cap-map \u2014 suggesting broadly"};let o;try{o=JSON.parse(p.readFileSync(i,"utf8"))}catch{return{relevant:!0,reason:"cap-map unreadable"}}const u=[];for(const w of e){const g=t.relative(process.cwd(),w).replace(/\\/g,"/");for(const[f,a]of Object.entries(o))g.startsWith(f.replace(/\\/g,"/"))&&u.push(...a)}return u.length>0?{relevant:!0,reason:`touches: ${[...new Set(u)].slice(0,3).join(", ")}`}:{relevant:!1,reason:"no mapped capabilities affected"}}function V(e,n,i,o,u){const g=`code changes in ${e.map(f=>t.basename(f,t.extname(f))).slice(0,3).join(", ")}`;if(u||process.stdout.write(` ${O("\u27F3")} suggesting from ${y(String(e.length))} changed file${e.length!==1?"s":""}\u2026 `),o){u||console.log(d("(dry run)"));return}try{I(process.execPath,[t.join(t.dirname(t.dirname(t.dirname(D(import.meta.url)))),"bin","infernoflow.mjs"),"suggest",g,"--json"],{cwd:n,encoding:"utf8",timeout:3e4,stdio:"ignore"}),u||console.log(L("done"))}catch{u||console.log(d("skipped (no changes)"))}try{const a=I(process.execPath,[t.join(t.dirname(t.dirname(t.dirname(D(import.meta.url)))),"bin","infernoflow.mjs"),"check","--json"],{cwd:n,encoding:"utf8",timeout:15e3}).stdout?.trim();if(a){const h=JSON.parse(a);if(h.status==="error"||h.status==="warning")p.writeFileSync(t.join(i,"WATCH.log"),a+`
|
|
4
|
+
`),u||x("Contract issues detected \u2014 see inferno/WATCH.log");else{const S=t.join(i,"WATCH.log");p.existsSync(S)&&p.unlinkSync(S)}}}catch{}}async function re(e){const n=e.slice(1),i=n.includes("--dry-run"),o=n.includes("--silent"),u=n.includes("--no-tips"),w=!o&&!u,g=n.indexOf("--interval"),f=((g!==-1?parseFloat(n[g+1]):3)||3)*1e3,a=process.cwd(),h=t.join(a,"inferno");p.existsSync(h)||(x("inferno/ not found. Run: infernoflow init"),process.exit(1));const S=n.filter(r=>!r.startsWith("-")&&r!==String(n[g+1])),$=(S.length?S.map(r=>t.resolve(a,r)):M(a)).filter(r=>p.existsSync(r));$.length||(x("No valid directories to watch."),process.exit(1)),o||(console.log(),console.log(` ${y("\u{1F525} infernoflow watch")} ${d("(Ctrl+C to stop)")}`),console.log(),$.forEach(r=>console.log(` ${N("watching")} ${d(t.relative(a,r)||".")}`)),console.log(` ${d(`debounce: ${f/1e3}s`)}`),console.log());let b=null;const T=new Set,F=new Set,m=[],E=(r,c)=>{if(w&&X(c)&&p.existsSync(c)){const s="dep:"+c,l=Date.now();l-(j.get(s)||0)>=C&&(j.set(s,l),m.push(Z(c,a)))}if(w&&Y(c))if(r==="rename"&&!p.existsSync(c)){const s="del:"+c,l=Date.now();l-(j.get(s)||0)>=C&&(j.set(s,l),m.push(z(c,a)))}else p.existsSync(c)&&F.add(c);if(!U(c)){if(m.length&&!o)for(const s of m.splice(0))console.log(s);return}if(T.add(c),w){const s=B(c);if(s!==null){const l=K(c,s,a);l&&m.push(l)}}b&&clearTimeout(b),b=setTimeout(()=>{const s=Array.from(T);if(T.clear(),!o){const _=s.map(A=>t.relative(a,A)).slice(0,3).join(", ");process.stdout.write(`
|
|
5
|
+
${d(new Date().toLocaleTimeString())} ${y(_)}${s.length>3?` +${s.length-3} more`:""} `)}const{relevant:l,reason:W}=Q(s,h);if(l?V(s,a,h,i,o):o||console.log(d(`skip (${W})`)),m.length&&!o)for(const _ of m.splice(0))console.log(_)},f)},v=[];for(const r of $)try{const c=p.watch(r,{recursive:!0},(s,l)=>{l&&E(s,t.join(r,l))});v.push(c)}catch(c){o||x(`Cannot watch ${r}: ${c.message}`)}try{const r=p.watch(a,{recursive:!1},(c,s)=>{s&&E(c,t.join(a,s))});v.push(r)}catch{}v.length||(x("No directories could be watched."),process.exit(1)),process.on("SIGINT",()=>{v.forEach(r=>r.close()),o||(console.log(`
|
|
4
6
|
|
|
5
|
-
Stopped.`),console.log()),process.exit(0)}),await new Promise(()=>{})}export{
|
|
7
|
+
Stopped.`),console.log()),process.exit(0)}),await new Promise(()=>{})}export{re as watchCommand};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "infernoflow",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.3",
|
|
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": {
|