@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,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`);
|