gswd 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/agents/gswd/architecture-drafter.md +70 -0
- package/agents/gswd/brainstorm-alternatives.md +60 -0
- package/agents/gswd/devils-advocate.md +57 -0
- package/agents/gswd/icp-persona.md +58 -0
- package/agents/gswd/integrations-checker.md +68 -0
- package/agents/gswd/journey-mapper.md +69 -0
- package/agents/gswd/market-researcher.md +54 -0
- package/agents/gswd/positioning.md +54 -0
- package/bin/gswd-tools.cjs +716 -0
- package/lib/audit.ts +959 -0
- package/lib/bootstrap.ts +617 -0
- package/lib/compile.ts +940 -0
- package/lib/config.ts +164 -0
- package/lib/imagine-agents.ts +154 -0
- package/lib/imagine-gate.ts +156 -0
- package/lib/imagine-input.ts +242 -0
- package/lib/imagine-synthesis.ts +402 -0
- package/lib/imagine.ts +433 -0
- package/lib/parse.ts +196 -0
- package/lib/render.ts +200 -0
- package/lib/specify-agents.ts +332 -0
- package/lib/specify-journeys.ts +410 -0
- package/lib/specify-nfr.ts +208 -0
- package/lib/specify-roles.ts +122 -0
- package/lib/specify.ts +773 -0
- package/lib/state.ts +305 -0
- package/package.json +26 -0
- package/templates/gswd/ARCHITECTURE.template.md +17 -0
- package/templates/gswd/AUDIT.template.md +31 -0
- package/templates/gswd/COMPETITION.template.md +18 -0
- package/templates/gswd/DECISIONS.template.md +18 -0
- package/templates/gswd/GTM.template.md +18 -0
- package/templates/gswd/ICP.template.md +18 -0
- package/templates/gswd/IMAGINE.template.md +24 -0
- package/templates/gswd/INTEGRATIONS.template.md +7 -0
- package/templates/gswd/JOURNEYS.template.md +7 -0
- package/templates/gswd/NFR.template.md +7 -0
- package/templates/gswd/PROJECT.template.md +21 -0
- package/templates/gswd/REQUIREMENTS.template.md +31 -0
- package/templates/gswd/ROADMAP.template.md +21 -0
- package/templates/gswd/SPEC.template.md +19 -0
- package/templates/gswd/STATE.template.md +15 -0
|
@@ -0,0 +1,716 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* GSWD Tools — CLI utility for GSWD workflow operations
|
|
5
|
+
*
|
|
6
|
+
* Usage: node gswd-tools.cjs <command> [args]
|
|
7
|
+
*
|
|
8
|
+
* Commands:
|
|
9
|
+
* init [--project-slug <slug>] Initialize STATE.json + config.json
|
|
10
|
+
* status Show per-stage status
|
|
11
|
+
* state read Read STATE.json
|
|
12
|
+
* state update-stage <stage> <status> Update stage status
|
|
13
|
+
* state checkpoint <workflow> <id> Write checkpoint
|
|
14
|
+
* config read Read config.json gswd section
|
|
15
|
+
* config merge [--overrides json] Merge gswd config
|
|
16
|
+
* render banner <name> Render stage banner
|
|
17
|
+
* render checkpoint <json> Render checkpoint box
|
|
18
|
+
* render next-up <json> Render Next Up block
|
|
19
|
+
* id-allocate <type> <agent> [size] Allocate ID range
|
|
20
|
+
* id-ranges [type] Get allocated ranges
|
|
21
|
+
* id-reset Reset ID registry
|
|
22
|
+
* parse ids <file> [--type X] Extract IDs from file
|
|
23
|
+
* parse validate-headings <file> <type> Validate headings
|
|
24
|
+
* commit <message> [--files f1 f2] Commit planning docs
|
|
25
|
+
* imagine [@idea.md] [--auto] [--skip-research] Run Imagine workflow
|
|
26
|
+
* specify [--auto] [--skip-agents] Run Specify workflow
|
|
27
|
+
* audit [--auto-fix] Run Audit workflow
|
|
28
|
+
* compile Run Compile workflow
|
|
29
|
+
* bootstrap [@idea.md] [--auto] [--resume] [--skip-research] [--policy <name>] Run full pipeline
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
'use strict';
|
|
33
|
+
|
|
34
|
+
const fs = require('fs');
|
|
35
|
+
const path = require('path');
|
|
36
|
+
const { execSync } = require('child_process');
|
|
37
|
+
|
|
38
|
+
// ─── Module Loading ──────────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Load a lib/ TypeScript module. Tries compiled dist/ first, falls back to
|
|
42
|
+
* tsx require hook for development.
|
|
43
|
+
*/
|
|
44
|
+
function loadModule(name) {
|
|
45
|
+
const distPath = path.join(__dirname, '..', 'dist', 'lib', `${name}.js`);
|
|
46
|
+
const srcPath = path.join(__dirname, '..', 'lib', `${name}.ts`);
|
|
47
|
+
|
|
48
|
+
// Try compiled dist/ first
|
|
49
|
+
try {
|
|
50
|
+
return require(distPath);
|
|
51
|
+
} catch (_e) {
|
|
52
|
+
// Fall through
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Try tsx require hook (development mode)
|
|
56
|
+
try {
|
|
57
|
+
// Register tsx if not already registered
|
|
58
|
+
if (!global.__tsxRegistered) {
|
|
59
|
+
require('tsx/cjs');
|
|
60
|
+
global.__tsxRegistered = true;
|
|
61
|
+
}
|
|
62
|
+
return require(srcPath);
|
|
63
|
+
} catch (_e) {
|
|
64
|
+
// Fall through
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Try direct require of .ts (may work with ts-node or similar)
|
|
68
|
+
try {
|
|
69
|
+
return require(srcPath);
|
|
70
|
+
} catch (e) {
|
|
71
|
+
outputError(`Failed to load module '${name}'. Run 'npm run build' or ensure tsx is installed.\n${e.message}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ─── Resolve Paths ───────────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
const CWD = process.cwd();
|
|
79
|
+
const PLANNING_DIR = path.join(CWD, '.planning');
|
|
80
|
+
const GSWD_DIR = path.join(PLANNING_DIR, 'gswd');
|
|
81
|
+
const STATE_PATH = path.join(GSWD_DIR, 'STATE.json');
|
|
82
|
+
const CONFIG_PATH = path.join(PLANNING_DIR, 'config.json');
|
|
83
|
+
|
|
84
|
+
// ─── Output Helpers ──────────────────────────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
function outputJson(data) {
|
|
87
|
+
process.stdout.write(JSON.stringify(data, null, 2) + '\n');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function outputError(message) {
|
|
91
|
+
process.stdout.write(JSON.stringify({ error: true, message }) + '\n');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function outputText(text) {
|
|
95
|
+
process.stdout.write(text + '\n');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ─── Argument Parsing ────────────────────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
const args = process.argv.slice(2);
|
|
101
|
+
const command = args[0];
|
|
102
|
+
const subcommand = args[1];
|
|
103
|
+
|
|
104
|
+
function getFlag(flag) {
|
|
105
|
+
const idx = args.indexOf(flag);
|
|
106
|
+
if (idx === -1) return null;
|
|
107
|
+
return args[idx + 1] || null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function hasFlag(flag) {
|
|
111
|
+
return args.includes(flag);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ─── Command Dispatch ────────────────────────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
function printUsage() {
|
|
117
|
+
outputText(`GSWD Tools — CLI utility for GSWD workflow operations
|
|
118
|
+
|
|
119
|
+
Usage: node gswd-tools.cjs <command> [args]
|
|
120
|
+
|
|
121
|
+
Commands:
|
|
122
|
+
init [--project-slug <slug>] Initialize STATE.json + config.json
|
|
123
|
+
status Show per-stage status
|
|
124
|
+
state read Read STATE.json
|
|
125
|
+
state update-stage <stage> <status> Update stage status
|
|
126
|
+
state checkpoint <workflow> <id> Write checkpoint
|
|
127
|
+
config read Read config.json gswd section
|
|
128
|
+
config merge [--overrides json] Merge gswd config
|
|
129
|
+
render banner <name> Render stage banner
|
|
130
|
+
render checkpoint <json> Render checkpoint box
|
|
131
|
+
render next-up <json> Render Next Up block
|
|
132
|
+
id-allocate <type> <agent> [size] Allocate ID range
|
|
133
|
+
id-ranges [type] Get allocated ranges
|
|
134
|
+
id-reset Reset ID registry
|
|
135
|
+
parse ids <file> [--type X] Extract IDs from file
|
|
136
|
+
parse validate-headings <file> <type> Validate headings
|
|
137
|
+
commit <message> [--files f1 f2] Commit planning docs
|
|
138
|
+
imagine [@idea.md] [--auto] [--skip-research] Run Imagine workflow
|
|
139
|
+
specify [--auto] [--skip-agents] Run Specify workflow
|
|
140
|
+
audit [--auto-fix] Run Audit workflow
|
|
141
|
+
compile Run Compile workflow
|
|
142
|
+
bootstrap [@idea.md] [--auto] [--resume] [--skip-research] [--policy <name>] Run full pipeline`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
switch (command) {
|
|
147
|
+
case 'init':
|
|
148
|
+
handleInit();
|
|
149
|
+
break;
|
|
150
|
+
case 'status':
|
|
151
|
+
handleStatus();
|
|
152
|
+
break;
|
|
153
|
+
case 'state':
|
|
154
|
+
handleState();
|
|
155
|
+
break;
|
|
156
|
+
case 'config':
|
|
157
|
+
handleConfig();
|
|
158
|
+
break;
|
|
159
|
+
case 'render':
|
|
160
|
+
handleRender();
|
|
161
|
+
break;
|
|
162
|
+
case 'id-allocate':
|
|
163
|
+
handleIdAllocate();
|
|
164
|
+
break;
|
|
165
|
+
case 'id-ranges':
|
|
166
|
+
handleIdRanges();
|
|
167
|
+
break;
|
|
168
|
+
case 'id-reset':
|
|
169
|
+
handleIdReset();
|
|
170
|
+
break;
|
|
171
|
+
case 'parse':
|
|
172
|
+
handleParse();
|
|
173
|
+
break;
|
|
174
|
+
case 'commit':
|
|
175
|
+
handleCommit();
|
|
176
|
+
break;
|
|
177
|
+
case 'imagine':
|
|
178
|
+
handleImagine();
|
|
179
|
+
break;
|
|
180
|
+
case 'specify':
|
|
181
|
+
handleSpecify();
|
|
182
|
+
break;
|
|
183
|
+
case 'audit':
|
|
184
|
+
handleAudit();
|
|
185
|
+
break;
|
|
186
|
+
case 'compile':
|
|
187
|
+
handleCompile();
|
|
188
|
+
break;
|
|
189
|
+
case 'bootstrap':
|
|
190
|
+
handleBootstrap();
|
|
191
|
+
break;
|
|
192
|
+
case '--help':
|
|
193
|
+
case '-h':
|
|
194
|
+
case 'help':
|
|
195
|
+
printUsage();
|
|
196
|
+
break;
|
|
197
|
+
default:
|
|
198
|
+
if (!command) {
|
|
199
|
+
printUsage();
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
outputError(`Unknown command: ${command}`);
|
|
203
|
+
printUsage();
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
} catch (err) {
|
|
207
|
+
outputError(err.message || String(err));
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ─── Command Handlers ────────────────────────────────────────────────────────
|
|
212
|
+
|
|
213
|
+
function handleInit() {
|
|
214
|
+
const stateModule = loadModule('state');
|
|
215
|
+
const configModule = loadModule('config');
|
|
216
|
+
|
|
217
|
+
const slug = getFlag('--project-slug') || path.basename(CWD).toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
218
|
+
|
|
219
|
+
const state = stateModule.initState(GSWD_DIR, slug);
|
|
220
|
+
configModule.mergeGswdConfig(CONFIG_PATH);
|
|
221
|
+
|
|
222
|
+
outputJson({
|
|
223
|
+
initialized: true,
|
|
224
|
+
state_path: path.relative(CWD, STATE_PATH),
|
|
225
|
+
config_path: path.relative(CWD, CONFIG_PATH),
|
|
226
|
+
project_slug: state.project_slug,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function handleStatus() {
|
|
231
|
+
const stateModule = loadModule('state');
|
|
232
|
+
const renderModule = loadModule('render');
|
|
233
|
+
|
|
234
|
+
const state = stateModule.readState(STATE_PATH);
|
|
235
|
+
if (!state) {
|
|
236
|
+
outputError('No STATE.json found. Run "gswd-tools.cjs init" first.');
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const banner = renderModule.renderBanner('STATUS');
|
|
241
|
+
|
|
242
|
+
const stages = ['imagine', 'specify', 'audit', 'compile'];
|
|
243
|
+
const stageLines = stages.map(s => {
|
|
244
|
+
const status = state.stage_status[s] || 'not_started';
|
|
245
|
+
return renderModule.renderStatusLine(
|
|
246
|
+
s.charAt(0).toUpperCase() + s.slice(1),
|
|
247
|
+
status
|
|
248
|
+
);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Calculate progress
|
|
252
|
+
const doneCount = stages.filter(s => {
|
|
253
|
+
const st = state.stage_status[s];
|
|
254
|
+
return st === 'done' || st === 'pass';
|
|
255
|
+
}).length;
|
|
256
|
+
const progressBar = renderModule.renderProgressBar(doneCount, stages.length);
|
|
257
|
+
|
|
258
|
+
const output = [
|
|
259
|
+
banner,
|
|
260
|
+
'',
|
|
261
|
+
...stageLines,
|
|
262
|
+
'',
|
|
263
|
+
progressBar,
|
|
264
|
+
'',
|
|
265
|
+
'Quick Links:',
|
|
266
|
+
` STATE.json: ${path.relative(CWD, STATE_PATH)}`,
|
|
267
|
+
` Config: ${path.relative(CWD, CONFIG_PATH)}`,
|
|
268
|
+
].join('\n');
|
|
269
|
+
|
|
270
|
+
outputText(output);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function handleState() {
|
|
274
|
+
const stateModule = loadModule('state');
|
|
275
|
+
|
|
276
|
+
switch (subcommand) {
|
|
277
|
+
case 'read': {
|
|
278
|
+
const state = stateModule.readState(STATE_PATH);
|
|
279
|
+
if (!state) {
|
|
280
|
+
outputError('No valid STATE.json found.');
|
|
281
|
+
process.exit(1);
|
|
282
|
+
}
|
|
283
|
+
outputJson(state);
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
case 'update-stage': {
|
|
287
|
+
const stage = args[2];
|
|
288
|
+
const status = args[3];
|
|
289
|
+
if (!stage || !status) {
|
|
290
|
+
outputError('Usage: state update-stage <stage> <status>');
|
|
291
|
+
process.exit(1);
|
|
292
|
+
}
|
|
293
|
+
stateModule.updateStageStatus(STATE_PATH, stage, status);
|
|
294
|
+
outputJson({ updated: true, stage, status });
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
case 'checkpoint': {
|
|
298
|
+
const workflow = args[2];
|
|
299
|
+
const checkpointId = args[3];
|
|
300
|
+
if (!workflow || !checkpointId) {
|
|
301
|
+
outputError('Usage: state checkpoint <workflow> <checkpoint_id>');
|
|
302
|
+
process.exit(1);
|
|
303
|
+
}
|
|
304
|
+
stateModule.writeCheckpoint(STATE_PATH, workflow, checkpointId);
|
|
305
|
+
outputJson({ checkpoint_written: true, workflow, checkpoint_id: checkpointId });
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
default:
|
|
309
|
+
outputError(`Unknown state subcommand: ${subcommand}`);
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function handleConfig() {
|
|
315
|
+
const configModule = loadModule('config');
|
|
316
|
+
|
|
317
|
+
switch (subcommand) {
|
|
318
|
+
case 'read': {
|
|
319
|
+
const config = configModule.getGswdConfig(CONFIG_PATH);
|
|
320
|
+
outputJson(config);
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
case 'merge': {
|
|
324
|
+
const overridesRaw = getFlag('--overrides');
|
|
325
|
+
let overrides;
|
|
326
|
+
if (overridesRaw) {
|
|
327
|
+
try {
|
|
328
|
+
overrides = JSON.parse(overridesRaw);
|
|
329
|
+
} catch {
|
|
330
|
+
outputError('Invalid JSON for --overrides');
|
|
331
|
+
process.exit(1);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
configModule.mergeGswdConfig(CONFIG_PATH, overrides);
|
|
335
|
+
outputJson({ merged: true });
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
default:
|
|
339
|
+
outputError(`Unknown config subcommand: ${subcommand}`);
|
|
340
|
+
process.exit(1);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function handleRender() {
|
|
345
|
+
const renderModule = loadModule('render');
|
|
346
|
+
|
|
347
|
+
switch (subcommand) {
|
|
348
|
+
case 'banner': {
|
|
349
|
+
const name = args[2];
|
|
350
|
+
if (!name) {
|
|
351
|
+
outputError('Usage: render banner <name>');
|
|
352
|
+
process.exit(1);
|
|
353
|
+
}
|
|
354
|
+
outputText(renderModule.renderBanner(name));
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
case 'checkpoint': {
|
|
358
|
+
const jsonStr = args[2];
|
|
359
|
+
if (!jsonStr) {
|
|
360
|
+
outputError('Usage: render checkpoint <json>');
|
|
361
|
+
process.exit(1);
|
|
362
|
+
}
|
|
363
|
+
try {
|
|
364
|
+
const data = JSON.parse(jsonStr);
|
|
365
|
+
outputText(renderModule.renderCheckpoint(
|
|
366
|
+
data.type || 'Checkpoint',
|
|
367
|
+
data.context || '',
|
|
368
|
+
data.options || [],
|
|
369
|
+
data.action || 'Select an option'
|
|
370
|
+
));
|
|
371
|
+
} catch {
|
|
372
|
+
outputError('Invalid JSON for checkpoint render');
|
|
373
|
+
process.exit(1);
|
|
374
|
+
}
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
case 'next-up': {
|
|
378
|
+
const jsonStr = args[2];
|
|
379
|
+
if (!jsonStr) {
|
|
380
|
+
outputError('Usage: render next-up <json>');
|
|
381
|
+
process.exit(1);
|
|
382
|
+
}
|
|
383
|
+
try {
|
|
384
|
+
const data = JSON.parse(jsonStr);
|
|
385
|
+
outputText(renderModule.renderNextUp(
|
|
386
|
+
data.files || [],
|
|
387
|
+
data.next_command || '',
|
|
388
|
+
data.also_available
|
|
389
|
+
));
|
|
390
|
+
} catch {
|
|
391
|
+
outputError('Invalid JSON for next-up render');
|
|
392
|
+
process.exit(1);
|
|
393
|
+
}
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
default:
|
|
397
|
+
outputError(`Unknown render subcommand: ${subcommand}`);
|
|
398
|
+
process.exit(1);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function handleIdAllocate() {
|
|
403
|
+
const stateModule = loadModule('state');
|
|
404
|
+
|
|
405
|
+
const idType = args[1];
|
|
406
|
+
const agent = args[2];
|
|
407
|
+
const size = args[3] ? parseInt(args[3], 10) : undefined;
|
|
408
|
+
|
|
409
|
+
if (!idType || !agent) {
|
|
410
|
+
outputError('Usage: id-allocate <type> <agent> [size]');
|
|
411
|
+
process.exit(1);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const range = stateModule.allocateIdRange(STATE_PATH, idType, agent, size);
|
|
415
|
+
outputJson({ type: idType, agent, ...range });
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function handleIdRanges() {
|
|
419
|
+
const stateModule = loadModule('state');
|
|
420
|
+
const idType = args[1] || undefined;
|
|
421
|
+
const ranges = stateModule.getIdRanges(STATE_PATH, idType);
|
|
422
|
+
outputJson(ranges);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function handleIdReset() {
|
|
426
|
+
const stateModule = loadModule('state');
|
|
427
|
+
stateModule.resetIdRegistry(STATE_PATH);
|
|
428
|
+
outputJson({ reset: true });
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function handleParse() {
|
|
432
|
+
const parseModule = loadModule('parse');
|
|
433
|
+
|
|
434
|
+
switch (subcommand) {
|
|
435
|
+
case 'ids': {
|
|
436
|
+
const filePath = args[2];
|
|
437
|
+
if (!filePath) {
|
|
438
|
+
outputError('Usage: parse ids <file> [--type X]');
|
|
439
|
+
process.exit(1);
|
|
440
|
+
}
|
|
441
|
+
const resolvedPath = path.resolve(CWD, filePath);
|
|
442
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
443
|
+
outputError(`File not found: ${resolvedPath}`);
|
|
444
|
+
process.exit(1);
|
|
445
|
+
}
|
|
446
|
+
const content = fs.readFileSync(resolvedPath, 'utf-8');
|
|
447
|
+
const idType = getFlag('--type') || undefined;
|
|
448
|
+
const ids = parseModule.extractIds(content, idType);
|
|
449
|
+
outputJson(ids);
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
case 'validate-headings': {
|
|
453
|
+
const filePath = args[2];
|
|
454
|
+
const fileType = args[3];
|
|
455
|
+
if (!filePath || !fileType) {
|
|
456
|
+
outputError('Usage: parse validate-headings <file> <type>');
|
|
457
|
+
process.exit(1);
|
|
458
|
+
}
|
|
459
|
+
const resolvedPath = path.resolve(CWD, filePath);
|
|
460
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
461
|
+
outputError(`File not found: ${resolvedPath}`);
|
|
462
|
+
process.exit(1);
|
|
463
|
+
}
|
|
464
|
+
const content = fs.readFileSync(resolvedPath, 'utf-8');
|
|
465
|
+
const result = parseModule.validateHeadings(content, fileType);
|
|
466
|
+
outputJson(result);
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
default:
|
|
470
|
+
outputError(`Unknown parse subcommand: ${subcommand}`);
|
|
471
|
+
process.exit(1);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function handleCommit() {
|
|
476
|
+
const message = args[1];
|
|
477
|
+
if (!message) {
|
|
478
|
+
outputError('Usage: commit <message> [--files f1 f2]');
|
|
479
|
+
process.exit(1);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Collect files to commit
|
|
483
|
+
let files = [];
|
|
484
|
+
const filesIdx = args.indexOf('--files');
|
|
485
|
+
if (filesIdx !== -1) {
|
|
486
|
+
files = args.slice(filesIdx + 1);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
try {
|
|
490
|
+
if (files.length > 0) {
|
|
491
|
+
execSync(`git add ${files.map(f => `"${f}"`).join(' ')}`, {
|
|
492
|
+
cwd: CWD,
|
|
493
|
+
stdio: 'pipe',
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const result = execSync(`git commit -m "${message.replace(/"/g, '\\"')}"`, {
|
|
498
|
+
cwd: CWD,
|
|
499
|
+
stdio: 'pipe',
|
|
500
|
+
encoding: 'utf-8',
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
// Extract commit hash
|
|
504
|
+
const hashMatch = result.match(/\[[\w-]+ ([a-f0-9]+)\]/);
|
|
505
|
+
const hash = hashMatch ? hashMatch[1] : 'unknown';
|
|
506
|
+
|
|
507
|
+
outputJson({
|
|
508
|
+
committed: true,
|
|
509
|
+
hash,
|
|
510
|
+
message,
|
|
511
|
+
files_staged: files.length > 0 ? files : 'all staged',
|
|
512
|
+
});
|
|
513
|
+
} catch (err) {
|
|
514
|
+
outputError(`Commit failed: ${err.message}`);
|
|
515
|
+
process.exit(1);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function handleImagine() {
|
|
520
|
+
const imagineModule = loadModule('imagine');
|
|
521
|
+
|
|
522
|
+
const autoMode = hasFlag('--auto');
|
|
523
|
+
const skipResearch = hasFlag('--skip-research');
|
|
524
|
+
|
|
525
|
+
// Find idea file: look for arg starting with @ or ending with .md
|
|
526
|
+
let ideaFilePath = null;
|
|
527
|
+
for (let i = 1; i < args.length; i++) {
|
|
528
|
+
const arg = args[i];
|
|
529
|
+
if (arg.startsWith('--')) continue;
|
|
530
|
+
if (arg.startsWith('@')) {
|
|
531
|
+
ideaFilePath = path.resolve(CWD, arg.slice(1));
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
534
|
+
if (arg.endsWith('.md')) {
|
|
535
|
+
ideaFilePath = path.resolve(CWD, arg);
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (!ideaFilePath) {
|
|
541
|
+
// No idea file — start interactive 3-question intake
|
|
542
|
+
const readline = require('readline');
|
|
543
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
544
|
+
|
|
545
|
+
rl.question('Vision -- what does it do? ', function(vision) {
|
|
546
|
+
rl.question('User -- who is it for? ', function(user) {
|
|
547
|
+
rl.question('Why now -- what is the opportunity? ', function(whyNow) {
|
|
548
|
+
rl.close();
|
|
549
|
+
|
|
550
|
+
const intakeOptions = {
|
|
551
|
+
intakeAnswers: { vision, user, whyNow },
|
|
552
|
+
autoMode,
|
|
553
|
+
skipResearch,
|
|
554
|
+
planningDir: PLANNING_DIR,
|
|
555
|
+
configPath: CONFIG_PATH,
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
imagineModule.runImagine(intakeOptions).then(function(result) {
|
|
559
|
+
outputJson(result);
|
|
560
|
+
if (result.status === 'error' || result.status === 'gate_failed') {
|
|
561
|
+
process.exit(1);
|
|
562
|
+
}
|
|
563
|
+
}).catch(function(err) {
|
|
564
|
+
outputError(err.message || String(err));
|
|
565
|
+
process.exit(1);
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
});
|
|
569
|
+
});
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const options = {
|
|
574
|
+
ideaFilePath,
|
|
575
|
+
autoMode,
|
|
576
|
+
skipResearch,
|
|
577
|
+
planningDir: PLANNING_DIR,
|
|
578
|
+
configPath: CONFIG_PATH,
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
// runImagine is async, handle with promise
|
|
582
|
+
imagineModule.runImagine(options).then(function(result) {
|
|
583
|
+
outputJson(result);
|
|
584
|
+
if (result.status === 'error' || result.status === 'gate_failed') {
|
|
585
|
+
process.exit(1);
|
|
586
|
+
}
|
|
587
|
+
}).catch(function(err) {
|
|
588
|
+
outputError(err.message || String(err));
|
|
589
|
+
process.exit(1);
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function handleSpecify() {
|
|
594
|
+
const specifyModule = loadModule('specify');
|
|
595
|
+
|
|
596
|
+
const autoMode = hasFlag('--auto');
|
|
597
|
+
const skipAgents = hasFlag('--skip-agents');
|
|
598
|
+
|
|
599
|
+
const budgetFlag = getFlag('--integration-budget');
|
|
600
|
+
const integrationBudget = budgetFlag ? parseInt(budgetFlag, 10) : undefined;
|
|
601
|
+
|
|
602
|
+
const options = {
|
|
603
|
+
auto: autoMode,
|
|
604
|
+
skipAgents,
|
|
605
|
+
integrationBudget,
|
|
606
|
+
planningDir: PLANNING_DIR,
|
|
607
|
+
configPath: CONFIG_PATH,
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
// runSpecify is async, handle with promise
|
|
611
|
+
specifyModule.runSpecify(options).then(function(result) {
|
|
612
|
+
outputJson(result);
|
|
613
|
+
if (result.status === 'failed') {
|
|
614
|
+
process.exit(1);
|
|
615
|
+
}
|
|
616
|
+
}).catch(function(err) {
|
|
617
|
+
outputError(err.message || String(err));
|
|
618
|
+
process.exit(1);
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
function handleBootstrap() {
|
|
623
|
+
const bootstrapModule = loadModule('bootstrap');
|
|
624
|
+
|
|
625
|
+
const autoMode = hasFlag('--auto');
|
|
626
|
+
const resume = hasFlag('--resume');
|
|
627
|
+
const skipResearch = hasFlag('--skip-research');
|
|
628
|
+
const policyFlag = getFlag('--policy');
|
|
629
|
+
|
|
630
|
+
// Find idea file: @ prefix or .md extension (same logic as handleImagine)
|
|
631
|
+
let ideaFilePath = null;
|
|
632
|
+
for (let i = 1; i < args.length; i++) {
|
|
633
|
+
const arg = args[i];
|
|
634
|
+
if (arg.startsWith('--')) continue;
|
|
635
|
+
if (arg.startsWith('@')) {
|
|
636
|
+
ideaFilePath = path.resolve(CWD, arg.slice(1));
|
|
637
|
+
break;
|
|
638
|
+
}
|
|
639
|
+
if (arg.endsWith('.md')) {
|
|
640
|
+
ideaFilePath = path.resolve(CWD, arg);
|
|
641
|
+
break;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const options = {
|
|
646
|
+
ideaFilePath,
|
|
647
|
+
auto: autoMode,
|
|
648
|
+
resume,
|
|
649
|
+
skipResearch,
|
|
650
|
+
policy: policyFlag || undefined,
|
|
651
|
+
planningDir: PLANNING_DIR,
|
|
652
|
+
configPath: CONFIG_PATH,
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
bootstrapModule.runBootstrap(options).then(function(result) {
|
|
656
|
+
outputJson(result);
|
|
657
|
+
if (result.status === 'failed' || result.status === 'interrupted') {
|
|
658
|
+
process.exit(1);
|
|
659
|
+
}
|
|
660
|
+
}).catch(function(err) {
|
|
661
|
+
outputError(err.message || String(err));
|
|
662
|
+
process.exit(1);
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function handleAudit() {
|
|
667
|
+
const auditModule = loadModule('audit');
|
|
668
|
+
|
|
669
|
+
const autoFix = hasFlag('--auto-fix');
|
|
670
|
+
|
|
671
|
+
try {
|
|
672
|
+
const result = auditModule.runAuditWorkflow({
|
|
673
|
+
planningDir: PLANNING_DIR,
|
|
674
|
+
statePath: STATE_PATH,
|
|
675
|
+
autoFix,
|
|
676
|
+
maxCycles: 2,
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
outputJson({
|
|
680
|
+
passed: result.passed,
|
|
681
|
+
autoFixCycles: result.autoFixCycles,
|
|
682
|
+
reportPath: result.reportPath,
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
if (!result.passed) {
|
|
686
|
+
process.exit(1);
|
|
687
|
+
}
|
|
688
|
+
} catch (err) {
|
|
689
|
+
outputError(err.message || String(err));
|
|
690
|
+
process.exit(1);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function handleCompile() {
|
|
695
|
+
const compileModule = loadModule('compile');
|
|
696
|
+
|
|
697
|
+
try {
|
|
698
|
+
const result = compileModule.runCompileWorkflow({
|
|
699
|
+
planningDir: PLANNING_DIR,
|
|
700
|
+
statePath: STATE_PATH,
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
outputJson({
|
|
704
|
+
passed: result.passed,
|
|
705
|
+
filesWritten: result.filesWritten,
|
|
706
|
+
error: result.error,
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
if (!result.passed) {
|
|
710
|
+
process.exit(1);
|
|
711
|
+
}
|
|
712
|
+
} catch (err) {
|
|
713
|
+
outputError(err.message || String(err));
|
|
714
|
+
process.exit(1);
|
|
715
|
+
}
|
|
716
|
+
}
|