infernoflow 0.10.4 → 0.10.5
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 +8 -0
- package/bin/infernoflow.mjs +3 -0
- package/lib/commands/adopt.mjs +66 -1
- package/lib/commands/init.mjs +51 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,6 +56,12 @@ Non-interactive adoption:
|
|
|
56
56
|
infernoflow init --adopt --yes
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
+
Override detected stack during adoption:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
infernoflow init --adopt --lang ts --framework angular --project-type frontend
|
|
63
|
+
```
|
|
64
|
+
|
|
59
65
|
JSON report for CI/logging:
|
|
60
66
|
|
|
61
67
|
```bash
|
|
@@ -79,6 +85,7 @@ What adoption creates:
|
|
|
79
85
|
- `inferno/capabilities.json` (inferred registry)
|
|
80
86
|
- `inferno/scenarios/adoption_baseline.json` (coverage baseline)
|
|
81
87
|
- `inferno/adoption_profile.json` (detected components, display fields, external libraries, UI layout, styling hints)
|
|
88
|
+
- `inferno/context-state.json` (saved development profile: language/framework/project type)
|
|
82
89
|
- `inferno/CHANGELOG.md` (adoption entry)
|
|
83
90
|
|
|
84
91
|
Safety:
|
|
@@ -162,6 +169,7 @@ infernoflow doc-gate --json
|
|
|
162
169
|
infernoflow init --force # overwrite existing files
|
|
163
170
|
infernoflow init --yes # skip prompts, use defaults
|
|
164
171
|
infernoflow init --adopt # infer baseline from existing project
|
|
172
|
+
infernoflow init --adopt --lang ts --framework react --project-type frontend
|
|
165
173
|
infernoflow init --adopt --report-human-only
|
|
166
174
|
infernoflow suggest "..." # describe what changed
|
|
167
175
|
infernoflow implement "..." --mode both
|
package/bin/infernoflow.mjs
CHANGED
|
@@ -45,6 +45,9 @@ ${formatCommandsHelp()}
|
|
|
45
45
|
|
|
46
46
|
${bold("init options:")}
|
|
47
47
|
--adopt Infer capabilities from an existing codebase
|
|
48
|
+
--lang <name> Override detected language (e.g. ts, js, py)
|
|
49
|
+
--framework <name> Override detected framework (e.g. react, angular, express)
|
|
50
|
+
--project-type <t> Override project type (frontend|backend|fullstack|cli|library)
|
|
48
51
|
--report-json Print inferred adoption report as JSON
|
|
49
52
|
--report-json-only Print JSON report only (no human-readable logs)
|
|
50
53
|
--report-human-only Print only human-readable adoption report (no JSON block)
|
package/lib/commands/adopt.mjs
CHANGED
|
@@ -218,7 +218,60 @@ function detectUiLayout(files) {
|
|
|
218
218
|
};
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
|
|
221
|
+
function detectDevelopmentProfile(cwd, files, externalLibraries, overrides = {}) {
|
|
222
|
+
const extCount = { ts: 0, js: 0, py: 0, java: 0, go: 0, rb: 0, rs: 0, cs: 0, php: 0 };
|
|
223
|
+
for (const filePath of files) {
|
|
224
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
225
|
+
if (ext === ".ts" || ext === ".tsx") extCount.ts += 1;
|
|
226
|
+
if (ext === ".js" || ext === ".jsx" || ext === ".mjs" || ext === ".cjs") extCount.js += 1;
|
|
227
|
+
if (ext === ".py") extCount.py += 1;
|
|
228
|
+
if (ext === ".java") extCount.java += 1;
|
|
229
|
+
if (ext === ".go") extCount.go += 1;
|
|
230
|
+
if (ext === ".rb") extCount.rb += 1;
|
|
231
|
+
if (ext === ".rs") extCount.rs += 1;
|
|
232
|
+
if (ext === ".cs") extCount.cs += 1;
|
|
233
|
+
if (ext === ".php") extCount.php += 1;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const sortedLang = Object.entries(extCount).sort((a, b) => b[1] - a[1]);
|
|
237
|
+
const autoLanguage = sortedLang[0]?.[1] > 0 ? sortedLang[0][0] : "unknown";
|
|
238
|
+
|
|
239
|
+
let autoFramework = "unknown";
|
|
240
|
+
const hasDep = (name) => externalLibraries.includes(name);
|
|
241
|
+
if (externalLibraries.some((d) => d.startsWith("@angular/"))) autoFramework = "angular";
|
|
242
|
+
else if (hasDep("react")) autoFramework = "react";
|
|
243
|
+
else if (hasDep("vue")) autoFramework = "vue";
|
|
244
|
+
else if (hasDep("svelte")) autoFramework = "svelte";
|
|
245
|
+
else if (hasDep("next")) autoFramework = "nextjs";
|
|
246
|
+
else if (hasDep("nuxt")) autoFramework = "nuxt";
|
|
247
|
+
else if (hasDep("express")) autoFramework = "express";
|
|
248
|
+
else if (hasDep("@nestjs/core")) autoFramework = "nestjs";
|
|
249
|
+
else if (hasDep("fastify")) autoFramework = "fastify";
|
|
250
|
+
else if (hasDep("flask")) autoFramework = "flask";
|
|
251
|
+
else if (hasDep("django")) autoFramework = "django";
|
|
252
|
+
else if (hasDep("spring-boot")) autoFramework = "spring";
|
|
253
|
+
|
|
254
|
+
let autoProjectType = "fullstack";
|
|
255
|
+
const hasClientRoots = ["src", "frontend", "app"].some((d) => fs.existsSync(path.join(cwd, d)));
|
|
256
|
+
const hasServerRoots = ["server", "backend", "api"].some((d) => fs.existsSync(path.join(cwd, d)));
|
|
257
|
+
if (["react", "angular", "vue", "svelte", "nextjs", "nuxt"].includes(autoFramework)) autoProjectType = "frontend";
|
|
258
|
+
if (["express", "nestjs", "fastify", "flask", "django", "spring"].includes(autoFramework)) autoProjectType = "backend";
|
|
259
|
+
if (hasClientRoots && hasServerRoots) autoProjectType = "fullstack";
|
|
260
|
+
if (!hasClientRoots && !hasServerRoots) autoProjectType = "library";
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
language: overrides.language || autoLanguage,
|
|
264
|
+
framework: overrides.framework || autoFramework,
|
|
265
|
+
projectType: overrides.projectType || autoProjectType,
|
|
266
|
+
detected: {
|
|
267
|
+
language: autoLanguage,
|
|
268
|
+
framework: autoFramework,
|
|
269
|
+
projectType: autoProjectType,
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export function discoverProjectSignals(cwd, profileOverrides = {}) {
|
|
222
275
|
const files = collectCodeFiles(cwd);
|
|
223
276
|
const inferred = new Map();
|
|
224
277
|
const addHit = (cap, filePath) => {
|
|
@@ -270,6 +323,7 @@ export function discoverProjectSignals(cwd) {
|
|
|
270
323
|
externalLibraries,
|
|
271
324
|
uiLayout: detectUiLayout(files),
|
|
272
325
|
styling: detectStyling(cwd, files, externalLibraries),
|
|
326
|
+
developmentProfile: detectDevelopmentProfile(cwd, files, externalLibraries, profileOverrides),
|
|
273
327
|
};
|
|
274
328
|
}
|
|
275
329
|
|
|
@@ -366,6 +420,11 @@ export function buildSignalsReport(signals) {
|
|
|
366
420
|
` - frameworks : ${(signals.styling?.cssFrameworks || []).join(", ") || "none detected"}`,
|
|
367
421
|
` - style files: ${signals.styling?.styleFileCount ?? 0}`,
|
|
368
422
|
` - tokens : ${(signals.styling?.designTokens || []).slice(0, 8).join(", ") || "none detected"}`,
|
|
423
|
+
"Development profile",
|
|
424
|
+
"-".repeat(56),
|
|
425
|
+
` - language : ${signals.developmentProfile?.language || "unknown"} (auto: ${signals.developmentProfile?.detected?.language || "unknown"})`,
|
|
426
|
+
` - framework : ${signals.developmentProfile?.framework || "unknown"} (auto: ${signals.developmentProfile?.detected?.framework || "unknown"})`,
|
|
427
|
+
` - project type: ${signals.developmentProfile?.projectType || "unknown"} (auto: ${signals.developmentProfile?.detected?.projectType || "unknown"})`,
|
|
369
428
|
"=".repeat(56),
|
|
370
429
|
].join("\n");
|
|
371
430
|
}
|
|
@@ -423,6 +482,12 @@ export function writeAdoptionBaseline(infernoDir, policyId, capabilities, signal
|
|
|
423
482
|
externalLibraries: signals.externalLibraries || [],
|
|
424
483
|
uiLayout: signals.uiLayout || { layoutType: "unknown", usesGrid: false, usesFlex: false, sections: [] },
|
|
425
484
|
styling: signals.styling || { cssFrameworks: [], styleFileCount: 0, styleFilesSample: [], designTokens: [] },
|
|
485
|
+
developmentProfile: signals.developmentProfile || {
|
|
486
|
+
language: "unknown",
|
|
487
|
+
framework: "unknown",
|
|
488
|
+
projectType: "unknown",
|
|
489
|
+
detected: { language: "unknown", framework: "unknown", projectType: "unknown" },
|
|
490
|
+
},
|
|
426
491
|
};
|
|
427
492
|
fs.writeFileSync(path.join(infernoDir, "adoption_profile.json"), JSON.stringify(profile, null, 2) + "\n");
|
|
428
493
|
}
|
package/lib/commands/init.mjs
CHANGED
|
@@ -27,6 +27,14 @@ function ask(rl, question, defaultVal = "") {
|
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
function getArgValue(args, ...flags) {
|
|
31
|
+
for (const flag of flags) {
|
|
32
|
+
const i = args.indexOf(flag);
|
|
33
|
+
if (i !== -1 && args[i + 1] && !args[i + 1].startsWith("-")) return args[i + 1];
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
30
38
|
function copyFile(src, dst, force, silent = false) {
|
|
31
39
|
if (fs.existsSync(dst) && !force) {
|
|
32
40
|
if (!silent) warn("Skipped (exists): " + path.relative(process.cwd(), dst));
|
|
@@ -144,6 +152,9 @@ export async function initCommand(args) {
|
|
|
144
152
|
const reportJson = args.includes("--report-json");
|
|
145
153
|
const reportJsonOnly = args.includes("--report-json-only");
|
|
146
154
|
const reportHumanOnly = args.includes("--report-human-only");
|
|
155
|
+
const langOverride = getArgValue(args, "--lang");
|
|
156
|
+
const frameworkOverride = getArgValue(args, "--framework");
|
|
157
|
+
const projectTypeOverride = getArgValue(args, "--project-type");
|
|
147
158
|
const silent = reportJsonOnly;
|
|
148
159
|
|
|
149
160
|
if (reportJsonOnly && reportHumanOnly) {
|
|
@@ -173,7 +184,23 @@ export async function initCommand(args) {
|
|
|
173
184
|
let capabilities = defaultCaps.split(",").map(c => c.trim());
|
|
174
185
|
|
|
175
186
|
if (adopt) {
|
|
176
|
-
const
|
|
187
|
+
const profileOverrides = {
|
|
188
|
+
language: langOverride || undefined,
|
|
189
|
+
framework: frameworkOverride || undefined,
|
|
190
|
+
projectType: projectTypeOverride || undefined,
|
|
191
|
+
};
|
|
192
|
+
let signals = discoverProjectSignals(cwd, profileOverrides);
|
|
193
|
+
if (!yes && !reportJsonOnly) {
|
|
194
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
195
|
+
const profile = signals.developmentProfile || {};
|
|
196
|
+
const detected = profile.detected || {};
|
|
197
|
+
console.log(gray(" Review inferred development stack (press Enter to accept detected values)\n"));
|
|
198
|
+
const language = await ask(rl, "Language", profile.language || detected.language || "unknown");
|
|
199
|
+
const framework = await ask(rl, "Framework", profile.framework || detected.framework || "unknown");
|
|
200
|
+
const projectType = await ask(rl, "Project type", profile.projectType || detected.projectType || "unknown");
|
|
201
|
+
rl.close();
|
|
202
|
+
signals = discoverProjectSignals(cwd, { language, framework, projectType });
|
|
203
|
+
}
|
|
177
204
|
const inferred = signals.capabilities;
|
|
178
205
|
const summarized = summarizeCapabilities(inferred);
|
|
179
206
|
if (reportJsonOnly) {
|
|
@@ -188,6 +215,7 @@ export async function initCommand(args) {
|
|
|
188
215
|
externalLibraries: signals.externalLibraries,
|
|
189
216
|
uiLayout: signals.uiLayout,
|
|
190
217
|
styling: signals.styling,
|
|
218
|
+
developmentProfile: signals.developmentProfile,
|
|
191
219
|
},
|
|
192
220
|
null,
|
|
193
221
|
2
|
|
@@ -211,6 +239,7 @@ export async function initCommand(args) {
|
|
|
211
239
|
externalLibraries: signals.externalLibraries,
|
|
212
240
|
uiLayout: signals.uiLayout,
|
|
213
241
|
styling: signals.styling,
|
|
242
|
+
developmentProfile: signals.developmentProfile,
|
|
214
243
|
},
|
|
215
244
|
null,
|
|
216
245
|
2
|
|
@@ -240,7 +269,11 @@ export async function initCommand(args) {
|
|
|
240
269
|
id,
|
|
241
270
|
title: id.replace(/([A-Z])/g, " $1").trim(),
|
|
242
271
|
}));
|
|
243
|
-
const signals = discoverProjectSignals(cwd
|
|
272
|
+
const signals = discoverProjectSignals(cwd, {
|
|
273
|
+
language: langOverride || undefined,
|
|
274
|
+
framework: frameworkOverride || undefined,
|
|
275
|
+
projectType: projectTypeOverride || undefined,
|
|
276
|
+
});
|
|
244
277
|
writeAdoptionBaseline(infernoDir, policyId, capDetails, signals);
|
|
245
278
|
if (!silent) {
|
|
246
279
|
ok("Created: " + cyan("inferno/contract.json"));
|
|
@@ -271,6 +304,22 @@ export async function initCommand(args) {
|
|
|
271
304
|
|
|
272
305
|
upsertScripts(cwd, silent);
|
|
273
306
|
|
|
307
|
+
if (adopt) {
|
|
308
|
+
const statePath = path.join(infernoDir, "context-state.json");
|
|
309
|
+
let state = {};
|
|
310
|
+
try {
|
|
311
|
+
state = JSON.parse(fs.readFileSync(statePath, "utf8"));
|
|
312
|
+
} catch {}
|
|
313
|
+
const signals = discoverProjectSignals(cwd, {
|
|
314
|
+
language: langOverride || undefined,
|
|
315
|
+
framework: frameworkOverride || undefined,
|
|
316
|
+
projectType: projectTypeOverride || undefined,
|
|
317
|
+
});
|
|
318
|
+
state.stack = signals.developmentProfile;
|
|
319
|
+
fs.writeFileSync(statePath, JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
320
|
+
if (!silent) ok("Created: " + cyan("inferno/context-state.json"));
|
|
321
|
+
}
|
|
322
|
+
|
|
274
323
|
if (!silent) {
|
|
275
324
|
done("infernoflow initialized!");
|
|
276
325
|
|