@smartmemory/compose 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/LICENSE +21 -0
- package/README.md +1014 -0
- package/bin/compose.js +1515 -0
- package/dist/assets/_baseUniq-CQwX6VLz.js +1 -0
- package/dist/assets/arc-SxJ2J1sh.js +1 -0
- package/dist/assets/architectureDiagram-Q4EWVU46-BykunY1F.js +36 -0
- package/dist/assets/blockDiagram-DXYQGD6D-ohAKBOUw.js +132 -0
- package/dist/assets/c4Diagram-AHTNJAMY-DBDC3ENB.js +10 -0
- package/dist/assets/channel-DGElom1e.js +1 -0
- package/dist/assets/chunk-4BX2VUAB-Cv93Z7uM.js +1 -0
- package/dist/assets/chunk-4TB4RGXK-DE0WBDkj.js +206 -0
- package/dist/assets/chunk-55IACEB6-CE1EXenG.js +1 -0
- package/dist/assets/chunk-EDXVE4YY-DA7Ana6H.js +1 -0
- package/dist/assets/chunk-FMBD7UC4-CTDIPA3p.js +15 -0
- package/dist/assets/chunk-OYMX7WX6-uGBaPaTX.js +231 -0
- package/dist/assets/chunk-QZHKN3VN-CYlnXuUO.js +1 -0
- package/dist/assets/chunk-YZCP3GAM-ojGkzcZK.js +1 -0
- package/dist/assets/classDiagram-6PBFFD2Q-KqWP9wWZ.js +1 -0
- package/dist/assets/classDiagram-v2-HSJHXN6E-KqWP9wWZ.js +1 -0
- package/dist/assets/clone-DUJKJXd7.js +1 -0
- package/dist/assets/cose-bilkent-S5V4N54A-Bktn9hL-.js +1 -0
- package/dist/assets/dagre-KV5264BT-DFaSzuRF.js +4 -0
- package/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/assets/diagram-5BDNPKRD-DnfmDzEm.js +10 -0
- package/dist/assets/diagram-G4DWMVQ6-Bm8W9YnG.js +24 -0
- package/dist/assets/diagram-MMDJMWI5-B5-TSKvp.js +43 -0
- package/dist/assets/diagram-TYMM5635-ls4rqlky.js +24 -0
- package/dist/assets/erDiagram-SMLLAGMA-giG6WO-r.js +85 -0
- package/dist/assets/flowDiagram-DWJPFMVM-XvlUuz-7.js +162 -0
- package/dist/assets/ganttDiagram-T4ZO3ILL-hLBV57oV.js +292 -0
- package/dist/assets/gitGraphDiagram-UUTBAWPF-BHu3s_Gn.js +106 -0
- package/dist/assets/graph-D0Cfv00Y.js +1 -0
- package/dist/assets/index-CUd6pFGF.css +1 -0
- package/dist/assets/index-DReRlzZI.js +1144 -0
- package/dist/assets/infoDiagram-42DDH7IO-DbqRsOo3.js +2 -0
- package/dist/assets/init-Gi6I4Gst.js +1 -0
- package/dist/assets/ishikawaDiagram-UXIWVN3A-DnCdx7zb.js +70 -0
- package/dist/assets/journeyDiagram-VCZTEJTY-CfD7eNcP.js +139 -0
- package/dist/assets/kanban-definition-6JOO6SKY-BYaO9-mK.js +89 -0
- package/dist/assets/katex-DkKDou_j.js +257 -0
- package/dist/assets/layout-Bj72wOEB.js +1 -0
- package/dist/assets/linear-BRFo114D.js +1 -0
- package/dist/assets/min-GCHnKlJS.js +1 -0
- package/dist/assets/mindmap-definition-QFDTVHPH-n0PMebY4.js +96 -0
- package/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/dist/assets/pieDiagram-DEJITSTG-pN4CljHF.js +30 -0
- package/dist/assets/quadrantDiagram-34T5L4WZ-DNoAy8-D.js +7 -0
- package/dist/assets/requirementDiagram-MS252O5E-BhtY05PT.js +84 -0
- package/dist/assets/sankeyDiagram-XADWPNL6-B6AD-16A.js +10 -0
- package/dist/assets/sequenceDiagram-FGHM5R23-DShHM-uk.js +157 -0
- package/dist/assets/stateDiagram-FHFEXIEX-DMxn7HTo.js +1 -0
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-o6PnCs4e.js +1 -0
- package/dist/assets/timeline-definition-GMOUNBTQ-Cdu6uq52.js +120 -0
- package/dist/assets/vennDiagram-DHZGUBPP-CpK29iRe.js +34 -0
- package/dist/assets/wardley-RL74JXVD-BQgSkdcO.js +162 -0
- package/dist/assets/wardleyDiagram-NUSXRM2D-DJHYev6O.js +20 -0
- package/dist/assets/xychartDiagram-5P7HB3ND-1d75pbaO.js +7 -0
- package/dist/index.html +30 -0
- package/lib/agent-chains.js +65 -0
- package/lib/agent-string.js +86 -0
- package/lib/budget-ledger.js +86 -0
- package/lib/build-all.js +162 -0
- package/lib/build-dag.js +120 -0
- package/lib/build-stream-writer.js +190 -0
- package/lib/build.js +2997 -0
- package/lib/capability-checker.js +53 -0
- package/lib/cert-inject.js +38 -0
- package/lib/cli-progress.js +483 -0
- package/lib/constants.js +69 -0
- package/lib/cross-layer-audit.js +84 -0
- package/lib/debug-discipline.js +173 -0
- package/lib/feature-json.js +106 -0
- package/lib/gate-prompt.js +291 -0
- package/lib/gate-tiers.js +194 -0
- package/lib/health-history.js +119 -0
- package/lib/health-score.js +227 -0
- package/lib/ideabox.js +570 -0
- package/lib/import.js +244 -0
- package/lib/migrate-roadmap.js +94 -0
- package/lib/model-pricing.js +67 -0
- package/lib/new.js +413 -0
- package/lib/pipeline-cli.js +489 -0
- package/lib/plan-parser.js +103 -0
- package/lib/qa-scoping.js +474 -0
- package/lib/questionnaire.js +200 -0
- package/lib/resolve-port.js +7 -0
- package/lib/result-normalizer.js +349 -0
- package/lib/review-lenses.js +166 -0
- package/lib/roadmap-gen.js +210 -0
- package/lib/roadmap-parser.js +176 -0
- package/lib/server-probe.js +23 -0
- package/lib/staleness.js +87 -0
- package/lib/step-prompt.js +260 -0
- package/lib/step-validator.js +49 -0
- package/lib/stratum-mcp-client.js +365 -0
- package/lib/team-flag.js +46 -0
- package/lib/test-bootstrap.js +401 -0
- package/lib/triage.js +274 -0
- package/lib/vision-writer.js +391 -0
- package/package.json +111 -0
- package/pipelines/bug-fix.stratum.yaml +230 -0
- package/pipelines/build.stratum.yaml +498 -0
- package/pipelines/content.stratum.yaml +112 -0
- package/pipelines/coverage-sweep.stratum.yaml +52 -0
- package/pipelines/refactor.stratum.yaml +169 -0
- package/pipelines/research.stratum.yaml +88 -0
- package/pipelines/review-fix.stratum.yaml +109 -0
- package/presets/team-feature.stratum.yaml +105 -0
- package/presets/team-research.stratum.yaml +108 -0
- package/presets/team-review.stratum.yaml +106 -0
- package/scripts/agent-activity-hook.sh +31 -0
- package/scripts/agent-error-hook.sh +28 -0
- package/scripts/analyze-orphans.mjs +50 -0
- package/scripts/find-orphans.mjs +26 -0
- package/scripts/fix-phases.mjs +49 -0
- package/scripts/generate-stratum-spec.mjs +137 -0
- package/scripts/import-roadmap.mjs +116 -0
- package/scripts/phase-audit.mjs +33 -0
- package/scripts/run-pipeline.mjs +314 -0
- package/scripts/session-end-hook.sh +18 -0
- package/scripts/session-start-hook.sh +38 -0
- package/scripts/vision-hook.sh +104 -0
- package/scripts/vision-track.mjs +554 -0
- package/scripts/wire-all-orphans.mjs +108 -0
- package/scripts/wire-orphans.mjs +164 -0
- package/server/activity-routes.js +123 -0
- package/server/agent-health.js +197 -0
- package/server/agent-hooks.js +102 -0
- package/server/agent-mcp.js +10 -0
- package/server/agent-registry.js +95 -0
- package/server/agent-server.js +290 -0
- package/server/agent-spawn.js +251 -0
- package/server/agent-templates.js +77 -0
- package/server/artifact-manager.js +247 -0
- package/server/artifact-templates/architecture.md +28 -0
- package/server/artifact-templates/blueprint.md +21 -0
- package/server/artifact-templates/design.md +36 -0
- package/server/artifact-templates/plan.md +25 -0
- package/server/artifact-templates/prd.md +43 -0
- package/server/artifact-templates/report.md +40 -0
- package/server/block-tracker.js +90 -0
- package/server/build-stream-bridge.js +502 -0
- package/server/coalescing-buffer.js +46 -0
- package/server/compose-mcp-tools.js +479 -0
- package/server/compose-mcp.js +324 -0
- package/server/connectors/agent-connector.js +78 -0
- package/server/connectors/claude-sdk-connector.js +198 -0
- package/server/connectors/codex-connector.js +240 -0
- package/server/connectors/connector-discovery.js +18 -0
- package/server/connectors/connector-runtime.js +13 -0
- package/server/connectors/opencode-connector.js +200 -0
- package/server/design-routes.js +540 -0
- package/server/design-session.js +161 -0
- package/server/feature-scan.js +593 -0
- package/server/file-watcher.js +284 -0
- package/server/find-root.js +29 -0
- package/server/graph-export.js +343 -0
- package/server/ideabox-cache.js +77 -0
- package/server/ideabox-routes.js +294 -0
- package/server/index.js +156 -0
- package/server/model-tiers.js +49 -0
- package/server/pipeline-routes.js +288 -0
- package/server/policy-evaluator.js +36 -0
- package/server/project-root.js +122 -0
- package/server/security.js +23 -0
- package/server/session-manager.js +403 -0
- package/server/session-routes.js +190 -0
- package/server/session-store.js +107 -0
- package/server/settings-routes.js +35 -0
- package/server/settings-store.js +234 -0
- package/server/stratum-api.js +102 -0
- package/server/stratum-client.js +192 -0
- package/server/stratum-sync.js +193 -0
- package/server/summarizer.js +139 -0
- package/server/supervisor.js +196 -0
- package/server/vision-routes.js +668 -0
- package/server/vision-server.js +393 -0
- package/server/vision-store.js +360 -0
- package/server/vision-utils.js +179 -0
- package/server/worktree-gc.js +137 -0
- package/templates/ROADMAP.md +46 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Compose MCP Server — stdio transport
|
|
4
|
+
*
|
|
5
|
+
* Exposes Compose tracker state as MCP tools for Claude Code agents running
|
|
6
|
+
* inside this project. Claude Code launches this process on-demand and
|
|
7
|
+
* communicates via stdin/stdout JSON-RPC. No port, no supervisor entry.
|
|
8
|
+
*
|
|
9
|
+
* Register in .mcp.json:
|
|
10
|
+
* { "mcpServers": { "compose": { "command": "node", "args": ["server/compose-mcp.js"] } } }
|
|
11
|
+
*
|
|
12
|
+
* Tools:
|
|
13
|
+
* get_vision_items — query items by phase/status/type/keyword
|
|
14
|
+
* get_item_detail — single item with its connections
|
|
15
|
+
* get_current_session — active session: tool count, items touched, summaries
|
|
16
|
+
* get_phase_summary — status distribution for a given phase
|
|
17
|
+
* get_blocked_items — items blocked by non-complete dependencies
|
|
18
|
+
*
|
|
19
|
+
* Token budget (per docs/features/mcp-connector/design.md Decision 6):
|
|
20
|
+
* Baseline (2026-02-24): ~519 tokens for all 5 tool definitions combined
|
|
21
|
+
* Soft cap: 2,000 tokens. Add typed tools for new operations; avoid proliferation.
|
|
22
|
+
* Per-tool: get_vision_items 235, get_phase_summary 104,
|
|
23
|
+
* get_item_detail 72, get_current_session 62, get_blocked_items 44
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
27
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
28
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
29
|
+
import {
|
|
30
|
+
toolGetVisionItems,
|
|
31
|
+
toolGetItemDetail,
|
|
32
|
+
toolGetPhasesSummary,
|
|
33
|
+
toolGetBlockedItems,
|
|
34
|
+
toolGetCurrentSession,
|
|
35
|
+
toolGetFeatureLifecycle,
|
|
36
|
+
toolKillFeature,
|
|
37
|
+
toolCompleteFeature,
|
|
38
|
+
toolAssessFeatureArtifacts,
|
|
39
|
+
toolScaffoldFeature,
|
|
40
|
+
toolApproveGate,
|
|
41
|
+
toolGetPendingGates,
|
|
42
|
+
toolBindSession,
|
|
43
|
+
toolIterationStart,
|
|
44
|
+
toolIterationReport,
|
|
45
|
+
toolIterationAbort,
|
|
46
|
+
} from './compose-mcp-tools.js';
|
|
47
|
+
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// Tool definitions
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
const TOOLS = [
|
|
53
|
+
{
|
|
54
|
+
name: 'get_vision_items',
|
|
55
|
+
description: 'Query Compose tracker items. Filter by phase, status, type, or keyword. Returns id, title, type, phase, status, confidence, description.',
|
|
56
|
+
inputSchema: {
|
|
57
|
+
type: 'object',
|
|
58
|
+
properties: {
|
|
59
|
+
phase: {
|
|
60
|
+
type: 'string',
|
|
61
|
+
description: 'Filter by phase: vision, requirements, design, planning, implementation, verification, release',
|
|
62
|
+
},
|
|
63
|
+
status: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
description: 'Filter by status (comma-separated for multiple): planned, in_progress, complete, blocked, parked, killed',
|
|
66
|
+
},
|
|
67
|
+
type: {
|
|
68
|
+
type: 'string',
|
|
69
|
+
description: 'Filter by type: task, decision, evaluation, idea, spec, thread, artifact, question, feature, track',
|
|
70
|
+
},
|
|
71
|
+
keyword: {
|
|
72
|
+
type: 'string',
|
|
73
|
+
description: 'Search keyword matched against title and description',
|
|
74
|
+
},
|
|
75
|
+
limit: {
|
|
76
|
+
type: 'number',
|
|
77
|
+
description: 'Max results to return (default 30)',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'get_item_detail',
|
|
84
|
+
description: 'Get full detail for a single tracker item including all its connections.',
|
|
85
|
+
inputSchema: {
|
|
86
|
+
type: 'object',
|
|
87
|
+
properties: {
|
|
88
|
+
id: {
|
|
89
|
+
type: 'string',
|
|
90
|
+
description: 'Item ID (UUID) or semanticId/slug',
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
required: ['id'],
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'get_phase_summary',
|
|
98
|
+
description: 'Get status and type distribution for a phase (or all phases). Useful for understanding overall project health.',
|
|
99
|
+
inputSchema: {
|
|
100
|
+
type: 'object',
|
|
101
|
+
properties: {
|
|
102
|
+
phase: {
|
|
103
|
+
type: 'string',
|
|
104
|
+
description: 'Phase to summarize: vision, requirements, design, planning, implementation, verification, release. Omit for all phases.',
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: 'get_blocked_items',
|
|
111
|
+
description: 'List all tracker items that are blocked by non-complete items.',
|
|
112
|
+
inputSchema: {
|
|
113
|
+
type: 'object',
|
|
114
|
+
properties: {},
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: 'get_current_session',
|
|
119
|
+
description: 'Get the most recent session: tool count, items touched, error count, and recent Haiku summaries of what was accomplished.',
|
|
120
|
+
inputSchema: {
|
|
121
|
+
type: 'object',
|
|
122
|
+
properties: {
|
|
123
|
+
featureCode: { type: 'string', description: 'Optional: get context for a specific feature' },
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: 'bind_session',
|
|
129
|
+
description: 'Bind the current agent session to a lifecycle feature. Call once per session after creating/identifying the feature. Binding is one-shot — calling again on a bound session returns already_bound.',
|
|
130
|
+
inputSchema: {
|
|
131
|
+
type: 'object',
|
|
132
|
+
properties: {
|
|
133
|
+
featureCode: { type: 'string', description: 'The feature code (e.g., "gate-ui")' },
|
|
134
|
+
},
|
|
135
|
+
required: ['featureCode'],
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: 'get_feature_lifecycle',
|
|
140
|
+
description: 'Get the lifecycle state of a feature: current phase, phase history, artifacts, warnings.',
|
|
141
|
+
inputSchema: {
|
|
142
|
+
type: 'object',
|
|
143
|
+
properties: {
|
|
144
|
+
id: { type: 'string', description: 'Item ID (UUID) or slug' },
|
|
145
|
+
},
|
|
146
|
+
required: ['id'],
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: 'kill_feature',
|
|
151
|
+
description: 'Kill a feature from any phase. Records reason and sets status to killed.',
|
|
152
|
+
inputSchema: {
|
|
153
|
+
type: 'object',
|
|
154
|
+
properties: {
|
|
155
|
+
id: { type: 'string', description: 'Item ID' },
|
|
156
|
+
reason: { type: 'string', description: 'Why the feature is being killed' },
|
|
157
|
+
},
|
|
158
|
+
required: ['id', 'reason'],
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: 'complete_feature',
|
|
163
|
+
description: 'Mark a feature as complete. Only callable from the ship phase.',
|
|
164
|
+
inputSchema: {
|
|
165
|
+
type: 'object',
|
|
166
|
+
properties: {
|
|
167
|
+
id: { type: 'string', description: 'Item ID' },
|
|
168
|
+
},
|
|
169
|
+
required: ['id'],
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: 'start_iteration_loop',
|
|
174
|
+
description: 'Start a review or coverage iteration loop on a feature. Returns loop state.',
|
|
175
|
+
inputSchema: {
|
|
176
|
+
type: 'object',
|
|
177
|
+
properties: {
|
|
178
|
+
id: { type: 'string', description: 'Item ID or semanticId' },
|
|
179
|
+
loopType: { type: 'string', enum: ['review', 'coverage'], description: 'Type of iteration loop' },
|
|
180
|
+
maxIterations: { type: 'number', description: 'Override max iterations (optional, defaults from settings)' },
|
|
181
|
+
},
|
|
182
|
+
required: ['id', 'loopType'],
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: 'report_iteration_result',
|
|
187
|
+
description: 'Report one iteration result. Compose evaluates exit criteria and returns whether to continue.',
|
|
188
|
+
inputSchema: {
|
|
189
|
+
type: 'object',
|
|
190
|
+
properties: {
|
|
191
|
+
id: { type: 'string', description: 'Item ID or semanticId' },
|
|
192
|
+
result: { type: 'object', description: 'Iteration result. Review: {clean: bool, findings: []}, Coverage: {passing: bool, failures: []}' },
|
|
193
|
+
},
|
|
194
|
+
required: ['id', 'result'],
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
name: 'abort_iteration_loop',
|
|
199
|
+
description: 'Abort the current iteration loop early.',
|
|
200
|
+
inputSchema: {
|
|
201
|
+
type: 'object',
|
|
202
|
+
properties: {
|
|
203
|
+
id: { type: 'string', description: 'Item ID or semanticId' },
|
|
204
|
+
reason: { type: 'string', description: 'Why the loop was aborted' },
|
|
205
|
+
},
|
|
206
|
+
required: ['id'],
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
name: 'assess_feature_artifacts',
|
|
211
|
+
description: 'Assess quality signals for all artifacts of a feature: section completeness, word count, last modified.',
|
|
212
|
+
inputSchema: {
|
|
213
|
+
type: 'object',
|
|
214
|
+
properties: {
|
|
215
|
+
featureCode: { type: 'string', description: 'Feature folder name (e.g. "artifact-awareness")' },
|
|
216
|
+
},
|
|
217
|
+
required: ['featureCode'],
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: 'scaffold_feature',
|
|
222
|
+
description: 'Create feature folder with template stubs for all phase artifacts. Existing files are never overwritten.',
|
|
223
|
+
inputSchema: {
|
|
224
|
+
type: 'object',
|
|
225
|
+
properties: {
|
|
226
|
+
featureCode: { type: 'string', description: 'Feature folder name' },
|
|
227
|
+
only: {
|
|
228
|
+
type: 'array',
|
|
229
|
+
items: { type: 'string' },
|
|
230
|
+
description: 'Limit to specific artifacts (e.g. ["design.md", "blueprint.md"]). Omit for all.',
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
required: ['featureCode'],
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: 'approve_gate',
|
|
238
|
+
description: 'Resolve a pending policy gate. Outcomes: approved (proceed), revised (stay in phase), killed (abandon feature).',
|
|
239
|
+
inputSchema: {
|
|
240
|
+
type: 'object',
|
|
241
|
+
properties: {
|
|
242
|
+
gateId: { type: 'string', description: 'Gate ID' },
|
|
243
|
+
outcome: { type: 'string', enum: ['approved', 'revised', 'killed'], description: 'Resolution outcome' },
|
|
244
|
+
comment: { type: 'string', description: 'Optional human feedback' },
|
|
245
|
+
},
|
|
246
|
+
required: ['gateId', 'outcome'],
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
name: 'get_pending_gates',
|
|
251
|
+
description: 'List pending policy gates. Optionally filter by item ID.',
|
|
252
|
+
inputSchema: {
|
|
253
|
+
type: 'object',
|
|
254
|
+
properties: {
|
|
255
|
+
itemId: { type: 'string', description: 'Filter to gates for a specific item (optional)' },
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
// Note: `agent_run` tool removed 2026-04-18 (STRAT-DEDUP-AGENTRUN v1).
|
|
260
|
+
// LLM-facing agent dispatch now goes through `mcp__stratum__stratum_agent_run`.
|
|
261
|
+
// `toolAgentRun` (and the Node connectors it uses) remain exported for Compose's
|
|
262
|
+
// internal callers (build.js, vision-server, pipelines).
|
|
263
|
+
];
|
|
264
|
+
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
// MCP Server setup
|
|
267
|
+
// ---------------------------------------------------------------------------
|
|
268
|
+
|
|
269
|
+
const server = new Server(
|
|
270
|
+
{ name: 'compose', version: '1.0.0' },
|
|
271
|
+
{ capabilities: { tools: {} } }
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
275
|
+
tools: TOOLS,
|
|
276
|
+
}));
|
|
277
|
+
|
|
278
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
279
|
+
const { name, arguments: args = {} } = request.params;
|
|
280
|
+
|
|
281
|
+
try {
|
|
282
|
+
let result;
|
|
283
|
+
switch (name) {
|
|
284
|
+
case 'get_vision_items': result = toolGetVisionItems(args); break;
|
|
285
|
+
case 'get_item_detail': result = toolGetItemDetail(args); break;
|
|
286
|
+
case 'get_phase_summary': result = toolGetPhasesSummary(args); break;
|
|
287
|
+
case 'get_blocked_items': result = toolGetBlockedItems(); break;
|
|
288
|
+
case 'get_current_session': result = await toolGetCurrentSession(args); break;
|
|
289
|
+
case 'bind_session': result = await toolBindSession(args); break;
|
|
290
|
+
case 'get_feature_lifecycle': result = toolGetFeatureLifecycle(args); break;
|
|
291
|
+
case 'kill_feature': result = await toolKillFeature(args); break;
|
|
292
|
+
case 'complete_feature': result = await toolCompleteFeature(args); break;
|
|
293
|
+
case 'start_iteration_loop': result = await toolIterationStart(args); break;
|
|
294
|
+
case 'report_iteration_result': result = await toolIterationReport(args); break;
|
|
295
|
+
case 'abort_iteration_loop': result = await toolIterationAbort(args); break;
|
|
296
|
+
case 'assess_feature_artifacts': result = toolAssessFeatureArtifacts(args); break;
|
|
297
|
+
case 'scaffold_feature': result = toolScaffoldFeature(args); break;
|
|
298
|
+
case 'approve_gate': result = await toolApproveGate(args); break;
|
|
299
|
+
case 'get_pending_gates': result = toolGetPendingGates(args); break;
|
|
300
|
+
// agent_run removed — STRAT-DEDUP-AGENTRUN v1. Use mcp__stratum__stratum_agent_run.
|
|
301
|
+
default:
|
|
302
|
+
return {
|
|
303
|
+
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
|
304
|
+
isError: true,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
309
|
+
};
|
|
310
|
+
} catch (err) {
|
|
311
|
+
return {
|
|
312
|
+
content: [{ type: 'text', text: `Error: ${err.message}` }],
|
|
313
|
+
isError: true,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// ---------------------------------------------------------------------------
|
|
319
|
+
// Start
|
|
320
|
+
// ---------------------------------------------------------------------------
|
|
321
|
+
|
|
322
|
+
const transport = new StdioServerTransport();
|
|
323
|
+
await server.connect(transport);
|
|
324
|
+
// Server runs until stdin closes — no explicit exit needed
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentConnector — base class for all agent connectors.
|
|
3
|
+
*
|
|
4
|
+
* @implements {ConnectorDiscovery} — see connector-discovery.js
|
|
5
|
+
* @implements {ConnectorRuntime} — see connector-runtime.js
|
|
6
|
+
*
|
|
7
|
+
* Subclasses implement run(), interrupt(), and isRunning.
|
|
8
|
+
* Duck typing — no enforcement at runtime beyond the throw in run().
|
|
9
|
+
*
|
|
10
|
+
* Message envelope (yielded by run()):
|
|
11
|
+
* { type: 'system', subtype: 'init' | 'complete', agent: string, model?: string }
|
|
12
|
+
* { type: 'assistant', content: string }
|
|
13
|
+
* { type: 'tool_use', tool: string, input: object }
|
|
14
|
+
* { type: 'tool_use_summary', summary: string, output?: string }
|
|
15
|
+
* { type: 'error', message: string }
|
|
16
|
+
*
|
|
17
|
+
* Schema mode: if opts.schema is provided, the connector injects it into the
|
|
18
|
+
* prompt as instructions and yields text output. JSON.parse() happens at the
|
|
19
|
+
* MCP layer (agent-mcp.js), never inside connectors.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export class AgentConnector {
|
|
23
|
+
// ── Discovery ──────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
/** @returns {string[]} */
|
|
26
|
+
listModels() { return []; }
|
|
27
|
+
|
|
28
|
+
/** @param {string} _modelId @returns {boolean} */
|
|
29
|
+
supportsModel(_modelId) { return false; }
|
|
30
|
+
|
|
31
|
+
/** @param {string} _sessionId @returns {Promise<object[]>} */
|
|
32
|
+
async loadHistory(_sessionId) { return []; }
|
|
33
|
+
|
|
34
|
+
// ── Runtime ────────────────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Run a prompt against the agent.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} prompt
|
|
40
|
+
* @param {object} [opts]
|
|
41
|
+
* @param {object} [opts.schema] — JSON Schema → structured output mode
|
|
42
|
+
* @param {string} [opts.modelID] — override model for this run
|
|
43
|
+
* @param {string} [opts.providerID] — provider ID (OpenCode subclasses only)
|
|
44
|
+
* @param {string} [opts.cwd] — working directory
|
|
45
|
+
* @param {string[]} [opts.tools] — restrict available tools
|
|
46
|
+
* @returns {AsyncGenerator}
|
|
47
|
+
*/
|
|
48
|
+
// eslint-disable-next-line require-yield
|
|
49
|
+
async *run(_prompt, _opts = {}) {
|
|
50
|
+
throw new Error(`${this.constructor.name}.run() not implemented`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Stop the active run cleanly. No-op if not running. */
|
|
54
|
+
interrupt() {}
|
|
55
|
+
|
|
56
|
+
/** Whether a run is currently active. */
|
|
57
|
+
get isRunning() { return false; }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Inject a JSON schema into a prompt so the agent knows to return structured JSON.
|
|
62
|
+
* Used by both ClaudeSDKConnector and OpencodeConnector — parsing happens at call site.
|
|
63
|
+
*
|
|
64
|
+
* @param {string} prompt
|
|
65
|
+
* @param {object} schema JSON Schema object
|
|
66
|
+
* @returns {string}
|
|
67
|
+
*/
|
|
68
|
+
export function injectSchema(prompt, schema) {
|
|
69
|
+
return (
|
|
70
|
+
`${prompt}\n\n` +
|
|
71
|
+
`IMPORTANT: After completing the task, include a JSON code block at the very end ` +
|
|
72
|
+
`of your response matching this schema:\n` +
|
|
73
|
+
'```json\n' +
|
|
74
|
+
`${JSON.stringify(schema, null, 2)}\n` +
|
|
75
|
+
'```\n' +
|
|
76
|
+
`The JSON block must be the last thing in your response.`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClaudeSDKConnector — wraps @anthropic-ai/claude-agent-sdk query().
|
|
3
|
+
*
|
|
4
|
+
* All Anthropic model execution goes through this connector.
|
|
5
|
+
* Yields the same typed message envelope as the other connectors.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
9
|
+
import { AgentConnector, injectSchema } from './agent-connector.js';
|
|
10
|
+
|
|
11
|
+
const DEFAULT_MODEL = process.env.CLAUDE_MODEL || 'claude-sonnet-4-6';
|
|
12
|
+
|
|
13
|
+
export class ClaudeSDKConnector extends AgentConnector {
|
|
14
|
+
// ── Discovery ──────────────────────────────────────────────────────────────
|
|
15
|
+
// No overrides — inherits stubs from AgentConnector. See agent-connector.js.
|
|
16
|
+
|
|
17
|
+
#model;
|
|
18
|
+
#cwd;
|
|
19
|
+
#query = null;
|
|
20
|
+
|
|
21
|
+
#allowedTools;
|
|
22
|
+
#disallowedTools;
|
|
23
|
+
#thinking;
|
|
24
|
+
#effort;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {object} [opts]
|
|
28
|
+
* @param {string} [opts.model] — default model (env CLAUDE_MODEL or claude-sonnet-4-6)
|
|
29
|
+
* @param {string} [opts.cwd] — default working directory
|
|
30
|
+
* @param {string[]} [opts.allowedTools] — restrict to these tools (overrides preset)
|
|
31
|
+
* @param {string[]} [opts.disallowedTools] — deny these tools (used alongside allowedTools or preset)
|
|
32
|
+
* @param {object|null} [opts.thinking] — Claude thinking config, e.g. { type: 'adaptive' } or { type: 'disabled' }
|
|
33
|
+
* @param {string|null} [opts.effort] — effort level: 'low' | 'medium' | 'high' | 'xhigh' | 'max'
|
|
34
|
+
*/
|
|
35
|
+
constructor({ model = DEFAULT_MODEL, cwd = process.cwd(), allowedTools, disallowedTools, thinking, effort } = {}) {
|
|
36
|
+
super();
|
|
37
|
+
this.#model = model;
|
|
38
|
+
this.#cwd = cwd;
|
|
39
|
+
this.#allowedTools = allowedTools ?? null;
|
|
40
|
+
this.#disallowedTools = disallowedTools ?? null;
|
|
41
|
+
this.#thinking = thinking ?? null;
|
|
42
|
+
this.#effort = effort ?? null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ── Runtime ────────────────────────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
async *run(prompt, { schema, modelID, cwd, thinking, effort } = {}) {
|
|
48
|
+
if (this.#query) {
|
|
49
|
+
throw new Error('ClaudeSDKConnector: run() already active. Call interrupt() first.');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const actualPrompt = schema ? injectSchema(prompt, schema) : prompt;
|
|
53
|
+
|
|
54
|
+
// Strip CLAUDECODE env var to allow spawning inside a Claude Code session
|
|
55
|
+
const cleanEnv = { ...process.env };
|
|
56
|
+
delete cleanEnv.CLAUDECODE;
|
|
57
|
+
|
|
58
|
+
// Build tools config: prefer explicit allow/deny lists if provided,
|
|
59
|
+
// otherwise fall back to the default claude_code preset (backward compat).
|
|
60
|
+
let toolsConfig;
|
|
61
|
+
if (this.#allowedTools !== null) {
|
|
62
|
+
toolsConfig = { type: 'allowed', allowedTools: this.#allowedTools };
|
|
63
|
+
if (this.#disallowedTools !== null) {
|
|
64
|
+
toolsConfig.disallowedTools = this.#disallowedTools;
|
|
65
|
+
}
|
|
66
|
+
} else if (this.#disallowedTools !== null) {
|
|
67
|
+
toolsConfig = { type: 'preset', preset: 'claude_code', disallowedTools: this.#disallowedTools };
|
|
68
|
+
} else {
|
|
69
|
+
toolsConfig = { type: 'preset', preset: 'claude_code' };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Resolve thinking/effort: per-run override beats constructor default.
|
|
73
|
+
const resolvedThinking = thinking !== undefined ? thinking : this.#thinking;
|
|
74
|
+
const resolvedEffort = effort !== undefined ? effort : this.#effort;
|
|
75
|
+
|
|
76
|
+
const sdkOptions = {
|
|
77
|
+
cwd: cwd ?? this.#cwd,
|
|
78
|
+
model: modelID ?? this.#model,
|
|
79
|
+
permissionMode: 'acceptEdits',
|
|
80
|
+
tools: toolsConfig,
|
|
81
|
+
env: cleanEnv,
|
|
82
|
+
};
|
|
83
|
+
if (resolvedThinking !== null && resolvedThinking !== undefined) {
|
|
84
|
+
sdkOptions.thinking = resolvedThinking;
|
|
85
|
+
}
|
|
86
|
+
if (resolvedEffort !== null && resolvedEffort !== undefined) {
|
|
87
|
+
sdkOptions.effort = resolvedEffort;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const q = query({
|
|
91
|
+
prompt: actualPrompt,
|
|
92
|
+
options: sdkOptions,
|
|
93
|
+
});
|
|
94
|
+
this.#query = q;
|
|
95
|
+
|
|
96
|
+
yield { type: 'system', subtype: 'init', agent: 'claude', model: modelID ?? this.#model };
|
|
97
|
+
|
|
98
|
+
const activeModel = modelID ?? this.#model;
|
|
99
|
+
try {
|
|
100
|
+
for await (const msg of q) {
|
|
101
|
+
if (process.env.COMPOSE_DEBUG) {
|
|
102
|
+
process.stderr.write(` [sdk] ${msg?.type ?? typeof msg}\n`);
|
|
103
|
+
}
|
|
104
|
+
for (const event of _normalizeAll(msg)) {
|
|
105
|
+
// Inject model into usage events extracted from result messages
|
|
106
|
+
if (event.type === 'usage' && event._from_result) {
|
|
107
|
+
const { _from_result: _, ...usageEvent } = event;
|
|
108
|
+
yield { ...usageEvent, model: activeModel };
|
|
109
|
+
} else {
|
|
110
|
+
yield event;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
yield { type: 'system', subtype: 'complete', agent: 'claude' };
|
|
115
|
+
} catch (err) {
|
|
116
|
+
if (err?.name !== 'AbortError') {
|
|
117
|
+
yield { type: 'error', message: err.message || String(err) };
|
|
118
|
+
}
|
|
119
|
+
} finally {
|
|
120
|
+
this.#query = null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
interrupt() {
|
|
125
|
+
if (this.#query) {
|
|
126
|
+
try { this.#query.interrupt(); } catch { /* already done */ }
|
|
127
|
+
this.#query = null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
get isRunning() {
|
|
132
|
+
return this.#query !== null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// Normalize Claude SDK message → shared envelope
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Normalize an SDK message into one or more envelope events.
|
|
142
|
+
* Returns an array because a single assistant message can contain
|
|
143
|
+
* multiple content blocks (text + tool_use).
|
|
144
|
+
*/
|
|
145
|
+
function _normalizeAll(msg) {
|
|
146
|
+
if (!msg || typeof msg !== 'object') {
|
|
147
|
+
return [{ type: 'assistant', content: String(msg) }];
|
|
148
|
+
}
|
|
149
|
+
if (msg.type === 'system' || msg.type === 'error') return [msg];
|
|
150
|
+
|
|
151
|
+
// SDK assistant message — extract content blocks from msg.message
|
|
152
|
+
if (msg.type === 'assistant' && msg.message?.content) {
|
|
153
|
+
const events = [];
|
|
154
|
+
for (const block of msg.message.content) {
|
|
155
|
+
if (block.type === 'text' && block.text) {
|
|
156
|
+
events.push({ type: 'assistant', content: block.text });
|
|
157
|
+
} else if (block.type === 'tool_use') {
|
|
158
|
+
events.push({ type: 'tool_use', tool: block.name, input: block.input ?? {} });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return events.length > 0 ? events : [msg];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// SDK result message — contains the final aggregated text and usage metadata
|
|
165
|
+
if (msg.type === 'result' && msg.result) {
|
|
166
|
+
const events = [{ type: 'result', content: msg.result }];
|
|
167
|
+
// Extract usage from msg.usage or msg.message.usage
|
|
168
|
+
const usage = msg.usage ?? msg.message?.usage;
|
|
169
|
+
if (usage) {
|
|
170
|
+
events.push({
|
|
171
|
+
type: 'usage',
|
|
172
|
+
input_tokens: usage.input_tokens ?? 0,
|
|
173
|
+
output_tokens: usage.output_tokens ?? 0,
|
|
174
|
+
cache_creation_input_tokens: usage.cache_creation_input_tokens ?? 0,
|
|
175
|
+
cache_read_input_tokens: usage.cache_read_input_tokens ?? 0,
|
|
176
|
+
// model is injected by the run() loop via _modelForUsage tag on the event
|
|
177
|
+
_from_result: true,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return events;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (msg.type === 'tool_use') {
|
|
184
|
+
return [{ type: 'tool_use', tool: msg.name ?? msg.tool, input: msg.input ?? {} }];
|
|
185
|
+
}
|
|
186
|
+
if (msg.type === 'tool_use_summary') {
|
|
187
|
+
const output = (msg.result ?? msg.output ?? '');
|
|
188
|
+
return [{ type: 'tool_use_summary', summary: msg.summary, output: output ? output.slice(0, 2048) : undefined }];
|
|
189
|
+
}
|
|
190
|
+
if (msg.type === 'tool_progress') {
|
|
191
|
+
return [{ type: 'tool_progress', tool: msg.tool_name, elapsed: msg.elapsed_time_seconds }];
|
|
192
|
+
}
|
|
193
|
+
// Delta or text content
|
|
194
|
+
if (msg.delta?.text) return [{ type: 'assistant', content: msg.delta.text }];
|
|
195
|
+
if (msg.content && typeof msg.content === 'string') return [{ type: 'assistant', content: msg.content }];
|
|
196
|
+
// Pass through unknown message types as-is
|
|
197
|
+
return [msg];
|
|
198
|
+
}
|