@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.
Files changed (181) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1014 -0
  3. package/bin/compose.js +1515 -0
  4. package/dist/assets/_baseUniq-CQwX6VLz.js +1 -0
  5. package/dist/assets/arc-SxJ2J1sh.js +1 -0
  6. package/dist/assets/architectureDiagram-Q4EWVU46-BykunY1F.js +36 -0
  7. package/dist/assets/blockDiagram-DXYQGD6D-ohAKBOUw.js +132 -0
  8. package/dist/assets/c4Diagram-AHTNJAMY-DBDC3ENB.js +10 -0
  9. package/dist/assets/channel-DGElom1e.js +1 -0
  10. package/dist/assets/chunk-4BX2VUAB-Cv93Z7uM.js +1 -0
  11. package/dist/assets/chunk-4TB4RGXK-DE0WBDkj.js +206 -0
  12. package/dist/assets/chunk-55IACEB6-CE1EXenG.js +1 -0
  13. package/dist/assets/chunk-EDXVE4YY-DA7Ana6H.js +1 -0
  14. package/dist/assets/chunk-FMBD7UC4-CTDIPA3p.js +15 -0
  15. package/dist/assets/chunk-OYMX7WX6-uGBaPaTX.js +231 -0
  16. package/dist/assets/chunk-QZHKN3VN-CYlnXuUO.js +1 -0
  17. package/dist/assets/chunk-YZCP3GAM-ojGkzcZK.js +1 -0
  18. package/dist/assets/classDiagram-6PBFFD2Q-KqWP9wWZ.js +1 -0
  19. package/dist/assets/classDiagram-v2-HSJHXN6E-KqWP9wWZ.js +1 -0
  20. package/dist/assets/clone-DUJKJXd7.js +1 -0
  21. package/dist/assets/cose-bilkent-S5V4N54A-Bktn9hL-.js +1 -0
  22. package/dist/assets/dagre-KV5264BT-DFaSzuRF.js +4 -0
  23. package/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  24. package/dist/assets/diagram-5BDNPKRD-DnfmDzEm.js +10 -0
  25. package/dist/assets/diagram-G4DWMVQ6-Bm8W9YnG.js +24 -0
  26. package/dist/assets/diagram-MMDJMWI5-B5-TSKvp.js +43 -0
  27. package/dist/assets/diagram-TYMM5635-ls4rqlky.js +24 -0
  28. package/dist/assets/erDiagram-SMLLAGMA-giG6WO-r.js +85 -0
  29. package/dist/assets/flowDiagram-DWJPFMVM-XvlUuz-7.js +162 -0
  30. package/dist/assets/ganttDiagram-T4ZO3ILL-hLBV57oV.js +292 -0
  31. package/dist/assets/gitGraphDiagram-UUTBAWPF-BHu3s_Gn.js +106 -0
  32. package/dist/assets/graph-D0Cfv00Y.js +1 -0
  33. package/dist/assets/index-CUd6pFGF.css +1 -0
  34. package/dist/assets/index-DReRlzZI.js +1144 -0
  35. package/dist/assets/infoDiagram-42DDH7IO-DbqRsOo3.js +2 -0
  36. package/dist/assets/init-Gi6I4Gst.js +1 -0
  37. package/dist/assets/ishikawaDiagram-UXIWVN3A-DnCdx7zb.js +70 -0
  38. package/dist/assets/journeyDiagram-VCZTEJTY-CfD7eNcP.js +139 -0
  39. package/dist/assets/kanban-definition-6JOO6SKY-BYaO9-mK.js +89 -0
  40. package/dist/assets/katex-DkKDou_j.js +257 -0
  41. package/dist/assets/layout-Bj72wOEB.js +1 -0
  42. package/dist/assets/linear-BRFo114D.js +1 -0
  43. package/dist/assets/min-GCHnKlJS.js +1 -0
  44. package/dist/assets/mindmap-definition-QFDTVHPH-n0PMebY4.js +96 -0
  45. package/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  46. package/dist/assets/pieDiagram-DEJITSTG-pN4CljHF.js +30 -0
  47. package/dist/assets/quadrantDiagram-34T5L4WZ-DNoAy8-D.js +7 -0
  48. package/dist/assets/requirementDiagram-MS252O5E-BhtY05PT.js +84 -0
  49. package/dist/assets/sankeyDiagram-XADWPNL6-B6AD-16A.js +10 -0
  50. package/dist/assets/sequenceDiagram-FGHM5R23-DShHM-uk.js +157 -0
  51. package/dist/assets/stateDiagram-FHFEXIEX-DMxn7HTo.js +1 -0
  52. package/dist/assets/stateDiagram-v2-QKLJ7IA2-o6PnCs4e.js +1 -0
  53. package/dist/assets/timeline-definition-GMOUNBTQ-Cdu6uq52.js +120 -0
  54. package/dist/assets/vennDiagram-DHZGUBPP-CpK29iRe.js +34 -0
  55. package/dist/assets/wardley-RL74JXVD-BQgSkdcO.js +162 -0
  56. package/dist/assets/wardleyDiagram-NUSXRM2D-DJHYev6O.js +20 -0
  57. package/dist/assets/xychartDiagram-5P7HB3ND-1d75pbaO.js +7 -0
  58. package/dist/index.html +30 -0
  59. package/lib/agent-chains.js +65 -0
  60. package/lib/agent-string.js +86 -0
  61. package/lib/budget-ledger.js +86 -0
  62. package/lib/build-all.js +162 -0
  63. package/lib/build-dag.js +120 -0
  64. package/lib/build-stream-writer.js +190 -0
  65. package/lib/build.js +2997 -0
  66. package/lib/capability-checker.js +53 -0
  67. package/lib/cert-inject.js +38 -0
  68. package/lib/cli-progress.js +483 -0
  69. package/lib/constants.js +69 -0
  70. package/lib/cross-layer-audit.js +84 -0
  71. package/lib/debug-discipline.js +173 -0
  72. package/lib/feature-json.js +106 -0
  73. package/lib/gate-prompt.js +291 -0
  74. package/lib/gate-tiers.js +194 -0
  75. package/lib/health-history.js +119 -0
  76. package/lib/health-score.js +227 -0
  77. package/lib/ideabox.js +570 -0
  78. package/lib/import.js +244 -0
  79. package/lib/migrate-roadmap.js +94 -0
  80. package/lib/model-pricing.js +67 -0
  81. package/lib/new.js +413 -0
  82. package/lib/pipeline-cli.js +489 -0
  83. package/lib/plan-parser.js +103 -0
  84. package/lib/qa-scoping.js +474 -0
  85. package/lib/questionnaire.js +200 -0
  86. package/lib/resolve-port.js +7 -0
  87. package/lib/result-normalizer.js +349 -0
  88. package/lib/review-lenses.js +166 -0
  89. package/lib/roadmap-gen.js +210 -0
  90. package/lib/roadmap-parser.js +176 -0
  91. package/lib/server-probe.js +23 -0
  92. package/lib/staleness.js +87 -0
  93. package/lib/step-prompt.js +260 -0
  94. package/lib/step-validator.js +49 -0
  95. package/lib/stratum-mcp-client.js +365 -0
  96. package/lib/team-flag.js +46 -0
  97. package/lib/test-bootstrap.js +401 -0
  98. package/lib/triage.js +274 -0
  99. package/lib/vision-writer.js +391 -0
  100. package/package.json +111 -0
  101. package/pipelines/bug-fix.stratum.yaml +230 -0
  102. package/pipelines/build.stratum.yaml +498 -0
  103. package/pipelines/content.stratum.yaml +112 -0
  104. package/pipelines/coverage-sweep.stratum.yaml +52 -0
  105. package/pipelines/refactor.stratum.yaml +169 -0
  106. package/pipelines/research.stratum.yaml +88 -0
  107. package/pipelines/review-fix.stratum.yaml +109 -0
  108. package/presets/team-feature.stratum.yaml +105 -0
  109. package/presets/team-research.stratum.yaml +108 -0
  110. package/presets/team-review.stratum.yaml +106 -0
  111. package/scripts/agent-activity-hook.sh +31 -0
  112. package/scripts/agent-error-hook.sh +28 -0
  113. package/scripts/analyze-orphans.mjs +50 -0
  114. package/scripts/find-orphans.mjs +26 -0
  115. package/scripts/fix-phases.mjs +49 -0
  116. package/scripts/generate-stratum-spec.mjs +137 -0
  117. package/scripts/import-roadmap.mjs +116 -0
  118. package/scripts/phase-audit.mjs +33 -0
  119. package/scripts/run-pipeline.mjs +314 -0
  120. package/scripts/session-end-hook.sh +18 -0
  121. package/scripts/session-start-hook.sh +38 -0
  122. package/scripts/vision-hook.sh +104 -0
  123. package/scripts/vision-track.mjs +554 -0
  124. package/scripts/wire-all-orphans.mjs +108 -0
  125. package/scripts/wire-orphans.mjs +164 -0
  126. package/server/activity-routes.js +123 -0
  127. package/server/agent-health.js +197 -0
  128. package/server/agent-hooks.js +102 -0
  129. package/server/agent-mcp.js +10 -0
  130. package/server/agent-registry.js +95 -0
  131. package/server/agent-server.js +290 -0
  132. package/server/agent-spawn.js +251 -0
  133. package/server/agent-templates.js +77 -0
  134. package/server/artifact-manager.js +247 -0
  135. package/server/artifact-templates/architecture.md +28 -0
  136. package/server/artifact-templates/blueprint.md +21 -0
  137. package/server/artifact-templates/design.md +36 -0
  138. package/server/artifact-templates/plan.md +25 -0
  139. package/server/artifact-templates/prd.md +43 -0
  140. package/server/artifact-templates/report.md +40 -0
  141. package/server/block-tracker.js +90 -0
  142. package/server/build-stream-bridge.js +502 -0
  143. package/server/coalescing-buffer.js +46 -0
  144. package/server/compose-mcp-tools.js +479 -0
  145. package/server/compose-mcp.js +324 -0
  146. package/server/connectors/agent-connector.js +78 -0
  147. package/server/connectors/claude-sdk-connector.js +198 -0
  148. package/server/connectors/codex-connector.js +240 -0
  149. package/server/connectors/connector-discovery.js +18 -0
  150. package/server/connectors/connector-runtime.js +13 -0
  151. package/server/connectors/opencode-connector.js +200 -0
  152. package/server/design-routes.js +540 -0
  153. package/server/design-session.js +161 -0
  154. package/server/feature-scan.js +593 -0
  155. package/server/file-watcher.js +284 -0
  156. package/server/find-root.js +29 -0
  157. package/server/graph-export.js +343 -0
  158. package/server/ideabox-cache.js +77 -0
  159. package/server/ideabox-routes.js +294 -0
  160. package/server/index.js +156 -0
  161. package/server/model-tiers.js +49 -0
  162. package/server/pipeline-routes.js +288 -0
  163. package/server/policy-evaluator.js +36 -0
  164. package/server/project-root.js +122 -0
  165. package/server/security.js +23 -0
  166. package/server/session-manager.js +403 -0
  167. package/server/session-routes.js +190 -0
  168. package/server/session-store.js +107 -0
  169. package/server/settings-routes.js +35 -0
  170. package/server/settings-store.js +234 -0
  171. package/server/stratum-api.js +102 -0
  172. package/server/stratum-client.js +192 -0
  173. package/server/stratum-sync.js +193 -0
  174. package/server/summarizer.js +139 -0
  175. package/server/supervisor.js +196 -0
  176. package/server/vision-routes.js +668 -0
  177. package/server/vision-server.js +393 -0
  178. package/server/vision-store.js +360 -0
  179. package/server/vision-utils.js +179 -0
  180. package/server/worktree-gc.js +137 -0
  181. package/templates/ROADMAP.md +46 -0
