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,869 +1,20 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import * as readline from "node:readline";
4
- import { seedProfileFromAdoption } from "../learning/profile.mjs";
5
- import { scanAngular } from "../adopters/angular.mjs";
6
- import { scanReact } from "../adopters/react.mjs";
7
- import { scanCSS } from "../adopters/css.mjs";
8
-
9
- function toCapabilityId(raw) {
10
- return raw
11
- .replace(/[^a-zA-Z0-9]+/g, " ")
12
- .trim()
13
- .split(/\s+/)
14
- .filter(Boolean)
15
- .map((w) => w[0].toUpperCase() + w.slice(1))
16
- .join("");
17
- }
18
-
19
- function capTitle(id) {
20
- return id.replace(/([A-Z])/g, " $1").trim();
21
- }
22
-
23
- function safeRead(filePath) {
24
- try {
25
- return fs.readFileSync(filePath, "utf8");
26
- } catch {
27
- return "";
28
- }
29
- }
30
-
31
- const HEURISTICS = [
32
- { id: "CreateItem", title: "Create Item", regex: /\b(post|create|add)\b/i },
33
- { id: "ReadItems", title: "Read Items", regex: /\b(get|read|list|fetch)\b/i },
34
- { id: "UpdateItem", title: "Update Item", regex: /\b(put|patch|update|edit)\b/i },
35
- { id: "DeleteItem", title: "Delete Item", regex: /\b(delete|remove)\b/i },
36
- { id: "SearchItems", title: "Search Items", regex: /\bsearch\b/i },
37
- { id: "FilterItems", title: "Filter Items", regex: /\bfilter\b/i },
38
- { id: "SetDueDate", title: "Set Due Date", regex: /\bdueDate|deadline|due\b/i },
39
- { id: "SetPriority", title: "Set Priority", regex: /\bpriority\b/i },
40
- { id: "ToggleComplete", title: "Toggle Complete", regex: /\bcomplete|completed|toggle\b/i },
41
- { id: "ClearCompleted", title: "Clear Completed", regex: /\bclearCompleted|clear completed\b/i },
42
- ];
43
-
44
- export function discoverCapabilities(cwd) {
45
- return discoverProjectSignals(cwd).capabilities;
46
- }
47
-
48
- function collectCodeFiles(cwd) {
49
- const files = [];
50
- const roots = ["src", "server", "app", "backend", "frontend", "api", "Controllers"];
51
- for (const r of roots) {
52
- const root = path.join(cwd, r);
53
- if (!fs.existsSync(root)) continue;
54
- const stack = [root];
55
- while (stack.length) {
56
- const cur = stack.pop();
57
- for (const entry of fs.readdirSync(cur, { withFileTypes: true })) {
58
- const p = path.join(cur, entry.name);
59
- if (entry.isDirectory()) {
60
- const SKIP_DIRS = new Set([
61
- "node_modules", ".git", "dist", "build", "out", "www", "tmp", ".tmp",
62
- "vendor", "assets", "public", "static", "coverage", ".nyc_output",
63
- ".angular", ".vite", ".cache", ".parcel-cache", ".next", ".nuxt",
64
- "__pycache__", "e2e", "test", "tests", "spec", "__tests__",
65
- "fixtures", "mocks", ".turbo", "storybook-static",
66
- ]);
67
- if (SKIP_DIRS.has(entry.name)) continue;
68
- stack.push(p);
69
- } else if (/\.(js|jsx|ts|tsx|mjs|cjs|json|md|html|htm|cs|csproj)$/.test(entry.name)) {
70
- // Skip minified, bundled, and source-map files
71
- if (/\.(min|bundle)\.(js|css)$/.test(entry.name)) continue;
72
- if (/\.map$/.test(entry.name)) continue;
73
- // Skip generated Angular/framework files
74
- if (/\.(spec|test)\.(ts|js|tsx|jsx)$/.test(entry.name)) continue;
75
- files.push(p);
76
- }
77
- }
78
- }
79
- }
80
- // Include common root-level .NET entry files without scanning the whole repo.
81
- for (const entry of fs.readdirSync(cwd, { withFileTypes: true })) {
82
- if (!entry.isFile()) continue;
83
- if (/^(Program\.cs|.+\.csproj)$/i.test(entry.name)) {
84
- files.push(path.join(cwd, entry.name));
85
- }
86
- }
87
- return files;
88
- }
89
-
90
- function detectComponents(files, cwd) {
91
- const names = new Set();
92
- for (const filePath of files) {
93
- const rel = path.relative(cwd, filePath);
94
- const text = safeRead(filePath);
95
- const classMatches = text.matchAll(/\bclass\s+([A-Z][A-Za-z0-9_]*?(?:Component|Page|View|Widget|Card))\b/g);
96
- for (const m of classMatches) names.add(m[1]);
97
- const selectorMatches = text.matchAll(/\bselector\s*:\s*["']([^"']+)["']/g);
98
- for (const m of selectorMatches) names.add(m[1]);
99
- const reactFnMatches = text.matchAll(/\bfunction\s+([A-Z][A-Za-z0-9_]*)\s*\(/g);
100
- for (const m of reactFnMatches) {
101
- if (/component|page|view|card|chart|dashboard/i.test(m[1])) names.add(m[1]);
102
- }
103
- const relMatch = rel.match(/([^/\\]+)\.(component|page|view|widget|card)\.(ts|tsx|js|jsx)$/i);
104
- if (relMatch) names.add(relMatch[1]);
105
- }
106
- return Array.from(names).sort();
107
- }
108
-
109
- function detectDisplayFields(files) {
110
- const fields = new Set();
111
- const methodNames = new Set();
112
- const stopWords = new Set([
113
- "if", "for", "while", "const", "let", "var", "return", "function", "class", "import", "export",
114
- "null", "undefined", "true", "false", "string", "number", "boolean", "any", "unknown", "never",
115
- "selector", "templateUrl", "styleUrl", "standalone", "imports", "providers", "providedIn",
116
- "options", "scales", "responsive", "display", "title", "type", "label",
117
- "component", "service", "routes", "appConfig", "ApplicationConfig",
118
- ]);
119
- const add = (v) => {
120
- if (!v) return;
121
- if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(v)) return;
122
- if (v.length <= 1) return;
123
- if (stopWords.has(v)) return;
124
- if (/^[A-Z0-9_]+$/.test(v)) return;
125
- fields.add(v);
126
- };
127
- for (const filePath of files) {
128
- const text = safeRead(filePath);
129
- if (/\.(html|htm)$/i.test(filePath)) {
130
- const angularInterpolations = text.matchAll(/\{\{\s*(?:this\.)?([a-zA-Z_][a-zA-Z0-9_]*)/g);
131
- for (const m of angularInterpolations) add(m[1]);
132
- const ngModels = text.matchAll(/\[\(ngModel\)\]\s*=\s*["']([a-zA-Z_][a-zA-Z0-9_]*)["']/g);
133
- for (const m of ngModels) add(m[1]);
134
- const ngInputs = text.matchAll(/\[[a-zA-Z0-9_-]+\]\s*=\s*["'](?:this\.)?([a-zA-Z_][a-zA-Z0-9_]*)["']/g);
135
- for (const m of ngInputs) add(m[1]);
136
- const ngIfs = text.matchAll(/\*ngIf\s*=\s*["'](?:this\.)?([a-zA-Z_][a-zA-Z0-9_]*)/g);
137
- for (const m of ngIfs) add(m[1]);
138
- }
139
- if (/\.(ts|tsx|js|jsx|mjs|cjs)$/i.test(filePath)) {
140
- const methodDecl = text.matchAll(
141
- /(?:^|\n)\s*(?:public|private|protected)?\s*(?:async\s+)?([a-zA-Z_][a-zA-Z0-9_]*)\s*\([^)]*\)\s*\{/g
142
- );
143
- for (const m of methodDecl) methodNames.add(m[1]);
144
-
145
- const thisRefs = text.matchAll(/\bthis\.([a-zA-Z_][a-zA-Z0-9_]*)\b/g);
146
- for (const m of thisRefs) add(m[1]);
147
-
148
- const classProps = text.matchAll(
149
- /(?:^|\n)\s*(?:public|private|protected)?\s*(?:readonly\s+)?([a-zA-Z_][a-zA-Z0-9_]*)\s*(?::|=)/g
150
- );
151
- for (const m of classProps) {
152
- add(m[1]);
153
- }
154
-
155
- const inputProps = text.matchAll(/@Input\([^)]*\)\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*[:=]/g);
156
- for (const m of inputProps) add(m[1]);
157
-
158
- const forEachParams = text.matchAll(/forEach\(\((\w+)\)\s*=>/g);
159
- for (const m of forEachParams) {
160
- const item = m[1];
161
- const propAccess = new RegExp(`\\b${item}\\.([a-zA-Z_][a-zA-Z0-9_]*)\\b`, "g");
162
- for (const p of text.matchAll(propAccess)) add(p[1]);
163
- }
164
- }
165
- }
166
- return Array.from(fields)
167
- .filter((name) => !methodNames.has(name))
168
- .sort()
169
- .slice(0, 80);
170
- }
171
-
172
- function detectExternalLibraries(cwd) {
173
- const libs = new Set();
174
- const pkgPath = path.join(cwd, "package.json");
175
- if (!fs.existsSync(pkgPath)) return [];
176
- try {
177
- const pkg = JSON.parse(safeRead(pkgPath) || "{}");
178
- const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
179
- for (const name of Object.keys(deps)) libs.add(name);
180
- } catch {}
181
- return Array.from(libs).sort();
182
- }
183
-
184
- function detectStyling(cwd, files, externalLibraries) {
185
- const styleFiles = files
186
- .filter((f) => /\.(css|scss|sass|less|styl)$/i.test(f))
187
- .map((f) => path.relative(cwd, f))
188
- .sort();
189
-
190
- const frameworks = [];
191
- const hasDep = (name) => externalLibraries.includes(name);
192
- if (hasDep("tailwindcss")) frameworks.push("Tailwind CSS");
193
- if (hasDep("bootstrap")) frameworks.push("Bootstrap");
194
- if (externalLibraries.some((lib) => lib.startsWith("@angular/material"))) frameworks.push("Angular Material");
195
- if (hasDep("antd")) frameworks.push("Ant Design");
196
- if (hasDep("styled-components")) frameworks.push("styled-components");
197
- if (hasDep("emotion") || hasDep("@emotion/react")) frameworks.push("Emotion");
198
-
199
- const tokenVars = new Set();
200
- for (const filePath of files) {
201
- if (!/\.(css|scss|sass|less|styl|html|htm|ts|tsx|js|jsx|mjs|cjs)$/i.test(filePath)) continue;
202
- const text = safeRead(filePath);
203
- for (const m of text.matchAll(/--([a-zA-Z][a-zA-Z0-9_-]*)/g)) tokenVars.add(`--${m[1]}`);
204
- }
205
-
206
- return {
207
- cssFrameworks: frameworks,
208
- styleFileCount: styleFiles.length,
209
- styleFilesSample: styleFiles.slice(0, 12),
210
- designTokens: Array.from(tokenVars).sort().slice(0, 24),
211
- };
212
- }
213
-
214
- function detectUiLayout(files) {
215
- let usesGrid = false;
216
- let usesFlex = false;
217
- const sections = new Set();
218
-
219
- for (const filePath of files) {
220
- if (!/\.(html|htm|tsx|jsx|ts|js|mjs|cjs)$/i.test(filePath)) continue;
221
- const text = safeRead(filePath);
222
-
223
- if (/\bgrid\b|grid-template|grid-cols-|display:\s*grid/i.test(text)) usesGrid = true;
224
- if (/\bflex\b|display:\s*flex|flex-row|flex-col|justify-|items-/i.test(text)) usesFlex = true;
225
-
226
- for (const m of text.matchAll(/<(main|header|footer|section|aside|nav)\b/gi)) {
227
- sections.add(m[1].toLowerCase());
228
- }
229
- for (const m of text.matchAll(/class(?:Name)?\s*=\s*["'`][^"'`]*(dashboard|chart|card|sidebar|content|toolbar|filter|panel|table)[^"'`]*["'`]/gi)) {
230
- const hit = m[1].toLowerCase();
231
- sections.add(hit === "filter" ? "filters" : hit);
232
- }
233
- }
234
-
235
- const layoutType = usesGrid && usesFlex ? "mixed" : usesGrid ? "grid" : usesFlex ? "flex" : "unknown";
236
- return {
237
- layoutType,
238
- usesGrid,
239
- usesFlex,
240
- sections: Array.from(sections).sort(),
241
- };
242
- }
243
-
244
- function detectDevelopmentProfile(cwd, files, externalLibraries, overrides = {}) {
245
- const extCount = { ts: 0, js: 0, py: 0, java: 0, go: 0, rb: 0, rs: 0, cs: 0, php: 0 };
246
- for (const filePath of files) {
247
- const ext = path.extname(filePath).toLowerCase();
248
- if (ext === ".ts" || ext === ".tsx") extCount.ts += 1;
249
- if (ext === ".js" || ext === ".jsx" || ext === ".mjs" || ext === ".cjs") extCount.js += 1;
250
- if (ext === ".py") extCount.py += 1;
251
- if (ext === ".java") extCount.java += 1;
252
- if (ext === ".go") extCount.go += 1;
253
- if (ext === ".rb") extCount.rb += 1;
254
- if (ext === ".rs") extCount.rs += 1;
255
- if (ext === ".cs") extCount.cs += 1;
256
- if (ext === ".php") extCount.php += 1;
257
- }
258
-
259
- const sortedLang = Object.entries(extCount).sort((a, b) => b[1] - a[1]);
260
- const autoLanguage = sortedLang[0]?.[1] > 0 ? sortedLang[0][0] : "unknown";
261
-
262
- let autoFramework = "unknown";
263
- let hasDotnetWebSdk = false;
264
- let hasMinimalApi = false;
265
- let hasBlazor = false;
266
- for (const filePath of files) {
267
- const base = path.basename(filePath).toLowerCase();
268
- if (base.endsWith(".csproj")) {
269
- const text = safeRead(filePath);
270
- if (/Microsoft\.NET\.Sdk\.Web/i.test(text)) hasDotnetWebSdk = true;
271
- if (/Blazor/i.test(text) || /Microsoft\.AspNetCore\.Components/i.test(text)) hasBlazor = true;
272
- }
273
- if (base === "program.cs") {
274
- const text = safeRead(filePath);
275
- if (/app\.Map(Get|Post|Put|Delete|Patch)\s*\(/i.test(text)) hasMinimalApi = true;
276
- }
277
- }
278
- const hasDep = (name) => externalLibraries.includes(name);
279
- if (externalLibraries.some((d) => d.startsWith("@angular/"))) autoFramework = "angular";
280
- else if (hasDep("react")) autoFramework = "react";
281
- else if (hasDep("vue")) autoFramework = "vue";
282
- else if (hasDep("svelte")) autoFramework = "svelte";
283
- else if (hasDep("next")) autoFramework = "nextjs";
284
- else if (hasDep("nuxt")) autoFramework = "nuxt";
285
- else if (hasDep("express")) autoFramework = "express";
286
- else if (hasDep("@nestjs/core")) autoFramework = "nestjs";
287
- else if (hasDep("fastify")) autoFramework = "fastify";
288
- else if (hasDep("flask")) autoFramework = "flask";
289
- else if (hasDep("django")) autoFramework = "django";
290
- else if (hasDep("spring-boot")) autoFramework = "spring";
291
- else if (hasBlazor) autoFramework = "blazor";
292
- else if (hasMinimalApi) autoFramework = "minimalapi";
293
- else if (hasDotnetWebSdk || extCount.cs > 0) autoFramework = "aspnet";
294
-
295
- let autoProjectType = "fullstack";
296
- const hasClientRoots = ["src", "frontend", "app"].some((d) => fs.existsSync(path.join(cwd, d)));
297
- const hasServerRoots = ["server", "backend", "api"].some((d) => fs.existsSync(path.join(cwd, d)));
298
- if (["react", "angular", "vue", "svelte", "nextjs", "nuxt"].includes(autoFramework)) autoProjectType = "frontend";
299
- if (["express", "nestjs", "fastify", "flask", "django", "spring", "aspnet", "minimalapi"].includes(autoFramework)) autoProjectType = "backend";
300
- if (hasClientRoots && hasServerRoots) autoProjectType = "fullstack";
301
- if (!hasClientRoots && !hasServerRoots) autoProjectType = "library";
302
- if (autoFramework === "blazor") autoProjectType = "frontend";
303
-
304
- return {
305
- language: overrides.language || autoLanguage,
306
- framework: overrides.framework || autoFramework,
307
- projectType: overrides.projectType || autoProjectType,
308
- detected: {
309
- language: autoLanguage,
310
- framework: autoFramework,
311
- projectType: autoProjectType,
312
- },
313
- };
314
- }
315
-
316
- function detectApiCalls(cwd, files) {
317
- const calls = [];
318
- const seen = new Set();
319
- const normalizeEndpointPattern = (value) => {
320
- let out = String(value || "").trim();
321
- if (!out) return "";
322
- out = out.replace(/https?:\/\/[^/]+/gi, "");
323
- out = out.replace(/\$\{[^}]+\}/g, "{var}");
324
- out = out.replace(/\{[A-Za-z_][A-Za-z0-9_]*\}/g, "{var}");
325
- out = out.replace(/:[A-Za-z_][A-Za-z0-9_]*/g, "{var}");
326
- out = out.replace(/\/\d+(?=\/|$)/g, "/{id}");
327
- out = out.replace(/=[^&\s]+/g, "={value}");
328
- out = out.replace(/\/+/g, "/");
329
- return out;
330
- };
331
- const addCall = (call) => {
332
- const endpointPattern = normalizeEndpointPattern(call.endpointPattern);
333
- if (!endpointPattern) return;
334
- const key = `${call.method}|${endpointPattern}|${call.sourceFile}|${call.style}`;
335
- if (seen.has(key)) return;
336
- seen.add(key);
337
- calls.push({ ...call, endpointPattern });
338
- };
339
-
340
- for (const filePath of files) {
341
- if (!/\.(ts|tsx|js|jsx|mjs|cjs|cs)$/i.test(filePath)) continue;
342
- const rel = path.relative(cwd, filePath);
343
- const text = safeRead(filePath);
344
- const looksLikeService = /service|api|client|controller|program\.cs/i.test(rel) || /HttpClient|fetch\(|app\.Map(Get|Post|Put|Delete|Patch)\(/i.test(text);
345
- if (!looksLikeService) continue;
346
-
347
- const normalized = text.replace(/\r\n/g, "\n");
348
-
349
- const constStrings = {};
350
- const normalizeExpr = (expr) => {
351
- let out = String(expr || "").trim();
352
- if (!out) return "";
353
- out = out.replace(/;+$/, "").trim();
354
- out = out.replace(/\(\s*$/, ""); // e.g. "this._nextPage("
355
- // unwrap surrounding quotes/templates
356
- while ((out.startsWith("'") && out.endsWith("'")) || (out.startsWith('"') && out.endsWith('"')) || (out.startsWith("`") && out.endsWith("`"))) {
357
- out = out.slice(1, -1).trim();
358
- }
359
- return out;
360
- };
361
- const isLikelyEndpoint = (value) => {
362
- const v = String(value || "").trim();
363
- if (!v) return false;
364
- if (/^https?:\/\//i.test(v)) return true;
365
- if (v.startsWith("/")) return true;
366
- if (/\bapi\b/i.test(v)) return true;
367
- if (/\$\{[^}]+\}/.test(v)) return true;
368
- if (/\?[^=\s]+=?/.test(v)) return true;
369
- if (/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_./${}-]+$/.test(v)) return true;
370
- return false;
371
- };
372
- const storeConst = (name, raw) => {
373
- if (!name || !raw) return;
374
- constStrings[name] = normalizeExpr(raw);
375
- };
376
-
377
- const constPattern = /(?:const|let|var)\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*([\s\S]*?);/g;
378
- for (const m of normalized.matchAll(constPattern)) {
379
- storeConst(m[1], m[2]);
380
- }
381
- const readonlyPattern =
382
- /(?:public|private|protected)?\s*(?:readonly\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*([\s\S]*?);/g;
383
- for (const m of normalized.matchAll(readonlyPattern)) {
384
- storeConst(m[1], m[2]);
385
- }
386
-
387
- const resolveExpr = (expr) => {
388
- const trimmed = normalizeExpr(expr);
389
- if (!trimmed) return "";
390
- if (/^['"`][\s\S]*['"`]$/.test(trimmed)) {
391
- return trimmed.replace(/^['"`]|['"`]$/g, "");
392
- }
393
- if (constStrings[trimmed] && isLikelyEndpoint(constStrings[trimmed])) return constStrings[trimmed];
394
- const thisRef = trimmed.match(/^this\.([A-Za-z_][A-Za-z0-9_]*)$/);
395
- if (thisRef && constStrings[thisRef[1]] && isLikelyEndpoint(constStrings[thisRef[1]])) return constStrings[thisRef[1]];
396
- const parts = trimmed.split("+").map((s) => s.trim()).filter(Boolean);
397
- if (parts.length > 1) {
398
- const rebuilt = parts
399
- .map((p) => {
400
- if (/^['"`][\s\S]*['"`]$/.test(p)) return p.replace(/^['"`]|['"`]$/g, "");
401
- if (constStrings[p] && isLikelyEndpoint(constStrings[p])) return constStrings[p];
402
- const thisP = p.match(/^this\.([A-Za-z_][A-Za-z0-9_]*)$/);
403
- if (thisP && constStrings[thisP[1]] && isLikelyEndpoint(constStrings[thisP[1]])) return constStrings[thisP[1]];
404
- return `{${p}}`;
405
- })
406
- .join("");
407
- if (rebuilt) return rebuilt;
408
- }
409
- const ternary = trimmed.match(/^(.+?)\?(.+?):(.+)$/s);
410
- if (ternary) {
411
- const left = resolveExpr(ternary[2]);
412
- const right = resolveExpr(ternary[3]);
413
- if (left || right) return `${left || "{optionA}"} | ${right || "{optionB}"}`;
414
- }
415
- return trimmed;
416
- };
417
-
418
- const httpClientPattern =
419
- /\.\s*(get|post|put|patch|delete)\s*(?:<[\s\S]*?>)?\s*\(\s*([\s\S]*?)(?:,|\))/gi;
420
- for (const m of normalized.matchAll(httpClientPattern)) {
421
- const method = m[1].toUpperCase();
422
- const raw = resolveExpr(m[2]);
423
- if (!raw || !isLikelyEndpoint(raw)) continue;
424
- addCall({
425
- method,
426
- endpointPattern: raw,
427
- style: "httpClient",
428
- sourceFile: rel,
429
- });
430
- }
431
-
432
- const fetchPattern = /\bfetch\s*\(\s*([\s\S]*?)(?:,|\))/gi;
433
- for (const m of normalized.matchAll(fetchPattern)) {
434
- const firstArg = resolveExpr(m[1]);
435
- if (!firstArg || !isLikelyEndpoint(firstArg)) continue;
436
- const fromIdx = m.index || 0;
437
- const lookahead = normalized.slice(fromIdx, fromIdx + 260);
438
- const methodMatch = /method\s*:\s*["'](GET|POST|PUT|PATCH|DELETE)["']/i.exec(lookahead);
439
- const method = (methodMatch?.[1] || "GET").toUpperCase();
440
- addCall({
441
- method,
442
- endpointPattern: firstArg,
443
- style: "fetch",
444
- sourceFile: rel,
445
- });
446
- }
447
-
448
- const axiosPattern = /\baxios\.(get|post|put|patch|delete)\s*\(\s*([\s\S]*?)(?:,|\))/gi;
449
- for (const m of normalized.matchAll(axiosPattern)) {
450
- const method = m[1].toUpperCase();
451
- const endpoint = resolveExpr(m[2]);
452
- if (!endpoint || !isLikelyEndpoint(endpoint)) continue;
453
- addCall({
454
- method,
455
- endpointPattern: endpoint,
456
- style: "axios",
457
- sourceFile: rel,
458
- });
459
- }
460
-
461
- const axiosObjPattern = /\baxios\s*\(\s*\{([\s\S]*?)\}\s*\)/gi;
462
- for (const m of normalized.matchAll(axiosObjPattern)) {
463
- const body = m[1];
464
- const methodMatch = /\bmethod\s*:\s*["']?(get|post|put|patch|delete)["']?/i.exec(body);
465
- const urlMatch = /\burl\s*:\s*([^,\n]+)/i.exec(body);
466
- const method = (methodMatch?.[1] || "get").toUpperCase();
467
- const endpoint = resolveExpr(urlMatch?.[1] || "");
468
- if (!endpoint || !isLikelyEndpoint(endpoint)) continue;
469
- addCall({
470
- method,
471
- endpointPattern: endpoint,
472
- style: "axios-config",
473
- sourceFile: rel,
474
- });
475
- }
476
-
477
- const requestPattern = /\.\s*request\s*\(\s*["'](GET|POST|PUT|PATCH|DELETE)["']\s*,\s*([\s\S]*?)(?:,|\))/gi;
478
- for (const m of normalized.matchAll(requestPattern)) {
479
- const method = m[1].toUpperCase();
480
- const endpoint = resolveExpr(m[2]);
481
- if (!endpoint || !isLikelyEndpoint(endpoint)) continue;
482
- addCall({
483
- method,
484
- endpointPattern: endpoint,
485
- style: "request",
486
- sourceFile: rel,
487
- });
488
- }
489
-
490
- if (/\.cs$/i.test(filePath)) {
491
- const mapPattern = /\bapp\.Map(Get|Post|Put|Delete|Patch)\s*\(\s*"([^"]+)"/gi;
492
- for (const m of normalized.matchAll(mapPattern)) {
493
- addCall({
494
- method: m[1].toUpperCase(),
495
- endpointPattern: m[2],
496
- style: "csharp-map",
497
- sourceFile: rel,
498
- });
499
- }
500
-
501
- const classRouteMatch = /\[Route\("([^"]+)"\)\][\s\S]*?class\s+\w+/i.exec(normalized);
502
- const classRoute = classRouteMatch ? classRouteMatch[1] : "";
503
- const attrPattern = /\[(HttpGet|HttpPost|HttpPut|HttpDelete|HttpPatch)(?:\("([^"]*)"\))?\]/gi;
504
- for (const m of normalized.matchAll(attrPattern)) {
505
- const method = m[1].replace("Http", "").toUpperCase();
506
- const attrRoute = m[2] || "";
507
- const endpoint = [classRoute, attrRoute].filter(Boolean).join("/").replace(/\/+/g, "/").replace(/\[controller\]/gi, "{controller}");
508
- addCall({
509
- method,
510
- endpointPattern: endpoint || classRoute || "{controller-route}",
511
- style: "csharp-controller",
512
- sourceFile: rel,
513
- });
514
- }
515
-
516
- const httpClientPattern = /\b(GetAsync|PostAsync|PutAsync|DeleteAsync|SendAsync)\s*\(\s*"([^"]+)"/gi;
517
- for (const m of normalized.matchAll(httpClientPattern)) {
518
- const method = m[1].replace("Async", "").replace("Send", "SEND").toUpperCase();
519
- addCall({
520
- method,
521
- endpointPattern: m[2],
522
- style: "csharp-httpclient",
523
- sourceFile: rel,
524
- });
525
- }
526
- }
527
- }
528
-
529
- const byMethod = calls.reduce((acc, c) => {
530
- acc[c.method] = (acc[c.method] || 0) + 1;
531
- return acc;
532
- }, {});
533
-
534
- return {
535
- totalCalls: calls.length,
536
- byMethod,
537
- calls: calls.slice(0, 80),
538
- };
539
- }
540
-
541
- export function discoverProjectSignals(cwd, profileOverrides = {}) {
542
- const files = collectCodeFiles(cwd);
543
- const inferred = new Map();
544
- const addHit = (cap, filePath) => {
545
- if (!inferred.has(cap.id)) {
546
- inferred.set(cap.id, {
547
- id: cap.id,
548
- title: cap.title,
549
- reason: "Detected from code signals",
550
- sourceFiles: new Set(),
551
- });
552
- }
553
- inferred.get(cap.id).sourceFiles.add(path.relative(cwd, filePath));
554
- };
555
-
556
- for (const filePath of files) {
557
- const text = safeRead(filePath);
558
- for (const h of HEURISTICS) {
559
- if (h.regex.test(text)) {
560
- addHit(h, filePath);
561
- }
562
- }
563
- }
564
-
565
- const pkgPath = path.join(cwd, "package.json");
566
- if (fs.existsSync(pkgPath)) {
567
- const pkg = JSON.parse(safeRead(pkgPath) || "{}");
568
- const name = typeof pkg.name === "string" ? pkg.name : path.basename(cwd);
569
- const idHint = toCapabilityId(name);
570
- if (idHint && !inferred.size) {
571
- inferred.set("ReadItems", { id: "ReadItems", title: "Read Items", reason: `Fallback default for ${name}`, sourceFiles: new Set() });
572
- inferred.set("CreateItem", { id: "CreateItem", title: "Create Item", reason: `Fallback default for ${name}`, sourceFiles: new Set() });
573
- }
574
- }
575
-
576
- if (!inferred.size) {
577
- inferred.set("CreateItem", { id: "CreateItem", title: "Create Item", reason: "Fallback default", sourceFiles: new Set() });
578
- inferred.set("ReadItems", { id: "ReadItems", title: "Read Items", reason: "Fallback default", sourceFiles: new Set() });
579
- }
580
-
581
- const externalLibraries = detectExternalLibraries(cwd);
582
- const devProfile = detectDevelopmentProfile(cwd, files, externalLibraries, profileOverrides);
583
-
584
- // ── Framework-specific scanners ─────────────────────────────────────────
585
- let frameworkCapabilities = [];
586
- try {
587
- if (devProfile.framework === "angular") {
588
- const angularSignals = scanAngular(cwd, files);
589
- for (const cap of angularSignals.capabilities) {
590
- if (!inferred.has(cap.id)) {
591
- inferred.set(cap.id, { ...cap, sourceFiles: new Set(cap.sourceFiles) });
592
- }
593
- }
594
- } else if (devProfile.framework === "react" || devProfile.framework === "nextjs") {
595
- const reactSignals = scanReact(cwd, files);
596
- for (const cap of reactSignals.capabilities) {
597
- if (!inferred.has(cap.id)) {
598
- inferred.set(cap.id, { ...cap, sourceFiles: new Set(cap.sourceFiles) });
599
- }
600
- }
601
- }
602
- } catch {}
603
-
604
- // ── CSS signals ─────────────────────────────────────────────────────────
605
- let cssSignals = { designTokens: [], colorTokens: [], spacingTokens: [], componentClasses: [], themeVars: [] };
606
- try { cssSignals = scanCSS(cwd, files); } catch {}
607
-
608
- const capabilities = Array.from(inferred.values()).map((c) => ({
609
- ...c,
610
- sourceFiles: Array.from(c.sourceFiles || []),
611
- }));
612
- return {
613
- capabilities,
614
- components: detectComponents(files, cwd),
615
- displayFields: detectDisplayFields(files),
616
- externalLibraries,
617
- uiLayout: detectUiLayout(files),
618
- styling: {
619
- ...detectStyling(cwd, files, externalLibraries),
620
- // Merge in richer CSS scanner results
621
- designTokens: cssSignals.designTokens.length > 0 ? cssSignals.designTokens : undefined,
622
- colorTokens: cssSignals.colorTokens,
623
- spacingTokens: cssSignals.spacingTokens,
624
- componentClasses: cssSignals.componentClasses,
625
- themeVars: cssSignals.themeVars,
626
- },
627
- developmentProfile: devProfile,
628
- apiCalls: detectApiCalls(cwd, files),
629
- };
630
- }
631
-
632
- export async function reviewCapabilitiesInteractive(capabilities, yes = false) {
633
- if (yes) return capabilities;
634
-
635
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
636
- const list = capabilities.map((c) => c.id).join(", ");
637
- const answer = await new Promise((resolve) =>
638
- rl.question(` Inferred capabilities (${list}). Press Enter to accept or type comma list: `, resolve)
639
- );
640
- rl.close();
641
- const trimmed = String(answer).trim();
642
- if (!trimmed) return capabilities;
643
- return trimmed
644
- .split(",")
645
- .map((s) => s.trim())
646
- .filter(Boolean)
647
- .map((id) => ({ id, title: capTitle(id), reason: "User provided during adopt review" }));
648
- }
649
-
650
- export function buildAdoptionReport(capabilities) {
651
- if (!capabilities.length) return "No capabilities inferred.";
652
- const summarized = summarizeCapabilities(capabilities);
653
- const totalSignals = summarized.reduce((acc, c) => acc + c.signalCount, 0);
654
- const byConfidence = {
655
- high: summarized.filter((c) => c.confidence === "high").length,
656
- medium: summarized.filter((c) => c.confidence === "medium").length,
657
- low: summarized.filter((c) => c.confidence === "low").length,
658
- };
659
-
660
- const lines = [];
661
- lines.push("Adoption Analysis");
662
- lines.push("=".repeat(56));
663
- lines.push(`Capabilities detected : ${summarized.length}`);
664
- lines.push(`Signal hits total : ${totalSignals}`);
665
- lines.push(
666
- `Confidence mix : high=${byConfidence.high}, medium=${byConfidence.medium}, low=${byConfidence.low}`
667
- );
668
- lines.push("-".repeat(56));
669
- lines.push("Capability Breakdown");
670
- lines.push("-".repeat(56));
671
- lines.push("Confidence Signals Capability");
672
- lines.push("-".repeat(56));
673
- for (const c of summarized) {
674
- const confidence = c.confidence.toUpperCase().padEnd(10, " ");
675
- const signals = String(c.signalCount).padEnd(7, " ");
676
- lines.push(`${confidence} ${signals} ${c.id} (${c.title})`);
677
- if (c.signalCount > 0) {
678
- const sample = c.sourceFiles.slice(0, 3).join(", ");
679
- lines.push(` sources: ${sample}`);
680
- if (c.sourceFiles.length > 3) {
681
- lines.push(` more : +${c.sourceFiles.length - 3} additional files`);
682
- }
683
- } else {
684
- lines.push(" sources: inferred fallback (no strong code signal)");
685
- }
686
- }
687
- lines.push("=".repeat(56));
688
- return lines.join("\n");
689
- }
690
-
691
- export function buildSignalsReport(signals) {
692
- const formatList = (title, items, limit = 10) => {
693
- const lines = [`${title} (${items.length})`];
694
- lines.push("-".repeat(56));
695
- if (!items.length) {
696
- lines.push(" - none");
697
- return lines.join("\n");
698
- }
699
- for (const item of items.slice(0, limit)) {
700
- lines.push(` - ${item}`);
701
- }
702
- if (items.length > limit) {
703
- lines.push(` - ... +${items.length - limit} more`);
704
- }
705
- return lines.join("\n");
706
- };
707
-
708
- return [
709
- "Project Structure Signals",
710
- "=".repeat(56),
711
- formatList("Components", signals.components || []),
712
- formatList("Display fields", signals.displayFields || []),
713
- formatList("External libraries", signals.externalLibraries || []),
714
- "UI layout",
715
- "-".repeat(56),
716
- ` - layout type: ${signals.uiLayout?.layoutType || "unknown"}`,
717
- ` - uses grid : ${signals.uiLayout?.usesGrid ? "yes" : "no"}`,
718
- ` - uses flex : ${signals.uiLayout?.usesFlex ? "yes" : "no"}`,
719
- ` - sections : ${(signals.uiLayout?.sections || []).slice(0, 10).join(", ") || "none"}`,
720
- "Styling",
721
- "-".repeat(56),
722
- ` - frameworks : ${(signals.styling?.cssFrameworks || []).join(", ") || "none detected"}`,
723
- ` - style files: ${signals.styling?.styleFileCount ?? 0}`,
724
- ` - tokens : ${(signals.styling?.designTokens || []).slice(0, 8).join(", ") || "none detected"}`,
725
- "Development profile",
726
- "-".repeat(56),
727
- ` - language : ${signals.developmentProfile?.language || "unknown"} (auto: ${signals.developmentProfile?.detected?.language || "unknown"})`,
728
- ` - framework : ${signals.developmentProfile?.framework || "unknown"} (auto: ${signals.developmentProfile?.detected?.framework || "unknown"})`,
729
- ` - project type: ${signals.developmentProfile?.projectType || "unknown"} (auto: ${signals.developmentProfile?.detected?.projectType || "unknown"})`,
730
- "API calls",
731
- "-".repeat(56),
732
- ` - total calls : ${signals.apiCalls?.totalCalls ?? 0}`,
733
- ` - by method : ${Object.entries(signals.apiCalls?.byMethod || {}).map(([k, v]) => `${k}:${v}`).join(", ") || "none"}`,
734
- ...(signals.apiCalls?.calls || []).slice(0, 6).map((c) => ` - ${c.method} ${c.endpointPattern} [${c.style}] (${c.sourceFile})`),
735
- ...((signals.apiCalls?.calls || []).length > 6
736
- ? [` - ... +${(signals.apiCalls?.calls || []).length - 6} more`]
737
- : []),
738
- "=".repeat(56),
739
- ].join("\n");
740
- }
741
-
742
- export function summarizeCapabilities(capabilities) {
743
- return capabilities.map((c) => {
744
- const hits = c.sourceFiles?.length || 0;
745
- const confidence = hits >= 3 ? "high" : hits >= 1 ? "medium" : "low";
746
- return {
747
- id: c.id,
748
- title: c.title,
749
- reason: c.reason,
750
- confidence,
751
- sourceFiles: c.sourceFiles || [],
752
- signalCount: hits,
753
- };
754
- });
755
- }
756
-
757
- /**
758
- * Build the ui section of contract.json from adoption signals.
759
- */
760
- export function buildUiContractSection(signals) {
761
- if (!signals) return null;
762
- const components = [
763
- ...(signals.components || []),
764
- ].filter(Boolean).slice(0, 40);
765
-
766
- const designTokens = (signals.styling?.designTokens || []).slice(0, 20);
767
- const layout = signals.uiLayout?.layoutType || "unknown";
768
- const sections = (signals.uiLayout?.sections || []).slice(0, 12);
769
- const cssFrameworks = signals.styling?.cssFrameworks || [];
770
- const colorTokens = (signals.styling?.colorTokens || []).slice(0, 10);
771
- const themeVars = (signals.styling?.themeVars || []).slice(0, 10);
772
-
773
- return {
774
- components,
775
- designTokens,
776
- colorTokens,
777
- themeVars,
778
- cssFrameworks,
779
- layout,
780
- sections,
781
- lastScanned: new Date().toISOString(),
782
- };
783
- }
784
-
785
- /**
786
- * Merge a fresh ui scan result into an existing contract's ui section.
787
- * Preserves manually added entries, updates detected ones.
788
- */
789
- export function mergeUiSection(existing, fresh) {
790
- if (!fresh) return existing;
791
- if (!existing) return fresh;
792
- return {
793
- ...fresh,
794
- // Keep any manually added components not in the fresh scan
795
- components: [...new Set([...(fresh.components || []), ...(existing.components || [])])].slice(0, 50),
796
- designTokens: [...new Set([...(fresh.designTokens || []), ...(existing.designTokens || [])])].slice(0, 30),
797
- };
798
- }
799
-
800
- export function writeAdoptionBaseline(infernoDir, policyId, capabilities, signals = null) {
801
- const capIds = capabilities.map((c) => c.id);
802
- const uiSection = buildUiContractSection(signals);
803
-
804
- const contract = {
805
- policyId,
806
- policyVersion: 1,
807
- capabilities: capIds,
808
- rules: {
809
- docsRequiredOnCapabilityChange: true,
810
- requireScenarioForEachCapability: true,
811
- requireChangelogOnCapabilityChange: true,
812
- },
813
- ...(uiSection ? { ui: uiSection } : {}),
814
- };
815
- fs.mkdirSync(path.join(infernoDir, "scenarios"), { recursive: true });
816
- fs.writeFileSync(path.join(infernoDir, "contract.json"), JSON.stringify(contract, null, 2) + "\n");
817
-
818
- const registry = {
819
- schemaVersion: 1,
820
- capabilities: capabilities.map((c) => ({ id: c.id, title: c.title || capTitle(c.id), since: "0.1.0" })),
821
- };
822
- fs.writeFileSync(path.join(infernoDir, "capabilities.json"), JSON.stringify(registry, null, 2) + "\n");
823
-
824
- const scenario = {
825
- scenarioId: "adoption_baseline",
826
- description: "Baseline inferred from existing codebase during adoption",
827
- capabilitiesCovered: capIds,
828
- steps: capIds.map((id) => ({ action: id, expect: `${id} behavior exists in the current project` })),
829
- };
830
- fs.writeFileSync(path.join(infernoDir, "scenarios", "adoption_baseline.json"), JSON.stringify(scenario, null, 2) + "\n");
831
-
832
- if (signals) {
833
- const profile = {
834
- profileId: "adoption_profile",
835
- generatedAt: new Date().toISOString(),
836
- components: signals.components || [],
837
- displayFields: signals.displayFields || [],
838
- externalLibraries: signals.externalLibraries || [],
839
- uiLayout: signals.uiLayout || { layoutType: "unknown", usesGrid: false, usesFlex: false, sections: [] },
840
- styling: signals.styling || { cssFrameworks: [], styleFileCount: 0, styleFilesSample: [], designTokens: [] },
841
- developmentProfile: signals.developmentProfile || {
842
- language: "unknown",
843
- framework: "unknown",
844
- projectType: "unknown",
845
- detected: { language: "unknown", framework: "unknown", projectType: "unknown" },
846
- },
847
- apiCalls: signals.apiCalls || { totalCalls: 0, byMethod: {}, calls: [] },
848
- };
849
- fs.writeFileSync(path.join(infernoDir, "adoption_profile.json"), JSON.stringify(profile, null, 2) + "\n");
850
-
851
- // Seed developer-profile.json from adoption signals
852
- try {
853
- seedProfileFromAdoption(infernoDir, signals, capabilities);
854
- } catch {}
855
- }
856
-
857
- const changelog = `# Changelog — ${policyId}
858
-
859
- ## Unreleased
860
-
861
- - Adopted infernoflow into an existing project and generated baseline capabilities.
862
- - Captured detected components, display fields, and external libraries in adoption profile.
863
-
864
- ## 0.1.0 — Adoption baseline
865
-
866
- - Initial baseline generated by infernoflow init --adopt
867
- `;
868
- fs.writeFileSync(path.join(infernoDir, "CHANGELOG.md"), changelog, "utf8");
869
- }
1
+ import*as j from"node:fs";import*as A from"node:path";import*as D from"node:readline";import{seedProfileFromAdoption as R}from"../learning/profile.mjs";import{scanAngular as U}from"../adopters/angular.mjs";import{scanReact as M}from"../adopters/react.mjs";import{scanCSS as O}from"../adopters/css.mjs";function H(e){return e.replace(/[^a-zA-Z0-9]+/g," ").trim().split(/\s+/).filter(Boolean).map(r=>r[0].toUpperCase()+r.slice(1)).join("")}function _(e){return e.replace(/([A-Z])/g," $1").trim()}function k(e){try{return j.readFileSync(e,"utf8")}catch{return""}}const W=[{id:"CreateItem",title:"Create Item",regex:/\b(post|create|add)\b/i},{id:"ReadItems",title:"Read Items",regex:/\b(get|read|list|fetch)\b/i},{id:"UpdateItem",title:"Update Item",regex:/\b(put|patch|update|edit)\b/i},{id:"DeleteItem",title:"Delete Item",regex:/\b(delete|remove)\b/i},{id:"SearchItems",title:"Search Items",regex:/\bsearch\b/i},{id:"FilterItems",title:"Filter Items",regex:/\bfilter\b/i},{id:"SetDueDate",title:"Set Due Date",regex:/\bdueDate|deadline|due\b/i},{id:"SetPriority",title:"Set Priority",regex:/\bpriority\b/i},{id:"ToggleComplete",title:"Toggle Complete",regex:/\bcomplete|completed|toggle\b/i},{id:"ClearCompleted",title:"Clear Completed",regex:/\bclearCompleted|clear completed\b/i}];function ie(e){return X(e).capabilities}function G(e){const r=[],i=["src","server","app","backend","frontend","api","Controllers"];for(const o of i){const n=A.join(e,o);if(!j.existsSync(n))continue;const t=[n];for(;t.length;){const p=t.pop();for(const s of j.readdirSync(p,{withFileTypes:!0})){const a=A.join(p,s.name);if(s.isDirectory()){if(new Set(["node_modules",".git","dist","build","out","www","tmp",".tmp","vendor","assets","public","static","coverage",".nyc_output",".angular",".vite",".cache",".parcel-cache",".next",".nuxt","__pycache__","e2e","test","tests","spec","__tests__","fixtures","mocks",".turbo","storybook-static"]).has(s.name))continue;t.push(a)}else if(/\.(js|jsx|ts|tsx|mjs|cjs|json|md|html|htm|cs|csproj)$/.test(s.name)){if(/\.(min|bundle)\.(js|css)$/.test(s.name)||/\.map$/.test(s.name)||/\.(spec|test)\.(ts|js|tsx|jsx)$/.test(s.name))continue;r.push(a)}}}}for(const o of j.readdirSync(e,{withFileTypes:!0}))o.isFile()&&/^(Program\.cs|.+\.csproj)$/i.test(o.name)&&r.push(A.join(e,o.name));return r}function N(e,r){const i=new Set;for(const o of e){const n=A.relative(r,o),t=k(o),p=t.matchAll(/\bclass\s+([A-Z][A-Za-z0-9_]*?(?:Component|Page|View|Widget|Card))\b/g);for(const m of p)i.add(m[1]);const s=t.matchAll(/\bselector\s*:\s*["']([^"']+)["']/g);for(const m of s)i.add(m[1]);const a=t.matchAll(/\bfunction\s+([A-Z][A-Za-z0-9_]*)\s*\(/g);for(const m of a)/component|page|view|card|chart|dashboard/i.test(m[1])&&i.add(m[1]);const d=n.match(/([^/\\]+)\.(component|page|view|widget|card)\.(ts|tsx|js|jsx)$/i);d&&i.add(d[1])}return Array.from(i).sort()}function B(e){const r=new Set,i=new Set,o=new Set(["if","for","while","const","let","var","return","function","class","import","export","null","undefined","true","false","string","number","boolean","any","unknown","never","selector","templateUrl","styleUrl","standalone","imports","providers","providedIn","options","scales","responsive","display","title","type","label","component","service","routes","appConfig","ApplicationConfig"]),n=t=>{t&&/^[A-Za-z_][A-Za-z0-9_]*$/.test(t)&&(t.length<=1||o.has(t)||/^[A-Z0-9_]+$/.test(t)||r.add(t))};for(const t of e){const p=k(t);if(/\.(html|htm)$/i.test(t)){const s=p.matchAll(/\{\{\s*(?:this\.)?([a-zA-Z_][a-zA-Z0-9_]*)/g);for(const c of s)n(c[1]);const a=p.matchAll(/\[\(ngModel\)\]\s*=\s*["']([a-zA-Z_][a-zA-Z0-9_]*)["']/g);for(const c of a)n(c[1]);const d=p.matchAll(/\[[a-zA-Z0-9_-]+\]\s*=\s*["'](?:this\.)?([a-zA-Z_][a-zA-Z0-9_]*)["']/g);for(const c of d)n(c[1]);const m=p.matchAll(/\*ngIf\s*=\s*["'](?:this\.)?([a-zA-Z_][a-zA-Z0-9_]*)/g);for(const c of m)n(c[1])}if(/\.(ts|tsx|js|jsx|mjs|cjs)$/i.test(t)){const s=p.matchAll(/(?:^|\n)\s*(?:public|private|protected)?\s*(?:async\s+)?([a-zA-Z_][a-zA-Z0-9_]*)\s*\([^)]*\)\s*\{/g);for(const f of s)i.add(f[1]);const a=p.matchAll(/\bthis\.([a-zA-Z_][a-zA-Z0-9_]*)\b/g);for(const f of a)n(f[1]);const d=p.matchAll(/(?:^|\n)\s*(?:public|private|protected)?\s*(?:readonly\s+)?([a-zA-Z_][a-zA-Z0-9_]*)\s*(?::|=)/g);for(const f of d)n(f[1]);const m=p.matchAll(/@Input\([^)]*\)\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*[:=]/g);for(const f of m)n(f[1]);const c=p.matchAll(/forEach\(\((\w+)\)\s*=>/g);for(const f of c){const C=f[1],S=new RegExp(`\\b${C}\\.([a-zA-Z_][a-zA-Z0-9_]*)\\b`,"g");for(const y of p.matchAll(S))n(y[1])}}}return Array.from(r).filter(t=>!i.has(t)).sort().slice(0,80)}function V(e){const r=new Set,i=A.join(e,"package.json");if(!j.existsSync(i))return[];try{const o=JSON.parse(k(i)||"{}"),n={...o.dependencies||{},...o.devDependencies||{}};for(const t of Object.keys(n))r.add(t)}catch{}return Array.from(r).sort()}function q(e,r,i){const o=r.filter(s=>/\.(css|scss|sass|less|styl)$/i.test(s)).map(s=>A.relative(e,s)).sort(),n=[],t=s=>i.includes(s);t("tailwindcss")&&n.push("Tailwind CSS"),t("bootstrap")&&n.push("Bootstrap"),i.some(s=>s.startsWith("@angular/material"))&&n.push("Angular Material"),t("antd")&&n.push("Ant Design"),t("styled-components")&&n.push("styled-components"),(t("emotion")||t("@emotion/react"))&&n.push("Emotion");const p=new Set;for(const s of r){if(!/\.(css|scss|sass|less|styl|html|htm|ts|tsx|js|jsx|mjs|cjs)$/i.test(s))continue;const a=k(s);for(const d of a.matchAll(/--([a-zA-Z][a-zA-Z0-9_-]*)/g))p.add(`--${d[1]}`)}return{cssFrameworks:n,styleFileCount:o.length,styleFilesSample:o.slice(0,12),designTokens:Array.from(p).sort().slice(0,24)}}function J(e){let r=!1,i=!1;const o=new Set;for(const t of e){if(!/\.(html|htm|tsx|jsx|ts|js|mjs|cjs)$/i.test(t))continue;const p=k(t);/\bgrid\b|grid-template|grid-cols-|display:\s*grid/i.test(p)&&(r=!0),/\bflex\b|display:\s*flex|flex-row|flex-col|justify-|items-/i.test(p)&&(i=!0);for(const s of p.matchAll(/<(main|header|footer|section|aside|nav)\b/gi))o.add(s[1].toLowerCase());for(const s of p.matchAll(/class(?:Name)?\s*=\s*["'`][^"'`]*(dashboard|chart|card|sidebar|content|toolbar|filter|panel|table)[^"'`]*["'`]/gi)){const a=s[1].toLowerCase();o.add(a==="filter"?"filters":a)}}return{layoutType:r&&i?"mixed":r?"grid":i?"flex":"unknown",usesGrid:r,usesFlex:i,sections:Array.from(o).sort()}}function K(e,r,i,o={}){const n={ts:0,js:0,py:0,java:0,go:0,rb:0,rs:0,cs:0,php:0};for(const y of r){const b=A.extname(y).toLowerCase();(b===".ts"||b===".tsx")&&(n.ts+=1),(b===".js"||b===".jsx"||b===".mjs"||b===".cjs")&&(n.js+=1),b===".py"&&(n.py+=1),b===".java"&&(n.java+=1),b===".go"&&(n.go+=1),b===".rb"&&(n.rb+=1),b===".rs"&&(n.rs+=1),b===".cs"&&(n.cs+=1),b===".php"&&(n.php+=1)}const t=Object.entries(n).sort((y,b)=>b[1]-y[1]),p=t[0]?.[1]>0?t[0][0]:"unknown";let s="unknown",a=!1,d=!1,m=!1;for(const y of r){const b=A.basename(y).toLowerCase();if(b.endsWith(".csproj")){const P=k(y);/Microsoft\.NET\.Sdk\.Web/i.test(P)&&(a=!0),(/Blazor/i.test(P)||/Microsoft\.AspNetCore\.Components/i.test(P))&&(m=!0)}if(b==="program.cs"){const P=k(y);/app\.Map(Get|Post|Put|Delete|Patch)\s*\(/i.test(P)&&(d=!0)}}const c=y=>i.includes(y);i.some(y=>y.startsWith("@angular/"))?s="angular":c("react")?s="react":c("vue")?s="vue":c("svelte")?s="svelte":c("next")?s="nextjs":c("nuxt")?s="nuxt":c("express")?s="express":c("@nestjs/core")?s="nestjs":c("fastify")?s="fastify":c("flask")?s="flask":c("django")?s="django":c("spring-boot")?s="spring":m?s="blazor":d?s="minimalapi":(a||n.cs>0)&&(s="aspnet");let f="fullstack";const C=["src","frontend","app"].some(y=>j.existsSync(A.join(e,y))),S=["server","backend","api"].some(y=>j.existsSync(A.join(e,y)));return["react","angular","vue","svelte","nextjs","nuxt"].includes(s)&&(f="frontend"),["express","nestjs","fastify","flask","django","spring","aspnet","minimalapi"].includes(s)&&(f="backend"),C&&S&&(f="fullstack"),!C&&!S&&(f="library"),s==="blazor"&&(f="frontend"),{language:o.language||p,framework:o.framework||s,projectType:o.projectType||f,detected:{language:p,framework:s,projectType:f}}}function Q(e,r){const i=[],o=new Set,n=s=>{let a=String(s||"").trim();return a?(a=a.replace(/https?:\/\/[^/]+/gi,""),a=a.replace(/\$\{[^}]+\}/g,"{var}"),a=a.replace(/\{[A-Za-z_][A-Za-z0-9_]*\}/g,"{var}"),a=a.replace(/:[A-Za-z_][A-Za-z0-9_]*/g,"{var}"),a=a.replace(/\/\d+(?=\/|$)/g,"/{id}"),a=a.replace(/=[^&\s]+/g,"={value}"),a=a.replace(/\/+/g,"/"),a):""},t=s=>{const a=n(s.endpointPattern);if(!a)return;const d=`${s.method}|${a}|${s.sourceFile}|${s.style}`;o.has(d)||(o.add(d),i.push({...s,endpointPattern:a}))};for(const s of r){if(!/\.(ts|tsx|js|jsx|mjs|cjs|cs)$/i.test(s))continue;const a=A.relative(e,s),d=k(s);if(!(/service|api|client|controller|program\.cs/i.test(a)||/HttpClient|fetch\(|app\.Map(Get|Post|Put|Delete|Patch)\(/i.test(d)))continue;const c=d.replace(/\r\n/g,`
2
+ `),f={},C=u=>{let l=String(u||"").trim();if(!l)return"";for(l=l.replace(/;+$/,"").trim(),l=l.replace(/\(\s*$/,"");l.startsWith("'")&&l.endsWith("'")||l.startsWith('"')&&l.endsWith('"')||l.startsWith("`")&&l.endsWith("`");)l=l.slice(1,-1).trim();return l},S=u=>{const l=String(u||"").trim();return l?!!(/^https?:\/\//i.test(l)||l.startsWith("/")||/\bapi\b/i.test(l)||/\$\{[^}]+\}/.test(l)||/\?[^=\s]+=?/.test(l)||/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_./${}-]+$/.test(l)):!1},y=(u,l)=>{!u||!l||(f[u]=C(l))},b=/(?:const|let|var)\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*([\s\S]*?);/g;for(const u of c.matchAll(b))y(u[1],u[2]);const P=/(?:public|private|protected)?\s*(?:readonly\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*([\s\S]*?);/g;for(const u of c.matchAll(P))y(u[1],u[2]);const F=u=>{const l=C(u);if(!l)return"";if(/^['"`][\s\S]*['"`]$/.test(l))return l.replace(/^['"`]|['"`]$/g,"");if(f[l]&&S(f[l]))return f[l];const h=l.match(/^this\.([A-Za-z_][A-Za-z0-9_]*)$/);if(h&&f[h[1]]&&S(f[h[1]]))return f[h[1]];const w=l.split("+").map(g=>g.trim()).filter(Boolean);if(w.length>1){const g=w.map(x=>{if(/^['"`][\s\S]*['"`]$/.test(x))return x.replace(/^['"`]|['"`]$/g,"");if(f[x]&&S(f[x]))return f[x];const v=x.match(/^this\.([A-Za-z_][A-Za-z0-9_]*)$/);return v&&f[v[1]]&&S(f[v[1]])?f[v[1]]:`{${x}}`}).join("");if(g)return g}const $=l.match(/^(.+?)\?(.+?):(.+)$/s);if($){const g=F($[2]),x=F($[3]);if(g||x)return`${g||"{optionA}"} | ${x||"{optionB}"}`}return l},T=/\.\s*(get|post|put|patch|delete)\s*(?:<[\s\S]*?>)?\s*\(\s*([\s\S]*?)(?:,|\))/gi;for(const u of c.matchAll(T)){const l=u[1].toUpperCase(),h=F(u[2]);!h||!S(h)||t({method:l,endpointPattern:h,style:"httpClient",sourceFile:a})}const z=/\bfetch\s*\(\s*([\s\S]*?)(?:,|\))/gi;for(const u of c.matchAll(z)){const l=F(u[1]);if(!l||!S(l))continue;const h=u.index||0,w=c.slice(h,h+260),g=(/method\s*:\s*["'](GET|POST|PUT|PATCH|DELETE)["']/i.exec(w)?.[1]||"GET").toUpperCase();t({method:g,endpointPattern:l,style:"fetch",sourceFile:a})}const I=/\baxios\.(get|post|put|patch|delete)\s*\(\s*([\s\S]*?)(?:,|\))/gi;for(const u of c.matchAll(I)){const l=u[1].toUpperCase(),h=F(u[2]);!h||!S(h)||t({method:l,endpointPattern:h,style:"axios",sourceFile:a})}const Z=/\baxios\s*\(\s*\{([\s\S]*?)\}\s*\)/gi;for(const u of c.matchAll(Z)){const l=u[1],h=/\bmethod\s*:\s*["']?(get|post|put|patch|delete)["']?/i.exec(l),w=/\burl\s*:\s*([^,\n]+)/i.exec(l),$=(h?.[1]||"get").toUpperCase(),g=F(w?.[1]||"");!g||!S(g)||t({method:$,endpointPattern:g,style:"axios-config",sourceFile:a})}const E=/\.\s*request\s*\(\s*["'](GET|POST|PUT|PATCH|DELETE)["']\s*,\s*([\s\S]*?)(?:,|\))/gi;for(const u of c.matchAll(E)){const l=u[1].toUpperCase(),h=F(u[2]);!h||!S(h)||t({method:l,endpointPattern:h,style:"request",sourceFile:a})}if(/\.cs$/i.test(s)){const u=/\bapp\.Map(Get|Post|Put|Delete|Patch)\s*\(\s*"([^"]+)"/gi;for(const g of c.matchAll(u))t({method:g[1].toUpperCase(),endpointPattern:g[2],style:"csharp-map",sourceFile:a});const l=/\[Route\("([^"]+)"\)\][\s\S]*?class\s+\w+/i.exec(c),h=l?l[1]:"",w=/\[(HttpGet|HttpPost|HttpPut|HttpDelete|HttpPatch)(?:\("([^"]*)"\))?\]/gi;for(const g of c.matchAll(w)){const x=g[1].replace("Http","").toUpperCase(),v=g[2]||"",L=[h,v].filter(Boolean).join("/").replace(/\/+/g,"/").replace(/\[controller\]/gi,"{controller}");t({method:x,endpointPattern:L||h||"{controller-route}",style:"csharp-controller",sourceFile:a})}const $=/\b(GetAsync|PostAsync|PutAsync|DeleteAsync|SendAsync)\s*\(\s*"([^"]+)"/gi;for(const g of c.matchAll($)){const x=g[1].replace("Async","").replace("Send","SEND").toUpperCase();t({method:x,endpointPattern:g[2],style:"csharp-httpclient",sourceFile:a})}}}const p=i.reduce((s,a)=>(s[a.method]=(s[a.method]||0)+1,s),{});return{totalCalls:i.length,byMethod:p,calls:i.slice(0,80)}}function X(e,r={}){const i=G(e),o=new Map,n=(c,f)=>{o.has(c.id)||o.set(c.id,{id:c.id,title:c.title,reason:"Detected from code signals",sourceFiles:new Set}),o.get(c.id).sourceFiles.add(A.relative(e,f))};for(const c of i){const f=k(c);for(const C of W)C.regex.test(f)&&n(C,c)}const t=A.join(e,"package.json");if(j.existsSync(t)){const c=JSON.parse(k(t)||"{}"),f=typeof c.name=="string"?c.name:A.basename(e);H(f)&&!o.size&&(o.set("ReadItems",{id:"ReadItems",title:"Read Items",reason:`Fallback default for ${f}`,sourceFiles:new Set}),o.set("CreateItem",{id:"CreateItem",title:"Create Item",reason:`Fallback default for ${f}`,sourceFiles:new Set}))}o.size||(o.set("CreateItem",{id:"CreateItem",title:"Create Item",reason:"Fallback default",sourceFiles:new Set}),o.set("ReadItems",{id:"ReadItems",title:"Read Items",reason:"Fallback default",sourceFiles:new Set}));const p=V(e),s=K(e,i,p,r);let a=[];try{if(s.framework==="angular"){const c=U(e,i);for(const f of c.capabilities)o.has(f.id)||o.set(f.id,{...f,sourceFiles:new Set(f.sourceFiles)})}else if(s.framework==="react"||s.framework==="nextjs"){const c=M(e,i);for(const f of c.capabilities)o.has(f.id)||o.set(f.id,{...f,sourceFiles:new Set(f.sourceFiles)})}}catch{}let d={designTokens:[],colorTokens:[],spacingTokens:[],componentClasses:[],themeVars:[]};try{d=O(e,i)}catch{}return{capabilities:Array.from(o.values()).map(c=>({...c,sourceFiles:Array.from(c.sourceFiles||[])})),components:N(i,e),displayFields:B(i),externalLibraries:p,uiLayout:J(i),styling:{...q(e,i,p),designTokens:d.designTokens.length>0?d.designTokens:void 0,colorTokens:d.colorTokens,spacingTokens:d.spacingTokens,componentClasses:d.componentClasses,themeVars:d.themeVars},developmentProfile:s,apiCalls:Q(e,i)}}async function re(e,r=!1){if(r)return e;const i=D.createInterface({input:process.stdin,output:process.stdout}),o=e.map(p=>p.id).join(", "),n=await new Promise(p=>i.question(` Inferred capabilities (${o}). Press Enter to accept or type comma list: `,p));i.close();const t=String(n).trim();return t?t.split(",").map(p=>p.trim()).filter(Boolean).map(p=>({id:p,title:_(p),reason:"User provided during adopt review"})):e}function ae(e){if(!e.length)return"No capabilities inferred.";const r=Y(e),i=r.reduce((t,p)=>t+p.signalCount,0),o={high:r.filter(t=>t.confidence==="high").length,medium:r.filter(t=>t.confidence==="medium").length,low:r.filter(t=>t.confidence==="low").length},n=[];n.push("Adoption Analysis"),n.push("=".repeat(56)),n.push(`Capabilities detected : ${r.length}`),n.push(`Signal hits total : ${i}`),n.push(`Confidence mix : high=${o.high}, medium=${o.medium}, low=${o.low}`),n.push("-".repeat(56)),n.push("Capability Breakdown"),n.push("-".repeat(56)),n.push("Confidence Signals Capability"),n.push("-".repeat(56));for(const t of r){const p=t.confidence.toUpperCase().padEnd(10," "),s=String(t.signalCount).padEnd(7," ");if(n.push(`${p} ${s} ${t.id} (${t.title})`),t.signalCount>0){const a=t.sourceFiles.slice(0,3).join(", ");n.push(` sources: ${a}`),t.sourceFiles.length>3&&n.push(` more : +${t.sourceFiles.length-3} additional files`)}else n.push(" sources: inferred fallback (no strong code signal)")}return n.push("=".repeat(56)),n.join(`
3
+ `)}function ce(e){const r=(i,o,n=10)=>{const t=[`${i} (${o.length})`];if(t.push("-".repeat(56)),!o.length)return t.push(" - none"),t.join(`
4
+ `);for(const p of o.slice(0,n))t.push(` - ${p}`);return o.length>n&&t.push(` - ... +${o.length-n} more`),t.join(`
5
+ `)};return["Project Structure Signals","=".repeat(56),r("Components",e.components||[]),r("Display fields",e.displayFields||[]),r("External libraries",e.externalLibraries||[]),"UI layout","-".repeat(56),` - layout type: ${e.uiLayout?.layoutType||"unknown"}`,` - uses grid : ${e.uiLayout?.usesGrid?"yes":"no"}`,` - uses flex : ${e.uiLayout?.usesFlex?"yes":"no"}`,` - sections : ${(e.uiLayout?.sections||[]).slice(0,10).join(", ")||"none"}`,"Styling","-".repeat(56),` - frameworks : ${(e.styling?.cssFrameworks||[]).join(", ")||"none detected"}`,` - style files: ${e.styling?.styleFileCount??0}`,` - tokens : ${(e.styling?.designTokens||[]).slice(0,8).join(", ")||"none detected"}`,"Development profile","-".repeat(56),` - language : ${e.developmentProfile?.language||"unknown"} (auto: ${e.developmentProfile?.detected?.language||"unknown"})`,` - framework : ${e.developmentProfile?.framework||"unknown"} (auto: ${e.developmentProfile?.detected?.framework||"unknown"})`,` - project type: ${e.developmentProfile?.projectType||"unknown"} (auto: ${e.developmentProfile?.detected?.projectType||"unknown"})`,"API calls","-".repeat(56),` - total calls : ${e.apiCalls?.totalCalls??0}`,` - by method : ${Object.entries(e.apiCalls?.byMethod||{}).map(([i,o])=>`${i}:${o}`).join(", ")||"none"}`,...(e.apiCalls?.calls||[]).slice(0,6).map(i=>` - ${i.method} ${i.endpointPattern} [${i.style}] (${i.sourceFile})`),...(e.apiCalls?.calls||[]).length>6?[` - ... +${(e.apiCalls?.calls||[]).length-6} more`]:[],"=".repeat(56)].join(`
6
+ `)}function Y(e){return e.map(r=>{const i=r.sourceFiles?.length||0,o=i>=3?"high":i>=1?"medium":"low";return{id:r.id,title:r.title,reason:r.reason,confidence:o,sourceFiles:r.sourceFiles||[],signalCount:i}})}function ee(e){if(!e)return null;const r=[...e.components||[]].filter(Boolean).slice(0,40),i=(e.styling?.designTokens||[]).slice(0,20),o=e.uiLayout?.layoutType||"unknown",n=(e.uiLayout?.sections||[]).slice(0,12),t=e.styling?.cssFrameworks||[],p=(e.styling?.colorTokens||[]).slice(0,10),s=(e.styling?.themeVars||[]).slice(0,10);return{components:r,designTokens:i,colorTokens:p,themeVars:s,cssFrameworks:t,layout:o,sections:n,lastScanned:new Date().toISOString()}}function le(e,r){return r?e?{...r,components:[...new Set([...r.components||[],...e.components||[]])].slice(0,50),designTokens:[...new Set([...r.designTokens||[],...e.designTokens||[]])].slice(0,30)}:r:e}function pe(e,r,i,o=null){const n=i.map(m=>m.id),t=ee(o),p={policyId:r,policyVersion:1,capabilities:n,rules:{docsRequiredOnCapabilityChange:!0,requireScenarioForEachCapability:!0,requireChangelogOnCapabilityChange:!0},...t?{ui:t}:{}};j.mkdirSync(A.join(e,"scenarios"),{recursive:!0}),j.writeFileSync(A.join(e,"contract.json"),JSON.stringify(p,null,2)+`
7
+ `);const s={schemaVersion:1,capabilities:i.map(m=>({id:m.id,title:m.title||_(m.id),since:"0.1.0"}))};j.writeFileSync(A.join(e,"capabilities.json"),JSON.stringify(s,null,2)+`
8
+ `);const a={scenarioId:"adoption_baseline",description:"Baseline inferred from existing codebase during adoption",capabilitiesCovered:n,steps:n.map(m=>({action:m,expect:`${m} behavior exists in the current project`}))};if(j.writeFileSync(A.join(e,"scenarios","adoption_baseline.json"),JSON.stringify(a,null,2)+`
9
+ `),o){const m={profileId:"adoption_profile",generatedAt:new Date().toISOString(),components:o.components||[],displayFields:o.displayFields||[],externalLibraries:o.externalLibraries||[],uiLayout:o.uiLayout||{layoutType:"unknown",usesGrid:!1,usesFlex:!1,sections:[]},styling:o.styling||{cssFrameworks:[],styleFileCount:0,styleFilesSample:[],designTokens:[]},developmentProfile:o.developmentProfile||{language:"unknown",framework:"unknown",projectType:"unknown",detected:{language:"unknown",framework:"unknown",projectType:"unknown"}},apiCalls:o.apiCalls||{totalCalls:0,byMethod:{},calls:[]}};j.writeFileSync(A.join(e,"adoption_profile.json"),JSON.stringify(m,null,2)+`
10
+ `);try{R(e,o,i)}catch{}}const d=`# Changelog \u2014 ${r}
11
+
12
+ ## Unreleased
13
+
14
+ - Adopted infernoflow into an existing project and generated baseline capabilities.
15
+ - Captured detected components, display fields, and external libraries in adoption profile.
16
+
17
+ ## 0.1.0 \u2014 Adoption baseline
18
+
19
+ - Initial baseline generated by infernoflow init --adopt
20
+ `;j.writeFileSync(A.join(e,"CHANGELOG.md"),d,"utf8")}export{ae as buildAdoptionReport,ce as buildSignalsReport,ee as buildUiContractSection,ie as discoverCapabilities,X as discoverProjectSignals,le as mergeUiSection,re as reviewCapabilitiesInteractive,Y as summarizeCapabilities,pe as writeAdoptionBaseline};