infernoflow 0.37.1 → 0.37.4

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 +71 -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,638 +1,52 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import * as readline from "node:readline";
4
- import { fileURLToPath } from "node:url";
5
- import { header, ok, warn, done, nextSteps, cyan, yellow, gray } from "../ui/output.mjs";
6
- import {
7
- discoverProjectSignals,
8
- reviewCapabilitiesInteractive,
9
- writeAdoptionBaseline,
10
- buildAdoptionReport,
11
- summarizeCapabilities,
12
- buildSignalsReport,
13
- } from "./adopt.mjs";
14
- import { installCursorHooksArtifacts } from "../cursorHooksInstall.mjs";
15
- import { installVsCodeCopilotHooksArtifacts } from "../vsCodeCopilotHooksInstall.mjs";
16
-
17
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
18
-
19
- function getTemplatesRoot() {
20
- return path.resolve(__dirname, "../../templates");
21
- }
22
-
23
- function ask(rl, question, defaultVal = "") {
24
- return new Promise(resolve => {
25
- const hint = defaultVal ? gray(` (${defaultVal})`) : "";
26
- rl.question(` ${question}${hint}: `, answer => {
27
- resolve(answer.trim() || defaultVal);
28
- });
29
- });
30
- }
31
-
32
- function getArgValue(args, ...flags) {
33
- for (const flag of flags) {
34
- const i = args.indexOf(flag);
35
- if (i !== -1 && args[i + 1] && !args[i + 1].startsWith("-")) return args[i + 1];
36
- }
37
- return null;
38
- }
39
-
40
- function copyFile(src, dst, force, silent = false) {
41
- if (fs.existsSync(dst) && !force) {
42
- if (!silent) warn("Skipped (exists): " + path.relative(process.cwd(), dst));
43
- return false;
44
- }
45
- fs.mkdirSync(path.dirname(dst), { recursive: true });
46
- fs.copyFileSync(src, dst);
47
- if (!silent) ok("Created: " + cyan(path.relative(process.cwd(), dst)));
48
- return true;
49
- }
50
-
51
- function copyDirDeep(srcDir, dstDir, force) {
52
- fs.mkdirSync(dstDir, { recursive: true });
53
- for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
54
- const src = path.join(srcDir, entry.name);
55
- const dst = path.join(dstDir, entry.name);
56
- if (entry.isDirectory()) copyDirDeep(src, dst, force);
57
- else copyFile(src, dst, force);
58
- }
59
- }
60
-
61
- function upsertScripts(cwd, silent = false) {
62
- const pkgPath = path.join(cwd, "package.json");
63
- if (!fs.existsSync(pkgPath)) return;
64
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
65
- pkg.scripts = pkg.scripts || {};
66
- let changed = false;
67
- const toAdd = {
68
- "inferno:check": "infernoflow check",
69
- "inferno:status": "infernoflow status",
70
- "inferno:gate": "infernoflow doc-gate",
71
- "inferno:impact": "infernoflow pr-impact --json",
72
- "inferno:sync": "infernoflow sync --auto --json",
73
- "inferno:run": "infernoflow run \"sync check\" --provider auto --json",
74
- "inferno:hooks": "node scripts/inferno-install-hooks.mjs"
75
- };
76
- for (const [k, v] of Object.entries(toAdd)) {
77
- if (!pkg.scripts[k]) { pkg.scripts[k] = v; changed = true; }
78
- }
79
- if (changed) {
80
- fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
81
- if (!silent) ok("Updated " + cyan("package.json") + " scripts");
82
- }
83
- }
84
-
85
- function detectProjectName(cwd) {
86
- const pkgPath = path.join(cwd, "package.json");
87
- if (fs.existsSync(pkgPath)) {
88
- try {
89
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
90
- if (pkg.name) return pkg.name.replace(/[^a-z0-9_-]/gi, "_");
91
- } catch {}
92
- }
93
- return path.basename(cwd);
94
- }
95
-
96
- function writeContract(contractPath, policyId, capabilities) {
97
- const contract = {
98
- policyId,
99
- policyVersion: 1,
100
- capabilities,
101
- rules: {
102
- docsRequiredOnCapabilityChange: true,
103
- requireScenarioForEachCapability: true,
104
- requireChangelogOnCapabilityChange: true
105
- }
106
- };
107
- fs.writeFileSync(contractPath, JSON.stringify(contract, null, 2) + "\n");
108
- }
109
-
110
- function writeCapabilities(capsPath, capabilities) {
111
- const registry = {
112
- schemaVersion: 1,
113
- capabilities: capabilities.map(id => ({
114
- id,
115
- title: id.replace(/([A-Z])/g, " $1").trim(),
116
- since: "0.1.0"
117
- }))
118
- };
119
- fs.writeFileSync(capsPath, JSON.stringify(registry, null, 2) + "\n");
120
- }
121
-
122
- function writeScenario(scenariosDir, capabilities) {
123
- fs.mkdirSync(scenariosDir, { recursive: true });
124
- const scenario = {
125
- scenarioId: "happy_path",
126
- description: "Basic happy-path flow covering all capabilities",
127
- capabilitiesCovered: capabilities,
128
- steps: capabilities.map(c => ({
129
- action: c,
130
- expect: `${c} works as expected`
131
- }))
132
- };
133
- fs.writeFileSync(
134
- path.join(scenariosDir, "happy_path.json"),
135
- JSON.stringify(scenario, null, 2) + "\n"
136
- );
137
- }
138
-
139
- function writeChangelog(changelogPath, policyId) {
140
- const content = `# Changelog — ${policyId}
1
+ import*as i from"node:fs";import*as t from"node:path";import*as L from"node:readline";import{fileURLToPath as te}from"node:url";import{header as se,ok as y,warn as _,done as K,nextSteps as re,cyan as f,yellow as J,gray as F}from"../ui/output.mjs";import{discoverProjectSignals as H,reviewCapabilitiesInteractive as ce,writeAdoptionBaseline as le,buildAdoptionReport as ae,summarizeCapabilities as pe,buildSignalsReport as fe}from"./adopt.mjs";import{installCursorHooksArtifacts as de}from"../cursorHooksInstall.mjs";import{installVsCodeCopilotHooksArtifacts as ue}from"../vsCodeCopilotHooksInstall.mjs";const me=t.dirname(te(import.meta.url));function ye(){return t.resolve(me,"../../templates")}function R(e,o,s=""){return new Promise(n=>{const c=s?F(` (${s})`):"";e.question(` ${o}${c}: `,a=>{n(a.trim()||s)})})}function Y(e,...o){for(const s of o){const n=e.indexOf(s);if(n!==-1&&e[n+1]&&!e[n+1].startsWith("-"))return e[n+1]}return null}function D(e,o,s,n=!1){return i.existsSync(o)&&!s?(n||_("Skipped (exists): "+t.relative(process.cwd(),o)),!1):(i.mkdirSync(t.dirname(o),{recursive:!0}),i.copyFileSync(e,o),n||y("Created: "+f(t.relative(process.cwd(),o))),!0)}function ge(e,o,s){i.mkdirSync(o,{recursive:!0});for(const n of i.readdirSync(e,{withFileTypes:!0})){const c=t.join(e,n.name),a=t.join(o,n.name);n.isDirectory()?ge(c,a,s):D(c,a,s)}}function we(e,o=!1){const s=t.join(e,"package.json");if(!i.existsSync(s))return;const n=JSON.parse(i.readFileSync(s,"utf8"));n.scripts=n.scripts||{};let c=!1;const a={"inferno:check":"infernoflow check","inferno:status":"infernoflow status","inferno:gate":"infernoflow doc-gate","inferno:impact":"infernoflow pr-impact --json","inferno:sync":"infernoflow sync --auto --json","inferno:run":'infernoflow run "sync check" --provider auto --json',"inferno:hooks":"node scripts/inferno-install-hooks.mjs"};for(const[h,A]of Object.entries(a))n.scripts[h]||(n.scripts[h]=A,c=!0);c&&(i.writeFileSync(s,JSON.stringify(n,null,2)+`
2
+ `,"utf8"),o||y("Updated "+f("package.json")+" scripts"))}function G(e){const o=t.join(e,"package.json");if(i.existsSync(o))try{const s=JSON.parse(i.readFileSync(o,"utf8"));if(s.name)return s.name.replace(/[^a-z0-9_-]/gi,"_")}catch{}return t.basename(e)}function je(e,o,s){const n={policyId:o,policyVersion:1,capabilities:s,rules:{docsRequiredOnCapabilityChange:!0,requireScenarioForEachCapability:!0,requireChangelogOnCapabilityChange:!0}};i.writeFileSync(e,JSON.stringify(n,null,2)+`
3
+ `)}function he(e,o){const s={schemaVersion:1,capabilities:o.map(n=>({id:n,title:n.replace(/([A-Z])/g," $1").trim(),since:"0.1.0"}))};i.writeFileSync(e,JSON.stringify(s,null,2)+`
4
+ `)}function ke(e,o){i.mkdirSync(e,{recursive:!0});const s={scenarioId:"happy_path",description:"Basic happy-path flow covering all capabilities",capabilitiesCovered:o,steps:o.map(n=>({action:n,expect:`${n} works as expected`}))};i.writeFileSync(t.join(e,"happy_path.json"),JSON.stringify(s,null,2)+`
5
+ `)}function z(e,o){const s=`# Changelog \u2014 ${o}
141
6
 
142
7
  ## Unreleased
143
8
 
144
9
  - Initial capabilities defined
145
10
 
146
- ## 0.1.0 Initial release
11
+ ## 0.1.0 \u2014 Initial release
147
12
 
148
13
  - Project initialized with infernoflow
149
- `;
150
- fs.writeFileSync(changelogPath, content);
151
- }
152
-
153
- // ── Lite init — minimal footprint for small projects ─────────────────────────
154
-
155
- async function initLite(cwd, force) {
156
- const { bold, cyan, gray, green, yellow, red } = await import("../ui/output.mjs");
157
-
158
- console.log("\n " + bold("🔥 infernoflow init --lite"));
159
- console.log(" " + "─".repeat(50) + "\n");
160
- console.log(gray(" Lite mode: 3 files, no scripts, no workflows, no hooks."));
161
- console.log(gray(" Use `infernoflow upgrade` later to expand to the full setup.\n"));
162
-
163
- const infernoDir = path.join(cwd, "inferno");
164
-
165
- if (fs.existsSync(infernoDir) && !force) {
166
- console.log(yellow(" inferno/ already exists. Use --force to overwrite.\n"));
167
- process.exit(0);
168
- }
169
-
170
- fs.mkdirSync(infernoDir, { recursive: true });
171
-
172
- const policyId = detectProjectName(cwd);
173
-
174
- // Prompt for a one-liner intent (skip if --yes)
175
- let intent = "";
176
- if (!process.argv.includes("--yes") && !process.argv.includes("-y") && process.stdin.isTTY) {
177
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
178
- intent = await new Promise(resolve => {
179
- rl.question(gray(" What does this project do? (one line, Enter to skip): "), ans => {
180
- rl.close();
181
- resolve(ans.trim());
182
- });
183
- });
184
- }
185
-
186
- // contract.json minimal, no rules bloat
187
- const contract = {
188
- policyId,
189
- policyVersion: 1,
190
- lite: true,
191
- capabilities: [],
192
- intent: intent || undefined,
193
- };
194
- fs.writeFileSync(path.join(infernoDir, "contract.json"), JSON.stringify(contract, null, 2) + "\n");
195
-
196
- // capabilities.json — bare array (lite projects stay flat)
197
- fs.writeFileSync(path.join(infernoDir, "capabilities.json"), JSON.stringify([], null, 2) + "\n");
198
-
199
- // sessions.jsonl — empty, ready to receive log entries
200
- fs.writeFileSync(path.join(infernoDir, "sessions.jsonl"), "", "utf8");
201
-
202
- // .lite marker — lets `infernoflow upgrade` know the mode
203
- fs.writeFileSync(path.join(infernoDir, ".lite"), "1", "utf8");
204
-
205
- console.log(green(" ✔ Created inferno/contract.json"));
206
- console.log(green(" ✔ Created inferno/capabilities.json"));
207
- console.log(green(" ✔ Created inferno/sessions.jsonl"));
208
- console.log();
209
- console.log(" " + bold("Ready. Start using it:"));
210
- console.log(" " + cyan("infernoflow log") + gray(' "what you\'re building" --type note'));
211
- console.log(" " + cyan("infernoflow theme") + gray(" — scan your fonts + colors"));
212
- console.log(" " + cyan("infernoflow context") + gray(" — generate AI context to paste"));
213
- console.log(" " + cyan("infernoflow upgrade") + gray(" — expand to full setup when you need it"));
214
- console.log();
215
- }
216
-
217
- /** Default init: memory-only, asks for first gotcha, 60 seconds */
218
- async function initMemory(cwd, force, yes) {
219
- const { bold, cyan, gray, green, yellow } = await import("../ui/output.mjs");
220
-
221
- const infernoDir = path.join(cwd, "inferno");
222
- const sessionsFile = path.join(infernoDir, "sessions.jsonl");
223
- const configFile = path.join(infernoDir, "config.json");
224
-
225
- if (fs.existsSync(infernoDir) && !force) {
226
- // Already initialized — just confirm and show commands
227
- console.log("\n " + bold("🔥 infernoflow") + gray(" — already set up\n"));
228
- console.log(" " + green("✔") + " inferno/ found\n");
229
- console.log(" Quick commands:");
230
- console.log(" " + cyan("infernoflow log \"...\"") + gray(" — remember something"));
231
- console.log(" " + cyan("infernoflow switch") + gray(" — handoff to next AI"));
232
- console.log(" " + cyan("infernoflow recap") + gray(" — session summary\n"));
233
- console.log(gray(" For contracts & CI gates: infernoflow init --mode full\n"));
234
- return;
235
- }
236
-
237
- const projectName = detectProjectName(cwd);
238
-
239
- console.log("\n " + bold("🔥 infernoflow") + gray(" — let's get you set up (30 seconds)\n"));
240
- console.log(" Detected: " + cyan(projectName) + "\n");
241
-
242
- // Create inferno/ directory
243
- fs.mkdirSync(infernoDir, { recursive: true });
244
-
245
- // Write minimal config
246
- fs.writeFileSync(configFile, JSON.stringify({
247
- project: projectName,
248
- version: "1",
249
- mode: "memory",
250
- created: new Date().toISOString(),
251
- }, null, 2) + "\n", "utf8");
252
-
253
- // Create empty sessions.jsonl
254
- if (!fs.existsSync(sessionsFile)) {
255
- fs.writeFileSync(sessionsFile, "", "utf8");
256
- }
257
-
258
- // Ask for first gotcha (skip if --yes or not TTY)
259
- let gotcha = "";
260
- if (!yes && process.stdin.isTTY) {
261
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
262
- gotcha = await new Promise(resolve => {
263
- rl.question(
264
- " " + gray("What should the next AI agent know about this project?\n > "),
265
- ans => { rl.close(); resolve(ans.trim()); }
266
- );
267
- });
268
- }
269
-
270
- if (gotcha) {
271
- const entry = {
272
- ts: new Date().toISOString(),
273
- agent: "user",
274
- type: "gotcha",
275
- summary: gotcha,
276
- source: "init",
277
- };
278
- fs.appendFileSync(sessionsFile, JSON.stringify(entry) + "\n", "utf8");
279
- console.log("\n " + green("✔") + " First gotcha logged!");
280
- }
281
-
282
- console.log("\n " + green("✔") + " You're set up. Quick commands:\n");
283
- console.log(" " + cyan("infernoflow log \"...\"") + gray(" — remember something"));
284
- console.log(" " + cyan("infernoflow switch") + gray(" — generate handoff for next AI"));
285
- console.log(" " + cyan("infernoflow recap") + gray(" — session summary\n"));
286
- console.log(gray(" Tip: infernoflow switch --copy puts the handoff on your clipboard.\n"));
287
- console.log(gray(" Want contracts & CI gates? Run: infernoflow init --mode full\n"));
288
- }
289
-
290
- export async function initCommand(args) {
291
- const cwd = process.cwd();
292
- const force = args.includes("--force") || args.includes("-f");
293
- const yes = args.includes("--yes") || args.includes("-y");
294
- const adopt = args.includes("--adopt");
295
-
296
- // ── Memory-first default (no flags) — 60-second onboarding ───────────────
297
- const modeArg = args.find(a => a.startsWith("--mode="))?.split("=")[1]
298
- || (args.indexOf("--mode") !== -1 ? args[args.indexOf("--mode") + 1] : null);
299
-
300
- const isFullMode = modeArg === "full" || modeArg === "contract";
301
- const hasAdvancedFlag = adopt || args.includes("--template") || args.includes("--cursor-hooks")
302
- || args.includes("--vscode-copilot-hooks") || args.includes("--lite");
303
-
304
- if (!isFullMode && !hasAdvancedFlag) {
305
- await initMemory(cwd, force, yes);
306
- return;
307
- }
308
-
309
- // ── Lite mode — tiny project, single directory, 3 files only ──────────────
310
- if (args.includes("--lite")) {
311
- await initLite(cwd, force);
312
- return;
313
- }
314
-
315
- // ── Template shortcut ──────────────────────────────────────────────────────
316
- const templateIdx = args.indexOf("--template");
317
- const templateName = templateIdx !== -1 ? args[templateIdx + 1] : null;
318
-
319
- if (templateName) {
320
- let tmplMod;
321
- try { tmplMod = await import("../templates/index.mjs"); } catch {}
322
- const tmpl = tmplMod?.getTemplate(templateName);
323
- if (!tmpl) {
324
- const available = tmplMod ? tmplMod.listTemplates().map(t => t.name).join(", ") : "rest-api, nextjs, cli, graphql, monorepo";
325
- warn(`Unknown template: ${templateName}. Available: ${available}`);
326
- process.exit(1);
327
- }
328
-
329
- const infernoDir = path.join(cwd, "inferno");
330
- const scenDir = path.join(infernoDir, "scenarios");
331
- if (!fs.existsSync(infernoDir)) fs.mkdirSync(infernoDir, { recursive: true });
332
- if (!fs.existsSync(scenDir)) fs.mkdirSync(scenDir, { recursive: true });
333
-
334
- const policyId = detectProjectName(cwd);
335
- const caps = tmpl.capabilities;
336
-
337
- // Write contract.json
338
- fs.writeFileSync(path.join(infernoDir, "contract.json"), JSON.stringify({
339
- policyId, policyVersion: 1,
340
- capabilities: caps.map(c => c.id),
341
- }, null, 2) + "\n");
342
-
343
- // Write capabilities.json
344
- fs.writeFileSync(path.join(infernoDir, "capabilities.json"), JSON.stringify({
345
- capabilities: caps.map(c => ({
346
- id: c.id, description: c.description,
347
- since: new Date().toISOString().slice(0, 10), source: `template:${templateName}`,
348
- })),
349
- }, null, 2) + "\n");
350
-
351
- // Write one scenario per capability
352
- for (const cap of caps) {
353
- fs.writeFileSync(path.join(scenDir, `${cap.id}.json`), JSON.stringify({
354
- id: `${cap.id}-happy-path`, capability: cap.id,
355
- description: `Happy path for ${cap.description || cap.id}`,
356
- steps: [
357
- { action: "invoke", target: cap.id, input: {} },
358
- { action: "assert", field: "status", value: "success" },
359
- ],
360
- capabilitiesCovered: [cap.id],
361
- }, null, 2) + "\n");
362
- }
363
-
364
- // Write CHANGELOG.md
365
- writeChangelog(path.join(infernoDir, "CHANGELOG.md"), policyId);
366
-
367
- // Write CONTEXT.md hint
368
- fs.writeFileSync(path.join(infernoDir, "CONTEXT.md"),
369
- `# ${policyId} — infernoflow context\n\n> Template: ${templateName} — ${tmpl.description}\n\n## Hint\n${tmpl.contextHint}\n\n## Capabilities (${caps.length})\n${caps.map(c => `- \`${c.id}\`: ${c.description}`).join("\n")}\n`
370
- );
371
-
372
- if (tmpl.scripts) {
373
- info(`Suggested package.json scripts for this template:`);
374
- Object.entries(tmpl.scripts).forEach(([k, v]) => console.log(` ${bold(k)}: ${gray(v)}`));
375
- console.log();
376
- }
377
-
378
- done(`Initialised from template ${bold(cyan(templateName))} — ${bold(String(caps.length))} capabilities`);
379
- console.log();
380
- info(`Run ${cyan("infernoflow vibe")} to start vibe coding mode`);
381
- console.log();
382
- return;
383
- }
384
- const cursorHooks = args.includes("--cursor-hooks");
385
- const vscodeCopilotHooks = args.includes("--vscode-copilot-hooks");
386
- const reportJson = args.includes("--report-json");
387
- const reportJsonOnly = args.includes("--report-json-only");
388
- const reportHumanOnly = args.includes("--report-human-only");
389
- const langOverride = getArgValue(args, "--lang");
390
- const frameworkOverride = getArgValue(args, "--framework");
391
- const projectTypeOverride = getArgValue(args, "--project-type");
392
- const silent = reportJsonOnly;
393
-
394
- if (reportJsonOnly && reportHumanOnly) {
395
- console.error("Error: --report-json-only and --report-human-only cannot be used together.");
396
- process.exit(1);
397
- }
398
-
399
- if (!silent) {
400
- header("init");
401
- }
402
-
403
- const infernoDir = path.join(cwd, "inferno");
404
- const workflowsDir = path.join(cwd, ".github", "workflows");
405
- if (fs.existsSync(infernoDir) && !force) {
406
- if (silent) {
407
- console.log(JSON.stringify({ ok: false, error: "inferno_exists", hint: "Use --force to overwrite" }, null, 2));
408
- process.exit(1);
409
- }
410
- warn("inferno/ already exists. Use --force to overwrite.");
411
- console.log();
412
- process.exit(0);
413
- }
414
-
415
- const detectedName = detectProjectName(cwd);
416
- const defaultCaps = "CreateTask, ReadTasks, UpdateTask, ToggleComplete, DeleteTask";
417
-
418
- let policyId = detectedName;
419
- let capabilities = defaultCaps.split(",").map(c => c.trim());
420
-
421
- if (adopt) {
422
- const profileOverrides = {
423
- language: langOverride || undefined,
424
- framework: frameworkOverride || undefined,
425
- projectType: projectTypeOverride || undefined,
426
- };
427
- let signals = discoverProjectSignals(cwd, profileOverrides);
428
- if (!yes && !reportJsonOnly) {
429
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
430
- const profile = signals.developmentProfile || {};
431
- const detected = profile.detected || {};
432
- console.log(gray(" Review inferred development stack (press Enter to accept detected values)\n"));
433
- const language = await ask(rl, "Language", profile.language || detected.language || "unknown");
434
- const framework = await ask(rl, "Framework", profile.framework || detected.framework || "unknown");
435
- const projectType = await ask(rl, "Project type", profile.projectType || detected.projectType || "unknown");
436
- rl.close();
437
- signals = discoverProjectSignals(cwd, { language, framework, projectType });
438
- }
439
- const inferred = signals.capabilities;
440
- const summarized = summarizeCapabilities(inferred);
441
- if (reportJsonOnly) {
442
- console.log(
443
- JSON.stringify(
444
- {
445
- mode: "adopt",
446
- policyId: detectedName,
447
- inferredCapabilities: summarized,
448
- components: signals.components,
449
- displayFields: signals.displayFields,
450
- externalLibraries: signals.externalLibraries,
451
- uiLayout: signals.uiLayout,
452
- styling: signals.styling,
453
- developmentProfile: signals.developmentProfile,
454
- apiCalls: signals.apiCalls,
455
- },
456
- null,
457
- 2
458
- )
459
- );
460
- } else {
461
- console.log();
462
- console.log(gray(buildAdoptionReport(inferred)));
463
- console.log();
464
- console.log(gray(buildSignalsReport(signals)));
465
- console.log();
466
- if (reportJson && !reportHumanOnly) {
467
- console.log(
468
- JSON.stringify(
469
- {
470
- mode: "adopt",
471
- policyId: detectedName,
472
- inferredCapabilities: summarized,
473
- components: signals.components,
474
- displayFields: signals.displayFields,
475
- externalLibraries: signals.externalLibraries,
476
- uiLayout: signals.uiLayout,
477
- styling: signals.styling,
478
- developmentProfile: signals.developmentProfile,
479
- apiCalls: signals.apiCalls,
480
- },
481
- null,
482
- 2
483
- )
484
- );
485
- console.log();
486
- }
487
- }
488
- const reviewed = await reviewCapabilitiesInteractive(inferred, yes);
489
- policyId = detectedName;
490
- capabilities = reviewed.map((c) => c.id);
491
- } else if (!yes) {
492
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
493
- console.log(gray(" Press Enter to accept defaults\n"));
494
- policyId = await ask(rl, "Project / policy name", detectedName);
495
- const capsRaw = await ask(rl, "Capabilities (comma-separated)", defaultCaps);
496
- capabilities = capsRaw.split(",").map(c => c.trim()).filter(Boolean);
497
- rl.close();
498
- console.log();
499
- }
500
-
501
- // Write files
502
- fs.mkdirSync(infernoDir, { recursive: true });
503
-
504
- if (adopt) {
505
- const capDetails = capabilities.map((id) => ({
506
- id,
507
- title: id.replace(/([A-Z])/g, " $1").trim(),
508
- }));
509
- const signals = discoverProjectSignals(cwd, {
510
- language: langOverride || undefined,
511
- framework: frameworkOverride || undefined,
512
- projectType: projectTypeOverride || undefined,
513
- });
514
- writeAdoptionBaseline(infernoDir, policyId, capDetails, signals);
515
- if (!silent) {
516
- ok("Created: " + cyan("inferno/contract.json"));
517
- ok("Created: " + cyan("inferno/capabilities.json"));
518
- ok("Created: " + cyan("inferno/scenarios/adoption_baseline.json"));
519
- ok("Created: " + cyan("inferno/adoption_profile.json"));
520
- ok("Created: " + cyan("inferno/CHANGELOG.md"));
521
- }
522
- } else {
523
- writeContract(path.join(infernoDir, "contract.json"), policyId, capabilities);
524
- if (!silent) ok("Created: " + cyan("inferno/contract.json"));
525
-
526
- writeCapabilities(path.join(infernoDir, "capabilities.json"), capabilities);
527
- if (!silent) ok("Created: " + cyan("inferno/capabilities.json"));
528
-
529
- writeScenario(path.join(infernoDir, "scenarios"), capabilities);
530
- if (!silent) ok("Created: " + cyan("inferno/scenarios/happy_path.json"));
531
-
532
- writeChangelog(path.join(infernoDir, "CHANGELOG.md"), policyId);
533
- if (!silent) ok("Created: " + cyan("inferno/CHANGELOG.md"));
534
- }
535
-
536
- // Copy doc-gate script
537
- const templates = getTemplatesRoot();
538
- const srcScript = path.join(templates, "scripts", "inferno-doc-gate.mjs");
539
- const dstScript = path.join(cwd, "scripts", "inferno-doc-gate.mjs");
540
- copyFile(srcScript, dstScript, force, silent);
541
- const srcHookScript = path.join(templates, "scripts", "inferno-install-hooks.mjs");
542
- const dstHookScript = path.join(cwd, "scripts", "inferno-install-hooks.mjs");
543
- copyFile(srcHookScript, dstHookScript, force, silent);
544
- const srcWorkflow = path.join(templates, "ci", "github-inferno-check.yml");
545
- const dstWorkflow = path.join(workflowsDir, "infernoflow-check.yml");
546
- copyFile(srcWorkflow, dstWorkflow, force, silent);
547
-
548
- upsertScripts(cwd, silent);
549
-
550
- if (cursorHooks) {
551
- installCursorHooksArtifacts({
552
- cwd,
553
- templatesRoot: templates,
554
- force,
555
- silent,
556
- logOk: (msg) => {
557
- if (!silent) ok(msg);
558
- },
559
- logWarn: (msg) => {
560
- if (!silent) warn(msg);
561
- },
562
- });
563
- }
564
- if (vscodeCopilotHooks) {
565
- installVsCodeCopilotHooksArtifacts({
566
- cwd,
567
- templatesRoot: templates,
568
- force,
569
- silent,
570
- logOk: (msg) => {
571
- if (!silent) ok(msg);
572
- },
573
- logWarn: (msg) => {
574
- if (!silent) warn(msg);
575
- },
576
- });
577
- }
578
-
579
- if (adopt) {
580
- const statePath = path.join(infernoDir, "context-state.json");
581
- let state = {};
582
- try {
583
- state = JSON.parse(fs.readFileSync(statePath, "utf8"));
584
- } catch {}
585
- const signals = discoverProjectSignals(cwd, {
586
- language: langOverride || undefined,
587
- framework: frameworkOverride || undefined,
588
- projectType: projectTypeOverride || undefined,
589
- });
590
- state.stack = signals.developmentProfile;
591
- fs.writeFileSync(statePath, JSON.stringify(state, null, 2) + "\n", "utf8");
592
- if (!silent) ok("Created: " + cyan("inferno/context-state.json"));
593
- }
594
-
595
- if (!silent) {
596
- done("infernoflow initialized!");
597
-
598
- // AI provider nudge — show once at init if nothing is configured
599
- const intPath = path.join(infernoDir, "integrations.json");
600
- const hasAiKey = process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY ||
601
- process.env.GOOGLE_AI_API_KEY || process.env.OPENROUTER_API_KEY ||
602
- process.env.GEMINI_API_KEY;
603
- if (!hasAiKey && !fs.existsSync(intPath)) {
604
- console.log();
605
- console.log(` ${yellow("💡")} ${bold("Tip:")} connect an AI provider for explain, why, review, and changelog AI.`);
606
- console.log(` ${cyan("infernoflow ai setup")} — takes 60 seconds`);
607
- }
608
-
609
- nextSteps([
610
- cyan("infernoflow status") + " — see your contract at a glance",
611
- cyan("infernoflow check") + " — validate everything",
612
- (adopt ? "Review inferred baseline in " : "Edit ") + yellow("inferno/capabilities.json") + (adopt ? " and refine IDs/titles" : " to describe each capability in detail"),
613
- "Add more " + yellow("inferno/scenarios/*.json") + " files for edge cases",
614
- "Add " + cyan("inferno:check") + " to your CI pipeline",
615
- ...(cursorHooks
616
- ? [
617
- "Restart Cursor — hooks write assistant text to " + yellow("inferno/CONTEXT.draft.md"),
618
- "Promote when ready: " + cyan("npm run inferno:promote-draft -- --append-notes"),
619
- ]
620
- : []),
621
- ...(vscodeCopilotHooks
622
- ? [
623
- "Restart VS Code — Copilot hooks append prompts + assistant (from transcript) to " +
624
- yellow("inferno/CONTEXT.draft.md"),
625
- "Promote when ready: " + cyan("npm run inferno:promote-draft -- --append-notes"),
626
- ]
627
- : []),
628
- ...(!cursorHooks && !vscodeCopilotHooks
629
- ? [
630
- "Optional: " +
631
- cyan("infernoflow install-cursor-hooks") +
632
- " or " +
633
- cyan("infernoflow install-vscode-copilot-hooks"),
634
- ]
635
- : []),
636
- ]);
637
- }
638
- }
14
+ `;i.writeFileSync(e,s)}async function Se(e,o){const{bold:s,cyan:n,gray:c,green:a,yellow:h,red:A}=await import("../ui/output.mjs");console.log(`
15
+ `+s("\u{1F525} infernoflow init --lite")),console.log(" "+"\u2500".repeat(50)+`
16
+ `),console.log(c(" Lite mode: 3 files, no scripts, no workflows, no hooks.")),console.log(c(" Use `infernoflow upgrade` later to expand to the full setup.\n"));const m=t.join(e,"inferno");i.existsSync(m)&&!o&&(console.log(h(` \u26A0 inferno/ already exists. Use --force to overwrite.
17
+ `)),process.exit(0)),i.mkdirSync(m,{recursive:!0});const w=G(e);let S="";if(!process.argv.includes("--yes")&&!process.argv.includes("-y")&&process.stdin.isTTY){const b=L.createInterface({input:process.stdin,output:process.stdout});S=await new Promise(j=>{b.question(c(" What does this project do? (one line, Enter to skip): "),O=>{b.close(),j(O.trim())})})}const v={policyId:w,policyVersion:1,lite:!0,capabilities:[],intent:S||void 0};i.writeFileSync(t.join(m,"contract.json"),JSON.stringify(v,null,2)+`
18
+ `),i.writeFileSync(t.join(m,"capabilities.json"),JSON.stringify([],null,2)+`
19
+ `),i.writeFileSync(t.join(m,"sessions.jsonl"),"","utf8"),i.writeFileSync(t.join(m,".lite"),"1","utf8"),console.log(a(" \u2714 Created inferno/contract.json")),console.log(a(" \u2714 Created inferno/capabilities.json")),console.log(a(" \u2714 Created inferno/sessions.jsonl")),console.log(),console.log(" "+s("Ready. Start using it:")),console.log(" "+n("infernoflow log")+c(` "what you're building" --type note`)),console.log(" "+n("infernoflow theme")+c(" \u2014 scan your fonts + colors")),console.log(" "+n("infernoflow context")+c(" \u2014 generate AI context to paste")),console.log(" "+n("infernoflow upgrade")+c(" \u2014 expand to full setup when you need it")),console.log()}async function ve(e,o,s){const{bold:n,cyan:c,gray:a,green:h,yellow:A}=await import("../ui/output.mjs"),m=t.join(e,"inferno"),w=t.join(m,"sessions.jsonl"),S=t.join(m,"config.json");if(i.existsSync(m)&&!o){console.log(`
20
+ `+n("\u{1F525} infernoflow")+a(` \u2014 already set up
21
+ `)),console.log(" "+h("\u2714")+` inferno/ found
22
+ `),console.log(" Quick commands:"),console.log(" "+c('infernoflow log "..."')+a(" \u2014 remember something")),console.log(" "+c("infernoflow switch")+a(" \u2014 handoff to next AI")),console.log(" "+c("infernoflow recap")+a(` \u2014 session summary
23
+ `)),console.log(a(` For contracts & CI gates: infernoflow init --mode full
24
+ `));return}const v=G(e);console.log(`
25
+ `+n("\u{1F525} infernoflow")+a(` \u2014 let's get you set up (30 seconds)
26
+ `)),console.log(" Detected: "+c(v)+`
27
+ `),i.mkdirSync(m,{recursive:!0}),i.writeFileSync(S,JSON.stringify({project:v,version:"1",mode:"memory",created:new Date().toISOString()},null,2)+`
28
+ `,"utf8"),i.existsSync(w)||i.writeFileSync(w,"","utf8");let b="";if(!s&&process.stdin.isTTY){const j=L.createInterface({input:process.stdin,output:process.stdout});b=await new Promise(O=>{j.question(" "+a(`What should the next AI agent know about this project?
29
+ > `),N=>{j.close(),O(N.trim())})})}if(b){const j={ts:new Date().toISOString(),agent:"user",type:"gotcha",summary:b,source:"init"};i.appendFileSync(w,JSON.stringify(j)+`
30
+ `,"utf8"),console.log(`
31
+ `+h("\u2714")+" First gotcha logged!")}console.log(`
32
+ `+h("\u2714")+` You're set up. Quick commands:
33
+ `),console.log(" "+c('infernoflow log "..."')+a(" \u2014 remember something")),console.log(" "+c("infernoflow switch")+a(" \u2014 generate handoff for next AI")),console.log(" "+c("infernoflow recap")+a(` \u2014 session summary
34
+ `)),console.log(a(` Tip: infernoflow switch --copy puts the handoff on your clipboard.
35
+ `)),console.log(a(` Want contracts & CI gates? Run: infernoflow init --mode full
36
+ `))}async function Fe(e){const o=process.cwd(),s=e.includes("--force")||e.includes("-f"),n=e.includes("--yes")||e.includes("-y"),c=e.includes("--adopt"),a=e.find(l=>l.startsWith("--mode="))?.split("=")[1]||(e.indexOf("--mode")!==-1?e[e.indexOf("--mode")+1]:null),h=a==="full"||a==="contract",A=c||e.includes("--template")||e.includes("--cursor-hooks")||e.includes("--vscode-copilot-hooks")||e.includes("--lite");if(!h&&!A){await ve(o,s,n);return}if(e.includes("--lite")){await Se(o,s);return}const m=e.indexOf("--template"),w=m!==-1?e[m+1]:null;if(w){let l;try{l=await import("../templates/index.mjs")}catch{}const r=l?.getTemplate(w);if(!r){const p=l?l.listTemplates().map(C=>C.name).join(", "):"rest-api, nextjs, cli, graphql, monorepo";_(`Unknown template: ${w}. Available: ${p}`),process.exit(1)}const u=t.join(o,"inferno"),I=t.join(u,"scenarios");i.existsSync(u)||i.mkdirSync(u,{recursive:!0}),i.existsSync(I)||i.mkdirSync(I,{recursive:!0});const E=G(o),g=r.capabilities;i.writeFileSync(t.join(u,"contract.json"),JSON.stringify({policyId:E,policyVersion:1,capabilities:g.map(p=>p.id)},null,2)+`
37
+ `),i.writeFileSync(t.join(u,"capabilities.json"),JSON.stringify({capabilities:g.map(p=>({id:p.id,description:p.description,since:new Date().toISOString().slice(0,10),source:`template:${w}`}))},null,2)+`
38
+ `);for(const p of g)i.writeFileSync(t.join(I,`${p.id}.json`),JSON.stringify({id:`${p.id}-happy-path`,capability:p.id,description:`Happy path for ${p.description||p.id}`,steps:[{action:"invoke",target:p.id,input:{}},{action:"assert",field:"status",value:"success"}],capabilitiesCovered:[p.id]},null,2)+`
39
+ `);z(t.join(u,"CHANGELOG.md"),E),i.writeFileSync(t.join(u,"CONTEXT.md"),`# ${E} \u2014 infernoflow context
40
+
41
+ > Template: ${w} \u2014 ${r.description}
42
+
43
+ ## Hint
44
+ ${r.contextHint}
45
+
46
+ ## Capabilities (${g.length})
47
+ ${g.map(p=>`- \`${p.id}\`: ${p.description}`).join(`
48
+ `)}
49
+ `),r.scripts&&(info("Suggested package.json scripts for this template:"),Object.entries(r.scripts).forEach(([p,C])=>console.log(` ${bold(p)}: ${F(C)}`)),console.log()),K(`Initialised from template ${bold(f(w))} \u2014 ${bold(String(g.length))} capabilities`),console.log(),info(`Run ${f("infernoflow vibe")} to start vibe coding mode`),console.log();return}const S=e.includes("--cursor-hooks"),v=e.includes("--vscode-copilot-hooks"),b=e.includes("--report-json"),j=e.includes("--report-json-only"),O=e.includes("--report-human-only"),N=Y(e,"--lang"),U=Y(e,"--framework"),W=Y(e,"--project-type"),d=j;j&&O&&(console.error("Error: --report-json-only and --report-human-only cannot be used together."),process.exit(1)),d||se("init");const k=t.join(o,"inferno"),V=t.join(o,".github","workflows");i.existsSync(k)&&!s&&(d&&(console.log(JSON.stringify({ok:!1,error:"inferno_exists",hint:"Use --force to overwrite"},null,2)),process.exit(1)),_("inferno/ already exists. Use --force to overwrite."),console.log(),process.exit(0));const T=G(o),q="CreateTask, ReadTasks, UpdateTask, ToggleComplete, DeleteTask";let P=T,x=q.split(",").map(l=>l.trim());if(c){let r=H(o,{language:N||void 0,framework:U||void 0,projectType:W||void 0});if(!n&&!j){const g=L.createInterface({input:process.stdin,output:process.stdout}),p=r.developmentProfile||{},C=p.detected||{};console.log(F(` Review inferred development stack (press Enter to accept detected values)
50
+ `));const oe=await R(g,"Language",p.language||C.language||"unknown"),ne=await R(g,"Framework",p.framework||C.framework||"unknown"),ie=await R(g,"Project type",p.projectType||C.projectType||"unknown");g.close(),r=H(o,{language:oe,framework:ne,projectType:ie})}const u=r.capabilities,I=pe(u);j?console.log(JSON.stringify({mode:"adopt",policyId:T,inferredCapabilities:I,components:r.components,displayFields:r.displayFields,externalLibraries:r.externalLibraries,uiLayout:r.uiLayout,styling:r.styling,developmentProfile:r.developmentProfile,apiCalls:r.apiCalls},null,2)):(console.log(),console.log(F(ae(u))),console.log(),console.log(F(fe(r))),console.log(),b&&!O&&(console.log(JSON.stringify({mode:"adopt",policyId:T,inferredCapabilities:I,components:r.components,displayFields:r.displayFields,externalLibraries:r.externalLibraries,uiLayout:r.uiLayout,styling:r.styling,developmentProfile:r.developmentProfile,apiCalls:r.apiCalls},null,2)),console.log()));const E=await ce(u,n);P=T,x=E.map(g=>g.id)}else if(!n){const l=L.createInterface({input:process.stdin,output:process.stdout});console.log(F(` Press Enter to accept defaults
51
+ `)),P=await R(l,"Project / policy name",T),x=(await R(l,"Capabilities (comma-separated)",q)).split(",").map(u=>u.trim()).filter(Boolean),l.close(),console.log()}if(i.mkdirSync(k,{recursive:!0}),c){const l=x.map(u=>({id:u,title:u.replace(/([A-Z])/g," $1").trim()})),r=H(o,{language:N||void 0,framework:U||void 0,projectType:W||void 0});le(k,P,l,r),d||(y("Created: "+f("inferno/contract.json")),y("Created: "+f("inferno/capabilities.json")),y("Created: "+f("inferno/scenarios/adoption_baseline.json")),y("Created: "+f("inferno/adoption_profile.json")),y("Created: "+f("inferno/CHANGELOG.md")))}else je(t.join(k,"contract.json"),P,x),d||y("Created: "+f("inferno/contract.json")),he(t.join(k,"capabilities.json"),x),d||y("Created: "+f("inferno/capabilities.json")),ke(t.join(k,"scenarios"),x),d||y("Created: "+f("inferno/scenarios/happy_path.json")),z(t.join(k,"CHANGELOG.md"),P),d||y("Created: "+f("inferno/CHANGELOG.md"));const $=ye(),M=t.join($,"scripts","inferno-doc-gate.mjs"),B=t.join(o,"scripts","inferno-doc-gate.mjs");D(M,B,s,d);const X=t.join($,"scripts","inferno-install-hooks.mjs"),Q=t.join(o,"scripts","inferno-install-hooks.mjs");D(X,Q,s,d);const Z=t.join($,"ci","github-inferno-check.yml"),ee=t.join(V,"infernoflow-check.yml");if(D(Z,ee,s,d),we(o,d),S&&de({cwd:o,templatesRoot:$,force:s,silent:d,logOk:l=>{d||y(l)},logWarn:l=>{d||_(l)}}),v&&ue({cwd:o,templatesRoot:$,force:s,silent:d,logOk:l=>{d||y(l)},logWarn:l=>{d||_(l)}}),c){const l=t.join(k,"context-state.json");let r={};try{r=JSON.parse(i.readFileSync(l,"utf8"))}catch{}const u=H(o,{language:N||void 0,framework:U||void 0,projectType:W||void 0});r.stack=u.developmentProfile,i.writeFileSync(l,JSON.stringify(r,null,2)+`
52
+ `,"utf8"),d||y("Created: "+f("inferno/context-state.json"))}if(!d){K("infernoflow initialized!");const l=t.join(k,"integrations.json");!(process.env.ANTHROPIC_API_KEY||process.env.OPENAI_API_KEY||process.env.GOOGLE_AI_API_KEY||process.env.OPENROUTER_API_KEY||process.env.GEMINI_API_KEY)&&!i.existsSync(l)&&(console.log(),console.log(` ${J("\u{1F4A1}")} ${bold("Tip:")} connect an AI provider for explain, why, review, and changelog AI.`),console.log(` ${f("infernoflow ai setup")} \u2014 takes 60 seconds`)),re([f("infernoflow status")+" \u2014 see your contract at a glance",f("infernoflow check")+" \u2014 validate everything",(c?"Review inferred baseline in ":"Edit ")+J("inferno/capabilities.json")+(c?" and refine IDs/titles":" to describe each capability in detail"),"Add more "+J("inferno/scenarios/*.json")+" files for edge cases","Add "+f("inferno:check")+" to your CI pipeline",...S?["Restart Cursor \u2014 hooks write assistant text to "+J("inferno/CONTEXT.draft.md"),"Promote when ready: "+f("npm run inferno:promote-draft -- --append-notes")]:[],...v?["Restart VS Code \u2014 Copilot hooks append prompts + assistant (from transcript) to "+J("inferno/CONTEXT.draft.md"),"Promote when ready: "+f("npm run inferno:promote-draft -- --append-notes")]:[],...!S&&!v?["Optional: "+f("infernoflow install-cursor-hooks")+" or "+f("infernoflow install-vscode-copilot-hooks")]:[]])}}export{Fe as initCommand};