infernoflow 0.44.11 → 0.44.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/lib/amp/io.mjs +1 -1
- package/dist/lib/ruleFiles.mjs +11 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -101,7 +101,7 @@ Cursor and Claude Code get the same capability through the MCP server the CLI in
|
|
|
101
101
|
|
|
102
102
|
## Keeping it lean: token budget + rotation
|
|
103
103
|
|
|
104
|
-
The injected memory block is paid for on every AI turn (and twice when a tool loads both `CLAUDE.md` and `copilot-instructions.md`). infernoflow ships lean defaults — 4 entries, 5 commits, 200-char
|
|
104
|
+
The injected memory block is paid for on every AI turn (and twice when a tool loads both `CLAUDE.md` and `copilot-instructions.md`). infernoflow ships lean defaults — 4 entries, 5 commits, 200-char truncation, and a **compact ~3-line protocol** (the full trigger table is redundant with the `amp_*` tool descriptions, so it's off by default: ~430 tokens/file/turn saved). Tune further in `.ai-memory/amp.json`:
|
|
105
105
|
|
|
106
106
|
```jsonc
|
|
107
107
|
"config": {
|
|
@@ -110,7 +110,7 @@ The injected memory block is paid for on every AI turn (and twice when a tool lo
|
|
|
110
110
|
"maxCommits": 5, // git commits injected
|
|
111
111
|
"maxEntryChars": 200, // per-entry truncation
|
|
112
112
|
"targets": ["CLAUDE.md", ".cursorrules"], // drop a file from the list and its stale block is stripped automatically
|
|
113
|
-
"
|
|
113
|
+
"protocolStyle": "compact" // "compact" (default) · "full" (restore the trigger table) · "off"
|
|
114
114
|
},
|
|
115
115
|
"rotation": {
|
|
116
116
|
"archiveAfterDays": 30,
|
package/dist/lib/amp/io.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import*as s from"node:fs";import*as J from"node:os";import*as r from"node:path";
|
|
|
8
8
|
`)+`
|
|
9
9
|
`:"","utf8"),f.files.push(p)):v>0&&f.files.push(p)}if(!t.dryRun&&l&&h.size>0)for(const[p,b]of h)try{s.mkdirSync(r.dirname(p),{recursive:!0}),s.appendFileSync(p,b.join(`
|
|
10
10
|
`)+`
|
|
11
|
-
`,"utf8")}catch{}return f}function X(e){const{config:t}=m(e);try{return JSON.parse(s.readFileSync(t,"utf8"))}catch{return null}}function ae(e,t={}){x(e);const{config:o}=m(e,{forWrite:!0});if(s.existsSync(o))return!1;const n={amp:T,project:t.project||r.basename(e),stack:t.stack||{},config:{autoCapture:!0,maxEntries:1e3,rotationStrategy:"archive",inject:["all"],injection:{maxEntries:4,maxCommits:5,maxEntryChars:200},rotation:{archiveAfterDays:30,archivableTypes:["note","attempt","detection"],auto:!1},...t.config||{}}};return s.writeFileSync(o,JSON.stringify(n,null,2)+`
|
|
11
|
+
`,"utf8")}catch{}return f}function X(e){const{config:t}=m(e);try{return JSON.parse(s.readFileSync(t,"utf8"))}catch{return null}}function ae(e,t={}){x(e);const{config:o}=m(e,{forWrite:!0});if(s.existsSync(o))return!1;const n={amp:T,project:t.project||r.basename(e),stack:t.stack||{},config:{autoCapture:!0,maxEntries:1e3,rotationStrategy:"archive",inject:["all"],injection:{maxEntries:4,maxCommits:5,maxEntryChars:200,protocolStyle:"compact"},rotation:{archiveAfterDays:30,archivableTypes:["note","attempt","detection"],auto:!1},...t.config||{}}};return s.writeFileSync(o,JSON.stringify(n,null,2)+`
|
|
12
12
|
`,"utf8"),!0}function fe(e,t={}){x(e);const{config:o}=m(e,{forWrite:!0});let n={};try{n=JSON.parse(s.readFileSync(o,"utf8"))}catch{n={}}(!n||typeof n!="object")&&(n={}),n.amp||(n.amp=T),n.project||(n.project=r.basename(e)),(!n.config||typeof n.config!="object")&&(n.config={});const c=n.config.injection&&typeof n.config.injection=="object"?n.config.injection:{};return n.config.injection={...c,...t},s.writeFileSync(o,JSON.stringify(n,null,2)+`
|
|
13
13
|
`,"utf8"),n.config.injection}function le(e){const t=r.join(e,"inferno"),o=r.join(t,"sessions.jsonl");if(!s.existsSync(o))return{migrated:0,reason:"no legacy sessions.jsonl"};const n=r.join(e,".ai-memory"),c=r.join(n,"sessions.jsonl");if(s.existsSync(c))return{migrated:0,reason:".ai-memory/sessions.jsonl already exists"};x(e);const l=s.readFileSync(o,"utf8").split(`
|
|
14
14
|
`).filter(Boolean);let a=0;for(const i of l)try{const u=JSON.parse(i),f=N(u);s.appendFileSync(c,JSON.stringify(f)+`
|
package/dist/lib/ruleFiles.mjs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import*as
|
|
1
|
+
import*as u from"node:fs";import*as y from"node:path";import{execSync as j}from"node:child_process";import{readEntries as v,readConfig as x}from"./amp/io.mjs";const g="<!-- infernoflow:start -->",m="<!-- infernoflow:end -->",w=[".cursorrules","CLAUDE.md",y.join(".github","copilot-instructions.md")],k=4,C=5,S=200;function A(e){const o=e&&e.config||{},t=o.injection&&typeof o.injection=="object"?o.injection:{},n=Array.isArray(o.inject)&&!o.inject.includes("all")?o.inject:null,r=["full","compact","off"].includes(t.protocolStyle)?t.protocolStyle:"compact",s=t.includeProtocol===!1?"off":r;return{maxEntries:Number.isInteger(t.maxEntries)&&t.maxEntries>=0?t.maxEntries:k,maxCommits:Number.isInteger(t.maxCommits)&&t.maxCommits>=0?t.maxCommits:C,maxEntryChars:Number.isInteger(t.maxEntryChars)&&t.maxEntryChars>0?t.maxEntryChars:S,targets:Array.isArray(t.targets)&&t.targets.length?t.targets:n||w,protocolStyle:s,includeProtocol:s!=="off"}}function T(e){if(!u.existsSync(e))return{stripped:!1};const o=u.readFileSync(e,"utf8"),t=o.indexOf(g),n=o.indexOf(m);if(t===-1||n===-1||n<=t)return{stripped:!1};const r=o.slice(0,t).replace(/\s+$/,""),s=o.slice(n+m.length).replace(/^\s+/,""),i=r&&s?r+`
|
|
2
2
|
|
|
3
|
-
`+s:r||s;return
|
|
4
|
-
`:"","utf8"),{stripped:!0}}function E(){
|
|
5
|
-
`)}function
|
|
6
|
-
`)}function
|
|
3
|
+
`+s:r||s;return u.writeFileSync(e,i?i.replace(/\s*$/,"")+`
|
|
4
|
+
`:"","utf8"),{stripped:!0}}function E(e="compact"){return e==="off"?"":e==="full"?M():F()}function F(){return["### Memory protocol","",'Use the `amp_write` and `amp_bookmark` MCP tools **proactively** (without being asked): log a one-sentence entry when the user hits frustration (`!!` / "not working" / "retry" \u2192 `attempt`), a `decision` is made, or you learn a non-obvious `gotcha`; drop a bookmark on "bookmark this". Don\'t log what\'s re-derivable from the code. The full trigger list and field shapes are in the tool descriptions \u2014 if the `amp_*` tools aren\'t visible, load them first (Claude Code: `ToolSearch` with query `infernoflow`).'].join(`
|
|
5
|
+
`)}function M(){const e=[];return e.push("### Memory protocol \u2014 capture as you go"),e.push(""),e.push("You have the `amp_write` MCP tool. **Use it proactively** (without the user asking) whenever you detect any of these moments. Capture the lesson in one sentence so the next AI session inherits it."),e.push(""),e.push("**If `amp_write` is not in your visible tools**, your client uses deferred MCP loading \u2014 call your tool-discovery mechanism (Claude Code: `ToolSearch` with query `infernoflow`) to load the `amp_*` tools BEFORE proceeding. Don't silently skip the protocol because the tool isn't visible yet."),e.push(""),e.push("| When you see\u2026 | Call `amp_write` with\u2026 |"),e.push("|---|---|"),e.push("| User types `!!`, `!!!`, `not working`, `still broken`, `no change`, `same error`, `retry` / `retrying`, repeated frustration | `{type: 'attempt', msg: '<one-line: what was tried and how it failed>', file: '<active file>'}` |"),e.push("| You generate a plan, numbered steps, or `Plan:` / `Approach:` block | `{type: 'note', msg: 'Plan: <one-line summary>', file: '<focus file>'}` |"),e.push("| User picks X over Y, decides an architectural approach, or you both agree on a direction | `{type: 'decision', msg: '<X> chosen over <Y> because <reason>', file: '<file>'}` |"),e.push("| Discovered non-obvious behavior, API quirk, or `aha \u2014 turns out\u2026` moment | `{type: 'gotcha', msg: '<one-line>', file: '<file>', line: <line if known>}` |"),e.push("| User says `switching` / `back to X`, or ~30 min passed without logging | `{type: 'note', msg: 'Session snapshot \u2014 worked on: <X>. Open threads: <Y>.'}` |"),e.push(""),e.push("**Do NOT log:** typos, syntax errors, obvious bugs visible in the code, anything the next AI can infer by re-reading the file."),e.push("**Do log:** anything that taught you something non-derivable from code."),e.push("**Be concise:** entries should be one sentence. Future-you will skim them."),e.push("**Bookmark resume points:** when the user says `bookmark this` / `mark this point`, or the context window is filling up while work is mid-flight, call `amp_bookmark` with a short `label` (omit `note` to auto-capture the session transcript) \u2014 so they can jump back to that exact point in the next session."),e.push("**When you use a \u{1F525} memory entry in your reply, briefly cite it** \u2014 e.g. `\u{1F525} (from infernoflow memory) gotcha at src/api.js:42: ...` \u2014 so the user can see which guidance came from project memory vs. your own inference."),e.join(`
|
|
6
|
+
`)}function D(e="compact"){const o=E(e);return[g,"<!-- Auto-managed by infernoflow. Don't edit between these markers. -->","## Project memory (infernoflow)","",...o?[o,""]:[],'_No entries yet. They\'ll appear here as you and your AI tools log them \u2014 run `infernoflow log "..."` or call `amp_write` from any MCP-aware AI._',m].join(`
|
|
7
|
+
`)}function R(e){const o=e.indexOf("<!-- AMP:START -->"),t=e.indexOf("<!-- AMP:END -->");if(o===-1||t===-1||t<=o)return e;const n=e.slice(0,o).replace(/\s+$/,""),r=e.slice(t+16).replace(/^\s+/,"");return(n?n+(r?`
|
|
7
8
|
|
|
8
|
-
`:""):"")+r}function
|
|
9
|
-
`,"utf8"),{created:!0,updated:!1};let
|
|
9
|
+
`:""):"")+r}function I(e,o){const t=y.dirname(e);if(u.existsSync(t)||u.mkdirSync(t,{recursive:!0}),!u.existsSync(e))return u.writeFileSync(e,o+`
|
|
10
|
+
`,"utf8"),{created:!0,updated:!1};let n=u.readFileSync(e,"utf8");n=R(n);const r=n.indexOf(g),s=n.indexOf(m);if(r===-1||s===-1){const d=o+`
|
|
10
11
|
|
|
11
|
-
`+
|
|
12
|
-
`).filter(Boolean).map(
|
|
13
|
-
`)}function
|
|
12
|
+
`+n;return u.writeFileSync(e,d,"utf8"),{created:!1,updated:!0}}const i=n.slice(0,r),p=n.slice(s+m.length),l=i+o+p;return l===n?{created:!1,updated:!1}:(u.writeFileSync(e,l,"utf8"),{created:!1,updated:!0})}function B(e){const{protocolStyle:o}=A(x(e)),t=D(o),n=[];for(const r of w){const s=y.join(e,r);try{const i=I(s,t);n.push({rel:r,...i})}catch(i){n.push({rel:r,error:i.message})}}return n}function N(e){return v(e)}function P(e,o=10){try{return j(`git log --pretty=format:"%h%x09%ad%x09%s" --date=short -n ${o}`,{cwd:e,encoding:"utf8",stdio:["ignore","pipe","ignore"]}).split(`
|
|
13
|
+
`).filter(Boolean).map(n=>{const[r,s,i]=n.split(" ");return{hash:(r||"").slice(0,7),date:s||"",subject:i||""}})}catch{return[]}}function O(e,o={}){const{maxEntries:t=k,maxCommits:n=C,maxEntryChars:r=S,protocolStyle:s="compact",includeProtocol:i=!0}=o,p=N(e),l=P(e,n);p.sort((a,h)=>{const f=typeof a.ts=="number"?a.ts:Date.parse(a.ts||0);return(typeof h.ts=="number"?h.ts:Date.parse(h.ts||0))-f});const d=p.slice(0,t),_={gotcha:"\u26A0",decision:"\u2713",attempt:"\u2717",note:"\xB7",detection:"\u25CB",pattern:"\u25C7"},c=[];if(c.push(g),c.push("<!-- Auto-managed by infernoflow. Don't edit between these markers. -->"),c.push("## Project memory (infernoflow)"),c.push(""),i&&s!=="off"&&(c.push(E(s)),c.push("")),l.length>0){c.push("### Recent commits");for(const a of l)c.push(`- \`${a.hash}\` _${a.date}_ ${a.subject}`);c.push("")}if(d.length>0){c.push("### Recent memory");for(const a of d){const h=a.file?` (\`${a.file}${a.line?":"+a.line:""}\`)`:"",f=(a.msg||a.summary||"").replace(/\n/g," "),b=r&&f.length>r?f.slice(0,r).trimEnd()+"\u2026":f;c.push(`- \u{1F525} ${_[a.type]||"\xB7"} **${a.type||"note"}**${h}: ${b}`)}c.push("")}return p.length===0&&l.length===0&&c.push('_No entries yet. They\'ll appear here as you and your AI tools log them \u2014 run `infernoflow log "..."` or call `amp_write` from any MCP-aware AI._'),c.push(m),c.join(`
|
|
14
|
+
`)}function L(e){const o=A(x(e)),t=O(e,o),n=i=>String(i).replace(/\\/g,"/"),r=new Set(o.targets.map(n)),s=[];for(const i of w){const p=y.join(e,i);try{if(r.has(n(i))){const l=I(p,t);s.push({rel:i,...l})}else T(p).stripped&&s.push({rel:i,stripped:!0})}catch(l){s.push({rel:i,error:l.message})}}return s}export{L as refreshRuleFilesFromMemory,A as resolveInjectionSettings,B as writeInitRuleFiles};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "infernoflow",
|
|
3
|
-
"version": "0.44.
|
|
3
|
+
"version": "0.44.12",
|
|
4
4
|
"description": "Persistent memory for AI coding sessions — captures what agents can't infer from code alone. Works with Copilot, Cursor, Claude, and Windsurf.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|