@@ -0,0 +1,554 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * vision-track — Agent bridge to the Vision Surface.
4
+ *
5
+ * Full CLI for creating, reading, updating, deleting, connecting,
6
+ * and querying vision board items from the embedded terminal.
7
+ */
8
+
9
+ import { fileURLToPath } from 'node:url';
10
+ import { dirname, resolve } from 'node:path';
11
+
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+
14
+ // Import validation constants from the server (single source of truth)
15
+ const { VALID_TYPES, VALID_STATUSES, VALID_CONNECTION_TYPES, VALID_PHASES } = await import(
16
+ resolve(__dirname, '..', 'server', 'vision-store.js')
17
+ );
18
+
19
+ const API = process.env.VISION_API || 'http://localhost:3001/api/vision';
20
+
21
+ // ---------------------------------------------------------------------------
22
+ // Helpers
23
+ // ---------------------------------------------------------------------------
24
+
25
+ async function apiCall(path, method = 'GET', body) {
26
+ const opts = { method, headers: { 'Content-Type': 'application/json' } };
27
+ if (body) opts.body = JSON.stringify(body);
28
+
29
+ let res;
30
+ try {
31
+ res = await fetch(`${API}${path}`, opts);
32
+ } catch (err) {
33
+ console.error(`Failed to reach server at ${API}. Is it running?`);
34
+ process.exit(1);
35
+ }
36
+
37
+ const data = await res.json();
38
+ if (!res.ok) {
39
+ console.error(`API error ${res.status}: ${data.error || JSON.stringify(data)}`);
40
+ process.exit(1);
41
+ }
42
+ return data;
43
+ }
44
+
45
+ function parseArgs(args) {
46
+ const result = { positional: [], flags: {}, connections: [] };
47
+ let i = 0;
48
+ while (i < args.length) {
49
+ if (args[i] === '--connects-to') {
50
+ result.connections.push(args[++i]);
51
+ i++;
52
+ } else if (args[i].startsWith('--')) {
53
+ const key = args[i].slice(2);
54
+ result.flags[key] = args[++i];
55
+ i++;
56
+ } else {
57
+ result.positional.push(args[i]);
58
+ i++;
59
+ }
60
+ }
61
+ return result;
62
+ }
63
+
64
+ function formatItem(i) {
65
+ return `${i.id} ${(i.status || '').padEnd(12)} ${(i.type || '').padEnd(10)} ${(i.phase || '-').padEnd(15)} ${i.title}`;
66
+ }
67
+
68
+ // ---------------------------------------------------------------------------
69
+ // Commands
70
+ // ---------------------------------------------------------------------------
71
+
72
+ async function createItem(parsed) {
73
+ const title = parsed.positional[0];
74
+ if (!title) {
75
+ console.error('Usage: vision-track create <title> [options]');
76
+ process.exit(1);
77
+ }
78
+
79
+ const type = parsed.flags.type || 'artifact';
80
+ const status = parsed.flags.status || 'planned';
81
+ const confidence = parseInt(parsed.flags.confidence || '0', 10);
82
+ const phase = parsed.flags.phase || undefined;
83
+ const description = parsed.flags.description || '';
84
+ const parentId = parsed.flags['parent-id'] || undefined;
85
+
86
+ // Validate before sending
87
+ if (!VALID_TYPES.includes(type)) {
88
+ console.error(`Invalid type: ${type}. Valid: ${VALID_TYPES.join(', ')}`);
89
+ process.exit(1);
90
+ }
91
+ if (!VALID_STATUSES.includes(status)) {
92
+ console.error(`Invalid status: ${status}. Valid: ${VALID_STATUSES.join(', ')}`);
93
+ process.exit(1);
94
+ }
95
+ if (phase && !VALID_PHASES.includes(phase)) {
96
+ console.error(`Invalid phase: ${phase}. Valid: ${VALID_PHASES.join(', ')}`);
97
+ process.exit(1);
98
+ }
99
+ if (confidence < 0 || confidence > 4) {
100
+ console.error('Confidence must be 0-4');
101
+ process.exit(1);
102
+ }
103
+
104
+ const body = { type, title, status, confidence, description };
105
+ if (phase) body.phase = phase;
106
+ if (parentId) body.parentId = parentId;
107
+ if (parsed.flags.files !== undefined) {
108
+ body.files = parsed.flags.files ? parsed.flags.files.split(',').map(f => f.trim()) : [];
109
+ }
110
+
111
+ const item = await apiCall('/items', 'POST', body);
112
+ console.error(`Created: ${item.id} (${title})`);
113
+
114
+ // Create connections
115
+ for (const conn of parsed.connections) {
116
+ const sepIdx = conn.lastIndexOf(':');
117
+ if (sepIdx === -1) {
118
+ console.error(`Invalid connection format: ${conn} (expected ID:TYPE)`);
119
+ continue;
120
+ }
121
+ const targetId = conn.slice(0, sepIdx);
122
+ const connType = conn.slice(sepIdx + 1);
123
+
124
+ if (!VALID_CONNECTION_TYPES.includes(connType)) {
125
+ console.error(`Invalid connection type: ${connType}. Valid: ${VALID_CONNECTION_TYPES.join(', ')}`);
126
+ continue;
127
+ }
128
+
129
+ await apiCall('/connections', 'POST', {
130
+ fromId: item.id,
131
+ toId: targetId,
132
+ type: connType,
133
+ });
134
+ console.error(` Connected: ${item.id} --[${connType}]--> ${targetId}`);
135
+ }
136
+
137
+ // Machine-readable output on stdout
138
+ process.stdout.write(item.id + '\n');
139
+ }
140
+
141
+ async function updateItem(parsed) {
142
+ const id = parsed.positional[0];
143
+ if (!id) {
144
+ console.error('Usage: vision-track update <id> [options]');
145
+ process.exit(1);
146
+ }
147
+
148
+ const updates = {};
149
+ if (parsed.flags.status) {
150
+ if (!VALID_STATUSES.includes(parsed.flags.status)) {
151
+ console.error(`Invalid status: ${parsed.flags.status}. Valid: ${VALID_STATUSES.join(', ')}`);
152
+ process.exit(1);
153
+ }
154
+ updates.status = parsed.flags.status;
155
+ }
156
+ if (parsed.flags.confidence !== undefined) {
157
+ updates.confidence = parseInt(parsed.flags.confidence, 10);
158
+ }
159
+ if (parsed.flags.phase) {
160
+ if (!VALID_PHASES.includes(parsed.flags.phase)) {
161
+ console.error(`Invalid phase: ${parsed.flags.phase}. Valid: ${VALID_PHASES.join(', ')}`);
162
+ process.exit(1);
163
+ }
164
+ updates.phase = parsed.flags.phase;
165
+ }
166
+ if (parsed.flags.title) updates.title = parsed.flags.title;
167
+ if (parsed.flags.description) updates.description = parsed.flags.description;
168
+ if (parsed.flags.files !== undefined) {
169
+ updates.files = parsed.flags.files ? parsed.flags.files.split(',').map(f => f.trim()) : [];
170
+ }
171
+ if (parsed.flags['add-files']) {
172
+ const current = await apiCall(`/items/${id}`);
173
+ const existing = current.files || [];
174
+ const adding = parsed.flags['add-files'].split(',').map(f => f.trim());
175
+ updates.files = [...new Set([...existing, ...adding])];
176
+ }
177
+
178
+ if (Object.keys(updates).length === 0) {
179
+ console.error('No updates specified');
180
+ process.exit(1);
181
+ }
182
+
183
+ const item = await apiCall(`/items/${id}`, 'PATCH', updates);
184
+ console.error(`Updated: ${item.id} (${item.title})`);
185
+ process.stdout.write(item.id + '\n');
186
+ }
187
+
188
+ async function getItem(parsed) {
189
+ const id = parsed.positional[0];
190
+ if (!id) {
191
+ console.error('Usage: vision-track get <id>');
192
+ process.exit(1);
193
+ }
194
+
195
+ // Try the single-item endpoint first; fall back to fetching all and filtering
196
+ let item, connections;
197
+ try {
198
+ const result = await apiCall(`/items/${id}`);
199
+ // If the endpoint returns item+connections (new server), use directly
200
+ if (result.id) {
201
+ connections = result.connections || [];
202
+ item = result;
203
+ } else {
204
+ throw new Error('fallback');
205
+ }
206
+ } catch {
207
+ // Fallback: fetch all items and filter client-side
208
+ const state = await apiCall('/items');
209
+ const allItems = state.items || [];
210
+ item = allItems.find(i => i.id === id);
211
+ if (!item) {
212
+ console.error(`Item not found: ${id}`);
213
+ process.exit(1);
214
+ }
215
+ connections = (state.connections || []).filter(
216
+ c => c.fromId === id || c.toId === id
217
+ );
218
+ }
219
+
220
+ console.log(`ID: ${item.id}`);
221
+ console.log(`Title: ${item.title}`);
222
+ console.log(`Type: ${item.type}`);
223
+ console.log(`Status: ${item.status}`);
224
+ console.log(`Phase: ${item.phase || '-'}`);
225
+ console.log(`Confidence: ${item.confidence}`);
226
+ console.log(`Description: ${item.description || '-'}`);
227
+ console.log(`Parent: ${item.parentId || '-'}`);
228
+ console.log(`Created: ${item.createdAt}`);
229
+ console.log(`Updated: ${item.updatedAt}`);
230
+ if (item.files && item.files.length > 0) {
231
+ console.log(`Files: ${item.files.join(', ')}`);
232
+ }
233
+
234
+ if (connections.length > 0) {
235
+ console.log(`\nConnections (${connections.length}):`);
236
+ for (const c of connections) {
237
+ const direction = c.fromId === item.id ? '-->' : '<--';
238
+ const otherId = c.fromId === item.id ? c.toId : c.fromId;
239
+ console.log(` ${c.id} ${direction} [${c.type}] ${otherId}`);
240
+ }
241
+ }
242
+ }
243
+
244
+ async function deleteItem(parsed) {
245
+ const id = parsed.positional[0];
246
+ if (!id) {
247
+ console.error('Usage: vision-track delete <id>');
248
+ process.exit(1);
249
+ }
250
+
251
+ await apiCall(`/items/${id}`, 'DELETE');
252
+ console.error(`Deleted: ${id}`);
253
+ }
254
+
255
+ async function connectItems(parsed) {
256
+ const fromId = parsed.positional[0];
257
+ const toId = parsed.positional[1];
258
+ const connType = parsed.flags.type;
259
+
260
+ if (!fromId || !toId) {
261
+ console.error('Usage: vision-track connect <fromId> <toId> --type <type>');
262
+ process.exit(1);
263
+ }
264
+ if (!connType) {
265
+ console.error(`Connection type required. Use --type <type>. Valid: ${VALID_CONNECTION_TYPES.join(', ')}`);
266
+ process.exit(1);
267
+ }
268
+ if (!VALID_CONNECTION_TYPES.includes(connType)) {
269
+ console.error(`Invalid connection type: ${connType}. Valid: ${VALID_CONNECTION_TYPES.join(', ')}`);
270
+ process.exit(1);
271
+ }
272
+
273
+ const conn = await apiCall('/connections', 'POST', { fromId, toId, type: connType });
274
+ console.error(`Connected: ${fromId} --[${connType}]--> ${toId}`);
275
+ process.stdout.write(conn.id + '\n');
276
+ }
277
+
278
+ async function disconnectItems(parsed) {
279
+ const connectionId = parsed.positional[0];
280
+ if (!connectionId) {
281
+ console.error('Usage: vision-track disconnect <connectionId>');
282
+ process.exit(1);
283
+ }
284
+
285
+ await apiCall(`/connections/${connectionId}`, 'DELETE');
286
+ console.error(`Disconnected: ${connectionId}`);
287
+ }
288
+
289
+ async function showStatus() {
290
+ // Compute summary client-side from /items — works with or without /summary endpoint
291
+ const state = await apiCall('/items');
292
+ const items = state.items || [];
293
+ const connections = state.connections || [];
294
+
295
+ const byPhase = {};
296
+ const byStatus = {};
297
+ const byType = {};
298
+ let totalConfidence = 0;
299
+ let confidenceCount = 0;
300
+ let openQuestions = 0;
301
+ let blockedItems = 0;
302
+
303
+ for (const item of items) {
304
+ const phase = item.phase || 'unassigned';
305
+ byPhase[phase] = (byPhase[phase] || 0) + 1;
306
+
307
+ const status = item.status || 'planned';
308
+ byStatus[status] = (byStatus[status] || 0) + 1;
309
+
310
+ const type = item.type || 'artifact';
311
+ byType[type] = (byType[type] || 0) + 1;
312
+
313
+ if (typeof item.confidence === 'number') {
314
+ totalConfidence += item.confidence;
315
+ confidenceCount++;
316
+ }
317
+
318
+ if (item.type === 'question' && item.status !== 'complete' && item.status !== 'killed') {
319
+ openQuestions++;
320
+ }
321
+
322
+ if (item.status === 'blocked') {
323
+ blockedItems++;
324
+ }
325
+ }
326
+
327
+ const avgConfidence = confidenceCount > 0
328
+ ? Math.round((totalConfidence / confidenceCount) * 100) / 100
329
+ : 0;
330
+
331
+ console.log(`=== Vision Surface Summary ===`);
332
+ console.log(`Total items: ${items.length} Connections: ${connections.length} Avg confidence: ${avgConfidence}`);
333
+ console.log(`Open questions: ${openQuestions} Blocked: ${blockedItems}`);
334
+
335
+ if (Object.keys(byPhase).length > 0) {
336
+ console.log(`\nBy phase:`);
337
+ for (const [phase, count] of Object.entries(byPhase).sort((a, b) => b[1] - a[1])) {
338
+ console.log(` ${phase.padEnd(18)} ${count}`);
339
+ }
340
+ }
341
+
342
+ if (Object.keys(byStatus).length > 0) {
343
+ console.log(`\nBy status:`);
344
+ for (const [status, count] of Object.entries(byStatus).sort((a, b) => b[1] - a[1])) {
345
+ console.log(` ${status.padEnd(18)} ${count}`);
346
+ }
347
+ }
348
+
349
+ if (Object.keys(byType).length > 0) {
350
+ console.log(`\nBy type:`);
351
+ for (const [type, count] of Object.entries(byType).sort((a, b) => b[1] - a[1])) {
352
+ console.log(` ${type.padEnd(18)} ${count}`);
353
+ }
354
+ }
355
+ }
356
+
357
+ async function showReady() {
358
+ const state = await apiCall('/items');
359
+ const items = state.items || [];
360
+ const connections = state.connections || [];
361
+
362
+ // Build set of items that are blocked by non-complete items
363
+ const blockedIds = new Set();
364
+ for (const conn of connections) {
365
+ if (conn.type === 'blocks') {
366
+ const blocker = items.find(i => i.id === conn.fromId);
367
+ if (blocker && blocker.status !== 'complete' && blocker.status !== 'killed') {
368
+ blockedIds.add(conn.toId);
369
+ }
370
+ }
371
+ }
372
+
373
+ // Ready = planned or ready status, not blocked
374
+ const ready = items.filter(i =>
375
+ (i.status === 'planned' || i.status === 'ready') &&
376
+ !blockedIds.has(i.id)
377
+ );
378
+
379
+ if (ready.length === 0) {
380
+ console.log('No ready items found.');
381
+ } else {
382
+ for (const i of ready) {
383
+ console.log(formatItem(i));
384
+ }
385
+ }
386
+ console.error(`${ready.length} ready item${ready.length !== 1 ? 's' : ''}`);
387
+ }
388
+
389
+ async function searchItems(parsed) {
390
+ const query = (parsed.positional[0] || '').toLowerCase();
391
+ const state = await apiCall('/items');
392
+ const items = state.items || [];
393
+
394
+ const matches = query
395
+ ? items.filter(i =>
396
+ i.title.toLowerCase().includes(query) ||
397
+ (i.description || '').toLowerCase().includes(query) ||
398
+ i.type.includes(query) ||
399
+ (i.phase || '').includes(query)
400
+ )
401
+ : items;
402
+
403
+ for (const i of matches) {
404
+ console.log(formatItem(i));
405
+ }
406
+ console.error(`${matches.length} item${matches.length !== 1 ? 's' : ''} found`);
407
+ }
408
+
409
+ async function listItems() {
410
+ const state = await apiCall('/items');
411
+ const items = state.items || [];
412
+ for (const i of items) {
413
+ console.log(formatItem(i));
414
+ }
415
+ console.error(`${items.length} item${items.length !== 1 ? 's' : ''} total`);
416
+ }
417
+
418
+ function showHelp() {
419
+ console.log(`vision-track — Agent bridge to the Vision Surface
420
+
421
+ COMMANDS
422
+
423
+ Items:
424
+ create <title> [options] Create a new item
425
+ get <id> Get item details + connections
426
+ update <id> [options] Update an item
427
+ delete <id> Delete an item and its connections
428
+ list List all items
429
+ search [query] Search items by title/description/type/phase
430
+
431
+ Connections:
432
+ connect <fromId> <toId> --type <type> Create a connection
433
+ disconnect <connectionId> Delete a connection
434
+
435
+ Queries:
436
+ status Board summary (counts by phase/status/type)
437
+ ready Items ready to work on (not blocked)
438
+ help Show this help
439
+
440
+ CREATE OPTIONS
441
+ --type TYPE ${VALID_TYPES.join(', ')}
442
+ --phase PHASE ${VALID_PHASES.join(', ')}
443
+ --status STATUS ${VALID_STATUSES.join(', ')}
444
+ --confidence N 0-4 (default: 0)
445
+ --description DESC Item description
446
+ --parent-id ID Parent item ID
447
+ --files <paths> Comma-separated file paths
448
+ --connects-to ID:TYPE Connection (repeatable)
449
+
450
+ UPDATE OPTIONS
451
+ --status STATUS New status
452
+ --confidence N New confidence (0-4)
453
+ --phase PHASE New phase
454
+ --title TITLE New title
455
+ --description DESC New description
456
+ --files <paths> Comma-separated file paths (replaces existing)
457
+ --add-files <paths> Comma-separated file paths (merges with existing)
458
+
459
+ CONNECTION TYPES
460
+ ${VALID_CONNECTION_TYPES.join(', ')}
461
+
462
+ EXAMPLES
463
+ # Create a spec in design phase
464
+ vision-track create "Auth flow spec" --type spec --phase design
465
+
466
+ # Create and connect to parent
467
+ vision-track create "Login UI" --type task --phase implementation \\
468
+ --connects-to abc123:supports
469
+
470
+ # Get item details
471
+ vision-track get abc123
472
+
473
+ # Update status and confidence
474
+ vision-track update abc123 --status complete --confidence 3
475
+
476
+ # Connect two existing items
477
+ vision-track connect item1 item2 --type informs
478
+
479
+ # Board overview
480
+ vision-track status
481
+
482
+ # What's ready to work on?
483
+ vision-track ready
484
+
485
+ # Delete an item
486
+ vision-track delete abc123
487
+
488
+ OUTPUT
489
+ stdout: machine-readable (IDs). stderr: human-readable messages.
490
+ Pipe-friendly: \`id=$(vision-track create "title" --type task)\``);
491
+ }
492
+
493
+ // ---------------------------------------------------------------------------
494
+ // Snapshot
495
+ // ---------------------------------------------------------------------------
496
+
497
+ async function takeSnapshot(parsed) {
498
+ const BASE = (process.env.VISION_API || 'http://localhost:3001/api/vision').replace('/api/vision', '');
499
+ const timeout = parsed.flags.timeout || '3000';
500
+ const noDom = parsed.flags['no-dom'] !== undefined;
501
+
502
+ let res;
503
+ try {
504
+ res = await fetch(`${BASE}/api/snapshot?timeout=${timeout}`);
505
+ } catch (err) {
506
+ console.error(`Failed to connect: ${err.message}`);
507
+ process.exit(1);
508
+ }
509
+
510
+ if (!res.ok) {
511
+ const data = await res.json().catch(() => ({}));
512
+ console.error(`Snapshot failed (${res.status}): ${data.error || res.statusText}`);
513
+ process.exit(1);
514
+ }
515
+
516
+ const snapshot = await res.json();
517
+
518
+ // Optionally strip DOM tree for compact output
519
+ if (noDom) delete snapshot.dom;
520
+
521
+ console.log(JSON.stringify(snapshot, null, 2));
522
+ }
523
+
524
+ // ---------------------------------------------------------------------------
525
+ // Main
526
+ // ---------------------------------------------------------------------------
527
+
528
+ const args = process.argv.slice(2);
529
+ const command = args[0];
530
+ const rest = args.slice(1);
531
+
532
+ if (!command || command === '--help' || command === '-h' || command === 'help') {
533
+ showHelp();
534
+ process.exit(0);
535
+ }
536
+
537
+ const parsed = parseArgs(rest);
538
+
539
+ switch (command) {
540
+ case 'create': await createItem(parsed); break;
541
+ case 'get': await getItem(parsed); break;
542
+ case 'update': await updateItem(parsed); break;
543
+ case 'delete': await deleteItem(parsed); break;
544
+ case 'connect': await connectItems(parsed); break;
545
+ case 'disconnect': await disconnectItems(parsed); break;
546
+ case 'status': await showStatus(); break;
547
+ case 'ready': await showReady(); break;
548
+ case 'search': await searchItems(parsed); break;
549
+ case 'list': await listItems(); break;
550
+ case 'snapshot': await takeSnapshot(parsed); break;
551
+ default:
552
+ console.error(`Unknown command: ${command}. Run 'vision-track help' for usage.`);
553
+ process.exit(1);
554
+ }
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from 'fs';
3
+ import { execFileSync } from 'child_process';
4
+
5
+ /*
6
+ * Wire all orphaned items to their parent feature/track.
7
+ * Mapping is manual — each orphan gets a parent and connection type.
8
+ */
9
+
10
+ // Parent IDs
11
+ const BOOTSTRAP = '3743457d-0fad-4d42-9361-e1a4670b1688';
12
+ const PIPELINE = '404db70c-62ec-4a1f-85bb-bbb5563e3298';
13
+ const VISIBILITY = '2c54bedf-313a-4e03-bd53-41ff84c39c45';
14
+ const DOGFOODING = 'e3fdc74d-b640-4968-9ec4-8f9153ba6c7d';
15
+ const FOUNDATION = '0bd89667-64d5-4a81-ac4c-d252225bb769';
16
+ const TERMINAL = 'c1e35905-eb7b-4abf-8b2a-63ef37789060';
17
+ const VIEWS = '1d0b7445-6253-4581-9505-b18c1c721978';
18
+ const THEME = 'c0cd61ae-bb9c-4a9b-afca-bd8cf1dcef06';
19
+ const ONTOLOGY = '44bd4826-b86d-4342-9041-883eb50f974c';
20
+ const VT_ENHANCE = '97d4f5b4-e7c7-4c7c-9997-e01fe426c331';
21
+ const DRILLDOWN = '9ad7174c-2d1d-456a-a60c-86b18ca92304';
22
+ const PERSISTENCE = '2083f287-8a22-4f0e-9072-b5cd39dce0c3';
23
+ const DISCOVERY_SUP = '29ba34a4-be37-4e2e-ba6d-f30a5cda38f2';
24
+ const CONNECTORS = '4675b520-3f21-4a48-b081-fcdb0c1ddfea';
25
+ const STANDALONE = '730004ed-1372-420d-9011-657b0a334c4a';
26
+ const BREADCRUMBS = '90c892e3-59d7-44ba-872f-0d62898a699e';
27
+ const FORGE_LOOP = 'f66a5788-0d96-41f3-9660-e85ae0d821c5';
28
+ const AGENT_MON = 'b81d24e2-9632-4e2a-967e-8aa0742740a7';
29
+
30
+ // [orphanId, parentId, connectionType]
31
+ const wiring = [
32
+ // Core decisions → Foundation & Discovery
33
+ ['5632a099-62f6-4f38-9d20-992a3072f369', FOUNDATION, 'supports'],
34
+ ['b16f899e-8e21-4d52-8ce6-2aecada67084', FOUNDATION, 'supports'],
35
+ ['9e90b79c-ce89-4fdf-a831-0be0c0677594', FOUNDATION, 'supports'],
36
+ ['f4702d82-992b-4027-a463-5cce923f9301', FOUNDATION, 'supports'],
37
+ ['b323d38c-0950-46ed-a64f-beefb9b66f72', FOUNDATION, 'supports'],
38
+ ['a11b2fe3-2b40-450c-804b-7c78e07813ed', FOUNDATION, 'supports'],
39
+ ['7853f84d-b121-4b6a-9849-5ae2e6576732', CONNECTORS, 'supports'],
40
+ ['cadd6004-8750-409c-857e-703061fd3f95', BOOTSTRAP, 'supports'],
41
+
42
+ // Ideas → various
43
+ ['2662567c-deac-4eec-9084-19d9756c093d', VISIBILITY, 'supports'],
44
+ ['bdbdd363-894d-48ad-be45-1680d1095c37', PIPELINE, 'supports'],
45
+ ['d45f80f0-43d5-48a2-bc80-83231f205c0c', PIPELINE, 'supports'],
46
+ ['83bdd6a9-c0aa-4c42-bc74-f65d2e320d0d', FOUNDATION, 'supports'],
47
+ ['1e555432-ce1c-4279-820c-0ba4fad53b2b', FOUNDATION, 'supports'],
48
+ ['orphan-persistence-model', PERSISTENCE, 'supports'],
49
+ ['orphan-voice-input', DISCOVERY_SUP, 'supports'],
50
+ ['orphan-mobile-view', STANDALONE, 'supports'],
51
+ ['idea-testing-strategy', PIPELINE, 'supports'],
52
+ ['e3091928-0b0d-4656-984b-8a2764eda72a', DRILLDOWN, 'supports'],
53
+
54
+ // Questions → various
55
+ ['37a333aa-1f0a-4f0d-8cab-c349f06a08cd', FOUNDATION, 'supports'],
56
+ ['5a836beb-afd4-4d21-b36d-1fe7c8a21eb8', FOUNDATION, 'supports'],
57
+ ['417e5c2c-992a-40e1-8ba1-3bc7e25564b0', VISIBILITY, 'supports'],
58
+ ['c38aa3ae-febb-4370-81ca-b22163825d10', ONTOLOGY, 'supports'],
59
+ ['question-release-strategy', STANDALONE, 'supports'],
60
+
61
+ // Threads → various
62
+ ['c3e13f70-2740-4558-997e-7f1dcb7a39c8', BOOTSTRAP, 'supports'],
63
+ ['86878969-e6cf-4002-8f50-b90122acf9ab', ONTOLOGY, 'supports'],
64
+ ['19f05faa-d50d-4697-87d2-c77b5d710a26', PIPELINE, 'supports'],
65
+ ['d28e17d8-b9e7-4484-84ec-27f83568371d', FOUNDATION, 'supports'],
66
+ ['thread-impl-bootstrap', BOOTSTRAP, 'supports'],
67
+
68
+ // Artifacts → various
69
+ ['09bcd9f9-316d-411b-9b66-22a2ddfa093e', VIEWS, 'supports'],
70
+ ['f29ec601-25ec-47c7-b5bb-6226512af135', ONTOLOGY, 'supports'],
71
+ ['8d3b67d7-122e-4231-a9ba-cb5ced9c92a7', BOOTSTRAP, 'supports'],
72
+
73
+ // Decisions (visibility) → Agent Visibility
74
+ ['dec-visibility-01', VISIBILITY, 'supports'],
75
+ ['dec-visibility-02', VISIBILITY, 'supports'],
76
+ ['dec-visibility-03', VISIBILITY, 'supports'],
77
+ ['dec-visibility-04', VISIBILITY, 'supports'],
78
+
79
+ // Specs → various
80
+ ['bf0f895c-f42e-4cbe-8322-101a4b6b8dff', VIEWS, 'supports'],
81
+ ['8d091d8f-10ba-41c1-8867-41032c4a60ef', VISIBILITY, 'supports'],
82
+ ['4b8c6dd8-cfb3-4d60-92b6-d745d7a4d4ce', VT_ENHANCE, 'supports'],
83
+ ['46481456-5233-4dc1-974e-17ff6c6c15d0', VT_ENHANCE, 'supports'],
84
+ ['6d1282a3-a64e-45c5-9c8c-fd2f46b1adc9', DRILLDOWN, 'supports'],
85
+
86
+ // Decisions (ontology/realignment) → Product Ontology
87
+ ['14d18445-158a-4c36-a352-8dbc7544c62f', ONTOLOGY, 'supports'],
88
+ ['c77d78ed-dfa3-4222-89e6-b3694335c6bf', ONTOLOGY, 'supports'],
89
+ ['13cfc103-0eaa-4c06-be0a-998174f49349', ONTOLOGY, 'supports'],
90
+
91
+ // Decisions (forge-loop) → Forge-Loop track
92
+ ['695aa0ff-6b48-4ddc-b19e-3d470a269c4e', FORGE_LOOP, 'supports'],
93
+ ['4bb32a05-f128-4822-976e-ccc96eb4c3ec', FORGE_LOOP, 'supports'],
94
+ ];
95
+
96
+ let success = 0, fail = 0;
97
+ for (const [fromId, toId, type] of wiring) {
98
+ try {
99
+ execFileSync('node', ['scripts/vision-track.mjs', 'connect', fromId, toId, '--type', type], { stdio: 'pipe' });
100
+ success++;
101
+ process.stdout.write('.');
102
+ } catch (e) {
103
+ fail++;
104
+ process.stdout.write('x');
105
+ console.error(`\n FAIL: ${fromId} -> ${toId}: ${e.stderr?.toString().trim()}`);
106
+ }
107
+ }
108
+ console.log(`\n\nDone: ${success} wired, ${fail} failed`);