gswd 1.0.0 → 1.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 (86) hide show
  1. package/bin/gswd-tools.cjs +228 -0
  2. package/bin/install.js +8 -0
  3. package/commands/gswd/imagine.md +7 -1
  4. package/commands/gswd/start.md +507 -32
  5. package/dist/lib/audit.d.ts +205 -0
  6. package/dist/lib/audit.js +805 -0
  7. package/dist/lib/bootstrap.d.ts +103 -0
  8. package/dist/lib/bootstrap.js +563 -0
  9. package/dist/lib/compile.d.ts +239 -0
  10. package/dist/lib/compile.js +1152 -0
  11. package/dist/lib/config.d.ts +49 -0
  12. package/dist/lib/config.js +150 -0
  13. package/dist/lib/imagine-agents.d.ts +54 -0
  14. package/dist/lib/imagine-agents.js +185 -0
  15. package/dist/lib/imagine-gate.d.ts +47 -0
  16. package/dist/lib/imagine-gate.js +131 -0
  17. package/dist/lib/imagine-input.d.ts +46 -0
  18. package/dist/lib/imagine-input.js +233 -0
  19. package/dist/lib/imagine-synthesis.d.ts +90 -0
  20. package/dist/lib/imagine-synthesis.js +453 -0
  21. package/dist/lib/imagine.d.ts +56 -0
  22. package/dist/lib/imagine.js +413 -0
  23. package/dist/lib/intake.d.ts +27 -0
  24. package/dist/lib/intake.js +82 -0
  25. package/dist/lib/parse.d.ts +59 -0
  26. package/dist/lib/parse.js +171 -0
  27. package/dist/lib/render.d.ts +309 -0
  28. package/dist/lib/render.js +624 -0
  29. package/dist/lib/specify-agents.d.ts +120 -0
  30. package/dist/lib/specify-agents.js +269 -0
  31. package/dist/lib/specify-journeys.d.ts +124 -0
  32. package/dist/lib/specify-journeys.js +279 -0
  33. package/dist/lib/specify-nfr.d.ts +45 -0
  34. package/dist/lib/specify-nfr.js +159 -0
  35. package/dist/lib/specify-roles.d.ts +46 -0
  36. package/dist/lib/specify-roles.js +88 -0
  37. package/dist/lib/specify.d.ts +70 -0
  38. package/dist/lib/specify.js +676 -0
  39. package/dist/lib/state.d.ts +140 -0
  40. package/dist/lib/state.js +340 -0
  41. package/dist/tests/audit.test.d.ts +4 -0
  42. package/dist/tests/audit.test.js +1579 -0
  43. package/dist/tests/bootstrap.test.d.ts +5 -0
  44. package/dist/tests/bootstrap.test.js +611 -0
  45. package/dist/tests/compile.test.d.ts +4 -0
  46. package/dist/tests/compile.test.js +862 -0
  47. package/dist/tests/config.test.d.ts +4 -0
  48. package/dist/tests/config.test.js +191 -0
  49. package/dist/tests/imagine-agents.test.d.ts +6 -0
  50. package/dist/tests/imagine-agents.test.js +179 -0
  51. package/dist/tests/imagine-gate.test.d.ts +6 -0
  52. package/dist/tests/imagine-gate.test.js +264 -0
  53. package/dist/tests/imagine-input.test.d.ts +6 -0
  54. package/dist/tests/imagine-input.test.js +283 -0
  55. package/dist/tests/imagine-synthesis.test.d.ts +7 -0
  56. package/dist/tests/imagine-synthesis.test.js +380 -0
  57. package/dist/tests/imagine.test.d.ts +8 -0
  58. package/dist/tests/imagine.test.js +406 -0
  59. package/dist/tests/parse.test.d.ts +4 -0
  60. package/dist/tests/parse.test.js +285 -0
  61. package/dist/tests/render.test.d.ts +4 -0
  62. package/dist/tests/render.test.js +236 -0
  63. package/dist/tests/specify-agents.test.d.ts +4 -0
  64. package/dist/tests/specify-agents.test.js +352 -0
  65. package/dist/tests/specify-journeys.test.d.ts +5 -0
  66. package/dist/tests/specify-journeys.test.js +440 -0
  67. package/dist/tests/specify-nfr.test.d.ts +4 -0
  68. package/dist/tests/specify-nfr.test.js +205 -0
  69. package/dist/tests/specify-roles.test.d.ts +4 -0
  70. package/dist/tests/specify-roles.test.js +136 -0
  71. package/dist/tests/specify.test.d.ts +9 -0
  72. package/dist/tests/specify.test.js +544 -0
  73. package/dist/tests/state.test.d.ts +4 -0
  74. package/dist/tests/state.test.js +316 -0
  75. package/lib/bootstrap.ts +37 -11
  76. package/lib/compile.ts +426 -4
  77. package/lib/imagine-agents.ts +53 -7
  78. package/lib/imagine-synthesis.ts +170 -6
  79. package/lib/imagine.ts +59 -5
  80. package/lib/intake.ts +60 -0
  81. package/lib/parse.ts +2 -1
  82. package/lib/render.ts +566 -5
  83. package/lib/specify-agents.ts +25 -3
  84. package/lib/state.ts +115 -0
  85. package/package.json +4 -2
  86. package/templates/gswd/DECISIONS.template.md +3 -0
