infernoflow 0.10.2 → 0.10.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.
package/README.md CHANGED
@@ -68,10 +68,17 @@ JSON-only output (clean machine output, no text logs):
68
68
  infernoflow init --adopt --yes --report-json-only
69
69
  ```
70
70
 
71
+ Human-only output (visual report only, no JSON block):
72
+
73
+ ```bash
74
+ infernoflow init --adopt --yes --report-human-only
75
+ ```
76
+
71
77
  What adoption creates:
72
78
  - `inferno/contract.json` (inferred capability baseline)
73
79
  - `inferno/capabilities.json` (inferred registry)
74
80
  - `inferno/scenarios/adoption_baseline.json` (coverage baseline)
81
+ - `inferno/adoption_profile.json` (detected components, display fields, external libraries, UI layout, styling hints)
75
82
  - `inferno/CHANGELOG.md` (adoption entry)
76
83
 
77
84
  Safety:
@@ -155,6 +162,7 @@ infernoflow doc-gate --json
155
162
  infernoflow init --force # overwrite existing files
156
163
  infernoflow init --yes # skip prompts, use defaults
157
164
  infernoflow init --adopt # infer baseline from existing project
165
+ infernoflow init --adopt --report-human-only
158
166
  infernoflow suggest "..." # describe what changed
159
167
  infernoflow implement "..." --mode both
160
168
  infernoflow implement "..." --mode cursor
@@ -47,6 +47,7 @@ ${formatCommandsHelp()}
47
47
  --adopt Infer capabilities from an existing codebase
48
48
  --report-json Print inferred adoption report as JSON
49
49
  --report-json-only Print JSON report only (no human-readable logs)
50
+ --report-human-only Print only human-readable adoption report (no JSON block)
50
51
  --yes, -y Skip prompts and accept inferred/default values
51
52
  --force, -f Overwrite existing inferno/ files
52
53
 
@@ -38,6 +38,10 @@ const HEURISTICS = [
38
38
  ];
39
39
 
40
40
  export function discoverCapabilities(cwd) {
41
+ return discoverProjectSignals(cwd).capabilities;
42
+ }
43
+
44
+ function collectCodeFiles(cwd) {
41
45
  const files = [];
42
46
  const roots = ["src", "server", "app", "backend", "frontend", "api"];
43
47
  for (const r of roots) {
@@ -51,13 +55,171 @@ export function discoverCapabilities(cwd) {
51
55
  if (entry.isDirectory()) {
52
56
  if (["node_modules", ".git", "dist", "build"].includes(entry.name)) continue;
53
57
  stack.push(p);
54
- } else if (/\.(js|jsx|ts|tsx|mjs|cjs|json|md)$/.test(entry.name)) {
58
+ } else if (/\.(js|jsx|ts|tsx|mjs|cjs|json|md|html|htm)$/.test(entry.name)) {
55
59
  files.push(p);
56
60
  }
57
61
  }
58
62
  }
59
63
  }
64
+ return files;
65
+ }
66
+
67
+ function detectComponents(files, cwd) {
68
+ const names = new Set();
69
+ for (const filePath of files) {
70
+ const rel = path.relative(cwd, filePath);
71
+ const text = safeRead(filePath);
72
+ const classMatches = text.matchAll(/\bclass\s+([A-Z][A-Za-z0-9_]*?(?:Component|Page|View|Widget|Card))\b/g);
73
+ for (const m of classMatches) names.add(m[1]);
74
+ const selectorMatches = text.matchAll(/\bselector\s*:\s*["']([^"']+)["']/g);
75
+ for (const m of selectorMatches) names.add(m[1]);
76
+ const reactFnMatches = text.matchAll(/\bfunction\s+([A-Z][A-Za-z0-9_]*)\s*\(/g);
77
+ for (const m of reactFnMatches) {
78
+ if (/component|page|view|card|chart|dashboard/i.test(m[1])) names.add(m[1]);
79
+ }
80
+ const relMatch = rel.match(/([^/\\]+)\.(component|page|view|widget|card)\.(ts|tsx|js|jsx)$/i);
81
+ if (relMatch) names.add(relMatch[1]);
82
+ }
83
+ return Array.from(names).sort();
84
+ }
85
+
86
+ function detectDisplayFields(files) {
87
+ const fields = new Set();
88
+ const methodNames = new Set();
89
+ const stopWords = new Set([
90
+ "if", "for", "while", "const", "let", "var", "return", "function", "class", "import", "export",
91
+ "null", "undefined", "true", "false", "string", "number", "boolean", "any", "unknown", "never",
92
+ "selector", "templateUrl", "styleUrl", "standalone", "imports", "providers", "providedIn",
93
+ "options", "scales", "responsive", "display", "title", "type", "label",
94
+ "component", "service", "routes", "appConfig", "ApplicationConfig",
95
+ ]);
96
+ const add = (v) => {
97
+ if (!v) return;
98
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(v)) return;
99
+ if (v.length <= 1) return;
100
+ if (stopWords.has(v)) return;
101
+ if (/^[A-Z0-9_]+$/.test(v)) return;
102
+ fields.add(v);
103
+ };
104
+ for (const filePath of files) {
105
+ const text = safeRead(filePath);
106
+ if (/\.(html|htm)$/i.test(filePath)) {
107
+ const angularInterpolations = text.matchAll(/\{\{\s*(?:this\.)?([a-zA-Z_][a-zA-Z0-9_]*)/g);
108
+ for (const m of angularInterpolations) add(m[1]);
109
+ const ngModels = text.matchAll(/\[\(ngModel\)\]\s*=\s*["']([a-zA-Z_][a-zA-Z0-9_]*)["']/g);
110
+ for (const m of ngModels) add(m[1]);
111
+ const ngInputs = text.matchAll(/\[[a-zA-Z0-9_-]+\]\s*=\s*["'](?:this\.)?([a-zA-Z_][a-zA-Z0-9_]*)["']/g);
112
+ for (const m of ngInputs) add(m[1]);
113
+ const ngIfs = text.matchAll(/\*ngIf\s*=\s*["'](?:this\.)?([a-zA-Z_][a-zA-Z0-9_]*)/g);
114
+ for (const m of ngIfs) add(m[1]);
115
+ }
116
+ if (/\.(ts|tsx|js|jsx|mjs|cjs)$/i.test(filePath)) {
117
+ const methodDecl = text.matchAll(
118
+ /(?:^|\n)\s*(?:public|private|protected)?\s*(?:async\s+)?([a-zA-Z_][a-zA-Z0-9_]*)\s*\([^)]*\)\s*\{/g
119
+ );
120
+ for (const m of methodDecl) methodNames.add(m[1]);
121
+
122
+ const thisRefs = text.matchAll(/\bthis\.([a-zA-Z_][a-zA-Z0-9_]*)\b/g);
123
+ for (const m of thisRefs) add(m[1]);
124
+
125
+ const classProps = text.matchAll(
126
+ /(?:^|\n)\s*(?:public|private|protected)?\s*(?:readonly\s+)?([a-zA-Z_][a-zA-Z0-9_]*)\s*(?::|=)/g
127
+ );
128
+ for (const m of classProps) {
129
+ add(m[1]);
130
+ }
131
+
132
+ const inputProps = text.matchAll(/@Input\([^)]*\)\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*[:=]/g);
133
+ for (const m of inputProps) add(m[1]);
134
+
135
+ const forEachParams = text.matchAll(/forEach\(\((\w+)\)\s*=>/g);
136
+ for (const m of forEachParams) {
137
+ const item = m[1];
138
+ const propAccess = new RegExp(`\\b${item}\\.([a-zA-Z_][a-zA-Z0-9_]*)\\b`, "g");
139
+ for (const p of text.matchAll(propAccess)) add(p[1]);
140
+ }
141
+ }
142
+ }
143
+ return Array.from(fields)
144
+ .filter((name) => !methodNames.has(name))
145
+ .sort()
146
+ .slice(0, 80);
147
+ }
148
+
149
+ function detectExternalLibraries(cwd) {
150
+ const libs = new Set();
151
+ const pkgPath = path.join(cwd, "package.json");
152
+ if (!fs.existsSync(pkgPath)) return [];
153
+ try {
154
+ const pkg = JSON.parse(safeRead(pkgPath) || "{}");
155
+ const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
156
+ for (const name of Object.keys(deps)) libs.add(name);
157
+ } catch {}
158
+ return Array.from(libs).sort();
159
+ }
160
+
161
+ function detectStyling(cwd, files, externalLibraries) {
162
+ const styleFiles = files
163
+ .filter((f) => /\.(css|scss|sass|less|styl)$/i.test(f))
164
+ .map((f) => path.relative(cwd, f))
165
+ .sort();
166
+
167
+ const frameworks = [];
168
+ const hasDep = (name) => externalLibraries.includes(name);
169
+ if (hasDep("tailwindcss")) frameworks.push("Tailwind CSS");
170
+ if (hasDep("bootstrap")) frameworks.push("Bootstrap");
171
+ if (externalLibraries.some((lib) => lib.startsWith("@angular/material"))) frameworks.push("Angular Material");
172
+ if (hasDep("antd")) frameworks.push("Ant Design");
173
+ if (hasDep("styled-components")) frameworks.push("styled-components");
174
+ if (hasDep("emotion") || hasDep("@emotion/react")) frameworks.push("Emotion");
175
+
176
+ const tokenVars = new Set();
177
+ for (const filePath of files) {
178
+ if (!/\.(css|scss|sass|less|styl|html|htm|ts|tsx|js|jsx|mjs|cjs)$/i.test(filePath)) continue;
179
+ const text = safeRead(filePath);
180
+ for (const m of text.matchAll(/--([a-zA-Z][a-zA-Z0-9_-]*)/g)) tokenVars.add(`--${m[1]}`);
181
+ }
182
+
183
+ return {
184
+ cssFrameworks: frameworks,
185
+ styleFileCount: styleFiles.length,
186
+ styleFilesSample: styleFiles.slice(0, 12),
187
+ designTokens: Array.from(tokenVars).sort().slice(0, 24),
188
+ };
189
+ }
190
+
191
+ function detectUiLayout(files) {
192
+ let usesGrid = false;
193
+ let usesFlex = false;
194
+ const sections = new Set();
195
+
196
+ for (const filePath of files) {
197
+ if (!/\.(html|htm|tsx|jsx|ts|js|mjs|cjs)$/i.test(filePath)) continue;
198
+ const text = safeRead(filePath);
199
+
200
+ if (/\bgrid\b|grid-template|grid-cols-|display:\s*grid/i.test(text)) usesGrid = true;
201
+ if (/\bflex\b|display:\s*flex|flex-row|flex-col|justify-|items-/i.test(text)) usesFlex = true;
202
+
203
+ for (const m of text.matchAll(/<(main|header|footer|section|aside|nav)\b/gi)) {
204
+ sections.add(m[1].toLowerCase());
205
+ }
206
+ for (const m of text.matchAll(/class(?:Name)?\s*=\s*["'`][^"'`]*(dashboard|chart|card|sidebar|content|toolbar|filter|panel|table)[^"'`]*["'`]/gi)) {
207
+ const hit = m[1].toLowerCase();
208
+ sections.add(hit === "filter" ? "filters" : hit);
209
+ }
210
+ }
211
+
212
+ const layoutType = usesGrid && usesFlex ? "mixed" : usesGrid ? "grid" : usesFlex ? "flex" : "unknown";
213
+ return {
214
+ layoutType,
215
+ usesGrid,
216
+ usesFlex,
217
+ sections: Array.from(sections).sort(),
218
+ };
219
+ }
60
220
 
221
+ export function discoverProjectSignals(cwd) {
222
+ const files = collectCodeFiles(cwd);
61
223
  const inferred = new Map();
62
224
  const addHit = (cap, filePath) => {
63
225
  if (!inferred.has(cap.id)) {
@@ -96,10 +258,19 @@ export function discoverCapabilities(cwd) {
96
258
  inferred.set("ReadItems", { id: "ReadItems", title: "Read Items", reason: "Fallback default", sourceFiles: new Set() });
97
259
  }
98
260
 
99
- return Array.from(inferred.values()).map((c) => ({
261
+ const capabilities = Array.from(inferred.values()).map((c) => ({
100
262
  ...c,
101
263
  sourceFiles: Array.from(c.sourceFiles || []),
102
264
  }));
265
+ const externalLibraries = detectExternalLibraries(cwd);
266
+ return {
267
+ capabilities,
268
+ components: detectComponents(files, cwd),
269
+ displayFields: detectDisplayFields(files),
270
+ externalLibraries,
271
+ uiLayout: detectUiLayout(files),
272
+ styling: detectStyling(cwd, files, externalLibraries),
273
+ };
103
274
  }
104
275
 
105
276
  export async function reviewCapabilitiesInteractive(capabilities, yes = false) {
@@ -122,19 +293,83 @@ export async function reviewCapabilitiesInteractive(capabilities, yes = false) {
122
293
 
123
294
  export function buildAdoptionReport(capabilities) {
124
295
  if (!capabilities.length) return "No capabilities inferred.";
125
- const lines = ["Inferred capabilities report:"];
126
- for (const c of summarizeCapabilities(capabilities)) {
127
- lines.push(`- ${c.id} (${c.title}) [confidence: ${c.confidence}]`);
296
+ const summarized = summarizeCapabilities(capabilities);
297
+ const totalSignals = summarized.reduce((acc, c) => acc + c.signalCount, 0);
298
+ const byConfidence = {
299
+ high: summarized.filter((c) => c.confidence === "high").length,
300
+ medium: summarized.filter((c) => c.confidence === "medium").length,
301
+ low: summarized.filter((c) => c.confidence === "low").length,
302
+ };
303
+
304
+ const lines = [];
305
+ lines.push("Adoption Analysis");
306
+ lines.push("=".repeat(56));
307
+ lines.push(`Capabilities detected : ${summarized.length}`);
308
+ lines.push(`Signal hits total : ${totalSignals}`);
309
+ lines.push(
310
+ `Confidence mix : high=${byConfidence.high}, medium=${byConfidence.medium}, low=${byConfidence.low}`
311
+ );
312
+ lines.push("-".repeat(56));
313
+ lines.push("Capability Breakdown");
314
+ lines.push("-".repeat(56));
315
+ lines.push("Confidence Signals Capability");
316
+ lines.push("-".repeat(56));
317
+ for (const c of summarized) {
318
+ const confidence = c.confidence.toUpperCase().padEnd(10, " ");
319
+ const signals = String(c.signalCount).padEnd(7, " ");
320
+ lines.push(`${confidence} ${signals} ${c.id} (${c.title})`);
128
321
  if (c.signalCount > 0) {
129
322
  const sample = c.sourceFiles.slice(0, 3).join(", ");
130
323
  lines.push(` sources: ${sample}`);
324
+ if (c.sourceFiles.length > 3) {
325
+ lines.push(` more : +${c.sourceFiles.length - 3} additional files`);
326
+ }
131
327
  } else {
132
- lines.push(` sources: inferred fallback (no strong code signal)`);
328
+ lines.push(" sources: inferred fallback (no strong code signal)");
133
329
  }
134
330
  }
331
+ lines.push("=".repeat(56));
135
332
  return lines.join("\n");
136
333
  }
137
334
 
335
+ export function buildSignalsReport(signals) {
336
+ const formatList = (title, items, limit = 10) => {
337
+ const lines = [`${title} (${items.length})`];
338
+ lines.push("-".repeat(56));
339
+ if (!items.length) {
340
+ lines.push(" - none");
341
+ return lines.join("\n");
342
+ }
343
+ for (const item of items.slice(0, limit)) {
344
+ lines.push(` - ${item}`);
345
+ }
346
+ if (items.length > limit) {
347
+ lines.push(` - ... +${items.length - limit} more`);
348
+ }
349
+ return lines.join("\n");
350
+ };
351
+
352
+ return [
353
+ "Project Structure Signals",
354
+ "=".repeat(56),
355
+ formatList("Components", signals.components || []),
356
+ formatList("Display fields", signals.displayFields || []),
357
+ formatList("External libraries", signals.externalLibraries || []),
358
+ "UI layout",
359
+ "-".repeat(56),
360
+ ` - layout type: ${signals.uiLayout?.layoutType || "unknown"}`,
361
+ ` - uses grid : ${signals.uiLayout?.usesGrid ? "yes" : "no"}`,
362
+ ` - uses flex : ${signals.uiLayout?.usesFlex ? "yes" : "no"}`,
363
+ ` - sections : ${(signals.uiLayout?.sections || []).slice(0, 10).join(", ") || "none"}`,
364
+ "Styling",
365
+ "-".repeat(56),
366
+ ` - frameworks : ${(signals.styling?.cssFrameworks || []).join(", ") || "none detected"}`,
367
+ ` - style files: ${signals.styling?.styleFileCount ?? 0}`,
368
+ ` - tokens : ${(signals.styling?.designTokens || []).slice(0, 8).join(", ") || "none detected"}`,
369
+ "=".repeat(56),
370
+ ].join("\n");
371
+ }
372
+
138
373
  export function summarizeCapabilities(capabilities) {
139
374
  return capabilities.map((c) => {
140
375
  const hits = c.sourceFiles?.length || 0;
@@ -150,7 +385,7 @@ export function summarizeCapabilities(capabilities) {
150
385
  });
151
386
  }
152
387
 
153
- export function writeAdoptionBaseline(infernoDir, policyId, capabilities) {
388
+ export function writeAdoptionBaseline(infernoDir, policyId, capabilities, signals = null) {
154
389
  const capIds = capabilities.map((c) => c.id);
155
390
  const contract = {
156
391
  policyId,
@@ -179,11 +414,25 @@ export function writeAdoptionBaseline(infernoDir, policyId, capabilities) {
179
414
  };
180
415
  fs.writeFileSync(path.join(infernoDir, "scenarios", "adoption_baseline.json"), JSON.stringify(scenario, null, 2) + "\n");
181
416
 
417
+ if (signals) {
418
+ const profile = {
419
+ profileId: "adoption_profile",
420
+ generatedAt: new Date().toISOString(),
421
+ components: signals.components || [],
422
+ displayFields: signals.displayFields || [],
423
+ externalLibraries: signals.externalLibraries || [],
424
+ uiLayout: signals.uiLayout || { layoutType: "unknown", usesGrid: false, usesFlex: false, sections: [] },
425
+ styling: signals.styling || { cssFrameworks: [], styleFileCount: 0, styleFilesSample: [], designTokens: [] },
426
+ };
427
+ fs.writeFileSync(path.join(infernoDir, "adoption_profile.json"), JSON.stringify(profile, null, 2) + "\n");
428
+ }
429
+
182
430
  const changelog = `# Changelog — ${policyId}
183
431
 
184
432
  ## Unreleased
185
433
 
186
434
  - Adopted infernoflow into an existing project and generated baseline capabilities.
435
+ - Captured detected components, display fields, and external libraries in adoption profile.
187
436
 
188
437
  ## 0.1.0 — Adoption baseline
189
438
 
@@ -3,7 +3,14 @@ import * as path from "node:path";
3
3
  import * as readline from "node:readline";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { header, ok, warn, done, nextSteps, cyan, yellow, gray } from "../ui/output.mjs";
6
- import { discoverCapabilities, reviewCapabilitiesInteractive, writeAdoptionBaseline, buildAdoptionReport, summarizeCapabilities } from "./adopt.mjs";
6
+ import {
7
+ discoverProjectSignals,
8
+ reviewCapabilitiesInteractive,
9
+ writeAdoptionBaseline,
10
+ buildAdoptionReport,
11
+ summarizeCapabilities,
12
+ buildSignalsReport,
13
+ } from "./adopt.mjs";
7
14
 
8
15
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
16
 
@@ -136,14 +143,21 @@ export async function initCommand(args) {
136
143
  const adopt = args.includes("--adopt");
137
144
  const reportJson = args.includes("--report-json");
138
145
  const reportJsonOnly = args.includes("--report-json-only");
146
+ const reportHumanOnly = args.includes("--report-human-only");
147
+ const silent = reportJsonOnly;
139
148
 
140
- if (!reportJsonOnly) {
149
+ if (reportJsonOnly && reportHumanOnly) {
150
+ console.error("Error: --report-json-only and --report-human-only cannot be used together.");
151
+ process.exit(1);
152
+ }
153
+
154
+ if (!silent) {
141
155
  header("init");
142
156
  }
143
157
 
144
158
  const infernoDir = path.join(cwd, "inferno");
145
159
  if (fs.existsSync(infernoDir) && !force) {
146
- if (reportJsonOnly) {
160
+ if (silent) {
147
161
  console.log(JSON.stringify({ ok: false, error: "inferno_exists", hint: "Use --force to overwrite" }, null, 2));
148
162
  process.exit(1);
149
163
  }
@@ -159,12 +173,22 @@ export async function initCommand(args) {
159
173
  let capabilities = defaultCaps.split(",").map(c => c.trim());
160
174
 
161
175
  if (adopt) {
162
- const inferred = discoverCapabilities(cwd);
176
+ const signals = discoverProjectSignals(cwd);
177
+ const inferred = signals.capabilities;
163
178
  const summarized = summarizeCapabilities(inferred);
164
179
  if (reportJsonOnly) {
165
180
  console.log(
166
181
  JSON.stringify(
167
- { mode: "adopt", policyId: detectedName, inferredCapabilities: summarized },
182
+ {
183
+ mode: "adopt",
184
+ policyId: detectedName,
185
+ inferredCapabilities: summarized,
186
+ components: signals.components,
187
+ displayFields: signals.displayFields,
188
+ externalLibraries: signals.externalLibraries,
189
+ uiLayout: signals.uiLayout,
190
+ styling: signals.styling,
191
+ },
168
192
  null,
169
193
  2
170
194
  )
@@ -173,13 +197,20 @@ export async function initCommand(args) {
173
197
  console.log();
174
198
  console.log(gray(buildAdoptionReport(inferred)));
175
199
  console.log();
176
- if (reportJson) {
200
+ console.log(gray(buildSignalsReport(signals)));
201
+ console.log();
202
+ if (reportJson && !reportHumanOnly) {
177
203
  console.log(
178
204
  JSON.stringify(
179
205
  {
180
206
  mode: "adopt",
181
207
  policyId: detectedName,
182
208
  inferredCapabilities: summarized,
209
+ components: signals.components,
210
+ displayFields: signals.displayFields,
211
+ externalLibraries: signals.externalLibraries,
212
+ uiLayout: signals.uiLayout,
213
+ styling: signals.styling,
183
214
  },
184
215
  null,
185
216
  2
@@ -209,36 +240,38 @@ export async function initCommand(args) {
209
240
  id,
210
241
  title: id.replace(/([A-Z])/g, " $1").trim(),
211
242
  }));
212
- writeAdoptionBaseline(infernoDir, policyId, capDetails);
213
- if (!reportJsonOnly) {
243
+ const signals = discoverProjectSignals(cwd);
244
+ writeAdoptionBaseline(infernoDir, policyId, capDetails, signals);
245
+ if (!silent) {
214
246
  ok("Created: " + cyan("inferno/contract.json"));
215
247
  ok("Created: " + cyan("inferno/capabilities.json"));
216
248
  ok("Created: " + cyan("inferno/scenarios/adoption_baseline.json"));
249
+ ok("Created: " + cyan("inferno/adoption_profile.json"));
217
250
  ok("Created: " + cyan("inferno/CHANGELOG.md"));
218
251
  }
219
252
  } else {
220
253
  writeContract(path.join(infernoDir, "contract.json"), policyId, capabilities);
221
- if (!reportJsonOnly) ok("Created: " + cyan("inferno/contract.json"));
254
+ if (!silent) ok("Created: " + cyan("inferno/contract.json"));
222
255
 
223
256
  writeCapabilities(path.join(infernoDir, "capabilities.json"), capabilities);
224
- if (!reportJsonOnly) ok("Created: " + cyan("inferno/capabilities.json"));
257
+ if (!silent) ok("Created: " + cyan("inferno/capabilities.json"));
225
258
 
226
259
  writeScenario(path.join(infernoDir, "scenarios"), capabilities);
227
- if (!reportJsonOnly) ok("Created: " + cyan("inferno/scenarios/happy_path.json"));
260
+ if (!silent) ok("Created: " + cyan("inferno/scenarios/happy_path.json"));
228
261
 
229
262
  writeChangelog(path.join(infernoDir, "CHANGELOG.md"), policyId);
230
- if (!reportJsonOnly) ok("Created: " + cyan("inferno/CHANGELOG.md"));
263
+ if (!silent) ok("Created: " + cyan("inferno/CHANGELOG.md"));
231
264
  }
232
265
 
233
266
  // Copy doc-gate script
234
267
  const templates = getTemplatesRoot();
235
268
  const srcScript = path.join(templates, "scripts", "inferno-doc-gate.mjs");
236
269
  const dstScript = path.join(cwd, "scripts", "inferno-doc-gate.mjs");
237
- copyFile(srcScript, dstScript, force, reportJsonOnly);
270
+ copyFile(srcScript, dstScript, force, silent);
238
271
 
239
- upsertScripts(cwd, reportJsonOnly);
272
+ upsertScripts(cwd, silent);
240
273
 
241
- if (!reportJsonOnly) {
274
+ if (!silent) {
242
275
  done("infernoflow initialized!");
243
276
 
244
277
  nextSteps([
package/package.json CHANGED
@@ -1,43 +1,43 @@
1
- {
2
- "name": "infernoflow",
3
- "version": "0.10.2",
4
- "description": "The forge for liquid code — keep capabilities, contracts, and docs in sync.",
5
- "type": "module",
6
- "bin": {
7
- "infernoflow": "bin/infernoflow.mjs"
8
- },
9
- "engines": {
10
- "node": ">=18"
11
- },
12
- "files": [
13
- "bin",
14
- "lib",
15
- "templates",
16
- "README.md"
17
- ],
18
- "scripts": {
19
- "test": "node scripts/smoke.mjs && node scripts/json-smoke.mjs && node scripts/json-negative-smoke.mjs && node scripts/implement-smoke.mjs",
20
- "test:help": "node bin/infernoflow.mjs --help"
21
- },
22
- "keywords": [
23
- "cli",
24
- "capabilities",
25
- "contract",
26
- "documentation",
27
- "ai",
28
- "liquid-code",
29
- "dx",
30
- "developer-tools"
31
- ],
32
- "author": "infernoflow",
33
- "license": "MIT",
34
- "repository": {
35
- "type": "git",
36
- "url": "git+https://github.com/ronmiz/infernoflow.git"
37
- },
38
- "homepage": "https://github.com/ronmiz/infernoflow#readme",
39
- "bugs": {
40
- "url": "https://github.com/ronmiz/infernoflow/issues"
41
- },
42
- "dependencies": {}
43
- }
1
+ {
2
+ "name": "infernoflow",
3
+ "version": "0.10.4",
4
+ "description": "The forge for liquid code — keep capabilities, contracts, and docs in sync.",
5
+ "type": "module",
6
+ "bin": {
7
+ "infernoflow": "bin/infernoflow.mjs"
8
+ },
9
+ "engines": {
10
+ "node": ">=18"
11
+ },
12
+ "files": [
13
+ "bin",
14
+ "lib",
15
+ "templates",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "test": "node scripts/smoke.mjs && node scripts/json-smoke.mjs && node scripts/json-negative-smoke.mjs && node scripts/implement-smoke.mjs",
20
+ "test:help": "node bin/infernoflow.mjs --help"
21
+ },
22
+ "keywords": [
23
+ "cli",
24
+ "capabilities",
25
+ "contract",
26
+ "documentation",
27
+ "ai",
28
+ "liquid-code",
29
+ "dx",
30
+ "developer-tools"
31
+ ],
32
+ "author": "infernoflow",
33
+ "license": "MIT",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/ronmiz/infernoflow.git"
37
+ },
38
+ "homepage": "https://github.com/ronmiz/infernoflow#readme",
39
+ "bugs": {
40
+ "url": "https://github.com/ronmiz/infernoflow/issues"
41
+ },
42
+ "dependencies": {}
43
+ }