muonroi-cli 1.4.1 → 1.5.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 (172) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +122 -122
  3. package/dist/packages/agent-harness-core/src/predicate.d.ts +1 -1
  4. package/dist/src/agent-harness/__tests__/mock-model.spec.js +48 -1
  5. package/dist/src/agent-harness/mock-model.d.ts +11 -0
  6. package/dist/src/agent-harness/mock-model.js +21 -0
  7. package/dist/src/cli/cost-forensics.js +12 -12
  8. package/dist/src/council/__tests__/clarification-prompt.test.js +51 -0
  9. package/dist/src/council/__tests__/clarifier-ready-gate.test.js +32 -0
  10. package/dist/src/council/__tests__/decisions-lock.test.js +17 -1
  11. package/dist/src/council/__tests__/oauth-reachable.test.d.ts +1 -0
  12. package/dist/src/council/__tests__/oauth-reachable.test.js +31 -0
  13. package/dist/src/council/__tests__/parse-outcome-fallback.test.js +11 -0
  14. package/dist/src/council/clarifier.js +9 -1
  15. package/dist/src/council/debate.js +5 -1
  16. package/dist/src/council/decisions-lock.js +3 -3
  17. package/dist/src/council/index.js +12 -5
  18. package/dist/src/council/leader.d.ts +0 -17
  19. package/dist/src/council/leader.js +22 -15
  20. package/dist/src/council/planner.js +1 -1
  21. package/dist/src/council/prompts.js +63 -57
  22. package/dist/src/council/types.d.ts +7 -0
  23. package/dist/src/ee/__tests__/ee-onboarding.test.d.ts +1 -0
  24. package/dist/src/ee/__tests__/ee-onboarding.test.js +32 -0
  25. package/dist/src/ee/auth.d.ts +9 -0
  26. package/dist/src/ee/auth.js +19 -0
  27. package/dist/src/ee/ee-onboarding.d.ts +5 -0
  28. package/dist/src/ee/ee-onboarding.js +76 -0
  29. package/dist/src/generated/version.d.ts +1 -1
  30. package/dist/src/generated/version.js +1 -1
  31. package/dist/src/headless/output.js +6 -4
  32. package/dist/src/headless/output.test.js +4 -3
  33. package/dist/src/index.js +20 -1
  34. package/dist/src/mcp/__tests__/auto-setup.test.js +74 -0
  35. package/dist/src/mcp/__tests__/client-pool.spec.d.ts +1 -0
  36. package/dist/src/mcp/__tests__/client-pool.spec.js +98 -0
  37. package/dist/src/mcp/__tests__/parallel-build.spec.d.ts +1 -0
  38. package/dist/src/mcp/__tests__/parallel-build.spec.js +67 -0
  39. package/dist/src/mcp/__tests__/smart-filter.test.js +56 -0
  40. package/dist/src/mcp/auto-setup.js +56 -2
  41. package/dist/src/mcp/client-pool.d.ts +46 -0
  42. package/dist/src/mcp/client-pool.js +212 -0
  43. package/dist/src/mcp/oauth-callback.js +2 -2
  44. package/dist/src/mcp/parse-headers.test.js +14 -14
  45. package/dist/src/mcp/runtime.d.ts +28 -0
  46. package/dist/src/mcp/runtime.js +117 -51
  47. package/dist/src/mcp/self-verify-runner.d.ts +14 -0
  48. package/dist/src/mcp/self-verify-runner.js +38 -0
  49. package/dist/src/mcp/setup-guide-text.d.ts +9 -0
  50. package/dist/src/mcp/setup-guide-text.js +84 -0
  51. package/dist/src/mcp/smart-filter.js +49 -0
  52. package/dist/src/mcp/smoke.test.js +43 -43
  53. package/dist/src/mcp/tools-server.d.ts +7 -0
  54. package/dist/src/mcp/tools-server.js +19 -22
  55. package/dist/src/models/catalog.json +349 -349
  56. package/dist/src/ops/__tests__/doctor-ee-health.test.js +21 -0
  57. package/dist/src/ops/doctor.d.ts +3 -2
  58. package/dist/src/ops/doctor.js +47 -11
  59. package/dist/src/ops/doctor.test.js +4 -3
  60. package/dist/src/orchestrator/__tests__/mcp-capability-block.test.d.ts +1 -0
  61. package/dist/src/orchestrator/__tests__/mcp-capability-block.test.js +39 -0
  62. package/dist/src/orchestrator/__tests__/project-stack.test.d.ts +1 -0
  63. package/dist/src/orchestrator/__tests__/project-stack.test.js +65 -0
  64. package/dist/src/orchestrator/batch-turn-runner.js +7 -11
  65. package/dist/src/orchestrator/message-processor.js +57 -27
  66. package/dist/src/orchestrator/orchestrator.js +26 -0
  67. package/dist/src/orchestrator/prompts.d.ts +51 -0
  68. package/dist/src/orchestrator/prompts.js +257 -134
  69. package/dist/src/orchestrator/scope-ceiling.js +6 -1
  70. package/dist/src/orchestrator/stream-runner.js +20 -15
  71. package/dist/src/orchestrator/text-tool-call-detector.test.js +13 -13
  72. package/dist/src/pil/__tests__/clarity-gate.test.js +24 -215
  73. package/dist/src/pil/__tests__/config.test.js +1 -17
  74. package/dist/src/pil/__tests__/discovery.test.js +144 -11
  75. package/dist/src/pil/__tests__/layer1-intent-trace.test.js +7 -2
  76. package/dist/src/pil/__tests__/layer1-intent.test.js +3 -0
  77. package/dist/src/pil/__tests__/layer16-clarity.test.js +32 -116
  78. package/dist/src/pil/__tests__/layer4-gsd.test.js +37 -0
  79. package/dist/src/pil/__tests__/layer6-output.test.js +137 -18
  80. package/dist/src/pil/__tests__/llm-classify.test.js +49 -2
  81. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  82. package/dist/src/pil/agent-operating-contract.js +2 -0
  83. package/dist/src/pil/agent-operating-contract.test.js +7 -2
  84. package/dist/src/pil/cheap-model-playbook.js +35 -35
  85. package/dist/src/pil/cheap-model-workbooks.js +16 -13
  86. package/dist/src/pil/clarity-gate.d.ts +21 -19
  87. package/dist/src/pil/clarity-gate.js +26 -153
  88. package/dist/src/pil/config.d.ts +9 -1
  89. package/dist/src/pil/config.js +15 -4
  90. package/dist/src/pil/discovery.js +211 -136
  91. package/dist/src/pil/layer1-intent.d.ts +12 -0
  92. package/dist/src/pil/layer1-intent.js +283 -38
  93. package/dist/src/pil/layer1-intent.test.js +210 -4
  94. package/dist/src/pil/layer16-clarity.d.ts +25 -11
  95. package/dist/src/pil/layer16-clarity.js +19 -306
  96. package/dist/src/pil/layer4-gsd.js +18 -6
  97. package/dist/src/pil/layer6-output.d.ts +2 -0
  98. package/dist/src/pil/layer6-output.js +137 -22
  99. package/dist/src/pil/llm-classify.d.ts +26 -0
  100. package/dist/src/pil/llm-classify.js +34 -5
  101. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  102. package/dist/src/pil/native-capabilities-workbook.js +82 -76
  103. package/dist/src/pil/schema.d.ts +8 -0
  104. package/dist/src/pil/schema.js +12 -1
  105. package/dist/src/pil/task-tier-map.js +4 -0
  106. package/dist/src/pil/types.d.ts +11 -1
  107. package/dist/src/product-loop/done-gate.js +3 -3
  108. package/dist/src/product-loop/loop-driver.js +18 -18
  109. package/dist/src/product-loop/progress-snapshot.js +4 -4
  110. package/dist/src/providers/auth/gemini-oauth.js +6 -15
  111. package/dist/src/providers/auth/grok-oauth.js +6 -15
  112. package/dist/src/providers/auth/openai-oauth.js +6 -15
  113. package/dist/src/providers/mcp-vision-bridge.js +48 -48
  114. package/dist/src/reporter/index.js +1 -1
  115. package/dist/src/scaffold/bb-ecosystem-apply.js +47 -47
  116. package/dist/src/scaffold/bb-quality-gate.js +5 -5
  117. package/dist/src/scaffold/continuation-prompt.js +60 -60
  118. package/dist/src/scaffold/init-new.js +453 -453
  119. package/dist/src/self-qa/__tests__/scenario-planner.test.js +3 -3
  120. package/dist/src/self-qa/agentic-loop.js +24 -19
  121. package/dist/src/self-qa/spec-emitter.js +26 -23
  122. package/dist/src/storage/__tests__/migrations.test.js +2 -2
  123. package/dist/src/storage/interaction-log.js +5 -5
  124. package/dist/src/storage/migrations.js +122 -122
  125. package/dist/src/storage/sessions.js +42 -42
  126. package/dist/src/storage/transcript.js +91 -84
  127. package/dist/src/storage/usage.js +14 -14
  128. package/dist/src/storage/workspaces.js +12 -12
  129. package/dist/src/tools/__tests__/native-tools.test.d.ts +1 -0
  130. package/dist/src/tools/__tests__/native-tools.test.js +53 -0
  131. package/dist/src/tools/git-safety.d.ts +61 -0
  132. package/dist/src/tools/git-safety.js +141 -0
  133. package/dist/src/tools/git-safety.test.d.ts +1 -0
  134. package/dist/src/tools/git-safety.test.js +111 -0
  135. package/dist/src/tools/native-tools.d.ts +31 -0
  136. package/dist/src/tools/native-tools.js +273 -0
  137. package/dist/src/tools/registry-git-safety.test.d.ts +7 -0
  138. package/dist/src/tools/registry-git-safety.test.js +92 -0
  139. package/dist/src/tools/registry.js +39 -4
  140. package/dist/src/ui/__tests__/markdown-render.test.d.ts +1 -0
  141. package/dist/src/ui/__tests__/markdown-render.test.js +48 -0
  142. package/dist/src/ui/app.js +0 -0
  143. package/dist/src/ui/components/message-view.js +4 -1
  144. package/dist/src/ui/components/structured-response-view.js +7 -3
  145. package/dist/src/ui/components/tool-group.js +7 -1
  146. package/dist/src/ui/markdown-render.d.ts +41 -0
  147. package/dist/src/ui/markdown-render.js +223 -0
  148. package/dist/src/ui/markdown.d.ts +10 -0
  149. package/dist/src/ui/markdown.js +12 -35
  150. package/dist/src/ui/slash/council-inspect.js +4 -4
  151. package/dist/src/ui/slash/export.js +4 -4
  152. package/dist/src/ui/utils/text.d.ts +8 -0
  153. package/dist/src/ui/utils/text.js +16 -0
  154. package/dist/src/ui/utils/text.test.d.ts +1 -0
  155. package/dist/src/ui/utils/text.test.js +23 -0
  156. package/dist/src/usage/ledger.js +48 -15
  157. package/dist/src/utils/__tests__/footprint-gitignore.test.d.ts +1 -0
  158. package/dist/src/utils/__tests__/footprint-gitignore.test.js +50 -0
  159. package/dist/src/utils/clipboard-image.js +23 -23
  160. package/dist/src/utils/open-url.d.ts +56 -0
  161. package/dist/src/utils/open-url.js +58 -0
  162. package/dist/src/utils/open-url.test.d.ts +1 -0
  163. package/dist/src/utils/open-url.test.js +86 -0
  164. package/dist/src/utils/settings.d.ts +12 -0
  165. package/dist/src/utils/settings.js +48 -0
  166. package/dist/src/utils/side-question.js +2 -2
  167. package/dist/src/utils/skills.js +3 -3
  168. package/dist/src/verify/__tests__/coverage-parsers.test.js +30 -30
  169. package/dist/src/verify/environment.js +2 -1
  170. package/package.json +1 -1
  171. package/dist/src/pil/layer16-clarity.test.js +0 -31
  172. /package/dist/src/{pil/layer16-clarity.test.d.ts → council/__tests__/clarification-prompt.test.d.ts} +0 -0
