buildanything 2.0.0 → 2.1.1

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 (115) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +9 -1
  3. package/README.md +57 -61
  4. package/agents/a11y-architect.md +2 -0
  5. package/agents/briefing-officer.md +172 -0
  6. package/agents/business-model.md +14 -12
  7. package/agents/code-architect.md +6 -1
  8. package/agents/code-reviewer.md +3 -2
  9. package/agents/code-simplifier.md +12 -4
  10. package/agents/design-brand-guardian.md +19 -0
  11. package/agents/design-critic.md +16 -11
  12. package/agents/design-inclusive-visuals-specialist.md +2 -0
  13. package/agents/design-ui-designer.md +17 -0
  14. package/agents/design-ux-architect.md +15 -0
  15. package/agents/design-ux-researcher.md +102 -7
  16. package/agents/engineering-ai-engineer.md +2 -0
  17. package/agents/engineering-backend-architect.md +2 -0
  18. package/agents/engineering-data-engineer.md +2 -0
  19. package/agents/engineering-devops-automator.md +2 -0
  20. package/agents/engineering-frontend-developer.md +13 -0
  21. package/agents/engineering-mobile-app-builder.md +2 -0
  22. package/agents/engineering-rapid-prototyper.md +15 -2
  23. package/agents/engineering-security-engineer.md +2 -0
  24. package/agents/engineering-senior-developer.md +13 -0
  25. package/agents/engineering-sre.md +2 -0
  26. package/agents/engineering-technical-writer.md +2 -0
  27. package/agents/feature-intel.md +8 -7
  28. package/agents/ios-app-review-guardian.md +2 -0
  29. package/agents/ios-foundation-models-specialist.md +2 -0
  30. package/agents/ios-product-reality-auditor.md +292 -0
  31. package/agents/ios-storekit-specialist.md +2 -0
  32. package/agents/ios-swift-architect.md +1 -0
  33. package/agents/ios-swift-search.md +1 -0
  34. package/agents/ios-swift-ui-design.md +7 -4
  35. package/agents/marketing-app-store-optimizer.md +2 -0
  36. package/agents/planner.md +6 -1
  37. package/agents/pr-test-analyzer.md +3 -2
  38. package/agents/product-feedback-synthesizer.md +62 -0
  39. package/agents/product-owner.md +163 -0
  40. package/agents/product-reality-auditor.md +216 -0
  41. package/agents/product-spec-writer.md +176 -0
  42. package/agents/refactor-cleaner.md +9 -1
  43. package/agents/security-reviewer.md +2 -1
  44. package/agents/silent-failure-hunter.md +2 -1
  45. package/agents/swift-build-resolver.md +2 -0
  46. package/agents/swift-reviewer.md +2 -1
  47. package/agents/tech-feasibility.md +5 -3
  48. package/agents/testing-api-tester.md +2 -0
  49. package/agents/testing-evidence-collector.md +24 -0
  50. package/agents/testing-performance-benchmarker.md +2 -0
  51. package/agents/testing-reality-checker.md +2 -1
  52. package/agents/visual-research.md +7 -5
  53. package/bin/adapters/scribe-tool.ts +4 -2
  54. package/bin/adapters/write-lease-tool.ts +1 -1
  55. package/bin/buildanything-runtime.ts +20 -107
  56. package/bin/graph-index.js +24 -0
  57. package/bin/graph-index.ts +340 -0
  58. package/bin/mcp-servers/graph-mcp.js +26 -0
  59. package/bin/mcp-servers/graph-mcp.ts +481 -0
  60. package/bin/mcp-servers/orchestrator-mcp.js +26 -0
  61. package/bin/mcp-servers/orchestrator-mcp.ts +361 -0
  62. package/bin/setup.js +272 -111
  63. package/commands/build.md +371 -158
  64. package/commands/idea-sweep.md +2 -2
  65. package/commands/setup.md +15 -4
  66. package/commands/ux-review.md +3 -3
  67. package/commands/verify.md +3 -0
  68. package/docs/migration/phase-graph.yaml +573 -157
  69. package/hooks/design-md-lint +4 -0
  70. package/hooks/design-md-lint.ts +295 -0
  71. package/hooks/pre-tool-use.ts +37 -6
  72. package/hooks/record-mode-transitions.ts +63 -6
  73. package/hooks/subagent-start.ts +3 -2
  74. package/package.json +3 -1
  75. package/protocols/agent-prompt-authoring.md +165 -0
  76. package/protocols/architecture-schema.md +10 -3
  77. package/protocols/cleanup.md +4 -0
  78. package/protocols/decision-log.md +8 -4
  79. package/protocols/design-md-authoring.md +520 -0
  80. package/protocols/design-md-spec.md +362 -0
  81. package/protocols/fake-data-detector.md +1 -1
  82. package/protocols/ios-fake-data-detector.md +65 -0
  83. package/protocols/ios-phase-branches.md +112 -27
  84. package/protocols/launch-readiness.md +9 -5
  85. package/protocols/metric-loop.md +1 -1
  86. package/protocols/page-spec-schema.md +234 -0
  87. package/protocols/product-spec-schema.md +354 -0
  88. package/protocols/sprint-tasks-schema.md +53 -0
  89. package/protocols/state-schema.json +38 -3
  90. package/protocols/state-schema.md +32 -2
  91. package/protocols/verify.md +29 -1
  92. package/protocols/web-phase-branches.md +234 -64
  93. package/skills/ios/ios-bootstrap/SKILL.md +1 -1
  94. package/src/graph/ids.ts +86 -0
  95. package/src/graph/index.ts +32 -0
  96. package/src/graph/parser/architecture.ts +603 -0
  97. package/src/graph/parser/component-manifest.ts +268 -0
  98. package/src/graph/parser/decisions-jsonl.ts +407 -0
  99. package/src/graph/parser/design-md-pass2.ts +253 -0
  100. package/src/graph/parser/design-md.ts +477 -0
  101. package/src/graph/parser/page-spec.ts +496 -0
  102. package/src/graph/parser/product-spec.ts +930 -0
  103. package/src/graph/parser/screenshot.ts +342 -0
  104. package/src/graph/parser/sprint-tasks.ts +317 -0
  105. package/src/graph/storage/index.ts +1154 -0
  106. package/src/graph/types.ts +432 -0
  107. package/src/graph/util/dhash.ts +84 -0
  108. package/src/lrr/aggregator.ts +105 -10
  109. package/src/orchestrator/hooks/context-header.ts +34 -10
  110. package/src/orchestrator/hooks/token-accounting.ts +25 -14
  111. package/src/orchestrator/mcp/cycle-counter.ts +2 -1
  112. package/src/orchestrator/mcp/scribe.ts +27 -16
  113. package/src/orchestrator/mcp/write-lease.ts +30 -13
  114. package/src/orchestrator/phase4-shared-context.ts +20 -4
  115. package/protocols/visual-dna.md +0 -185
