infernoflow 0.10.1 → 0.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -0
- package/bin/infernoflow.mjs +8 -1
- package/lib/commands/adopt.mjs +193 -0
- package/lib/commands/init.mjs +84 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,6 +41,43 @@ infernoflow check
|
|
|
41
41
|
infernoflow doc-gate
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
## Adopt Existing Project
|
|
45
|
+
|
|
46
|
+
Use this when your project already has code and you want InfernoFlow to bootstrap from current behavior.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# from existing project root
|
|
50
|
+
infernoflow init --adopt
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Non-interactive adoption:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
infernoflow init --adopt --yes
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
JSON report for CI/logging:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
infernoflow init --adopt --yes --report-json
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
JSON-only output (clean machine output, no text logs):
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
infernoflow init --adopt --yes --report-json-only
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
What adoption creates:
|
|
72
|
+
- `inferno/contract.json` (inferred capability baseline)
|
|
73
|
+
- `inferno/capabilities.json` (inferred registry)
|
|
74
|
+
- `inferno/scenarios/adoption_baseline.json` (coverage baseline)
|
|
75
|
+
- `inferno/CHANGELOG.md` (adoption entry)
|
|
76
|
+
|
|
77
|
+
Safety:
|
|
78
|
+
- Existing `inferno/` is not overwritten unless `--force` is provided.
|
|
79
|
+
- Adoption prints an inferred capability report with source-file hints and confidence.
|
|
80
|
+
|
|
44
81
|
## Recommended Workflow
|
|
45
82
|
|
|
46
83
|
```bash
|
|
@@ -117,6 +154,7 @@ infernoflow doc-gate --json
|
|
|
117
154
|
```bash
|
|
118
155
|
infernoflow init --force # overwrite existing files
|
|
119
156
|
infernoflow init --yes # skip prompts, use defaults
|
|
157
|
+
infernoflow init --adopt # infer baseline from existing project
|
|
120
158
|
infernoflow suggest "..." # describe what changed
|
|
121
159
|
infernoflow implement "..." --mode both
|
|
122
160
|
infernoflow implement "..." --mode cursor
|
package/bin/infernoflow.mjs
CHANGED
|
@@ -8,7 +8,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
8
8
|
const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf8"));
|
|
9
9
|
const VERSION = pkg.version || "0.0.0";
|
|
10
10
|
const COMMAND_DESCRIPTIONS = {
|
|
11
|
-
init: "Scaffold inferno/ in your project",
|
|
11
|
+
init: "Scaffold inferno/ in your project (or adopt existing project)",
|
|
12
12
|
check: "Validate contract, capabilities, scenarios, changelog",
|
|
13
13
|
status: "Show contract health at a glance",
|
|
14
14
|
"doc-gate": "Fail if code changed but docs were not updated",
|
|
@@ -43,6 +43,13 @@ const HELP = `
|
|
|
43
43
|
${bold("Commands:")}
|
|
44
44
|
${formatCommandsHelp()}
|
|
45
45
|
|
|
46
|
+
${bold("init options:")}
|
|
47
|
+
--adopt Infer capabilities from an existing codebase
|
|
48
|
+
--report-json Print inferred adoption report as JSON
|
|
49
|
+
--report-json-only Print JSON report only (no human-readable logs)
|
|
50
|
+
--yes, -y Skip prompts and accept inferred/default values
|
|
51
|
+
--force, -f Overwrite existing inferno/ files
|
|
52
|
+
|
|
46
53
|
${bold("context options:")}
|
|
47
54
|
--intent "..." What you plan to build next
|
|
48
55
|
--working "..." What you are building right now
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import * as readline from "node:readline";
|
|
4
|
+
|
|
5
|
+
function toCapabilityId(raw) {
|
|
6
|
+
return raw
|
|
7
|
+
.replace(/[^a-zA-Z0-9]+/g, " ")
|
|
8
|
+
.trim()
|
|
9
|
+
.split(/\s+/)
|
|
10
|
+
.filter(Boolean)
|
|
11
|
+
.map((w) => w[0].toUpperCase() + w.slice(1))
|
|
12
|
+
.join("");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function capTitle(id) {
|
|
16
|
+
return id.replace(/([A-Z])/g, " $1").trim();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function safeRead(filePath) {
|
|
20
|
+
try {
|
|
21
|
+
return fs.readFileSync(filePath, "utf8");
|
|
22
|
+
} catch {
|
|
23
|
+
return "";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const HEURISTICS = [
|
|
28
|
+
{ id: "CreateItem", title: "Create Item", regex: /\b(post|create|add)\b/i },
|
|
29
|
+
{ id: "ReadItems", title: "Read Items", regex: /\b(get|read|list|fetch)\b/i },
|
|
30
|
+
{ id: "UpdateItem", title: "Update Item", regex: /\b(put|patch|update|edit)\b/i },
|
|
31
|
+
{ id: "DeleteItem", title: "Delete Item", regex: /\b(delete|remove)\b/i },
|
|
32
|
+
{ id: "SearchItems", title: "Search Items", regex: /\bsearch\b/i },
|
|
33
|
+
{ id: "FilterItems", title: "Filter Items", regex: /\bfilter\b/i },
|
|
34
|
+
{ id: "SetDueDate", title: "Set Due Date", regex: /\bdueDate|deadline|due\b/i },
|
|
35
|
+
{ id: "SetPriority", title: "Set Priority", regex: /\bpriority\b/i },
|
|
36
|
+
{ id: "ToggleComplete", title: "Toggle Complete", regex: /\bcomplete|completed|toggle\b/i },
|
|
37
|
+
{ id: "ClearCompleted", title: "Clear Completed", regex: /\bclearCompleted|clear completed\b/i },
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
export function discoverCapabilities(cwd) {
|
|
41
|
+
const files = [];
|
|
42
|
+
const roots = ["src", "server", "app", "backend", "frontend", "api"];
|
|
43
|
+
for (const r of roots) {
|
|
44
|
+
const root = path.join(cwd, r);
|
|
45
|
+
if (!fs.existsSync(root)) continue;
|
|
46
|
+
const stack = [root];
|
|
47
|
+
while (stack.length) {
|
|
48
|
+
const cur = stack.pop();
|
|
49
|
+
for (const entry of fs.readdirSync(cur, { withFileTypes: true })) {
|
|
50
|
+
const p = path.join(cur, entry.name);
|
|
51
|
+
if (entry.isDirectory()) {
|
|
52
|
+
if (["node_modules", ".git", "dist", "build"].includes(entry.name)) continue;
|
|
53
|
+
stack.push(p);
|
|
54
|
+
} else if (/\.(js|jsx|ts|tsx|mjs|cjs|json|md)$/.test(entry.name)) {
|
|
55
|
+
files.push(p);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const inferred = new Map();
|
|
62
|
+
const addHit = (cap, filePath) => {
|
|
63
|
+
if (!inferred.has(cap.id)) {
|
|
64
|
+
inferred.set(cap.id, {
|
|
65
|
+
id: cap.id,
|
|
66
|
+
title: cap.title,
|
|
67
|
+
reason: "Detected from code signals",
|
|
68
|
+
sourceFiles: new Set(),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
inferred.get(cap.id).sourceFiles.add(path.relative(cwd, filePath));
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
for (const filePath of files) {
|
|
75
|
+
const text = safeRead(filePath);
|
|
76
|
+
for (const h of HEURISTICS) {
|
|
77
|
+
if (h.regex.test(text)) {
|
|
78
|
+
addHit(h, filePath);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const pkgPath = path.join(cwd, "package.json");
|
|
84
|
+
if (fs.existsSync(pkgPath)) {
|
|
85
|
+
const pkg = JSON.parse(safeRead(pkgPath) || "{}");
|
|
86
|
+
const name = typeof pkg.name === "string" ? pkg.name : path.basename(cwd);
|
|
87
|
+
const idHint = toCapabilityId(name);
|
|
88
|
+
if (idHint && !inferred.size) {
|
|
89
|
+
inferred.set("ReadItems", { id: "ReadItems", title: "Read Items", reason: `Fallback default for ${name}`, sourceFiles: new Set() });
|
|
90
|
+
inferred.set("CreateItem", { id: "CreateItem", title: "Create Item", reason: `Fallback default for ${name}`, sourceFiles: new Set() });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (!inferred.size) {
|
|
95
|
+
inferred.set("CreateItem", { id: "CreateItem", title: "Create Item", reason: "Fallback default", sourceFiles: new Set() });
|
|
96
|
+
inferred.set("ReadItems", { id: "ReadItems", title: "Read Items", reason: "Fallback default", sourceFiles: new Set() });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return Array.from(inferred.values()).map((c) => ({
|
|
100
|
+
...c,
|
|
101
|
+
sourceFiles: Array.from(c.sourceFiles || []),
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export async function reviewCapabilitiesInteractive(capabilities, yes = false) {
|
|
106
|
+
if (yes) return capabilities;
|
|
107
|
+
|
|
108
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
109
|
+
const list = capabilities.map((c) => c.id).join(", ");
|
|
110
|
+
const answer = await new Promise((resolve) =>
|
|
111
|
+
rl.question(` Inferred capabilities (${list}). Press Enter to accept or type comma list: `, resolve)
|
|
112
|
+
);
|
|
113
|
+
rl.close();
|
|
114
|
+
const trimmed = String(answer).trim();
|
|
115
|
+
if (!trimmed) return capabilities;
|
|
116
|
+
return trimmed
|
|
117
|
+
.split(",")
|
|
118
|
+
.map((s) => s.trim())
|
|
119
|
+
.filter(Boolean)
|
|
120
|
+
.map((id) => ({ id, title: capTitle(id), reason: "User provided during adopt review" }));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function buildAdoptionReport(capabilities) {
|
|
124
|
+
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}]`);
|
|
128
|
+
if (c.signalCount > 0) {
|
|
129
|
+
const sample = c.sourceFiles.slice(0, 3).join(", ");
|
|
130
|
+
lines.push(` sources: ${sample}`);
|
|
131
|
+
} else {
|
|
132
|
+
lines.push(` sources: inferred fallback (no strong code signal)`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return lines.join("\n");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function summarizeCapabilities(capabilities) {
|
|
139
|
+
return capabilities.map((c) => {
|
|
140
|
+
const hits = c.sourceFiles?.length || 0;
|
|
141
|
+
const confidence = hits >= 3 ? "high" : hits >= 1 ? "medium" : "low";
|
|
142
|
+
return {
|
|
143
|
+
id: c.id,
|
|
144
|
+
title: c.title,
|
|
145
|
+
reason: c.reason,
|
|
146
|
+
confidence,
|
|
147
|
+
sourceFiles: c.sourceFiles || [],
|
|
148
|
+
signalCount: hits,
|
|
149
|
+
};
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function writeAdoptionBaseline(infernoDir, policyId, capabilities) {
|
|
154
|
+
const capIds = capabilities.map((c) => c.id);
|
|
155
|
+
const contract = {
|
|
156
|
+
policyId,
|
|
157
|
+
policyVersion: 1,
|
|
158
|
+
capabilities: capIds,
|
|
159
|
+
rules: {
|
|
160
|
+
docsRequiredOnCapabilityChange: true,
|
|
161
|
+
requireScenarioForEachCapability: true,
|
|
162
|
+
requireChangelogOnCapabilityChange: true,
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
fs.mkdirSync(path.join(infernoDir, "scenarios"), { recursive: true });
|
|
166
|
+
fs.writeFileSync(path.join(infernoDir, "contract.json"), JSON.stringify(contract, null, 2) + "\n");
|
|
167
|
+
|
|
168
|
+
const registry = {
|
|
169
|
+
schemaVersion: 1,
|
|
170
|
+
capabilities: capabilities.map((c) => ({ id: c.id, title: c.title || capTitle(c.id), since: "0.1.0" })),
|
|
171
|
+
};
|
|
172
|
+
fs.writeFileSync(path.join(infernoDir, "capabilities.json"), JSON.stringify(registry, null, 2) + "\n");
|
|
173
|
+
|
|
174
|
+
const scenario = {
|
|
175
|
+
scenarioId: "adoption_baseline",
|
|
176
|
+
description: "Baseline inferred from existing codebase during adoption",
|
|
177
|
+
capabilitiesCovered: capIds,
|
|
178
|
+
steps: capIds.map((id) => ({ action: id, expect: `${id} behavior exists in the current project` })),
|
|
179
|
+
};
|
|
180
|
+
fs.writeFileSync(path.join(infernoDir, "scenarios", "adoption_baseline.json"), JSON.stringify(scenario, null, 2) + "\n");
|
|
181
|
+
|
|
182
|
+
const changelog = `# Changelog — ${policyId}
|
|
183
|
+
|
|
184
|
+
## Unreleased
|
|
185
|
+
|
|
186
|
+
- Adopted infernoflow into an existing project and generated baseline capabilities.
|
|
187
|
+
|
|
188
|
+
## 0.1.0 — Adoption baseline
|
|
189
|
+
|
|
190
|
+
- Initial baseline generated by infernoflow init --adopt
|
|
191
|
+
`;
|
|
192
|
+
fs.writeFileSync(path.join(infernoDir, "CHANGELOG.md"), changelog, "utf8");
|
|
193
|
+
}
|
package/lib/commands/init.mjs
CHANGED
|
@@ -3,6 +3,7 @@ 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
7
|
|
|
7
8
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
9
|
|
|
@@ -19,14 +20,14 @@ function ask(rl, question, defaultVal = "") {
|
|
|
19
20
|
});
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
function copyFile(src, dst, force) {
|
|
23
|
+
function copyFile(src, dst, force, silent = false) {
|
|
23
24
|
if (fs.existsSync(dst) && !force) {
|
|
24
|
-
warn("Skipped (exists): " + path.relative(process.cwd(), dst));
|
|
25
|
+
if (!silent) warn("Skipped (exists): " + path.relative(process.cwd(), dst));
|
|
25
26
|
return false;
|
|
26
27
|
}
|
|
27
28
|
fs.mkdirSync(path.dirname(dst), { recursive: true });
|
|
28
29
|
fs.copyFileSync(src, dst);
|
|
29
|
-
ok("Created: " + cyan(path.relative(process.cwd(), dst)));
|
|
30
|
+
if (!silent) ok("Created: " + cyan(path.relative(process.cwd(), dst)));
|
|
30
31
|
return true;
|
|
31
32
|
}
|
|
32
33
|
|
|
@@ -40,7 +41,7 @@ function copyDirDeep(srcDir, dstDir, force) {
|
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
function upsertScripts(cwd) {
|
|
44
|
+
function upsertScripts(cwd, silent = false) {
|
|
44
45
|
const pkgPath = path.join(cwd, "package.json");
|
|
45
46
|
if (!fs.existsSync(pkgPath)) return;
|
|
46
47
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
@@ -56,7 +57,7 @@ function upsertScripts(cwd) {
|
|
|
56
57
|
}
|
|
57
58
|
if (changed) {
|
|
58
59
|
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
59
|
-
ok("Updated " + cyan("package.json") + " scripts");
|
|
60
|
+
if (!silent) ok("Updated " + cyan("package.json") + " scripts");
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
|
|
@@ -132,11 +133,20 @@ export async function initCommand(args) {
|
|
|
132
133
|
const cwd = process.cwd();
|
|
133
134
|
const force = args.includes("--force") || args.includes("-f");
|
|
134
135
|
const yes = args.includes("--yes") || args.includes("-y");
|
|
136
|
+
const adopt = args.includes("--adopt");
|
|
137
|
+
const reportJson = args.includes("--report-json");
|
|
138
|
+
const reportJsonOnly = args.includes("--report-json-only");
|
|
135
139
|
|
|
136
|
-
|
|
140
|
+
if (!reportJsonOnly) {
|
|
141
|
+
header("init");
|
|
142
|
+
}
|
|
137
143
|
|
|
138
144
|
const infernoDir = path.join(cwd, "inferno");
|
|
139
145
|
if (fs.existsSync(infernoDir) && !force) {
|
|
146
|
+
if (reportJsonOnly) {
|
|
147
|
+
console.log(JSON.stringify({ ok: false, error: "inferno_exists", hint: "Use --force to overwrite" }, null, 2));
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
140
150
|
warn("inferno/ already exists. Use --force to overwrite.");
|
|
141
151
|
console.log();
|
|
142
152
|
process.exit(0);
|
|
@@ -148,7 +158,40 @@ export async function initCommand(args) {
|
|
|
148
158
|
let policyId = detectedName;
|
|
149
159
|
let capabilities = defaultCaps.split(",").map(c => c.trim());
|
|
150
160
|
|
|
151
|
-
if (
|
|
161
|
+
if (adopt) {
|
|
162
|
+
const inferred = discoverCapabilities(cwd);
|
|
163
|
+
const summarized = summarizeCapabilities(inferred);
|
|
164
|
+
if (reportJsonOnly) {
|
|
165
|
+
console.log(
|
|
166
|
+
JSON.stringify(
|
|
167
|
+
{ mode: "adopt", policyId: detectedName, inferredCapabilities: summarized },
|
|
168
|
+
null,
|
|
169
|
+
2
|
|
170
|
+
)
|
|
171
|
+
);
|
|
172
|
+
} else {
|
|
173
|
+
console.log();
|
|
174
|
+
console.log(gray(buildAdoptionReport(inferred)));
|
|
175
|
+
console.log();
|
|
176
|
+
if (reportJson) {
|
|
177
|
+
console.log(
|
|
178
|
+
JSON.stringify(
|
|
179
|
+
{
|
|
180
|
+
mode: "adopt",
|
|
181
|
+
policyId: detectedName,
|
|
182
|
+
inferredCapabilities: summarized,
|
|
183
|
+
},
|
|
184
|
+
null,
|
|
185
|
+
2
|
|
186
|
+
)
|
|
187
|
+
);
|
|
188
|
+
console.log();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const reviewed = await reviewCapabilitiesInteractive(inferred, yes);
|
|
192
|
+
policyId = detectedName;
|
|
193
|
+
capabilities = reviewed.map((c) => c.id);
|
|
194
|
+
} else if (!yes) {
|
|
152
195
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
153
196
|
console.log(gray(" Press Enter to accept defaults\n"));
|
|
154
197
|
policyId = await ask(rl, "Project / policy name", detectedName);
|
|
@@ -161,33 +204,49 @@ export async function initCommand(args) {
|
|
|
161
204
|
// Write files
|
|
162
205
|
fs.mkdirSync(infernoDir, { recursive: true });
|
|
163
206
|
|
|
164
|
-
|
|
165
|
-
|
|
207
|
+
if (adopt) {
|
|
208
|
+
const capDetails = capabilities.map((id) => ({
|
|
209
|
+
id,
|
|
210
|
+
title: id.replace(/([A-Z])/g, " $1").trim(),
|
|
211
|
+
}));
|
|
212
|
+
writeAdoptionBaseline(infernoDir, policyId, capDetails);
|
|
213
|
+
if (!reportJsonOnly) {
|
|
214
|
+
ok("Created: " + cyan("inferno/contract.json"));
|
|
215
|
+
ok("Created: " + cyan("inferno/capabilities.json"));
|
|
216
|
+
ok("Created: " + cyan("inferno/scenarios/adoption_baseline.json"));
|
|
217
|
+
ok("Created: " + cyan("inferno/CHANGELOG.md"));
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
writeContract(path.join(infernoDir, "contract.json"), policyId, capabilities);
|
|
221
|
+
if (!reportJsonOnly) ok("Created: " + cyan("inferno/contract.json"));
|
|
166
222
|
|
|
167
|
-
|
|
168
|
-
|
|
223
|
+
writeCapabilities(path.join(infernoDir, "capabilities.json"), capabilities);
|
|
224
|
+
if (!reportJsonOnly) ok("Created: " + cyan("inferno/capabilities.json"));
|
|
169
225
|
|
|
170
|
-
|
|
171
|
-
|
|
226
|
+
writeScenario(path.join(infernoDir, "scenarios"), capabilities);
|
|
227
|
+
if (!reportJsonOnly) ok("Created: " + cyan("inferno/scenarios/happy_path.json"));
|
|
172
228
|
|
|
173
|
-
|
|
174
|
-
|
|
229
|
+
writeChangelog(path.join(infernoDir, "CHANGELOG.md"), policyId);
|
|
230
|
+
if (!reportJsonOnly) ok("Created: " + cyan("inferno/CHANGELOG.md"));
|
|
231
|
+
}
|
|
175
232
|
|
|
176
233
|
// Copy doc-gate script
|
|
177
234
|
const templates = getTemplatesRoot();
|
|
178
235
|
const srcScript = path.join(templates, "scripts", "inferno-doc-gate.mjs");
|
|
179
236
|
const dstScript = path.join(cwd, "scripts", "inferno-doc-gate.mjs");
|
|
180
|
-
copyFile(srcScript, dstScript, force);
|
|
237
|
+
copyFile(srcScript, dstScript, force, reportJsonOnly);
|
|
181
238
|
|
|
182
|
-
upsertScripts(cwd);
|
|
239
|
+
upsertScripts(cwd, reportJsonOnly);
|
|
183
240
|
|
|
184
|
-
|
|
241
|
+
if (!reportJsonOnly) {
|
|
242
|
+
done("infernoflow initialized!");
|
|
185
243
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
244
|
+
nextSteps([
|
|
245
|
+
cyan("infernoflow status") + " — see your contract at a glance",
|
|
246
|
+
cyan("infernoflow check") + " — validate everything",
|
|
247
|
+
(adopt ? "Review inferred baseline in " : "Edit ") + yellow("inferno/capabilities.json") + (adopt ? " and refine IDs/titles" : " to describe each capability in detail"),
|
|
248
|
+
"Add more " + yellow("inferno/scenarios/*.json") + " files for edge cases",
|
|
249
|
+
"Add " + cyan("inferno:check") + " to your CI pipeline"
|
|
250
|
+
]);
|
|
251
|
+
}
|
|
193
252
|
}
|