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.
- package/LICENSE +21 -21
- package/README.md +122 -122
- package/dist/packages/agent-harness-core/src/predicate.d.ts +1 -1
- package/dist/src/agent-harness/__tests__/mock-model.spec.js +48 -1
- package/dist/src/agent-harness/mock-model.d.ts +11 -0
- package/dist/src/agent-harness/mock-model.js +21 -0
- package/dist/src/cli/cost-forensics.js +12 -12
- package/dist/src/council/__tests__/clarification-prompt.test.js +51 -0
- package/dist/src/council/__tests__/clarifier-ready-gate.test.js +32 -0
- package/dist/src/council/__tests__/decisions-lock.test.js +17 -1
- package/dist/src/council/__tests__/oauth-reachable.test.d.ts +1 -0
- package/dist/src/council/__tests__/oauth-reachable.test.js +31 -0
- package/dist/src/council/__tests__/parse-outcome-fallback.test.js +11 -0
- package/dist/src/council/clarifier.js +9 -1
- package/dist/src/council/debate.js +5 -1
- package/dist/src/council/decisions-lock.js +3 -3
- package/dist/src/council/index.js +12 -5
- package/dist/src/council/leader.d.ts +0 -17
- package/dist/src/council/leader.js +22 -15
- package/dist/src/council/planner.js +1 -1
- package/dist/src/council/prompts.js +63 -57
- package/dist/src/council/types.d.ts +7 -0
- package/dist/src/ee/__tests__/ee-onboarding.test.d.ts +1 -0
- package/dist/src/ee/__tests__/ee-onboarding.test.js +32 -0
- package/dist/src/ee/auth.d.ts +9 -0
- package/dist/src/ee/auth.js +19 -0
- package/dist/src/ee/ee-onboarding.d.ts +5 -0
- package/dist/src/ee/ee-onboarding.js +76 -0
- package/dist/src/generated/version.d.ts +1 -1
- package/dist/src/generated/version.js +1 -1
- package/dist/src/headless/output.js +6 -4
- package/dist/src/headless/output.test.js +4 -3
- package/dist/src/index.js +20 -1
- package/dist/src/mcp/__tests__/auto-setup.test.js +74 -0
- package/dist/src/mcp/__tests__/client-pool.spec.d.ts +1 -0
- package/dist/src/mcp/__tests__/client-pool.spec.js +98 -0
- package/dist/src/mcp/__tests__/parallel-build.spec.d.ts +1 -0
- package/dist/src/mcp/__tests__/parallel-build.spec.js +67 -0
- package/dist/src/mcp/__tests__/smart-filter.test.js +56 -0
- package/dist/src/mcp/auto-setup.js +56 -2
- package/dist/src/mcp/client-pool.d.ts +46 -0
- package/dist/src/mcp/client-pool.js +212 -0
- package/dist/src/mcp/oauth-callback.js +2 -2
- package/dist/src/mcp/parse-headers.test.js +14 -14
- package/dist/src/mcp/runtime.d.ts +28 -0
- package/dist/src/mcp/runtime.js +117 -51
- package/dist/src/mcp/self-verify-runner.d.ts +14 -0
- package/dist/src/mcp/self-verify-runner.js +38 -0
- package/dist/src/mcp/setup-guide-text.d.ts +9 -0
- package/dist/src/mcp/setup-guide-text.js +84 -0
- package/dist/src/mcp/smart-filter.js +49 -0
- package/dist/src/mcp/smoke.test.js +43 -43
- package/dist/src/mcp/tools-server.d.ts +7 -0
- package/dist/src/mcp/tools-server.js +19 -22
- package/dist/src/models/catalog.json +349 -349
- package/dist/src/ops/__tests__/doctor-ee-health.test.js +21 -0
- package/dist/src/ops/doctor.d.ts +3 -2
- package/dist/src/ops/doctor.js +47 -11
- package/dist/src/ops/doctor.test.js +4 -3
- package/dist/src/orchestrator/__tests__/mcp-capability-block.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/mcp-capability-block.test.js +39 -0
- package/dist/src/orchestrator/__tests__/project-stack.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/project-stack.test.js +65 -0
- package/dist/src/orchestrator/batch-turn-runner.js +7 -11
- package/dist/src/orchestrator/message-processor.js +57 -27
- package/dist/src/orchestrator/orchestrator.js +26 -0
- package/dist/src/orchestrator/prompts.d.ts +51 -0
- package/dist/src/orchestrator/prompts.js +257 -134
- package/dist/src/orchestrator/scope-ceiling.js +6 -1
- package/dist/src/orchestrator/stream-runner.js +20 -15
- package/dist/src/orchestrator/text-tool-call-detector.test.js +13 -13
- package/dist/src/pil/__tests__/clarity-gate.test.js +24 -215
- package/dist/src/pil/__tests__/config.test.js +1 -17
- package/dist/src/pil/__tests__/discovery.test.js +144 -11
- package/dist/src/pil/__tests__/layer1-intent-trace.test.js +7 -2
- package/dist/src/pil/__tests__/layer1-intent.test.js +3 -0
- package/dist/src/pil/__tests__/layer16-clarity.test.js +32 -116
- package/dist/src/pil/__tests__/layer4-gsd.test.js +37 -0
- package/dist/src/pil/__tests__/layer6-output.test.js +137 -18
- package/dist/src/pil/__tests__/llm-classify.test.js +49 -2
- package/dist/src/pil/agent-operating-contract.d.ts +1 -1
- package/dist/src/pil/agent-operating-contract.js +2 -0
- package/dist/src/pil/agent-operating-contract.test.js +7 -2
- package/dist/src/pil/cheap-model-playbook.js +35 -35
- package/dist/src/pil/cheap-model-workbooks.js +16 -13
- package/dist/src/pil/clarity-gate.d.ts +21 -19
- package/dist/src/pil/clarity-gate.js +26 -153
- package/dist/src/pil/config.d.ts +9 -1
- package/dist/src/pil/config.js +15 -4
- package/dist/src/pil/discovery.js +211 -136
- package/dist/src/pil/layer1-intent.d.ts +12 -0
- package/dist/src/pil/layer1-intent.js +283 -38
- package/dist/src/pil/layer1-intent.test.js +210 -4
- package/dist/src/pil/layer16-clarity.d.ts +25 -11
- package/dist/src/pil/layer16-clarity.js +19 -306
- package/dist/src/pil/layer4-gsd.js +18 -6
- package/dist/src/pil/layer6-output.d.ts +2 -0
- package/dist/src/pil/layer6-output.js +137 -22
- package/dist/src/pil/llm-classify.d.ts +26 -0
- package/dist/src/pil/llm-classify.js +34 -5
- package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
- package/dist/src/pil/native-capabilities-workbook.js +82 -76
- package/dist/src/pil/schema.d.ts +8 -0
- package/dist/src/pil/schema.js +12 -1
- package/dist/src/pil/task-tier-map.js +4 -0
- package/dist/src/pil/types.d.ts +11 -1
- package/dist/src/product-loop/done-gate.js +3 -3
- package/dist/src/product-loop/loop-driver.js +18 -18
- package/dist/src/product-loop/progress-snapshot.js +4 -4
- package/dist/src/providers/auth/gemini-oauth.js +6 -15
- package/dist/src/providers/auth/grok-oauth.js +6 -15
- package/dist/src/providers/auth/openai-oauth.js +6 -15
- package/dist/src/providers/mcp-vision-bridge.js +48 -48
- package/dist/src/reporter/index.js +1 -1
- package/dist/src/scaffold/bb-ecosystem-apply.js +47 -47
- package/dist/src/scaffold/bb-quality-gate.js +5 -5
- package/dist/src/scaffold/continuation-prompt.js +60 -60
- package/dist/src/scaffold/init-new.js +453 -453
- package/dist/src/self-qa/__tests__/scenario-planner.test.js +3 -3
- package/dist/src/self-qa/agentic-loop.js +24 -19
- package/dist/src/self-qa/spec-emitter.js +26 -23
- package/dist/src/storage/__tests__/migrations.test.js +2 -2
- package/dist/src/storage/interaction-log.js +5 -5
- package/dist/src/storage/migrations.js +122 -122
- package/dist/src/storage/sessions.js +42 -42
- package/dist/src/storage/transcript.js +91 -84
- package/dist/src/storage/usage.js +14 -14
- package/dist/src/storage/workspaces.js +12 -12
- package/dist/src/tools/__tests__/native-tools.test.d.ts +1 -0
- package/dist/src/tools/__tests__/native-tools.test.js +53 -0
- package/dist/src/tools/git-safety.d.ts +61 -0
- package/dist/src/tools/git-safety.js +141 -0
- package/dist/src/tools/git-safety.test.d.ts +1 -0
- package/dist/src/tools/git-safety.test.js +111 -0
- package/dist/src/tools/native-tools.d.ts +31 -0
- package/dist/src/tools/native-tools.js +273 -0
- package/dist/src/tools/registry-git-safety.test.d.ts +7 -0
- package/dist/src/tools/registry-git-safety.test.js +92 -0
- package/dist/src/tools/registry.js +39 -4
- package/dist/src/ui/__tests__/markdown-render.test.d.ts +1 -0
- package/dist/src/ui/__tests__/markdown-render.test.js +48 -0
- package/dist/src/ui/app.js +0 -0
- package/dist/src/ui/components/message-view.js +4 -1
- package/dist/src/ui/components/structured-response-view.js +7 -3
- package/dist/src/ui/components/tool-group.js +7 -1
- package/dist/src/ui/markdown-render.d.ts +41 -0
- package/dist/src/ui/markdown-render.js +223 -0
- package/dist/src/ui/markdown.d.ts +10 -0
- package/dist/src/ui/markdown.js +12 -35
- package/dist/src/ui/slash/council-inspect.js +4 -4
- package/dist/src/ui/slash/export.js +4 -4
- package/dist/src/ui/utils/text.d.ts +8 -0
- package/dist/src/ui/utils/text.js +16 -0
- package/dist/src/ui/utils/text.test.d.ts +1 -0
- package/dist/src/ui/utils/text.test.js +23 -0
- package/dist/src/usage/ledger.js +48 -15
- package/dist/src/utils/__tests__/footprint-gitignore.test.d.ts +1 -0
- package/dist/src/utils/__tests__/footprint-gitignore.test.js +50 -0
- package/dist/src/utils/clipboard-image.js +23 -23
- package/dist/src/utils/open-url.d.ts +56 -0
- package/dist/src/utils/open-url.js +58 -0
- package/dist/src/utils/open-url.test.d.ts +1 -0
- package/dist/src/utils/open-url.test.js +86 -0
- package/dist/src/utils/settings.d.ts +12 -0
- package/dist/src/utils/settings.js +48 -0
- package/dist/src/utils/side-question.js +2 -2
- package/dist/src/utils/skills.js +3 -3
- package/dist/src/verify/__tests__/coverage-parsers.test.js +30 -30
- package/dist/src/verify/environment.js +2 -1
- package/package.json +1 -1
- package/dist/src/pil/layer16-clarity.test.js +0 -31
- /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
|