infernoflow 0.37.1 → 0.37.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.
Files changed (88) hide show
  1. package/CHANGELOG.md +64 -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,428 +1,4 @@
1
- /**
2
- * infernoflow monorepo
3
- *
4
- * Monorepo-aware capability tracking. Detects packages in a monorepo
5
- * (nx, turborepo, pnpm workspaces, yarn workspaces, Lerna) and manages
6
- * per-package contracts.
7
- *
8
- * Sub-commands:
9
- * monorepo init Detect packages, scaffold inferno/ per package
10
- * monorepo list List all detected packages + contract status
11
- * monorepo status Show health across all packages at once
12
- * monorepo diff [--package] Diff capabilities for one or all packages
13
- * monorepo sync Sync all package contracts to root summary
14
- *
15
- * Usage:
16
- * infernoflow monorepo init
17
- * infernoflow monorepo list
18
- * infernoflow monorepo status
19
- * infernoflow monorepo diff --package auth
20
- * infernoflow monorepo sync
21
- * infernoflow monorepo status --json
22
- */
23
-
24
- import * as fs from "node:fs";
25
- import * as path from "node:path";
26
- import { fileURLToPath } from "node:url";
27
- import { spawnSync } from "node:child_process";
28
- import { header, ok, warn, info, done, bold, cyan, gray, green, yellow, red } from "../ui/output.mjs";
29
-
30
- // ── Package detection ─────────────────────────────────────────────────────────
31
-
32
- function detectWorkspaceType(cwd) {
33
- const has = (f) => fs.existsSync(path.join(cwd, f));
34
-
35
- if (has("nx.json")) return "nx";
36
- if (has("turbo.json")) return "turborepo";
37
- if (has("lerna.json")) return "lerna";
38
- if (has("pnpm-workspace.yaml")) return "pnpm";
39
-
40
- const pkg = (() => {
41
- try { return JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf8")); }
42
- catch { return {}; }
43
- })();
44
-
45
- if (pkg.workspaces) return Array.isArray(pkg.workspaces) ? "yarn" : "npm-workspaces";
46
- return null;
47
- }
48
-
49
- function readWorkspaceGlobs(cwd, wsType) {
50
- try {
51
- if (wsType === "pnpm") {
52
- const raw = fs.readFileSync(path.join(cwd, "pnpm-workspace.yaml"), "utf8");
53
- const matches = [...raw.matchAll(/^\s*-\s*['"]?([^'"]+)['"]?/gm)];
54
- return matches.map(m => m[1].trim());
55
- }
56
- if (wsType === "lerna") {
57
- const cfg = JSON.parse(fs.readFileSync(path.join(cwd, "lerna.json"), "utf8"));
58
- return cfg.packages || ["packages/*"];
59
- }
60
- const pkg = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf8"));
61
- const ws = pkg.workspaces;
62
- if (Array.isArray(ws)) return ws;
63
- if (ws?.packages) return ws.packages;
64
- } catch {}
65
- return ["packages/*", "apps/*", "libs/*"];
66
- }
67
-
68
- function globToPackages(cwd, globs) {
69
- const packages = [];
70
- for (const pattern of globs) {
71
- // Simple glob: handle "packages/*" and "apps/*" patterns
72
- const parts = pattern.split("/");
73
- const parent = parts.slice(0, -1).join("/");
74
- const leaf = parts[parts.length - 1];
75
- const dir = path.join(cwd, parent);
76
-
77
- if (!fs.existsSync(dir)) continue;
78
- if (leaf === "*") {
79
- try {
80
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
81
- if (entry.isDirectory()) {
82
- const pkgPath = path.join(dir, entry.name, "package.json");
83
- if (fs.existsSync(pkgPath)) {
84
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
85
- packages.push({
86
- name: pkg.name || entry.name,
87
- dir: path.join(dir, entry.name),
88
- version: pkg.version || "0.0.0",
89
- });
90
- }
91
- }
92
- }
93
- } catch {}
94
- } else {
95
- const fullDir = path.join(cwd, pattern);
96
- const pkgPath = path.join(fullDir, "package.json");
97
- if (fs.existsSync(pkgPath)) {
98
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
99
- packages.push({ name: pkg.name || leaf, dir: fullDir, version: pkg.version || "0.0.0" });
100
- }
101
- }
102
- }
103
- return packages;
104
- }
105
-
106
- function detectPackages(cwd) {
107
- const wsType = detectWorkspaceType(cwd);
108
- if (!wsType) return { type: null, packages: [] };
109
- const globs = readWorkspaceGlobs(cwd, wsType);
110
- const packages = globToPackages(cwd, globs);
111
- return { type: wsType, packages };
112
- }
113
-
114
- // ── Contract helpers ──────────────────────────────────────────────────────────
115
-
116
- function readPackageContract(pkgDir) {
117
- for (const f of ["contract.json", "capabilities.json"]) {
118
- const p = path.join(pkgDir, "inferno", f);
119
- if (fs.existsSync(p)) { try { return JSON.parse(fs.readFileSync(p, "utf8")); } catch {} }
120
- }
121
- return null;
122
- }
123
-
124
- function hasInferno(pkgDir) {
125
- return fs.existsSync(path.join(pkgDir, "inferno"));
126
- }
127
-
128
- function contractStatus(pkgDir) {
129
- if (!hasInferno(pkgDir)) return "not-init";
130
- const contract = readPackageContract(pkgDir);
131
- if (!contract) return "no-contract";
132
- return "ok";
133
- }
134
-
135
- // ── CLI runner ────────────────────────────────────────────────────────────────
136
-
137
- function runInferno(args, cwd) {
138
- const binPath = path.join(path.dirname(path.dirname(fileURLToPath(import.meta.url))), "..", "bin", "infernoflow.mjs");
139
- const result = spawnSync(process.execPath, [binPath, ...args], {
140
- cwd,
141
- encoding: "utf8",
142
- timeout: 60_000,
143
- env: { ...process.env, NO_COLOR: "1" },
144
- });
145
- return { stdout: result.stdout || "", stderr: result.stderr || "", status: result.status };
146
- }
147
-
148
- // ── Sub-commands ──────────────────────────────────────────────────────────────
149
-
150
- async function subcmdInit(args, cwd) {
151
- const jsonMode = args.includes("--json");
152
- const force = args.includes("--force") || args.includes("-f");
153
- const autoYes = args.includes("--yes") || args.includes("-y");
154
-
155
- const { type, packages } = detectPackages(cwd);
156
-
157
- if (!type) {
158
- const msg = "No monorepo configuration detected. Supported: nx, turborepo, pnpm workspaces, yarn workspaces, lerna.";
159
- if (jsonMode) { console.log(JSON.stringify({ ok: false, error: msg })); } else { warn(msg); }
160
- process.exit(1);
161
- }
162
-
163
- if (!jsonMode) {
164
- header(`Monorepo init (${type})`);
165
- console.log(` Detected ${bold(String(packages.length))} packages:\n`);
166
- packages.forEach(p => {
167
- const status = contractStatus(p.dir);
168
- const icon = status === "ok" ? green("✔") : yellow("·");
169
- console.log(` ${icon} ${bold(p.name)} ${gray(path.relative(cwd, p.dir))}`);
170
- });
171
- console.log();
172
- }
173
-
174
- if (packages.length === 0) {
175
- if (jsonMode) { console.log(JSON.stringify({ ok: false, error: "No packages found" })); }
176
- else { warn("No packages found matching workspace globs."); }
177
- process.exit(1);
178
- }
179
-
180
- const results = [];
181
- for (const pkg of packages) {
182
- const status = contractStatus(pkg.dir);
183
- if (status === "ok" && !force) {
184
- if (!jsonMode) info(`${pkg.name}: already initialised (use --force to reinit)`);
185
- results.push({ name: pkg.name, status: "skipped" });
186
- continue;
187
- }
188
-
189
- if (!jsonMode) process.stdout.write(` Initialising ${cyan(pkg.name)}… `);
190
-
191
- const initArgs = ["init", "--adopt", "--yes"];
192
- if (force) initArgs.push("--force");
193
- const r = runInferno(initArgs, pkg.dir);
194
-
195
- if (r.status === 0) {
196
- if (!jsonMode) console.log(green("done"));
197
- results.push({ name: pkg.name, status: "ok" });
198
- } else {
199
- if (!jsonMode) console.log(red("failed"));
200
- results.push({ name: pkg.name, status: "error", error: r.stderr.trim().slice(0, 120) });
201
- }
202
- }
203
-
204
- // Write root summary
205
- const summary = {
206
- monorepoType: type,
207
- packages: results,
208
- updatedAt: new Date().toISOString(),
209
- };
210
- fs.writeFileSync(path.join(cwd, "inferno-monorepo.json"), JSON.stringify(summary, null, 2) + "\n");
211
-
212
- if (jsonMode) {
213
- console.log(JSON.stringify({ ok: true, type, packages: results }));
214
- } else {
215
- console.log();
216
- const succeeded = results.filter(r => r.status === "ok").length;
217
- done(`Initialised ${bold(String(succeeded))} of ${results.length} packages`);
218
- console.log(` ${gray("Root summary:")} ${cyan("inferno-monorepo.json")}`);
219
- console.log();
220
- }
221
- }
222
-
223
- async function subcmdList(args, cwd) {
224
- const jsonMode = args.includes("--json");
225
- const { type, packages } = detectPackages(cwd);
226
-
227
- if (!type && packages.length === 0) {
228
- if (jsonMode) { console.log(JSON.stringify({ ok: false, error: "No monorepo detected" })); }
229
- else { warn("No monorepo detected. Run: infernoflow monorepo init"); }
230
- return;
231
- }
232
-
233
- const rows = packages.map(p => ({
234
- name: p.name,
235
- dir: path.relative(cwd, p.dir),
236
- version: p.version,
237
- status: contractStatus(p.dir),
238
- caps: (() => {
239
- const c = readPackageContract(p.dir);
240
- return c ? (c.capabilities || []).length : 0;
241
- })(),
242
- }));
243
-
244
- if (jsonMode) {
245
- console.log(JSON.stringify({ ok: true, type, packages: rows }));
246
- return;
247
- }
248
-
249
- console.log();
250
- console.log(` ${bold("Monorepo packages")} ${gray("(" + type + ")")}`);
251
- console.log();
252
- const w = Math.max(...rows.map(r => r.name.length), 8) + 2;
253
- rows.forEach(r => {
254
- const icon = r.status === "ok" ? green("✔") : r.status === "not-init" ? yellow("○") : red("✗");
255
- const caps = r.status === "ok" ? gray(`${r.caps} caps`) : gray(r.status);
256
- console.log(` ${icon} ${r.name.padEnd(w)}${r.version.padEnd(12)}${caps}`);
257
- });
258
- console.log();
259
- }
260
-
261
- async function subcmdStatus(args, cwd) {
262
- const jsonMode = args.includes("--json");
263
- const { type, packages } = detectPackages(cwd);
264
-
265
- if (!packages.length) {
266
- if (jsonMode) { console.log(JSON.stringify({ ok: false, error: "No packages found" })); }
267
- else { warn("No packages found."); }
268
- return;
269
- }
270
-
271
- if (!jsonMode) header(`Monorepo status (${packages.length} packages)`);
272
-
273
- const results = [];
274
- for (const pkg of packages) {
275
- if (!hasInferno(pkg.dir)) {
276
- results.push({ name: pkg.name, status: "not-init", caps: 0 });
277
- if (!jsonMode) console.log(` ${yellow("○")} ${bold(pkg.name)} ${gray("not initialised")}`);
278
- continue;
279
- }
280
- const r = runInferno(["status", "--json"], pkg.dir);
281
- try {
282
- const data = JSON.parse(r.stdout.trim());
283
- const caps = (data.capabilityDetails || []).length;
284
- const ok_ = data.ok !== false;
285
- results.push({ name: pkg.name, status: ok_ ? "ok" : "error", caps, version: data.policyVersion });
286
- if (!jsonMode) {
287
- const icon = ok_ ? green("✔") : red("✗");
288
- console.log(` ${icon} ${bold(pkg.name.padEnd(28))}${gray("v" + (data.policyVersion || "?"))} ${caps} caps`);
289
- }
290
- } catch {
291
- results.push({ name: pkg.name, status: "error", caps: 0, error: "status failed" });
292
- if (!jsonMode) console.log(` ${red("✗")} ${bold(pkg.name)} ${gray("status check failed")}`);
293
- }
294
- }
295
-
296
- if (jsonMode) {
297
- const allOk = results.every(r => r.status === "ok");
298
- console.log(JSON.stringify({ ok: allOk, type, packages: results }));
299
- } else {
300
- console.log();
301
- const ok_ = results.filter(r => r.status === "ok").length;
302
- console.log(` ${ok_ === results.length ? green("✔") : yellow("⚠")} ${ok_}/${results.length} packages healthy`);
303
- console.log();
304
- }
305
- }
306
-
307
- async function subcmdDiff(args, cwd) {
308
- const jsonMode = args.includes("--json");
309
- const pkgFilter = args.includes("--package") ? args[args.indexOf("--package") + 1] : null;
310
- const { packages } = detectPackages(cwd);
311
-
312
- const targets = pkgFilter
313
- ? packages.filter(p => p.name === pkgFilter || p.name.endsWith("/" + pkgFilter))
314
- : packages.filter(p => hasInferno(p.dir));
315
-
316
- if (!targets.length) {
317
- if (jsonMode) { console.log(JSON.stringify({ ok: false, error: pkgFilter ? `Package not found: ${pkgFilter}` : "No initialised packages found" })); }
318
- else { warn(pkgFilter ? `Package not found: ${pkgFilter}` : "No initialised packages found."); }
319
- return;
320
- }
321
-
322
- if (!jsonMode && !pkgFilter) header(`Monorepo diff (${targets.length} packages)`);
323
-
324
- const allResults = [];
325
- for (const pkg of targets) {
326
- const r = runInferno(["diff", "--json"], pkg.dir);
327
- try {
328
- const data = JSON.parse(r.stdout.trim());
329
- const added = (data.added || []).length;
330
- const removed = (data.removed || []).length;
331
- const changed = (data.changed || []).length;
332
- allResults.push({ name: pkg.name, added, removed, changed, data });
333
- if (!jsonMode) {
334
- if (added || removed || changed) {
335
- console.log(` ${bold(pkg.name)}`);
336
- if (added) console.log(` ${green("+")} ${added} added`);
337
- if (removed) console.log(` ${red("-")} ${removed} removed`);
338
- if (changed) console.log(` ${yellow("~")} ${changed} changed`);
339
- } else {
340
- console.log(` ${green("✔")} ${bold(pkg.name)} ${gray("no changes")}`);
341
- }
342
- }
343
- } catch {
344
- allResults.push({ name: pkg.name, error: "diff failed" });
345
- if (!jsonMode) console.log(` ${red("✗")} ${bold(pkg.name)} ${gray("diff failed")}`);
346
- }
347
- }
348
-
349
- if (jsonMode) {
350
- console.log(JSON.stringify({ ok: true, packages: allResults }));
351
- } else {
352
- console.log();
353
- }
354
- }
355
-
356
- async function subcmdSync(args, cwd) {
357
- const jsonMode = args.includes("--json");
358
- const { type, packages } = detectPackages(cwd);
359
-
360
- if (!jsonMode) header("Syncing monorepo contracts");
361
-
362
- // Build a root aggregate contract
363
- const aggregate = {
364
- monorepoType: type,
365
- updatedAt: new Date().toISOString(),
366
- packages: [],
367
- };
368
-
369
- for (const pkg of packages) {
370
- const contract = readPackageContract(pkg.dir);
371
- if (!contract) continue;
372
- aggregate.packages.push({
373
- name: pkg.name,
374
- version: contract.policyVersion || pkg.version,
375
- capabilities: (contract.capabilities || []).map(c => typeof c === "string" ? c : c.id),
376
- capsCount: (contract.capabilities || []).length,
377
- });
378
- if (!jsonMode) console.log(` ${green("✔")} ${bold(pkg.name)} ${gray((contract.capabilities || []).length + " caps")}`);
379
- }
380
-
381
- const outPath = path.join(cwd, "inferno-monorepo.json");
382
- fs.writeFileSync(outPath, JSON.stringify(aggregate, null, 2) + "\n");
383
-
384
- const totalCaps = aggregate.packages.reduce((sum, p) => sum + p.capsCount, 0);
385
-
386
- if (jsonMode) {
387
- console.log(JSON.stringify({ ok: true, packages: aggregate.packages.length, totalCaps }));
388
- } else {
389
- console.log();
390
- done(`Synced ${bold(String(aggregate.packages.length))} packages (${totalCaps} total capabilities)`);
391
- console.log(` ${cyan(outPath)}`);
392
- console.log();
393
- }
394
- }
395
-
396
- // ── Entry ─────────────────────────────────────────────────────────────────────
397
-
398
- export async function monorepoCommand(rawArgs) {
399
- const args = rawArgs.slice(1);
400
- const subcmd = args[0];
401
- const cwd = process.cwd();
402
- const rest = args.slice(1);
403
-
404
- switch (subcmd) {
405
- case "init": return subcmdInit(rest, cwd);
406
- case "list": return subcmdList(rest, cwd);
407
- case "status": return subcmdStatus(rest, cwd);
408
- case "diff": return subcmdDiff(rest, cwd);
409
- case "sync": return subcmdSync(rest, cwd);
410
- default: {
411
- const jsonMode = args.includes("--json");
412
- const msg = `Unknown monorepo sub-command: ${subcmd || "(none)"}`;
413
- if (jsonMode) { console.log(JSON.stringify({ ok: false, error: msg })); return; }
414
- console.log();
415
- console.log(` ${bold("infernoflow monorepo")} — per-package capability tracking`);
416
- console.log();
417
- console.log(` ${cyan("infernoflow monorepo init")} Scaffold inferno/ in each package`);
418
- console.log(` ${cyan("infernoflow monorepo list")} List packages and contract status`);
419
- console.log(` ${cyan("infernoflow monorepo status")} Health check across all packages`);
420
- console.log(` ${cyan("infernoflow monorepo diff")} Capability diff for all packages`);
421
- console.log(` ${cyan("infernoflow monorepo diff --package auth")} Diff a specific package`);
422
- console.log(` ${cyan("infernoflow monorepo sync")} Aggregate all contracts to inferno-monorepo.json`);
423
- console.log();
424
- console.log(` ${gray("Supported: nx, turborepo, pnpm workspaces, yarn workspaces, lerna")}`);
425
- console.log();
426
- }
427
- }
428
- }
1
+ import*as g from"node:fs";import*as p from"node:path";import{fileURLToPath as F}from"node:url";import{spawnSync as M}from"node:child_process";import{header as b,warn as S,info as P,done as x,bold as u,cyan as k,gray as d,green as y,yellow as j,red as $}from"../ui/output.mjs";function A(e){const a=s=>g.existsSync(p.join(e,s));if(a("nx.json"))return"nx";if(a("turbo.json"))return"turborepo";if(a("lerna.json"))return"lerna";if(a("pnpm-workspace.yaml"))return"pnpm";const n=(()=>{try{return JSON.parse(g.readFileSync(p.join(e,"package.json"),"utf8"))}catch{return{}}})();return n.workspaces?Array.isArray(n.workspaces)?"yarn":"npm-workspaces":null}function C(e,a){try{if(a==="pnpm")return[...g.readFileSync(p.join(e,"pnpm-workspace.yaml"),"utf8").matchAll(/^\s*-\s*['"]?([^'"]+)['"]?/gm)].map(c=>c[1].trim());if(a==="lerna")return JSON.parse(g.readFileSync(p.join(e,"lerna.json"),"utf8")).packages||["packages/*"];const s=JSON.parse(g.readFileSync(p.join(e,"package.json"),"utf8")).workspaces;if(Array.isArray(s))return s;if(s?.packages)return s.packages}catch{}return["packages/*","apps/*","libs/*"]}function D(e,a){const n=[];for(const s of a){const l=s.split("/"),r=l.slice(0,-1).join("/"),c=l[l.length-1],o=p.join(e,r);if(g.existsSync(o))if(c==="*")try{for(const i of g.readdirSync(o,{withFileTypes:!0}))if(i.isDirectory()){const t=p.join(o,i.name,"package.json");if(g.existsSync(t)){const f=JSON.parse(g.readFileSync(t,"utf8"));n.push({name:f.name||i.name,dir:p.join(o,i.name),version:f.version||"0.0.0"})}}}catch{}else{const i=p.join(e,s),t=p.join(i,"package.json");if(g.existsSync(t)){const f=JSON.parse(g.readFileSync(t,"utf8"));n.push({name:f.name||c,dir:i,version:f.version||"0.0.0"})}}}return n}function N(e){const a=A(e);if(!a)return{type:null,packages:[]};const n=C(e,a),s=D(e,n);return{type:a,packages:s}}function w(e){for(const a of["contract.json","capabilities.json"]){const n=p.join(e,"inferno",a);if(g.existsSync(n))try{return JSON.parse(g.readFileSync(n,"utf8"))}catch{}}return null}function O(e){return g.existsSync(p.join(e,"inferno"))}function J(e){return O(e)?w(e)?"ok":"no-contract":"not-init"}function v(e,a){const n=p.join(p.dirname(p.dirname(F(import.meta.url))),"..","bin","infernoflow.mjs"),s=M(process.execPath,[n,...e],{cwd:a,encoding:"utf8",timeout:6e4,env:{...process.env,NO_COLOR:"1"}});return{stdout:s.stdout||"",stderr:s.stderr||"",status:s.status}}async function I(e,a){const n=e.includes("--json"),s=e.includes("--force")||e.includes("-f"),l=e.includes("--yes")||e.includes("-y"),{type:r,packages:c}=N(a);if(!r){const t="No monorepo configuration detected. Supported: nx, turborepo, pnpm workspaces, yarn workspaces, lerna.";n?console.log(JSON.stringify({ok:!1,error:t})):S(t),process.exit(1)}n||(b(`Monorepo init (${r})`),console.log(` Detected ${u(String(c.length))} packages:
2
+ `),c.forEach(t=>{const m=J(t.dir)==="ok"?y("\u2714"):j("\xB7");console.log(` ${m} ${u(t.name)} ${d(p.relative(a,t.dir))}`)}),console.log()),c.length===0&&(n?console.log(JSON.stringify({ok:!1,error:"No packages found"})):S("No packages found matching workspace globs."),process.exit(1));const o=[];for(const t of c){if(J(t.dir)==="ok"&&!s){n||P(`${t.name}: already initialised (use --force to reinit)`),o.push({name:t.name,status:"skipped"});continue}n||process.stdout.write(` Initialising ${k(t.name)}\u2026 `);const m=["init","--adopt","--yes"];s&&m.push("--force");const h=v(m,t.dir);h.status===0?(n||console.log(y("done")),o.push({name:t.name,status:"ok"})):(n||console.log($("failed")),o.push({name:t.name,status:"error",error:h.stderr.trim().slice(0,120)}))}const i={monorepoType:r,packages:o,updatedAt:new Date().toISOString()};if(g.writeFileSync(p.join(a,"inferno-monorepo.json"),JSON.stringify(i,null,2)+`
3
+ `),n)console.log(JSON.stringify({ok:!0,type:r,packages:o}));else{console.log();const t=o.filter(f=>f.status==="ok").length;x(`Initialised ${u(String(t))} of ${o.length} packages`),console.log(` ${d("Root summary:")} ${k("inferno-monorepo.json")}`),console.log()}}async function T(e,a){const n=e.includes("--json"),{type:s,packages:l}=N(a);if(!s&&l.length===0){n?console.log(JSON.stringify({ok:!1,error:"No monorepo detected"})):S("No monorepo detected. Run: infernoflow monorepo init");return}const r=l.map(o=>({name:o.name,dir:p.relative(a,o.dir),version:o.version,status:J(o.dir),caps:(()=>{const i=w(o.dir);return i?(i.capabilities||[]).length:0})()}));if(n){console.log(JSON.stringify({ok:!0,type:s,packages:r}));return}console.log(),console.log(` ${u("Monorepo packages")} ${d("("+s+")")}`),console.log();const c=Math.max(...r.map(o=>o.name.length),8)+2;r.forEach(o=>{const i=o.status==="ok"?y("\u2714"):o.status==="not-init"?j("\u25CB"):$("\u2717"),t=o.status==="ok"?d(`${o.caps} caps`):d(o.status);console.log(` ${i} ${o.name.padEnd(c)}${o.version.padEnd(12)}${t}`)}),console.log()}async function E(e,a){const n=e.includes("--json"),{type:s,packages:l}=N(a);if(!l.length){n?console.log(JSON.stringify({ok:!1,error:"No packages found"})):S("No packages found.");return}n||b(`Monorepo status (${l.length} packages)`);const r=[];for(const c of l){if(!O(c.dir)){r.push({name:c.name,status:"not-init",caps:0}),n||console.log(` ${j("\u25CB")} ${u(c.name)} ${d("not initialised")}`);continue}const o=v(["status","--json"],c.dir);try{const i=JSON.parse(o.stdout.trim()),t=(i.capabilityDetails||[]).length,f=i.ok!==!1;if(r.push({name:c.name,status:f?"ok":"error",caps:t,version:i.policyVersion}),!n){const m=f?y("\u2714"):$("\u2717");console.log(` ${m} ${u(c.name.padEnd(28))}${d("v"+(i.policyVersion||"?"))} ${t} caps`)}}catch{r.push({name:c.name,status:"error",caps:0,error:"status failed"}),n||console.log(` ${$("\u2717")} ${u(c.name)} ${d("status check failed")}`)}}if(n){const c=r.every(o=>o.status==="ok");console.log(JSON.stringify({ok:c,type:s,packages:r}))}else{console.log();const c=r.filter(o=>o.status==="ok").length;console.log(` ${c===r.length?y("\u2714"):j("\u26A0")} ${c}/${r.length} packages healthy`),console.log()}}async function R(e,a){const n=e.includes("--json"),s=e.includes("--package")?e[e.indexOf("--package")+1]:null,{packages:l}=N(a),r=s?l.filter(o=>o.name===s||o.name.endsWith("/"+s)):l.filter(o=>O(o.dir));if(!r.length){n?console.log(JSON.stringify({ok:!1,error:s?`Package not found: ${s}`:"No initialised packages found"})):S(s?`Package not found: ${s}`:"No initialised packages found.");return}!n&&!s&&b(`Monorepo diff (${r.length} packages)`);const c=[];for(const o of r){const i=v(["diff","--json"],o.dir);try{const t=JSON.parse(i.stdout.trim()),f=(t.added||[]).length,m=(t.removed||[]).length,h=(t.changed||[]).length;c.push({name:o.name,added:f,removed:m,changed:h,data:t}),n||(f||m||h?(console.log(` ${u(o.name)}`),f&&console.log(` ${y("+")} ${f} added`),m&&console.log(` ${$("-")} ${m} removed`),h&&console.log(` ${j("~")} ${h} changed`)):console.log(` ${y("\u2714")} ${u(o.name)} ${d("no changes")}`))}catch{c.push({name:o.name,error:"diff failed"}),n||console.log(` ${$("\u2717")} ${u(o.name)} ${d("diff failed")}`)}}n?console.log(JSON.stringify({ok:!0,packages:c})):console.log()}async function L(e,a){const n=e.includes("--json"),{type:s,packages:l}=N(a);n||b("Syncing monorepo contracts");const r={monorepoType:s,updatedAt:new Date().toISOString(),packages:[]};for(const i of l){const t=w(i.dir);t&&(r.packages.push({name:i.name,version:t.policyVersion||i.version,capabilities:(t.capabilities||[]).map(f=>typeof f=="string"?f:f.id),capsCount:(t.capabilities||[]).length}),n||console.log(` ${y("\u2714")} ${u(i.name)} ${d((t.capabilities||[]).length+" caps")}`))}const c=p.join(a,"inferno-monorepo.json");g.writeFileSync(c,JSON.stringify(r,null,2)+`
4
+ `);const o=r.packages.reduce((i,t)=>i+t.capsCount,0);n?console.log(JSON.stringify({ok:!0,packages:r.packages.length,totalCaps:o})):(console.log(),x(`Synced ${u(String(r.packages.length))} packages (${o} total capabilities)`),console.log(` ${k(c)}`),console.log())}async function G(e){const a=e.slice(1),n=a[0],s=process.cwd(),l=a.slice(1);switch(n){case"init":return I(l,s);case"list":return T(l,s);case"status":return E(l,s);case"diff":return R(l,s);case"sync":return L(l,s);default:{const r=a.includes("--json"),c=`Unknown monorepo sub-command: ${n||"(none)"}`;if(r){console.log(JSON.stringify({ok:!1,error:c}));return}console.log(),console.log(` ${u("infernoflow monorepo")} \u2014 per-package capability tracking`),console.log(),console.log(` ${k("infernoflow monorepo init")} Scaffold inferno/ in each package`),console.log(` ${k("infernoflow monorepo list")} List packages and contract status`),console.log(` ${k("infernoflow monorepo status")} Health check across all packages`),console.log(` ${k("infernoflow monorepo diff")} Capability diff for all packages`),console.log(` ${k("infernoflow monorepo diff --package auth")} Diff a specific package`),console.log(` ${k("infernoflow monorepo sync")} Aggregate all contracts to inferno-monorepo.json`),console.log(),console.log(` ${d("Supported: nx, turborepo, pnpm workspaces, yarn workspaces, lerna")}`),console.log()}}}export{G as monorepoCommand};