infernoflow 0.32.8 → 0.32.9

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 (78) hide show
  1. package/dist/bin/infernoflow.mjs +84 -255
  2. package/dist/lib/adopters/angular.mjs +1 -128
  3. package/dist/lib/adopters/css.mjs +1 -111
  4. package/dist/lib/adopters/react.mjs +1 -104
  5. package/dist/lib/ai/ideDetection.mjs +1 -31
  6. package/dist/lib/ai/localProvider.mjs +1 -88
  7. package/dist/lib/ai/providerRouter.mjs +2 -295
  8. package/dist/lib/commands/adopt.mjs +20 -869
  9. package/dist/lib/commands/adoptWizard.mjs +9 -320
  10. package/dist/lib/commands/agent.mjs +5 -191
  11. package/dist/lib/commands/ai.mjs +2 -407
  12. package/dist/lib/commands/audit.mjs +13 -300
  13. package/dist/lib/commands/changelog.mjs +26 -594
  14. package/dist/lib/commands/check.mjs +3 -184
  15. package/dist/lib/commands/ci.mjs +3 -208
  16. package/dist/lib/commands/claudeMd.mjs +25 -130
  17. package/dist/lib/commands/cloud.mjs +5 -521
  18. package/dist/lib/commands/context.mjs +31 -287
  19. package/dist/lib/commands/coverage.mjs +2 -282
  20. package/dist/lib/commands/dashboard.mjs +123 -635
  21. package/dist/lib/commands/demo.mjs +8 -465
  22. package/dist/lib/commands/diff.mjs +5 -274
  23. package/dist/lib/commands/docGate.mjs +2 -81
  24. package/dist/lib/commands/doctor.mjs +3 -321
  25. package/dist/lib/commands/explain.mjs +8 -438
  26. package/dist/lib/commands/export.mjs +10 -239
  27. package/dist/lib/commands/generateSkills.mjs +38 -163
  28. package/dist/lib/commands/graph.mjs +203 -321
  29. package/dist/lib/commands/health.mjs +2 -309
  30. package/dist/lib/commands/impact.mjs +2 -325
  31. package/dist/lib/commands/implement.mjs +7 -103
  32. package/dist/lib/commands/init.mjs +23 -475
  33. package/dist/lib/commands/installCursorHooks.mjs +1 -36
  34. package/dist/lib/commands/installVsCodeCopilotHooks.mjs +1 -37
  35. package/dist/lib/commands/link.mjs +2 -342
  36. package/dist/lib/commands/monorepo.mjs +4 -428
  37. package/dist/lib/commands/notify.mjs +4 -258
  38. package/dist/lib/commands/onboard.mjs +4 -296
  39. package/dist/lib/commands/prComment.mjs +2 -361
  40. package/dist/lib/commands/prImpact.mjs +2 -157
  41. package/dist/lib/commands/publish.mjs +15 -316
  42. package/dist/lib/commands/report.mjs +28 -272
  43. package/dist/lib/commands/review.mjs +9 -223
  44. package/dist/lib/commands/run.mjs +8 -336
  45. package/dist/lib/commands/scaffold.mjs +54 -419
  46. package/dist/lib/commands/scan.mjs +5 -558
  47. package/dist/lib/commands/scout.mjs +2 -291
  48. package/dist/lib/commands/setup.mjs +5 -310
  49. package/dist/lib/commands/share.mjs +13 -196
  50. package/dist/lib/commands/snapshot.mjs +3 -383
  51. package/dist/lib/commands/stability.mjs +2 -293
  52. package/dist/lib/commands/status.mjs +4 -172
  53. package/dist/lib/commands/suggest.mjs +21 -563
  54. package/dist/lib/commands/syncAuto.mjs +1 -96
  55. package/dist/lib/commands/synthesize.mjs +10 -228
  56. package/dist/lib/commands/teamSync.mjs +2 -388
  57. package/dist/lib/commands/test.mjs +6 -363
  58. package/dist/lib/commands/version.mjs +2 -282
  59. package/dist/lib/commands/vibe.mjs +7 -357
  60. package/dist/lib/commands/watch.mjs +4 -203
  61. package/dist/lib/commands/why.mjs +4 -358
  62. package/dist/lib/cursorHooksInstall.mjs +1 -60
  63. package/dist/lib/draftToolingInstall.mjs +7 -68
  64. package/dist/lib/git/detect-drift.mjs +4 -208
  65. package/dist/lib/learning/adapt.mjs +6 -101
  66. package/dist/lib/learning/observe.mjs +1 -119
  67. package/dist/lib/learning/patternDetector.mjs +1 -298
  68. package/dist/lib/learning/profile.mjs +2 -279
  69. package/dist/lib/learning/skillSynthesizer.mjs +24 -145
  70. package/dist/lib/templates/index.mjs +1 -131
  71. package/dist/lib/ui/errors.mjs +1 -142
  72. package/dist/lib/ui/output.mjs +6 -72
  73. package/dist/lib/ui/prompts.mjs +6 -147
  74. package/dist/lib/vsCodeCopilotHooksInstall.mjs +1 -42
  75. package/dist/templates/cursor/inferno-mcp-server.mjs +29 -0
  76. package/dist/templates/github-app/GITHUB_APP.md +67 -0
  77. package/dist/templates/github-app/app-manifest.json +20 -0
  78. package/package.json +1 -1
