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,406 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tests for GSWD Imagine Workflow Orchestrator
|
|
4
|
+
*
|
|
5
|
+
* Integration-level tests using mock spawnFn and temp directories.
|
|
6
|
+
* Covers: full workflow, gate enforcement, auto mode, error handling.
|
|
7
|
+
* Minimum 10 test cases per Plan 02-05.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
const node_test_1 = require("node:test");
|
|
44
|
+
const assert = __importStar(require("node:assert"));
|
|
45
|
+
const fs = __importStar(require("node:fs"));
|
|
46
|
+
const path = __importStar(require("node:path"));
|
|
47
|
+
const os = __importStar(require("node:os"));
|
|
48
|
+
const imagine_js_1 = require("../lib/imagine.js");
|
|
49
|
+
const state_js_1 = require("../lib/state.js");
|
|
50
|
+
const config_js_1 = require("../lib/config.js");
|
|
51
|
+
// ─── Test Fixtures ───────────────────────────────────────────────────────────
|
|
52
|
+
/** Realistic mock agent outputs for a complete workflow */
|
|
53
|
+
const MOCK_AGENT_OUTPUTS = {
|
|
54
|
+
'market-researcher': `## Market Overview
|
|
55
|
+
The developer tools market is expanding rapidly. Specification tools remain a gap.
|
|
56
|
+
|
|
57
|
+
## Competitors
|
|
58
|
+
- Notion AI: General-purpose doc editing
|
|
59
|
+
- Linear: Project management
|
|
60
|
+
- Coda AI: Document automation
|
|
61
|
+
|
|
62
|
+
## Market Gaps
|
|
63
|
+
No tool converts ideas into execution-grade specs automatically.
|
|
64
|
+
|
|
65
|
+
## Opportunities
|
|
66
|
+
Open source community adoption through GitHub and Discord channels.`,
|
|
67
|
+
'icp-persona': `## ICP Profile
|
|
68
|
+
Solo SaaS founders who code but skip specifications. Technical enough for CLI tools but struggle with product thinking.
|
|
69
|
+
|
|
70
|
+
## Pain Points
|
|
71
|
+
- Pain of manual spec writing: tedious, slow, frustrating
|
|
72
|
+
- Waste time building wrong features
|
|
73
|
+
- Struggle to think through edge cases
|
|
74
|
+
|
|
75
|
+
## Willingness to Pay
|
|
76
|
+
Early-stage founders have limited budget but will pay for tools that save time. Price point $29-49/mo for premium subscription.
|
|
77
|
+
|
|
78
|
+
## Reachability
|
|
79
|
+
Active on Indie Hackers, Twitter, Hacker News. Attend virtual meetups and subscribe to community newsletters.`,
|
|
80
|
+
'positioning': `## Value Proposition
|
|
81
|
+
For solo founders who struggle with specifications, GSWD is a CLI tool that turns fuzzy ideas into execution-grade specs.
|
|
82
|
+
|
|
83
|
+
## Positioning Statement
|
|
84
|
+
Category: Developer productivity
|
|
85
|
+
Differentiation: AI-powered spec generation vs manual templates
|
|
86
|
+
|
|
87
|
+
## Key Differentiators
|
|
88
|
+
- Automated research agents vs manual brainstorming
|
|
89
|
+
- Deterministic compilation to project plans
|
|
90
|
+
|
|
91
|
+
## Go-to-Market Angle
|
|
92
|
+
Channel: Indie Hackers, open source GitHub community
|
|
93
|
+
Wedge: Free CLI that generates DECISIONS.md from 3-question intake`,
|
|
94
|
+
'brainstorm-alternatives': `## Direction 1: CLI Spec Generator
|
|
95
|
+
**ICP:** Solo SaaS founders who code but don't spec
|
|
96
|
+
**Problem:** Founders skip specs, leading to painful rework and wasted build cycles
|
|
97
|
+
**Wedge:** 3-question CLI intake that produces DECISIONS.md in 5 minutes
|
|
98
|
+
**Differentiator:** AI agents do the research a PM would do
|
|
99
|
+
**MVP scope:** Imagine stage only (intake to decisions)
|
|
100
|
+
**Risk:** Founders may not trust AI-generated specs
|
|
101
|
+
|
|
102
|
+
## Direction 2: Team Alignment Tool
|
|
103
|
+
**ICP:** Small engineering teams (2-5) at early startups who pay for premium tools
|
|
104
|
+
**Problem:** Misalignment between founder vision and dev execution is expensive and frustrating
|
|
105
|
+
**Wedge:** Shared spec generated from async inputs
|
|
106
|
+
**Differentiator:** Bridges business thinking and technical execution
|
|
107
|
+
**MVP scope:** Collaborative editing with conflict detection
|
|
108
|
+
**Risk:** Multi-user features are slow and complex to build
|
|
109
|
+
|
|
110
|
+
## Direction 3: Idea Validator
|
|
111
|
+
**ICP:** Pre-revenue founders exploring ideas via community forums and Reddit
|
|
112
|
+
**Problem:** Founders invest months building products nobody wants — a tedious manual waste
|
|
113
|
+
**Wedge:** Instant market viability score from one-paragraph idea
|
|
114
|
+
**Differentiator:** Transparent scoring algorithm, not a black box
|
|
115
|
+
**MVP scope:** Input to score to risks to go/no-go
|
|
116
|
+
**Risk:** Scoring without real data feels unreliable`,
|
|
117
|
+
'devils-advocate': `## Assumptions Challenged
|
|
118
|
+
- Assumption: Founders want CLI tools — Plausible but niche
|
|
119
|
+
|
|
120
|
+
## Risks
|
|
121
|
+
- **Risk:** AI specs may be too generic
|
|
122
|
+
- **Likelihood:** Medium
|
|
123
|
+
- **Impact:** High
|
|
124
|
+
- **Risk:** Solo founders may not pay — expensive alternatives exist
|
|
125
|
+
- **Likelihood:** High
|
|
126
|
+
- **Impact:** High
|
|
127
|
+
- **Risk:** LLM quality degrades over time
|
|
128
|
+
- **Likelihood:** Low
|
|
129
|
+
- **Impact:** Medium
|
|
130
|
+
- **Risk:** Manual spec habit is hard to break — struggle to change
|
|
131
|
+
- **Likelihood:** Medium
|
|
132
|
+
- **Impact:** Medium
|
|
133
|
+
- **Risk:** Competitors add spec features to existing tools
|
|
134
|
+
- **Likelihood:** Medium
|
|
135
|
+
- **Impact:** High
|
|
136
|
+
|
|
137
|
+
## Mitigations
|
|
138
|
+
- Validate with 10 user interviews before premium features
|
|
139
|
+
- Open source core to reduce friction via community channels
|
|
140
|
+
|
|
141
|
+
## Red Flags
|
|
142
|
+
- If no one completes intake in beta, rethink UX`,
|
|
143
|
+
};
|
|
144
|
+
/** Mock spawn function returning realistic agent outputs */
|
|
145
|
+
function createMockSpawnFn() {
|
|
146
|
+
return async (prompt) => {
|
|
147
|
+
for (const [agentName, output] of Object.entries(MOCK_AGENT_OUTPUTS)) {
|
|
148
|
+
if (prompt.includes(`name: ${agentName}`) || prompt.includes(agentName)) {
|
|
149
|
+
return output;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return '## Output\nGeneric agent output.';
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/** Create a temporary test directory with initialized state + config */
|
|
156
|
+
function createTestEnv() {
|
|
157
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gswd-imagine-test-'));
|
|
158
|
+
const planningDir = tmpDir;
|
|
159
|
+
const gswdDir = path.join(planningDir, 'gswd');
|
|
160
|
+
fs.mkdirSync(gswdDir, { recursive: true });
|
|
161
|
+
// Initialize STATE.json
|
|
162
|
+
const state = (0, state_js_1.createDefaultState)('test-project');
|
|
163
|
+
const statePath = path.join(gswdDir, 'STATE.json');
|
|
164
|
+
(0, state_js_1.safeWriteJson)(statePath, state);
|
|
165
|
+
// Initialize config.json
|
|
166
|
+
const configPath = path.join(planningDir, 'config.json');
|
|
167
|
+
(0, config_js_1.mergeGswdConfig)(configPath);
|
|
168
|
+
return {
|
|
169
|
+
planningDir,
|
|
170
|
+
configPath,
|
|
171
|
+
cleanup: () => {
|
|
172
|
+
try {
|
|
173
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
// ignore cleanup errors
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/** Create a test idea file in the given directory */
|
|
182
|
+
function createIdeaFile(dir) {
|
|
183
|
+
const ideaPath = path.join(dir, 'idea.md');
|
|
184
|
+
const content = `# GSWD — Get Shit Well Done
|
|
185
|
+
|
|
186
|
+
A CLI tool that turns unstructured founder ideas into execution-grade specifications.
|
|
187
|
+
|
|
188
|
+
## Target Audience
|
|
189
|
+
Built for solo founders building SaaS products who are technical enough to use terminal tools but struggle with product thinking and specification writing.
|
|
190
|
+
|
|
191
|
+
## Why Now
|
|
192
|
+
LLMs can now understand product requirements deeply. The gap between idea and execution is the biggest problem founders face — there's a growing frustration with manual spec writing.
|
|
193
|
+
|
|
194
|
+
## Key Themes
|
|
195
|
+
- CLI-first developer experience
|
|
196
|
+
- Automated research and brainstorming
|
|
197
|
+
- Deterministic spec compilation
|
|
198
|
+
- Zero-dependency Node.js tooling
|
|
199
|
+
`;
|
|
200
|
+
fs.writeFileSync(ideaPath, content, 'utf-8');
|
|
201
|
+
return ideaPath;
|
|
202
|
+
}
|
|
203
|
+
// ─── Tests ───────────────────────────────────────────────────────────────────
|
|
204
|
+
(0, node_test_1.describe)('Imagine workflow with mock agents', () => {
|
|
205
|
+
let env;
|
|
206
|
+
(0, node_test_1.beforeEach)(() => {
|
|
207
|
+
env = createTestEnv();
|
|
208
|
+
});
|
|
209
|
+
(0, node_test_1.afterEach)(() => {
|
|
210
|
+
env.cleanup();
|
|
211
|
+
});
|
|
212
|
+
(0, node_test_1.it)('runImagine with ideaFilePath and mock spawnFn produces all 5 artifacts', async () => {
|
|
213
|
+
const ideaPath = createIdeaFile(env.planningDir);
|
|
214
|
+
const result = await (0, imagine_js_1.runImagine)({
|
|
215
|
+
ideaFilePath: ideaPath,
|
|
216
|
+
autoMode: true,
|
|
217
|
+
planningDir: env.planningDir,
|
|
218
|
+
configPath: env.configPath,
|
|
219
|
+
spawnFn: createMockSpawnFn(),
|
|
220
|
+
});
|
|
221
|
+
assert.strictEqual(result.status, 'complete', `Expected complete but got: ${result.error || result.gate_result?.summary}`);
|
|
222
|
+
assert.strictEqual(result.artifacts_written.length, 5, 'Should write 5 artifacts');
|
|
223
|
+
});
|
|
224
|
+
(0, node_test_1.it)('runImagine with intakeAnswers and mock spawnFn produces all 5 artifacts', async () => {
|
|
225
|
+
const result = await (0, imagine_js_1.runImagine)({
|
|
226
|
+
intakeAnswers: {
|
|
227
|
+
vision: 'A CLI tool that turns ideas into execution-grade specifications',
|
|
228
|
+
user: 'Solo SaaS founders who code but skip product specs',
|
|
229
|
+
whyNow: 'LLMs now understand product requirements — growing frustration with manual specs',
|
|
230
|
+
},
|
|
231
|
+
autoMode: true,
|
|
232
|
+
planningDir: env.planningDir,
|
|
233
|
+
configPath: env.configPath,
|
|
234
|
+
spawnFn: createMockSpawnFn(),
|
|
235
|
+
});
|
|
236
|
+
assert.strictEqual(result.status, 'complete', `Expected complete but got: ${result.error || result.gate_result?.summary}`);
|
|
237
|
+
assert.strictEqual(result.artifacts_written.length, 5);
|
|
238
|
+
});
|
|
239
|
+
(0, node_test_1.it)('artifacts exist on disk after successful run', async () => {
|
|
240
|
+
const ideaPath = createIdeaFile(env.planningDir);
|
|
241
|
+
const result = await (0, imagine_js_1.runImagine)({
|
|
242
|
+
ideaFilePath: ideaPath,
|
|
243
|
+
autoMode: true,
|
|
244
|
+
planningDir: env.planningDir,
|
|
245
|
+
configPath: env.configPath,
|
|
246
|
+
spawnFn: createMockSpawnFn(),
|
|
247
|
+
});
|
|
248
|
+
assert.strictEqual(result.status, 'complete');
|
|
249
|
+
const expectedFiles = ['IMAGINE.md', 'ICP.md', 'GTM.md', 'COMPETITION.md', 'DECISIONS.md'];
|
|
250
|
+
for (const file of expectedFiles) {
|
|
251
|
+
const filePath = path.join(env.planningDir, file);
|
|
252
|
+
assert.ok(fs.existsSync(filePath), `${file} should exist on disk`);
|
|
253
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
254
|
+
assert.ok(content.length > 0, `${file} should have content`);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
(0, node_test_1.it)('STATE.json shows imagine: done after successful run', async () => {
|
|
258
|
+
const ideaPath = createIdeaFile(env.planningDir);
|
|
259
|
+
await (0, imagine_js_1.runImagine)({
|
|
260
|
+
ideaFilePath: ideaPath,
|
|
261
|
+
autoMode: true,
|
|
262
|
+
planningDir: env.planningDir,
|
|
263
|
+
configPath: env.configPath,
|
|
264
|
+
spawnFn: createMockSpawnFn(),
|
|
265
|
+
});
|
|
266
|
+
const statePath = path.join(env.planningDir, 'gswd', 'STATE.json');
|
|
267
|
+
const state = JSON.parse(fs.readFileSync(statePath, 'utf-8'));
|
|
268
|
+
assert.strictEqual(state.stage_status.imagine, 'done');
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
(0, node_test_1.describe)('Decision gate enforcement', () => {
|
|
272
|
+
let env;
|
|
273
|
+
(0, node_test_1.beforeEach)(() => {
|
|
274
|
+
env = createTestEnv();
|
|
275
|
+
});
|
|
276
|
+
(0, node_test_1.afterEach)(() => {
|
|
277
|
+
env.cleanup();
|
|
278
|
+
});
|
|
279
|
+
(0, node_test_1.it)('skipResearch with minimal brief still produces gate-passing output', async () => {
|
|
280
|
+
const ideaPath = createIdeaFile(env.planningDir);
|
|
281
|
+
const result = await (0, imagine_js_1.runImagine)({
|
|
282
|
+
ideaFilePath: ideaPath,
|
|
283
|
+
autoMode: true,
|
|
284
|
+
skipResearch: true,
|
|
285
|
+
planningDir: env.planningDir,
|
|
286
|
+
configPath: env.configPath,
|
|
287
|
+
});
|
|
288
|
+
// skipResearch produces minimal agent data — the orchestrator should still
|
|
289
|
+
// build valid DECISIONS.md with enough frozen decisions to pass the gate
|
|
290
|
+
assert.strictEqual(result.status, 'complete', `Expected complete but got: ${result.error || result.gate_result?.summary}`);
|
|
291
|
+
});
|
|
292
|
+
(0, node_test_1.it)('gate result details are included in ImagineResult', async () => {
|
|
293
|
+
const ideaPath = createIdeaFile(env.planningDir);
|
|
294
|
+
const result = await (0, imagine_js_1.runImagine)({
|
|
295
|
+
ideaFilePath: ideaPath,
|
|
296
|
+
autoMode: true,
|
|
297
|
+
planningDir: env.planningDir,
|
|
298
|
+
configPath: env.configPath,
|
|
299
|
+
spawnFn: createMockSpawnFn(),
|
|
300
|
+
});
|
|
301
|
+
assert.ok(result.gate_result, 'Should include gate result');
|
|
302
|
+
assert.ok(typeof result.gate_result.passed === 'boolean', 'Gate result should have passed field');
|
|
303
|
+
assert.ok(result.gate_result.summary, 'Gate result should have summary');
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
(0, node_test_1.describe)('Auto mode', () => {
|
|
307
|
+
let env;
|
|
308
|
+
(0, node_test_1.beforeEach)(() => {
|
|
309
|
+
env = createTestEnv();
|
|
310
|
+
});
|
|
311
|
+
(0, node_test_1.afterEach)(() => {
|
|
312
|
+
env.cleanup();
|
|
313
|
+
});
|
|
314
|
+
(0, node_test_1.it)('runImagine with autoMode=true produces AutoDecision[] in result', async () => {
|
|
315
|
+
const ideaPath = createIdeaFile(env.planningDir);
|
|
316
|
+
const result = await (0, imagine_js_1.runImagine)({
|
|
317
|
+
ideaFilePath: ideaPath,
|
|
318
|
+
autoMode: true,
|
|
319
|
+
planningDir: env.planningDir,
|
|
320
|
+
configPath: env.configPath,
|
|
321
|
+
spawnFn: createMockSpawnFn(),
|
|
322
|
+
});
|
|
323
|
+
assert.strictEqual(result.status, 'complete');
|
|
324
|
+
assert.ok(result.auto_decisions, 'Should have auto_decisions');
|
|
325
|
+
assert.ok(result.auto_decisions.length >= 3, 'Should have at least 3 auto decisions');
|
|
326
|
+
const types = result.auto_decisions.map(d => d.type);
|
|
327
|
+
assert.ok(types.includes('direction'), 'Should have direction decision');
|
|
328
|
+
assert.ok(types.includes('icp'), 'Should have ICP decision');
|
|
329
|
+
assert.ok(types.includes('wedge'), 'Should have wedge decision');
|
|
330
|
+
});
|
|
331
|
+
(0, node_test_1.it)('STATE.json auto.decisions is populated after auto run', async () => {
|
|
332
|
+
const ideaPath = createIdeaFile(env.planningDir);
|
|
333
|
+
await (0, imagine_js_1.runImagine)({
|
|
334
|
+
ideaFilePath: ideaPath,
|
|
335
|
+
autoMode: true,
|
|
336
|
+
planningDir: env.planningDir,
|
|
337
|
+
configPath: env.configPath,
|
|
338
|
+
spawnFn: createMockSpawnFn(),
|
|
339
|
+
});
|
|
340
|
+
const statePath = path.join(env.planningDir, 'gswd', 'STATE.json');
|
|
341
|
+
const state = JSON.parse(fs.readFileSync(statePath, 'utf-8'));
|
|
342
|
+
assert.ok(state.auto.decisions, 'State should have auto.decisions');
|
|
343
|
+
assert.ok(Array.isArray(state.auto.decisions), 'auto.decisions should be an array');
|
|
344
|
+
assert.ok(state.auto.decisions.length >= 3, 'Should have at least 3 decisions');
|
|
345
|
+
});
|
|
346
|
+
(0, node_test_1.it)('DECISIONS.md contains Auto-chosen markers in auto mode', async () => {
|
|
347
|
+
const ideaPath = createIdeaFile(env.planningDir);
|
|
348
|
+
await (0, imagine_js_1.runImagine)({
|
|
349
|
+
ideaFilePath: ideaPath,
|
|
350
|
+
autoMode: true,
|
|
351
|
+
planningDir: env.planningDir,
|
|
352
|
+
configPath: env.configPath,
|
|
353
|
+
spawnFn: createMockSpawnFn(),
|
|
354
|
+
});
|
|
355
|
+
const decisionsPath = path.join(env.planningDir, 'DECISIONS.md');
|
|
356
|
+
const content = fs.readFileSync(decisionsPath, 'utf-8');
|
|
357
|
+
assert.ok(content.includes('Auto-chosen'), 'DECISIONS.md should contain Auto-chosen markers');
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
(0, node_test_1.describe)('Error handling', () => {
|
|
361
|
+
let env;
|
|
362
|
+
(0, node_test_1.beforeEach)(() => {
|
|
363
|
+
env = createTestEnv();
|
|
364
|
+
});
|
|
365
|
+
(0, node_test_1.afterEach)(() => {
|
|
366
|
+
env.cleanup();
|
|
367
|
+
});
|
|
368
|
+
(0, node_test_1.it)('no input (no file, no answers) returns error result', async () => {
|
|
369
|
+
const result = await (0, imagine_js_1.runImagine)({
|
|
370
|
+
planningDir: env.planningDir,
|
|
371
|
+
configPath: env.configPath,
|
|
372
|
+
});
|
|
373
|
+
assert.strictEqual(result.status, 'error');
|
|
374
|
+
assert.ok(result.error, 'Should have error message');
|
|
375
|
+
assert.ok(result.error.includes('No input'), 'Error should mention missing input');
|
|
376
|
+
});
|
|
377
|
+
(0, node_test_1.it)('non-existent idea file returns error result', async () => {
|
|
378
|
+
const result = await (0, imagine_js_1.runImagine)({
|
|
379
|
+
ideaFilePath: '/nonexistent/idea.md',
|
|
380
|
+
planningDir: env.planningDir,
|
|
381
|
+
configPath: env.configPath,
|
|
382
|
+
});
|
|
383
|
+
assert.strictEqual(result.status, 'error');
|
|
384
|
+
assert.ok(result.error, 'Should have error message');
|
|
385
|
+
});
|
|
386
|
+
(0, node_test_1.it)('missing STATE.json returns error result', async () => {
|
|
387
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gswd-imagine-nostate-'));
|
|
388
|
+
const gswdDir = path.join(tmpDir, 'gswd');
|
|
389
|
+
fs.mkdirSync(gswdDir, { recursive: true });
|
|
390
|
+
const result = await (0, imagine_js_1.runImagine)({
|
|
391
|
+
intakeAnswers: {
|
|
392
|
+
vision: 'A test product for testing purposes',
|
|
393
|
+
user: 'Test users who test things',
|
|
394
|
+
whyNow: 'Testing is important right now',
|
|
395
|
+
},
|
|
396
|
+
planningDir: tmpDir,
|
|
397
|
+
configPath: path.join(tmpDir, 'config.json'),
|
|
398
|
+
});
|
|
399
|
+
assert.strictEqual(result.status, 'error');
|
|
400
|
+
assert.ok(result.error?.includes('STATE.json'), 'Error should mention STATE.json');
|
|
401
|
+
try {
|
|
402
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
403
|
+
}
|
|
404
|
+
catch { /* ignore */ }
|
|
405
|
+
});
|
|
406
|
+
});
|