infernoflow 0.7.0 → 0.8.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.
@@ -23,6 +23,7 @@ const HELP = `
23
23
  --working "..." What you are building right now
24
24
  --decision "..." Record a decision or note
25
25
  --show Print context without writing file
26
+ --copy, -c Copy context to clipboard instantly
26
27
  --reset Clear all stored state
27
28
 
28
29
  ${bold("Typical workflow:")}
@@ -1,230 +1,139 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
+ import { execSync } from "node:child_process";
3
4
  import { bold, gray, cyan, red, green, yellow } from "../ui/output.mjs";
4
5
 
5
- const INFERNO_DIR = "inferno";
6
- const CONTEXT_FILE = path.join(INFERNO_DIR, "CONTEXT.md");
7
- const STATE_FILE = path.join(INFERNO_DIR, "context-state.json");
8
-
9
- function readJSON(f) {
10
- try {
11
- return JSON.parse(fs.readFileSync(f, "utf8"));
12
- } catch {
13
- return null;
14
- }
15
- }
16
- function readFile(f) {
6
+ function copyToClipboard(text) {
17
7
  try {
18
- return fs.readFileSync(f, "utf8");
19
- } catch {
20
- return null;
21
- }
22
- }
23
- function loadState() {
24
- const r = readFile(STATE_FILE);
25
- if (!r) return {};
26
- try {
27
- return JSON.parse(r);
28
- } catch {
29
- return {};
30
- }
31
- }
32
- function saveState(s) {
33
- fs.writeFileSync(STATE_FILE, JSON.stringify(s, null, 2), "utf8");
34
- }
35
- function formatDate(iso) {
36
- if (!iso) return "unknown";
37
- return new Date(iso).toLocaleDateString("en-GB", {
38
- day: "2-digit",
39
- month: "short",
40
- year: "numeric",
41
- });
8
+ const p = process.platform;
9
+ if (p === "win32") execSync("clip", { input: text });
10
+ else if (p === "darwin") execSync("pbcopy", { input: text });
11
+ else { try { execSync("xclip -selection clipboard", { input: text }); } catch { execSync("xsel --clipboard --input", { input: text }); } }
12
+ return true;
13
+ } catch { return false; }
42
14
  }
43
- function parseChangelog(txt, max) {
44
- if (!txt) return [];
45
- const entries = [];
46
- let cur = null;
47
- for (const line of txt.split("\n")) {
48
- if (line.startsWith("## ")) {
49
- if (cur && entries.length < max) entries.push(cur);
50
- if (entries.length >= max) break;
51
- cur = { title: line.replace("## ", "").trim(), items: [] };
52
- } else if (cur && line.startsWith("- ")) {
53
- cur.items.push(line.replace("- ", "").trim());
54
- }
15
+
16
+ const INFERNO_DIR = "inferno";
17
+ const CONTEXT_FILE = path.join(INFERNO_DIR, "CONTEXT.md");
18
+ const STATE_FILE = path.join(INFERNO_DIR, "context-state.json");
19
+
20
+ function readJSON(f) { try { return JSON.parse(fs.readFileSync(f,"utf8")); } catch { return null; } }
21
+ function readFile(f) { try { return fs.readFileSync(f,"utf8"); } catch { return null; } }
22
+ function loadState() { const r=readFile(STATE_FILE); if(!r) return {}; try { return JSON.parse(r); } catch { return {}; } }
23
+ function saveState(s) { fs.writeFileSync(STATE_FILE,JSON.stringify(s,null,2),"utf8"); }
24
+ function fmtDate(iso) { if(!iso) return "unknown"; return new Date(iso).toLocaleDateString("en-GB",{day:"2-digit",month:"short",year:"numeric"}); }
25
+ function parseChangelog(txt,max) {
26
+ if(!txt) return [];
27
+ const entries=[]; let cur=null;
28
+ for(const line of txt.split("\n")) {
29
+ if(line.startsWith("## ")) { if(cur&&entries.length<max) entries.push(cur); if(entries.length>=max) break; cur={title:line.replace("## ","").trim(),items:[]}; }
30
+ else if(cur&&line.startsWith("- ")) cur.items.push(line.replace("- ","").trim());
55
31
  }
56
- if (cur && entries.length < max) entries.push(cur);
57
- return entries.filter((e) => e.items.length > 0);
32
+ if(cur&&entries.length<max) entries.push(cur);
33
+ return entries.filter(e=>e.items.length>0);
58
34
  }
59
35
 
60
36
  export async function contextCommand(args) {
61
- const flag = (f) => {
62
- const i = args.indexOf(f);
63
- return i !== -1 && args[i + 1] ? args[i + 1] : null;
64
- };
65
- const has = (f) => args.includes(f);
66
-
67
- const intent = flag("--intent") || flag("-i");
68
- const working = flag("--working") || flag("-w");
37
+ const has = (f) => args.includes(f);
38
+ const flag = (f) => { const i=args.indexOf(f); return i!==-1&&args[i+1]?args[i+1]:null; };
39
+
40
+ const intent = flag("--intent") || flag("-i");
41
+ const working = flag("--working") || flag("-w");
69
42
  const decision = flag("--decision") || flag("-d");
70
- const showOnly = has("--show") || has("-s");
43
+ const showOnly = has("--show") || has("-s");
44
+ const copyFlag = has("--copy") || has("-c");
45
+ const cursorFlag = has("--cursor");
46
+ const copilotFlag = has("--copilot");
47
+ const resetFlag= has("--reset");
71
48
 
72
- console.log("\n " + bold("🔥 infernoflow — context"));
73
- console.log(" " + "─".repeat(50) + "\n");
49
+ console.log("\n "+bold("��� infernoflow — context"));
50
+ console.log(" "+"─".repeat(50)+"\n");
74
51
 
75
- if (!fs.existsSync(INFERNO_DIR)) {
52
+ if(!fs.existsSync(INFERNO_DIR)){
76
53
  console.error(red(" ✘ inferno/ not found"));
77
54
  console.error(gray(" → Run: infernoflow init\n"));
78
55
  process.exit(1);
79
56
  }
80
57
 
81
- const contract = readJSON(path.join(INFERNO_DIR, "contract.json"));
82
- const capabilities = readJSON(path.join(INFERNO_DIR, "capabilities.json"));
83
- const changelog = readFile(path.join(INFERNO_DIR, "CHANGELOG.md"));
58
+ const contract = readJSON(path.join(INFERNO_DIR,"contract.json"));
59
+ const capabilities = readJSON(path.join(INFERNO_DIR,"capabilities.json"));
60
+ const changelog = readFile(path.join(INFERNO_DIR,"CHANGELOG.md"));
84
61
 
85
- if (!contract || !capabilities) {
62
+ if(!contract||!capabilities){
86
63
  console.error(red(" ✘ Missing contract.json or capabilities.json\n"));
87
64
  process.exit(1);
88
65
  }
89
66
 
90
67
  let state = loadState();
91
-
92
- if (intent) {
93
- state.intent = intent;
94
- state.intentUpdated = new Date().toISOString();
95
- console.log(green(' ✔ Intent saved: "' + intent + '"'));
96
- }
97
- if (working) {
98
- state.working = working;
99
- state.workingUpdated = new Date().toISOString();
100
- console.log(green(' Working on: "' + working + '"'));
101
- }
102
- if (decision) {
103
- if (!state.decisions) state.decisions = [];
104
- state.decisions.push({ text: decision, date: new Date().toISOString() });
105
- console.log(green(' Decision recorded: "' + decision + '"'));
106
- }
107
- if (intent || working || decision) saveState(state);
108
-
109
- const capList = capabilities.capabilities || [];
110
- const allInSync = capList.length === (contract.capabilities || []).length;
111
- const recent = parseChangelog(changelog, 3);
112
- const version = String(contract.policyVersion).replace(/^v/i, "");
113
- const now = new Date().toLocaleDateString("en-GB", {
114
- day: "2-digit",
115
- month: "short",
116
- year: "numeric",
117
- });
118
- const syncBadge = allInSync ? "✓ validated" : "⚠ out of sync";
119
-
120
- const capLines = capList
121
- .map((c) => "- **" + c.id + "** — " + c.title)
122
- .join("\n");
123
-
124
- const changelogLines =
125
- recent.length > 0
126
- ? recent
127
- .map(
128
- (e) =>
129
- "### " +
130
- e.title +
131
- "\n" +
132
- e.items.map((i) => " - " + i).join("\n"),
133
- )
134
- .join("\n\n")
135
- : "_No recent changes_";
136
-
137
- const intentLine = state.intent
138
- ? state.intent + " _(" + formatDate(state.intentUpdated) + ")_"
139
- : '_Not set — run: infernoflow context --intent "..._"';
140
-
141
- const workingLine = state.working
142
- ? state.working + " _(" + formatDate(state.workingUpdated) + ")_"
143
- : '_Not set — run: infernoflow context --working "..._"';
144
-
145
- const decLines =
146
- state.decisions && state.decisions.length > 0
147
- ? state.decisions
148
- .slice(-5)
149
- .map((d) => "- " + d.text + " _(" + formatDate(d.date) + ")_")
150
- .join("\n")
151
- : "_No decisions recorded_";
68
+ if(resetFlag){ state={}; console.log(yellow(" ⚠ State reset\n")); }
69
+ if(intent) { state.intent=intent; state.intentUpdated=new Date().toISOString(); console.log(green(' ✔ Intent saved: "'+intent+'"')); }
70
+ if(working) { state.working=working; state.workingUpdated=new Date().toISOString(); console.log(green(' ✔ Working on: "'+working+'"')); }
71
+ if(decision) { if(!state.decisions) state.decisions=[]; state.decisions.push({text:decision,date:new Date().toISOString()}); console.log(green(' ✔ Decision recorded: "'+decision+'"')); }
72
+ if(intent||working||decision) saveState(state);
73
+
74
+ const capList = capabilities.capabilities||[];
75
+ const allInSync = capList.length===(contract.capabilities||[]).length;
76
+ const recent = parseChangelog(changelog,3);
77
+ const version = String(contract.policyVersion).replace(/^v/i,"");
78
+ const now = new Date().toLocaleDateString("en-GB",{day:"2-digit",month:"short",year:"numeric"});
79
+ const syncBadge = allInSync?"✓ validated":"⚠ out of sync";
80
+
81
+ const capLines = capList.map(c=>"- **"+c.id+"** "+c.title).join("\n");
82
+ const chgLines = recent.length>0 ? recent.map(e=>"### "+e.title+"\n"+e.items.map(i=>" - "+i).join("\n")).join("\n\n") : "_No recent changes_";
83
+ const intentLine = state.intent ? state.intent+" _("+fmtDate(state.intentUpdated)+")_" : "_Not set — run: infernoflow context --intent \"...\"_";
84
+ const workingLine = state.working ? state.working+" _("+fmtDate(state.workingUpdated)+")_" : "_Not set — run: infernoflow context --working \"...\"_";
85
+ const decLines = state.decisions&&state.decisions.length>0 ? state.decisions.slice(-5).map(d=>"- "+d.text+" _("+fmtDate(d.date)+")_").join("\n") : "_No decisions recorded_";
152
86
 
153
87
  const md = [
154
- "# Project Context — " + contract.policyId + " v" + version,
155
- "> Generated by infernoflow | " + now + " | " + syncBadge,
156
- "",
157
- "---",
158
- "",
159
- "## What this system does",
160
- "",
161
- capLines,
162
- "",
163
- "---",
164
- "",
165
- "## Recent changes",
166
- "",
167
- changelogLines,
168
- "",
169
- "---",
170
- "",
171
- "## Current state",
172
- "",
173
- "- **Capabilities:** " + capList.length,
174
- "- **Version:** v" + version,
175
- "- **Sync:** " + syncBadge,
176
- "",
177
- "---",
178
- "",
179
- "## What I am working on right now",
180
- "",
181
- workingLine,
182
- "",
183
- "---",
184
- "",
185
- "## Intent — what I want to build next",
186
- "",
187
- intentLine,
188
- "",
189
- "---",
190
- "",
191
- "## Decisions & notes",
192
- "",
193
- decLines,
194
- "",
195
- "---",
196
- "_Paste this block at the start of any new AI session._",
88
+ "# Project Context — "+contract.policyId+" v"+version,
89
+ "> Generated by infernoflow | "+now+" | "+syncBadge,
90
+ "","---","",
91
+ "## What this system does","",capLines,"","---","",
92
+ "## Recent changes","",chgLines,"","---","",
93
+ "## Current state","",
94
+ "- **Capabilities:** "+capList.length,
95
+ "- **Version:** v"+version,
96
+ "- **Sync:** "+syncBadge,
97
+ "","---","",
98
+ "## What I am working on right now","",workingLine,"","---","",
99
+ "## Intent — what I want to build next","",intentLine,"","---","",
100
+ "## Decisions & notes","",decLines,"","---",
101
+ "_Paste this block at the start of any new AI session._"
197
102
  ].join("\n");
198
103
 
199
- if (!showOnly) {
200
- fs.writeFileSync(CONTEXT_FILE, md, "utf8");
201
- console.log(green("\n ✔ Context written → " + CONTEXT_FILE));
104
+ if(!showOnly){ fs.writeFileSync(CONTEXT_FILE,md,"utf8"); console.log(green("\n ✔ Context written → "+CONTEXT_FILE)); }
105
+
106
+ if(copyFlag){
107
+ const ok=copyToClipboard(md);
108
+ console.log(ok ? green(" ✔ Copied to clipboard — paste with Ctrl+V") : yellow(" ⚠ Clipboard copy failed — open inferno/CONTEXT.md manually"));
202
109
  }
203
110
 
204
- console.log("\n " + bold("Context Summary"));
205
- console.log(" " + "".repeat(50));
206
- console.log(" Project " + contract.policyId + " v" + version);
207
- console.log(" Capabilities " + capList.length + " registered");
208
- console.log(
209
- " Sync " +
210
- (allInSync ? green("✓ in sync") : yellow("⚠ check needed")),
211
- );
212
- console.log(
213
- " Working on " + (state.working ? cyan(state.working) : gray("not set")),
214
- );
215
- console.log(
216
- " Intent " + (state.intent ? cyan(state.intent) : gray("not set")),
217
- );
218
- console.log(
219
- " Decisions " +
220
- (state.decisions ? state.decisions.length : 0) +
221
- " recorded\n",
222
- );
223
-
224
- console.log(" " + bold("Ready to use:"));
225
- console.log(" " + cyan("1.") + " Open " + cyan("inferno/CONTEXT.md"));
226
- console.log(" " + cyan("2.") + " Copy everything");
227
- console.log(
228
- " " + cyan("3.") + " Paste at the start of your next AI session\n",
229
- );
111
+ if (cursorFlag) {
112
+ fs.writeFileSync(".cursorrules", md, "utf8");
113
+ console.log(green(" Written to .cursorrules Cursor loads this automatically"));
114
+ }
115
+ if (copilotFlag) {
116
+ if (!fs.existsSync(".github")) fs.mkdirSync(".github");
117
+ fs.writeFileSync(".github/copilot-instructions.md", md, "utf8");
118
+ console.log(green(" ✔ Written to .github/copilot-instructions.md — Copilot loads this automatically"));
119
+ }
120
+ console.log("\n "+bold("Context Summary"));
121
+ console.log(" "+"─".repeat(50));
122
+ console.log(" Project "+contract.policyId+" — v"+version);
123
+ console.log(" Capabilities "+capList.length+" registered");
124
+ console.log(" Sync "+(allInSync?green("✓ in sync"):yellow("⚠ check needed")));
125
+ console.log(" Working on "+(state.working?cyan(state.working):gray("not set")));
126
+ console.log(" Intent "+(state.intent ?cyan(state.intent) :gray("not set")));
127
+ console.log(" Decisions "+(state.decisions?state.decisions.length:0)+" recorded\n");
128
+
129
+ if(copyFlag){
130
+ console.log(" "+bold("Ready to use:"));
131
+ console.log(" "+cyan("→")+" Paste into Claude / Cursor / Copilot with "+cyan("Ctrl+V")+"\n");
132
+ } else {
133
+ console.log(" "+bold("Ready to use:"));
134
+ console.log(" "+cyan("1.")+" Open "+cyan("inferno/CONTEXT.md"));
135
+ console.log(" "+cyan("2.")+" Copy everything");
136
+ console.log(" "+cyan("3.")+" Paste at the start of your next AI session");
137
+ console.log(" "+gray(" tip: use --copy to skip steps 1-2 automatically")+"\n");
138
+ }
230
139
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "infernoflow",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "The forge for liquid code — keep capabilities, contracts, and docs in sync.",
5
5
  "type": "module",
6
6
  "bin": {