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 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
@@ -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)
@@ -218,7 +218,60 @@ function detectUiLayout(files) {
218
218
  };
219
219
  }
220
220
 
221
- export function discoverProjectSignals(cwd) {
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
  }
@@ -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 signals = discoverProjectSignals(cwd);
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "infernoflow",
3
- "version": "0.10.4",
3
+ "version": "0.10.5",
4
4
  "description": "The forge for liquid code — keep capabilities, contracts, and docs in sync.",
5
5
  "type": "module",
6
6
  "bin": {