@@ -0,0 +1,361 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Orchestrator stdio MCP server.
4
+ *
5
+ * This is the stdio bridge that Claude-Code-hosted sessions auto-start via
6
+ * plugin.json's `mcpServers` block. It exposes the four orchestrator
7
+ * toolsets — state_save, write_lease, cycle_counter, scribe — as a single
8
+ * MCP server with 10 tools total.
9
+ *
10
+ * The Agent SDK code path (bin/buildanything-runtime.ts) registers the same
11
+ * underlying handlers in-process via createSdkMcpServer; this file owns the
12
+ * stdio path. Both import the same handler implementations from
13
+ * src/orchestrator/mcp/*.ts — the bifurcation is only at the transport layer.
14
+ *
15
+ * Tools exposed:
16
+ * state-save: state_save, state_read, verify_integrity
17
+ * write-lease: acquire_write_lease, release_write_lease, list_write_leases
18
+ * cycle-counter: cycle_counter_check, clear_in_flight_edge, handle_stale_edge
19
+ * scribe: scribe_decision
20
+ */
21
+
22
+ import { resolve } from "node:path";
23
+ import { z } from "zod";
24
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
25
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
26
+
27
+ import {
28
+ stateSave,
29
+ stateRead,
30
+ verifyIntegrity,
31
+ } from "../../src/orchestrator/mcp/state-save.js";
32
+ import {
33
+ acquireWriteLease,
34
+ releaseLease,
35
+ getActiveLeases,
36
+ init as initWriteLease,
37
+ } from "../../src/orchestrator/mcp/write-lease.js";
38
+ import {
39
+ cycleCounterCheck,
40
+ clearInFlightEdge,
41
+ handleStaleEdge,
42
+ type CounterState,
43
+ } from "../../src/orchestrator/mcp/cycle-counter.js";
44
+ import { scribeDecision, loadCounters } from "../../src/orchestrator/mcp/scribe.js";
45
+
46
+ // ---------------------------------------------------------------------------
47
+ // Helpers
48
+ // ---------------------------------------------------------------------------
49
+
50
+ function okResult(payload: unknown) {
51
+ return {
52
+ content: [{ type: "text" as const, text: JSON.stringify(payload) }],
53
+ };
54
+ }
55
+
56
+ function errResult(toolName: string, err: unknown) {
57
+ const message = err instanceof Error ? err.message : String(err);
58
+ return {
59
+ isError: true,
60
+ content: [
61
+ { type: "text" as const, text: `${toolName} error: ${message}` },
62
+ ],
63
+ };
64
+ }
65
+
66
+ // ---------------------------------------------------------------------------
67
+ // Zod input shapes (mirror bin/adapters/*-tool.ts verbatim)
68
+ // ---------------------------------------------------------------------------
69
+
70
+ const stateSaveShape = {
71
+ path: z.string().min(1),
72
+ state: z.record(z.string(), z.unknown()),
73
+ };
74
+
75
+ const stateReadShape = {
76
+ path: z.string().min(1),
77
+ };
78
+
79
+ const verifyIntegrityShape = {
80
+ path: z.string().min(1),
81
+ expected_sha256: z.string().min(1),
82
+ };
83
+
84
+ const acquireLeaseShape = {
85
+ task_id: z.string().min(1),
86
+ file_paths: z.array(z.string().min(1)).min(1),
87
+ };
88
+
89
+ const releaseLeaseShape = {
90
+ task_id: z.string().min(1),
91
+ };
92
+
93
+ const listLeasesShape = {} as const;
94
+
95
+ const inFlightEdgeSchema = z.object({
96
+ decision_id: z.string().min(1),
97
+ target_phase: z.string().min(1),
98
+ counter_value: z.number().int().nonnegative(),
99
+ started_at: z.string().min(1),
100
+ });
101
+
102
+ const counterStateSchema = z.object({
103
+ backward_routing_count: z.record(z.string(), z.number().int().nonnegative()),
104
+ backward_routing_count_by_target_phase: z.record(z.string(), z.number().int().nonnegative()),
105
+ in_flight_backward_edge: inFlightEdgeSchema.optional(),
106
+ });
107
+
108
+ const cycleCheckShape = {
109
+ state: counterStateSchema,
110
+ input: z.object({
111
+ decision_id: z.string().min(1),
112
+ target_phase: z.string().min(1),
113
+ }),
114
+ max_cycles: z.number().int().positive().optional(),
115
+ };
116
+
117
+ const clearEdgeShape = {
118
+ state: counterStateSchema,
119
+ };
120
+
121
+ const staleEdgeShape = {
122
+ state: counterStateSchema,
123
+ threshold_ms: z.number().int().positive().optional(),
124
+ };
125
+
126
+ const rejectedAlternativeSchema = z.object({
127
+ approach: z.string().min(1),
128
+ reason: z.string().min(1),
129
+ revisit_criterion: z.string().min(1),
130
+ });
131
+
132
+ const scribeShape = {
133
+ phase: z.string().min(1),
134
+ summary: z.string().min(1),
135
+ decided_by: z.string().min(1),
136
+ impact_level: z.enum(["low", "medium", "high", "critical"]),
137
+ chosen_approach: z.string().min(1),
138
+ rejected_alternatives: z.array(rejectedAlternativeSchema).max(3).optional(),
139
+ ref: z
140
+ .string()
141
+ .min(1)
142
+ .regex(/^[a-zA-Z0-9_\-./]+\.(md|json|jsonl|yaml|yml)(#[a-zA-Z0-9_\-/.]+)?$/),
143
+ };
144
+
145
+ // ---------------------------------------------------------------------------
146
+ // Server registration
147
+ // ---------------------------------------------------------------------------
148
+
149
+ const BUILD_STATE_PATH_REL = "docs/plans/.build-state.json";
150
+ const DECISIONS_PATH_REL = "docs/plans/decisions.jsonl";
151
+
152
+ function registerTools(server: McpServer, decisionsPath: string): void {
153
+ // --- state_save toolset ------------------------------------------------
154
+
155
+ server.registerTool(
156
+ "state_save",
157
+ {
158
+ description:
159
+ "Atomically persist state to a JSON file via write-to-.tmp + fsync + rename. Returns {success, path, sha256, bytesWritten}. Single writer for .build-state.json.",
160
+ inputSchema: stateSaveShape,
161
+ },
162
+ async (args) => {
163
+ try {
164
+ const result = stateSave(args.path, args.state as Record<string, unknown>);
165
+ return okResult(result);
166
+ } catch (err) {
167
+ return errResult("state_save", err);
168
+ }
169
+ },
170
+ );
171
+
172
+ server.registerTool(
173
+ "state_read",
174
+ {
175
+ description:
176
+ "Read state from a JSON file and compute its SHA-256 checksum. Returns {state, sha256}.",
177
+ inputSchema: stateReadShape,
178
+ },
179
+ async (args) => {
180
+ try {
181
+ return okResult(stateRead(args.path));
182
+ } catch (err) {
183
+ return errResult("state_read", err);
184
+ }
185
+ },
186
+ );
187
+
188
+ server.registerTool(
189
+ "verify_integrity",
190
+ {
191
+ description:
192
+ "Verify a state file's SHA-256 matches the expected checksum. Returns {matches:boolean}.",
193
+ inputSchema: verifyIntegrityShape,
194
+ },
195
+ async (args) => {
196
+ try {
197
+ const matches = verifyIntegrity(args.path, args.expected_sha256);
198
+ return okResult({ matches });
199
+ } catch (err) {
200
+ return errResult("verify_integrity", err);
201
+ }
202
+ },
203
+ );
204
+
205
+ // --- write_lease toolset ----------------------------------------------
206
+
207
+ server.registerTool(
208
+ "acquire_write_lease",
209
+ {
210
+ description:
211
+ "Acquire an exclusive write lease on one or more file paths for a task. Returns granted:true with the lease, or granted:false with conflict details when another task holds an overlapping lease.",
212
+ inputSchema: acquireLeaseShape,
213
+ },
214
+ async (args) => {
215
+ try {
216
+ const result = await acquireWriteLease(args.task_id, args.file_paths);
217
+ return okResult(result);
218
+ } catch (err) {
219
+ return errResult("acquire_write_lease", err);
220
+ }
221
+ },
222
+ );
223
+
224
+ server.registerTool(
225
+ "release_write_lease",
226
+ {
227
+ description:
228
+ "Release all write leases held by a task. Called by SubagentStop hook on dispatch return. Returns released:true if a lease existed, false otherwise.",
229
+ inputSchema: releaseLeaseShape,
230
+ },
231
+ async (args) => {
232
+ try {
233
+ const released = releaseLease(args.task_id);
234
+ return okResult({ released });
235
+ } catch (err) {
236
+ return errResult("release_write_lease", err);
237
+ }
238
+ },
239
+ );
240
+
241
+ server.registerTool(
242
+ "list_write_leases",
243
+ {
244
+ description:
245
+ "List all active write leases (holder, paths, acquired_at). Read-only; intended for orchestrator diagnostics and .build-state.json persistence snapshots.",
246
+ inputSchema: listLeasesShape,
247
+ },
248
+ async () => {
249
+ try {
250
+ const leases = getActiveLeases();
251
+ return okResult({ leases });
252
+ } catch (err) {
253
+ return errResult("list_write_leases", err);
254
+ }
255
+ },
256
+ );
257
+
258
+ // --- cycle_counter toolset --------------------------------------------
259
+
260
+ server.registerTool(
261
+ "cycle_counter_check",
262
+ {
263
+ description:
264
+ "Check and increment backward-routing counters (per-decision + per-target-phase). Returns {action: allow|escalate_to_user, decision_count, phase_count, state} where state is the mutated CounterState to persist via state_save.",
265
+ inputSchema: cycleCheckShape,
266
+ },
267
+ async (args) => {
268
+ try {
269
+ const state = args.state as CounterState;
270
+ const result = cycleCounterCheck(state, args.input, args.max_cycles);
271
+ return okResult({ ...result, state });
272
+ } catch (err) {
273
+ return errResult("cycle_counter_check", err);
274
+ }
275
+ },
276
+ );
277
+
278
+ server.registerTool(
279
+ "clear_in_flight_edge",
280
+ {
281
+ description:
282
+ "Clear the in_flight_backward_edge field (called by target phase on re-entry). Returns {state} for the caller to persist via state_save.",
283
+ inputSchema: clearEdgeShape,
284
+ },
285
+ async (args) => {
286
+ try {
287
+ const state = args.state as CounterState;
288
+ clearInFlightEdge(state);
289
+ return okResult({ state });
290
+ } catch (err) {
291
+ return errResult("clear_in_flight_edge", err);
292
+ }
293
+ },
294
+ );
295
+
296
+ server.registerTool(
297
+ "handle_stale_edge",
298
+ {
299
+ description:
300
+ "On --resume, if in_flight_backward_edge is older than threshold_ms (default 60000), decrement both counters and clear the edge. Returns {cleaned:boolean, state}.",
301
+ inputSchema: staleEdgeShape,
302
+ },
303
+ async (args) => {
304
+ try {
305
+ const state = args.state as CounterState;
306
+ const cleaned = handleStaleEdge(state, args.threshold_ms);
307
+ return okResult({ cleaned, state });
308
+ } catch (err) {
309
+ return errResult("handle_stale_edge", err);
310
+ }
311
+ },
312
+ );
313
+
314
+ // --- scribe toolset ----------------------------------------------------
315
+
316
+ server.registerTool(
317
+ "scribe_decision",
318
+ {
319
+ description:
320
+ "Append a decision row to docs/plans/decisions.jsonl. Single-writer per migration plan; validates against decisions.schema.json.",
321
+ inputSchema: scribeShape,
322
+ },
323
+ async (args) => {
324
+ try {
325
+ const row = scribeDecision(args, decisionsPath);
326
+ return okResult(row);
327
+ } catch (err) {
328
+ return errResult("scribe_decision", err);
329
+ }
330
+ },
331
+ );
332
+ }
333
+
334
+ async function main(): Promise<void> {
335
+ const cwd = process.cwd();
336
+ const buildStatePath = resolve(cwd, BUILD_STATE_PATH_REL);
337
+ const decisionsPath = resolve(cwd, DECISIONS_PATH_REL);
338
+
339
+ // Hydrate in-memory stores from disk so they match persisted state across
340
+ // restarts. Mirrors bin/buildanything-runtime.ts:235 (write-lease) and
341
+ // bin/adapters/scribe-tool.ts:40 (scribe counters).
342
+ initWriteLease(buildStatePath);
343
+ loadCounters(decisionsPath);
344
+
345
+ const server = new McpServer(
346
+ { name: "buildanything-orchestrator", version: "0.1.0" },
347
+ { capabilities: { tools: {} } },
348
+ );
349
+
350
+ registerTools(server, decisionsPath);
351
+
352
+ const transport = new StdioServerTransport();
353
+ await server.connect(transport);
354
+ }
355
+
356
+ main().catch((err) => {
357
+ process.stderr.write(
358
+ `[orchestrator-mcp] fatal: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}\n`,
359
+ );
360
+ process.exit(1);
361
+ });