agent-composer 0.1.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/README.md +148 -0
- package/composer.config.schema.json +79 -0
- package/dist/cli/init.d.ts +20 -0
- package/dist/cli/init.js +122 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/config/env.d.ts +13 -0
- package/dist/config/env.js +65 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/loader.d.ts +3 -0
- package/dist/config/loader.js +34 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +93 -0
- package/dist/config/schema.js +44 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/evolve/budget.d.ts +23 -0
- package/dist/evolve/budget.js +55 -0
- package/dist/evolve/budget.js.map +1 -0
- package/dist/evolve/lengthPenalty.d.ts +3 -0
- package/dist/evolve/lengthPenalty.js +30 -0
- package/dist/evolve/lengthPenalty.js.map +1 -0
- package/dist/evolve/operators.d.ts +24 -0
- package/dist/evolve/operators.js +110 -0
- package/dist/evolve/operators.js.map +1 -0
- package/dist/evolve/pareto.d.ts +24 -0
- package/dist/evolve/pareto.js +153 -0
- package/dist/evolve/pareto.js.map +1 -0
- package/dist/evolve/plateau.d.ts +18 -0
- package/dist/evolve/plateau.js +45 -0
- package/dist/evolve/plateau.js.map +1 -0
- package/dist/evolve/postflight.d.ts +12 -0
- package/dist/evolve/postflight.js +61 -0
- package/dist/evolve/postflight.js.map +1 -0
- package/dist/evolve/preflight.d.ts +13 -0
- package/dist/evolve/preflight.js +39 -0
- package/dist/evolve/preflight.js.map +1 -0
- package/dist/evolve/reflection.d.ts +12 -0
- package/dist/evolve/reflection.js +41 -0
- package/dist/evolve/reflection.js.map +1 -0
- package/dist/evolve/runner.d.ts +62 -0
- package/dist/evolve/runner.js +202 -0
- package/dist/evolve/runner.js.map +1 -0
- package/dist/evolve/s2-deny.d.ts +26 -0
- package/dist/evolve/s2-deny.js +75 -0
- package/dist/evolve/s2-deny.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/AnthropicCompatibleProvider.d.ts +48 -0
- package/dist/providers/AnthropicCompatibleProvider.js +50 -0
- package/dist/providers/AnthropicCompatibleProvider.js.map +1 -0
- package/dist/providers/CLIProvider.d.ts +30 -0
- package/dist/providers/CLIProvider.js +106 -0
- package/dist/providers/CLIProvider.js.map +1 -0
- package/dist/providers/IProvider.d.ts +17 -0
- package/dist/providers/IProvider.js +4 -0
- package/dist/providers/IProvider.js.map +1 -0
- package/dist/providers/MockProvider.d.ts +28 -0
- package/dist/providers/MockProvider.js +66 -0
- package/dist/providers/MockProvider.js.map +1 -0
- package/dist/registry.d.ts +21 -0
- package/dist/registry.js +79 -0
- package/dist/registry.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.js +85 -0
- package/dist/server.js.map +1 -0
- package/dist/util/slug.d.ts +1 -0
- package/dist/util/slug.js +7 -0
- package/dist/util/slug.js.map +1 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Composer — multi-agent orchestration for Claude Code
|
|
2
|
+
|
|
3
|
+
[](#install) [](#contributing) [](#license)
|
|
4
|
+
|
|
5
|
+
> **Claude orchestrates. GLM and `agy` execute.** Composer is an MCP server + Claude Code plugin that lets the most-capable model hold the plan while cheaper models do the typing — saving Claude Max5 tokens and keeping every dispatched task reviewable.
|
|
6
|
+
|
|
7
|
+
## What it is
|
|
8
|
+
|
|
9
|
+
Two coordinated artefacts:
|
|
10
|
+
|
|
11
|
+
| Artefact | Purpose |
|
|
12
|
+
|---|---|
|
|
13
|
+
| **`agent-composer`** (this npm package) | MCP server exposing `composer_research`, `composer_code`, `composer_review` tools. Wraps GLM (via Anthropic-compatible endpoint) and the `agy` CLI (Gemini). |
|
|
14
|
+
| **`composer-mastermind`** (Claude Code plugin) | Orchestrator skill + three haiku-wrapped subagents (`coder`, `researcher`, `reviewer`) + `boundary_guard` PreToolUse hook + `/evolve` slash command. |
|
|
15
|
+
|
|
16
|
+
Combined, they turn the main Claude session into a coordinator that never writes code, runs bash, or edits files directly. Work is dispatched through the three MCP tools; the boundary hook fails closed if a denied tool is requested.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# 1. Install the MCP server
|
|
22
|
+
npm install -g agent-composer
|
|
23
|
+
|
|
24
|
+
# 2. Bootstrap a project (creates composer.config.json + .env.json template +
|
|
25
|
+
# .gitignore + .claude/settings.json with mcpServers.composer entry)
|
|
26
|
+
cd your-project
|
|
27
|
+
agent-composer init
|
|
28
|
+
|
|
29
|
+
# 3. Fill credentials
|
|
30
|
+
$EDITOR .env.json # ANTHROPIC_BASE_URL + ANTHROPIC_AUTH_TOKEN
|
|
31
|
+
|
|
32
|
+
# 4. Install the plugin (manual until Claude Code plugin marketplace lands)
|
|
33
|
+
mkdir -p ~/.claude/plugins
|
|
34
|
+
git clone <this-repo> /tmp/composer
|
|
35
|
+
cp -R /tmp/composer/plugin/composer-mastermind ~/.claude/plugins/
|
|
36
|
+
|
|
37
|
+
# 5. Launch
|
|
38
|
+
claude
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Verify the orchestrator skill loaded:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
/composer-mastermind
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Smoke-test the self-evolution loop:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
/evolve --eval-mode synthetic
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Configuration
|
|
54
|
+
|
|
55
|
+
Two files at the consumer-project root, both gitignored or partially gitignored:
|
|
56
|
+
|
|
57
|
+
**`composer.config.json`** (committed) — provider routing + spend caps:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"roles": {
|
|
62
|
+
"researcher": { "provider": "cli", "cli": ["agy", "--dangerously-skip-permissions", "-p"] },
|
|
63
|
+
"coder": { "provider": "anthropic", "baseUrl": "https://api.z.ai/api/anthropic", "apiKeyEnv": "ANTHROPIC_AUTH_TOKEN" },
|
|
64
|
+
"reviewer": { "provider": "cli", "cli": ["agy", "--dangerously-skip-permissions", "-p"] }
|
|
65
|
+
},
|
|
66
|
+
"spendAuthorization": {
|
|
67
|
+
"mode": "interactive",
|
|
68
|
+
"maxUsdPerCall": 0.50,
|
|
69
|
+
"maxUsdPerSession": 5.00
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**`.env.json`** (NEVER commit) — credentials only:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"ANTHROPIC_BASE_URL": "https://api.z.ai/api/anthropic",
|
|
79
|
+
"ANTHROPIC_AUTH_TOKEN": "<your-glm-or-anthropic-compatible-token>"
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The MCP server reads `.env.json` via `fs.readFileSync` — it is **never** exposed to the orchestrator session.
|
|
84
|
+
|
|
85
|
+
## How dispatch works
|
|
86
|
+
|
|
87
|
+
Inside a Claude Code session, dispatch flow:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
User asks for code work
|
|
91
|
+
↓
|
|
92
|
+
Composer-mastermind SKILL.md picks a subagent
|
|
93
|
+
↓
|
|
94
|
+
Task → coder.md / researcher.md / reviewer.md
|
|
95
|
+
↓
|
|
96
|
+
Subagent calls mcp__composer__composer_code (etc.)
|
|
97
|
+
↓
|
|
98
|
+
MCP server routes to GLM (anthropic) or agy CLI (cli) per composer.config.json
|
|
99
|
+
↓
|
|
100
|
+
Subagent returns summary; orchestrator integrates
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Five resilience layers ensure unattended `/evolve` runs cannot damage the host repo:
|
|
104
|
+
|
|
105
|
+
1. **Sandbox isolation** — each per-task eval runs in a throwaway `git worktree` at `/tmp/composer-eval-<pid>-<taskId>`
|
|
106
|
+
2. **Per-task fault isolation** — one task's spawn failure records `score: 0` and continues
|
|
107
|
+
3. **Stat-gate precondition guards** — Wilcoxon paired-test skips when arrays are asymmetric
|
|
108
|
+
4. **Spawn diagnostics** — stderr/stdout tail appended to error messages
|
|
109
|
+
5. **Per-task wall-time bound** — `execFile` `timeout: 180_000` with SIGTERM; absorbed by layer 2
|
|
110
|
+
|
|
111
|
+
## Security model
|
|
112
|
+
|
|
113
|
+
- **`agent-composer` publish surface**: `dist/`, `composer.config.schema.json`, `README.md`, `package.json`. No tests, no source, no `.env*` (gitignored). 34 KB tarball.
|
|
114
|
+
- **Spend caps**: per-call (`maxUsdPerCall`, default $0.50) and per-session (`maxUsdPerSession`, default $5.00) enforced in the runner before any external API call. Configurable per project.
|
|
115
|
+
- **Self-evolution scope** (see ADR 0003): five layers gate any SKILL.md mutation — diff-path regex, text deny-list, stat gate, human-promote-only, audit trail. Auto-promote is permanently off the table.
|
|
116
|
+
- **Boundary hook**: PreToolUse fail-closed denial of `Edit`/`Write`/`Bash`/`NotebookEdit` in the orchestrator session. The C0.5 subagent tools allowlist is append-only.
|
|
117
|
+
|
|
118
|
+
## Contributing
|
|
119
|
+
|
|
120
|
+
Clone, install, run tests:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
git clone <this-repo>
|
|
124
|
+
cd composer
|
|
125
|
+
npm install
|
|
126
|
+
npx tsc --noEmit # type check
|
|
127
|
+
./node_modules/.bin/vitest run # 319 tests
|
|
128
|
+
./node_modules/.bin/ajv validate \ # schema lint
|
|
129
|
+
--strict=false -c ajv-formats \
|
|
130
|
+
-s composer.config.schema.json \
|
|
131
|
+
-d composer.config.json
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Per-task layer reference docs (in the source tree):
|
|
135
|
+
|
|
136
|
+
- `docs/STATUS.md` — current state + dogfood audit log + every /evolve run
|
|
137
|
+
- `docs/multi_agent_orchestration_plan.md` — architecture
|
|
138
|
+
- `docs/tdd_plan.md` — build sequence + quality rubric
|
|
139
|
+
- `docs/self_evolving_composer.md` — autonomous skill evolution (T1/T2/T3)
|
|
140
|
+
- `docs/adr/0001-contracts.md` — frozen C0.1–C0.5 contracts (append-only)
|
|
141
|
+
- `docs/adr/0002-meta-mcp.md` — Wave 4 packaging contract (M0.1–M0.5)
|
|
142
|
+
- `docs/adr/0003-self-evolution.md` — self-evolution mutation scope (S1–S5)
|
|
143
|
+
|
|
144
|
+
The `/evolve` loop mutates only the project-local `.claude/skills/composer-mastermind/SKILL.md` — the published plugin install is read-only. Release sync from dev to plugin happens via `scripts/release-sync.mjs --bump <semver>`.
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://composer.dev/schemas/composer.config.schema.json",
|
|
4
|
+
"title": "Composer Configuration",
|
|
5
|
+
"description": "Dependency Inversion at runtime: maps subagent role → provider impl. C0.2 — frozen Wave 0, append-only during Wave 1.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["roles"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"roles": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"required": ["researcher", "coder", "reviewer"],
|
|
13
|
+
"additionalProperties": false,
|
|
14
|
+
"properties": {
|
|
15
|
+
"researcher": { "$ref": "#/$defs/roleConfig" },
|
|
16
|
+
"coder": { "$ref": "#/$defs/roleConfig" },
|
|
17
|
+
"reviewer": { "$ref": "#/$defs/roleConfig" }
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"spendAuthorization": { "$ref": "#/$defs/spendAuthorization" }
|
|
21
|
+
},
|
|
22
|
+
"$defs": {
|
|
23
|
+
"spendAuthorization": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"required": ["mode"],
|
|
26
|
+
"additionalProperties": false,
|
|
27
|
+
"description": "Orchestrator behaviour when a real-money provider call is about to be made. Added 2026-05-24 after the first dogfood audit revealed the soft 'always ask' rule was inconsistent across sessions. Optional — omit for the default 'interactive' behaviour.",
|
|
28
|
+
"properties": {
|
|
29
|
+
"mode": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"enum": ["interactive", "auto", "deny"],
|
|
32
|
+
"description": "interactive: ask the user 'go' before any priced call (legacy default). auto: proceed without asking, respect the maxUsd caps. deny: refuse all real-money calls; force tape replay / mock providers."
|
|
33
|
+
},
|
|
34
|
+
"maxUsdPerSession": {
|
|
35
|
+
"type": "number",
|
|
36
|
+
"minimum": 0,
|
|
37
|
+
"description": "Hard cap on accumulated USD spend across the whole orchestrator session. 0 = no spend; omitted = no cap."
|
|
38
|
+
},
|
|
39
|
+
"maxUsdPerCall": {
|
|
40
|
+
"type": "number",
|
|
41
|
+
"minimum": 0,
|
|
42
|
+
"description": "Hard cap on a single provider call's USD estimate. 0 = block everything; omitted = no cap."
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"providerId": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"enum": ["anthropic", "openai_compatible", "cli", "mock"],
|
|
49
|
+
"description": "Discriminator picked up by ProviderFactory. Must match IProvider.id."
|
|
50
|
+
},
|
|
51
|
+
"roleConfig": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"required": ["provider"],
|
|
54
|
+
"additionalProperties": false,
|
|
55
|
+
"properties": {
|
|
56
|
+
"provider": { "$ref": "#/$defs/providerId" },
|
|
57
|
+
"apiKeyEnv": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"description": "Name of the env var holding the API key. Resolved by ProviderFactory at startup; the key value itself is NEVER stored in this file."
|
|
60
|
+
},
|
|
61
|
+
"baseUrl": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"format": "uri",
|
|
64
|
+
"description": "HTTP base URL for SDK-based providers (e.g. GLM Anthropic-compat endpoint)."
|
|
65
|
+
},
|
|
66
|
+
"model": {
|
|
67
|
+
"type": "string",
|
|
68
|
+
"description": "Provider-specific model identifier."
|
|
69
|
+
},
|
|
70
|
+
"cli": {
|
|
71
|
+
"type": "array",
|
|
72
|
+
"items": { "type": "string" },
|
|
73
|
+
"minItems": 1,
|
|
74
|
+
"description": "argv array for CLIProvider, e.g. [\"agy\",\"--dangerously-skip-permissions\",\"-p\"]. Passed to child_process.execFile — NEVER shell-interpolated."
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type InitStepStatus = "created" | "updated" | "skipped";
|
|
2
|
+
export interface InitStep {
|
|
3
|
+
name: string;
|
|
4
|
+
status: InitStepStatus;
|
|
5
|
+
path?: string;
|
|
6
|
+
reason?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface InitOptions {
|
|
9
|
+
cwd: string;
|
|
10
|
+
/** When false, do not print to stdout. Defaults true. */
|
|
11
|
+
verbose?: boolean;
|
|
12
|
+
/** Override the default base URL written into .env.json stub. */
|
|
13
|
+
defaultBaseUrl?: string;
|
|
14
|
+
/** Override the default auth token placeholder. */
|
|
15
|
+
defaultAuthToken?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface InitResult {
|
|
18
|
+
steps: InitStep[];
|
|
19
|
+
}
|
|
20
|
+
export declare function runInit(opts: InitOptions): InitResult;
|
package/dist/cli/init.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// Wave 4 M0.3 — composer init bootstrap CLI.
|
|
2
|
+
//
|
|
3
|
+
// Scaffolds a consumer project so it can launch the composer MCP server:
|
|
4
|
+
// - .claude/ directory
|
|
5
|
+
// - composer.config.json (default roles + spendAuthorization caps)
|
|
6
|
+
// - .env.json placeholder (gitignored)
|
|
7
|
+
// - .gitignore entry for .env.json
|
|
8
|
+
// - .claude/settings.json mcpServers["composer"] entry
|
|
9
|
+
//
|
|
10
|
+
// Idempotent: each step checks existing state and skips if already correct.
|
|
11
|
+
// Never overwrites a present, non-default file (no --force flag in this slice).
|
|
12
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { join, resolve } from "node:path";
|
|
14
|
+
const DEFAULT_COMPOSER_CONFIG = {
|
|
15
|
+
roles: {
|
|
16
|
+
researcher: { provider: "cli", cli: ["agy", "--dangerously-skip-permissions", "-p"] },
|
|
17
|
+
coder: { provider: "anthropic", baseUrl: "https://api.z.ai/api/anthropic", apiKeyEnv: "ANTHROPIC_AUTH_TOKEN" },
|
|
18
|
+
reviewer: { provider: "cli", cli: ["agy", "--dangerously-skip-permissions", "-p"] },
|
|
19
|
+
},
|
|
20
|
+
spendAuthorization: {
|
|
21
|
+
mode: "interactive",
|
|
22
|
+
maxUsdPerCall: 0.5,
|
|
23
|
+
maxUsdPerSession: 5.0,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
const DEFAULT_ENV_TEMPLATE = (baseUrl, token) => ({
|
|
27
|
+
ANTHROPIC_BASE_URL: baseUrl,
|
|
28
|
+
ANTHROPIC_AUTH_TOKEN: token,
|
|
29
|
+
});
|
|
30
|
+
const DEFAULT_BASE_URL = "https://api.z.ai/api/anthropic";
|
|
31
|
+
const DEFAULT_AUTH_TOKEN_PLACEHOLDER = "<replace-with-your-glm-or-anthropic-compatible-token>";
|
|
32
|
+
const DEFAULT_MCP_SETTINGS = {
|
|
33
|
+
mcpServers: {
|
|
34
|
+
composer: {
|
|
35
|
+
command: "npx",
|
|
36
|
+
args: ["-y", "agent-composer"],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
export function runInit(opts) {
|
|
41
|
+
const cwd = resolve(opts.cwd);
|
|
42
|
+
const steps = [];
|
|
43
|
+
const log = (...args) => {
|
|
44
|
+
if (opts.verbose !== false)
|
|
45
|
+
process.stdout.write(args.map(String).join(" ") + "\n");
|
|
46
|
+
};
|
|
47
|
+
steps.push(ensureClaudeDir(cwd));
|
|
48
|
+
steps.push(writeComposerConfig(cwd));
|
|
49
|
+
steps.push(writeEnvJsonStub(cwd, opts.defaultBaseUrl ?? DEFAULT_BASE_URL, opts.defaultAuthToken ?? DEFAULT_AUTH_TOKEN_PLACEHOLDER));
|
|
50
|
+
steps.push(ensureEnvGitignored(cwd));
|
|
51
|
+
steps.push(wireMcpServer(cwd));
|
|
52
|
+
for (const s of steps) {
|
|
53
|
+
const tag = s.status === "created" ? "+" : s.status === "updated" ? "~" : "=";
|
|
54
|
+
log(` ${tag} ${s.name}${s.path ? ` (${s.path})` : ""}${s.reason ? ` — ${s.reason}` : ""}`);
|
|
55
|
+
}
|
|
56
|
+
log("");
|
|
57
|
+
log("composer init: done.");
|
|
58
|
+
log("");
|
|
59
|
+
log("Next steps:");
|
|
60
|
+
log(" 1. Fill .env.json with real ANTHROPIC_AUTH_TOKEN + ANTHROPIC_BASE_URL.");
|
|
61
|
+
log(" 2. Launch Claude Code: claude");
|
|
62
|
+
log(" 3. Verify the orchestrator skill loads: /composer-mastermind");
|
|
63
|
+
log(" 4. Smoke-test the autoresearch loop: /evolve --eval-mode synthetic");
|
|
64
|
+
return { steps };
|
|
65
|
+
}
|
|
66
|
+
function ensureClaudeDir(cwd) {
|
|
67
|
+
const path = join(cwd, ".claude");
|
|
68
|
+
if (existsSync(path))
|
|
69
|
+
return { name: ".claude/ directory", status: "skipped", path, reason: "already exists" };
|
|
70
|
+
mkdirSync(path, { recursive: true });
|
|
71
|
+
return { name: ".claude/ directory", status: "created", path };
|
|
72
|
+
}
|
|
73
|
+
function writeComposerConfig(cwd) {
|
|
74
|
+
const path = join(cwd, "composer.config.json");
|
|
75
|
+
if (existsSync(path)) {
|
|
76
|
+
return { name: "composer.config.json", status: "skipped", path, reason: "already exists; not overwritten" };
|
|
77
|
+
}
|
|
78
|
+
writeFileSync(path, JSON.stringify(DEFAULT_COMPOSER_CONFIG, null, 2) + "\n", "utf8");
|
|
79
|
+
return { name: "composer.config.json", status: "created", path };
|
|
80
|
+
}
|
|
81
|
+
function writeEnvJsonStub(cwd, baseUrl, token) {
|
|
82
|
+
const path = join(cwd, ".env.json");
|
|
83
|
+
if (existsSync(path)) {
|
|
84
|
+
return { name: ".env.json", status: "skipped", path, reason: "already exists; not overwritten" };
|
|
85
|
+
}
|
|
86
|
+
writeFileSync(path, JSON.stringify(DEFAULT_ENV_TEMPLATE(baseUrl, token), null, 2) + "\n", "utf8");
|
|
87
|
+
return { name: ".env.json", status: "created", path, reason: "placeholder — fill before launching claude" };
|
|
88
|
+
}
|
|
89
|
+
function ensureEnvGitignored(cwd) {
|
|
90
|
+
const path = join(cwd, ".gitignore");
|
|
91
|
+
const entry = ".env.json";
|
|
92
|
+
if (!existsSync(path)) {
|
|
93
|
+
writeFileSync(path, `${entry}\n`, "utf8");
|
|
94
|
+
return { name: ".gitignore", status: "created", path, reason: `added ${entry}` };
|
|
95
|
+
}
|
|
96
|
+
const current = readFileSync(path, "utf8");
|
|
97
|
+
const hasEntry = current.split(/\r?\n/).some((line) => line.trim() === entry);
|
|
98
|
+
if (hasEntry)
|
|
99
|
+
return { name: ".gitignore", status: "skipped", path, reason: `${entry} already listed` };
|
|
100
|
+
const next = current.endsWith("\n") ? current + entry + "\n" : current + "\n" + entry + "\n";
|
|
101
|
+
writeFileSync(path, next, "utf8");
|
|
102
|
+
return { name: ".gitignore", status: "updated", path, reason: `appended ${entry}` };
|
|
103
|
+
}
|
|
104
|
+
function wireMcpServer(cwd) {
|
|
105
|
+
const path = join(cwd, ".claude", "settings.json");
|
|
106
|
+
if (!existsSync(path)) {
|
|
107
|
+
writeFileSync(path, JSON.stringify(DEFAULT_MCP_SETTINGS, null, 2) + "\n", "utf8");
|
|
108
|
+
return { name: ".claude/settings.json", status: "created", path, reason: "mcpServers.composer wired" };
|
|
109
|
+
}
|
|
110
|
+
const current = JSON.parse(readFileSync(path, "utf8"));
|
|
111
|
+
const mcpServers = current["mcpServers"] ?? {};
|
|
112
|
+
if (mcpServers["composer"]) {
|
|
113
|
+
return { name: ".claude/settings.json", status: "skipped", path, reason: "mcpServers.composer already wired" };
|
|
114
|
+
}
|
|
115
|
+
const merged = {
|
|
116
|
+
...current,
|
|
117
|
+
mcpServers: { ...mcpServers, composer: DEFAULT_MCP_SETTINGS.mcpServers.composer },
|
|
118
|
+
};
|
|
119
|
+
writeFileSync(path, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
120
|
+
return { name: ".claude/settings.json", status: "updated", path, reason: "mcpServers.composer wired" };
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ComposerEnv {
|
|
2
|
+
ANTHROPIC_AUTH_TOKEN?: string;
|
|
3
|
+
ANTHROPIC_BASE_URL?: string;
|
|
4
|
+
/**
|
|
5
|
+
* Wave 3 Step 4 — model identifier override for the AnthropicCompatible
|
|
6
|
+
* provider. Precedence (resolved in src/registry.ts):
|
|
7
|
+
* process.env.ANTHROPIC_MODEL > composer.config.json role.model > "glm-5.1"
|
|
8
|
+
*/
|
|
9
|
+
ANTHROPIC_MODEL?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function loadEnvJson(envPath?: string): ComposerEnv;
|
|
12
|
+
export declare function applyEnvJson(envPath?: string): void;
|
|
13
|
+
export declare function getEnv(): ComposerEnv;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Wave 1 Day 2 — .env.json loader.
|
|
2
|
+
//
|
|
3
|
+
// IMPORTANT (per CLAUDE.md): the Read tool MUST NOT open .env.json.
|
|
4
|
+
// This loader uses fs.readFileSync at runtime — the file is gitignored.
|
|
5
|
+
// Failure modes (missing file, malformed JSON, wrong types) ALL degrade
|
|
6
|
+
// silently to an empty env object, never throw — the registry will fail
|
|
7
|
+
// loud later if a required key is missing.
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
const DEFAULT_ENV_FILE = ".env.json";
|
|
11
|
+
export function loadEnvJson(envPath = DEFAULT_ENV_FILE) {
|
|
12
|
+
const resolved = path.resolve(envPath);
|
|
13
|
+
if (!fs.existsSync(resolved))
|
|
14
|
+
return {};
|
|
15
|
+
let raw;
|
|
16
|
+
try {
|
|
17
|
+
raw = fs.readFileSync(resolved, "utf8");
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
let parsed;
|
|
23
|
+
try {
|
|
24
|
+
parsed = JSON.parse(raw);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
30
|
+
return {};
|
|
31
|
+
const obj = parsed;
|
|
32
|
+
const result = {};
|
|
33
|
+
if (typeof obj["ANTHROPIC_AUTH_TOKEN"] === "string") {
|
|
34
|
+
result.ANTHROPIC_AUTH_TOKEN = obj["ANTHROPIC_AUTH_TOKEN"];
|
|
35
|
+
}
|
|
36
|
+
if (typeof obj["ANTHROPIC_BASE_URL"] === "string") {
|
|
37
|
+
result.ANTHROPIC_BASE_URL = obj["ANTHROPIC_BASE_URL"];
|
|
38
|
+
}
|
|
39
|
+
if (typeof obj["ANTHROPIC_MODEL"] === "string") {
|
|
40
|
+
result.ANTHROPIC_MODEL = obj["ANTHROPIC_MODEL"];
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
export function applyEnvJson(envPath) {
|
|
45
|
+
const env = loadEnvJson(envPath);
|
|
46
|
+
for (const [k, v] of Object.entries(env)) {
|
|
47
|
+
if (typeof v === "string" && v.length > 0 && !process.env[k]) {
|
|
48
|
+
process.env[k] = v;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function getEnv() {
|
|
53
|
+
const result = {};
|
|
54
|
+
const t = process.env["ANTHROPIC_AUTH_TOKEN"];
|
|
55
|
+
const u = process.env["ANTHROPIC_BASE_URL"];
|
|
56
|
+
const m = process.env["ANTHROPIC_MODEL"];
|
|
57
|
+
if (typeof t === "string" && t.length > 0)
|
|
58
|
+
result.ANTHROPIC_AUTH_TOKEN = t;
|
|
59
|
+
if (typeof u === "string" && u.length > 0)
|
|
60
|
+
result.ANTHROPIC_BASE_URL = u;
|
|
61
|
+
if (typeof m === "string" && m.length > 0)
|
|
62
|
+
result.ANTHROPIC_MODEL = m;
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/config/env.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Wave 1 F1.4 — config loader (disk + in-memory).
|
|
2
|
+
// `loadConfig` reads + validates `composer.config.json`; `parseConfig`
|
|
3
|
+
// validates an already-parsed object. Both throw on invalid input so
|
|
4
|
+
// the MCP server fails fast at startup instead of mid-request.
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { ComposerConfigSchema } from "./schema.js";
|
|
8
|
+
export function parseConfig(input) {
|
|
9
|
+
const result = ComposerConfigSchema.safeParse(input);
|
|
10
|
+
if (!result.success) {
|
|
11
|
+
const issues = result.error.issues
|
|
12
|
+
.map((i) => ` - ${i.path.join(".") || "<root>"}: ${i.message}`)
|
|
13
|
+
.join("\n");
|
|
14
|
+
throw new Error(`Composer config failed schema validation:\n${issues}`);
|
|
15
|
+
}
|
|
16
|
+
return result.data;
|
|
17
|
+
}
|
|
18
|
+
export function loadConfig(configPath) {
|
|
19
|
+
const resolved = path.resolve(configPath);
|
|
20
|
+
if (!fs.existsSync(resolved)) {
|
|
21
|
+
throw new Error(`Composer config not found at ${resolved}`);
|
|
22
|
+
}
|
|
23
|
+
const raw = fs.readFileSync(resolved, "utf8");
|
|
24
|
+
let parsed;
|
|
25
|
+
try {
|
|
26
|
+
parsed = JSON.parse(raw);
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
const detail = e instanceof Error ? e.message : String(e);
|
|
30
|
+
throw new Error(`Composer config at ${resolved} is not valid JSON: ${detail}`);
|
|
31
|
+
}
|
|
32
|
+
return parseConfig(parsed);
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,uEAAuE;AACvE,qEAAqE;AACrE,+DAA+D;AAE/D,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAuB,MAAM,aAAa,CAAC;AAExE,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,MAAM,MAAM,GAAG,oBAAoB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACrD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM;aAC/B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,QAAQ,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;aAC/D,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,8CAA8C,MAAM,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,UAAkB;IAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,gCAAgC,QAAQ,EAAE,CAAC,CAAC;IAC9D,CAAC;IACD,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC9C,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,MAAM,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,sBAAsB,QAAQ,uBAAuB,MAAM,EAAE,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const ProviderIdSchema: z.ZodEnum<{
|
|
3
|
+
anthropic: "anthropic";
|
|
4
|
+
openai_compatible: "openai_compatible";
|
|
5
|
+
cli: "cli";
|
|
6
|
+
mock: "mock";
|
|
7
|
+
}>;
|
|
8
|
+
export type ProviderIdParsed = z.infer<typeof ProviderIdSchema>;
|
|
9
|
+
export declare const RoleConfigSchema: z.ZodObject<{
|
|
10
|
+
provider: z.ZodEnum<{
|
|
11
|
+
anthropic: "anthropic";
|
|
12
|
+
openai_compatible: "openai_compatible";
|
|
13
|
+
cli: "cli";
|
|
14
|
+
mock: "mock";
|
|
15
|
+
}>;
|
|
16
|
+
apiKeyEnv: z.ZodOptional<z.ZodString>;
|
|
17
|
+
baseUrl: z.ZodOptional<z.ZodURL>;
|
|
18
|
+
model: z.ZodOptional<z.ZodString>;
|
|
19
|
+
cli: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
20
|
+
}, z.core.$strict>;
|
|
21
|
+
export type RoleConfig = z.infer<typeof RoleConfigSchema>;
|
|
22
|
+
export declare const RoleNameSchema: z.ZodEnum<{
|
|
23
|
+
researcher: "researcher";
|
|
24
|
+
coder: "coder";
|
|
25
|
+
reviewer: "reviewer";
|
|
26
|
+
}>;
|
|
27
|
+
export type RoleName = z.infer<typeof RoleNameSchema>;
|
|
28
|
+
export declare const SpendAuthorizationModeSchema: z.ZodEnum<{
|
|
29
|
+
interactive: "interactive";
|
|
30
|
+
auto: "auto";
|
|
31
|
+
deny: "deny";
|
|
32
|
+
}>;
|
|
33
|
+
export type SpendAuthorizationMode = z.infer<typeof SpendAuthorizationModeSchema>;
|
|
34
|
+
export declare const SpendAuthorizationSchema: z.ZodObject<{
|
|
35
|
+
mode: z.ZodEnum<{
|
|
36
|
+
interactive: "interactive";
|
|
37
|
+
auto: "auto";
|
|
38
|
+
deny: "deny";
|
|
39
|
+
}>;
|
|
40
|
+
maxUsdPerSession: z.ZodOptional<z.ZodNumber>;
|
|
41
|
+
maxUsdPerCall: z.ZodOptional<z.ZodNumber>;
|
|
42
|
+
}, z.core.$strict>;
|
|
43
|
+
export type SpendAuthorization = z.infer<typeof SpendAuthorizationSchema>;
|
|
44
|
+
export declare const ComposerConfigSchema: z.ZodObject<{
|
|
45
|
+
roles: z.ZodObject<{
|
|
46
|
+
researcher: z.ZodObject<{
|
|
47
|
+
provider: z.ZodEnum<{
|
|
48
|
+
anthropic: "anthropic";
|
|
49
|
+
openai_compatible: "openai_compatible";
|
|
50
|
+
cli: "cli";
|
|
51
|
+
mock: "mock";
|
|
52
|
+
}>;
|
|
53
|
+
apiKeyEnv: z.ZodOptional<z.ZodString>;
|
|
54
|
+
baseUrl: z.ZodOptional<z.ZodURL>;
|
|
55
|
+
model: z.ZodOptional<z.ZodString>;
|
|
56
|
+
cli: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
57
|
+
}, z.core.$strict>;
|
|
58
|
+
coder: z.ZodObject<{
|
|
59
|
+
provider: z.ZodEnum<{
|
|
60
|
+
anthropic: "anthropic";
|
|
61
|
+
openai_compatible: "openai_compatible";
|
|
62
|
+
cli: "cli";
|
|
63
|
+
mock: "mock";
|
|
64
|
+
}>;
|
|
65
|
+
apiKeyEnv: z.ZodOptional<z.ZodString>;
|
|
66
|
+
baseUrl: z.ZodOptional<z.ZodURL>;
|
|
67
|
+
model: z.ZodOptional<z.ZodString>;
|
|
68
|
+
cli: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
69
|
+
}, z.core.$strict>;
|
|
70
|
+
reviewer: z.ZodObject<{
|
|
71
|
+
provider: z.ZodEnum<{
|
|
72
|
+
anthropic: "anthropic";
|
|
73
|
+
openai_compatible: "openai_compatible";
|
|
74
|
+
cli: "cli";
|
|
75
|
+
mock: "mock";
|
|
76
|
+
}>;
|
|
77
|
+
apiKeyEnv: z.ZodOptional<z.ZodString>;
|
|
78
|
+
baseUrl: z.ZodOptional<z.ZodURL>;
|
|
79
|
+
model: z.ZodOptional<z.ZodString>;
|
|
80
|
+
cli: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
81
|
+
}, z.core.$strict>;
|
|
82
|
+
}, z.core.$strict>;
|
|
83
|
+
spendAuthorization: z.ZodOptional<z.ZodObject<{
|
|
84
|
+
mode: z.ZodEnum<{
|
|
85
|
+
interactive: "interactive";
|
|
86
|
+
auto: "auto";
|
|
87
|
+
deny: "deny";
|
|
88
|
+
}>;
|
|
89
|
+
maxUsdPerSession: z.ZodOptional<z.ZodNumber>;
|
|
90
|
+
maxUsdPerCall: z.ZodOptional<z.ZodNumber>;
|
|
91
|
+
}, z.core.$strict>>;
|
|
92
|
+
}, z.core.$strict>;
|
|
93
|
+
export type ComposerConfig = z.infer<typeof ComposerConfigSchema>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Wave 1 F1.4 — zod mirror of composer.config.schema.json (C0.2).
|
|
2
|
+
// If the JSON schema changes, this MUST mirror it; tests assert symmetry.
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
export const ProviderIdSchema = z.enum([
|
|
5
|
+
"anthropic",
|
|
6
|
+
"openai_compatible",
|
|
7
|
+
"cli",
|
|
8
|
+
"mock",
|
|
9
|
+
]);
|
|
10
|
+
export const RoleConfigSchema = z
|
|
11
|
+
.object({
|
|
12
|
+
provider: ProviderIdSchema,
|
|
13
|
+
apiKeyEnv: z.string().min(1).optional(),
|
|
14
|
+
baseUrl: z.url().optional(),
|
|
15
|
+
model: z.string().min(1).optional(),
|
|
16
|
+
cli: z.array(z.string().min(1)).min(1).optional(),
|
|
17
|
+
})
|
|
18
|
+
.strict();
|
|
19
|
+
export const RoleNameSchema = z.enum(["researcher", "coder", "reviewer"]);
|
|
20
|
+
export const SpendAuthorizationModeSchema = z.enum([
|
|
21
|
+
"interactive",
|
|
22
|
+
"auto",
|
|
23
|
+
"deny",
|
|
24
|
+
]);
|
|
25
|
+
export const SpendAuthorizationSchema = z
|
|
26
|
+
.object({
|
|
27
|
+
mode: SpendAuthorizationModeSchema,
|
|
28
|
+
maxUsdPerSession: z.number().nonnegative().optional(),
|
|
29
|
+
maxUsdPerCall: z.number().nonnegative().optional(),
|
|
30
|
+
})
|
|
31
|
+
.strict();
|
|
32
|
+
export const ComposerConfigSchema = z
|
|
33
|
+
.object({
|
|
34
|
+
roles: z
|
|
35
|
+
.object({
|
|
36
|
+
researcher: RoleConfigSchema,
|
|
37
|
+
coder: RoleConfigSchema,
|
|
38
|
+
reviewer: RoleConfigSchema,
|
|
39
|
+
})
|
|
40
|
+
.strict(),
|
|
41
|
+
spendAuthorization: SpendAuthorizationSchema.optional(),
|
|
42
|
+
})
|
|
43
|
+
.strict();
|
|
44
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,0EAA0E;AAE1E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,IAAI,CAAC;IACrC,WAAW;IACX,mBAAmB;IACnB,KAAK;IACL,MAAM;CACP,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC;KAC9B,MAAM,CAAC;IACN,QAAQ,EAAE,gBAAgB;IAC1B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACvC,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC3B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACnC,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CAClD,CAAC;KACD,MAAM,EAAE,CAAC;AAGZ,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;AAG1E,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC,IAAI,CAAC;IACjD,aAAa;IACb,MAAM;IACN,MAAM;CACP,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC;KACtC,MAAM,CAAC;IACN,IAAI,EAAE,4BAA4B;IAClC,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE;IACrD,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE;CACnD,CAAC;KACD,MAAM,EAAE,CAAC;AAGZ,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC;KAClC,MAAM,CAAC;IACN,KAAK,EAAE,CAAC;SACL,MAAM,CAAC;QACN,UAAU,EAAE,gBAAgB;QAC5B,KAAK,EAAE,gBAAgB;QACvB,QAAQ,EAAE,gBAAgB;KAC3B,CAAC;SACD,MAAM,EAAE;IACX,kBAAkB,EAAE,wBAAwB,CAAC,QAAQ,EAAE;CACxD,CAAC;KACD,MAAM,EAAE,CAAC"}
|