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.
Files changed (69) hide show
  1. package/README.md +148 -0
  2. package/composer.config.schema.json +79 -0
  3. package/dist/cli/init.d.ts +20 -0
  4. package/dist/cli/init.js +122 -0
  5. package/dist/cli/init.js.map +1 -0
  6. package/dist/config/env.d.ts +13 -0
  7. package/dist/config/env.js +65 -0
  8. package/dist/config/env.js.map +1 -0
  9. package/dist/config/loader.d.ts +3 -0
  10. package/dist/config/loader.js +34 -0
  11. package/dist/config/loader.js.map +1 -0
  12. package/dist/config/schema.d.ts +93 -0
  13. package/dist/config/schema.js +44 -0
  14. package/dist/config/schema.js.map +1 -0
  15. package/dist/evolve/budget.d.ts +23 -0
  16. package/dist/evolve/budget.js +55 -0
  17. package/dist/evolve/budget.js.map +1 -0
  18. package/dist/evolve/lengthPenalty.d.ts +3 -0
  19. package/dist/evolve/lengthPenalty.js +30 -0
  20. package/dist/evolve/lengthPenalty.js.map +1 -0
  21. package/dist/evolve/operators.d.ts +24 -0
  22. package/dist/evolve/operators.js +110 -0
  23. package/dist/evolve/operators.js.map +1 -0
  24. package/dist/evolve/pareto.d.ts +24 -0
  25. package/dist/evolve/pareto.js +153 -0
  26. package/dist/evolve/pareto.js.map +1 -0
  27. package/dist/evolve/plateau.d.ts +18 -0
  28. package/dist/evolve/plateau.js +45 -0
  29. package/dist/evolve/plateau.js.map +1 -0
  30. package/dist/evolve/postflight.d.ts +12 -0
  31. package/dist/evolve/postflight.js +61 -0
  32. package/dist/evolve/postflight.js.map +1 -0
  33. package/dist/evolve/preflight.d.ts +13 -0
  34. package/dist/evolve/preflight.js +39 -0
  35. package/dist/evolve/preflight.js.map +1 -0
  36. package/dist/evolve/reflection.d.ts +12 -0
  37. package/dist/evolve/reflection.js +41 -0
  38. package/dist/evolve/reflection.js.map +1 -0
  39. package/dist/evolve/runner.d.ts +62 -0
  40. package/dist/evolve/runner.js +202 -0
  41. package/dist/evolve/runner.js.map +1 -0
  42. package/dist/evolve/s2-deny.d.ts +26 -0
  43. package/dist/evolve/s2-deny.js +75 -0
  44. package/dist/evolve/s2-deny.js.map +1 -0
  45. package/dist/index.d.ts +2 -0
  46. package/dist/index.js +36 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/providers/AnthropicCompatibleProvider.d.ts +48 -0
  49. package/dist/providers/AnthropicCompatibleProvider.js +50 -0
  50. package/dist/providers/AnthropicCompatibleProvider.js.map +1 -0
  51. package/dist/providers/CLIProvider.d.ts +30 -0
  52. package/dist/providers/CLIProvider.js +106 -0
  53. package/dist/providers/CLIProvider.js.map +1 -0
  54. package/dist/providers/IProvider.d.ts +17 -0
  55. package/dist/providers/IProvider.js +4 -0
  56. package/dist/providers/IProvider.js.map +1 -0
  57. package/dist/providers/MockProvider.d.ts +28 -0
  58. package/dist/providers/MockProvider.js +66 -0
  59. package/dist/providers/MockProvider.js.map +1 -0
  60. package/dist/registry.d.ts +21 -0
  61. package/dist/registry.js +79 -0
  62. package/dist/registry.js.map +1 -0
  63. package/dist/server.d.ts +6 -0
  64. package/dist/server.js +85 -0
  65. package/dist/server.js.map +1 -0
  66. package/dist/util/slug.d.ts +1 -0
  67. package/dist/util/slug.js +7 -0
  68. package/dist/util/slug.js.map +1 -0
  69. package/package.json +56 -0
package/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # Composer — multi-agent orchestration for Claude Code
2
+
3
+ [![npm](https://img.shields.io/badge/npm-agent--composer-blue)](#install) [![tests](https://img.shields.io/badge/vitest-319%20passing-brightgreen)](#contributing) [![license](https://img.shields.io/badge/license-MIT-lightgrey)](#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;
@@ -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,3 @@
1
+ import { type ComposerConfig } from "./schema.js";
2
+ export declare function parseConfig(input: unknown): ComposerConfig;
3
+ export declare function loadConfig(configPath: string): ComposerConfig;
@@ -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"}