@@ -451,54 +451,54 @@ function reactClientPackageJson(projectName) {
451
451
  }, null, 2);
452
452
  }
453
453
  function reactViteConfig() {
454
- return `import { defineConfig } from "vite";
455
- import react from "@vitejs/plugin-react";
456
-
457
- export default defineConfig({
458
- plugins: [react()],
459
- define: {
460
- __MUONROI_HARNESS__: JSON.stringify(process.env.NODE_ENV !== "production"),
461
- },
462
- });
454
+ return `import { defineConfig } from "vite";
455
+ import react from "@vitejs/plugin-react";
456
+
457
+ export default defineConfig({
458
+ plugins: [react()],
459
+ define: {
460
+ __MUONROI_HARNESS__: JSON.stringify(process.env.NODE_ENV !== "production"),
461
+ },
462
+ });
463
463
  `;
464
464
  }
465
465
  function reactIndexHtml(projectName) {
466
- return `<!doctype html>
467
- <html lang="en">
468
- <head>
469
- <meta charset="UTF-8" />
470
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
471
- <title>${projectName}</title>
472
- </head>
473
- <body>
474
- <div id="root"></div>
475
- <script type="module" src="/src/main.tsx"></script>
476
- </body>
477
- </html>
466
+ return `<!doctype html>
467
+ <html lang="en">
468
+ <head>
469
+ <meta charset="UTF-8" />
470
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
471
+ <title>${projectName}</title>
472
+ </head>
473
+ <body>
474
+ <div id="root"></div>
475
+ <script type="module" src="/src/main.tsx"></script>
476
+ </body>
477
+ </html>
478
478
  `;
479
479
  }
