infernoflow 0.37.1 → 0.37.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.
Files changed (88) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/dist/bin/infernoflow.mjs +29 -277
  3. package/dist/lib/adopters/angular.mjs +1 -128
  4. package/dist/lib/adopters/css.mjs +1 -111
  5. package/dist/lib/adopters/react.mjs +1 -104
  6. package/dist/lib/ai/ideDetection.mjs +1 -31
  7. package/dist/lib/ai/localProvider.mjs +1 -88
  8. package/dist/lib/ai/providerRouter.mjs +2 -295
  9. package/dist/lib/commands/adopt.mjs +20 -869
  10. package/dist/lib/commands/adoptWizard.mjs +9 -320
  11. package/dist/lib/commands/agent.mjs +5 -191
  12. package/dist/lib/commands/ai.mjs +2 -407
  13. package/dist/lib/commands/ask.mjs +4 -299
  14. package/dist/lib/commands/audit.mjs +13 -300
  15. package/dist/lib/commands/changelog.mjs +26 -594
  16. package/dist/lib/commands/check.mjs +3 -184
  17. package/dist/lib/commands/ci.mjs +3 -208
  18. package/dist/lib/commands/claudeMd.mjs +30 -135
  19. package/dist/lib/commands/cloud.mjs +10 -773
  20. package/dist/lib/commands/context.mjs +34 -346
  21. package/dist/lib/commands/coverage.mjs +2 -282
  22. package/dist/lib/commands/dashboard.mjs +123 -635
  23. package/dist/lib/commands/demo.mjs +8 -465
  24. package/dist/lib/commands/diff.mjs +5 -274
  25. package/dist/lib/commands/docGate.mjs +2 -81
  26. package/dist/lib/commands/doctor.mjs +3 -321
  27. package/dist/lib/commands/explain.mjs +8 -438
  28. package/dist/lib/commands/export.mjs +10 -239
  29. package/dist/lib/commands/feedback.mjs +12 -216
  30. package/dist/lib/commands/generateSkills.mjs +38 -163
  31. package/dist/lib/commands/graph.mjs +11 -378
  32. package/dist/lib/commands/health.mjs +2 -309
  33. package/dist/lib/commands/impact.mjs +2 -325
  34. package/dist/lib/commands/implement.mjs +7 -103
  35. package/dist/lib/commands/init.mjs +45 -631
  36. package/dist/lib/commands/installCursorHooks.mjs +1 -36
  37. package/dist/lib/commands/installVsCodeCopilotHooks.mjs +1 -37
  38. package/dist/lib/commands/link.mjs +2 -342
  39. package/dist/lib/commands/log.mjs +18 -248
  40. package/dist/lib/commands/monorepo.mjs +4 -428
  41. package/dist/lib/commands/notify.mjs +4 -258
  42. package/dist/lib/commands/onboard.mjs +4 -296
  43. package/dist/lib/commands/prComment.mjs +2 -361
  44. package/dist/lib/commands/prImpact.mjs +2 -157
  45. package/dist/lib/commands/publish.mjs +15 -316
  46. package/dist/lib/commands/recap.mjs +6 -380
  47. package/dist/lib/commands/report.mjs +28 -272
  48. package/dist/lib/commands/review.mjs +9 -223
  49. package/dist/lib/commands/run.mjs +8 -336
  50. package/dist/lib/commands/scaffold.mjs +54 -419
  51. package/dist/lib/commands/scan.mjs +11 -1118
  52. package/dist/lib/commands/scout.mjs +2 -291
  53. package/dist/lib/commands/setup.mjs +5 -310
  54. package/dist/lib/commands/share.mjs +13 -196
  55. package/dist/lib/commands/snapshot.mjs +3 -383
  56. package/dist/lib/commands/stability.mjs +2 -293
  57. package/dist/lib/commands/stats.mjs +5 -402
  58. package/dist/lib/commands/status.mjs +4 -172
  59. package/dist/lib/commands/suggest.mjs +21 -563
  60. package/dist/lib/commands/switch.mjs +13 -520
  61. package/dist/lib/commands/syncAuto.mjs +1 -96
  62. package/dist/lib/commands/synthesize.mjs +10 -228
  63. package/dist/lib/commands/teamSync.mjs +2 -388
  64. package/dist/lib/commands/test.mjs +6 -363
  65. package/dist/lib/commands/theme.mjs +18 -195
  66. package/dist/lib/commands/uninstall.mjs +13 -406
  67. package/dist/lib/commands/upgrade.mjs +20 -153
  68. package/dist/lib/commands/version.mjs +2 -282
  69. package/dist/lib/commands/vibe.mjs +7 -357
  70. package/dist/lib/commands/watch.mjs +4 -203
  71. package/dist/lib/commands/why.mjs +4 -358
  72. package/dist/lib/cursorHooksInstall.mjs +1 -60
  73. package/dist/lib/draftToolingInstall.mjs +7 -68
  74. package/dist/lib/git/detect-drift.mjs +4 -208
  75. package/dist/lib/learning/adapt.mjs +6 -101
  76. package/dist/lib/learning/observe.mjs +1 -119
  77. package/dist/lib/learning/patternDetector.mjs +1 -298
  78. package/dist/lib/learning/profile.mjs +2 -279
  79. package/dist/lib/learning/skillSynthesizer.mjs +24 -145
  80. package/dist/lib/telemetry.mjs +19 -269
  81. package/dist/lib/templates/index.mjs +1 -131
  82. package/dist/lib/theme/scanner.mjs +4 -343
  83. package/dist/lib/ui/errors.mjs +1 -142
  84. package/dist/lib/ui/output.mjs +6 -95
  85. package/dist/lib/ui/prompts.mjs +6 -147
  86. package/dist/lib/vsCodeCopilotHooksInstall.mjs +1 -42
  87. package/package.json +2 -4
  88. package/scripts/postinstall.js +2 -2
