infernoflow 0.32.8 → 0.33.0

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 (81) hide show
  1. package/dist/bin/infernoflow.mjs +84 -255
  2. package/dist/lib/adopters/angular.mjs +1 -128
  3. package/dist/lib/adopters/css.mjs +1 -111
  4. package/dist/lib/adopters/react.mjs +1 -104
  5. package/dist/lib/ai/ideDetection.mjs +1 -31
  6. package/dist/lib/ai/localProvider.mjs +1 -88
  7. package/dist/lib/ai/providerRouter.mjs +2 -295
  8. package/dist/lib/commands/adopt.mjs +20 -869
  9. package/dist/lib/commands/adoptWizard.mjs +9 -320
  10. package/dist/lib/commands/agent.mjs +5 -191
  11. package/dist/lib/commands/ai.mjs +2 -407
  12. package/dist/lib/commands/audit.mjs +13 -300
  13. package/dist/lib/commands/changelog.mjs +26 -594
  14. package/dist/lib/commands/check.mjs +3 -184
  15. package/dist/lib/commands/ci.mjs +3 -208
  16. package/dist/lib/commands/claudeMd.mjs +25 -130
  17. package/dist/lib/commands/cloud.mjs +5 -521
  18. package/dist/lib/commands/context.mjs +34 -287
  19. package/dist/lib/commands/coverage.mjs +2 -282
  20. package/dist/lib/commands/dashboard.mjs +123 -635
  21. package/dist/lib/commands/demo.mjs +8 -465
  22. package/dist/lib/commands/diff.mjs +5 -274
  23. package/dist/lib/commands/docGate.mjs +2 -81
  24. package/dist/lib/commands/doctor.mjs +3 -321
  25. package/dist/lib/commands/explain.mjs +8 -438
  26. package/dist/lib/commands/export.mjs +10 -239
  27. package/dist/lib/commands/generateSkills.mjs +38 -163
  28. package/dist/lib/commands/graph.mjs +203 -321
  29. package/dist/lib/commands/health.mjs +2 -309
  30. package/dist/lib/commands/impact.mjs +2 -325
  31. package/dist/lib/commands/implement.mjs +7 -103
  32. package/dist/lib/commands/init.mjs +23 -475
  33. package/dist/lib/commands/installCursorHooks.mjs +1 -36
  34. package/dist/lib/commands/installVsCodeCopilotHooks.mjs +1 -37
  35. package/dist/lib/commands/link.mjs +2 -342
  36. package/dist/lib/commands/log.mjs +16 -0
  37. package/dist/lib/commands/monorepo.mjs +4 -428
  38. package/dist/lib/commands/notify.mjs +4 -258
  39. package/dist/lib/commands/onboard.mjs +4 -296
  40. package/dist/lib/commands/prComment.mjs +2 -361
  41. package/dist/lib/commands/prImpact.mjs +2 -157
  42. package/dist/lib/commands/publish.mjs +15 -316
  43. package/dist/lib/commands/report.mjs +28 -272
  44. package/dist/lib/commands/review.mjs +9 -223
  45. package/dist/lib/commands/run.mjs +8 -336
  46. package/dist/lib/commands/scaffold.mjs +54 -419
  47. package/dist/lib/commands/scan.mjs +5 -558
  48. package/dist/lib/commands/scout.mjs +2 -291
  49. package/dist/lib/commands/setup.mjs +5 -310
  50. package/dist/lib/commands/share.mjs +13 -196
  51. package/dist/lib/commands/snapshot.mjs +3 -383
  52. package/dist/lib/commands/stability.mjs +2 -293
  53. package/dist/lib/commands/status.mjs +4 -172
  54. package/dist/lib/commands/suggest.mjs +21 -563
  55. package/dist/lib/commands/syncAuto.mjs +1 -96
  56. package/dist/lib/commands/synthesize.mjs +10 -228
  57. package/dist/lib/commands/teamSync.mjs +2 -388
  58. package/dist/lib/commands/test.mjs +6 -363
  59. package/dist/lib/commands/theme.mjs +18 -0
  60. package/dist/lib/commands/version.mjs +2 -282
  61. package/dist/lib/commands/vibe.mjs +7 -357
  62. package/dist/lib/commands/watch.mjs +4 -203
  63. package/dist/lib/commands/why.mjs +4 -358
  64. package/dist/lib/cursorHooksInstall.mjs +1 -60
  65. package/dist/lib/draftToolingInstall.mjs +7 -68
  66. package/dist/lib/git/detect-drift.mjs +4 -208
  67. package/dist/lib/learning/adapt.mjs +6 -101
  68. package/dist/lib/learning/observe.mjs +1 -119
  69. package/dist/lib/learning/patternDetector.mjs +1 -298
  70. package/dist/lib/learning/profile.mjs +2 -279
  71. package/dist/lib/learning/skillSynthesizer.mjs +24 -145
  72. package/dist/lib/templates/index.mjs +1 -131
  73. package/dist/lib/theme/scanner.mjs +4 -0
  74. package/dist/lib/ui/errors.mjs +1 -142
  75. package/dist/lib/ui/output.mjs +6 -72
  76. package/dist/lib/ui/prompts.mjs +6 -147
  77. package/dist/lib/vsCodeCopilotHooksInstall.mjs +1 -42
  78. package/dist/templates/cursor/inferno-mcp-server.mjs +29 -0
  79. package/dist/templates/github-app/GITHUB_APP.md +67 -0
  80. package/dist/templates/github-app/app-manifest.json +20 -0
  81. package/package.json +1 -1
@@ -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};