480
480
  function reactMainTsx() {
481
- return `import { StrictMode } from "react";
482
- import { createRoot } from "react-dom/client";
483
- import { SemanticProvider, Semantic } from "@muonroi/agent-harness-react";
484
- import { createSemanticRegistry } from "@muonroi/agent-harness-core/registry";
485
- import { ErrorBoundary } ${"from"} "./components/ErrorBoundary";
486
- ${"import"} "./styles/app.css";
487
-
488
- const registry = createSemanticRegistry();
489
-
490
- createRoot(document.getElementById("root")!).render(
491
- <StrictMode>
492
- <SemanticProvider registry={registry}>
493
- <ErrorBoundary>
494
- <Semantic id="root" role="region" name="App root">
495
- {/* Your application components go here */}
496
- <h1>Hello from ${"{projectName}"}</h1>
497
- </Semantic>
498
- </ErrorBoundary>
499
- </SemanticProvider>
500
- </StrictMode>,
501
- );
481
+ return `import { StrictMode } from "react";
482
+ import { createRoot } from "react-dom/client";
483
+ import { SemanticProvider, Semantic } from "@muonroi/agent-harness-react";
484
+ import { createSemanticRegistry } from "@muonroi/agent-harness-core/registry";
485
+ import { ErrorBoundary } ${"from"} "./components/ErrorBoundary";
486
+ ${"import"} "./styles/app.css";
487
+
488
+ const registry = createSemanticRegistry();
489
+
490
+ createRoot(document.getElementById("root")!).render(
491
+ <StrictMode>
492
+ <SemanticProvider registry={registry}>
493
+ <ErrorBoundary>
494
+ <Semantic id="root" role="region" name="App root">
495
+ {/* Your application components go here */}
496
+ <h1>Hello from ${"{projectName}"}</h1>
497
+ </Semantic>
498
+ </ErrorBoundary>
499
+ </SemanticProvider>
500
+ </StrictMode>,
501
+ );
502
502
  `;
503
503
  }
504
504
  function reactTsConfig() {
@@ -525,211 +525,211 @@ function reactTsConfig() {
525
525
  }, null, 2);
526
526
  }
527
527
  function reactEnvExample() {
528
- return `# Copy to .env.local and edit. NEVER commit .env.local.
529
- VITE_API_BASE=http://localhost:5000
528
+ return `# Copy to .env.local and edit. NEVER commit .env.local.
529
+ VITE_API_BASE=http://localhost:5000
530
530
  `;
531
531
  }
532
532
  function reactGitignore() {
533
- return `node_modules/
534
- dist/
535
- .env
536
- .env.local
537
- .env.*.local
538
- *.log
539
- .vite/
540
- coverage/
533
+ return `node_modules/
534
+ dist/
535
+ .env
536
+ .env.local
537
+ .env.*.local
538
+ *.log
539
+ .vite/
540
+ coverage/
541
541
  `;
542
542
  }
543
543
  function reactReadme(projectName) {
544
- return `# ${projectName} — client
545
-
546
- React + Vite + TypeScript (strict) frontend wired to the agent harness via
547
- \`@muonroi/agent-harness-react\`.
548
-
549
- ## Setup
550
-
551
- \`\`\`bash
552
- bun install
553
- cp .env.example .env.local # then edit VITE_API_BASE
554
- bun run dev
555
- \`\`\`
556
-
557
- ## Layout
558
-
559
- - \`src/api/\` — typed HTTP client + DTO types (mirror server contracts)
560
- - \`src/components/\` — reusable UI (ErrorBoundary, Toast)
561
- - \`src/styles/\` — tokens + global reset
562
- - \`src/main.tsx\` — bootstrap (do NOT remove SemanticProvider)
563
-
564
- ## Conventions
565
-
566
- - Every async view has loading / empty / error states.
567
- - No inline \`style={{...}}\` — use CSS modules or tokens from \`styles/app.css\`.
568
- - API base lives in \`import.meta.env.VITE_API_BASE\`. Never hardcode URLs.
569
- - Wrap user-visible regions with \`<Semantic id role name>\` so harness specs can target them.
544
+ return `# ${projectName} — client
545
+
546
+ React + Vite + TypeScript (strict) frontend wired to the agent harness via
547
+ \`@muonroi/agent-harness-react\`.
548
+
549
+ ## Setup
550
+
551
+ \`\`\`bash
552
+ bun install
553
+ cp .env.example .env.local # then edit VITE_API_BASE
554
+ bun run dev
555
+ \`\`\`
556
+
557
+ ## Layout
558
+
559
+ - \`src/api/\` — typed HTTP client + DTO types (mirror server contracts)
560
+ - \`src/components/\` — reusable UI (ErrorBoundary, Toast)
561
+ - \`src/styles/\` — tokens + global reset
562
+ - \`src/main.tsx\` — bootstrap (do NOT remove SemanticProvider)
563
+
564
+ ## Conventions
565
+
566
+ - Every async view has loading / empty / error states.
567
+ - No inline \`style={{...}}\` — use CSS modules or tokens from \`styles/app.css\`.
568
+ - API base lives in \`import.meta.env.VITE_API_BASE\`. Never hardcode URLs.
569
+ - Wrap user-visible regions with \`<Semantic id role name>\` so harness specs can target them.
570
570
  `;
571
571
  }