@@ -1,346 +1,34 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { execSync } from "node:child_process";
4
- import { bold, gray, cyan, red, green, yellow } from "../ui/output.mjs";
5
- import { buildCursorImplementPrompt, buildGenericImplementPrompt } from "../ui/prompts.mjs";
6
- import { detectDrift } from "../git/detect-drift.mjs";
7
-
8
- function copyToClipboard(text) {
9
- try {
10
- const p = process.platform;
11
- if (p === "win32") execSync("clip", { input: text });
12
- else if (p === "darwin") execSync("pbcopy", { input: text });
13
- else { try { execSync("xclip -selection clipboard", { input: text }); } catch { execSync("xsel --clipboard --input", { input: text }); } }
14
- return true;
15
- } catch { return false; }
16
- }
17
-
18
- const INFERNO_DIR = "inferno";
19
- const CONTEXT_FILE = path.join(INFERNO_DIR, "CONTEXT.md");
20
- const STATE_FILE = path.join(INFERNO_DIR, "context-state.json");
21
-
22
- function readJSON(f) { try { return JSON.parse(fs.readFileSync(f,"utf8")); } catch { return null; } }
23
- function readFile(f) { try { return fs.readFileSync(f,"utf8"); } catch { return null; } }
24
- function loadState() { const r=readFile(STATE_FILE); if(!r) return {}; try { return JSON.parse(r); } catch { return {}; } }
25
- function saveState(s) { fs.writeFileSync(STATE_FILE,JSON.stringify(s,null,2),"utf8"); }
26
- function fmtDate(iso) { if(!iso) return "unknown"; return new Date(iso).toLocaleDateString("en-GB",{day:"2-digit",month:"short",year:"numeric"}); }
27
- function parseChangelog(txt,max) {
28
- if(!txt) return [];
29
- const entries=[]; let cur=null;
30
- for(const line of txt.split("\n")) {
31
- if(line.startsWith("## ")) { if(cur&&entries.length<max) entries.push(cur); if(entries.length>=max) break; cur={title:line.replace("## ","").trim(),items:[]}; }
32
- else if(cur&&line.startsWith("- ")) cur.items.push(line.replace("- ","").trim());
33
- }
34
- if(cur&&entries.length<max) entries.push(cur);
35
- return entries.filter(e=>e.items.length>0);
36
- }
37
-
38
- export async function contextCommand(args) {
39
- const has = (f) => args.includes(f);
40
- const flag = (f) => { const i=args.indexOf(f); return i!==-1&&args[i+1]?args[i+1]:null; };
41
-
42
- const intent = flag("--intent") || flag("-i");
43
- const working = flag("--working") || flag("-w");
44
- const decision = flag("--decision") || flag("-d");
45
- const showOnly = has("--show") || has("-s");
46
- const copyFlag = has("--copy") || has("-c");
47
- const cursorFlag = has("--cursor");
48
- const copilotFlag = has("--copilot");
49
- const resetFlag= has("--reset");
50
- const watchFlag = has("--watch");
51
- const autoCommit = has("--auto-commit") || has("--auto-push");
52
- const autoPush = has("--auto-push");
53
- const statsFlag = has("--stats");
54
- const watchInterval = parseInt(flag("--interval") || "30", 10) * 1000;
55
-
56
- console.log("\n "+bold("��� infernoflow — context"));
57
- console.log(" "+"─".repeat(50)+"\n");
58
-
59
- if(!fs.existsSync(INFERNO_DIR)){
60
- console.error(red(" ✘ inferno/ not found"));
61
- console.error(gray(" → Run: infernoflow init\n"));
62
- process.exit(1);
63
- }
64
-
65
- const contract = readJSON(path.join(INFERNO_DIR,"contract.json"));
66
- const capabilities = readJSON(path.join(INFERNO_DIR,"capabilities.json"));
67
- const changelog = readFile(path.join(INFERNO_DIR,"CHANGELOG.md"));
68
-
69
- if(!contract||!capabilities){
70
- console.error(red(" ✘ Missing contract.json or capabilities.json\n"));
71
- process.exit(1);
72
- }
73
-
74
- let state = loadState();
75
- if(resetFlag){ state={}; console.log(yellow(" ⚠ State reset\n")); }
76
- if(intent) { state.intent=intent; state.intentUpdated=new Date().toISOString(); console.log(green(' ✔ Intent saved: "'+intent+'"')); }
77
- if(working) { state.working=working; state.workingUpdated=new Date().toISOString(); console.log(green(' ✔ Working on: "'+working+'"')); }
78
- if(decision) { if(!state.decisions) state.decisions=[]; state.decisions.push({text:decision,date:new Date().toISOString()}); console.log(green(' ✔ Decision recorded: "'+decision+'"')); }
79
- if(intent||working||decision) saveState(state);
80
-
81
- const capList = capabilities.capabilities||[];
82
- const allInSync = capList.length===(contract.capabilities||[]).length;
83
- const recent = parseChangelog(changelog,3);
84
- const version = String(contract.policyVersion).replace(/^v/i,"");
85
- const now = new Date().toLocaleDateString("en-GB",{day:"2-digit",month:"short",year:"numeric"});
86
- const syncBadge = allInSync?"✓ validated":"⚠ out of sync";
87
- const implementTask = state.intent || "describe the exact task to implement";
88
- const implementInput = { task: implementTask, contract, caps: capabilities, scenarios: [], state };
89
- const cursorPrompt = buildCursorImplementPrompt(implementInput);
90
- const genericPrompt = buildGenericImplementPrompt(implementInput);
91
-
92
- const capLines = capList.map(c=>"- **"+c.id+"** — "+c.title).join("\n");
93
- const chgLines = recent.length>0 ? recent.map(e=>"### "+e.title+"\n"+e.items.map(i=>" - "+i).join("\n")).join("\n\n") : "_No recent changes_";
94
- const intentLine = state.intent ? state.intent+" _("+fmtDate(state.intentUpdated)+")_" : "_Not set — run: infernoflow context --intent \"...\"_";
95
- const workingLine = state.working ? state.working+" _("+fmtDate(state.workingUpdated)+")_" : "_Not set — run: infernoflow context --working \"...\"_";
96
- const decLines = state.decisions&&state.decisions.length>0 ? state.decisions.slice(-5).map(d=>"- "+d.text+" _("+fmtDate(d.date)+")_").join("\n") : "_No decisions recorded_";
97
-
98
- // ── Session memory (what AI can't infer from code) ────────────────────────
99
- const sessionsFile = path.join(INFERNO_DIR, "sessions.jsonl");
100
- let sessionLines = "_No session history yet — use: infernoflow log \"<what happened>\"_";
101
- if (fs.existsSync(sessionsFile)) {
102
- const entries = fs.readFileSync(sessionsFile, "utf8")
103
- .split("\n").filter(Boolean)
104
- .map(l => { try { return JSON.parse(l); } catch { return null; } })
105
- .filter(Boolean)
106
- .slice(-10); // last 10 entries
107
- if (entries.length) {
108
- sessionLines = entries.map(e => {
109
- const ts = new Date(e.ts).toLocaleString("en-GB",{day:"2-digit",month:"short",hour:"2-digit",minute:"2-digit"});
110
- const result = e.result ? ` [${e.result}]` : "";
111
- return `- **${e.type||"note"}**${result} ${e.summary} _(${ts})_`;
112
- }).join("\n");
113
- }
114
- }
115
-
116
- // ── Design system (theme.json) ────────────────────────────────────────────
117
- const themeFile = path.join(INFERNO_DIR, "theme.json");
118
- let themeLines = "_Not scanned yet — run: infernoflow theme_";
119
- if (fs.existsSync(themeFile)) {
120
- try {
121
- const theme = JSON.parse(fs.readFileSync(themeFile, "utf8"));
122
- const lines = [];
123
- if (theme.fonts?.primary) lines.push(`- **Font (primary):** ${theme.fonts.primary}`);
124
- if (theme.fonts?.mono) lines.push(`- **Font (mono):** ${theme.fonts.mono}`);
125
- if (theme.colors?.mode) lines.push(`- **Color mode:** ${theme.colors.mode}`);
126
- if (theme.colors?.palette) {
127
- const pal = Object.entries(theme.colors.palette).map(([k,v])=>`${k}=${v}`).join(" ");
128
- lines.push(`- **Palette:** ${pal}`);
129
- }
130
- if (theme.cssVars && Object.keys(theme.cssVars).length) {
131
- const vars = Object.entries(theme.cssVars).slice(0,8).map(([k,v])=>`${k}: ${v}`).join(" | ");
132
- lines.push(`- **CSS vars:** ${vars}`);
133
- }
134
- if (theme.framework) lines.push(`- **Framework:** ${theme.framework}`);
135
- lines.push("", "> ⚠ Always use these exact values. Do not introduce new colors or fonts.");
136
- themeLines = lines.join("\n");
137
- } catch {}
138
- }
139
-
140
- const md = [
141
- "# Project Context — "+contract.policyId+" v"+version,
142
- "> Generated by infernoflow | "+now+" | "+syncBadge,
143
- "","---","",
144
- "## Session Memory — what AI can't infer from code",
145
- "_Decisions made, failed attempts, gotchas, preferences_","",
146
- sessionLines,"","---","",
147
- "## Design System",
148
- "_Fonts, colors, CSS vars — always match these exactly_","",
149
- themeLines,"","---","",
150
- "## What this system does","",capLines,"","---","",
151
- "## Recent changes","",chgLines,"","---","",
152
- "## Current state","",
153
- "- **Capabilities:** "+capList.length,
154
- "- **Version:** v"+version,
155
- "- **Sync:** "+syncBadge,
156
- "","---","",
157
- "## What I am working on right now","",workingLine,"","---","",
158
- "## Intent — what I want to build next","",intentLine,"","---","",
159
- "## Decisions & notes","",decLines,"","---",
160
- "",
161
- "## Implementation Prompt Seed","",
162
- "Use this to start coding immediately with an agent:","",
163
- "```bash",
164
- `infernoflow implement "${implementTask}" --mode both`,
165
- "```",
166
- "",
167
- "### Cursor Agent Prompt","",
168
- "```text",
169
- cursorPrompt,
170
- "```",
171
- "",
172
- "### Generic Agent Prompt","",
173
- "```text",
174
- genericPrompt,
175
- "```",
176
- "",
177
- "---",
178
- "_Paste this block at the start of any new AI session._"
179
- ].join("\n");
180
-
181
- if(!showOnly){ fs.writeFileSync(CONTEXT_FILE,md,"utf8"); console.log(green("\n ✔ Context written → "+CONTEXT_FILE)); }
182
-
183
- if(copyFlag){
184
- const ok=copyToClipboard(md);
185
- console.log(ok ? green(" ✔ Copied to clipboard — paste with Ctrl+V") : yellow(" ⚠ Clipboard copy failed — open inferno/CONTEXT.md manually"));
186
- }
187
-
188
- if (cursorFlag) {
189
- fs.writeFileSync(".cursorrules", md, "utf8");
190
- console.log(green(" ✔ Written to .cursorrules — Cursor loads this automatically"));
191
- }
192
- if (copilotFlag) {
193
- if (!fs.existsSync(".github")) fs.mkdirSync(".github");
194
- fs.writeFileSync(".github/copilot-instructions.md", md, "utf8");
195
- console.log(green(" ✔ Written to .github/copilot-instructions.md — Copilot loads this automatically"));
196
- }
197
- console.log("\n "+bold("Context Summary"));
198
- console.log(" "+"─".repeat(50));
199
- console.log(" Project "+contract.policyId+" — v"+version);
200
- console.log(" Capabilities "+capList.length+" registered");
201
- console.log(" Sync "+(allInSync?green("✓ in sync"):yellow("⚠ check needed")));
202
- console.log(" Working on "+(state.working?cyan(state.working):gray("not set")));
203
- console.log(" Intent "+(state.intent ?cyan(state.intent) :gray("not set")));
204
- console.log(" Decisions "+(state.decisions?state.decisions.length:0)+" recorded");
205
-
206
- // Brief stats line when --stats flag is set
207
- if (statsFlag) {
208
- try {
209
- const { statsCommand } = await import("./stats.mjs");
210
- process.stdout.write("\n");
211
- await statsCommand(["--brief"]);
212
- } catch {}
213
- }
214
- console.log();
215
- console.log(" "+bold("Implementation Prompt"));
216
- console.log(" "+cyan("→")+" Run "+cyan(`infernoflow implement "${implementTask}" --mode both`)+"\n");
217
-
218
- if(copyFlag){
219
- console.log(" "+bold("Ready to use:"));
220
- console.log(" "+cyan("→")+" Paste into Claude / Cursor / Copilot with "+cyan("Ctrl+V")+"\n");
221
- } else {
222
- console.log(" "+bold("Ready to use:"));
223
- console.log(" "+cyan("1.")+" Open "+cyan("inferno/CONTEXT.md"));
224
- console.log(" "+cyan("2.")+" Copy everything");
225
- console.log(" "+cyan("3.")+" Paste at the start of your next AI session");
226
- console.log(" "+gray(" tip: use --copy to skip steps 1-2 automatically")+"\n");
227
- }
228
-
229
- // ── Watch mode ────────────────────────────────────────────────────────────
230
- if (watchFlag) {
231
- const modeLabel = autoPush ? "auto-push" : autoCommit ? "auto-commit" : "watch";
232
- console.log(" " + cyan("👁 Watch mode active") + gray(
233
- ` — polling every ${watchInterval / 1000}s` +
234
- (autoPush ? " · will commit + push on change" : autoCommit ? " · will commit on change" : "")
235
- ));
236
- console.log(" " + gray("Press Ctrl+C to stop\n"));
237
-
238
- let lastChangedFiles = "";
239
- let lastCommittedContent = null;
240
-
241
- // ── git helpers ──────────────────────────────────────────────────────
242
- function gitRun(cmd) {
243
- try {
244
- execSync(cmd, { cwd: process.cwd(), encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
245
- return true;
246
- } catch { return false; }
247
- }
248
-
249
- function gitIsCleanFor(filePath) {
250
- // Returns true if the file has no staged/unstaged changes (nothing to commit)
251
- try {
252
- const out = execSync(`git status --porcelain "${filePath}"`, {
253
- cwd: process.cwd(), encoding: "utf8", stdio: ["ignore", "pipe", "pipe"]
254
- }).trim();
255
- return out === "";
256
- } catch { return true; }
257
- }
258
-
259
- function commitContext(contextPath, affectedCaps, changedCount) {
260
- const caps = affectedCaps.length > 0 ? affectedCaps.slice(0, 3).join(", ") : `${changedCount} files`;
261
- const msg = `chore: update context [${caps}]`;
262
- const staged = gitRun(`git add "${contextPath}"`);
263
- if (!staged) return { ok: false, reason: "git add failed" };
264
- if (gitIsCleanFor(contextPath)) return { ok: false, reason: "nothing to commit" };
265
- const committed = gitRun(`git commit -m "${msg}"`);
266
- if (!committed) return { ok: false, reason: "git commit failed (lock?)" };
267
- return { ok: true, msg };
268
- }
269
-
270
- function pushContext() {
271
- const ok = gitRun("git push");
272
- return ok;
273
- }
274
-
275
- // ── poll loop ────────────────────────────────────────────────────────
276
- const poll = async () => {
277
- try {
278
- const cwd = process.cwd();
279
- const drift = detectDrift(cwd, { sinceCommits: 1 });
280
- const changedKey = drift.changedFiles.sort().join("|");
281
-
282
- if (changedKey === lastChangedFiles) return;
283
- lastChangedFiles = changedKey;
284
- if (drift.changedFiles.length === 0) return;
285
-
286
- // Update "working" field
287
- const affected = drift.affectedCapabilities.map(c => c.id);
288
- const newWorking = affected.length > 0
289
- ? `Working on: ${affected.join(", ")} (${drift.changedFiles.length} files changed)`
290
- : `${drift.changedFiles.length} files changed — no capability match yet`;
291
-
292
- const currentState = loadState();
293
- if (currentState.working !== newWorking) {
294
- currentState.working = newWorking;
295
- currentState.workingUpdated = new Date().toISOString();
296
- saveState(currentState);
297
-
298
- // Regenerate CONTEXT.md silently
299
- await contextCommand(args.filter(a => a !== "--watch" && a !== "--auto-commit" && a !== "--auto-push"));
300
-
301
- const newContent = readFile(CONTEXT_FILE);
302
- const ts = new Date().toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit", second: "2-digit" });
303
-
304
- process.stderr.write(
305
- `\n ${green("✔")} [${ts}] Context updated — ${affected.length} capabilities affected\n` +
306
- ` ${gray(drift.changedFiles.slice(0, 3).join(", ") + (drift.changedFiles.length > 3 ? ` +${drift.changedFiles.length - 3} more` : ""))}\n`
307
- );
308
-
309
- // Auto-commit if enabled and content actually changed
310
- if (autoCommit && newContent !== lastCommittedContent) {
311
- lastCommittedContent = newContent;
312
- const result = commitContext(CONTEXT_FILE, affected, drift.changedFiles.length);
313
- if (result.ok) {
314
- process.stderr.write(` ${green("✔")} Committed: ${gray(result.msg)}\n`);
315
- if (autoPush) {
316
- const pushed = pushContext();
317
- process.stderr.write(
318
- pushed
319
- ? ` ${green("✔")} Pushed to origin\n`
320
- : ` ${yellow("⚠")} Push failed — will retry next change\n`
321
- );
322
- }
323
- } else {
324
- process.stderr.write(` ${yellow("⚠")} Commit skipped: ${gray(result.reason)}\n`);
325
- }
326
- }
327
- }
328
- } catch {
329
- // Silent — watch mode never crashes
330
- }
331
- };
332
-
333
- // Poll immediately then on interval
334
- await poll();
335
- const timer = setInterval(poll, watchInterval);
336
-
337
- process.on("SIGINT", () => {
338
- clearInterval(timer);
339
- process.stderr.write("\n " + gray("Watch stopped.\n\n"));
340
- process.exit(0);
341
- });
342
-
343
- // Prevent Node from exiting
344
- await new Promise(() => {});
345
- }
346
- }
1
+ import c from"node:fs";import y from"node:path";import{execSync as C}from"node:child_process";import{bold as I,gray as m,cyan as p,red as Z,green as d,yellow as _}from"../ui/output.mjs";import{buildCursorImplementPrompt as $t,buildGenericImplementPrompt as bt}from"../ui/prompts.mjs";import{detectDrift as Ft}from"../git/detect-drift.mjs";function jt(n){try{const i=process.platform;if(i==="win32")C("clip",{input:n});else if(i==="darwin")C("pbcopy",{input:n});else try{C("xclip -selection clipboard",{input:n})}catch{C("xsel --clipboard --input",{input:n})}return!0}catch{return!1}}const h="inferno",D=y.join(h,"CONTEXT.md"),tt=y.join(h,"context-state.json");function et(n){try{return JSON.parse(c.readFileSync(n,"utf8"))}catch{return null}}function E(n){try{return c.readFileSync(n,"utf8")}catch{return null}}function nt(){const n=E(tt);if(!n)return{};try{return JSON.parse(n)}catch{return{}}}function ot(n){c.writeFileSync(tt,JSON.stringify(n,null,2),"utf8")}function A(n){return n?new Date(n).toLocaleDateString("en-GB",{day:"2-digit",month:"short",year:"numeric"}):"unknown"}function It(n,i){if(!n)return[];const s=[];let l=null;for(const a of n.split(`
2
+ `))if(a.startsWith("## ")){if(l&&s.length<i&&s.push(l),s.length>=i)break;l={title:a.replace("## ","").trim(),items:[]}}else l&&a.startsWith("- ")&&l.items.push(a.replace("- ","").trim());return l&&s.length<i&&s.push(l),s.filter(a=>a.items.length>0)}async function _t(n){const i=t=>n.includes(t),s=t=>{const o=n.indexOf(t);return o!==-1&&n[o+1]?n[o+1]:null},l=s("--intent")||s("-i"),a=s("--working")||s("-w"),x=s("--decision")||s("-d"),it=i("--show")||i("-s"),B=i("--copy")||i("-c"),st=i("--cursor"),rt=i("--copilot"),ct=i("--reset"),lt=i("--watch"),P=i("--auto-commit")||i("--auto-push"),L=i("--auto-push"),at=i("--stats"),R=parseInt(s("--interval")||"30",10)*1e3;console.log(`
3
+ `+I("\uFFFD\uFFFD\uFFFD infernoflow \u2014 context")),console.log(" "+"\u2500".repeat(50)+`
4
+ `),c.existsSync(h)||(console.error(Z(" \u2718 inferno/ not found")),console.error(m(` \u2192 Run: infernoflow init
5
+ `)),process.exit(1));const S=et(y.join(h,"contract.json")),T=et(y.join(h,"capabilities.json")),pt=E(y.join(h,"CHANGELOG.md"));(!S||!T)&&(console.error(Z(` \u2718 Missing contract.json or capabilities.json
6
+ `)),process.exit(1));let e=nt();ct&&(e={},console.log(_(` \u26A0 State reset
7
+ `))),l&&(e.intent=l,e.intentUpdated=new Date().toISOString(),console.log(d(' \u2714 Intent saved: "'+l+'"'))),a&&(e.working=a,e.workingUpdated=new Date().toISOString(),console.log(d(' \u2714 Working on: "'+a+'"'))),x&&(e.decisions||(e.decisions=[]),e.decisions.push({text:x,date:new Date().toISOString()}),console.log(d(' \u2714 Decision recorded: "'+x+'"'))),(l||a||x)&&ot(e);const O=T.capabilities||[],V=O.length===(S.capabilities||[]).length,J=It(pt,3),W=String(S.policyVersion).replace(/^v/i,""),dt=new Date().toLocaleDateString("en-GB",{day:"2-digit",month:"short",year:"numeric"}),U=V?"\u2713 validated":"\u26A0 out of sync",G=e.intent||"describe the exact task to implement",X={task:G,contract:S,caps:T,scenarios:[],state:e},ut=$t(X),mt=bt(X),gt=O.map(t=>"- **"+t.id+"** \u2014 "+t.title).join(`
8
+ `),ft=J.length>0?J.map(t=>"### "+t.title+`
9
+ `+t.items.map(o=>" - "+o).join(`
10
+ `)).join(`
11
+
12
+ `):"_No recent changes_",ht=e.intent?e.intent+" _("+A(e.intentUpdated)+")_":'_Not set \u2014 run: infernoflow context --intent "..."_',wt=e.working?e.working+" _("+A(e.workingUpdated)+")_":'_Not set \u2014 run: infernoflow context --working "..."_',yt=e.decisions&&e.decisions.length>0?e.decisions.slice(-5).map(t=>"- "+t.text+" _("+A(t.date)+")_").join(`
13
+ `):"_No decisions recorded_",M=y.join(h,"sessions.jsonl");let H='_No session history yet \u2014 use: infernoflow log "<what happened>"_';if(c.existsSync(M)){const t=c.readFileSync(M,"utf8").split(`
14
+ `).filter(Boolean).map(o=>{try{return JSON.parse(o)}catch{return null}}).filter(Boolean).slice(-10);t.length&&(H=t.map(o=>{const f=new Date(o.ts).toLocaleString("en-GB",{day:"2-digit",month:"short",hour:"2-digit",minute:"2-digit"}),u=o.result?` [${o.result}]`:"";return`- **${o.type||"note"}**${u} ${o.summary} _(${f})_`}).join(`
15
+ `))}const K=y.join(h,"theme.json");let q="_Not scanned yet \u2014 run: infernoflow theme_";if(c.existsSync(K))try{const t=JSON.parse(c.readFileSync(K,"utf8")),o=[];if(t.fonts?.primary&&o.push(`- **Font (primary):** ${t.fonts.primary}`),t.fonts?.mono&&o.push(`- **Font (mono):** ${t.fonts.mono}`),t.colors?.mode&&o.push(`- **Color mode:** ${t.colors.mode}`),t.colors?.palette){const f=Object.entries(t.colors.palette).map(([u,k])=>`${u}=${k}`).join(" ");o.push(`- **Palette:** ${f}`)}if(t.cssVars&&Object.keys(t.cssVars).length){const f=Object.entries(t.cssVars).slice(0,8).map(([u,k])=>`${u}: ${k}`).join(" | ");o.push(`- **CSS vars:** ${f}`)}t.framework&&o.push(`- **Framework:** ${t.framework}`),o.push("","> \u26A0 Always use these exact values. Do not introduce new colors or fonts."),q=o.join(`
16
+ `)}catch{}const N=["# Project Context \u2014 "+S.policyId+" v"+W,"> Generated by infernoflow | "+dt+" | "+U,"","---","","## Session Memory \u2014 what AI can't infer from code","_Decisions made, failed attempts, gotchas, preferences_","",H,"","---","","## Design System","_Fonts, colors, CSS vars \u2014 always match these exactly_","",q,"","---","","## What this system does","",gt,"","---","","## Recent changes","",ft,"","---","","## Current state","","- **Capabilities:** "+O.length,"- **Version:** v"+W,"- **Sync:** "+U,"","---","","## What I am working on right now","",wt,"","---","","## Intent \u2014 what I want to build next","",ht,"","---","","## Decisions & notes","",yt,"","---","","## Implementation Prompt Seed","","Use this to start coding immediately with an agent:","","```bash",`infernoflow implement "${G}" --mode both`,"```","","### Cursor Agent Prompt","","```text",ut,"```","","### Generic Agent Prompt","","```text",mt,"```","","---","_Paste this block at the start of any new AI session._"].join(`
17
+ `);if(it||(c.writeFileSync(D,N,"utf8"),console.log(d(`
18
+ \u2714 Context written \u2192 `+D))),B){const t=jt(N);console.log(t?d(" \u2714 Copied to clipboard \u2014 paste with Ctrl+V"):_(" \u26A0 Clipboard copy failed \u2014 open inferno/CONTEXT.md manually"))}if(st&&(c.writeFileSync(".cursorrules",N,"utf8"),console.log(d(" \u2714 Written to .cursorrules \u2014 Cursor loads this automatically"))),rt&&(c.existsSync(".github")||c.mkdirSync(".github"),c.writeFileSync(".github/copilot-instructions.md",N,"utf8"),console.log(d(" \u2714 Written to .github/copilot-instructions.md \u2014 Copilot loads this automatically"))),console.log(`
19
+ `+I("Context Summary")),console.log(" "+"\u2500".repeat(50)),console.log(" Project "+S.policyId+" \u2014 v"+W),console.log(" Capabilities "+O.length+" registered"),console.log(" Sync "+(V?d("\u2713 in sync"):_("\u26A0 check needed"))),console.log(" Working on "+(e.working?p(e.working):m("not set"))),console.log(" Intent "+(e.intent?p(e.intent):m("not set"))),console.log(" Decisions "+(e.decisions?e.decisions.length:0)+" recorded"),at)try{const{statsCommand:t}=await import("./stats.mjs");process.stdout.write(`
20
+ `),await t(["--brief"])}catch{}if(console.log(),console.log(" "+I("Implementation Prompt")),console.log(" "+p("\u2192")+" Run "+p(`infernoflow implement "${G}" --mode both`)+`
21
+ `),B?(console.log(" "+I("Ready to use:")),console.log(" "+p("\u2192")+" Paste into Claude / Cursor / Copilot with "+p("Ctrl+V")+`
22
+ `)):(console.log(" "+I("Ready to use:")),console.log(" "+p("1.")+" Open "+p("inferno/CONTEXT.md")),console.log(" "+p("2.")+" Copy everything"),console.log(" "+p("3.")+" Paste at the start of your next AI session"),console.log(" "+m(" tip: use --copy to skip steps 1-2 automatically")+`
23
+ `)),lt){let u=function(g){try{return C(g,{cwd:process.cwd(),encoding:"utf8",stdio:["ignore","pipe","pipe"]}),!0}catch{return!1}},k=function(g){try{return C(`git status --porcelain "${g}"`,{cwd:process.cwd(),encoding:"utf8",stdio:["ignore","pipe","pipe"]}).trim()===""}catch{return!0}},z=function(g,r,v){const b=`chore: update context [${r.length>0?r.slice(0,3).join(", "):`${v} files`}]`;return u(`git add "${g}"`)?k(g)?{ok:!1,reason:"nothing to commit"}:u(`git commit -m "${b}"`)?{ok:!0,msg:b}:{ok:!1,reason:"git commit failed (lock?)"}:{ok:!1,reason:"git add failed"}},Q=function(){return u("git push")};var xt=u,Ot=k,Nt=z,vt=Q;const t=L?"auto-push":P?"auto-commit":"watch";console.log(" "+p("\u{1F441} Watch mode active")+m(` \u2014 polling every ${R/1e3}s`+(L?" \xB7 will commit + push on change":P?" \xB7 will commit on change":""))),console.log(" "+m(`Press Ctrl+C to stop
24
+ `));let o="",f=null;const Y=async()=>{try{const g=process.cwd(),r=Ft(g,{sinceCommits:1}),v=r.changedFiles.sort().join("|");if(v===o||(o=v,r.changedFiles.length===0))return;const $=r.affectedCapabilities.map(j=>j.id),b=$.length>0?`Working on: ${$.join(", ")} (${r.changedFiles.length} files changed)`:`${r.changedFiles.length} files changed \u2014 no capability match yet`,F=nt();if(F.working!==b){F.working=b,F.workingUpdated=new Date().toISOString(),ot(F),await _t(n.filter(w=>w!=="--watch"&&w!=="--auto-commit"&&w!=="--auto-push"));const j=E(D),kt=new Date().toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit",second:"2-digit"});if(process.stderr.write(`
25
+ ${d("\u2714")} [${kt}] Context updated \u2014 ${$.length} capabilities affected
26
+ ${m(r.changedFiles.slice(0,3).join(", ")+(r.changedFiles.length>3?` +${r.changedFiles.length-3} more`:""))}
27
+ `),P&&j!==f){f=j;const w=z(D,$,r.changedFiles.length);if(w.ok){if(process.stderr.write(` ${d("\u2714")} Committed: ${m(w.msg)}
28
+ `),L){const Ct=Q();process.stderr.write(Ct?` ${d("\u2714")} Pushed to origin
29
+ `:` ${_("\u26A0")} Push failed \u2014 will retry next change
30
+ `)}}else process.stderr.write(` ${_("\u26A0")} Commit skipped: ${m(w.reason)}
31
+ `)}}}catch{}};await Y();const St=setInterval(Y,R);process.on("SIGINT",()=>{clearInterval(St),process.stderr.write(`
32
+ `+m(`Watch stopped.
33
+
34
+ `)),process.exit(0)}),await new Promise(()=>{})}}export{_t as contextCommand};