@@ -1,358 +1,4 @@
1
- /**
2
- * infernoflow why
3
- *
4
- * Given a file path or function name — answer:
5
- * • Which capability does this serve?
6
- * • What is its stability level?
7
- * • What scenarios cover it?
8
- * • Who introduced it and when?
9
- * • What does it call / what calls it?
10
- *
11
- * Pure correlation — no AI needed. Uses scan.json + graph.json + scenarios/ + git log.
12
- *
13
- * Usage:
14
- * infernoflow why src/auth.ts
15
- * infernoflow why loginUser
16
- * infernoflow why src/auth.ts --function loginUser
17
- * infernoflow why --json src/auth.ts
18
- */
19
-
20
- import * as fs from "node:fs";
21
- import * as path from "node:path";
22
- import { execSync } from "node:child_process";
23
- import { bold, cyan, gray, green, yellow, red } from "../ui/output.mjs";
24
-
25
- // ── helpers ───────────────────────────────────────────────────────────────────
26
-
27
- function loadJson(p) {
28
- try { return JSON.parse(fs.readFileSync(p, "utf8")); }
29
- catch { return null; }
30
- }
31
-
32
- function runGit(cmd, cwd) {
33
- try {
34
- return execSync(cmd, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
35
- } catch { return ""; }
36
- }
37
-
38
- const LEVEL_ICON = { frozen: "🧊", stable: "〰️ ", experimental: "🌊" };
39
- const LEVEL_COLOR = { frozen: red, stable: yellow, experimental: green };
40
-
41
- function stability(cap) {
42
- return cap?.stability || "experimental";
43
- }
44
-
45
- // ── matchers ─────────────────────────────────────────────────────────────────
46
-
47
- /**
48
- * Given a target (file path or function name), find all matching
49
- * capabilities from scan.json.
50
- * Returns: [{ capId, capEntry, matchedVia, score }]
51
- */
52
- function findCapabilities(target, scanCaps, allCaps, cwd) {
53
- const results = [];
54
- const isFile = target.includes("/") || target.includes("\\") || target.includes(".");
55
- const relTarget = isFile ? path.relative(cwd, path.resolve(cwd, target)) : null;
56
-
57
- for (const entry of scanCaps) {
58
- const analysis = entry.codeAnalysis;
59
- if (!analysis) continue;
60
-
61
- const capFull = allCaps.find(c => c.id === entry.id) || {};
62
-
63
- if (isFile) {
64
- // Match by source file
65
- const fileMatch = (analysis.sourceFiles || []).some(f =>
66
- f === relTarget || f.endsWith(relTarget) || relTarget?.endsWith(f)
67
- );
68
- if (fileMatch) {
69
- results.push({ capId: entry.id, capEntry: entry, capFull, matchedVia: "file", score: 1.0 });
70
- continue;
71
- }
72
- }
73
-
74
- if (!isFile) {
75
- // Match by function name (exact or contains)
76
- const fnMatch = (analysis.functions || []).some(fn =>
77
- fn.toLowerCase() === target.toLowerCase() ||
78
- fn.toLowerCase().includes(target.toLowerCase()) ||
79
- target.toLowerCase().includes(fn.toLowerCase())
80
- );
81
- if (fnMatch) {
82
- results.push({ capId: entry.id, capEntry: entry, capFull, matchedVia: "function", score: 1.0 });
83
- continue;
84
- }
85
- }
86
- }
87
-
88
- return results;
89
- }
90
-
91
- // ── scenario finder ───────────────────────────────────────────────────────────
92
-
93
- function findScenarios(capId, infernoDir) {
94
- const scenariosDir = path.join(infernoDir, "scenarios");
95
- if (!fs.existsSync(scenariosDir)) return [];
96
-
97
- const found = [];
98
- for (const f of fs.readdirSync(scenariosDir)) {
99
- if (!f.endsWith(".json")) continue;
100
- try {
101
- const s = JSON.parse(fs.readFileSync(path.join(scenariosDir, f), "utf8"));
102
- const covered = s.capabilitiesCovered || s.capabilities || [];
103
- if (covered.includes(capId) || covered.some(c =>
104
- c.toLowerCase() === capId.toLowerCase()
105
- )) {
106
- found.push({ file: f, scenario: s });
107
- }
108
- } catch {}
109
- }
110
- return found;
111
- }
112
-
113
- // ── git history ───────────────────────────────────────────────────────────────
114
-
115
- function getGitHistory(filePath, cwd, limit = 5) {
116
- if (!filePath) return [];
117
- const rel = path.relative(cwd, path.resolve(cwd, filePath));
118
- const log = runGit(
119
- `git log --follow --format="%h|%aI|%ae|%s" -${limit} -- ${JSON.stringify(rel)}`,
120
- cwd
121
- );
122
- if (!log) return [];
123
-
124
- return log.split("\n").filter(Boolean).map(line => {
125
- const [hash, date, author, ...subjectParts] = line.split("|");
126
- return {
127
- hash: hash?.trim(),
128
- date: date?.trim() ? new Date(date.trim()).toLocaleDateString() : "",
129
- author: author?.trim(),
130
- subject: subjectParts.join("|").trim(),
131
- };
132
- });
133
- }
134
-
135
- function getFirstCommit(filePath, cwd) {
136
- if (!filePath) return null;
137
- const rel = path.relative(cwd, path.resolve(cwd, filePath));
138
- const log = runGit(
139
- `git log --follow --format="%h|%aI|%ae|%s" -- ${JSON.stringify(rel)}`,
140
- cwd
141
- );
142
- if (!log) return null;
143
- const lines = log.split("\n").filter(Boolean);
144
- if (!lines.length) return null;
145
- const [hash, date, author, ...subjectParts] = lines[lines.length - 1].split("|");
146
- return {
147
- hash: hash?.trim(),
148
- date: date?.trim() ? new Date(date.trim()).toLocaleDateString() : "",
149
- author: author?.trim(),
150
- subject: subjectParts.join("|").trim(),
151
- };
152
- }
153
-
154
- // ── printer ───────────────────────────────────────────────────────────────────
155
-
156
- function printResult(result, scenarios, history, firstCommit, graph, allCaps, target) {
157
- const { capId, capEntry, capFull, matchedVia } = result;
158
- const level = stability(capFull);
159
- const icon = LEVEL_ICON[level] || "🌊";
160
- const color = LEVEL_COLOR[level] || green;
161
-
162
- console.log();
163
- console.log(bold(` ${icon} ${color(capId)}`));
164
- if (capFull.name || capFull.title) {
165
- console.log(gray(` ${capFull.name || capFull.title}`));
166
- }
167
- if (capFull.description) {
168
- console.log(gray(` ${capFull.description}`));
169
- }
170
- console.log();
171
-
172
- // Matched via
173
- console.log(gray(` matched via: `) + matchedVia + gray(` → `) + cyan(target));
174
-
175
- // Stability
176
- console.log(gray(` stability: `) + color(level));
177
-
178
- // Source files
179
- const files = capEntry.codeAnalysis?.sourceFiles || [];
180
- if (files.length) {
181
- console.log(gray(` source files: `) + files.join(", "));
182
- }
183
-
184
- // Functions
185
- const fns = capEntry.codeAnalysis?.functions || [];
186
- if (fns.length) {
187
- console.log(gray(` functions: `) + fns.join(", "));
188
- }
189
-
190
- // External services
191
- const services = capEntry.codeAnalysis?.services || [];
192
- if (services.length) {
193
- console.log(gray(` uses: `) + cyan(services.join(", ")));
194
- }
195
-
196
- // Throws
197
- const throws = capEntry.codeAnalysis?.throws || [];
198
- if (throws.length) {
199
- console.log(gray(` throws: `) + yellow(throws.join(", ")));
200
- }
201
-
202
- // Dependencies from graph
203
- if (graph) {
204
- const deps = graph.deps?.[capId] || [];
205
- const dependents = graph.dependents?.[capId] || [];
206
- if (deps.length) {
207
- console.log(gray(` calls: `) + deps.map(d => {
208
- const dCap = allCaps.find(c => c.id === d);
209
- const dIcon = LEVEL_ICON[stability(dCap)] || "🌊";
210
- return `${dIcon} ${d}`;
211
- }).join(" "));
212
- }
213
- if (dependents.length) {
214
- console.log(gray(` called by: `) + dependents.map(d => {
215
- const dCap = allCaps.find(c => c.id === d);
216
- const dIcon = LEVEL_ICON[stability(dCap)] || "🌊";
217
- return `${dIcon} ${d}`;
218
- }).join(" "));
219
- }
220
- }
221
-
222
- console.log();
223
-
224
- // Scenarios
225
- if (scenarios.length > 0) {
226
- console.log(bold(" Scenarios that cover this capability:"));
227
- for (const { scenario } of scenarios) {
228
- const steps = scenario.steps?.length || 0;
229
- console.log(` ${green("✔")} ${scenario.scenarioId || scenario.description || scenario.file}`);
230
- if (scenario.description) console.log(gray(` ${scenario.description}`));
231
- if (steps) console.log(gray(` ${steps} step(s)`));
232
- }
233
- console.log();
234
- } else {
235
- console.log(yellow(" ⚠ No scenarios found for this capability."));
236
- console.log(gray(` Run: infernoflow suggest "add scenario for ${capId}"`));
237
- console.log();
238
- }
239
-
240
- // Git history
241
- if (firstCommit) {
242
- console.log(bold(" Origin:"));
243
- console.log(` ${gray("first commit:")} ${firstCommit.hash} · ${firstCommit.date} · ${firstCommit.author}`);
244
- console.log(` ${gray("subject:")} ${firstCommit.subject}`);
245
- console.log();
246
- }
247
-
248
- if (history.length > 0) {
249
- console.log(bold(" Recent changes:"));
250
- for (const h of history.slice(0, 4)) {
251
- console.log(` ${gray(h.hash)} ${gray(h.date.padEnd(12))} ${h.subject}`);
252
- }
253
- console.log();
254
- }
255
-
256
- // Frozen warning
257
- if (level === "frozen") {
258
- console.log(red(" 🧊 This capability is FROZEN — do not modify without explicit instruction."));
259
- console.log();
260
- }
261
- }
262
-
263
- // ── entry point ───────────────────────────────────────────────────────────────
264
-
265
- export async function whyCommand(rawArgs) {
266
- const args = (rawArgs || []).slice(1); // skip command name
267
- const jsonMode = args.includes("--json");
268
- const fnFlag = args.indexOf("--function");
269
- const fnFilter = fnFlag !== -1 ? args[fnFlag + 1] : null;
270
-
271
- // Target: first non-flag argument (skip the value after --function if present)
272
- const target = args.find((a, i) => !a.startsWith("--") && (fnFlag === -1 || i !== fnFlag + 1));
273
-
274
- if (!target) {
275
- console.error(red("✗ Usage: infernoflow why <file-or-function> [--function <name>] [--json]"));
276
- console.error(gray(" Examples:"));
277
- console.error(gray(" infernoflow why src/auth.ts"));
278
- console.error(gray(" infernoflow why loginUser"));
279
- process.exit(1);
280
- }
281
-
282
- const cwd = process.cwd();
283
- const infernoDir = path.join(cwd, "inferno");
284
-
285
- // Load scan.json
286
- const scan = loadJson(path.join(infernoDir, "scan.json"));
287
- if (!scan) {
288
- console.error(red("✗ inferno/scan.json not found — run `infernoflow scan` first."));
289
- process.exit(1);
290
- }
291
-
292
- // Load capabilities.json for stability + metadata
293
- let allCaps = [];
294
- const rawCaps = loadJson(path.join(infernoDir, "capabilities.json"));
295
- if (rawCaps) allCaps = Array.isArray(rawCaps) ? rawCaps : (rawCaps.capabilities || []);
296
-
297
- // Load graph.json
298
- const graph = loadJson(path.join(infernoDir, "graph.json"));
299
-
300
- // Find capabilities
301
- const scanCaps = scan.capabilities || [];
302
- let results = findCapabilities(target, scanCaps, allCaps, cwd);
303
-
304
- // Apply --function filter
305
- if (fnFilter && results.length > 1) {
306
- results = results.filter(r =>
307
- (r.capEntry.codeAnalysis?.functions || []).some(fn =>
308
- fn.toLowerCase().includes(fnFilter.toLowerCase())
309
- )
310
- );
311
- }
312
-
313
- if (results.length === 0) {
314
- console.log();
315
- console.log(yellow(` No capability found matching: ${bold(target)}`));
316
- console.log(gray(" Tip: run `infernoflow scan` to update code analysis, then try again."));
317
- console.log(gray(" Tip: use a function name or relative file path."));
318
- console.log();
319
- process.exit(0);
320
- }
321
-
322
- if (jsonMode) {
323
- const out = results.map(r => {
324
- const scenarios = findScenarios(r.capId, infernoDir);
325
- const files = r.capEntry.codeAnalysis?.sourceFiles || [];
326
- const history = getGitHistory(files[0], cwd);
327
- const first = getFirstCommit(files[0], cwd);
328
- return {
329
- capId: r.capId,
330
- name: r.capFull.name || r.capFull.title,
331
- stability: stability(r.capFull),
332
- matchedVia: r.matchedVia,
333
- sourceFiles: files,
334
- functions: r.capEntry.codeAnalysis?.functions || [],
335
- services: r.capEntry.codeAnalysis?.services || [],
336
- throws: r.capEntry.codeAnalysis?.throws || [],
337
- deps: graph?.deps?.[r.capId] || [],
338
- dependents: graph?.dependents?.[r.capId] || [],
339
- scenarios: scenarios.map(s => s.scenario?.scenarioId || s.file),
340
- firstCommit: first,
341
- recentHistory: history,
342
- };
343
- });
344
- console.log(JSON.stringify(out, null, 2));
345
- return;
346
- }
347
-
348
- console.log(gray(`\n infernoflow why → ${bold(target)}`));
349
- console.log(gray(" ──────────────────────────────────────────────────────────────"));
350
-
351
- for (const result of results) {
352
- const files = result.capEntry.codeAnalysis?.sourceFiles || [];
353
- const scenarios = findScenarios(result.capId, infernoDir);
354
- const history = getGitHistory(files[0], cwd);
355
- const first = getFirstCommit(files[0], cwd);
356
- printResult(result, scenarios, history, first, graph, allCaps, target);
357
- }
358
- }
1
+ import*as F from"node:fs";import*as h from"node:path";import{execSync as W}from"node:child_process";import{bold as C,cyan as V,gray as n,green as x,yellow as I,red as L}from"../ui/output.mjs";function O(o){try{return JSON.parse(F.readFileSync(o,"utf8"))}catch{return null}}function D(o,t){try{return W(o,{cwd:t,encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim()}catch{return""}}const N={frozen:"\u{1F9CA}",stable:"\u3030\uFE0F ",experimental:"\u{1F30A}"},z={frozen:L,stable:I,experimental:x};function v(o){return o?.stability||"experimental"}function B(o,t,p,i){const c=[],l=o.includes("/")||o.includes("\\")||o.includes("."),r=l?h.relative(i,h.resolve(i,o)):null;for(const e of t){const d=e.codeAnalysis;if(!d)continue;const f=p.find(y=>y.id===e.id)||{};if(l&&(d.sourceFiles||[]).some(u=>u===r||u.endsWith(r)||r?.endsWith(u))){c.push({capId:e.id,capEntry:e,capFull:f,matchedVia:"file",score:1});continue}if(!l&&(d.functions||[]).some(u=>u.toLowerCase()===o.toLowerCase()||u.toLowerCase().includes(o.toLowerCase())||o.toLowerCase().includes(u.toLowerCase()))){c.push({capId:e.id,capEntry:e,capFull:f,matchedVia:"function",score:1});continue}}return c}function J(o,t){const p=h.join(t,"scenarios");if(!F.existsSync(p))return[];const i=[];for(const c of F.readdirSync(p))if(c.endsWith(".json"))try{const l=JSON.parse(F.readFileSync(h.join(p,c),"utf8")),r=l.capabilitiesCovered||l.capabilities||[];(r.includes(o)||r.some(e=>e.toLowerCase()===o.toLowerCase()))&&i.push({file:c,scenario:l})}catch{}return i}function M(o,t,p=5){if(!o)return[];const i=h.relative(t,h.resolve(t,o)),c=D(`git log --follow --format="%h|%aI|%ae|%s" -${p} -- ${JSON.stringify(i)}`,t);return c?c.split(`
2
+ `).filter(Boolean).map(l=>{const[r,e,d,...f]=l.split("|");return{hash:r?.trim(),date:e?.trim()?new Date(e.trim()).toLocaleDateString():"",author:d?.trim(),subject:f.join("|").trim()}}):[]}function R(o,t){if(!o)return null;const p=h.relative(t,h.resolve(t,o)),i=D(`git log --follow --format="%h|%aI|%ae|%s" -- ${JSON.stringify(p)}`,t);if(!i)return null;const c=i.split(`
3
+ `).filter(Boolean);if(!c.length)return null;const[l,r,e,...d]=c[c.length-1].split("|");return{hash:l?.trim(),date:r?.trim()?new Date(r.trim()).toLocaleDateString():"",author:e?.trim(),subject:d.join("|").trim()}}function G(o,t,p,i,c,l,r){const{capId:e,capEntry:d,capFull:f,matchedVia:y}=o,u=v(f),E=N[u]||"\u{1F30A}",m=z[u]||x;console.log(),console.log(C(` ${E} ${m(e)}`)),(f.name||f.title)&&console.log(n(` ${f.name||f.title}`)),f.description&&console.log(n(` ${f.description}`)),console.log(),console.log(n(" matched via: ")+y+n(" \u2192 ")+V(r)),console.log(n(" stability: ")+m(u));const g=d.codeAnalysis?.sourceFiles||[];g.length&&console.log(n(" source files: ")+g.join(", "));const s=d.codeAnalysis?.functions||[];s.length&&console.log(n(" functions: ")+s.join(", "));const b=d.codeAnalysis?.services||[];b.length&&console.log(n(" uses: ")+V(b.join(", ")));const w=d.codeAnalysis?.throws||[];if(w.length&&console.log(n(" throws: ")+I(w.join(", "))),c){const a=c.deps?.[e]||[],$=c.dependents?.[e]||[];a.length&&console.log(n(" calls: ")+a.map(j=>{const S=l.find(A=>A.id===j);return`${N[v(S)]||"\u{1F30A}"} ${j}`}).join(" ")),$.length&&console.log(n(" called by: ")+$.map(j=>{const S=l.find(A=>A.id===j);return`${N[v(S)]||"\u{1F30A}"} ${j}`}).join(" "))}if(console.log(),t.length>0){console.log(C(" Scenarios that cover this capability:"));for(const{scenario:a}of t){const $=a.steps?.length||0;console.log(` ${x("\u2714")} ${a.scenarioId||a.description||a.file}`),a.description&&console.log(n(` ${a.description}`)),$&&console.log(n(` ${$} step(s)`))}console.log()}else console.log(I(" \u26A0 No scenarios found for this capability.")),console.log(n(` Run: infernoflow suggest "add scenario for ${e}"`)),console.log();if(i&&(console.log(C(" Origin:")),console.log(` ${n("first commit:")} ${i.hash} \xB7 ${i.date} \xB7 ${i.author}`),console.log(` ${n("subject:")} ${i.subject}`),console.log()),p.length>0){console.log(C(" Recent changes:"));for(const a of p.slice(0,4))console.log(` ${n(a.hash)} ${n(a.date.padEnd(12))} ${a.subject}`);console.log()}u==="frozen"&&(console.log(L(" \u{1F9CA} This capability is FROZEN \u2014 do not modify without explicit instruction.")),console.log())}async function _(o){const t=(o||[]).slice(1),p=t.includes("--json"),i=t.indexOf("--function"),c=i!==-1?t[i+1]:null,l=t.find((g,s)=>!g.startsWith("--")&&(i===-1||s!==i+1));l||(console.error(L("\u2717 Usage: infernoflow why <file-or-function> [--function <name>] [--json]")),console.error(n(" Examples:")),console.error(n(" infernoflow why src/auth.ts")),console.error(n(" infernoflow why loginUser")),process.exit(1));const r=process.cwd(),e=h.join(r,"inferno"),d=O(h.join(e,"scan.json"));d||(console.error(L("\u2717 inferno/scan.json not found \u2014 run `infernoflow scan` first.")),process.exit(1));let f=[];const y=O(h.join(e,"capabilities.json"));y&&(f=Array.isArray(y)?y:y.capabilities||[]);const u=O(h.join(e,"graph.json")),E=d.capabilities||[];let m=B(l,E,f,r);if(c&&m.length>1&&(m=m.filter(g=>(g.capEntry.codeAnalysis?.functions||[]).some(s=>s.toLowerCase().includes(c.toLowerCase())))),m.length===0&&(console.log(),console.log(I(` No capability found matching: ${C(l)}`)),console.log(n(" Tip: run `infernoflow scan` to update code analysis, then try again.")),console.log(n(" Tip: use a function name or relative file path.")),console.log(),process.exit(0)),p){const g=m.map(s=>{const b=J(s.capId,e),w=s.capEntry.codeAnalysis?.sourceFiles||[],a=M(w[0],r),$=R(w[0],r);return{capId:s.capId,name:s.capFull.name||s.capFull.title,stability:v(s.capFull),matchedVia:s.matchedVia,sourceFiles:w,functions:s.capEntry.codeAnalysis?.functions||[],services:s.capEntry.codeAnalysis?.services||[],throws:s.capEntry.codeAnalysis?.throws||[],deps:u?.deps?.[s.capId]||[],dependents:u?.dependents?.[s.capId]||[],scenarios:b.map(j=>j.scenario?.scenarioId||j.file),firstCommit:$,recentHistory:a}});console.log(JSON.stringify(g,null,2));return}console.log(n(`
4
+ infernoflow why \u2192 ${C(l)}`)),console.log(n(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));for(const g of m){const s=g.capEntry.codeAnalysis?.sourceFiles||[],b=J(g.capId,e),w=M(s[0],r),a=R(s[0],r);G(g,b,w,a,u,f,l)}}export{_ as whyCommand};
@@ -1,60 +1 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { installInfernoDraftTooling } from "./draftToolingInstall.mjs";
4
-
5
- /**
6
- * @param {object} opts
7
- * @param {string} opts.cwd
8
- * @param {string} opts.templatesRoot
9
- * @param {boolean} opts.force
10
- * @param {boolean} opts.silent
11
- * @param {(msg: string) => void} [opts.logOk]
12
- * @param {(msg: string) => void} [opts.logWarn]
13
- */
14
- export function installCursorHooksArtifacts(opts) {
15
- const { cwd, templatesRoot, force, silent } = opts;
16
- const logOk = opts.logOk || (() => {});
17
- const logWarn = opts.logWarn || (() => {});
18
-
19
- function copyFile(src, dst) {
20
- if (fs.existsSync(dst) && !force) {
21
- if (!silent) logWarn("Skipped (exists): " + path.relative(cwd, dst));
22
- return false;
23
- }
24
- fs.mkdirSync(path.dirname(dst), { recursive: true });
25
- fs.copyFileSync(src, dst);
26
- if (!silent) logOk("Created: " + path.relative(cwd, dst));
27
- return true;
28
- }
29
-
30
- installInfernoDraftTooling({ cwd, templatesRoot, force, silent, logOk, logWarn });
31
-
32
- const srcHooksJson = path.join(templatesRoot, "cursor", "hooks.json");
33
- const dstHooksJson = path.join(cwd, ".cursor", "hooks.json");
34
- const srcHook = path.join(templatesRoot, "cursor", "hooks", "inferno-session-draft.mjs");
35
- const dstHook = path.join(cwd, ".cursor", "hooks", "inferno-session-draft.mjs");
36
-
37
- copyFile(srcHooksJson, dstHooksJson);
38
- copyFile(srcHook, dstHook);
39
-
40
- // MCP server
41
- const srcMcp = path.join(templatesRoot, "cursor", "inferno-mcp-server.mjs");
42
- const dstMcp = path.join(cwd, "inferno-mcp-server.mjs");
43
- copyFile(srcMcp, dstMcp);
44
-
45
- // .cursor/mcp.json
46
- const mcpJsonPath = path.join(cwd, ".cursor", "mcp.json");
47
- if (!fs.existsSync(mcpJsonPath) || force) {
48
- let existing = {};
49
- if (fs.existsSync(mcpJsonPath)) {
50
- try { existing = JSON.parse(fs.readFileSync(mcpJsonPath, "utf8")); } catch {}
51
- }
52
- if (!existing.mcpServers) existing.mcpServers = {};
53
- existing.mcpServers.infernoflow = { command: "node", args: ["./inferno-mcp-server.mjs"], env: {} };
54
- fs.mkdirSync(path.dirname(mcpJsonPath), { recursive: true });
55
- fs.writeFileSync(mcpJsonPath, JSON.stringify(existing, null, 2), "utf8");
56
- if (!silent) logOk("Created: .cursor/mcp.json");
57
- } else {
58
- if (!silent) logWarn("Skipped (exists): .cursor/mcp.json");
59
- }
60
- }
1
+ import*as r from"node:fs";import*as o from"node:path";import{installInfernoDraftTooling as v}from"./draftToolingInstall.mjs";function h(f){const{cwd:s,templatesRoot:t,force:m,silent:e}=f,a=f.logOk||(()=>{}),l=f.logWarn||(()=>{});function p(n,c){return r.existsSync(c)&&!m?(e||l("Skipped (exists): "+o.relative(s,c)),!1):(r.mkdirSync(o.dirname(c),{recursive:!0}),r.copyFileSync(n,c),e||a("Created: "+o.relative(s,c)),!0)}v({cwd:s,templatesRoot:t,force:m,silent:e,logOk:a,logWarn:l});const u=o.join(t,"cursor","hooks.json"),j=o.join(s,".cursor","hooks.json"),k=o.join(t,"cursor","hooks","inferno-session-draft.mjs"),S=o.join(s,".cursor","hooks","inferno-session-draft.mjs");p(u,j),p(k,S);const d=o.join(t,"cursor","inferno-mcp-server.mjs"),y=o.join(s,"inferno-mcp-server.mjs");p(d,y);const i=o.join(s,".cursor","mcp.json");if(!r.existsSync(i)||m){let n={};if(r.existsSync(i))try{n=JSON.parse(r.readFileSync(i,"utf8"))}catch{}n.mcpServers||(n.mcpServers={}),n.mcpServers.infernoflow={command:"node",args:["./inferno-mcp-server.mjs"],env:{}},r.mkdirSync(o.dirname(i),{recursive:!0}),r.writeFileSync(i,JSON.stringify(n,null,2),"utf8"),e||a("Created: .cursor/mcp.json")}else e||l("Skipped (exists): .cursor/mcp.json")}export{h as installCursorHooksArtifacts};
@@ -1,69 +1,8 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
-
4
- const GITIGNORE_SNIPPET = `
5
- # infernoflow: agent draft (IDE hooks — review before commit)
1
+ import*as t from"node:fs";import*as n from"node:path";const d=`
2
+ # infernoflow: agent draft (IDE hooks \u2014 review before commit)
6
3
  inferno/CONTEXT.draft.md
7
- `.trimStart();
8
-
9
- function upsertPromoteScript(cwd, silent, logOk) {
10
- const pkgPath = path.join(cwd, "package.json");
11
- if (!fs.existsSync(pkgPath)) {
12
- if (!silent) logOk("No package.json — add script manually: inferno:promote-draft");
13
- return;
14
- }
15
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
16
- pkg.scripts = pkg.scripts || {};
17
- if (!pkg.scripts["inferno:promote-draft"]) {
18
- pkg.scripts["inferno:promote-draft"] = "node scripts/inferno-promote-draft.mjs";
19
- fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
20
- if (!silent) logOk("Updated package.json script: inferno:promote-draft");
21
- }
22
- }
23
-
24
- /**
25
- * inferno/CONTEXT.draft.md gitignore + promote script (shared by Cursor and VS Code installers).
26
- * @param {object} opts
27
- * @param {string} opts.cwd
28
- * @param {string} opts.templatesRoot
29
- * @param {boolean} opts.force
30
- * @param {boolean} opts.silent
31
- * @param {(msg: string) => void} [opts.logOk]
32
- * @param {(msg: string) => void} [opts.logWarn]
33
- */
34
- export function installInfernoDraftTooling(opts) {
35
- const { cwd, templatesRoot, force, silent } = opts;
36
- const logOk = opts.logOk || (() => {});
37
- const logWarn = opts.logWarn || (() => {});
38
-
39
- function copyFile(src, dst) {
40
- if (fs.existsSync(dst) && !force) {
41
- if (!silent) logWarn("Skipped (exists): " + path.relative(cwd, dst));
42
- return false;
43
- }
44
- fs.mkdirSync(path.dirname(dst), { recursive: true });
45
- fs.copyFileSync(src, dst);
46
- if (!silent) logOk("Created: " + path.relative(cwd, dst));
47
- return true;
48
- }
49
-
50
- const srcPromote = path.join(templatesRoot, "scripts", "inferno-promote-draft.mjs");
51
- const dstPromote = path.join(cwd, "scripts", "inferno-promote-draft.mjs");
52
- copyFile(srcPromote, dstPromote);
53
-
54
- upsertPromoteScript(cwd, silent, logOk);
55
-
56
- const gi = path.join(cwd, ".gitignore");
57
- if (fs.existsSync(gi)) {
58
- const cur = fs.readFileSync(gi, "utf8");
59
- if (cur.includes("CONTEXT.draft.md")) {
60
- if (!silent) logOk(".gitignore already mentions CONTEXT.draft.md");
61
- } else {
62
- fs.appendFileSync(gi, `\n${GITIGNORE_SNIPPET}\n`, "utf8");
63
- if (!silent) logOk("Updated: " + path.relative(cwd, gi));
64
- }
65
- } else {
66
- fs.writeFileSync(gi, `${GITIGNORE_SNIPPET}\n`, "utf8");
67
- if (!silent) logOk("Created: " + path.relative(cwd, gi));
68
- }
69
- }
4
+ `.trimStart();function S(o,e,c){const s=n.join(o,"package.json");if(!t.existsSync(s)){e||c("No package.json \u2014 add script manually: inferno:promote-draft");return}const r=JSON.parse(t.readFileSync(s,"utf8"));r.scripts=r.scripts||{},r.scripts["inferno:promote-draft"]||(r.scripts["inferno:promote-draft"]="node scripts/inferno-promote-draft.mjs",t.writeFileSync(s,JSON.stringify(r,null,2)+`
5
+ `,"utf8"),e||c("Updated package.json script: inferno:promote-draft"))}function y(o){const{cwd:e,templatesRoot:c,force:s,silent:r}=o,f=o.logOk||(()=>{}),l=o.logWarn||(()=>{});function m(p,a){return t.existsSync(a)&&!s?(r||l("Skipped (exists): "+n.relative(e,a)),!1):(t.mkdirSync(n.dirname(a),{recursive:!0}),t.copyFileSync(p,a),r||f("Created: "+n.relative(e,a)),!0)}const u=n.join(c,"scripts","inferno-promote-draft.mjs"),g=n.join(e,"scripts","inferno-promote-draft.mjs");m(u,g),S(e,r,f);const i=n.join(e,".gitignore");t.existsSync(i)?t.readFileSync(i,"utf8").includes("CONTEXT.draft.md")?r||f(".gitignore already mentions CONTEXT.draft.md"):(t.appendFileSync(i,`
6
+ ${d}
7
+ `,"utf8"),r||f("Updated: "+n.relative(e,i))):(t.writeFileSync(i,`${d}
8
+ `,"utf8"),r||f("Created: "+n.relative(e,i)))}export{y as installInfernoDraftTooling};