@@ -0,0 +1,413 @@
1
+ "use strict";
2
+ /**
3
+ * GSWD Imagine Workflow Orchestrator
4
+ *
5
+ * Full pipeline: input -> agents -> synthesis -> gate -> artifacts -> state
6
+ * Implements GSWD_SPEC Section 8.2 end-to-end.
7
+ *
8
+ * Both interactive and auto modes are supported:
9
+ * - Interactive: presents direction checkpoint, awaits selection
10
+ * - Auto: scores directions via pain x WTP x reachability, selects highest
11
+ *
12
+ * Schema: GSWD_SPEC.md Section 8.2
13
+ */
14
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ var desc = Object.getOwnPropertyDescriptor(m, k);
17
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
+ desc = { enumerable: true, get: function() { return m[k]; } };
19
+ }
20
+ Object.defineProperty(o, k2, desc);
21
+ }) : (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ o[k2] = m[k];
24
+ }));
25
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
26
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
27
+ }) : function(o, v) {
28
+ o["default"] = v;
29
+ });
30
+ var __importStar = (this && this.__importStar) || (function () {
31
+ var ownKeys = function(o) {
32
+ ownKeys = Object.getOwnPropertyNames || function (o) {
33
+ var ar = [];
34
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
35
+ return ar;
36
+ };
37
+ return ownKeys(o);
38
+ };
39
+ return function (mod) {
40
+ if (mod && mod.__esModule) return mod;
41
+ var result = {};
42
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
43
+ __setModuleDefault(result, mod);
44
+ return result;
45
+ };
46
+ })();
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.runImagine = runImagine;
49
+ const path = __importStar(require("node:path"));
50
+ const state_js_1 = require("./state.js");
51
+ const config_js_1 = require("./config.js");
52
+ const imagine_input_js_1 = require("./imagine-input.js");
53
+ const imagine_agents_js_1 = require("./imagine-agents.js");
54
+ const imagine_synthesis_js_1 = require("./imagine-synthesis.js");
55
+ const imagine_gate_js_1 = require("./imagine-gate.js");
56
+ // ─── Artifact Building ──────────────────────────────────────────────────────
57
+ /**
58
+ * Build IMAGINE.md content from selected direction and synthesis data.
59
+ */
60
+ function buildImagineContent(brief, selected, synthesis, visionStatement, autoDecisions) {
61
+ const alternatives = synthesis.alternatives
62
+ .map((alt, i) => `### Alternative ${i + 1}: ${alt.label}\n- **ICP:** ${alt.icp_summary}\n- **Problem:** ${alt.problem_framing}\n- **Wedge:** ${alt.wedge}\n- **Differentiator:** ${alt.differentiator}`)
63
+ .join('\n\n');
64
+ const metrics = autoDecisions
65
+ ? autoDecisions.filter(d => d.type === 'metric').map(d => `- ${d.chosen}`).join('\n')
66
+ : '- To be defined during decision gate';
67
+ return `# Imagine
68
+
69
+ ## Vision
70
+ ${visionStatement}
71
+
72
+ ## Target User
73
+ ${brief.target_user}
74
+
75
+ ## Problem Statement
76
+ ${selected.problem_framing}
77
+
78
+ ## Product Direction
79
+ ### ${selected.label}
80
+ - **ICP:** ${selected.icp_summary}
81
+ - **Problem:** ${selected.problem_framing}
82
+ - **Wedge:** ${selected.wedge}
83
+ - **Differentiator:** ${selected.differentiator}
84
+ - **Risks:** ${selected.risks.join('; ')}
85
+
86
+ ## Alternatives
87
+ ${alternatives}
88
+
89
+ ## Wedge / MVP Boundary
90
+ ${selected.wedge}
91
+
92
+ ## Success Metrics
93
+ ${metrics}
94
+ `;
95
+ }
96
+ /**
97
+ * Sanitize a value for use in a single-line markdown bullet.
98
+ * Collapses newlines, strips heading markers, and trims to prevent
99
+ * multi-line content from breaking section boundaries in DECISIONS.md.
100
+ */
101
+ function sanitizeBulletValue(value) {
102
+ return value
103
+ .replace(/\n+/g, ' ') // collapse newlines to spaces
104
+ .replace(/#+\s*/g, '') // strip markdown heading markers
105
+ .replace(/\s{2,}/g, ' ') // collapse multiple spaces
106
+ .trim();
107
+ }
108
+ /**
109
+ * Build DECISIONS.md content from selected direction, auto decisions, and risks.
110
+ */
111
+ function buildDecisionsContent(selected, synthesis, brief, visionStatement, autoDecisions) {
112
+ // Build frozen decisions (need >= 8)
113
+ // Sanitize all interpolated values to prevent multi-line content from breaking section structure
114
+ const decisionPrefix = autoDecisions ? 'Auto-chosen: ' : '';
115
+ const frozenDecisions = [
116
+ `- ${decisionPrefix}**ICP:** ${sanitizeBulletValue(selected.icp_summary)}`,
117
+ `- ${decisionPrefix}**Problem Statement:** ${sanitizeBulletValue(selected.problem_framing)}`,
118
+ `- ${decisionPrefix}**Product Direction:** ${sanitizeBulletValue(selected.label)}`,
119
+ `- ${decisionPrefix}**Wedge / Entry Point:** ${sanitizeBulletValue(selected.wedge)}`,
120
+ `- ${decisionPrefix}**Differentiator:** ${sanitizeBulletValue(selected.differentiator)}`,
121
+ `- ${decisionPrefix}**Target User:** ${sanitizeBulletValue(brief.target_user)}`,
122
+ `- ${decisionPrefix}**Timing Rationale:** ${sanitizeBulletValue(brief.why_now)}`,
123
+ `- ${decisionPrefix}**MVP Boundary:** ${sanitizeBulletValue(selected.wedge)} (full product expansion deferred to post-validation)`,
124
+ ];
125
+ // Success metrics
126
+ const metricDecision = autoDecisions?.find(d => d.type === 'metric');
127
+ const metricsItems = metricDecision
128
+ ? metricDecision.chosen.split(',').map(m => `- ${m.trim()}`)
129
+ : ['- Activation rate', '- Week-1 retention'];
130
+ // Out of scope
131
+ const outOfScope = [
132
+ '- Features beyond MVP wedge scope',
133
+ '- Paid integrations (unless pre-approved)',
134
+ '- Multi-user / team collaboration (v1 is single-user)',
135
+ ];
136
+ // Risks from devils-advocate agent and direction risks
137
+ const risksContent = synthesis.raw_agent_outputs['devils-advocate'] || '';
138
+ const riskLines = extractRiskItems(risksContent, selected.risks);
139
+ // Open questions
140
+ const openQuestions = synthesis.agent_warnings.length > 0
141
+ ? synthesis.agent_warnings.map(w => `- ${w}`)
142
+ : ['- None at this time'];
143
+ return `# Decisions
144
+
145
+ ## Vision
146
+ ${visionStatement}
147
+
148
+ ## Frozen Decisions
149
+ ${frozenDecisions.join('\n')}
150
+
151
+ ## Success Metrics
152
+ ${metricsItems.join('\n')}
153
+
154
+ ## Out of Scope
155
+ ${outOfScope.join('\n')}
156
+
157
+ ## Risks & Mitigations
158
+ ${riskLines.join('\n')}
159
+
160
+ ## Open Questions
161
+ ${openQuestions.join('\n')}
162
+ `;
163
+ }
164
+ /**
165
+ * Extract risk items from devils-advocate output, ensuring >= 5 items.
166
+ */
167
+ function extractRiskItems(risksContent, directionRisks) {
168
+ const items = [];
169
+ if (risksContent) {
170
+ // Extract from ## Risks section
171
+ const risksMatch = risksContent.match(/##\s*Risks\s*\n([\s\S]*?)(?=\n##|$)/);
172
+ if (risksMatch) {
173
+ const lines = risksMatch[1].split('\n')
174
+ .filter(l => l.trim().startsWith('-'))
175
+ .map(l => l.trim())
176
+ .slice(0, 8);
177
+ items.push(...lines);
178
+ }
179
+ // Extract from ## Mitigations section
180
+ const mitigationsMatch = risksContent.match(/##\s*Mitigations\s*\n([\s\S]*?)(?=\n##|$)/);
181
+ if (mitigationsMatch && items.length < 5) {
182
+ const lines = mitigationsMatch[1].split('\n')
183
+ .filter(l => l.trim().startsWith('-'))
184
+ .map(l => l.trim())
185
+ .slice(0, 5 - items.length);
186
+ items.push(...lines);
187
+ }
188
+ }
189
+ // Add direction risks if we need more
190
+ for (const risk of directionRisks) {
191
+ if (items.length >= 5)
192
+ break;
193
+ items.push(`- **Direction risk:** ${risk}`);
194
+ }
195
+ // Ensure minimum 5
196
+ while (items.length < 5) {
197
+ items.push(`- Risk ${items.length + 1}: To be identified during Specify stage`);
198
+ }
199
+ return items;
200
+ }
201
+ /**
202
+ * Build artifact content from agent raw output, falling back to a message.
203
+ */
204
+ function buildAgentArtifact(agentName, rawOutputs, fallback) {
205
+ const content = rawOutputs[agentName];
206
+ return content || fallback;
207
+ }
208
+ // ─── Vision Synthesis ───────────────────────────────────────────────────────
209
+ /**
210
+ * Synthesize an aspirational vision statement from brief and selected direction.
211
+ * Vision = future state the product enables, NOT product description.
212
+ * Example: "A world where solo founders never build the wrong thing because
213
+ * their specification process is as rigorous as their code."
214
+ *
215
+ * This is a template-based synthesis; in manual mode, the LLM in start.md
216
+ * will generate a richer vision and pass it via options.visionStatement.
217
+ */
218
+ function synthesizeVisionStatement(brief, selected) {
219
+ const userContext = brief.target_user || 'users';
220
+ const problemContext = selected.problem_framing || brief.vision;
221
+ return `A world where ${userContext} achieve ${problemContext} effortlessly — because the right tools make clarity the default, not the exception.`;
222
+ }
223
+ // ─── Main Workflow ──────────────────────────────────────────────────────────
224
+ /**
225
+ * Run the Imagine workflow end-to-end.
226
+ *
227
+ * Implements GSWD_SPEC Section 8.2 Steps 1-6:
228
+ * 1. Load state and config
229
+ * 2. Collect founder input (file or intake)
230
+ * 3. Spawn parallel agents
231
+ * 4. Synthesize into proposed direction + 2 alternatives
232
+ * 5. Decision gate (must freeze)
233
+ * 6. Write docs and update state
234
+ */
235
+ async function runImagine(options) {
236
+ const planningDir = options.planningDir || path.join(process.cwd(), '.planning');
237
+ const gswdDir = path.join(planningDir, 'gswd');
238
+ const statePath = path.join(gswdDir, 'STATE.json');
239
+ const configPath = options.configPath || path.join(planningDir, 'config.json');
240
+ try {
241
+ // ── Step 1: Load state and config ──────────────────────────────────
242
+ const state = (0, state_js_1.readState)(statePath);
243
+ if (!state) {
244
+ return { status: 'error', artifacts_written: [], error: 'No STATE.json found. Run init first.' };
245
+ }
246
+ const config = (0, config_js_1.getGswdConfig)(configPath);
247
+ // Mark imagine as in_progress
248
+ state.stage = 'imagine';
249
+ state.stage_status.imagine = 'in_progress';
250
+ (0, state_js_1.writeState)(statePath, state);
251
+ // ── Step 2: Collect input ──────────────────────────────────────────
252
+ let brief;
253
+ if (options.ideaFilePath) {
254
+ brief = (0, imagine_input_js_1.parseIdeaFile)(options.ideaFilePath);
255
+ }
256
+ else if (options.intakeAnswers) {
257
+ brief = (0, imagine_input_js_1.buildFromIntake)(options.intakeAnswers);
258
+ }
259
+ else {
260
+ return {
261
+ status: 'error',
262
+ artifacts_written: [],
263
+ error: 'No input: provide ideaFilePath or intakeAnswers',
264
+ };
265
+ }
266
+ const validation = (0, imagine_input_js_1.validateBrief)(brief);
267
+ if (!validation.valid) {
268
+ return {
269
+ status: 'error',
270
+ artifacts_written: [],
271
+ error: `Invalid brief: missing fields [${validation.missing.join(', ')}]`,
272
+ };
273
+ }
274
+ // ── Step 3: Spawn agents ──────────────────────────────────────────
275
+ let agentResults;
276
+ const agentHeadlines = [];
277
+ if (options.skipResearch) {
278
+ // Create minimal results from brief content
279
+ agentResults = imagine_agents_js_1.IMAGINE_AGENTS.map(agent => ({
280
+ agent: agent.name,
281
+ content: `## ${agent.requiredHeadings[0]?.replace('## ', '') || 'Output'}\nGenerated from starter brief.\n\n${brief.vision}\n${brief.target_user}\n${brief.why_now}`,
282
+ status: 'complete',
283
+ duration_ms: 0,
284
+ }));
285
+ }
286
+ else {
287
+ const spawnFn = options.spawnFn;
288
+ if (!spawnFn) {
289
+ return {
290
+ status: 'error',
291
+ artifacts_written: [],
292
+ error: 'No spawnFn provided for agent orchestration',
293
+ };
294
+ }
295
+ // Allocate ID ranges for imagine agents before spawning (FNDN-05)
296
+ try {
297
+ (0, state_js_1.allocateIdRange)(statePath, 'J', 'journey-mapper', 50);
298
+ (0, state_js_1.allocateIdRange)(statePath, 'FR', 'market-researcher', 50);
299
+ }
300
+ catch {
301
+ // Non-fatal — ID allocation failure should not block imagine
302
+ }
303
+ agentResults = await (0, imagine_agents_js_1.orchestrateAgents)(imagine_agents_js_1.IMAGINE_AGENTS, config.max_parallel_agents, brief, spawnFn, options.previousAgentOutputs, options.userFeedback, (result) => { agentHeadlines.push(result); });
304
+ }
305
+ // Build research summary (IMAGINE-01)
306
+ const researchSummary = (0, imagine_synthesis_js_1.buildResearchSummary)(agentResults, brief);
307
+ // Mid-stage checkpoint: agents complete (RESM-02)
308
+ (0, state_js_1.writeCheckpoint)(statePath, 'gswd/imagine', 'agents-complete');
309
+ // ── Step 4: Synthesize directions ─────────────────────────────────
310
+ const synthesis = (0, imagine_synthesis_js_1.synthesizeDirections)(agentResults);
311
+ // ── Step 5: Direction selection ───────────────────────────────────
312
+ let selected;
313
+ let autoDecisions;
314
+ if (options.autoMode) {
315
+ const autoResult = (0, imagine_synthesis_js_1.autoSelectDirection)(synthesis);
316
+ selected = autoResult.selected;
317
+ autoDecisions = autoResult.decisions;
318
+ }
319
+ else if (options.selectedDirectionIndex !== undefined) {
320
+ const allDirections = [synthesis.proposed, ...synthesis.alternatives];
321
+ const idx = Math.max(0, Math.min(options.selectedDirectionIndex, allDirections.length - 1));
322
+ selected = allDirections[idx];
323
+ }
324
+ else {
325
+ // Default to proposed direction
326
+ selected = synthesis.proposed;
327
+ }
328
+ // Mid-stage checkpoint: direction selected (RESM-02)
329
+ (0, state_js_1.writeCheckpoint)(statePath, 'gswd/imagine', 'direction-selected');
330
+ // ── Step 5.5: Synthesize vision statement (IMAGINE-05) ───────────
331
+ let visionStatement;
332
+ if (options.visionStatement) {
333
+ // User already approved/edited a vision statement (manual mode)
334
+ visionStatement = options.visionStatement;
335
+ }
336
+ else {
337
+ // Auto-generate from brief + selected direction
338
+ visionStatement = synthesizeVisionStatement(brief, selected);
339
+ }
340
+ // ── Step 6: Build artifacts ──────────────────────────────────────
341
+ const imagineContent = buildImagineContent(brief, selected, synthesis, visionStatement, autoDecisions);
342
+ const decisionsContent = buildDecisionsContent(selected, synthesis, brief, visionStatement, autoDecisions);
343
+ const icpContent = buildAgentArtifact('icp-persona', synthesis.raw_agent_outputs, `# Ideal Customer Profile\n\n## ICP Profile\n${selected.icp_summary}\n\n## Pain Points\nDerived from direction analysis.\n`);
344
+ const gtmContent = buildAgentArtifact('positioning', synthesis.raw_agent_outputs, `# Go-to-Market\n\n## Value Proposition\n${selected.differentiator}\n\n## Go-to-Market Angle\nTo be defined in Specify stage.\n`);
345
+ const competitionContent = buildAgentArtifact('market-researcher', synthesis.raw_agent_outputs, `# Competition\n\n## Market Overview\nMarket research data unavailable.\n\n## Competitors\nTo be researched.\n`);
346
+ // ── Step 7: Validate decision gate ───────────────────────────────
347
+ const gateResult = (0, imagine_gate_js_1.validateDecisionGate)(decisionsContent);
348
+ if (!gateResult.passed) {
349
+ // Do NOT update state to done
350
+ return {
351
+ status: 'gate_failed',
352
+ artifacts_written: [],
353
+ gate_result: gateResult,
354
+ selected_direction: selected,
355
+ error: `Decision gate failed: ${gateResult.summary}`,
356
+ };
357
+ }
358
+ // ── Step 8: Write artifacts ──────────────────────────────────────
359
+ const directionsContent = synthesis.raw_agent_outputs['brainstorm-alternatives'] || '';
360
+ const risksRawContent = synthesis.raw_agent_outputs['devils-advocate'] || '';
361
+ const artifacts = [
362
+ { name: 'IMAGINE.md', content: imagineContent },
363
+ { name: 'ICP.md', content: icpContent },
364
+ { name: 'GTM.md', content: gtmContent },
365
+ { name: 'COMPETITION.md', content: competitionContent },
366
+ { name: 'DECISIONS.md', content: decisionsContent },
367
+ ];
368
+ if (directionsContent)
369
+ artifacts.push({ name: 'DIRECTIONS.md', content: directionsContent });
370
+ if (risksRawContent)
371
+ artifacts.push({ name: 'RISKS.md', content: risksRawContent });
372
+ const artifactsWritten = [];
373
+ for (const artifact of artifacts) {
374
+ const artifactPath = path.join(planningDir, artifact.name);
375
+ (0, state_js_1.safeWriteFile)(artifactPath, artifact.content);
376
+ artifactsWritten.push(artifactPath);
377
+ }
378
+ // ── Step 9: Update state ─────────────────────────────────────────
379
+ const finalState = (0, state_js_1.readState)(statePath);
380
+ if (finalState) {
381
+ finalState.stage_status.imagine = 'done';
382
+ finalState.last_checkpoint = {
383
+ workflow: 'gswd/imagine',
384
+ checkpoint_id: 'complete',
385
+ timestamp: new Date().toISOString(),
386
+ };
387
+ // Record auto decisions in state
388
+ if (autoDecisions) {
389
+ finalState.auto.decisions = autoDecisions;
390
+ }
391
+ (0, state_js_1.writeState)(statePath, finalState);
392
+ }
393
+ // ── Step 10: Return result ───────────────────────────────────────
394
+ return {
395
+ status: 'complete',
396
+ artifacts_written: artifactsWritten,
397
+ gate_result: gateResult,
398
+ auto_decisions: autoDecisions,
399
+ selected_direction: selected,
400
+ research_summary: researchSummary,
401
+ vision_statement: visionStatement,
402
+ agent_headlines: agentHeadlines.length > 0 ? agentHeadlines : undefined,
403
+ };
404
+ }
405
+ catch (err) {
406
+ const message = err instanceof Error ? err.message : String(err);
407
+ return {
408
+ status: 'error',
409
+ artifacts_written: [],
410
+ error: message,
411
+ };
412
+ }
413
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * GSWD Intake Module — INTAKE.json persistence for idempotent resume
3
+ *
4
+ * Writes/reads the user's confirmed product description to .planning/gswd/INTAKE.json.
5
+ * Uses atomic write pattern from state.ts (safeWriteJson).
6
+ *
7
+ * INTAKE.json is separate from intake.md:
8
+ * - intake.md: text file passed to bootstrap CLI (imagine agents)
9
+ * - INTAKE.json: machine-readable state for resume
10
+ */
11
+ export interface IntakeData {
12
+ version: 1;
13
+ recorded_at: string;
14
+ product_description: string;
15
+ source: 'interactive' | 'file';
16
+ idea_file?: string;
17
+ }
18
+ /**
19
+ * Write INTAKE.json atomically to the given GSWD directory.
20
+ * Persistence timing: called once after user confirms their product description
21
+ * (the "Did I get that right?" moment in start.md).
22
+ */
23
+ export declare function writeIntake(gswdDir: string, data: Omit<IntakeData, 'version' | 'recorded_at'>): IntakeData;
24
+ /**
25
+ * Read INTAKE.json. Returns null if missing or corrupt.
26
+ */
27
+ export declare function readIntake(gswdDir: string): IntakeData | null;
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ /**
3
+ * GSWD Intake Module — INTAKE.json persistence for idempotent resume
4
+ *
5
+ * Writes/reads the user's confirmed product description to .planning/gswd/INTAKE.json.
6
+ * Uses atomic write pattern from state.ts (safeWriteJson).
7
+ *
8
+ * INTAKE.json is separate from intake.md:
9
+ * - intake.md: text file passed to bootstrap CLI (imagine agents)
10
+ * - INTAKE.json: machine-readable state for resume
11
+ */
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || (function () {
29
+ var ownKeys = function(o) {
30
+ ownKeys = Object.getOwnPropertyNames || function (o) {
31
+ var ar = [];
32
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
+ return ar;
34
+ };
35
+ return ownKeys(o);
36
+ };
37
+ return function (mod) {
38
+ if (mod && mod.__esModule) return mod;
39
+ var result = {};
40
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
+ __setModuleDefault(result, mod);
42
+ return result;
43
+ };
44
+ })();
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.writeIntake = writeIntake;
47
+ exports.readIntake = readIntake;
48
+ const path = __importStar(require("node:path"));
49
+ const fs = __importStar(require("node:fs"));
50
+ const state_js_1 = require("./state.js");
51
+ // ─── CRUD ────────────────────────────────────────────────────────────────────
52
+ /**
53
+ * Write INTAKE.json atomically to the given GSWD directory.
54
+ * Persistence timing: called once after user confirms their product description
55
+ * (the "Did I get that right?" moment in start.md).
56
+ */
57
+ function writeIntake(gswdDir, data) {
58
+ const intake = {
59
+ version: 1,
60
+ recorded_at: new Date().toISOString(),
61
+ ...data,
62
+ };
63
+ const intakePath = path.join(gswdDir, 'INTAKE.json');
64
+ (0, state_js_1.safeWriteJson)(intakePath, intake);
65
+ return intake;
66
+ }
67
+ /**
68
+ * Read INTAKE.json. Returns null if missing or corrupt.
69
+ */
70
+ function readIntake(gswdDir) {
71
+ const intakePath = path.join(gswdDir, 'INTAKE.json');
72
+ try {
73
+ const content = fs.readFileSync(intakePath, 'utf-8');
74
+ const parsed = JSON.parse(content);
75
+ if (typeof parsed.product_description !== 'string')
76
+ return null;
77
+ return parsed;
78
+ }
79
+ catch {
80
+ return null;
81
+ }
82
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * GSWD Parse Module — ID extraction, heading validation, normalization
3
+ *
4
+ * Parses GSWD artifact files for IDs (J-NNN, FR-NNN, NFR-NNN, I-NNN, C-NNN),
5
+ * validates required heading structure, and normalizes malformed IDs.
6
+ *
7
+ * Schema: GSWD_SPEC.md Section 6.1-6.3
8
+ */
9
+ /**
10
+ * Required headings per artifact file type, from GSWD_SPEC Section 6.3.
11
+ * These are the stable anchors used by audit/compile parsers.
12
+ */
13
+ export declare const REQUIRED_HEADINGS: Record<string, string[]>;
14
+ /**
15
+ * Normalize an ID to canonical format: PREFIX-NNN (3-digit minimum, zero-padded).
16
+ *
17
+ * - FR-1 -> FR-001
18
+ * - FR-01 -> FR-001
19
+ * - FR-001 -> FR-001 (no change)
20
+ * - FR-1000 -> FR-1000 (4+ digits kept as-is)
21
+ * - INVALID -> INVALID (unrecognized format returned as-is)
22
+ */
23
+ export declare function normalizeId(rawId: string): string;
24
+ export interface ExtractedId {
25
+ id: string;
26
+ raw: string;
27
+ normalized: boolean;
28
+ }
29
+ /**
30
+ * Extract IDs from content using regex.
31
+ *
32
+ * Regex: /\b(J|FR|NFR|I|C)-(\d{1,4})\b/g
33
+ * - Word boundary prevents partial matches (e.g., INFRASTRUCTURE-001)
34
+ * - Returns deduplicated array sorted by normalized ID ascending
35
+ * - Optional filter by idType (e.g., 'FR')
36
+ */
37
+ export declare function extractIds(content: string, idType?: string): ExtractedId[];
38
+ export interface HeadingValidation {
39
+ valid: boolean;
40
+ missing: string[];
41
+ present: string[];
42
+ }
43
+ /**
44
+ * Validate that a file contains all required headings for its type.
45
+ *
46
+ * - Looks up required headings from REQUIRED_HEADINGS[fileType]
47
+ * - Case-insensitive search as safety layer
48
+ * - Returns which headings are present and which are missing
49
+ * - Unknown file types are always valid (no required headings)
50
+ */
51
+ export declare function validateHeadings(content: string, fileType: string): HeadingValidation;
52
+ /**
53
+ * Extract content between a heading and the next heading of same or higher level.
54
+ *
55
+ * @param content - Full file content
56
+ * @param heading - The heading to extract content from (e.g., "## Section A")
57
+ * @returns Content string (trimmed) or null if heading not found
58
+ */
59
+ export declare function extractHeadingContent(content: string, heading: string): string | null;