572
572
  function reactApiClient() {
573
- return `/**
574
- * Typed HTTP client. Reads API base from \`VITE_API_BASE\` env var.
575
- * Never hardcode URLs in components — import this client.
576
- */
577
-
578
- const API_BASE: string = import.meta.env.VITE_API_BASE ?? "";
579
-
580
- if (!API_BASE) {
581
- // Surfaces during dev if VITE_API_BASE is missing from .env.local.
582
- console.warn("[api] VITE_API_BASE is empty — set it in .env.local");
583
- }
584
-
585
- export class ApiError extends Error {
586
- constructor(public readonly status: number, public readonly body: unknown, message: string) {
587
- super(message);
588
- this.name = "ApiError";
589
- }
590
- }
591
-
592
- interface RequestOptions {
593
- method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
594
- body?: unknown;
595
- signal?: AbortSignal;
596
- token?: string | null;
597
- }
598
-
599
- export async function apiFetch<T>(path: string, opts: RequestOptions = {}): Promise<T> {
600
- const headers: Record<string, string> = { "Content-Type": "application/json" };
601
- const token = opts.token ?? (typeof localStorage !== "undefined" ? localStorage.getItem("access_token") : null);
602
- if (token) headers.Authorization = \`Bearer \${token}\`;
603
-
604
- const res = await fetch(\`\${API_BASE}\${path}\`, {
605
- method: opts.method ?? "GET",
606
- headers,
607
- body: opts.body ? JSON.stringify(opts.body) : undefined,
608
- signal: opts.signal,
609
- });
610
-
611
- let body: unknown = null;
612
- const text = await res.text();
613
- if (text) {
614
- try {
615
- body = JSON.parse(text);
616
- } catch {
617
- body = text;
618
- }
619
- }
620
-
621
- if (!res.ok) {
622
- throw new ApiError(res.status, body, \`HTTP \${res.status} on \${opts.method ?? "GET"} \${path}\`);
623
- }
624
- return body as T;
625
- }
573
+ return `/**
574
+ * Typed HTTP client. Reads API base from \`VITE_API_BASE\` env var.
575
+ * Never hardcode URLs in components — import this client.
576
+ */
577
+
578
+ const API_BASE: string = import.meta.env.VITE_API_BASE ?? "";
579
+
580
+ if (!API_BASE) {
581
+ // Surfaces during dev if VITE_API_BASE is missing from .env.local.
582
+ console.warn("[api] VITE_API_BASE is empty — set it in .env.local");
583
+ }
584
+
585
+ export class ApiError extends Error {
586
+ constructor(public readonly status: number, public readonly body: unknown, message: string) {
587
+ super(message);
588
+ this.name = "ApiError";
589
+ }
590
+ }
591
+
592
+ interface RequestOptions {
593
+ method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
594
+ body?: unknown;
595
+ signal?: AbortSignal;
596
+ token?: string | null;
597
+ }
598
+
599
+ export async function apiFetch<T>(path: string, opts: RequestOptions = {}): Promise<T> {
600
+ const headers: Record<string, string> = { "Content-Type": "application/json" };
601
+ const token = opts.token ?? (typeof localStorage !== "undefined" ? localStorage.getItem("access_token") : null);
602
+ if (token) headers.Authorization = \`Bearer \${token}\`;
603
+
604
+ const res = await fetch(\`\${API_BASE}\${path}\`, {
605
+ method: opts.method ?? "GET",
606
+ headers,
607
+ body: opts.body ? JSON.stringify(opts.body) : undefined,
608
+ signal: opts.signal,
609
+ });
610
+
611
+ let body: unknown = null;
612
+ const text = await res.text();
613
+ if (text) {
614
+ try {
615
+ body = JSON.parse(text);
616
+ } catch {
617
+ body = text;
618
+ }
619
+ }
620
+
621
+ if (!res.ok) {
622
+ throw new ApiError(res.status, body, \`HTTP \${res.status} on \${opts.method ?? "GET"} \${path}\`);
623
+ }
624
+ return body as T;
625
+ }
626
626
  `;
627
627
  }
628
628
  function reactApiTypes() {
629
- return `/**
630
- * API request/response DTOs. Mirror server-side contracts here so the
631
- * compiler catches contract drift early. Replace the placeholder once
632
- * you generate domain types.
633
- */
634
- export interface Envelope<T> {
635
- result: T;
636
- error?: string | null;
637
- }
638
-
639
- // EXAMPLE — delete or replace with your real DTOs.
640
- // export interface TodoDto {
641
- // id: string;
642
- // title: string;
643
- // isCompleted: boolean;
644
- // createdAt: string;
645
- // }
646
- export {};
629
+ return `/**
630
+ * API request/response DTOs. Mirror server-side contracts here so the
631
+ * compiler catches contract drift early. Replace the placeholder once
632
+ * you generate domain types.
633
+ */
634
+ export interface Envelope<T> {
635
+ result: T;
636
+ error?: string | null;
637
+ }
638
+
639
+ // EXAMPLE — delete or replace with your real DTOs.
640
+ // export interface TodoDto {
641
+ // id: string;
642
+ // title: string;
643
+ // isCompleted: boolean;
644
+ // createdAt: string;
645
+ // }
646
+ export {};
647
647
  `;
648
648
  }
649
649
  function reactErrorBoundary() {
650
- return `import { Component, type ErrorInfo, type ReactNode } from "react";
651
-
652
- interface Props {
653
- children: ReactNode;
654
- fallback?: (error: Error) => ReactNode;
655
- }
656
-
657
- interface State {
658
- error: Error | null;
659
- }
660
-
661
- /**
662
- * Root-level error boundary. Catches uncaught render/effect errors and
663
- * shows a fallback instead of an unmounted blank page. Logs to console
664
- * for dev; replace the logger with your telemetry sink in prod.
665
- */
666
- export class ErrorBoundary extends Component<Props, State> {
667
- state: State = { error: null };
668
-
669
- static getDerivedStateFromError(error: Error): State {
670
- return { error };
671
- }
672
-
673
- componentDidCatch(error: Error, info: ErrorInfo): void {
674
- console.error("[ErrorBoundary]", error, info);
675
- }
676
-
677
- render(): ReactNode {
678
- if (this.state.error) {
679
- if (this.props.fallback) return this.props.fallback(this.state.error);
680
- return (
681
- <div role="alert" style={{ padding: "1rem", color: "var(--color-error, #b91c1c)" }}>
682
- <h2>Something went wrong</h2>
683
- <pre style={{ whiteSpace: "pre-wrap" }}>{this.state.error.message}</pre>
684
- </div>
685
- );
686
- }
687
- return this.props.children;
688
- }
689
- }
650
+ return `import { Component, type ErrorInfo, type ReactNode } from "react";
651
+
652
+ interface Props {
653
+ children: ReactNode;
654
+ fallback?: (error: Error) => ReactNode;
655
+ }
656
+
657
+ interface State {
658
+ error: Error | null;
659
+ }
660
+
661
+ /**
662
+ * Root-level error boundary. Catches uncaught render/effect errors and
663
+ * shows a fallback instead of an unmounted blank page. Logs to console
664
+ * for dev; replace the logger with your telemetry sink in prod.
665
+ */
666
+ export class ErrorBoundary extends Component<Props, State> {
667
+ state: State = { error: null };
668
+
669
+ static getDerivedStateFromError(error: Error): State {
670
+ return { error };
671
+ }
672
+
673
+ componentDidCatch(error: Error, info: ErrorInfo): void {
674
+ console.error("[ErrorBoundary]", error, info);
675
+ }
676
+
677
+ render(): ReactNode {
678
+ if (this.state.error) {
679
+ if (this.props.fallback) return this.props.fallback(this.state.error);
680
+ return (
681
+ <div role="alert" style={{ padding: "1rem", color: "var(--color-error, #b91c1c)" }}>
682
+ <h2>Something went wrong</h2>
683
+ <pre style={{ whiteSpace: "pre-wrap" }}>{this.state.error.message}</pre>
684
+ </div>
685
+ );
686
+ }
687
+ return this.props.children;
688
+ }
689
+ }
690
690
  `;
691
691
  }
692
692
  function reactAppCss() {
693
- return `:root {
694
- --color-bg: #ffffff;
695
- --color-fg: #111827;
696
- --color-muted: #6b7280;
697
- --color-primary: #2563eb;
698
- --color-primary-hover: #1d4ed8;
699
- --color-border: #e5e7eb;
700
- --color-error: #b91c1c;
701
- --color-success: #15803d;
702
- --space-1: 0.25rem;
703
- --space-2: 0.5rem;
704
- --space-3: 0.75rem;
705
- --space-4: 1rem;
706
- --space-6: 1.5rem;
707
- --space-8: 2rem;
708
- --radius: 0.5rem;
709
- --font-stack: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
710
- }
711
-
712
- @media (prefers-color-scheme: dark) {
713
- :root {
714
- --color-bg: #0b1220;
715
- --color-fg: #f3f4f6;
716
- --color-muted: #9ca3af;
717
- --color-border: #1f2937;
718
- }
719
- }
720
-
721
- * { box-sizing: border-box; }
722
- html, body, #root { height: 100%; }
723
- body {
724
- margin: 0;
725
- background: var(--color-bg);
726
- color: var(--color-fg);
727
- font-family: var(--font-stack);
728
- line-height: 1.5;
729
- }
730
- button { font: inherit; cursor: pointer; }
731
- input, textarea, select { font: inherit; }
732
- a { color: var(--color-primary); }
693
+ return `:root {
694
+ --color-bg: #ffffff;
695
+ --color-fg: #111827;
696
+ --color-muted: #6b7280;
697
+ --color-primary: #2563eb;
698
+ --color-primary-hover: #1d4ed8;
699
+ --color-border: #e5e7eb;
700
+ --color-error: #b91c1c;
701
+ --color-success: #15803d;
702
+ --space-1: 0.25rem;
703
+ --space-2: 0.5rem;
704
+ --space-3: 0.75rem;
705
+ --space-4: 1rem;
706
+ --space-6: 1.5rem;
707
+ --space-8: 2rem;
708
+ --radius: 0.5rem;
709
+ --font-stack: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
710
+ }
711
+
712
+ @media (prefers-color-scheme: dark) {
713
+ :root {
714
+ --color-bg: #0b1220;
715
+ --color-fg: #f3f4f6;
716
+ --color-muted: #9ca3af;
717
+ --color-border: #1f2937;
718
+ }
719
+ }
720
+
721
+ * { box-sizing: border-box; }
722
+ html, body, #root { height: 100%; }
723
+ body {
724
+ margin: 0;
725
+ background: var(--color-bg);
726
+ color: var(--color-fg);
727
+ font-family: var(--font-stack);
728
+ line-height: 1.5;
729
+ }
730
+ button { font: inherit; cursor: pointer; }
731
+ input, textarea, select { font: inherit; }
732
+ a { color: var(--color-primary); }
733
733
  `;
734
734
  }
735
735
  function angularClientPackageJson(projectName) {
@@ -756,37 +756,37 @@ function angularClientPackageJson(projectName) {
756
756
  }, null, 2);
757
757
  }
758
758
  function angularMainTs() {
759
- return `import { bootstrapApplication } from "@angular/platform-browser";
760
- import { ErrorHandler } from "@angular/core";
761
- import { provideHttpClient } from "@angular/common/http";
762
- import { AppComponent } } ${"from"} "./app/app.component";
763
- ${"import"} { AppErrorHandler } ${"from"} "./app/error-handler";
764
- ${"import"} "./styles.css";
765
-
766
- bootstrapApplication(AppComponent, {
767
- providers: [
768
- provideHttpClient(),
769
- { provide: ErrorHandler, useClass: AppErrorHandler },
770
- ],
771
- }).catch((err) => console.error(err));
759
+ return `import { bootstrapApplication } from "@angular/platform-browser";
760
+ import { ErrorHandler } from "@angular/core";
761
+ import { provideHttpClient } from "@angular/common/http";
762
+ import { AppComponent } } ${"from"} "./app/app.component";
763
+ ${"import"} { AppErrorHandler } ${"from"} "./app/error-handler";
764
+ ${"import"} "./styles.css";
765
+
766
+ bootstrapApplication(AppComponent, {
767
+ providers: [
768
+ provideHttpClient(),
769
+ { provide: ErrorHandler, useClass: AppErrorHandler },
770
+ ],
771
+ }).catch((err) => console.error(err));
772
772
  `;
773
773
  }
