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.
- package/bin/gswd-tools.cjs +228 -0
- package/bin/install.js +8 -0
- package/commands/gswd/imagine.md +7 -1
- package/commands/gswd/start.md +507 -32
- package/dist/lib/audit.d.ts +205 -0
- package/dist/lib/audit.js +805 -0
- package/dist/lib/bootstrap.d.ts +103 -0
- package/dist/lib/bootstrap.js +563 -0
- package/dist/lib/compile.d.ts +239 -0
- package/dist/lib/compile.js +1152 -0
- package/dist/lib/config.d.ts +49 -0
- package/dist/lib/config.js +150 -0
- package/dist/lib/imagine-agents.d.ts +54 -0
- package/dist/lib/imagine-agents.js +185 -0
- package/dist/lib/imagine-gate.d.ts +47 -0
- package/dist/lib/imagine-gate.js +131 -0
- package/dist/lib/imagine-input.d.ts +46 -0
- package/dist/lib/imagine-input.js +233 -0
- package/dist/lib/imagine-synthesis.d.ts +90 -0
- package/dist/lib/imagine-synthesis.js +453 -0
- package/dist/lib/imagine.d.ts +56 -0
- package/dist/lib/imagine.js +413 -0
- package/dist/lib/intake.d.ts +27 -0
- package/dist/lib/intake.js +82 -0
- package/dist/lib/parse.d.ts +59 -0
- package/dist/lib/parse.js +171 -0
- package/dist/lib/render.d.ts +309 -0
- package/dist/lib/render.js +624 -0
- package/dist/lib/specify-agents.d.ts +120 -0
- package/dist/lib/specify-agents.js +269 -0
- package/dist/lib/specify-journeys.d.ts +124 -0
- package/dist/lib/specify-journeys.js +279 -0
- package/dist/lib/specify-nfr.d.ts +45 -0
- package/dist/lib/specify-nfr.js +159 -0
- package/dist/lib/specify-roles.d.ts +46 -0
- package/dist/lib/specify-roles.js +88 -0
- package/dist/lib/specify.d.ts +70 -0
- package/dist/lib/specify.js +676 -0
- package/dist/lib/state.d.ts +140 -0
- package/dist/lib/state.js +340 -0
- package/dist/tests/audit.test.d.ts +4 -0
- package/dist/tests/audit.test.js +1579 -0
- package/dist/tests/bootstrap.test.d.ts +5 -0
- package/dist/tests/bootstrap.test.js +611 -0
- package/dist/tests/compile.test.d.ts +4 -0
- package/dist/tests/compile.test.js +862 -0
- package/dist/tests/config.test.d.ts +4 -0
- package/dist/tests/config.test.js +191 -0
- package/dist/tests/imagine-agents.test.d.ts +6 -0
- package/dist/tests/imagine-agents.test.js +179 -0
- package/dist/tests/imagine-gate.test.d.ts +6 -0
- package/dist/tests/imagine-gate.test.js +264 -0
- package/dist/tests/imagine-input.test.d.ts +6 -0
- package/dist/tests/imagine-input.test.js +283 -0
- package/dist/tests/imagine-synthesis.test.d.ts +7 -0
- package/dist/tests/imagine-synthesis.test.js +380 -0
- package/dist/tests/imagine.test.d.ts +8 -0
- package/dist/tests/imagine.test.js +406 -0
- package/dist/tests/parse.test.d.ts +4 -0
- package/dist/tests/parse.test.js +285 -0
- package/dist/tests/render.test.d.ts +4 -0
- package/dist/tests/render.test.js +236 -0
- package/dist/tests/specify-agents.test.d.ts +4 -0
- package/dist/tests/specify-agents.test.js +352 -0
- package/dist/tests/specify-journeys.test.d.ts +5 -0
- package/dist/tests/specify-journeys.test.js +440 -0
- package/dist/tests/specify-nfr.test.d.ts +4 -0
- package/dist/tests/specify-nfr.test.js +205 -0
- package/dist/tests/specify-roles.test.d.ts +4 -0
- package/dist/tests/specify-roles.test.js +136 -0
- package/dist/tests/specify.test.d.ts +9 -0
- package/dist/tests/specify.test.js +544 -0
- package/dist/tests/state.test.d.ts +4 -0
- package/dist/tests/state.test.js +316 -0
- package/lib/bootstrap.ts +37 -11
- package/lib/compile.ts +426 -4
- package/lib/imagine-agents.ts +53 -7
- package/lib/imagine-synthesis.ts +170 -6
- package/lib/imagine.ts +59 -5
- package/lib/intake.ts +60 -0
- package/lib/parse.ts +2 -1
- package/lib/render.ts +566 -5
- package/lib/specify-agents.ts +25 -3
- package/lib/state.ts +115 -0
- package/package.json +4 -2
- 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;
|