774
774
  function angularAppComponentTs(projectName) {
775
- return `import { Component } from "@angular/core";
776
- import { SemanticDirective } from "@muonroi/agent-harness-angular";
777
-
778
- @Component({
779
- selector: "app-root",
780
- standalone: true,
781
- imports: [SemanticDirective],
782
- template: \`
783
- <div muonroiSemantic id="root" role="region" name="App root">
784
- <!-- Your application components go here -->
785
- <h1>Hello from ${projectName}</h1>
786
- </div>
787
- \`,
788
- })
789
- export class AppComponent {}
775
+ return `import { Component } from "@angular/core";
776
+ import { SemanticDirective } from "@muonroi/agent-harness-angular";
777
+
778
+ @Component({
779
+ selector: "app-root",
780
+ standalone: true,
781
+ imports: [SemanticDirective],
782
+ template: \`
783
+ <div muonroiSemantic id="root" role="region" name="App root">
784
+ <!-- Your application components go here -->
785
+ <h1>Hello from ${projectName}</h1>
786
+ </div>
787
+ \`,
788
+ })
789
+ export class AppComponent {}
790
790
  `;
791
791
  }
792
792
  function angularTsConfig() {
@@ -807,193 +807,193 @@ function angularTsConfig() {
807
807
  }, null, 2);
808
808
  }
809
809
  function angularEnvironmentTs() {
810
- return `/**
811
- * Build-time environment config. Angular CLI swaps this file with
812
- * \`environment.prod.ts\` when building for production via fileReplacements
813
- * in angular.json. Never hardcode API URLs in components — import this.
814
- */
815
- export const environment = {
816
- production: false,
817
- apiBase: "http://localhost:5000",
818
- };
810
+ return `/**
811
+ * Build-time environment config. Angular CLI swaps this file with
812
+ * \`environment.prod.ts\` when building for production via fileReplacements
813
+ * in angular.json. Never hardcode API URLs in components — import this.
814
+ */
815
+ export const environment = {
816
+ production: false,
817
+ apiBase: "http://localhost:5000",
818
+ };
819
819
  `;
820
820
  }
821
821
  function angularEnvironmentProdTs() {
822
- return `export const environment = {
823
- production: true,
824
- // Replaced at build time; set via your deploy pipeline.
825
- apiBase: "",
826
- };
822
+ return `export const environment = {
823
+ production: true,
824
+ // Replaced at build time; set via your deploy pipeline.
825
+ apiBase: "",
826
+ };
827
827
  `;
828
828
  }
829
829
  function angularApiServiceTs() {
830
- return `import { Injectable, inject } from "@angular/core";
831
- import { HttpClient, HttpErrorResponse, HttpHeaders } from "@angular/common/http";
832
- import { Observable, catchError, throwError } from "rxjs";
833
- ${"import"} { environment } ${"from"} "../environments/environment";
834
-
835
- /**
836
- * Typed HTTP client wrapper. Reads API base from environment.apiBase.
837
- * Components must call methods on this service, never inject HttpClient
838
- * directly, so URL configuration stays centralized.
839
- */
840
- export class ApiError extends Error {
841
- constructor(public readonly status: number, public readonly body: unknown, message: string) {
842
- super(message);
843
- this.name = "ApiError";
844
- }
845
- }
846
-
847
- @Injectable({ providedIn: "root" })
848
- export class ApiService {
849
- private readonly http = inject(HttpClient);
850
-
851
- private headers(): HttpHeaders {
852
- let h = new HttpHeaders({ "Content-Type": "application/json" });
853
- const token =
854
- typeof localStorage !== "undefined" ? localStorage.getItem("access_token") : null;
855
- if (token) h = h.set("Authorization", \`Bearer \${token}\`);
856
- return h;
857
- }
858
-
859
- private wrap<T>(obs: Observable<T>): Observable<T> {
860
- return obs.pipe(
861
- catchError((err: HttpErrorResponse) =>
862
- throwError(() => new ApiError(err.status, err.error, err.message)),
863
- ),
864
- );
865
- }
866
-
867
- get<T>(path: string): Observable<T> {
868
- return this.wrap(this.http.get<T>(\`\${environment.apiBase}\${path}\`, { headers: this.headers() }));
869
- }
870
-
871
- post<T>(path: string, body: unknown): Observable<T> {
872
- return this.wrap(
873
- this.http.post<T>(\`\${environment.apiBase}\${path}\`, body, { headers: this.headers() }),
874
- );
875
- }
876
-
877
- put<T>(path: string, body: unknown): Observable<T> {
878
- return this.wrap(
879
- this.http.put<T>(\`\${environment.apiBase}\${path}\`, body, { headers: this.headers() }),
880
- );
881
- }
882
-
883
- delete<T>(path: string): Observable<T> {
884
- return this.wrap(
885
- this.http.delete<T>(\`\${environment.apiBase}\${path}\`, { headers: this.headers() }),
886
- );
887
- }
888
- }
830
+ return `import { Injectable, inject } from "@angular/core";
831
+ import { HttpClient, HttpErrorResponse, HttpHeaders } from "@angular/common/http";
832
+ import { Observable, catchError, throwError } from "rxjs";
833
+ ${"import"} { environment } ${"from"} "../environments/environment";
834
+
835
+ /**
836
+ * Typed HTTP client wrapper. Reads API base from environment.apiBase.
837
+ * Components must call methods on this service, never inject HttpClient
838
+ * directly, so URL configuration stays centralized.
839
+ */
840
+ export class ApiError extends Error {
841
+ constructor(public readonly status: number, public readonly body: unknown, message: string) {
842
+ super(message);
843
+ this.name = "ApiError";
844
+ }
845
+ }
846
+
847
+ @Injectable({ providedIn: "root" })
848
+ export class ApiService {
849
+ private readonly http = inject(HttpClient);
850
+
851
+ private headers(): HttpHeaders {
852
+ let h = new HttpHeaders({ "Content-Type": "application/json" });
853
+ const token =
854
+ typeof localStorage !== "undefined" ? localStorage.getItem("access_token") : null;
855
+ if (token) h = h.set("Authorization", \`Bearer \${token}\`);
856
+ return h;
857
+ }
858
+
859
+ private wrap<T>(obs: Observable<T>): Observable<T> {
860
+ return obs.pipe(
861
+ catchError((err: HttpErrorResponse) =>
862
+ throwError(() => new ApiError(err.status, err.error, err.message)),
863
+ ),
864
+ );
865
+ }
866
+
867
+ get<T>(path: string): Observable<T> {
868
+ return this.wrap(this.http.get<T>(\`\${environment.apiBase}\${path}\`, { headers: this.headers() }));
869
+ }
870
+
871
+ post<T>(path: string, body: unknown): Observable<T> {
872
+ return this.wrap(
873
+ this.http.post<T>(\`\${environment.apiBase}\${path}\`, body, { headers: this.headers() }),
874
+ );
875
+ }
876
+
877
+ put<T>(path: string, body: unknown): Observable<T> {
878
+ return this.wrap(
879
+ this.http.put<T>(\`\${environment.apiBase}\${path}\`, body, { headers: this.headers() }),
880
+ );
881
+ }
882
+
883
+ delete<T>(path: string): Observable<T> {
884
+ return this.wrap(
885
+ this.http.delete<T>(\`\${environment.apiBase}\${path}\`, { headers: this.headers() }),
886
+ );
887
+ }
888
+ }
889
889
  `;
890
890
  }
891
891
  function angularErrorHandlerTs() {
892
- return `import { ErrorHandler, Injectable } from "@angular/core";
893
-
894
- /**
895
- * Root error handler. Replace the console sink with your telemetry
896
- * provider in prod. Angular wires this via providers: [{ provide:
897
- * ErrorHandler, useClass: AppErrorHandler }].
898
- */
899
- @Injectable({ providedIn: "root" })
900
- export class AppErrorHandler implements ErrorHandler {
901
- handleError(error: unknown): void {
902
- console.error("[AppErrorHandler]", error);
903
- }
904
- }
892
+ return `import { ErrorHandler, Injectable } from "@angular/core";
893
+
894
+ /**
895
+ * Root error handler. Replace the console sink with your telemetry
896
+ * provider in prod. Angular wires this via providers: [{ provide:
897
+ * ErrorHandler, useClass: AppErrorHandler }].
898
+ */
899
+ @Injectable({ providedIn: "root" })
900
+ export class AppErrorHandler implements ErrorHandler {
901
+ handleError(error: unknown): void {
902
+ console.error("[AppErrorHandler]", error);
903
+ }
904
+ }
905
905
  `;
906
906
  }
907
907
  function angularStylesCss() {
908
- return `:root {
909
- --color-bg: #ffffff;
910
- --color-fg: #111827;
911
- --color-muted: #6b7280;
912
- --color-primary: #2563eb;
913
- --color-primary-hover: #1d4ed8;
914
- --color-border: #e5e7eb;
915
- --color-error: #b91c1c;
916
- --color-success: #15803d;
917
- --space-1: 0.25rem;
918
- --space-2: 0.5rem;
919
- --space-3: 0.75rem;
920
- --space-4: 1rem;
921
- --space-6: 1.5rem;
922
- --space-8: 2rem;
923
- --radius: 0.5rem;
924
- --font-stack: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
925
- }
926
-
927
- @media (prefers-color-scheme: dark) {
928
- :root {
929
- --color-bg: #0b1220;
930
- --color-fg: #f3f4f6;
931
- --color-muted: #9ca3af;
932
- --color-border: #1f2937;
933
- }
934
- }
935
-
936
- * { box-sizing: border-box; }
937
- html, body { height: 100%; margin: 0; }
938
- body {
939
- background: var(--color-bg);
940
- color: var(--color-fg);
941
- font-family: var(--font-stack);
942
- line-height: 1.5;
943
- }
944
- button { font: inherit; cursor: pointer; }
945
- input, textarea, select { font: inherit; }
946
- a { color: var(--color-primary); }
908
+ return `:root {
909
+ --color-bg: #ffffff;
910
+ --color-fg: #111827;
911
+ --color-muted: #6b7280;
912
+ --color-primary: #2563eb;
913
+ --color-primary-hover: #1d4ed8;
914
+ --color-border: #e5e7eb;
915
+ --color-error: #b91c1c;
916
+ --color-success: #15803d;
917
+ --space-1: 0.25rem;
918
+ --space-2: 0.5rem;
919
+ --space-3: 0.75rem;
920
+ --space-4: 1rem;
921
+ --space-6: 1.5rem;
922
+ --space-8: 2rem;
923
+ --radius: 0.5rem;
924
+ --font-stack: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
925
+ }
926
+
927
+ @media (prefers-color-scheme: dark) {
928
+ :root {
929
+ --color-bg: #0b1220;
930
+ --color-fg: #f3f4f6;
931
+ --color-muted: #9ca3af;
932
+ --color-border: #1f2937;
933
+ }
934
+ }
935
+
936
+ * { box-sizing: border-box; }
937
+ html, body { height: 100%; margin: 0; }
938
+ body {
939
+ background: var(--color-bg);
940
+ color: var(--color-fg);
941
+ font-family: var(--font-stack);
942
+ line-height: 1.5;
943
+ }
944
+ button { font: inherit; cursor: pointer; }
945
+ input, textarea, select { font: inherit; }
946
+ a { color: var(--color-primary); }
947
947
  `;
948
948
  }
949
949
  function angularGitignore() {
950
- return `node_modules/
951
- dist/
952
- .angular/
953
- .env
954
- .env.local
955
- *.log
956
- coverage/
950
+ return `node_modules/
951
+ dist/
952
+ .angular/
953
+ .env
954
+ .env.local
955
+ *.log
956
+ coverage/
957
957
  `;
958
958
  }
959
959
  function angularReadme(projectName) {
960
- return `# ${projectName} — client
961
-
962
- Angular (standalone components) + agent harness via \`@muonroi/agent-harness-angular\`.
963
-
964
- ## Setup
965
-
966
- \`\`\`bash
967
- bun install
968
- bun run dev
969
- \`\`\`
970
-
971
- Edit \`src/environments/environment.ts\` to point \`apiBase\` at your backend.
972
-
973
- ## Layout
974
-
975
- - \`src/api/\` — \`ApiService\` (typed HTTP) + DTO types
976
- - \`src/environments/\` — env config (dev + prod). \`apiBase\` is the only URL knob.
977
- - \`src/app/\` — components. Do NOT remove \`muonroiSemantic\` attributes.
978
-
979
- ## Conventions
980
-
981
- - Every async view has loading / empty / error states.
982
- - API URLs come from \`environment.apiBase\`. Never hardcode.
983
- - Wrap user-visible regions with \`[muonroiSemantic]\` so harness specs can target them.
984
- - AppErrorHandler is wired via providers — extend it instead of console.error.
960
+ return `# ${projectName} — client
961
+
962
+ Angular (standalone components) + agent harness via \`@muonroi/agent-harness-angular\`.
963
+
964
+ ## Setup
965
+
966
+ \`\`\`bash
967
+ bun install
968
+ bun run dev
969
+ \`\`\`
970
+
971
+ Edit \`src/environments/environment.ts\` to point \`apiBase\` at your backend.
972
+
973
+ ## Layout
974
+
975
+ - \`src/api/\` — \`ApiService\` (typed HTTP) + DTO types
976
+ - \`src/environments/\` — env config (dev + prod). \`apiBase\` is the only URL knob.
977
+ - \`src/app/\` — components. Do NOT remove \`muonroiSemantic\` attributes.
978
+
979
+ ## Conventions
980
+
981
+ - Every async view has loading / empty / error states.
982
+ - API URLs come from \`environment.apiBase\`. Never hardcode.
983
+ - Wrap user-visible regions with \`[muonroiSemantic]\` so harness specs can target them.
984
+ - AppErrorHandler is wired via providers — extend it instead of console.error.
985
985
  `;
986
986
  }
987
987
  function angularApiTypes() {
988
- return `/**
989
- * API request/response DTOs. Mirror server-side contracts here so the
990
- * compiler catches contract drift early.
991
- */
992
- export interface Envelope<T> {
993
- result: T;
994
- error?: string | null;
995
- }
996
- export {};
988
+ return `/**
989
+ * API request/response DTOs. Mirror server-side contracts here so the
990
+ * compiler catches contract drift early.
991
+ */
992
+ export interface Envelope<T> {
993
+ result: T;
994
+ error?: string | null;
995
+ }
996
+ export {};
997
997
  `;
998
998
  }
999
999
  // ---------------------------------------------------------------------------
@@ -1374,41 +1374,41 @@ async function injectPackagesProps(propsPath, eePackages, commercial, fsOps) {
1374
1374
  await fsOps.writeFile(propsPath, updatedContent);
1375
1375
  }
1376
1376
  function buildMinimalPackagesProps() {
1377
- return `<Project>
1378
- <PropertyGroup>
1379
- <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
1380
- </PropertyGroup>
1381
- <ItemGroup>
1382
- </ItemGroup>
1377
+ return `<Project>
1378
+ <PropertyGroup>
1379
+ <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
1380
+ </PropertyGroup>
1381
+ <ItemGroup>
1382
+ </ItemGroup>
1383
1383
  </Project>`;
1384
1384
  }
1385
1385
  function buildEEIntentMd(opts) {
1386
1386
  const date = new Date().toISOString().slice(0, 10);
1387
1387
  const packageList = opts.eePackages.length > 0 ? opts.eePackages.map((p) => `- ${p}`).join("\n") : "_(none)_";
1388
- return `# EE-INTENT.md
1389
-
1390
- Generated by muonroi-cli on ${date}.
1391
-
1392
- ## Project
1393
- **Name:** ${opts.projectName}
1394
-
1395
- ## Template
1396
- **Package:** ${opts.template.nugetId}
1397
- **Short name:** ${opts.template.shortName}
1398
- **Version:** ${opts.template.version}
1399
-
1400
- ## EE-Recommended Packages
1401
- ${packageList}
1402
-
1403
- ## Coverage
1404
- **Status:** ${opts.coverage}
1405
- ${opts.coverage === "partial" ? "\n> Some recommended packages have weak EE coverage (< 0.70). Code-gen applied generic wiring only for low-coverage packages. Run `bun run ee:ingest-bb` to improve coverage.\n" : ""}
1406
-
1407
- ## Resume
1408
- To re-apply or fix scaffold issues interactively:
1409
- \`\`\`
1410
- /ideal --resume .
1411
- \`\`\`
1388
+ return `# EE-INTENT.md
1389
+
1390
+ Generated by muonroi-cli on ${date}.
1391
+
1392
+ ## Project
1393
+ **Name:** ${opts.projectName}
1394
+
1395
+ ## Template
1396
+ **Package:** ${opts.template.nugetId}
1397
+ **Short name:** ${opts.template.shortName}
1398
+ **Version:** ${opts.template.version}
1399
+
1400
+ ## EE-Recommended Packages
1401
+ ${packageList}
1402
+
1403
+ ## Coverage
1404
+ **Status:** ${opts.coverage}
1405
+ ${opts.coverage === "partial" ? "\n> Some recommended packages have weak EE coverage (< 0.70). Code-gen applied generic wiring only for low-coverage packages. Run `bun run ee:ingest-bb` to improve coverage.\n" : ""}
1406
+
1407
+ ## Resume
1408
+ To re-apply or fix scaffold issues interactively:
1409
+ \`\`\`
1410
+ /ideal --resume .
1411
+ \`\`\`
1412
1412
  `;
1413
1413
  }
1414
1414
  //# sourceMappingURL=init-new.js.map