specwf 0.2.2 → 0.3.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 (56) hide show
  1. package/README.md +96 -59
  2. package/bin/cli.d.ts +2 -0
  3. package/bin/cli.js +4425 -0
  4. package/bin/specwf.js +4424 -1
  5. package/dist/cli.js +3821 -1462
  6. package/package.json +2 -1
  7. package/dist/templates/agents/archiver.md +0 -112
  8. package/dist/templates/agents/executor.md +0 -125
  9. package/dist/templates/agents/planner.md +0 -114
  10. package/dist/templates/agents/researcher.md +0 -104
  11. package/dist/templates/agents/reviewer.md +0 -98
  12. package/dist/templates/agents/verifier.md +0 -129
  13. package/dist/templates/artifacts/context.md +0 -151
  14. package/dist/templates/artifacts/design.md +0 -224
  15. package/dist/templates/artifacts/goal-review.md +0 -95
  16. package/dist/templates/artifacts/project.yml +0 -117
  17. package/dist/templates/artifacts/proposal.md +0 -124
  18. package/dist/templates/artifacts/quality-review.md +0 -131
  19. package/dist/templates/artifacts/research.md +0 -127
  20. package/dist/templates/artifacts/spec-review.md +0 -84
  21. package/dist/templates/artifacts/state.md +0 -158
  22. package/dist/templates/artifacts/summary.md +0 -129
  23. package/dist/templates/artifacts/tasks.md +0 -109
  24. package/dist/templates/artifacts/verification.md +0 -113
  25. package/dist/templates/commands/adhoc.md +0 -38
  26. package/dist/templates/commands/apply.md +0 -20
  27. package/dist/templates/commands/archive.md +0 -21
  28. package/dist/templates/commands/continue.md +0 -27
  29. package/dist/templates/commands/discuss.md +0 -24
  30. package/dist/templates/commands/grill.md +0 -24
  31. package/dist/templates/commands/init.md +0 -37
  32. package/dist/templates/commands/milestone.md +0 -27
  33. package/dist/templates/commands/plan.md +0 -22
  34. package/dist/templates/commands/research-phase.md +0 -20
  35. package/dist/templates/commands/research.md +0 -24
  36. package/dist/templates/commands/review.md +0 -20
  37. package/dist/templates/commands/roadmap.md +0 -23
  38. package/dist/templates/commands/ship.md +0 -36
  39. package/dist/templates/commands/split.md +0 -16
  40. package/dist/templates/commands/verify.md +0 -22
  41. package/dist/templates/skills/adhoc.md +0 -53
  42. package/dist/templates/skills/apply.md +0 -134
  43. package/dist/templates/skills/archive.md +0 -109
  44. package/dist/templates/skills/continue.md +0 -97
  45. package/dist/templates/skills/discuss.md +0 -95
  46. package/dist/templates/skills/grill.md +0 -90
  47. package/dist/templates/skills/init.md +0 -116
  48. package/dist/templates/skills/milestone.md +0 -36
  49. package/dist/templates/skills/plan.md +0 -144
  50. package/dist/templates/skills/research-phase.md +0 -66
  51. package/dist/templates/skills/research.md +0 -76
  52. package/dist/templates/skills/review.md +0 -148
  53. package/dist/templates/skills/roadmap.md +0 -104
  54. package/dist/templates/skills/ship.md +0 -94
  55. package/dist/templates/skills/split.md +0 -96
  56. package/dist/templates/skills/verify.md +0 -147
package/bin/cli.js ADDED
@@ -0,0 +1,4425 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import { program } from "commander";
5
+ import { readFileSync as readFileSync12 } from "fs";
6
+ import { join as join19, dirname as dirname3 } from "path";
7
+ import { fileURLToPath as fileURLToPath2 } from "url";
8
+
9
+ // src/commands/specwf-init.ts
10
+ import { join as join6 } from "path";
11
+
12
+ // src/core/config.ts
13
+ import { join } from "path";
14
+ import { existsSync } from "fs";
15
+ import { z } from "zod";
16
+
17
+ // src/parser/yaml.ts
18
+ import { readFileSync, writeFileSync } from "fs";
19
+ import { parse, parseDocument } from "yaml";
20
+ function readYamlDoc(path) {
21
+ return parseDocument(readFileSync(path, "utf-8"));
22
+ }
23
+ function writeYamlDoc(path, doc) {
24
+ writeFileSync(path, String(doc), "utf-8");
25
+ }
26
+
27
+ // src/types/config.ts
28
+ var PROFILE_MODEL_MAP = {
29
+ lite: {
30
+ research: "default",
31
+ plan: "default",
32
+ execute: "default",
33
+ review: "default",
34
+ verify: "default",
35
+ archive: "smol"
36
+ },
37
+ standard: {
38
+ research: "slow",
39
+ plan: "slow",
40
+ execute: "default",
41
+ review: "slow",
42
+ verify: "default",
43
+ archive: "default"
44
+ },
45
+ strict: {
46
+ research: "slow:high",
47
+ plan: "slow:high",
48
+ execute: "slow",
49
+ review: "slow:high",
50
+ verify: "slow",
51
+ archive: "default"
52
+ }
53
+ };
54
+
55
+ // src/core/config.ts
56
+ import { Document as Document2 } from "yaml";
57
+ var CONFIG_FILE = "project.yml";
58
+ var ProjectConfigSchema = z.object({
59
+ version: z.number(),
60
+ platform: z.array(z.string()),
61
+ profile: z.enum(["lite", "standard", "strict"]),
62
+ context: z.string(),
63
+ workflow: z.object({
64
+ research: z.boolean().optional(),
65
+ plan_check: z.boolean().optional(),
66
+ tdd: z.boolean().optional(),
67
+ triple_review: z.boolean().optional(),
68
+ auto_advance: z.boolean().optional(),
69
+ spec_injection: z.boolean().optional()
70
+ }).optional().default({}),
71
+ review: z.object({
72
+ gate: z.enum(["all-pass", "severity", "report-only"]).optional(),
73
+ parallel: z.boolean().optional()
74
+ }).optional().default({}),
75
+ change: z.object({
76
+ parallel: z.enum(["serial", "dependency-graph", "pipeline"]).optional(),
77
+ isolation: z.boolean().optional()
78
+ }).optional().default({}),
79
+ git: z.object({
80
+ branching: z.enum(["none", "phase", "milestone"]).optional(),
81
+ create_tag: z.boolean().optional()
82
+ }).optional().default({}),
83
+ conventions: z.object({
84
+ inject: z.boolean().optional().default(true)
85
+ }).optional().default({ inject: true }),
86
+ models: z.record(z.string(), z.string()).optional().default({})
87
+ });
88
+ function configPath(specwfDir) {
89
+ return join(specwfDir, CONFIG_FILE);
90
+ }
91
+ function loadConfig(specwfDir) {
92
+ const doc = readYamlDoc(configPath(specwfDir));
93
+ const raw = doc.toJS();
94
+ return ProjectConfigSchema.parse(raw);
95
+ }
96
+ function saveConfig(specwfDir, config) {
97
+ let doc;
98
+ if (existsSync(configPath(specwfDir))) {
99
+ doc = readYamlDoc(configPath(specwfDir));
100
+ } else {
101
+ doc = new Document2({});
102
+ }
103
+ doc.set("version", config.version);
104
+ doc.set("platform", config.platform);
105
+ doc.set("profile", config.profile);
106
+ doc.set("context", config.context);
107
+ if (config.workflow) doc.set("workflow", config.workflow);
108
+ if (config.review) doc.set("review", config.review);
109
+ if (config.change) doc.set("change", config.change);
110
+ if (config.git) doc.set("git", config.git);
111
+ if (config.conventions) doc.set("conventions", config.conventions);
112
+ if (config.models) doc.set("models", config.models);
113
+ writeYamlDoc(configPath(specwfDir), doc);
114
+ }
115
+ function updateConfig(specwfDir, updater) {
116
+ const config = loadConfig(specwfDir);
117
+ updater(config);
118
+ saveConfig(specwfDir, config);
119
+ }
120
+ function resolveModels(config) {
121
+ const profile = config.profile;
122
+ const defaults = PROFILE_MODEL_MAP[profile];
123
+ return { ...defaults, ...config.models };
124
+ }
125
+
126
+ // src/core/file-tree.ts
127
+ import { mkdirSync, existsSync as existsSync2, readdirSync, statSync } from "fs";
128
+ import { join as join2 } from "path";
129
+ import { renameSync } from "fs";
130
+ var SPECWF_DIRS = [
131
+ "specs",
132
+ "conventions",
133
+ "research",
134
+ "milestones",
135
+ "changes",
136
+ "archive",
137
+ "workspace"
138
+ ];
139
+ function createSpecwfStructure(specwfDir) {
140
+ mkdirSync(specwfDir, { recursive: true });
141
+ for (const dir of SPECWF_DIRS) {
142
+ mkdirSync(join2(specwfDir, dir), { recursive: true });
143
+ }
144
+ }
145
+ function isInitialized(specwfDir) {
146
+ return existsSync2(join2(specwfDir, "project.yml")) && existsSync2(join2(specwfDir, "state.md"));
147
+ }
148
+ function createAdhocChangeDir(specwfDir, changeName) {
149
+ const dir = join2(specwfDir, "changes", changeName);
150
+ mkdirSync(dir, { recursive: true });
151
+ mkdirSync(join2(dir, "specs"), { recursive: true });
152
+ return dir;
153
+ }
154
+ function archiveChangeDir(specwfDir, changeDir) {
155
+ const changeName = changeDir.split("/").pop() ?? "unknown";
156
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
157
+ const archiveRoot = join2(specwfDir, "archive", "changes");
158
+ mkdirSync(archiveRoot, { recursive: true });
159
+ const archiveDir = join2(archiveRoot, `${date}-${changeName}`);
160
+ if (existsSync2(changeDir)) {
161
+ renameSync(changeDir, archiveDir);
162
+ }
163
+ return archiveDir;
164
+ }
165
+ function archiveMilestoneDir(specwfDir, milestoneId) {
166
+ const sourceDir = join2(specwfDir, "milestones", milestoneId);
167
+ const archiveDir = join2(specwfDir, "archive", "milestones", milestoneId);
168
+ if (!existsSync2(sourceDir)) {
169
+ return archiveDir;
170
+ }
171
+ mkdirSync(join2(specwfDir, "archive", "milestones"), { recursive: true });
172
+ renameSync(sourceDir, archiveDir);
173
+ return archiveDir;
174
+ }
175
+ function listMilestones(specwfDir) {
176
+ const dir = join2(specwfDir, "milestones");
177
+ if (!existsSync2(dir)) return [];
178
+ return readdirSync(dir).filter((e) => {
179
+ const stat = statSync(join2(dir, e));
180
+ return stat.isDirectory();
181
+ });
182
+ }
183
+ function listPhases(specwfDir, milestoneId) {
184
+ const dir = join2(specwfDir, "milestones", milestoneId, "phases");
185
+ if (!existsSync2(dir)) return [];
186
+ return readdirSync(dir).filter((e) => {
187
+ const stat = statSync(join2(dir, e));
188
+ return stat.isDirectory();
189
+ });
190
+ }
191
+ function listChanges(specwfDir, milestoneId, phaseId) {
192
+ const dir = join2(specwfDir, "milestones", milestoneId, "phases", phaseId, "changes");
193
+ if (!existsSync2(dir)) return [];
194
+ return readdirSync(dir).filter((e) => {
195
+ const stat = statSync(join2(dir, e));
196
+ return stat.isDirectory();
197
+ });
198
+ }
199
+ function listAdhocChanges(specwfDir) {
200
+ const dir = join2(specwfDir, "changes");
201
+ if (!existsSync2(dir)) return [];
202
+ return readdirSync(dir).filter((e) => {
203
+ const stat = statSync(join2(dir, e));
204
+ return stat.isDirectory();
205
+ });
206
+ }
207
+ function listArchived(specwfDir) {
208
+ const dir = join2(specwfDir, "archive", "changes");
209
+ if (!existsSync2(dir)) return [];
210
+ return readdirSync(dir).filter((e) => {
211
+ const stat = statSync(join2(dir, e));
212
+ return stat.isDirectory();
213
+ });
214
+ }
215
+
216
+ // src/core/state-file.ts
217
+ import { join as join3 } from "path";
218
+ import { z as z2 } from "zod";
219
+
220
+ // src/parser/frontmatter.ts
221
+ import matter from "gray-matter";
222
+ import { readFileSync as readFileSync3 } from "fs";
223
+ function parseFrontmatter(content) {
224
+ const parsed = matter(content);
225
+ return {
226
+ data: parsed.data,
227
+ content: parsed.content
228
+ };
229
+ }
230
+ function stringifyFrontmatter(data, body) {
231
+ return matter.stringify(body, data);
232
+ }
233
+ function readFrontmatterFile(path) {
234
+ return parseFrontmatter(readFileSync3(path, "utf-8"));
235
+ }
236
+
237
+ // src/core/state-file.ts
238
+ import { writeFileSync as writeFileSync3, existsSync as existsSync3 } from "fs";
239
+ var STATE_FILE = "state.md";
240
+ var ChangeStateSchema = z2.object({
241
+ name: z2.string(),
242
+ status: z2.string(),
243
+ depends_on: z2.array(z2.string()).optional().default([])
244
+ });
245
+ var StateFileSchema = z2.object({
246
+ project: z2.object({
247
+ name: z2.string(),
248
+ status: z2.string(),
249
+ current_milestone: z2.string().nullable(),
250
+ current_phase: z2.string().nullable()
251
+ }),
252
+ active_context: z2.object({
253
+ type: z2.enum(["project", "milestone", "phase", "change", "adhoc"]),
254
+ ref: z2.string().nullable(),
255
+ step: z2.string()
256
+ }),
257
+ changes: z2.array(ChangeStateSchema).optional().default([]),
258
+ adhoc: z2.array(ChangeStateSchema).optional().default([])
259
+ });
260
+ function statePath(specwfDir) {
261
+ return join3(specwfDir, STATE_FILE);
262
+ }
263
+ function loadState(specwfDir) {
264
+ const result = readFrontmatterFile(statePath(specwfDir));
265
+ return StateFileSchema.parse(result.data);
266
+ }
267
+ function saveState(specwfDir, state) {
268
+ let body;
269
+ try {
270
+ const existing = readFrontmatterFile(statePath(specwfDir));
271
+ body = existing.content;
272
+ } catch {
273
+ body = generateStateBody(state);
274
+ }
275
+ const output = stringifyFrontmatter(state, body);
276
+ writeFileSync3(statePath(specwfDir), output, "utf-8");
277
+ }
278
+ function updateState(specwfDir, updater) {
279
+ const state = loadState(specwfDir);
280
+ updater(state);
281
+ saveState(specwfDir, state);
282
+ }
283
+ function generateStateBody(state) {
284
+ const ctx = state.active_context;
285
+ const lines = [
286
+ "# State",
287
+ "",
288
+ "## \u5F53\u524D\u4F4D\u7F6E",
289
+ "",
290
+ formatContext(state),
291
+ "",
292
+ "## \u72B6\u6001\u673A",
293
+ "",
294
+ "\u9879\u76EE\u5C42\u8DEF\u5F84: `initialized \u2192 requirements-defined \u2192 researched \u2192 roadmap-defined`",
295
+ ""
296
+ ];
297
+ if (state.project) {
298
+ lines.push("## \u5386\u53F2", "");
299
+ }
300
+ return lines.join("\n");
301
+ }
302
+ function formatContext(state) {
303
+ const { type, ref, step } = state.active_context;
304
+ switch (type) {
305
+ case "project":
306
+ return `\u9879\u76EE\u5C42 \u2014 ${step}\u3002`;
307
+ case "milestone":
308
+ return `Milestone ${state.project.current_milestone ?? "?"} \u2014 ${step}\u3002`;
309
+ case "phase":
310
+ return `Phase ${state.project.current_phase ?? "?"} \u2014 ${step}\u3002`;
311
+ case "change":
312
+ return `Change (${ref ?? "?"}) \u2014 ${step}\u3002`;
313
+ case "adhoc":
314
+ return `\u4E34\u65F6 Change (${ref ?? "?"}) \u2014 ${step}\u3002`;
315
+ default:
316
+ return step;
317
+ }
318
+ }
319
+
320
+ // src/prompts/init-wizard.ts
321
+ async function runInitWizard(defaults) {
322
+ if (defaults.yes) {
323
+ return { profile: defaults.profile, context: "", platform: ["omp"], brownfield: false };
324
+ }
325
+ try {
326
+ const clack = await import("@clack/prompts");
327
+ const val = await clack.select({
328
+ message: "\u9009\u62E9\u5DE5\u4F5C\u6D41\u4E25\u683C\u5EA6:",
329
+ options: [{ value: "lite", label: "Lite" }, { value: "standard", label: "Standard\uFF08\u63A8\u8350\uFF09" }, { value: "strict", label: "Strict" }],
330
+ initialValue: defaults.profile
331
+ });
332
+ const profile = typeof val === "string" ? val : defaults.profile;
333
+ const ctxVal = await clack.text({ message: "\u9879\u76EE\u4E0A\u4E0B\u6587\u63CF\u8FF0\uFF08\u53EF\u9009\uFF09:", placeholder: "\u6280\u672F\u6808: TypeScript, Node.js..." });
334
+ const context = typeof ctxVal === "string" ? ctxVal : "";
335
+ const pfVal = await clack.multiselect({ message: "\u9009\u62E9\u76EE\u6807\u5E73\u53F0:", options: [{ value: "omp", label: "Oh My Pi" }], initialValues: ["omp"] });
336
+ const platform = Array.isArray(pfVal) ? pfVal : ["omp"];
337
+ const bfVal = await clack.confirm({ message: "\u8FD9\u662F\u4E00\u4E2A\u5B58\u91CF\u9879\u76EE\u5417\uFF1F", initialValue: false });
338
+ const brownfield = typeof bfVal === "boolean" ? bfVal : false;
339
+ return { profile, context, platform, brownfield };
340
+ } catch {
341
+ console.log("(@clack/prompts \u672A\u5B89\u88C5\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E)");
342
+ return { profile: defaults.profile, context: "", platform: ["omp"], brownfield: false };
343
+ }
344
+ }
345
+
346
+ // src/core/brownfield.ts
347
+ import { readFileSync as readFileSync5, readdirSync as readdirSync2, existsSync as existsSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync2 } from "fs";
348
+ import { join as join4 } from "path";
349
+ function detectProjectInfo(rootDir) {
350
+ const info = {
351
+ type: "unknown",
352
+ language: "unknown",
353
+ framework: "unknown",
354
+ hasPackageJson: false,
355
+ hasTests: false,
356
+ srcDirs: [],
357
+ structFiles: []
358
+ };
359
+ if (existsSync4(join4(rootDir, "package.json"))) {
360
+ info.hasPackageJson = true;
361
+ info.type = "node";
362
+ info.language = "typescript";
363
+ try {
364
+ const pkg2 = JSON.parse(readFileSync5(join4(rootDir, "package.json"), "utf-8"));
365
+ if (pkg2.dependencies?.next) info.framework = "next.js";
366
+ else if (pkg2.dependencies?.react) info.framework = "react";
367
+ else if (pkg2.dependencies?.vue) info.framework = "vue";
368
+ else if (pkg2.dependencies?.express) info.framework = "express";
369
+ else if (pkg2.dependencies?.fastify) info.framework = "fastify";
370
+ } catch {
371
+ }
372
+ }
373
+ if (existsSync4(join4(rootDir, "Cargo.toml"))) {
374
+ info.type = "rust";
375
+ info.language = "rust";
376
+ }
377
+ if (existsSync4(join4(rootDir, "go.mod"))) {
378
+ info.type = "go";
379
+ info.language = "go";
380
+ }
381
+ for (const dir of ["src", "app", "lib", "pkg", "cmd"]) {
382
+ if (existsSync4(join4(rootDir, dir)) && readdirSync2(join4(rootDir, dir), { withFileTypes: true }).some((e) => e.isDirectory())) {
383
+ info.srcDirs.push(dir);
384
+ }
385
+ }
386
+ if (existsSync4(join4(rootDir, "__tests__")) || existsSync4(join4(rootDir, "tests"))) {
387
+ info.hasTests = true;
388
+ }
389
+ if (existsSync4(join4(rootDir, "vitest.config.ts")) || existsSync4(join4(rootDir, "jest.config.ts"))) {
390
+ info.hasTests = true;
391
+ }
392
+ return info;
393
+ }
394
+ function generateCodebaseReport(rootDir, info) {
395
+ const stack = buildStackSection(info);
396
+ const structure = buildStructureSection(rootDir);
397
+ const conventions = detectConventions(rootDir);
398
+ return { stack, structure, conventions };
399
+ }
400
+ function buildStackSection(info) {
401
+ return `# \u6280\u672F\u6808
402
+
403
+ - \u9879\u76EE\u7C7B\u578B: ${info.type}
404
+ - \u8BED\u8A00: ${info.language}
405
+ - \u6846\u67B6: ${info.framework}
406
+ - src \u76EE\u5F55: ${info.srcDirs.join(", ")}
407
+ - \u6D4B\u8BD5: ${info.hasTests ? "\u6709" : "\u65E0"}
408
+ `;
409
+ }
410
+ function buildStructureSection(rootDir) {
411
+ const lines = ["# \u9879\u76EE\u7ED3\u6784", ""];
412
+ try {
413
+ const entries = readdirSync2(rootDir, { withFileTypes: true }).filter((e) => !e.name.startsWith(".") && !e.name.startsWith("node_modules") && e.name !== "dist").map((e) => `${e.isDirectory() ? " [dir] " : " [file] "}${e.name}`).slice(0, 30);
414
+ lines.push(...entries);
415
+ } catch {
416
+ }
417
+ return lines.join("\n");
418
+ }
419
+ function detectConventions(rootDir) {
420
+ const configs = [];
421
+ if (existsSync4(join4(rootDir, "tsconfig.json"))) configs.push("TypeScript");
422
+ if (existsSync4(join4(rootDir, ".eslintrc.js")) || existsSync4(join4(rootDir, "eslint.config.js"))) configs.push("ESLint");
423
+ if (existsSync4(join4(rootDir, ".prettierrc"))) configs.push("Prettier");
424
+ return `# \u9879\u76EE\u7EA6\u5B9A
425
+
426
+ \u68C0\u6D4B\u5230: ${configs.length > 0 ? configs.join(", ") : "\u65E0"}`;
427
+ }
428
+ function bootstrapSpecs(rootDir, specwfDir) {
429
+ const specs = [];
430
+ if (existsSync4(join4(rootDir, "src"))) {
431
+ try {
432
+ for (const entry of readdirSync2(join4(rootDir, "src"), { withFileTypes: true })) {
433
+ if (entry.isDirectory() && !entry.name.startsWith("_")) {
434
+ const domainDir = join4(specwfDir, "specs", entry.name);
435
+ mkdirSync2(domainDir, { recursive: true });
436
+ writeFileSync4(
437
+ join4(domainDir, "spec.md"),
438
+ `# ${entry.name} Specification
439
+
440
+ ## Purpose
441
+
442
+ [\u4ECE\u4E0A\u4F4D\u4EE3\u7801\u81EA\u52A8\u63D0\u53D6\u7684\u521D\u59CB spec \u2014 \u5F85\u4EBA\u5DE5\u5BA1\u6838]
443
+
444
+ ## Requirements
445
+
446
+ `,
447
+ "utf-8"
448
+ );
449
+ specs.push(entry.name);
450
+ }
451
+ }
452
+ } catch {
453
+ }
454
+ }
455
+ if (specs.length === 0) {
456
+ const domainDir = join4(specwfDir, "specs", "general");
457
+ mkdirSync2(domainDir, { recursive: true });
458
+ writeFileSync4(
459
+ join4(domainDir, "spec.md"),
460
+ `# General Specification
461
+
462
+ ## Purpose
463
+
464
+ [\u4ECE\u4E0A\u4F4D\u4EE3\u7801\u81EA\u52A8\u63D0\u53D6\u7684\u521D\u59CB spec \u2014 \u5F85\u4EBA\u5DE5\u5BA1\u6838]
465
+
466
+ ## Requirements
467
+
468
+ `,
469
+ "utf-8"
470
+ );
471
+ specs.push("general");
472
+ }
473
+ return specs;
474
+ }
475
+ async function runBrownfieldInit(rootDir, specwfDir, info) {
476
+ const report = generateCodebaseReport(rootDir, info);
477
+ const researchDir = join4(specwfDir, "research", "codebase");
478
+ mkdirSync2(researchDir, { recursive: true });
479
+ writeFileSync4(join4(researchDir, "stack.md"), report.stack, "utf-8");
480
+ writeFileSync4(join4(researchDir, "structure.md"), report.structure, "utf-8");
481
+ writeFileSync4(join4(researchDir, "conventions.md"), report.conventions, "utf-8");
482
+ const domains = bootstrapSpecs(rootDir, specwfDir);
483
+ return domains;
484
+ }
485
+
486
+ // src/templates/workflows/init.ts
487
+ var instructions = `## Input
488
+ - No prior state required (this is the project entry point)
489
+ - Node.js 20+ must be installed
490
+
491
+ ## Steps
492
+
493
+ ### Step 1: Check state and get context
494
+ Run \`specwf context init\` \u2014 outputs JSON with state and file manifest. Read all listed files before proceeding.
495
+
496
+ ### Step 1: Execute initialization
497
+ Run \`specwf init --yes\` to create the project skeleton:
498
+
499
+ - \`specwf/\` directory structure
500
+ - \`specwf/project.yml\` \u2014 project workflow configuration
501
+ - \`specwf/state.md\` \u2014 state machine file
502
+ - \`specwf/requirements.md\` \u2014 requirements document (template)
503
+ - \`specwf/conventions/\` \u2014 coding conventions directory
504
+ - \`.omp/commands/specwf-*.md\` \u2014 16 slash commands
505
+ - \`.omp/agents/specwf-*.md\` \u2014 8 agent definitions
506
+ - \`.omp/skills/specwf-*/SKILL.md\` \u2014 16 skill guides
507
+
508
+ ### Step 2: Brownfield mode (existing projects)
509
+ For projects with existing code, use \`specwf init --yes --brownfield\`:
510
+
511
+ Dispatches two sub-agents in parallel:
512
+
513
+ **Agent 1: specwf-codebase-mapper** \u2014 analyzes existing codebase for tech stack, architecture, conventions, and pitfalls.
514
+
515
+ **Agent 2: specwf-spec-bootstrapper** \u2014 extracts behavioral contracts from existing code signatures, comments, and tests.
516
+
517
+ ### Step 3: Advance
518
+ Run \`specwf continue\` to proceed to the requirements exploration phase (grill).
519
+
520
+ ## Output
521
+
522
+ | File | Description |
523
+ |------|-------------|
524
+ | \`specwf/\` directory | Project skeleton |
525
+ | \`specwf/project.yml\` | Workflow configuration |
526
+ | \`specwf/state.md\` | State machine |
527
+ | \`specwf/requirements.md\` | Requirements template |
528
+ | \`.omp/commands/*.md\` | Generated slash commands |
529
+ | \`.omp/agents/*.md\` | Generated agent definitions |
530
+ | \`.omp/skills/*/SKILL.md\` | Generated skill guides |
531
+
532
+ Brownfield extras: \`codebase/stack.md\`, \`codebase/architecture.md\`, \`codebase/pitfalls.md\`, \`conventions/codebase-conventions.md\`, \`specs/<domain>/spec.md\`.
533
+
534
+ ## Guardrails
535
+
536
+ - Run \`specwf init\` only once per project \u2014 re-running overwrites generated files
537
+ - Use \`--yes\` to skip interactive prompts in CI/non-interactive environments
538
+ - Brownfield mode is read-only analysis \u2014 it never modifies source code
539
+ - After initialization, fill in \`requirements.md\` before advancing`;
540
+ function getInitSkillTemplate() {
541
+ return {
542
+ name: "specwf-init",
543
+ description: "Initialize specwf project structure and generate platform files",
544
+ instructions
545
+ };
546
+ }
547
+ function getInitCommandTemplate() {
548
+ return {
549
+ name: "SpecWF: Init",
550
+ description: "Initialize specwf project structure and generate platform files",
551
+ category: "Setup",
552
+ tags: ["specwf", "init", "setup"],
553
+ content: instructions
554
+ };
555
+ }
556
+
557
+ // src/templates/workflows/grill.ts
558
+ var instructions2 = `## Input
559
+
560
+ ### Parameters
561
+ - **No parameters**: operate on the current project from \`specwf state\`
562
+
563
+ ### Prerequisites
564
+ - \`specwf/requirements.md\` must exist (created by init)
565
+ - \`specwf/project.yml\` for project context
566
+
567
+ ## Steps
568
+
569
+ ### Step 1: Get context
570
+ Run \`specwf context grill\` \u2014 outputs JSON with state and requirements.md path. Read requirements.md.
571
+
572
+ ### Step 2: Explore requirements \u2014 one question at a time
573
+ Interview the user relentlessly until shared understanding is reached. Follow these rules:
574
+
575
+ 1. **One question at a time.** Never batch multiple questions.
576
+ 2. **Provide your recommended answer** for each question, then ask if they agree.
577
+ 3. **Explore the codebase first** if the question can be answered by reading existing code or docs.
578
+ 4. **Walk each branch of the decision tree** \u2014 resolve dependencies between decisions one by one.
579
+
580
+ Use the 5W1H framework:
581
+ - **What** \u2014 core goals and value proposition
582
+ - **Who** \u2014 target users, their roles and workflows
583
+ - **Where** \u2014 deployment environment, platform constraints
584
+ - **When** \u2014 timeline, phases, priorities
585
+ - **Why** \u2014 business motivation, success metrics
586
+ - **How** \u2014 technical preferences, non-functional requirements
587
+
588
+ ### Step 3: Record decisions incrementally
589
+ After each consensus point, write it to \`specwf/requirements.md\` immediately \u2014 do not batch at the end.
590
+
591
+ ### Step 4: Confirm consensus
592
+ Review requirements.md with the user. Ensure no ambiguity remains. Mark unresolved items as \`[TODO: decide]\`.
593
+
594
+ ### Step 5: Advance
595
+ Run \`specwf continue\` to proceed to the research phase.
596
+
597
+ ## Output
598
+ - \`specwf/requirements.md\` \u2014 populated with agreed requirements
599
+
600
+ ## Guardrails
601
+ - No code is written during grill \u2014 this is pure exploration
602
+ - Do not skip questions that seem obvious \u2014 hidden assumptions cause rework
603
+ - Record decisions as they are made, not at the end
604
+ - If the user cannot answer, mark \`[TODO: decide]\` and move on`;
605
+ function getGrillSkillTemplate() {
606
+ return {
607
+ name: "specwf-grill",
608
+ description: "Requirements exploration \u2014 one question at a time, provide recommendations",
609
+ instructions: instructions2
610
+ };
611
+ }
612
+ function getGrillCommandTemplate() {
613
+ return {
614
+ name: "SpecWF: Grill",
615
+ description: "Requirements exploration \u2014 one question at a time, provide recommendations",
616
+ category: "Discovery",
617
+ tags: ["specwf", "grill", "requirements", "discovery"],
618
+ content: instructions2
619
+ };
620
+ }
621
+
622
+ // src/templates/workflows/research.ts
623
+ var instructions3 = `## Input
624
+ - \`specwf/requirements.md\` must be complete (grill phase done)
625
+ - \`specwf/project.yml\` for technical constraints
626
+
627
+ ## Steps
628
+
629
+ ### Step 1: Check state and get context
630
+ Run \`specwf context research\` \u2014 outputs JSON with state and file manifest. Read all listed files before proceeding.
631
+
632
+ ### Step 1: Dispatch research sub-agents
633
+ **You are the orchestrator \u2014 dispatch, do not research yourself.** Spawn \`specwf-researcher\` sub-agents in parallel, one per technical direction (stack, architecture, pitfalls).
634
+
635
+ Construct each sub-agent prompt:
636
+
637
+ \`\`\`text
638
+ Sub-agent: specwf-researcher
639
+ Task: Research <direction> for project <project-name>
640
+
641
+ [Context]
642
+ - Read requirements.md from specwf/requirements.md
643
+ - Extract constraints from specwf/project.yml
644
+ - Research scope: <stack | architecture | pitfalls>
645
+
646
+ [Responsibilities]
647
+ - Compare at least 2 candidate solutions
648
+ - Assess feasibility, risk, and trade-offs
649
+ - Produce a recommended approach with rationale
650
+ - Output to specwf/research/<stack.md | architecture.md | pitfalls.md>
651
+
652
+ [Constraints]
653
+ - Read-only \u2014 do not modify code or config
654
+ - Mark speculative findings with confidence levels
655
+ \`\`\`
656
+
657
+ ### Step 2: Verify sub-agent output
658
+ After all sub-agents complete, verify:
659
+ - \`research/stack.md\` exists with tech stack comparison and recommendation
660
+ - \`research/architecture.md\` exists with architecture evaluation
661
+ - \`research/pitfalls.md\` exists with risk assessment
662
+ - Write \`research/summary.md\` synthesizing all findings into one recommendation
663
+
664
+ ### Step 3: Advance
665
+ Run \`specwf continue\` to proceed to roadmap definition.
666
+
667
+ ## Output
668
+ - \`research/stack.md\` \u2014 recommended tech stack with alternatives compared
669
+ - \`research/architecture.md\` \u2014 recommended architecture with rationale
670
+ - \`research/pitfalls.md\` \u2014 known risks and mitigation strategies
671
+ - \`research/summary.md\` \u2014 consolidated research conclusion
672
+
673
+ ## Guardrails
674
+ - **You are the orchestrator** \u2014 dispatch sub-agents, do not research yourself
675
+ - Each sub-agent must compare at least 2 alternatives \u2014 never recommend the first option found
676
+ - Sub-agents are independent and run in parallel
677
+ - Mark speculative findings with confidence levels`;
678
+ function getResearchSkillTemplate() {
679
+ return {
680
+ name: "specwf-research",
681
+ description: "Project-level technical research \u2014 dispatch researcher sub-agents in parallel",
682
+ instructions: instructions3
683
+ };
684
+ }
685
+ function getResearchCommandTemplate() {
686
+ return {
687
+ name: "SpecWF: Research",
688
+ description: "Project-level technical research \u2014 dispatch researcher sub-agents in parallel",
689
+ category: "Discovery",
690
+ tags: ["specwf", "research", "architecture", "tech-stack", "sub-agent"],
691
+ content: instructions3
692
+ };
693
+ }
694
+
695
+ // src/templates/workflows/roadmap.ts
696
+ var instructions4 = `## Input
697
+
698
+ ### Parameters
699
+ - **No parameters**: operate on the current project
700
+
701
+ ### Prerequisites
702
+ - \`specwf/requirements.md\` \u2014 complete requirements
703
+ - \`specwf/research/\` \u2014 technical research results (if available)
704
+
705
+ ## Steps
706
+
707
+ ### Step 1: Get context
708
+ Run \`specwf context roadmap\` \u2014 outputs JSON with state, requirements.md, and research/ paths. Read all listed files.
709
+
710
+ ### Step 2: Define Milestones
711
+ Define milestones by functional area or release cadence. Write to \`specwf/roadmap.md\`:
712
+
713
+ \`\`\`markdown
714
+ # Roadmap
715
+
716
+ ## Milestones
717
+
718
+ | ID | Goal | Phases | Duration |
719
+ |----|------|--------|----------|
720
+ | M1-<name> | <one-sentence goal> | <count> | <duration> |
721
+
722
+ ## M1-<name>: <goal>
723
+
724
+ ### Success Criteria
725
+ - <measurable condition>
726
+
727
+ ### Phases
728
+
729
+ | ID | Goal | Depends On | Changes |
730
+ |----|------|-----------|---------|
731
+ | ph.1-<name> | <goal> | - | <count> |
732
+ | ph.2-<name> | <goal> | ph.1 | <count> |
733
+
734
+ ### ph.1-<name>
735
+ - **Goal**: <what this phase delivers>
736
+ - **Inputs**: <specs, conventions, docs>
737
+ - **Outputs**: <code, specs, docs>
738
+ \`\`\`
739
+
740
+ ### Step 3: Create milestone directories
741
+ For each milestone, create: \`specwf/milestones/<id>/\`
742
+
743
+ ### Step 4: Validate coverage
744
+ - All functional scope covered by milestones and phases
745
+ - Phase dependencies form a DAG (no cycles)
746
+ - Each phase has verifiable success criteria
747
+ - Total phase count: 3-15
748
+ - First phase is the minimum viable path
749
+
750
+ ### Step 5: Advance
751
+ Run \`specwf state set-milestone <id>\` to activate the first milestone, then \`specwf continue\`.
752
+
753
+ ## Output
754
+ - \`specwf/roadmap.md\` \u2014 structured roadmap with milestone and phase tables
755
+ - \`specwf/milestones/<id>/\` \u2014 per-milestone directories
756
+
757
+ ## Guardrails
758
+ - Start with the smallest viable milestone first
759
+ - Phase count per milestone: 3-8
760
+ - Each phase must produce a demonstrable increment
761
+ - Use the table format above \u2014 it's structured and parseable`;
762
+ function getRoadmapSkillTemplate() {
763
+ return {
764
+ name: "specwf-roadmap",
765
+ description: "Roadmap definition \u2014 split project into Milestones x Phases with structured format",
766
+ instructions: instructions4
767
+ };
768
+ }
769
+ function getRoadmapCommandTemplate() {
770
+ return {
771
+ name: "SpecWF: Roadmap",
772
+ description: "Roadmap definition \u2014 split project into Milestones x Phases with structured format",
773
+ category: "Planning",
774
+ tags: ["specwf", "roadmap", "planning", "milestones"],
775
+ content: instructions4
776
+ };
777
+ }
778
+
779
+ // src/templates/workflows/milestone.ts
780
+ var instructions5 = `## Input
781
+
782
+ ### Parameters
783
+ - **No parameters**: operate on the currently active milestone from \`specwf state\`
784
+ - **\`<milestone-id>\`**: switch to a specific milestone
785
+
786
+ ### Prerequisites
787
+ - \`specwf/roadmap.md\` must exist with defined milestones
788
+
789
+ ## Steps
790
+
791
+ ### Step 1: Get context
792
+ Run \`specwf context milestone\` \u2014 outputs JSON with state and roadmap file paths. Read all listed files.
793
+
794
+ ### Step 2: Select milestone
795
+ If a milestone ID was provided: run \`specwf state set-milestone <id>\`.
796
+ If no ID: run \`specwf state\` to see the current milestone. To switch, use \`specwf state set-milestone <id>\`.
797
+
798
+ ### Step 3: Select phase (optional)
799
+ If you need to jump to a specific phase within the milestone: run \`specwf state set-phase <id>\`.
800
+
801
+ ### Step 4: Advance
802
+ Run \`specwf continue\` to proceed to the discuss phase for the active milestone/phase.
803
+
804
+ ## When to use milestone vs continue
805
+
806
+ | Situation | Command |
807
+ |-----------|---------|
808
+ | Just finished roadmap, want to start first milestone | \`specwf state set-milestone <id>\` then \`specwf continue\` |
809
+ | Currently in a milestone, want to advance | \`specwf continue\` |
810
+ | Want to switch to a different milestone | \`specwf state set-milestone <id>\` |
811
+ | Want to jump to a specific phase | \`specwf state set-phase <id>\` |
812
+
813
+ ## Output
814
+ - Updated state.md with new active milestone/phase
815
+
816
+ ## Guardrails
817
+ - Switching milestones archives the current one if not yet shipped
818
+ - Phase transitions within a milestone do not trigger archival
819
+ - After switching, always run \`specwf continue\` to get the next step's instructions`;
820
+ function getMilestoneSkillTemplate() {
821
+ return {
822
+ name: "specwf-milestone",
823
+ description: "Milestone management \u2014 switch/create milestones, set current phase",
824
+ instructions: instructions5
825
+ };
826
+ }
827
+ function getMilestoneCommandTemplate() {
828
+ return {
829
+ name: "SpecWF: Milestone",
830
+ description: "Milestone management \u2014 switch/create milestones, set current phase",
831
+ category: "Planning",
832
+ tags: ["specwf", "milestone", "planning"],
833
+ content: instructions5
834
+ };
835
+ }
836
+
837
+ // src/templates/workflows/discuss.ts
838
+ var instructions6 = `## Input
839
+
840
+ ### Parameters
841
+ - **No parameters**: operate on the currently active phase from \`specwf state\`
842
+
843
+ ### Prerequisites
844
+ - Active milestone and phase must be set
845
+ - \`specwf/roadmap.md\` \u2014 current phase description and boundaries
846
+
847
+ ## Steps
848
+
849
+ ### Step 1: Get context
850
+ Run \`specwf context discuss\` \u2014 outputs JSON with state and roadmap path. Read roadmap.md.
851
+
852
+ ### Step 2: Discuss and record decisions
853
+ Walk through each topic with the user. Record every decision using this format in context.md:
854
+
855
+ \`\`\`markdown
856
+ ## D1: <decision title>
857
+ - **Decision**: <what we chose>
858
+ - **Rationale**: <why we chose it>
859
+ - **Alternatives considered**: <what else we evaluated and why rejected>
860
+ - **Impact**: <what this affects \u2014 files, interfaces, constraints>
861
+
862
+ ## D2: <decision title>
863
+ ...
864
+ \`\`\`
865
+
866
+ Topics to cover:
867
+ - **Phase goals** \u2014 what this phase delivers
868
+ - **Interface contracts** \u2014 key APIs and data models
869
+ - **Implementation constraints** \u2014 technical limits
870
+ - **Change split plan** \u2014 preliminary breakdown
871
+ - **Non-goals** \u2014 explicitly excluded
872
+
873
+ ### Step 3: Mark gray areas
874
+ Unresolved items: mark as \`[TODO: discuss]\` with a brief note on what's blocking.
875
+
876
+ ### Step 4: Advance
877
+ Run \`specwf continue\` to proceed to research-phase.
878
+
879
+ ## Output
880
+ - \`context.md\` \u2014 phase-level implementation decisions (D1, D2, ... format)
881
+
882
+ ## Guardrails
883
+ - Every architecture decision gets a unique D-number for traceability
884
+ - Do not skip gray areas \u2014 mark them and revisit
885
+ - context.md is the single source of truth for phase implementation`;
886
+ function getDiscussSkillTemplate() {
887
+ return {
888
+ name: "specwf-discuss",
889
+ description: "Phase discussion \u2014 capture implementation decisions with D1/D2 format",
890
+ instructions: instructions6
891
+ };
892
+ }
893
+ function getDiscussCommandTemplate() {
894
+ return {
895
+ name: "SpecWF: Discuss",
896
+ description: "Phase discussion \u2014 capture implementation decisions with D1/D2 format",
897
+ category: "Planning",
898
+ tags: ["specwf", "discuss", "context", "decisions"],
899
+ content: instructions6
900
+ };
901
+ }
902
+
903
+ // src/templates/workflows/research-phase.ts
904
+ var instructions7 = `## Input
905
+ - \`context.md\` must exist (discuss phase done)
906
+ - Related specs, conventions, and external dependencies
907
+
908
+ ## Steps
909
+
910
+ ### Step 1: Check state and get context
911
+ Run \`specwf context research-phase\` \u2014 outputs JSON with state and file manifest. Read all listed files before proceeding.
912
+
913
+ ### Step 1: Dispatch phase researcher
914
+ **You are the orchestrator \u2014 dispatch, do not research yourself.** Spawn \`specwf-phase-researcher\` sub-agent.
915
+
916
+ Construct the sub-agent prompt:
917
+
918
+ \`\`\`text
919
+ Sub-agent: specwf-phase-researcher
920
+ Task: Research implementation paths for phase <phase-id>
921
+
922
+ [Context]
923
+ - Read context.md for locked decisions and discretion areas
924
+ - Read related specs/ for existing behavioral contracts
925
+ - Read conventions/ for coding standards
926
+
927
+ [Responsibilities]
928
+ - Investigate concrete implementation approaches
929
+ - Identify reusable patterns from existing codebase
930
+ - Flag known pitfalls and edge cases
931
+ - Produce research.md with recommended paths and TDD implications
932
+ - Output to milestones/<ms>/phases/<ph>/research.md
933
+
934
+ [Constraints]
935
+ - Respect context.md locked decisions \u2014 they are non-negotiable
936
+ - Surface trade-offs explicitly \u2014 do not present one option as the only path
937
+ - Note confidence levels for speculative findings
938
+ \`\`\`
939
+
940
+ ### Step 2: Verify output
941
+ Confirm \`research.md\` was written by the sub-agent with:
942
+ - Recommended implementation paths with rationale
943
+ - Known pitfalls and edge cases
944
+ - TDD implications annotated
945
+
946
+ ### Step 3: Advance
947
+ Run \`specwf continue\` to proceed to the split phase.
948
+
949
+ ## Output
950
+ - \`research.md\` \u2014 phase-level implementation research with recommended paths and known pitfalls
951
+
952
+ ## Guardrails
953
+ - **You are the orchestrator** \u2014 dispatch the sub-agent, do not research yourself
954
+ - Research must respect context.md locked decisions
955
+ - Surface trade-offs explicitly`;
956
+ function getResearchPhaseSkillTemplate() {
957
+ return {
958
+ name: "specwf-research-phase",
959
+ description: "Phase research \u2014 dispatch phase-researcher sub-agent",
960
+ instructions: instructions7
961
+ };
962
+ }
963
+ function getResearchPhaseCommandTemplate() {
964
+ return {
965
+ name: "SpecWF: Research Phase",
966
+ description: "Phase research \u2014 dispatch phase-researcher sub-agent",
967
+ category: "Discovery",
968
+ tags: ["specwf", "research-phase", "implementation", "sub-agent"],
969
+ content: instructions7
970
+ };
971
+ }
972
+
973
+ // src/templates/workflows/split.ts
974
+ var instructions8 = `## Input
975
+
976
+ ### Parameters
977
+ - **No parameters**: operate on the current phase from \`specwf state\`
978
+
979
+ ### Prerequisites
980
+ - \`context.md\` \u2014 phase decisions and constraints
981
+ - \`research.md\` \u2014 implementation research
982
+
983
+ ## Steps
984
+
985
+ ### Step 1: Get context
986
+ Run \`specwf context split\` \u2014 outputs JSON with state, context.md, and research.md paths. Read all listed files.
987
+
988
+ ### Step 2: Split into changes
989
+ Decompose the phase scope into independently implementable Change units:
990
+ - Each change is a vertical slice (not layer-by-layer)
991
+ - Identify dependencies \u2192 dependency graph (must be a DAG)
992
+ - Each change gets a descriptive kebab-case name
993
+
994
+ Create each change:
995
+ \`\`\`bash
996
+ specwf change new <name> --phase <phase-id>
997
+ \`\`\`
998
+
999
+ ### Step 3: Document dependency graph
1000
+ Record in state.md:
1001
+ \`\`\`yaml
1002
+ changes:
1003
+ - name: <change-1>
1004
+ status: planned
1005
+ depends_on: []
1006
+ - name: <change-2>
1007
+ status: planned
1008
+ depends_on: [<change-1>]
1009
+ \`\`\`
1010
+
1011
+ ### Step 4: Validation checklist
1012
+ Before advancing, verify:
1013
+ - [ ] Each change is a vertical slice (delivers end-to-end value)
1014
+ - [ ] Dependency graph is a DAG (no cycles)
1015
+ - [ ] Change count: 3-8
1016
+ - [ ] No purely sequential changes (merge them into one)
1017
+ - [ ] Every change has a directory with proposal.md template
1018
+
1019
+ ### Step 5: Advance
1020
+ Run \`specwf continue\` to proceed to change planning.
1021
+
1022
+ ## Output
1023
+ - \`specwf/changes/<name>/\` directories \u2014 one per change
1024
+ - Updated \`state.md\` with change dependency graph
1025
+
1026
+ ## Guardrails
1027
+ - Changes must be vertical slices \u2014 each delivers end-to-end value
1028
+ - Dependency graph must be a DAG \u2014 no cycles
1029
+ - 3-8 changes per phase is the sweet spot
1030
+ - Merge purely sequential changes into one`;
1031
+ function getSplitSkillTemplate() {
1032
+ return {
1033
+ name: "specwf-split",
1034
+ description: "Change splitting \u2014 dependency graph + N changes with validation",
1035
+ instructions: instructions8
1036
+ };
1037
+ }
1038
+ function getSplitCommandTemplate() {
1039
+ return {
1040
+ name: "SpecWF: Split",
1041
+ description: "Change splitting \u2014 dependency graph + N changes with validation",
1042
+ category: "Planning",
1043
+ tags: ["specwf", "split", "changes", "dependency-graph"],
1044
+ content: instructions8
1045
+ };
1046
+ }
1047
+
1048
+ // src/templates/workflows/adhoc.ts
1049
+ var instructions9 = `## Input
1050
+
1051
+ ### Parameters
1052
+ - **\`<change-name>\`** (required) \u2014 kebab-case name for the new adhoc change.
1053
+ - If no name is provided, ask the user: "What should we call this change? (kebab-case, e.g. fix-login-timeout)"
1054
+
1055
+ ### Prerequisites
1056
+ - specwf project must be initialized
1057
+
1058
+ ## Steps
1059
+
1060
+ ### Step 1: Resolve change name
1061
+ If a name was provided: use it directly.
1062
+ If no name: ask the user for a descriptive kebab-case name.
1063
+
1064
+ ### Step 2: Create the adhoc change
1065
+ Run \`specwf change new <name>\` to create:
1066
+ - \`specwf/changes/<name>/proposal.md\` \u2014 proposal template
1067
+ - \`specwf/changes/<name>/design.md\` \u2014 design template
1068
+ - \`specwf/changes/<name>/tasks.md\` \u2014 tasks template
1069
+ - \`specwf/changes/<name>/specs/\` \u2014 delta-specs directory
1070
+
1071
+ The change is registered in \`state.md\` under the adhoc list with status \`proposal\`.
1072
+
1073
+ ### Step 1: Fill the proposal
1074
+ Edit \`proposal.md\` \u2014 describe the intent, scope, approach, and must-haves.
1075
+
1076
+ ### Step 2: Advance
1077
+ Run \`specwf continue change <name>\` to proceed through the standard change cycle.
1078
+
1079
+ ## Output
1080
+ - \`specwf/changes/<name>/\` \u2014 change directory with template files
1081
+ - Updated \`state.md\` with new adhoc entry
1082
+
1083
+ ## Guardrails
1084
+ - Adhoc changes do NOT go through milestone/phase discuss/research-phase/split flow
1085
+ - To associate with a phase, use \`specwf change new --phase <id>\`
1086
+ - Archived adhoc changes are stored under \`specwf/archive/\`
1087
+ - Adhoc changes follow the same plan->apply->review->verify->archive cycle as phase changes`;
1088
+ function getAdhocSkillTemplate() {
1089
+ return {
1090
+ name: "specwf-adhoc",
1091
+ description: "Create adhoc change \u2014 independent change unrelated to milestone/phase",
1092
+ instructions: instructions9
1093
+ };
1094
+ }
1095
+ function getAdhocCommandTemplate() {
1096
+ return {
1097
+ name: "SpecWF: Adhoc",
1098
+ description: "Create adhoc change \u2014 independent change unrelated to milestone/phase",
1099
+ category: "Workflow",
1100
+ tags: ["specwf", "adhoc", "change"],
1101
+ content: instructions9
1102
+ };
1103
+ }
1104
+
1105
+ // src/templates/workflows/plan.ts
1106
+ var instructions10 = `## Input
1107
+
1108
+ ### Parameters
1109
+ - **\`<change-name>\`** (required) \u2014 the change to plan. Provided by \`specwf continue\` output or user.
1110
+ - If no change name is available, check the \`pending\` array from \`specwf context <step>\` JSON output, then ask the user which to work on.
1111
+
1112
+ ### Prerequisites
1113
+ - Change \`proposal.md\` must be confirmed (not template)
1114
+ - \`context.md\` \u2014 phase-level implementation decisions (for phase changes)
1115
+ - Related \`specs/\` and \`conventions/\` files
1116
+
1117
+ ## Steps
1118
+
1119
+ ### Step 1: Resolve change name and get context
1120
+ If a change name was provided: use it directly. If not: run \`specwf state\`, list pending changes with status \`planned\` or \`proposal\`, ask the user to pick. Then run \`specwf context plan\` to get the file manifest. Read all listed files.
1121
+
1122
+ ### Step 2: Dispatch planner sub-agent
1123
+ **You are the orchestrator \u2014 dispatch, do not design yourself.** Spawn \`specwf-planner\` sub-agent with:
1124
+
1125
+ \`\`\`text
1126
+ Sub-agent: specwf-planner
1127
+ Change: <change-name> (from specwf/changes/<change-name>/)
1128
+
1129
+ Task: Produce design.md, tasks.md, and delta-specs for this change.
1130
+ Read proposal.md, context.md, related specs/, and conventions/ first.
1131
+ Use templates: specwf template design, specwf template tasks.
1132
+ Write all output to specwf/changes/<change-name>/.
1133
+
1134
+ Completion: write completion.md to specwf/changes/<change-name>/ listing files created and decisions made.
1135
+ \`\`\`
1136
+
1137
+ ### Step 3: Verify output and completion report
1138
+ After the planner finishes, check that \`completion.md\` exists and confirm:
1139
+ - \`design.md\` \u2014 architecture, data flow, alternatives
1140
+ - \`tasks.md\` \u2014 type annotations, TDD triples, wave grouping
1141
+ - \`specs/<domain>/spec.md\` \u2014 SHALL/MUST with scenarios
1142
+ - All must_haves from proposal.md covered
1143
+ - No contradictions with context.md
1144
+
1145
+ ### Step 4: Advance
1146
+ Run \`specwf continue\` to proceed to the apply phase.
1147
+
1148
+ ## Output
1149
+ - \`design.md\` \u2014 technical design document
1150
+ - \`tasks.md\` \u2014 implementation task checklist
1151
+ - \`specs/<domain>/spec.md\` \u2014 delta-specs
1152
+ - \`completion.md\` \u2014 sub-agent completion report
1153
+
1154
+ ## Guardrails
1155
+ - **You are the orchestrator** \u2014 dispatch specwf-planner, do not design yourself
1156
+ - type:behavior tasks MUST have RED->GREEN->REFACTOR triples
1157
+ - Delta-specs describe behavior, not implementation details
1158
+ - If a change is too large to split into clear tasks, return to split phase`;
1159
+ function getPlanSkillTemplate() {
1160
+ return {
1161
+ name: "specwf-plan",
1162
+ description: "Change design \u2014 dispatch planner sub-agent for design + tasks + delta-specs",
1163
+ instructions: instructions10
1164
+ };
1165
+ }
1166
+ function getPlanCommandTemplate() {
1167
+ return {
1168
+ name: "SpecWF: Plan",
1169
+ description: "Change design \u2014 dispatch planner sub-agent for design + tasks + delta-specs",
1170
+ category: "Workflow",
1171
+ tags: ["specwf", "plan", "design", "tasks", "delta-specs", "sub-agent"],
1172
+ content: instructions10
1173
+ };
1174
+ }
1175
+
1176
+ // src/templates/workflows/apply.ts
1177
+ var instructions11 = `## Input
1178
+
1179
+ ### Parameters
1180
+ - **\`<change-name>\`** (required) \u2014 the change to implement. Provided by \`specwf continue\` output or user.
1181
+ - If no change name is available, check the \`pending\` array from \`specwf context <step>\` JSON output, then ask the user which to work on.
1182
+
1183
+ ### Prerequisites
1184
+ - Plan phase complete: \`design.md\`, \`tasks.md\`, delta-specs ready
1185
+
1186
+ ## Steps
1187
+
1188
+ ### Step 1: Resolve change name and get context
1189
+ Run \`specwf context apply\` \u2014 outputs JSON with state (including pending changes) and file manifest. If a change name was provided, use it directly. If not, read the \`pending\` array from the JSON, filter by status \`planning\`, and ask the user to pick. Then read all files listed in \`specs\`, \`conventions\`, and \`artifacts\`.
1190
+
1191
+ ### Step 2: Dispatch executor sub-agent
1192
+ **You are the orchestrator \u2014 dispatch, do not implement yourself.** Spawn \`specwf-executor\` sub-agent with:
1193
+
1194
+ \`\`\`text
1195
+ Sub-agent: specwf-executor
1196
+ Change: <change-name> (from specwf/changes/<change-name>/)
1197
+
1198
+ Task: Implement all tasks in tasks.md following TDD protocol.
1199
+ Read design.md for approach, delta-specs for constraints.
1200
+ type:behavior tasks: RED->GREEN->REFACTOR (mandatory).
1201
+ All commits atomic, Conventional Commits format.
1202
+ Run type check and tests after each wave.
1203
+
1204
+ Completion: write completion.md listing tasks done, tests passed, commits made.
1205
+ \`\`\`
1206
+
1207
+ ### Step 3: Verify output and completion report
1208
+ After the executor finishes, check \`completion.md\` exists and confirm:
1209
+ - All tasks.md checkboxes checked
1210
+ - Type check passes (\`tsc --noEmit\`)
1211
+ - All tests pass (\`vitest run\`)
1212
+ - Each delta-spec SHALL/MUST has test coverage
1213
+
1214
+ ### Step 4: Generate change summary
1215
+ Run \`specwf template change-summary --name <change-name> --dir specwf/changes/<change-name>\`, then fill it with actual details. Do NOT skip \u2014 the summary is the handoff artifact for review.
1216
+
1217
+ ### Step 5: Pre-advance checklist
1218
+ - [ ] All wave tasks complete
1219
+ - [ ] Type check passes
1220
+ - [ ] All tests pass
1221
+ - [ ] Change summary written and filled (not template)
1222
+ - [ ] completion.md from executor exists and verified
1223
+
1224
+ ### Step 6: Advance
1225
+ Run \`specwf continue\` to proceed to review.
1226
+
1227
+ ## Guardrails
1228
+ - **You are the orchestrator** \u2014 dispatch specwf-executor, never implement yourself
1229
+ - GREEN phase writes ONLY enough code to pass \u2014 save refactoring for REFACTOR
1230
+ - Never skip RED \u2014 always write the failing test first
1231
+ - **Summary is mandatory**: advancing without a filled change-summary.md is a process violation`;
1232
+ function getApplySkillTemplate() {
1233
+ return {
1234
+ name: "specwf-apply",
1235
+ description: "Code implementation \u2014 dispatch executor sub-agent for TDD RED->GREEN->REFACTOR",
1236
+ instructions: instructions11
1237
+ };
1238
+ }
1239
+ function getApplyCommandTemplate() {
1240
+ return {
1241
+ name: "SpecWF: Apply",
1242
+ description: "Code implementation \u2014 dispatch executor sub-agent for TDD RED->GREEN->REFACTOR",
1243
+ category: "Workflow",
1244
+ tags: ["specwf", "apply", "implementation", "tdd", "sub-agent"],
1245
+ content: instructions11
1246
+ };
1247
+ }
1248
+
1249
+ // src/templates/workflows/review.ts
1250
+ var instructions12 = `## Input
1251
+
1252
+ ### Parameters
1253
+ - **\`<change-name>\`** (required) \u2014 the change to review. Provided by \`specwf continue\` output or user.
1254
+ - If no change name is available, check the \`pending\` array from \`specwf context <step>\` JSON output, then ask the user.
1255
+
1256
+ ### Prerequisites
1257
+ - Apply phase complete: implementation code, tests, summary.md
1258
+
1259
+ ## Steps
1260
+
1261
+ ### Step 1: Resolve change name and get context
1262
+ If a change name was provided: use it directly. If not: run \`specwf state\`, list pending changes with status \`applying\`, ask the user to pick. Then run \`specwf context review\` to get the file manifest. Read all listed files.
1263
+
1264
+ ### Step 2: Dispatch parallel review sub-agents
1265
+ **You are the orchestrator \u2014 dispatch, do not review yourself.** Spawn three \`specwf-reviewer\` sub-agents in parallel, each with a different role:
1266
+
1267
+ \`\`\`text
1268
+ Sub-agent: specwf-reviewer
1269
+ Change: <change-name>
1270
+ Role: spec-review | quality-review | goal-review (pick one per agent)
1271
+
1272
+ Task: Review the change according to your assigned role.
1273
+ Read proposal.md, delta-specs, design.md, and the implementation.
1274
+ Cite specific file:line references for every finding.
1275
+
1276
+ Output: write spec-review.md | quality-review.md | goal-review.md to specwf/changes/<change-name>/
1277
+ \`\`\`
1278
+
1279
+ ### Step 3: Aggregate results
1280
+ After all three complete, check each report:
1281
+ - \`spec-review.md\`: all SHALL/MUST covered? BLOCKERs?
1282
+ - \`quality-review.md\`: any BLOCKERs or MAJOR issues?
1283
+ - \`goal-review.md\`: all goals ACHIEVED?
1284
+
1285
+ ### Step 4: Handle findings
1286
+ - Auto-fixable issues \u2192 fix and re-verify
1287
+ - Architecture-level issues \u2192 pause and ask user
1288
+ - No BLOCKERs \u2192 advance
1289
+
1290
+ ### Step 5: Advance
1291
+ Run \`specwf continue\` to proceed to verify.
1292
+
1293
+ ## Guardrails
1294
+ - **You are the orchestrator** \u2014 dispatch reviewers, do not review yourself
1295
+ - All three reviews run in parallel \u2014 no inter-dependencies
1296
+ - Gate behavior depends on project.yml \`review.gate\` setting
1297
+ - Findings classified: BLOCKER (must fix), FLAG (should fix), NOTE (informational)`;
1298
+ function getReviewSkillTemplate() {
1299
+ return {
1300
+ name: "specwf-review",
1301
+ description: "Triple review \u2014 dispatch reviewer sub-agents in parallel",
1302
+ instructions: instructions12
1303
+ };
1304
+ }
1305
+ function getReviewCommandTemplate() {
1306
+ return {
1307
+ name: "SpecWF: Review",
1308
+ description: "Triple review \u2014 dispatch reviewer sub-agents in parallel",
1309
+ category: "Workflow",
1310
+ tags: ["specwf", "review", "quality", "specs", "sub-agent"],
1311
+ content: instructions12
1312
+ };
1313
+ }
1314
+
1315
+ // src/templates/workflows/verify.ts
1316
+ var instructions13 = `## Input
1317
+
1318
+ ### Parameters
1319
+ - **\`<change-name>\`** (required) \u2014 the change to verify. Provided by \`specwf continue\` output or user.
1320
+ - If no change name, check the \`pending\` array from \`specwf context verify\` JSON, filter by status \`reviewing\`, ask the user.
1321
+
1322
+ ### Prerequisites
1323
+ - Review phase complete: spec-review.md, quality-review.md, goal-review.md
1324
+ - All review blockers resolved
1325
+
1326
+ ## Steps
1327
+
1328
+ ### Step 1: Resolve change name and get context
1329
+ Run \`specwf context verify\` \u2014 outputs JSON with state and file manifest. If a change name was provided, use it directly. If not, read the \`pending\` array, filter by status \`reviewing\`, ask the user.
1330
+
1331
+ ### Step 2: Dispatch verifier sub-agent
1332
+ **You are the orchestrator \u2014 dispatch, do not verify yourself.** Spawn \`specwf-verifier\` with:
1333
+
1334
+ \`\`\`text
1335
+ Sub-agent: specwf-verifier
1336
+ Change: <change-name> (from specwf/changes/<change-name>/)
1337
+
1338
+ ## Goal-Backward Verification
1339
+
1340
+ Start from what the change SHOULD deliver, then verify it actually exists and works. Do NOT trust summary.md claims \u2014 they document what was SAID, not what EXISTS.
1341
+
1342
+ ## Step 1: Establish truths
1343
+ Read proposal.md must-haves and delta-specs. For each must-have, ask:
1344
+ 1. What must be TRUE for this to be achieved?
1345
+ 2. What must EXIST in the codebase for those truths?
1346
+ 3. What must be WIRED for those artifacts to function?
1347
+
1348
+ ## Step 2: Run automated checks
1349
+ - Full test suite (diagnose any failures to root cause)
1350
+ - Type check (tsc --noEmit or equivalent)
1351
+ - Lint check
1352
+ - Each delta-spec SHALL/MUST must have passing test coverage
1353
+
1354
+ ## Step 3: Adversarial stance
1355
+ Assume each must-have is NOT achieved until codebase evidence proves it. Classify:
1356
+ - **VERIFIED**: code evidence confirms the must-have is delivered
1357
+ - **FAILED (BLOCKER)**: must-have not achieved; change must not proceed
1358
+ - **UNCERTAIN (WARNING)**: insufficient evidence; human decision required
1359
+
1360
+ ## Step 4: UAT \u2014 User Acceptance Testing
1361
+ For each must-have that describes user-observable behavior:
1362
+ - If a UI exists: open it, verify the behavior matches the spec
1363
+ - If an API exists: call it, verify the response matches the spec
1364
+ - If a CLI command exists: run it, verify the output matches the spec
1365
+ - Record what was tested and the result
1366
+
1367
+ ## Step 5: Output VERIFICATION.md
1368
+ Write to specwf/changes/<change-name>/verification.md:
1369
+ - Status: passed | gaps_found | human_needed
1370
+ - Truth table: each must-have \u2192 VERIFIED / FAILED / UNCERTAIN with evidence
1371
+ - UAT results: what was tested manually, what passed
1372
+ - BLOCKERs: list of must-haves that failed (blocks advancement)
1373
+ - WARNINGs: items needing human review
1374
+
1375
+ ## Routing
1376
+ - passed \u2192 archive
1377
+ - gaps_found \u2192 BLOCKERs listed \u2192 route back to apply (reapply)
1378
+ - human_needed \u2192 surface to user with specific questions
1379
+ \`\`\`
1380
+
1381
+ ### Step 3: Handle results
1382
+ - \`passed\` \u2192 advance to archive
1383
+ - \`gaps_found\` \u2192 route back to apply (reapply) or plan (replan)
1384
+ - \`human_needed\` \u2192 surface to user with specific blocking questions
1385
+
1386
+ ### Step 4: Advance
1387
+ Run \`specwf continue\` to proceed to archive (if passed).
1388
+
1389
+ ## Output
1390
+ - \`verification.md\` \u2014 verification report with truth table, UAT results, BLOCKERs/WARNINGs
1391
+
1392
+ ## Guardrails
1393
+ - **You are the orchestrator** \u2014 dispatch verifier, do not verify yourself
1394
+ - Goal-backward: what should be delivered \u2192 is it actually there?
1395
+ - Adversarial stance: assume NOT achieved until evidence proves it
1396
+ - BLOCKERs block advancement \u2014 do not advance with unresolved BLOCKERs`;
1397
+ function getVerifySkillTemplate() {
1398
+ return {
1399
+ name: "specwf-verify",
1400
+ description: "Test verification \u2014 goal-backward analysis + UAT, dispatch verifier sub-agent",
1401
+ instructions: instructions13
1402
+ };
1403
+ }
1404
+ function getVerifyCommandTemplate() {
1405
+ return {
1406
+ name: "SpecWF: Verify",
1407
+ description: "Test verification \u2014 goal-backward analysis + UAT, dispatch verifier sub-agent",
1408
+ category: "Workflow",
1409
+ tags: ["specwf", "verify", "testing", "uat", "sub-agent"],
1410
+ content: instructions13
1411
+ };
1412
+ }
1413
+
1414
+ // src/templates/workflows/archive.ts
1415
+ var instructions14 = `## Input
1416
+
1417
+ ### Parameters
1418
+ - **\`<change-name>\`** (required) \u2014 the change to archive. Provided by \`specwf continue\` output or user.
1419
+ - If no change name, check the \`pending\` array from \`specwf context archive\` JSON, filter by status \`verifying\`, and ask the user.
1420
+
1421
+ ### Prerequisites
1422
+ - Verify phase passed (verification.md status: passed)
1423
+ - All changes committed and pushed
1424
+
1425
+ ## Steps
1426
+
1427
+ ### Step 1: Resolve change name and get context
1428
+ Run \`specwf context archive\` \u2014 outputs JSON with state (pending list) and file manifest. If a change name was provided, use it directly. If not, read the \`pending\` array, filter by status \`verifying\`, ask the user to pick. Then read files from \`specs\` and \`artifacts\`.
1429
+
1430
+ ### Step 2: Dispatch archiver sub-agent
1431
+ **You are the orchestrator \u2014 dispatch, do not archive yourself.** Spawn \`specwf-archiver\` with:
1432
+
1433
+ \`\`\`text
1434
+ Sub-agent: specwf-archiver
1435
+ Change: <change-name>
1436
+
1437
+ Task: Archive the completed change.
1438
+ - Merge delta-specs from specwf/changes/<change-name>/specs/ into specwf/specs/
1439
+ - Run code cognition backfill (update context.md)
1440
+ - Move change to specwf/archive/<YYYY-MM-DD>-<change-name>/
1441
+ - Update state.md: mark as archived
1442
+
1443
+ Output: write completion.md with merge results and archived path.
1444
+ \`\`\`
1445
+
1446
+ ### Step 3: Verify archival
1447
+ Check \`completion.md\` and confirm:
1448
+ - Global \`specwf/specs/\` updated with delta-specs
1449
+ - Change directory moved to \`specwf/archive/<date>-<name>/\`
1450
+ - \`state.md\` reflects archived status
1451
+
1452
+ ### Step 4: Advance
1453
+ Run \`specwf continue\` \u2014 if all phase changes are archived, routes to ship-phase.
1454
+
1455
+ ## Guardrails
1456
+ - **You are the orchestrator** \u2014 dispatch archiver, do not archive yourself
1457
+ - Delta-spec merge must resolve conflicts, not overwrite
1458
+ - Archived changes are never deleted
1459
+ - If archival fails, the change stays in place`;
1460
+ function getArchiveSkillTemplate() {
1461
+ return {
1462
+ name: "specwf-archive",
1463
+ description: "Archive \u2014 dispatch archiver sub-agent for delta-spec merge + backfill",
1464
+ instructions: instructions14
1465
+ };
1466
+ }
1467
+ function getArchiveCommandTemplate() {
1468
+ return {
1469
+ name: "SpecWF: Archive",
1470
+ description: "Archive \u2014 dispatch archiver sub-agent for delta-spec merge + backfill",
1471
+ category: "Workflow",
1472
+ tags: ["specwf", "archive", "specs", "merge", "sub-agent"],
1473
+ content: instructions14
1474
+ };
1475
+ }
1476
+
1477
+ // src/templates/workflows/ship.ts
1478
+ var instructions15 = `## Input
1479
+
1480
+ ### Parameters
1481
+ - **No parameters**: auto-detect ship context from \`specwf state\`
1482
+
1483
+ ### Prerequisites
1484
+ - Phase ship: all phase changes archived
1485
+ - Milestone ship: all phases shipped
1486
+ - Git remote configured (for PR creation)
1487
+
1488
+ ## Steps
1489
+
1490
+ ### Step 1: Get context
1491
+ Run \`specwf context ship\` \u2014 outputs JSON with state info. Determine ship context (phase or milestone).
1492
+
1493
+ ### Step 2: Dry-run first
1494
+ Always preview before executing:
1495
+
1496
+ \`\`\`bash
1497
+ specwf ship --dry-run
1498
+ \`\`\`
1499
+
1500
+ Review the output \u2014 what will be created, what state will change.
1501
+
1502
+ ### Step 3: Phase ship
1503
+ Creates a PR summarizing all changes in the phase:
1504
+ \`\`\`bash
1505
+ specwf ship phase
1506
+ \`\`\`
1507
+ - Generates phase summary from archived changes
1508
+ - Creates PR via \`gh pr create\`
1509
+ - Updates state.md: marks phase as shipped
1510
+
1511
+ ### Step 4: Milestone ship
1512
+ Publishes a release tag:
1513
+ \`\`\`bash
1514
+ specwf ship milestone
1515
+ \`\`\`
1516
+ - Creates release tag (e.g. v0.1.0)
1517
+ - Updates project.md version
1518
+ - Updates state.md: marks milestone as shipped
1519
+
1520
+ ### Step 5: Advance
1521
+ Run \`specwf continue\` to start the next phase or milestone.
1522
+
1523
+ ## Output
1524
+ - PR on GitHub (phase ship)
1525
+ - Release tag (milestone ship)
1526
+ - Updated \`state.md\` and \`project.md\`
1527
+
1528
+ ## Guardrails
1529
+ - Always run \`--dry-run\` first \u2014 never ship without previewing
1530
+ - Phase ship requires all changes archived
1531
+ - Milestone ship requires all phases shipped`;
1532
+ function getShipSkillTemplate() {
1533
+ return {
1534
+ name: "specwf-ship",
1535
+ description: "Ship \u2014 dry-run first, then create PR or release tag",
1536
+ instructions: instructions15
1537
+ };
1538
+ }
1539
+ function getShipCommandTemplate() {
1540
+ return {
1541
+ name: "SpecWF: Ship",
1542
+ description: "Ship \u2014 dry-run first, then create PR or release tag",
1543
+ category: "Workflow",
1544
+ tags: ["specwf", "ship", "release", "pr"],
1545
+ content: instructions15
1546
+ };
1547
+ }
1548
+
1549
+ // src/templates/workflows/continue.ts
1550
+ var instructions16 = `## Input
1551
+
1552
+ ### Two commands \u2014 two scopes
1553
+
1554
+ | Command | Scope | When to use |
1555
+ |---------|-------|------------|
1556
+ | \`specwf continue\` | Project-level / Phase-level | After init, grill, research, roadmap, discuss, research-phase, split, ship |
1557
+ | \`specwf continue change <name>\` | Single change | Advance a specific change through plan \u2192 apply \u2192 review \u2192 verify \u2192 archive |
1558
+
1559
+ ### Prerequisites
1560
+ - \`specwf/state.md\` must exist and be valid
1561
+ - Previous step must be complete (exit conditions met)
1562
+
1563
+ ## Steps
1564
+
1565
+ ### Scenario A: Project-level advancement (\`specwf continue\`)
1566
+
1567
+ Use this after project-level steps (init, grill, research, roadmap, discuss, research-phase, split, ship).
1568
+
1569
+ Run \`specwf continue\`. The CLI:
1570
+ 1. Reads \`active_context\` from state.md to determine what's current
1571
+ 2. Validates the current step's exit conditions
1572
+ 3. Advances state to the next step
1573
+ 4. Outputs the next step's inline instructions
1574
+
1575
+ Examples:
1576
+ - After \`specwf init\` \u2192 \`specwf continue\` routes to grill
1577
+ - After grill \u2192 routes to research
1578
+ - After research \u2192 routes to roadmap
1579
+ - After roadmap \u2192 routes to discuss (first phase)
1580
+ - After discuss \u2192 routes to research-phase
1581
+ - After research-phase \u2192 routes to split
1582
+ - After split \u2192 routes to plan (for first change)
1583
+
1584
+ If \`specwf continue\` says "No available next step" but the project has pending changes, switch to Scenario B for those changes.
1585
+
1586
+ ### Scenario B: Change-level advancement (\`specwf continue change <name>\`)
1587
+
1588
+ Use this to advance an individual change through its cycle. The change name comes from:
1589
+ - The \`specwf continue\` output (which tells you which change to work on)
1590
+ - \`specwf state\` output (lists pending changes with their status)
1591
+ - User explicitly naming a change
1592
+
1593
+ **With a name**: run \`specwf continue change <name>\`. The CLI advances that change's state and outputs the next step's inline instructions.
1594
+
1595
+ **Without a name**: run \`specwf state\`, read the pending changes list, and present options:
1596
+
1597
+ \`\`\`
1598
+ Which change should I advance?
1599
+
1600
+ 1. <change-name> [planning] \u2014 ready for apply
1601
+ 2. <change-name> [applying] \u2014 ready for review
1602
+ 3. <change-name> [proposal] \u2014 ready for plan
1603
+
1604
+ Pick a number or name.
1605
+ \`\`\`
1606
+
1607
+ Then run \`specwf continue change <selected-name>\`.
1608
+
1609
+ ### Scenario C: No pending work
1610
+
1611
+ If both \`specwf continue\` and all pending changes report "No available next step":
1612
+
1613
+ \`\`\`
1614
+ All work is complete or blocked.
1615
+
1616
+ Current state:
1617
+ - Project status: <status>
1618
+ - Active milestone: <name>
1619
+ - Pending changes: <list or "none">
1620
+ - Adhoc changes: <list or "none">
1621
+
1622
+ Run \`specwf state\` to inspect. To start new work, use:
1623
+ - \`specwf change new <name>\` for an adhoc change
1624
+ - \`specwf continue\` after completing blocking prerequisites
1625
+ \`\`\`
1626
+
1627
+ ## Output
1628
+ Inline instructions for the next workflow step, including:
1629
+ - Current position and context
1630
+ - Next command name and slash command
1631
+ - Whether sub-agents are needed
1632
+ - Full step instructions (Input, Steps, Output, Guardrails)
1633
+
1634
+ ## Guardrails
1635
+ - \`specwf continue\` (no args) = project/phase level; \`specwf continue change <name>\` = change level \u2014 do not mix them
1636
+ - Continue does NOT advance state if exit conditions are not met \u2014 it reports what's blocking
1637
+ - The output instructions are self-contained \u2014 execute them directly, no need to read another file
1638
+ - When multiple changes are pending, always present choices \u2014 do not silently pick one`;
1639
+ function getContinueSkillTemplate() {
1640
+ return {
1641
+ name: "specwf-continue",
1642
+ description: "Auto-advance \u2014 project-level or change-level, present pending work, route to next step",
1643
+ instructions: instructions16
1644
+ };
1645
+ }
1646
+ function getContinueCommandTemplate() {
1647
+ return {
1648
+ name: "SpecWF: Continue",
1649
+ description: "Auto-advance \u2014 project-level or change-level, present pending work, route to next step",
1650
+ category: "Workflow",
1651
+ tags: ["specwf", "continue", "state-machine"],
1652
+ content: instructions16
1653
+ };
1654
+ }
1655
+
1656
+ // src/templates/workflows/registry.ts
1657
+ var WORKFLOW_REGISTRY = {
1658
+ init: { skill: getInitSkillTemplate, command: getInitCommandTemplate },
1659
+ grill: { skill: getGrillSkillTemplate, command: getGrillCommandTemplate },
1660
+ research: { skill: getResearchSkillTemplate, command: getResearchCommandTemplate },
1661
+ roadmap: { skill: getRoadmapSkillTemplate, command: getRoadmapCommandTemplate },
1662
+ milestone: { skill: getMilestoneSkillTemplate, command: getMilestoneCommandTemplate },
1663
+ discuss: { skill: getDiscussSkillTemplate, command: getDiscussCommandTemplate },
1664
+ "research-phase": { skill: getResearchPhaseSkillTemplate, command: getResearchPhaseCommandTemplate },
1665
+ split: { skill: getSplitSkillTemplate, command: getSplitCommandTemplate },
1666
+ adhoc: { skill: getAdhocSkillTemplate, command: getAdhocCommandTemplate },
1667
+ plan: { skill: getPlanSkillTemplate, command: getPlanCommandTemplate },
1668
+ apply: { skill: getApplySkillTemplate, command: getApplyCommandTemplate },
1669
+ review: { skill: getReviewSkillTemplate, command: getReviewCommandTemplate },
1670
+ verify: { skill: getVerifySkillTemplate, command: getVerifyCommandTemplate },
1671
+ archive: { skill: getArchiveSkillTemplate, command: getArchiveCommandTemplate },
1672
+ ship: { skill: getShipSkillTemplate, command: getShipCommandTemplate },
1673
+ continue: { skill: getContinueSkillTemplate, command: getContinueCommandTemplate }
1674
+ };
1675
+
1676
+ // src/integrations/omp/commands.ts
1677
+ var STEP_DEFS = [
1678
+ { step: "init", name: "specwf:init", description: "Initialize specwf project structure and generate platform files", usesAgent: true, agents: ["researcher"] },
1679
+ { step: "grill", name: "specwf:grill", description: "Requirements exploration \u2014 detailed questioning until shared understanding", usesAgent: false, agents: [] },
1680
+ { step: "research", name: "specwf:research", description: "Project-level technical research \u2014 parallel multi-direction investigation", usesAgent: true, agents: ["researcher"] },
1681
+ { step: "roadmap", name: "specwf:roadmap", description: "Roadmap definition \u2014 split project into Milestones \xD7 Phases", usesAgent: false, agents: [] },
1682
+ { step: "milestone", name: "specwf:milestone", description: "Milestone management \u2014 switch/create milestones, set current phase", usesAgent: false, agents: [] },
1683
+ { step: "discuss", name: "specwf:discuss", description: "Phase discussion \u2014 capture implementation decisions into context.md", usesAgent: false, agents: [] },
1684
+ { step: "research-phase", name: "specwf:research-phase", description: "Phase research \u2014 implementation path investigation", usesAgent: true, agents: ["researcher"] },
1685
+ { step: "split", name: "specwf:split", description: "Change splitting \u2014 dependency graph + N changes", usesAgent: false, agents: [] },
1686
+ { step: "adhoc", name: "specwf:adhoc", description: "Create adhoc change \u2014 independent change unrelated to milestone/phase", usesAgent: false, agents: [] },
1687
+ { step: "plan", name: "specwf:plan", description: "Change design \u2014 technical design + task breakdown + delta-specs", usesAgent: true, agents: ["planner"] },
1688
+ { step: "apply", name: "specwf:apply", description: "Code implementation \u2014 TDD RED\u2192GREEN\u2192REFACTOR", usesAgent: true, agents: ["executor"] },
1689
+ { step: "review", name: "specwf:review", description: "Triple review \u2014 spec/quality/goal reviews in parallel", usesAgent: true, agents: ["reviewer"] },
1690
+ { step: "verify", name: "specwf:verify", description: "Test verification \u2014 diagnose root cause + route loopback", usesAgent: true, agents: ["verifier"] },
1691
+ { step: "archive", name: "specwf:archive", description: "Archive \u2014 delta-spec merge + code cognition backfill", usesAgent: false, agents: [] },
1692
+ { step: "ship", name: "specwf:ship", description: "Ship \u2014 create PR + update state / release tag", usesAgent: false, agents: [] },
1693
+ { step: "continue", name: "specwf:continue", description: "Auto-advance \u2014 read STATE and route to next step", usesAgent: false, agents: [] }
1694
+ ];
1695
+ function generateSlashCommand(def, _config) {
1696
+ const entry = WORKFLOW_REGISTRY[def.step];
1697
+ const body = entry ? entry.command().content : fallbackBody(def);
1698
+ return `---
1699
+ name: ${def.name}
1700
+ description: ${def.description}
1701
+ ---
1702
+
1703
+ ${body}
1704
+ `;
1705
+ }
1706
+ function fallbackBody(def) {
1707
+ const agentsSection = def.usesAgent && def.agents.length > 0 ? `Dispatch \`specwf-${def.agents[0]}\` sub-agent via task tool.` : "This step does not use sub-agents.";
1708
+ return `# ${def.description}
1709
+
1710
+ ## Input
1711
+
1712
+ - state.md status is correct
1713
+ - All prerequisite steps are complete
1714
+
1715
+ ## Steps
1716
+
1717
+ ### Step 1: Check state
1718
+ Run \`specwf state\` to verify current position.
1719
+
1720
+ ### Step 2: Get context
1721
+ Run \`specwf context ${def.step}\` to read the file manifest.
1722
+
1723
+ ### Step 3: Execute
1724
+ Run \`specwf ${def.step}\` to perform the step.
1725
+
1726
+ ## Sub-agents
1727
+
1728
+ ${agentsSection}
1729
+
1730
+ ## Output
1731
+
1732
+ Check \`specwf state\` for updated status.
1733
+
1734
+ ## Advance
1735
+
1736
+ Run \`specwf continue\` to proceed to the next step.
1737
+ `;
1738
+ }
1739
+ function generateAllCommands(config) {
1740
+ return STEP_DEFS.map((def) => ({
1741
+ path: `.omp/commands/specwf-${def.step}.md`,
1742
+ content: generateSlashCommand(def, config)
1743
+ }));
1744
+ }
1745
+
1746
+ // src/integrations/omp/agents.ts
1747
+ import { dirname } from "path";
1748
+ import { fileURLToPath } from "url";
1749
+
1750
+ // src/templates/agents/index.ts
1751
+ var PLANNER_PROMPT = `## Role
1752
+
1753
+ You are a **Change Design Specialist** for specwf.
1754
+
1755
+ Your core responsibility is to analyze proposals, design technical solutions, create executable task checklists, and pre-write delta-specs as quality contracts. Your output directly drives the executor's implementation.
1756
+
1757
+ - Design complete technical solutions including architecture, data flow, and component trees
1758
+ - Break changes into independently committable task granularity
1759
+ - Annotate TDD protocol requirements for each type:behavior task
1760
+ - Pre-write delta-specs to ensure specification consistency
1761
+ - NEVER reduce or simplify the user's decision scope
1762
+
1763
+ ## Core Constraints
1764
+
1765
+ - All artifacts written to the specwf/ directory
1766
+ - Use bash to invoke specwf CLI for state management
1767
+ - Respect project.yml context field
1768
+ - Follow conventions/ for coding standards
1769
+ - All output files use English
1770
+
1771
+ ## Execution Flow
1772
+
1773
+ ### Step 1: Read project context and proposal
1774
+ - Read specwf/project.yml for profile and workflow configuration
1775
+ - Read the change's proposal.md for intent, scope, and must-haves
1776
+ - Read specwf/specs/ for existing global specs
1777
+ - Read specwf/conventions/ for coding standards
1778
+
1779
+ ### Step 2: Design technical solution
1780
+ - Design overall architecture based on proposal and context
1781
+ - Consider at least 2 alternatives and compare
1782
+ - Document the complete design in design.md using \`specwf template design\`
1783
+
1784
+ ### Step 3: Break down into executable tasks
1785
+ - Use tracer-bullet vertical slice principle
1786
+ - First wave is typically an end-to-end skeleton
1787
+ - Annotate each task's type and dependencies
1788
+
1789
+ ### Step 4: Pre-write delta-specs
1790
+ - Create spec files under specs/<domain>/
1791
+ - Use SHALL/MUST/SHOULD/MAY keywords
1792
+ - Ensure each spec item is testable
1793
+
1794
+ ## Deviation Rules
1795
+
1796
+ 1. **Scope reduction prohibition**: NEVER reduce user decision points to simplify implementation
1797
+ 2. **Spec gap fill**: Annotate missing specs as SPEC_GAP_FILL
1798
+ 3. **Task granularity**: behavior task \u2264 50 lines, refactor task \u2264 200 lines changed
1799
+ 4. **Alternative archiving**: Record rejected alternatives in design.md
1800
+
1801
+ ## Output Requirements
1802
+
1803
+ - design.md \u2014 technical design with architecture, data flow, alternatives
1804
+ - tasks.md \u2014 implementation checklist with TDD annotations
1805
+ - specs/<domain>/spec.md \u2014 delta behavioral contracts
1806
+
1807
+ ## Verification Criteria
1808
+
1809
+ - tasks.md covers all must_haves from proposal.md
1810
+ - Each type:behavior task has a RED test description
1811
+ - Delta-spec SHALL/MUST constraints are testable
1812
+ - No circular dependencies between tasks`;
1813
+ var EXECUTOR_PROMPT = `## Role
1814
+
1815
+ You are a **Code Implementation Specialist** for specwf.
1816
+
1817
+ Your core responsibility is to implement code according to tasks.md, strictly following TDD protocol (RED\u2192GREEN\u2192REFACTOR), and ensuring each commit is atomic and verifiable.
1818
+
1819
+ - Execute tasks in strict order, never skipping any task
1820
+ - Follow TDD protocol: write failing test first, then implement, then refactor
1821
+ - Ensure each commit is an independent atomic change
1822
+ - Auto-fix bugs or missing code when discovered
1823
+ - Pause and ask when encountering architecture-level changes
1824
+
1825
+ ## Core Constraints
1826
+
1827
+ - All artifacts written to the specwf/ directory
1828
+ - Use bash to invoke specwf CLI for state management
1829
+ - Respect project.yml context field
1830
+ - Follow conventions/ for project conventions
1831
+ - All output files use English
1832
+
1833
+ ## Execution Flow
1834
+
1835
+ ### Step 1: Read task list
1836
+ - Read tasks.md for current wave task list and order
1837
+ - Read design.md for technical approach
1838
+ - Read delta-specs for specification constraints
1839
+
1840
+ ### Step 2: Execute by type
1841
+
1842
+ **type:behavior \u2192 TDD three-step protocol**
1843
+ 1. **RED**: Write a failing test \u2014 test must be runnable and fail on assertion
1844
+ Commit: \`test(<scope>): RED - <description>\`
1845
+ 2. **GREEN**: Write minimal implementation to pass the test \u2014 only what's needed
1846
+ Commit: \`feat(<scope>): GREEN - <description>\`
1847
+ 3. **REFACTOR**: Improve code quality without changing behavior
1848
+ Commit: \`refactor(<scope>): REFACTOR - <description>\`
1849
+
1850
+ **type:config** \u2014 direct implementation, single commit: \`config(<scope>): <description>\`
1851
+ **type:refactor** \u2014 verify tests pass first, then refactor: \`refactor(<scope>): <description>\`
1852
+ **type:docs** \u2014 documentation update: \`docs(<scope>): <description>\`
1853
+ **type:scaffolding** \u2014 skeleton code: \`chore(<scope>): <description>\`
1854
+
1855
+ ### Step 3: Per-task verification
1856
+ - Run related tests, confirm no regressions
1857
+ - Confirm delta-spec constraints are satisfied
1858
+
1859
+ ### Step 4: Wave completion
1860
+ - Confirm all wave tasks complete
1861
+ - Run full test suite
1862
+
1863
+ ## Deviation Rules
1864
+
1865
+ 1. **auto-fix**: Auto-fix bugs discovered in code, annotate [auto-fix]
1866
+ 2. **auto-add**: Auto-add missing helper code, annotate [auto-add]
1867
+ 3. **auto-fix-blocking**: Attempt auto-fix for build/dependency issues up to 3 times, then pause
1868
+ 4. **ask-architectural**: Pause and describe architectural changes for confirmation
1869
+
1870
+ **Analysis paralysis guard**: After 5 consecutive reads without a write, stop and diagnose what's blocking.
1871
+
1872
+ ## Output
1873
+ - Code changes per tasks.md
1874
+ - Tests co-located with source files (*.test.ts)
1875
+ - Atomic git commits in Conventional Commits format
1876
+
1877
+ ## Verification
1878
+ - All type:behavior tests pass (RED\u2192GREEN\u2192REFACTOR complete)
1879
+ - Implementation matches delta-spec SHALL/MUST
1880
+ - Each commit is atomic, commit messages conform to spec`;
1881
+ var REVIEWER_PROMPT = `## Role
1882
+
1883
+ You are a **Triple Review Specialist** for specwf.
1884
+
1885
+ Your orchestrator will assign you one of three roles: **spec-review**, **quality-review**, or **goal-review**. Execute only the assigned role.
1886
+
1887
+ ## Core Constraints
1888
+ - All output files use English
1889
+ - Every finding must cite specific file:line references
1890
+
1891
+ ## Role: spec-review
1892
+ Cross-reference delta-spec SHALL/MUST constraints against implementation:
1893
+ - Read delta-specs from specwf/changes/<change-name>/specs/
1894
+ - Use grep/ast_grep to find corresponding implementation
1895
+ - Annotate each constraint: PASS / FAIL / NOT_APPLICABLE with file:line
1896
+ - Check edge cases for each constraint
1897
+ - Output to specwf/changes/<change-name>/spec-review.md
1898
+
1899
+ ## Role: quality-review
1900
+ Audit code for bugs, security, conventions, and AI mistakes:
1901
+ - Bug patterns: null pointer, resource leak, race condition, type error
1902
+ - Security: injection, XSS, auth bypass, sensitive data exposure
1903
+ - Conventions: naming, directory structure, import style vs conventions/
1904
+ - AI mistakes: hallucinated APIs, over-abstraction, missing error handling, hard-coded values
1905
+ - Severity: BLOCKER / MAJOR / MINOR / INFO
1906
+ - Output to specwf/changes/<change-name>/quality-review.md
1907
+
1908
+ ## Role: goal-review
1909
+ Verify the change achieves what it promised:
1910
+ - Read proposal.md for goals and must_haves
1911
+ - Cross-reference each goal against implementation
1912
+ - Annotate: ACHIEVED / PARTIAL / NOT_ACHIEVED with evidence
1913
+ - Assess overall completeness
1914
+ - Output to specwf/changes/<change-name>/goal-review.md
1915
+
1916
+ ## Output Format
1917
+ Every review report must include:
1918
+ - Overall verdict: PASS / FAIL / NEEDS_REVISION
1919
+ - Numbered findings with file:line references
1920
+ - NO_ISSUES_FOUND if nothing found (never leave a review blank)`;
1921
+ var VERIFIER_PROMPT = `## Role
1922
+
1923
+ You are a **Test Verification Specialist** for specwf.
1924
+
1925
+ Your core responsibility is to verify that implemented changes meet their goals. Run the full test suite, diagnose failures to root cause, and verify TDD commit integrity.
1926
+
1927
+ ## Core Constraints
1928
+
1929
+ - All artifacts written to the specwf/ directory
1930
+ - All output files use English
1931
+
1932
+ ## Execution Flow
1933
+
1934
+ ### Step 1: Read context
1935
+ - Read delta-specs, tasks.md, review reports
1936
+
1937
+ ### Step 2: Run test suite
1938
+ - Execute all tests, diagnose any failures to root cause
1939
+
1940
+ ### Step 3: Verify coverage
1941
+ - Each delta-spec SHALL/MUST has a passing test
1942
+ - TDD commit integrity: RED\u2192GREEN\u2192REFACTOR sequence for type:behavior
1943
+
1944
+ ### Step 4: Output verification.md
1945
+ Status: passed | gaps_found | human_needed
1946
+
1947
+ ## Routing
1948
+ - passed \u2192 archive
1949
+ - gaps_found \u2192 reapply or replan
1950
+ - human_needed \u2192 surface to user with specific questions`;
1951
+ var ARCHIVER_PROMPT = `## Role
1952
+
1953
+ You are an **Archive Specialist** for specwf.
1954
+
1955
+ Your core responsibility is to merge delta-specs into global specs, run code cognition backfill, and move completed changes to the archive.
1956
+
1957
+ ## Core Constraints
1958
+
1959
+ - All artifacts written to the specwf/ directory
1960
+ - All output files use English
1961
+
1962
+ ## Execution Flow
1963
+
1964
+ ### Step 1: Read context
1965
+ - Read the change directory and global specs/
1966
+
1967
+ ### Step 2: Merge delta-specs
1968
+ - Merge changes/<name>/specs/ into global specs/
1969
+ - New specs append, modified specs update, removed specs archive
1970
+
1971
+ ### Step 3: Code cognition backfill
1972
+ - Update context.md with learned patterns from this change
1973
+
1974
+ ### Step 4: Move to archive
1975
+ - Move change to specwf/archive/<date>-<name>/
1976
+ - Update state.md: mark change as archived
1977
+
1978
+ ## Guardrails
1979
+ - Delta-spec merge must resolve conflicts, not overwrite
1980
+ - Archived changes are never deleted \u2014 they form project decision history`;
1981
+ var RESEARCHER_PROMPT = `## Role
1982
+
1983
+ You are a **Technical Researcher** for specwf.
1984
+
1985
+ Your core responsibility is to investigate technical directions, compare alternatives, and produce structured research outputs.
1986
+
1987
+ ## Core Constraints
1988
+
1989
+ - All artifacts written to the specwf/ directory
1990
+ - Read-only analysis \u2014 never modify source code
1991
+ - All output files use English
1992
+
1993
+ ## Execution Flow
1994
+
1995
+ ### Step 1: Read context
1996
+ - Read requirements.md for research scope
1997
+ - Read project.yml for technical constraints
1998
+
1999
+ ### Step 2: Research
2000
+ - Compare at least 2 candidate solutions per direction
2001
+ - Assess feasibility, risk, and trade-offs
2002
+ - Produce a recommended approach with rationale
2003
+
2004
+ ### Step 3: Output
2005
+ - stack.md \u2014 tech stack recommendations
2006
+ - architecture.md \u2014 architecture approach
2007
+ - pitfalls.md \u2014 known risks and mitigations
2008
+
2009
+ ## Guardrails
2010
+ - Never recommend the first option found without comparison
2011
+ - Mark speculative findings with confidence levels`;
2012
+ var PHASE_RESEARCHER_PROMPT = `## Role
2013
+
2014
+ You are a **Phase Researcher** for specwf.
2015
+
2016
+ Your core responsibility is to investigate implementation paths for a specific phase, building on context.md decisions and parent project research.
2017
+
2018
+ ## Core Constraints
2019
+
2020
+ - All artifacts written to the specwf/ directory
2021
+ - All output files use English
2022
+
2023
+ ## Execution Flow
2024
+
2025
+ ### Step 1: Read context
2026
+ - Read context.md for locked decisions and discretion areas
2027
+ - Read related specs/ for existing behavioral contracts
2028
+
2029
+ ### Step 2: Research
2030
+ - Investigate concrete implementation approaches
2031
+ - Identify reusable patterns from existing codebase
2032
+ - Flag known pitfalls and edge cases
2033
+
2034
+ ### Step 3: Output research.md
2035
+ - Recommended paths with rationale
2036
+ - Known pitfalls and TDD implications`;
2037
+ var CODEBASE_MAPPER_PROMPT = `## Role
2038
+
2039
+ You are a **Codebase Mapper** for specwf.
2040
+
2041
+ Your core responsibility is to analyze existing (brownfield) codebases and produce structured technical reports.
2042
+
2043
+ ## Core Constraints
2044
+
2045
+ - Read-only analysis \u2014 never modify source code
2046
+ - All output files use English
2047
+
2048
+ ## Execution Flow
2049
+
2050
+ ### Step 1: Scan codebase
2051
+ - Analyze directory structure, package.json, config files
2052
+ - Identify tech stack, frameworks, and dependencies
2053
+
2054
+ ### Step 2: Analyze architecture
2055
+ - Map module structure and dependencies
2056
+ - Identify architectural patterns in use
2057
+
2058
+ ### Step 3: Extract conventions
2059
+ - Naming patterns, code style, directory conventions
2060
+
2061
+ ### Step 4: Identify pitfalls
2062
+ - Anti-patterns, technical debt, risky areas
2063
+
2064
+ ### Step 5: Output
2065
+ - codebase/stack.md, codebase/architecture.md
2066
+ - conventions/codebase-conventions.md
2067
+ - codebase/pitfalls.md`;
2068
+ var SPEC_BOOTSTRAPPER_PROMPT = `## Role
2069
+
2070
+ You are a **Spec Bootstrapper** for specwf.
2071
+
2072
+ Your core responsibility is to extract behavioral contracts from existing code \u2014 code signatures, comments, and tests \u2014 and produce initial spec files.
2073
+
2074
+ ## Core Constraints
2075
+
2076
+ - Read-only analysis \u2014 never modify source code
2077
+ - All output files use English
2078
+
2079
+ ## Execution Flow
2080
+
2081
+ ### Step 1: Scan codebase
2082
+ - Scan src/ to identify core modules
2083
+ - Read function signatures, JSDoc comments, and existing tests
2084
+
2085
+ ### Step 2: Extract behavioral contracts
2086
+ - Infer SHALL/MUST constraints from tests and signatures
2087
+ - Annotate with confidence levels (HIGH/MEDIUM/LOW)
2088
+
2089
+ ### Step 3: Output specs/<domain>/spec.md
2090
+ - Mark all entries as BOOTSTRAPPED
2091
+ - Low-confidence entries flagged for human review`;
2092
+ var AGENT_PROMPTS = {
2093
+ planner: PLANNER_PROMPT,
2094
+ executor: EXECUTOR_PROMPT,
2095
+ reviewer: REVIEWER_PROMPT,
2096
+ verifier: VERIFIER_PROMPT,
2097
+ archiver: ARCHIVER_PROMPT,
2098
+ researcher: RESEARCHER_PROMPT,
2099
+ "phase-researcher": PHASE_RESEARCHER_PROMPT,
2100
+ "codebase-mapper": CODEBASE_MAPPER_PROMPT,
2101
+ "spec-bootstrapper": SPEC_BOOTSTRAPPER_PROMPT
2102
+ };
2103
+
2104
+ // src/integrations/omp/agents.ts
2105
+ var __dirname = dirname(fileURLToPath(import.meta.url));
2106
+ var AGENT_DEFS = [
2107
+ // specwf-researcher
2108
+ {
2109
+ role: "researcher",
2110
+ description: "Technical research \u2014 produce STACK/ARCH/PITFALLS/RESEARCH docs",
2111
+ tools: ["read", "grep", "glob", "lsp", "web_search", "write", "bash"],
2112
+ spawns: "*"
2113
+ },
2114
+ // specwf-planner
2115
+ {
2116
+ role: "planner",
2117
+ description: "Change design \u2014 produce proposal/design/tasks/delta-specs",
2118
+ tools: ["read", "grep", "glob", "lsp", "write", "bash"],
2119
+ spawns: "*"
2120
+ },
2121
+ // specwf-executor
2122
+ {
2123
+ role: "executor",
2124
+ description: "Code implementation \u2014 TDD RED\u2192GREEN\u2192REFACTOR",
2125
+ tools: ["read", "edit", "write", "bash", "grep", "glob", "lsp", "ast_grep", "ast_edit"],
2126
+ spawns: "*"
2127
+ },
2128
+ // specwf-reviewer
2129
+ {
2130
+ role: "reviewer",
2131
+ description: "Triple review \u2014 spec review + quality review + goal review",
2132
+ tools: ["read", "grep", "glob", "lsp", "ast_grep", "bash"],
2133
+ spawns: "*"
2134
+ },
2135
+ // specwf-verifier
2136
+ {
2137
+ role: "verifier",
2138
+ description: "Test verification \u2014 diagnose + route loopback",
2139
+ tools: ["read", "bash", "grep", "glob", "lsp", "edit", "write"],
2140
+ spawns: "*"
2141
+ },
2142
+ // specwf-archiver
2143
+ {
2144
+ role: "archiver",
2145
+ description: "Archive \u2014 delta-spec merge + code cognition backfill",
2146
+ tools: ["read", "write", "bash", "grep", "glob", "lsp"],
2147
+ spawns: "*"
2148
+ },
2149
+ // specwf-phase-researcher
2150
+ {
2151
+ role: "phase-researcher",
2152
+ description: "Phase research \u2014 produce RESEARCH.md for planner",
2153
+ tools: ["read", "grep", "glob", "lsp", "write", "bash"],
2154
+ spawns: "*"
2155
+ },
2156
+ // specwf-codebase-mapper + specwf-spec-bootstrapper (combined as aux agents)
2157
+ {
2158
+ role: "codebase-mapper",
2159
+ description: "Codebase mapping \u2014 analyze existing code, produce technical reports",
2160
+ tools: ["read", "grep", "glob", "lsp", "write", "bash"],
2161
+ spawns: "*"
2162
+ }
2163
+ ];
2164
+ function resolveAgentModel(role, config) {
2165
+ return resolveModels(config)[role] ?? "default";
2166
+ }
2167
+ function resolveThinkingLevel(role) {
2168
+ const highThinkingRoles = ["planner", "researcher", "reviewer"];
2169
+ return highThinkingRoles.includes(role) ? "high" : "medium";
2170
+ }
2171
+ function generateAgent(def, model) {
2172
+ const thinkingLevel = resolveThinkingLevel(def.role);
2173
+ const body = AGENT_PROMPTS[def.role] ?? `# ${def.description}
2174
+
2175
+ Agent system prompt for specwf-${def.role}.`;
2176
+ return `---
2177
+ name: specwf-${def.role}
2178
+ description: ${def.description}
2179
+ tools:
2180
+ ${def.tools.map((t) => ` - ${t}`).join("\n")}
2181
+ model: ${model}
2182
+ thinkingLevel: ${thinkingLevel}
2183
+ spawns: "${def.spawns}"
2184
+ blocking: false
2185
+ autoloadSkills: false
2186
+ readSummarize: true
2187
+ ---
2188
+
2189
+ ${body}
2190
+ `;
2191
+ }
2192
+ function generateAllAgents(config) {
2193
+ return AGENT_DEFS.map((def) => ({
2194
+ path: `.omp/agents/specwf-${def.role}.md`,
2195
+ content: generateAgent(def, resolveAgentModel(def.role, config))
2196
+ }));
2197
+ }
2198
+
2199
+ // src/integrations/omp/skills.ts
2200
+ function skillName(step) {
2201
+ return `specwf-${step}`;
2202
+ }
2203
+ function skillDescription(step) {
2204
+ const map = {
2205
+ init: "Initialize specwf project structure, generate platform files",
2206
+ grill: "Requirements exploration \u2014 detailed questioning until shared understanding is reached",
2207
+ research: "Project-level technical research \u2014 parallel multi-direction investigation",
2208
+ roadmap: "Roadmap definition \u2014 split project into Milestones \xD7 Phases",
2209
+ milestone: "Milestone management \u2014 switch/create milestones, set current phase",
2210
+ discuss: "Phase discussion \u2014 capture implementation decisions into context.md",
2211
+ "research-phase": "Phase research \u2014 implementation path investigation",
2212
+ split: "Change splitting \u2014 dependency graph + N changes",
2213
+ adhoc: "Create adhoc change \u2014 independent change unrelated to milestone/phase",
2214
+ plan: "Change design \u2014 technical design + task breakdown + delta-specs",
2215
+ apply: "Code implementation \u2014 TDD RED\u2192GREEN\u2192REFACTOR",
2216
+ review: "Triple review \u2014 spec review, quality review, goal review in parallel",
2217
+ verify: "Test verification \u2014 diagnose root cause + route loopback",
2218
+ archive: "Archive \u2014 delta-spec merge + code cognition backfill",
2219
+ ship: "Ship \u2014 create PR + update state / release tag",
2220
+ continue: "Auto-advance \u2014 read STATE and route to next step"
2221
+ };
2222
+ return map[step] ?? "";
2223
+ }
2224
+ var STEPS = ["init", "grill", "research", "roadmap", "milestone", "discuss", "research-phase", "split", "adhoc", "plan", "apply", "review", "verify", "archive", "ship", "continue"];
2225
+ var SKILL_DEFS = STEPS.map((step) => ({
2226
+ step,
2227
+ name: skillName(step),
2228
+ description: skillDescription(step)
2229
+ }));
2230
+ function generateSkill(def) {
2231
+ const entry = WORKFLOW_REGISTRY[def.step];
2232
+ const body = entry ? entry.skill().instructions : `# ${def.description}
2233
+
2234
+ Workflow guide for the \`${def.step}\` step.`;
2235
+ return `---
2236
+ name: ${def.name}
2237
+ description: ${def.description}
2238
+ hide: false
2239
+ ---
2240
+
2241
+ ${body}
2242
+ `;
2243
+ }
2244
+ function generateAllSkills(_config) {
2245
+ return SKILL_DEFS.map((def) => ({
2246
+ path: `.omp/skills/specwf-${def.step}/SKILL.md`,
2247
+ content: generateSkill(def)
2248
+ }));
2249
+ }
2250
+
2251
+ // src/generators/index.ts
2252
+ function generateAll(config) {
2253
+ return [
2254
+ ...generateAllCommands(config),
2255
+ ...generateAllAgents(config),
2256
+ ...generateAllSkills(config)
2257
+ ];
2258
+ }
2259
+
2260
+ // src/commands/_utils.ts
2261
+ import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
2262
+ function writeGeneratedFiles(files) {
2263
+ for (const file of files) {
2264
+ const dir = file.path.split("/").slice(0, -1).join("/");
2265
+ if (dir) mkdirSync3(dir, { recursive: true });
2266
+ writeFileSync5(file.path, file.content, "utf-8");
2267
+ console.log(` \u2713 ${file.path}`);
2268
+ }
2269
+ }
2270
+
2271
+ // src/commands/specwf-init.ts
2272
+ function register(program2) {
2273
+ program2.command("init").description("\u521D\u59CB\u5316 specwf \u9879\u76EE\u7ED3\u6784").option("--dir <path>", "\u76EE\u6807\u76EE\u5F55", ".").option("--profile <profile>", "\u5DE5\u4F5C\u6D41\u4E25\u683C\u5EA6 (lite|standard|strict)", "standard").option("--brownfield", "\u5B58\u91CF\u9879\u76EE\u6A21\u5F0F\uFF08codebase mapping + spec bootstrap\uFF09").option("--yes", "\u8DF3\u8FC7\u786E\u8BA4\u4F7F\u7528\u9ED8\u8BA4\u503C").action(initHandler);
2274
+ }
2275
+ async function initHandler(options) {
2276
+ const baseDir = options.dir.startsWith("/") ? options.dir : join6(process.cwd(), options.dir);
2277
+ const specwfDir = join6(baseDir, "specwf");
2278
+ if (isInitialized(specwfDir)) {
2279
+ console.error("specwf \u5DF2\u521D\u59CB\u5316\u3002\u8FD0\u884C `specwf update` \u66F4\u65B0\u5E73\u53F0\u6587\u4EF6\u3002");
2280
+ process.exit(1);
2281
+ }
2282
+ const wizard = await runInitWizard({ profile: options.profile, yes: options.yes });
2283
+ const profile = wizard.profile;
2284
+ const platform = wizard.platform;
2285
+ const isBrownfield = options.brownfield || wizard.brownfield;
2286
+ createSpecwfStructure(specwfDir);
2287
+ console.log("\u2713 \u521B\u5EFA specwf/ \u76EE\u5F55\u7ED3\u6784");
2288
+ saveConfig(specwfDir, {
2289
+ version: 1,
2290
+ platform,
2291
+ profile,
2292
+ context: wizard.context,
2293
+ workflow: {},
2294
+ review: {},
2295
+ change: {},
2296
+ git: { branching: "none", create_tag: true },
2297
+ conventions: { inject: true },
2298
+ models: {}
2299
+ });
2300
+ console.log("\u2713 \u521B\u5EFA project.yml (profile: " + profile + ")");
2301
+ saveState(specwfDir, {
2302
+ project: {
2303
+ name: baseDir.split("/").pop() || "project",
2304
+ status: "initialized",
2305
+ current_milestone: null,
2306
+ current_phase: null
2307
+ },
2308
+ active_context: {
2309
+ type: "project",
2310
+ ref: null,
2311
+ step: "init"
2312
+ },
2313
+ changes: [],
2314
+ adhoc: []
2315
+ });
2316
+ console.log("\u2713 \u521B\u5EFA state.md");
2317
+ if (isBrownfield) {
2318
+ const info = detectProjectInfo(process.cwd());
2319
+ const domains = await runBrownfieldInit(process.cwd(), specwfDir, info);
2320
+ console.log("\u2713 \u5DF2\u626B\u63CF\u9879\u76EE\u7ED3\u6784\u3002\u8BF7\u6D3E\u53D1 specwf-codebase-mapper \u548C specwf-spec-bootstrapper \u5B50\u4EE3\u7406\u5B8C\u6210\u5B8C\u6574\u5206\u6790\u3002");
2321
+ }
2322
+ console.log("specwf \u521D\u59CB\u5316\u5B8C\u6210\u3002");
2323
+ try {
2324
+ const files = generateAll({ version: 1, platform, profile, context: wizard.context, workflow: {}, review: {}, change: {}, git: { branching: "none", create_tag: true }, conventions: { inject: true }, models: {} });
2325
+ writeGeneratedFiles(files);
2326
+ console.log(`\u2713 \u5E73\u53F0\u6587\u4EF6\u5DF2\u751F\u6210 (${files.length} \u4E2A)`);
2327
+ } catch {
2328
+ console.log("\u26A0 \u5E73\u53F0\u6587\u4EF6\u751F\u6210\u5931\u8D25\uFF0C\u53EF\u7A0D\u540E\u8FD0\u884C `specwf update` \u91CD\u8BD5");
2329
+ }
2330
+ }
2331
+
2332
+ // src/commands/specwf-update.ts
2333
+ import { join as join7 } from "path";
2334
+ import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync6 } from "fs";
2335
+ var HOOK_TEMPLATE = `/**
2336
+ * specwf OMP Hook \u2014 automated spec injection and workflow guidance.
2337
+ * Generated by specwf update. Do not edit manually.
2338
+ */
2339
+ import { existsSync } from "node:fs";
2340
+ import { join } from "node:path";
2341
+ import { execSync } from "node:child_process";
2342
+
2343
+ export default function specwfHook(api: any): void {
2344
+ api.on("session_start", async (_event: any, ctx: any) => {
2345
+ const cwd = ctx.cwd ?? process.cwd();
2346
+ if (!existsSync(join(cwd, "specwf", "state.md"))) return;
2347
+ try {
2348
+ const output = execSync("specwf context current", { cwd, encoding: "utf-8", timeout: 5000 });
2349
+ const data = JSON.parse(output);
2350
+ const lines: string[] = [];
2351
+ lines.push(\`[specwf] Project: \${data.project} | Status: \${data.status}\`);
2352
+ if (data.specs?.length) {
2353
+ lines.push("\\n\u2500\u2500\u2500 Specs \u2500\u2500\u2500");
2354
+ for (const spec of data.specs) {
2355
+ if (spec.content) lines.push(\`\\n### \${spec.path}\\n\${spec.content.slice(0, 4096)}\`);
2356
+ }
2357
+ }
2358
+ if (data.conventions?.length) {
2359
+ lines.push("\\n\u2500\u2500\u2500 Conventions \u2500\u2500\u2500");
2360
+ for (const conv of data.conventions) {
2361
+ if (conv.content) lines.push(\`\\n### \${conv.path}\\n\${conv.content.slice(0, 2048)}\`);
2362
+ }
2363
+ }
2364
+ api.sendMessage({ role: "custom", customType: "specwf-session", content: [{ type: "text", text: lines.join("\\n") }], timestamp: Date.now() });
2365
+ } catch { /* specwf CLI not available */ }
2366
+ });
2367
+
2368
+ // \u2500\u2500 Quality gate: warn on test/lint/typecheck failures \u2500\u2500
2369
+ api.on("tool_result", async (event: any) => {
2370
+ if (event.isError || event.toolName !== "bash") return;
2371
+ const cmd = String(event.input?.command ?? "");
2372
+ const isCheck = /\b(vitest|jest|tsc\b|typecheck|eslint|prettier.*--check)\b/.test(cmd);
2373
+ if (!isCheck) return;
2374
+ const text = String(event.content?.[0]?.text ?? "");
2375
+ const m = text.match(/exited with code (d+)/i);
2376
+ if (m && parseInt(m[1]) !== 0) {
2377
+ api.sendMessage({ role: "custom", customType: "specwf-quality-gate", content: [{ type: "text", text: \`[specwf] Quality gate FAILED: \${cmd.trim().split("\\n")[0]}\\nFix failures before running specwf continue.\` }], timestamp: Date.now() }, { deliverAs: "followUp" });
2378
+ }
2379
+ });
2380
+
2381
+ api.on("before_agent_start", async (_event: any, ctx: any) => {
2382
+ const cwd = ctx.cwd ?? process.cwd();
2383
+ if (!existsSync(join(cwd, "specwf", "state.md"))) return;
2384
+ try {
2385
+ const output = execSync("specwf state", { cwd, encoding: "utf-8", timeout: 3000 });
2386
+ const state = JSON.parse(output);
2387
+ const pending = state.pending || [];
2388
+ const hint = pending.length > 0
2389
+ ? \`[specwf] Pending: \${pending.map((p: any) => \`\${p.name}[\${p.status}]\`).join(", ")}. Run specwf continue.\`
2390
+ : \`[specwf] Status: \${state.status}. Run specwf continue.\`;
2391
+ ctx.ui?.setStatus?.("specwf", hint);
2392
+ } catch { /* skip */ }
2393
+ });
2394
+ }
2395
+ `;
2396
+ function register2(program2) {
2397
+ program2.command("update").description("Regenerate platform files (commands + agents + skills + hooks)").option("--dir <path>", "specwf directory", "specwf").action(updateHandler);
2398
+ }
2399
+ function updateHandler(options) {
2400
+ const specwfDir = join7(process.cwd(), options.dir);
2401
+ const config = loadConfig(specwfDir);
2402
+ const files = generateAll(config);
2403
+ console.log("Regenerating platform files...");
2404
+ writeGeneratedFiles(files);
2405
+ const hookDir = join7(process.cwd(), ".omp", "hooks", "pre");
2406
+ mkdirSync4(hookDir, { recursive: true });
2407
+ writeFileSync6(join7(hookDir, "specwf.ts"), HOOK_TEMPLATE, "utf-8");
2408
+ console.log(" \u2713 .omp/hooks/pre/specwf.ts");
2409
+ console.log(`\u2713 Update complete (${files.length + 1} files)`);
2410
+ }
2411
+
2412
+ // src/commands/specwf-config.ts
2413
+ import { join as join8 } from "path";
2414
+ function register3(program2) {
2415
+ const cmd = program2.command("config").description("\u67E5\u770B/\u4FEE\u6539\u914D\u7F6E\u9879\u76EE");
2416
+ cmd.command("list").description("\u67E5\u770B\u5F53\u524D\u914D\u7F6E").action(configList);
2417
+ cmd.command("set <key> <value>").description("\u4FEE\u6539\u914D\u7F6E\u9879").action(configSet);
2418
+ cmd.action(configList);
2419
+ }
2420
+ function configList(options, cmd) {
2421
+ if (cmd?.parent?.args?.length > 1) return;
2422
+ const specwfDir = findSpecwfDir();
2423
+ const config = loadConfig(specwfDir);
2424
+ console.log(JSON.stringify(config, null, 2));
2425
+ }
2426
+ function configSet(key, value) {
2427
+ const specwfDir = findSpecwfDir();
2428
+ updateConfig(specwfDir, (config) => {
2429
+ const parts = key.split(".");
2430
+ let target = config;
2431
+ for (let i = 0; i < parts.length - 1; i++) {
2432
+ if (!target[parts[i]]) target[parts[i]] = {};
2433
+ target = target[parts[i]];
2434
+ }
2435
+ const lastKey = parts[parts.length - 1];
2436
+ const typedValue = parseTypedValue(value);
2437
+ target[lastKey] = typedValue;
2438
+ });
2439
+ console.log(`\u2713 ${key} = ${value}`);
2440
+ }
2441
+ function parseTypedValue(value) {
2442
+ if (value === "true") return true;
2443
+ if (value === "false") return false;
2444
+ if (/^\d+$/.test(value)) return parseInt(value, 10);
2445
+ if (value === "null") return null;
2446
+ return value;
2447
+ }
2448
+ function findSpecwfDir() {
2449
+ return join8(process.cwd(), "specwf");
2450
+ }
2451
+
2452
+ // src/commands/specwf-state.ts
2453
+ import { join as join9 } from "path";
2454
+ import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
2455
+ function register4(program2) {
2456
+ const cmd = program2.command("state").description("View/modify current state");
2457
+ cmd.command("show").description("Show current state as JSON").action(showState);
2458
+ cmd.command("set-milestone <id>").description("Switch to specified milestone").action(setMilestone);
2459
+ cmd.command("set-phase <id>").description("Switch to specified phase").action(setPhase);
2460
+ cmd.command("set-step <step>").description("Set current step").action(setStep);
2461
+ cmd.action(showState);
2462
+ }
2463
+ function findSpecwfDir2() {
2464
+ return join9(process.cwd(), "specwf");
2465
+ }
2466
+ function showState() {
2467
+ const specwfDir = findSpecwfDir2();
2468
+ const state = loadState(specwfDir);
2469
+ const { project, active_context } = state;
2470
+ const pendingChanges = state.changes.filter((c) => c.status !== "archived");
2471
+ const pendingAdhoc = state.adhoc.filter((c) => c.status !== "archived");
2472
+ const withProgress = (items, type) => items.map((c) => {
2473
+ const tasksPath = join9(specwfDir, "changes", c.name, "tasks.md");
2474
+ let tasks = null;
2475
+ try {
2476
+ const content = readFileSync6(tasksPath, "utf-8");
2477
+ const total = (content.match(/^\s*-\s*\[[ x]\]/gm) || []).length;
2478
+ const completed = (content.match(/^\s*-\s*\[x\]/gm) || []).length;
2479
+ if (total > 0) tasks = { total, completed };
2480
+ } catch {
2481
+ }
2482
+ return { type, name: c.name, status: c.status, tasks };
2483
+ });
2484
+ let roadmap = null;
2485
+ try {
2486
+ const roadmapPath = join9(specwfDir, "roadmap.md");
2487
+ if (existsSync6(roadmapPath)) {
2488
+ const content = readFileSync6(roadmapPath, "utf-8");
2489
+ const msMatches = content.match(/^##\s+(M\d+[^\n]*)/gm);
2490
+ if (msMatches) {
2491
+ roadmap = msMatches.map((m) => {
2492
+ const name = m.replace(/^##\s+/, "").trim();
2493
+ const id = name.split(/\s*[-–—]\s*/)[0].trim();
2494
+ const isActive = project.current_milestone === id;
2495
+ return { name, id, active: isActive };
2496
+ });
2497
+ }
2498
+ }
2499
+ } catch {
2500
+ }
2501
+ const output = {
2502
+ project: project.name,
2503
+ status: project.status,
2504
+ milestone: project.current_milestone,
2505
+ phase: project.current_phase,
2506
+ context: {
2507
+ type: active_context.type,
2508
+ step: active_context.step,
2509
+ ref: active_context.ref || null
2510
+ },
2511
+ pending: withProgress(pendingChanges, "change").concat(withProgress(pendingAdhoc, "adhoc"))
2512
+ };
2513
+ if (roadmap) output.roadmap = roadmap;
2514
+ console.log(JSON.stringify(output, null, 2));
2515
+ }
2516
+ function setMilestone(id) {
2517
+ const specwfDir = findSpecwfDir2();
2518
+ updateState(specwfDir, (state) => {
2519
+ const current = state.project.current_milestone;
2520
+ if (current && state.project.status !== "milestone-shipped") {
2521
+ archiveMilestoneDir(specwfDir, current);
2522
+ }
2523
+ state.project.current_milestone = id;
2524
+ state.project.current_phase = null;
2525
+ state.active_context = { type: "milestone", ref: `milestones/${id}`, step: "active" };
2526
+ });
2527
+ console.log(JSON.stringify({ ok: true, milestone: id }));
2528
+ }
2529
+ function setPhase(id) {
2530
+ const specwfDir = findSpecwfDir2();
2531
+ updateState(specwfDir, (state) => {
2532
+ state.project.current_phase = id;
2533
+ state.active_context = { type: "phase", ref: `milestones/${state.project.current_milestone}/phases/${id}`, step: "discuss" };
2534
+ });
2535
+ console.log(JSON.stringify({ ok: true, phase: id }));
2536
+ }
2537
+ function setStep(step) {
2538
+ const specwfDir = findSpecwfDir2();
2539
+ updateState(specwfDir, (state) => {
2540
+ state.active_context.step = step;
2541
+ });
2542
+ console.log(JSON.stringify({ ok: true, step }));
2543
+ }
2544
+
2545
+ // src/commands/specwf-context.ts
2546
+ import { join as join11 } from "path";
2547
+
2548
+ // src/core/spec-injector.ts
2549
+ import { join as join10 } from "path";
2550
+ import { readdirSync as readdirSync3, existsSync as existsSync7, readFileSync as readFileSync7, statSync as statSync2 } from "fs";
2551
+ var PROJECT_STEPS = ["init", "grill", "research", "roadmap"];
2552
+ var PHASE_STEPS = ["discuss", "research-phase", "split"];
2553
+ var CHANGE_STEPS = ["plan", "apply", "review", "verify", "archive"];
2554
+ function isProjectStep(step) {
2555
+ return PROJECT_STEPS.includes(step);
2556
+ }
2557
+ function isPhaseStep(step) {
2558
+ return PHASE_STEPS.includes(step);
2559
+ }
2560
+ function isChangeStep(step) {
2561
+ return CHANGE_STEPS.includes(step);
2562
+ }
2563
+ function generateContext(specwfDir, step) {
2564
+ const state = loadState(specwfDir);
2565
+ const ctx = state.active_context;
2566
+ const result = {
2567
+ step,
2568
+ scope: { type: ctx.type, ref: ctx.ref },
2569
+ specs: [],
2570
+ conventions: [],
2571
+ changeArtifacts: [],
2572
+ requirements: []
2573
+ };
2574
+ result.conventions = getAllConventions(specwfDir).map(withContent(specwfDir));
2575
+ if (existsSync7(join10(specwfDir, "requirements.md"))) {
2576
+ const reqPath = join10(specwfDir, "requirements.md");
2577
+ result.requirements.push({
2578
+ path: "requirements.md",
2579
+ description: "Requirements specification",
2580
+ content: readContent(reqPath)
2581
+ });
2582
+ }
2583
+ if (isProjectStep(step)) {
2584
+ result.specs = getAllSpecs(specwfDir).map(withContent(specwfDir));
2585
+ } else if (isPhaseStep(step)) {
2586
+ result.specs = getRelatedSpecs(specwfDir, state).map(withContent(specwfDir));
2587
+ } else if (isChangeStep(step)) {
2588
+ result.specs = getRelatedSpecs(specwfDir, state).map(withContent(specwfDir));
2589
+ result.changeArtifacts = getChangeArtifacts(specwfDir, state).map(withContent(specwfDir));
2590
+ }
2591
+ return result;
2592
+ }
2593
+ function readContent(absPath) {
2594
+ try {
2595
+ const content = readFileSync7(absPath, "utf-8");
2596
+ return content.length > 8192 ? content.slice(0, 8192) + "\n... [truncated]" : content;
2597
+ } catch {
2598
+ return void 0;
2599
+ }
2600
+ }
2601
+ function withContent(specwfDir) {
2602
+ return (ref) => ({
2603
+ ...ref,
2604
+ content: readContent(join10(specwfDir, ref.path))
2605
+ });
2606
+ }
2607
+ function getAllSpecs(specwfDir) {
2608
+ const specsDir = join10(specwfDir, "specs");
2609
+ return listSpecFiles(specsDir, "specs");
2610
+ }
2611
+ function getRelatedSpecs(specwfDir, state) {
2612
+ const allSpecs = getAllSpecs(specwfDir);
2613
+ if (allSpecs.length === 0) return [];
2614
+ const ref = state.active_context.ref ?? "";
2615
+ const changeName = ref.split("/").pop() ?? "";
2616
+ const related = allSpecs.filter((spec) => {
2617
+ const domain = spec.path.split("/")[1] ?? "";
2618
+ return changeName.toLowerCase().includes(domain.toLowerCase());
2619
+ });
2620
+ return related.length > 0 ? related : allSpecs;
2621
+ }
2622
+ function getAllConventions(specwfDir) {
2623
+ const convDir = join10(specwfDir, "conventions");
2624
+ if (!existsSync7(convDir)) return [];
2625
+ return readdirSync3(convDir).filter((f) => f.endsWith(".md")).map((f) => ({ path: `conventions/${f}`, description: "\u9879\u76EE\u7EA6\u5B9A" }));
2626
+ }
2627
+ function getChangeArtifacts(specwfDir, state) {
2628
+ const ref = state.active_context.ref;
2629
+ if (!ref) return [];
2630
+ const changeDir = join10(specwfDir, ref);
2631
+ if (!existsSync7(changeDir)) return [];
2632
+ const artifacts = [];
2633
+ for (const file of ["proposal.md", "design.md", "tasks.md", ".specwf.yaml"]) {
2634
+ const fullPath = join10(changeDir, file);
2635
+ if (existsSync7(fullPath)) {
2636
+ artifacts.push({ path: `${ref}/${file}`, description: "change \u4EA7\u7269" });
2637
+ }
2638
+ }
2639
+ const specsDir = join10(changeDir, "specs");
2640
+ if (existsSync7(specsDir)) {
2641
+ const deltaSpecs = listSpecFiles(specsDir, `${ref}/specs`);
2642
+ artifacts.push(...deltaSpecs);
2643
+ }
2644
+ return artifacts;
2645
+ }
2646
+ function listSpecFiles(dir, prefix) {
2647
+ if (!existsSync7(dir)) return [];
2648
+ const results = [];
2649
+ for (const entry of readdirSync3(dir)) {
2650
+ const fullPath = join10(dir, entry);
2651
+ const stat = statSync2(fullPath);
2652
+ if (stat.isDirectory()) {
2653
+ results.push(...listSpecFiles(fullPath, `${prefix}/${entry}`));
2654
+ } else if (entry.endsWith(".md")) {
2655
+ results.push({ path: `${prefix}/${entry}`, description: "\u884C\u4E3A\u5951\u7EA6" });
2656
+ }
2657
+ }
2658
+ return results;
2659
+ }
2660
+
2661
+ // src/commands/specwf-context.ts
2662
+ function register5(program2) {
2663
+ program2.command("context <step>").description("Output state + file manifest as JSON").action(contextHandler);
2664
+ }
2665
+ function contextHandler(step) {
2666
+ const specwfDir = join11(process.cwd(), "specwf");
2667
+ const state = loadState(specwfDir);
2668
+ const { project, active_context } = state;
2669
+ const pendingChanges = state.changes.filter((c) => c.status !== "archived");
2670
+ const pendingAdhoc = state.adhoc.filter((c) => c.status !== "archived");
2671
+ const result = generateContext(specwfDir, step);
2672
+ const output = {
2673
+ project: project.name,
2674
+ status: project.status,
2675
+ milestone: project.current_milestone,
2676
+ phase: project.current_phase,
2677
+ context: {
2678
+ type: active_context.type,
2679
+ step: active_context.step,
2680
+ ref: active_context.ref || null
2681
+ },
2682
+ pending: [
2683
+ ...pendingChanges.map((c) => ({ type: "change", name: c.name, status: c.status })),
2684
+ ...pendingAdhoc.map((c) => ({ type: "adhoc", name: c.name, status: c.status }))
2685
+ ],
2686
+ specs: result.specs,
2687
+ conventions: result.conventions,
2688
+ artifacts: result.changeArtifacts,
2689
+ requirements: result.requirements
2690
+ };
2691
+ console.log(JSON.stringify(output));
2692
+ }
2693
+
2694
+ // src/commands/specwf-continue.ts
2695
+ import { join as join13 } from "path";
2696
+
2697
+ // src/types/state.ts
2698
+ var STATE_TRANSITIONS = [
2699
+ // 项目层路径
2700
+ { from: "initialized", command: "grill", to: "requirements-defined", slashCommand: "/specwf:grill" },
2701
+ { from: "requirements-defined", command: "research", to: "researching", slashCommand: "/specwf:research", subagent: true },
2702
+ { from: "researching", command: "research-done", to: "researched", slashCommand: "" },
2703
+ { from: "researched", command: "roadmap", to: "roadmap-defined", slashCommand: "/specwf:roadmap" },
2704
+ { from: "roadmap-defined", command: "discuss", to: "phase-discuss", slashCommand: "/specwf:discuss" },
2705
+ // Phase 路径
2706
+ { from: "phase-discuss", command: "research-phase", to: "phase-research", slashCommand: "/specwf:research-phase", subagent: true },
2707
+ { from: "phase-research", command: "split", to: "phase-split", slashCommand: "/specwf:split" },
2708
+ { from: "phase-split", command: "plan", to: "change-planning", slashCommand: "/specwf:plan", subagent: true },
2709
+ { from: "change-planning", command: "apply", to: "change-applying", slashCommand: "/specwf:apply", subagent: true },
2710
+ { from: "change-applying", command: "review", to: "change-reviewing", slashCommand: "/specwf:review", subagent: true },
2711
+ { from: "change-reviewing", command: "verify", to: "change-verifying", slashCommand: "/specwf:verify", subagent: true },
2712
+ { from: "change-verifying", command: "archive", to: "change-archiving", slashCommand: "/specwf:archive", subagent: true },
2713
+ { from: "change-archiving", command: "archive-done", to: "change-archived", slashCommand: "" },
2714
+ // 回环
2715
+ { from: "change-verifying", command: "replan", to: "change-planning", slashCommand: "/specwf:plan", subagent: true },
2716
+ { from: "change-verifying", command: "reapply", to: "change-applying", slashCommand: "/specwf:apply", subagent: true },
2717
+ { from: "change-reviewing", command: "fix", to: "change-applying", slashCommand: "/specwf:apply", subagent: true },
2718
+ // Milestone 层(新里程碑 = 项目流程 - init)
2719
+ { from: "milestone-active", command: "grill", to: "requirements-defined", slashCommand: "/specwf:grill" },
2720
+ // Ship
2721
+ { from: "change-archived", command: "ship-phase", to: "phase-shipped", slashCommand: "/specwf:ship" },
2722
+ { from: "phase-shipped", command: "next-phase", to: "phase-discuss", slashCommand: "/specwf:discuss" },
2723
+ { from: "phase-shipped", command: "ship-milestone", to: "milestone-shipped", slashCommand: "/specwf:ship" },
2724
+ // 临时 change
2725
+ { from: "adhoc-proposal", command: "plan", to: "change-planning", slashCommand: "/specwf:plan", subagent: true },
2726
+ { from: "change-archived", command: "adhoc-done", to: "adhoc-archived", slashCommand: "" },
2727
+ { from: "adhoc-archived", command: "new-change", to: "adhoc-proposal", slashCommand: "" }
2728
+ ];
2729
+
2730
+ // src/core/state-machine.ts
2731
+ function getTransition(from, command) {
2732
+ return STATE_TRANSITIONS.find(
2733
+ (t) => t.from === from && t.command === command
2734
+ ) ?? null;
2735
+ }
2736
+ function getNextSteps(from) {
2737
+ return STATE_TRANSITIONS.filter((t) => t.from === from);
2738
+ }
2739
+
2740
+ // src/core/continue.ts
2741
+ function determineNextStep(specwfDir) {
2742
+ return determineFromState(loadState(specwfDir));
2743
+ }
2744
+ function determineChangeNextStep(specwfDir, changeName) {
2745
+ const state = loadState(specwfDir);
2746
+ const change = state.changes.find((c) => c.name === changeName);
2747
+ if (change) {
2748
+ return determineFromChangeStatus(changeName, `change-${change.status}`, "change");
2749
+ }
2750
+ const adhoc = state.adhoc.find((c) => c.name === changeName);
2751
+ if (adhoc) {
2752
+ const prefix = adhoc.status === "proposal" ? "adhoc" : "change";
2753
+ return determineFromChangeStatus(
2754
+ changeName,
2755
+ `${prefix}-${adhoc.status}`,
2756
+ "adhoc"
2757
+ );
2758
+ }
2759
+ return {
2760
+ error: `change \u4E0D\u5B58\u5728: ${changeName}\u3002\u53EF\u7528: ${listAvailableChanges(state)}`
2761
+ };
2762
+ }
2763
+ var STEP_INFO = {
2764
+ grill: {
2765
+ command: "grill",
2766
+ description: "Requirements exploration \u2014 5W1H questioning, output requirements.md",
2767
+ artifacts: ["specwf/requirements.md"],
2768
+ fileRef: ""
2769
+ },
2770
+ research: {
2771
+ command: "research",
2772
+ description: "Parallel technical research \u2014 dispatch researcher sub-agents",
2773
+ artifacts: ["specwf/research/stack.md", "specwf/research/architecture.md", "specwf/research/pitfalls.md", "specwf/research/summary.md"],
2774
+ fileRef: ""
2775
+ },
2776
+ "research-done": {
2777
+ command: "research-done",
2778
+ description: "Mark research complete, proceed to roadmap",
2779
+ artifacts: [],
2780
+ fileRef: ""
2781
+ },
2782
+ roadmap: {
2783
+ command: "roadmap",
2784
+ description: "Split project into Milestones x Phases",
2785
+ artifacts: ["specwf/roadmap.md"],
2786
+ fileRef: ""
2787
+ },
2788
+ discuss: {
2789
+ command: "discuss",
2790
+ description: "Phase discussion \u2014 capture implementation decisions into context.md",
2791
+ artifacts: ["context.md"],
2792
+ fileRef: ""
2793
+ },
2794
+ "research-phase": {
2795
+ command: "research-phase",
2796
+ description: "Phase-level technical research \u2014 dispatch phase-researcher sub-agent",
2797
+ artifacts: ["research.md"],
2798
+ fileRef: ""
2799
+ },
2800
+ split: {
2801
+ command: "split",
2802
+ description: "Split phase into changes with dependency graph",
2803
+ artifacts: ["specwf/changes/<name>/"],
2804
+ fileRef: ""
2805
+ },
2806
+ plan: {
2807
+ command: "plan",
2808
+ description: "Change design \u2014 dispatch planner sub-agent for design + tasks + delta-specs",
2809
+ artifacts: ["design.md", "tasks.md", "specs/<domain>/spec.md"],
2810
+ fileRef: ""
2811
+ },
2812
+ apply: {
2813
+ command: "apply",
2814
+ description: "Code implementation \u2014 dispatch executor sub-agent for TDD",
2815
+ artifacts: ["code changes", "tests", "change-summary.md"],
2816
+ fileRef: ""
2817
+ },
2818
+ review: {
2819
+ command: "review",
2820
+ description: "Triple review \u2014 dispatch three reviewer sub-agents in parallel",
2821
+ artifacts: ["spec-review.md", "quality-review.md", "goal-review.md"],
2822
+ fileRef: ""
2823
+ },
2824
+ verify: {
2825
+ command: "verify",
2826
+ description: "Test verification \u2014 dispatch verifier sub-agent",
2827
+ artifacts: ["verification.md"],
2828
+ fileRef: ""
2829
+ },
2830
+ archive: {
2831
+ command: "archive",
2832
+ description: "Archive \u2014 dispatch archiver sub-agent for delta-spec merge + backfill",
2833
+ artifacts: ["archive/<change-id>/"],
2834
+ fileRef: ""
2835
+ },
2836
+ "ship-phase": {
2837
+ command: "ship-phase",
2838
+ description: "Phase ship \u2014 create PR, update state.md",
2839
+ artifacts: ["GitHub PR"],
2840
+ fileRef: ""
2841
+ },
2842
+ "ship-milestone": {
2843
+ command: "ship-milestone",
2844
+ description: "Milestone ship \u2014 release tag, update version",
2845
+ artifacts: ["git tag", "RELEASE.md"],
2846
+ fileRef: ""
2847
+ }
2848
+ };
2849
+ var STEP_TO_WORKFLOW = {
2850
+ grill: "grill",
2851
+ research: "research",
2852
+ roadmap: "roadmap",
2853
+ discuss: "discuss",
2854
+ "research-phase": "research-phase",
2855
+ split: "split",
2856
+ plan: "plan",
2857
+ apply: "apply",
2858
+ review: "review",
2859
+ verify: "verify",
2860
+ archive: "archive",
2861
+ "ship-phase": "ship",
2862
+ "ship-milestone": "ship",
2863
+ init: "init",
2864
+ adhoc: "adhoc",
2865
+ continue: "continue",
2866
+ milestone: "milestone"
2867
+ };
2868
+ function getStepInfo(command) {
2869
+ const info = STEP_INFO[command];
2870
+ if (!info) return void 0;
2871
+ const wfStep = STEP_TO_WORKFLOW[command];
2872
+ if (wfStep && WORKFLOW_REGISTRY[wfStep]) {
2873
+ return {
2874
+ ...info,
2875
+ instructions: WORKFLOW_REGISTRY[wfStep].command().content
2876
+ };
2877
+ }
2878
+ return info;
2879
+ }
2880
+ function determineFromChangeStatus(name, statusKey, type) {
2881
+ const available = getNextSteps(statusKey);
2882
+ const availableSteps = available.map((t) => ({
2883
+ command: t.command,
2884
+ slashCommand: t.slashCommand,
2885
+ subagent: t.subagent ?? false
2886
+ }));
2887
+ const first = available[0];
2888
+ const stepInfo = first ? getStepInfo(first.command) : void 0;
2889
+ return {
2890
+ currentStep: statusKey,
2891
+ context: `${type === "adhoc" ? "Adhoc Change" : "Change"} (${name})`,
2892
+ nextCommand: first?.command ?? null,
2893
+ slashCommand: first?.slashCommand || null,
2894
+ needsSubagent: first?.subagent ?? false,
2895
+ availableSteps,
2896
+ hint: available.length === 0 ? "This change has no available next steps. Create a new change to continue." : null,
2897
+ nextStepInfo: stepInfo,
2898
+ instructions: stepInfo?.instructions
2899
+ };
2900
+ }
2901
+ function listAvailableChanges(state) {
2902
+ const names = [
2903
+ ...state.changes.map((c) => c.name),
2904
+ ...state.adhoc.map((c) => c.name)
2905
+ ];
2906
+ return names.join(", ") || "(\u65E0)";
2907
+ }
2908
+ function determineFromState(state) {
2909
+ const ctx = state.active_context;
2910
+ const currentStatus = resolveStatus(state);
2911
+ const available = getNextSteps(currentStatus);
2912
+ const availableSteps = available.map((t) => ({
2913
+ command: t.command,
2914
+ slashCommand: t.slashCommand,
2915
+ subagent: t.subagent ?? false
2916
+ }));
2917
+ const first = available[0];
2918
+ const hint = available.length === 0 ? generateHint(state) : null;
2919
+ const stepInfo = first ? getStepInfo(first.command) : void 0;
2920
+ return {
2921
+ currentStep: ctx.step,
2922
+ context: formatContext2(state),
2923
+ nextCommand: first?.command ?? null,
2924
+ slashCommand: first?.slashCommand || null,
2925
+ needsSubagent: first?.subagent ?? false,
2926
+ availableSteps,
2927
+ hint,
2928
+ nextStepInfo: stepInfo,
2929
+ instructions: stepInfo?.instructions
2930
+ };
2931
+ }
2932
+ function resolveStatus(state) {
2933
+ const ctx = state.active_context;
2934
+ switch (ctx.type) {
2935
+ case "project":
2936
+ return state.project.status;
2937
+ case "milestone":
2938
+ return state.project.status === "milestone-shipped" ? "milestone-shipped" : "milestone-active";
2939
+ case "phase":
2940
+ return `phase-${ctx.step}`;
2941
+ case "change":
2942
+ return `change-${ctx.step}`;
2943
+ case "adhoc":
2944
+ return `adhoc-${ctx.step}`;
2945
+ default:
2946
+ return state.project.status;
2947
+ }
2948
+ }
2949
+ function formatContext2(state) {
2950
+ const { type, ref, step } = state.active_context;
2951
+ switch (type) {
2952
+ case "project":
2953
+ return `\u9879\u76EE\u5C42 \u2014 ${step}`;
2954
+ case "milestone":
2955
+ return `Milestone ${state.project.current_milestone ?? "?"} \u2014 ${step}`;
2956
+ case "phase":
2957
+ return `Phase ${state.project.current_phase ?? "?"} \u2014 ${step}`;
2958
+ case "change":
2959
+ return `Change (${ref ?? "?"}) \u2014 ${step}`;
2960
+ case "adhoc":
2961
+ return `\u4E34\u65F6 Change (${ref ?? "?"}) \u2014 ${step}`;
2962
+ default:
2963
+ return step;
2964
+ }
2965
+ }
2966
+ function generateHint(state) {
2967
+ const status = state.project.status;
2968
+ if (status === "milestone-shipped") {
2969
+ const pendingAdhoc = state.adhoc.filter((c) => c.status !== "archived");
2970
+ const hintParts = ["\u5F53\u524D milestone \u5DF2\u5B8C\u6210\u3002\u521B\u5EFA\u65B0 milestone: specwf state set-milestone <id>"];
2971
+ if (pendingAdhoc.length > 0) {
2972
+ hintParts.push(
2973
+ `\u5F85\u5904\u7406\u7684\u4E34\u65F6 change: ${pendingAdhoc.map((c) => c.name).join(", ")}\u3002\u4F7F\u7528: specwf continue change <name>`
2974
+ );
2975
+ }
2976
+ return hintParts.join("\n ");
2977
+ }
2978
+ if (status === "phase-shipped") {
2979
+ return "\u5F53\u524D phase \u5DF2\u5B8C\u6210\u3002\u521B\u5EFA\u65B0 phase \u6216\u5207\u6362: specwf state set-milestone <id>";
2980
+ }
2981
+ return null;
2982
+ }
2983
+
2984
+ // src/core/state-validator.ts
2985
+ import { existsSync as existsSync8, readFileSync as readFileSync8, readdirSync as readdirSync4 } from "fs";
2986
+ import { join as join12 } from "path";
2987
+ var EXIT_CRITERIA = [
2988
+ // milestone/active → 必须有 requirements.md(grill 产出后才能推进)
2989
+ {
2990
+ type: "milestone",
2991
+ step: "active",
2992
+ checks: [
2993
+ { path: "requirements.md", description: "requirements.md \u4E0D\u5B58\u5728" }
2994
+ ]
2995
+ },
2996
+ // project/requirements-defined → 必须有完整的 requirements.md
2997
+ {
2998
+ type: "project",
2999
+ step: "requirements-defined",
3000
+ checks: [
3001
+ { path: "requirements.md", description: "requirements.md \u5185\u5BB9\u4E3A\u6A21\u677F\uFF0C\u8BF7\u586B\u5199\u540E\u91CD\u8BD5" }
3002
+ ]
3003
+ },
3004
+ // project/researched → 必须有调研产出
3005
+ {
3006
+ type: "project",
3007
+ step: "researched",
3008
+ checks: [
3009
+ { path: "research/summary.md", description: "research/summary.md \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u5B8C\u6210\u8C03\u7814" }
3010
+ ]
3011
+ },
3012
+ // phase/discuss → 必须先产出 context.md
3013
+ {
3014
+ type: "phase",
3015
+ step: "discuss",
3016
+ checks: [
3017
+ { path: "context.md", description: "context.md \u4E0D\u5B58\u5728\u6216\u4E3A\u6A21\u677F\u7A7A\u58F3\u3002\u8BF7\u5148\u5B8C\u6210 discuss \u6B65\u9AA4\u3002" }
3018
+ ]
3019
+ },
3020
+ // phase/research → 必须有 phase 调研报告
3021
+ {
3022
+ type: "phase",
3023
+ step: "research",
3024
+ checks: [
3025
+ { path: "research.md", description: "research.md \u4E0D\u5B58\u5728\u6216\u4E3A\u6A21\u677F\u7A7A\u58F3\u3002\u8BF7\u5148\u5B8C\u6210 research-phase \u6B65\u9AA4\u3002" }
3026
+ ]
3027
+ },
3028
+ // adhoc/proposal → proposal.md 不能是模板
3029
+ {
3030
+ type: "adhoc",
3031
+ step: "proposal",
3032
+ checks: [
3033
+ { path: "changes/", description: "change \u7684 proposal.md \u4E3A\u6A21\u677F\u7A7A\u58F3\uFF0C\u8BF7\u586B\u5199\u540E\u91CD\u8BD5" }
3034
+ ]
3035
+ },
3036
+ // change/planning → design.md + tasks.md 不能是模板
3037
+ {
3038
+ type: "change",
3039
+ step: "planning",
3040
+ checks: [
3041
+ { path: "changes/", description: "design.md \u6216 tasks.md \u4E3A\u6A21\u677F\u7A7A\u58F3\uFF0C\u8BF7\u586B\u5199\u540E\u91CD\u8BD5" }
3042
+ ]
3043
+ }
3044
+ ];
3045
+ function isTemplateFile(filePath) {
3046
+ try {
3047
+ const content = readFileSync8(filePath, "utf-8");
3048
+ const placeholders = content.match(/\{\{[a-zA-Z_-]+\}\}/g);
3049
+ return (placeholders?.length ?? 0) > 3;
3050
+ } catch {
3051
+ return false;
3052
+ }
3053
+ }
3054
+ function findChangeDir(specwfDir) {
3055
+ const changesDir = join12(specwfDir, "changes");
3056
+ if (!existsSync8(changesDir)) return [];
3057
+ try {
3058
+ return readdirSync4(changesDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
3059
+ } catch {
3060
+ return [];
3061
+ }
3062
+ }
3063
+ function checkExitCondition(specwfDir, check, resolvedPath) {
3064
+ const fullPath = resolvedPath ?? join12(specwfDir, check.path);
3065
+ if (check.path.endsWith("/") || check.description.includes("\u7684 ")) {
3066
+ const changes = findChangeDir(specwfDir);
3067
+ for (const change of changes) {
3068
+ for (const doc of ["proposal.md", "design.md", "tasks.md"]) {
3069
+ const docPath = join12(specwfDir, "changes", change, doc);
3070
+ if (existsSync8(docPath) && isTemplateFile(docPath)) {
3071
+ return `changes/${change}/${doc} \u4ECD\u4E3A\u6A21\u677F\u7A7A\u58F3\uFF0C\u8BF7\u586B\u5199\u540E\u91CD\u8BD5`;
3072
+ }
3073
+ }
3074
+ }
3075
+ return null;
3076
+ }
3077
+ if (!existsSync8(fullPath)) {
3078
+ return check.description;
3079
+ }
3080
+ if (isTemplateFile(fullPath)) {
3081
+ return check.description;
3082
+ }
3083
+ return null;
3084
+ }
3085
+ function validateStepAdvance(contextType, contextStep, ref, cwd) {
3086
+ const specwfDir = join12(cwd, "specwf");
3087
+ const criteria = EXIT_CRITERIA.find(
3088
+ (c) => c.type === contextType && c.step === contextStep
3089
+ );
3090
+ if (!criteria) {
3091
+ return { valid: true, errors: [] };
3092
+ }
3093
+ const errors = [];
3094
+ for (const check of criteria.checks) {
3095
+ const resolvedPath = ref && !check.path.startsWith("changes/") && existsSync8(join12(specwfDir, ref, check.path)) ? join12(specwfDir, ref, check.path) : join12(specwfDir, check.path);
3096
+ const error = checkExitCondition(specwfDir, check, resolvedPath);
3097
+ if (error) {
3098
+ errors.push(error);
3099
+ }
3100
+ }
3101
+ return { valid: errors.length === 0, errors };
3102
+ }
3103
+
3104
+ // src/commands/specwf-continue.ts
3105
+ function register6(program2) {
3106
+ const cmd = program2.command("continue").description("Auto-advance to next step");
3107
+ cmd.command("change <name>").description("Advance a specific change").action(continueChangeHandler);
3108
+ cmd.action(continueHandler);
3109
+ }
3110
+ function formatContinueResult(result) {
3111
+ const output = {
3112
+ current: {
3113
+ context: result.context,
3114
+ step: result.currentStep
3115
+ }
3116
+ };
3117
+ if (result.nextCommand) {
3118
+ output.next = {
3119
+ command: result.nextCommand,
3120
+ slash: result.slashCommand || null,
3121
+ subagent: result.needsSubagent,
3122
+ description: result.nextStepInfo?.description || null,
3123
+ outputs: result.nextStepInfo?.artifacts || []
3124
+ };
3125
+ if (result.instructions) {
3126
+ output.next.instructions = result.instructions;
3127
+ }
3128
+ } else {
3129
+ output.next = null;
3130
+ if (result.hint) output.hint = result.hint;
3131
+ }
3132
+ console.log(JSON.stringify(output, null, 2));
3133
+ }
3134
+ function resolveStatusKey(type, step, projectStatus) {
3135
+ switch (type) {
3136
+ case "project":
3137
+ return projectStatus;
3138
+ case "milestone":
3139
+ return projectStatus === "milestone-shipped" ? "milestone-shipped" : "milestone-active";
3140
+ case "phase":
3141
+ return `phase-${step}`;
3142
+ case "change":
3143
+ return `change-${step}`;
3144
+ case "adhoc":
3145
+ return `adhoc-${step}`;
3146
+ default:
3147
+ return projectStatus;
3148
+ }
3149
+ }
3150
+ function continueHandler() {
3151
+ const specwfDir = join13(process.cwd(), "specwf");
3152
+ const cwd = process.cwd();
3153
+ const state = loadState(specwfDir);
3154
+ const validation = validateStepAdvance(state.active_context.type, state.active_context.step, state.active_context.ref, cwd);
3155
+ if (!validation.valid) {
3156
+ console.log(JSON.stringify({ error: "exit_conditions_not_met", details: validation.errors }));
3157
+ return;
3158
+ }
3159
+ const result = determineNextStep(specwfDir);
3160
+ if (result.nextCommand) {
3161
+ const currentStatus = resolveStatusKey(state.active_context.type, state.active_context.step, state.project.status);
3162
+ const transition = getTransition(currentStatus, result.nextCommand);
3163
+ if (transition) {
3164
+ updateState(specwfDir, (s) => {
3165
+ s.active_context.step = transition.to;
3166
+ if (s.active_context.type === "project" || s.active_context.type === "milestone") {
3167
+ s.project.status = transition.to;
3168
+ }
3169
+ });
3170
+ }
3171
+ }
3172
+ formatContinueResult(result);
3173
+ }
3174
+ function continueChangeHandler(name) {
3175
+ const specwfDir = join13(process.cwd(), "specwf");
3176
+ const result = determineChangeNextStep(specwfDir, name);
3177
+ if ("error" in result) {
3178
+ console.log(JSON.stringify({ error: "not_found", message: result.error }));
3179
+ return;
3180
+ }
3181
+ if (result.nextCommand) {
3182
+ const state = loadState(specwfDir);
3183
+ const transition = getTransition(result.currentStep, result.nextCommand);
3184
+ if (transition) {
3185
+ const shortStatus = transition.to.replace(/^(change|adhoc)-/, "");
3186
+ updateState(specwfDir, (s) => {
3187
+ const adhoc = s.adhoc.find((c) => c.name === name);
3188
+ if (adhoc) {
3189
+ adhoc.status = shortStatus;
3190
+ s.active_context = { type: "adhoc", ref: `changes/${name}`, step: shortStatus };
3191
+ return;
3192
+ }
3193
+ const change = s.changes.find((c) => c.name === name);
3194
+ if (change) {
3195
+ change.status = shortStatus;
3196
+ s.active_context = { type: "change", ref: `changes/${name}`, step: shortStatus };
3197
+ }
3198
+ });
3199
+ }
3200
+ }
3201
+ formatContinueResult(result);
3202
+ }
3203
+
3204
+ // src/commands/specwf-archive.ts
3205
+ import { join as join15 } from "path";
3206
+ import { existsSync as existsSync11, readdirSync as readdirSync6, mkdirSync as mkdirSync6, copyFileSync } from "fs";
3207
+ import { execSync as execSync2 } from "child_process";
3208
+
3209
+ // src/core/delta-merge.ts
3210
+ import { createHash } from "crypto";
3211
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, existsSync as existsSync9 } from "fs";
3212
+
3213
+ // src/parser/heading-tree.ts
3214
+ function parseHeadings(markdown) {
3215
+ const lines = markdown.split("\n");
3216
+ const nodes = [];
3217
+ const stack = [];
3218
+ for (let i = 0; i < lines.length; i++) {
3219
+ const line = lines[i];
3220
+ const match = line.match(/^(#{1,6})\s+(.+)$/);
3221
+ if (!match) continue;
3222
+ const level = match[1].length;
3223
+ const text = match[2].trim();
3224
+ const lineNum = i + 1;
3225
+ const contentLines = [];
3226
+ for (let j = i + 1; j < lines.length; j++) {
3227
+ const nextMatch = lines[j].match(/^(#{1,6})\s+(.+)$/);
3228
+ if (nextMatch) break;
3229
+ contentLines.push(lines[j]);
3230
+ }
3231
+ const content = contentLines.join("\n").trim();
3232
+ const node = { level, text, line: lineNum, children: [], content };
3233
+ while (stack.length > 0 && stack[stack.length - 1].level >= level) {
3234
+ stack.pop();
3235
+ }
3236
+ if (stack.length === 0) {
3237
+ nodes.push(node);
3238
+ } else {
3239
+ stack[stack.length - 1].node.children.push(node);
3240
+ }
3241
+ stack.push({ node, level });
3242
+ }
3243
+ return nodes;
3244
+ }
3245
+
3246
+ // src/core/delta-merge.ts
3247
+ function fingerprint(content) {
3248
+ return createHash("sha256").update(content).digest("hex");
3249
+ }
3250
+ function mergeDeltaSpec(baseSpec, deltaSpec, baseFingerprint) {
3251
+ if (baseFingerprint) {
3252
+ const liveFingerprint = fingerprint(baseSpec);
3253
+ if (liveFingerprint === baseFingerprint) {
3254
+ return { type: "ok", merged: deltaSpec };
3255
+ }
3256
+ }
3257
+ const baseTree = parseHeadings(baseSpec);
3258
+ const deltaTree = parseHeadings(deltaSpec);
3259
+ const merged = mergeTrees(baseTree, deltaTree);
3260
+ if (merged.conflicts.length > 0) {
3261
+ return { type: "conflict", conflicts: merged.conflicts };
3262
+ }
3263
+ return { type: "ok", merged: renderTree(merged.nodes) };
3264
+ }
3265
+ function mergeTrees(base, delta) {
3266
+ const conflicts = [];
3267
+ const nodes = [];
3268
+ const baseIndex = indexNodes(base);
3269
+ const deltaIndex = indexNodes(delta);
3270
+ const allKeys = /* @__PURE__ */ new Set([...baseIndex.keys(), ...deltaIndex.keys()]);
3271
+ for (const key of allKeys) {
3272
+ const b = baseIndex.get(key);
3273
+ const d = deltaIndex.get(key);
3274
+ if (b && !d) {
3275
+ nodes.push({ node: b, children: b.children.map((c) => ({ node: c, children: [] })) });
3276
+ } else if (!b && d) {
3277
+ nodes.push({ node: d, children: d.children.map((c) => ({ node: c, children: [] })) });
3278
+ } else if (b && d) {
3279
+ const childMerge = mergeTrees(b.children, d.children);
3280
+ if (b.content === d.content) {
3281
+ nodes.push({ node: b, children: childMerge.nodes });
3282
+ } else {
3283
+ const lineMerge = tryLineMerge(b.content, d.content);
3284
+ if (lineMerge !== null) {
3285
+ nodes.push({ node: { ...b, content: lineMerge }, children: childMerge.nodes });
3286
+ } else {
3287
+ conflicts.push({
3288
+ section: b.text,
3289
+ message: `Content conflict in section: ${b.text}`,
3290
+ baseContent: b.content,
3291
+ deltaContent: d.content
3292
+ });
3293
+ nodes.push({ node: b, children: childMerge.nodes });
3294
+ }
3295
+ }
3296
+ conflicts.push(...childMerge.conflicts);
3297
+ }
3298
+ }
3299
+ return { nodes, conflicts };
3300
+ }
3301
+ function indexNodes(nodes) {
3302
+ const map = /* @__PURE__ */ new Map();
3303
+ for (const node of nodes) {
3304
+ map.set(`${node.level}:${node.text}`, node);
3305
+ }
3306
+ return map;
3307
+ }
3308
+ function tryLineMerge(baseText, deltaText) {
3309
+ const baseLines = baseText.split("\n");
3310
+ const deltaLines = deltaText.split("\n");
3311
+ const baseSet = new Set(baseLines);
3312
+ const removedFromBase = baseLines.filter(
3313
+ (l) => l.trim() && !deltaLines.includes(l)
3314
+ );
3315
+ if (removedFromBase.length === 0) {
3316
+ const result = [...baseLines];
3317
+ for (const line of deltaLines) {
3318
+ if (!baseSet.has(line)) {
3319
+ result.push(line);
3320
+ }
3321
+ }
3322
+ return result.join("\n");
3323
+ }
3324
+ return null;
3325
+ }
3326
+ function renderTree(nodes) {
3327
+ const lines = [];
3328
+ renderNodes(nodes, lines);
3329
+ return lines.join("\n").trim();
3330
+ }
3331
+ function renderNodes(nodes, lines) {
3332
+ for (const { node, children } of nodes) {
3333
+ lines.push(`${"#".repeat(node.level)} ${node.text}`);
3334
+ if (node.content) {
3335
+ lines.push("");
3336
+ lines.push(node.content);
3337
+ }
3338
+ if (children.length > 0) {
3339
+ lines.push("");
3340
+ renderNodes(children, lines);
3341
+ }
3342
+ lines.push("");
3343
+ }
3344
+ }
3345
+ function mergeAndWrite(liveSpecPath, deltaSpecPath, baseFingerprint) {
3346
+ const baseSpec = readFileSync9(liveSpecPath, "utf-8");
3347
+ const deltaSpec = readFileSync9(deltaSpecPath, "utf-8");
3348
+ const result = mergeDeltaSpec(baseSpec, deltaSpec, baseFingerprint);
3349
+ if (result.type === "ok") {
3350
+ writeFileSync7(liveSpecPath, result.merged, "utf-8");
3351
+ }
3352
+ return result;
3353
+ }
3354
+
3355
+ // src/core/code-extract.ts
3356
+ import { execSync } from "child_process";
3357
+ import { existsSync as existsSync10, readFileSync as readFileSync10, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5, readdirSync as readdirSync5 } from "fs";
3358
+ import { join as join14 } from "path";
3359
+ function extractFromGitDiff(repoDir, changeDir) {
3360
+ const diff = getGitDiff(repoDir);
3361
+ if (diff === null) {
3362
+ return { extractions: [], available: false };
3363
+ }
3364
+ const domains = changeDir ? detectDomains(changeDir) : ["general"];
3365
+ const extractions = [];
3366
+ for (const domain of domains) {
3367
+ const behaviors = extractBehaviors(diff, domain);
3368
+ const constraints = extractConstraints(diff, domain);
3369
+ if (behaviors.length > 0 || constraints.length > 0) {
3370
+ extractions.push({ domain, behaviors, constraints });
3371
+ }
3372
+ }
3373
+ return { extractions, available: true };
3374
+ }
3375
+ function getGitDiff(repoDir) {
3376
+ try {
3377
+ const diff = execSync("git diff HEAD", {
3378
+ cwd: repoDir,
3379
+ encoding: "utf-8",
3380
+ stdio: ["pipe", "pipe", "pipe"]
3381
+ });
3382
+ if (diff.trim()) return diff;
3383
+ const lastCommit = execSync("git diff HEAD~1 HEAD", {
3384
+ cwd: repoDir,
3385
+ encoding: "utf-8",
3386
+ stdio: ["pipe", "pipe", "pipe"]
3387
+ });
3388
+ return lastCommit.trim() ? lastCommit : null;
3389
+ } catch {
3390
+ return null;
3391
+ }
3392
+ }
3393
+ function detectDomains(changeDir) {
3394
+ const specsDir = join14(changeDir, "specs");
3395
+ if (!existsSync10(specsDir)) return ["general"];
3396
+ try {
3397
+ return readdirSync5(specsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
3398
+ } catch {
3399
+ return ["general"];
3400
+ }
3401
+ }
3402
+ function extractBehaviors(diff, _domain) {
3403
+ const behaviors = [];
3404
+ const lines = diff.split("\n");
3405
+ for (const line of lines) {
3406
+ if (line.startsWith("+") && !line.startsWith("+++")) {
3407
+ const content = line.slice(1).trim();
3408
+ if (/\b(SHALL|MUST|SHOULD|MAY)\b/.test(content)) {
3409
+ behaviors.push(content);
3410
+ }
3411
+ if (/^(export\s+)?(async\s+)?function\s+/.test(content) || /^(export\s+)?class\s+/.test(content)) {
3412
+ behaviors.push(`\u65B0\u589E: ${content}`);
3413
+ }
3414
+ }
3415
+ }
3416
+ return behaviors;
3417
+ }
3418
+ function extractConstraints(diff, _domain) {
3419
+ const constraints = [];
3420
+ const lines = diff.split("\n");
3421
+ for (const line of lines) {
3422
+ if (line.startsWith("+") && !line.startsWith("+++")) {
3423
+ const content = line.slice(1).trim();
3424
+ if (/^(throw|assert|if\s*\()/.test(content) && !content.startsWith("//")) {
3425
+ constraints.push(`\u7EA6\u675F: ${content}`);
3426
+ }
3427
+ if (/^(export\s+)?(interface|type)\s+/.test(content)) {
3428
+ constraints.push(`\u7C7B\u578B\u7EA6\u675F: ${content}`);
3429
+ }
3430
+ }
3431
+ }
3432
+ return constraints;
3433
+ }
3434
+ function writeExtractionToSpec(specsDir, extraction) {
3435
+ const domainDir = join14(specsDir, extraction.domain);
3436
+ const specPath = join14(domainDir, "spec.md");
3437
+ let existing = "";
3438
+ if (existsSync10(specPath)) {
3439
+ existing = readFileSync10(specPath, "utf-8");
3440
+ }
3441
+ const section = generateAutoExtractedSection(extraction);
3442
+ const updated = existing.trim() ? `${existing.trim()}
3443
+
3444
+ ${section}` : section;
3445
+ mkdirSync5(domainDir, { recursive: true });
3446
+ writeFileSync8(specPath, updated, "utf-8");
3447
+ }
3448
+ function generateAutoExtractedSection(extraction) {
3449
+ const lines = [
3450
+ "<!-- AUTO-EXTRACTED: \u4EE5\u4E0B\u5185\u5BB9\u7531 code-extract \u4ECE\u4EE3\u7801 diff \u63D0\u53D6\uFF0C\u8BF7\u4EBA\u5DE5\u5BA1\u6838 -->",
3451
+ "",
3452
+ "## Auto-Extracted Behaviors",
3453
+ ""
3454
+ ];
3455
+ if (extraction.behaviors.length > 0) {
3456
+ lines.push("### Detected Behaviors", "");
3457
+ for (const b of extraction.behaviors) {
3458
+ lines.push(`- ${b}`);
3459
+ }
3460
+ lines.push("");
3461
+ }
3462
+ if (extraction.constraints.length > 0) {
3463
+ lines.push("### Detected Constraints", "");
3464
+ for (const c of extraction.constraints) {
3465
+ lines.push(`- ${c}`);
3466
+ }
3467
+ lines.push("");
3468
+ }
3469
+ lines.push("<!-- END AUTO-EXTRACTED -->");
3470
+ return lines.join("\n");
3471
+ }
3472
+
3473
+ // src/commands/specwf-archive.ts
3474
+ function register7(program2) {
3475
+ program2.command("archive <change>").description("\u5F52\u6863 change\uFF08delta \u5408\u5E76 + \u4EE3\u7801\u56DE\u704C\uFF09").action(archiveHandler);
3476
+ }
3477
+ function archiveHandler(changePath) {
3478
+ const specwfDir = join15(process.cwd(), "specwf");
3479
+ const fullChangePath = join15(process.cwd(), changePath);
3480
+ if (!existsSync11(fullChangePath)) {
3481
+ console.error(`\u9519\u8BEF: change \u76EE\u5F55\u4E0D\u5B58\u5728: ${changePath}`);
3482
+ process.exit(1);
3483
+ }
3484
+ const specsDir = join15(fullChangePath, "specs");
3485
+ if (existsSync11(specsDir)) {
3486
+ mergeDeltaSpecs(specsDir, specwfDir);
3487
+ console.log("\u2713 delta-specs \u5408\u5E76\u5B8C\u6210");
3488
+ }
3489
+ const summaryPath = join15(fullChangePath, "change-summary.md");
3490
+ if (!existsSync11(summaryPath)) {
3491
+ console.warn("\u26A0 change-summary.md \u4E0D\u5B58\u5728\u3002\u5EFA\u8BAE\u5148\u4F7F\u7528 `specwf template change-summary` \u751F\u6210\u53D8\u66F4\u603B\u7ED3\u3002");
3492
+ }
3493
+ const repoDir = process.cwd();
3494
+ const extractResult = extractFromGitDiff(repoDir, fullChangePath);
3495
+ if (extractResult.available && extractResult.extractions.length > 0) {
3496
+ for (const extraction of extractResult.extractions) {
3497
+ writeExtractionToSpec(join15(specwfDir, "specs"), extraction);
3498
+ }
3499
+ if (extractResult.extractions.length > 0) {
3500
+ console.log(`\u2713 \u4EE3\u7801\u8BA4\u77E5\u63D0\u53D6\u5B8C\u6210 (${extractResult.extractions.length} \u4E2A\u57DF)`);
3501
+ }
3502
+ }
3503
+ const archiveDir = archiveChangeDir(specwfDir, fullChangePath);
3504
+ console.log(`\u2713 \u5F52\u6863\u5230: ${archiveDir}`);
3505
+ try {
3506
+ execSync2(`git rm -r "${changePath}" 2>/dev/null || true`, { cwd: process.cwd() });
3507
+ } catch {
3508
+ }
3509
+ const changeName = changePath.split("/").pop() ?? "unknown";
3510
+ try {
3511
+ updateState(specwfDir, (state) => {
3512
+ const change = state.changes.find((c) => c.name === changeName);
3513
+ if (change) {
3514
+ change.status = "archived";
3515
+ return;
3516
+ }
3517
+ const adhoc = state.adhoc.find((c) => c.name === changeName);
3518
+ if (adhoc) {
3519
+ adhoc.status = "archived";
3520
+ }
3521
+ });
3522
+ console.log("\u2713 state.md \u5DF2\u66F4\u65B0");
3523
+ } catch {
3524
+ }
3525
+ console.log("\u5F52\u6863\u5B8C\u6210\u3002");
3526
+ }
3527
+ function mergeDeltaSpecs(deltaDir, specwfDir) {
3528
+ const entries = readdirSync6(deltaDir, { withFileTypes: true });
3529
+ for (const entry of entries) {
3530
+ if (!entry.isDirectory()) continue;
3531
+ const deltaSpecPath = join15(deltaDir, entry.name, "spec.md");
3532
+ const liveSpecPath = join15(specwfDir, "specs", entry.name, "spec.md");
3533
+ if (!existsSync11(deltaSpecPath)) continue;
3534
+ if (!existsSync11(liveSpecPath)) {
3535
+ mkdirSync6(join15(specwfDir, "specs", entry.name), { recursive: true });
3536
+ copyFileSync(deltaSpecPath, liveSpecPath);
3537
+ continue;
3538
+ }
3539
+ const result = mergeAndWrite(liveSpecPath, deltaSpecPath);
3540
+ if (result.type === "conflict") {
3541
+ console.warn(`\u26A0 \u5408\u5E76\u51B2\u7A81: ${entry.name}/spec.md`);
3542
+ for (const c of result.conflicts) {
3543
+ console.warn(` \u8282: ${c.section}`);
3544
+ }
3545
+ }
3546
+ }
3547
+ }
3548
+
3549
+ // src/commands/specwf-list.ts
3550
+ import { join as join16 } from "path";
3551
+ function register8(program2) {
3552
+ program2.command("list").description("\u5217\u51FA milestones/phases/changes").option("--all", "\u5305\u542B\u5F52\u6863").action(listHandler);
3553
+ }
3554
+ function listHandler(options) {
3555
+ const specwfDir = join16(process.cwd(), "specwf");
3556
+ let hasItems = false;
3557
+ const milestones = listMilestones(specwfDir);
3558
+ if (milestones.length > 0) {
3559
+ console.log("Milestones:");
3560
+ for (const ms of milestones) {
3561
+ console.log(` ${ms}/`);
3562
+ const phases = listPhases(specwfDir, ms);
3563
+ for (const ph of phases) {
3564
+ console.log(` ${ph}/`);
3565
+ const changes = listChanges(specwfDir, ms, ph);
3566
+ for (const ch of changes) {
3567
+ console.log(` ${ch}/`);
3568
+ }
3569
+ }
3570
+ }
3571
+ hasItems = true;
3572
+ }
3573
+ const adhoc = listAdhocChanges(specwfDir);
3574
+ if (adhoc.length > 0) {
3575
+ if (hasItems) console.log("");
3576
+ console.log("\u4E34\u65F6 Changes:");
3577
+ for (const ch of adhoc) {
3578
+ console.log(` ${ch}/`);
3579
+ }
3580
+ hasItems = true;
3581
+ }
3582
+ if (options.all) {
3583
+ const archived = listArchived(specwfDir);
3584
+ if (archived.length > 0) {
3585
+ if (hasItems) console.log("");
3586
+ console.log("\u5F52\u6863:");
3587
+ for (const a of archived) {
3588
+ console.log(` ${a}/`);
3589
+ }
3590
+ hasItems = true;
3591
+ }
3592
+ }
3593
+ if (!hasItems) {
3594
+ console.log("(\u65E0\u6761\u76EE)");
3595
+ }
3596
+ }
3597
+
3598
+ // src/commands/specwf-template.ts
3599
+ import { join as join17, dirname as dirname2 } from "path";
3600
+ import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync9 } from "fs";
3601
+
3602
+ // src/templates/artifacts/index.ts
3603
+ var PROPOSAL_TEMPLATE = `# Proposal: {{name}}
3604
+
3605
+ > This document is a Change Proposal \u2014 align intent, scope, and approach before implementation. Complete each section; reviewers will evaluate this proposal before the design phase.
3606
+
3607
+ ---
3608
+
3609
+ ## Intent
3610
+
3611
+ <!--
3612
+ Describe why this change is needed:
3613
+ 1. What specific problem exists or what capability is missing?
3614
+ 2. Who is affected (users/developers/ops)? How severely?
3615
+ 3. What happens if we don't make this change?
3616
+ 4. Is this a bug fix / feature / tech debt / perf improvement?
3617
+ 5. Is this linked to a known issue, user feedback, or metric? (attach issue link if available)
3618
+ -->
3619
+
3620
+ {{intent}}
3621
+
3622
+ ---
3623
+
3624
+ ## Scope
3625
+
3626
+ ### In scope
3627
+
3628
+ <!--
3629
+ List all items covered by this change. One per line, verb-first.
3630
+ Example:
3631
+ - Add skeleton loading state on list pull-to-refresh
3632
+ - Add useScrollPerformance hook for scroll metrics
3633
+ - Memoize UserCard component
3634
+ -->
3635
+
3636
+ {{in-scope-items}}
3637
+
3638
+ ### Out of scope
3639
+
3640
+ <!--
3641
+ Explicitly excluded changes to prevent scope creep. One per line with reason.
3642
+ Example:
3643
+ - Homepage skeleton screen (planned for next phase)
3644
+ - Server-side API pagination (unrelated to client performance)
3645
+ - Android list optimization (platform-specific, needs separate research)
3646
+ -->
3647
+
3648
+ {{out-of-scope-items}}
3649
+
3650
+ ---
3651
+
3652
+ ## Approach
3653
+
3654
+ <!--
3655
+ Describe the technical direction at a high level:
3656
+ 1. Architecture layer: Which layer does the change touch (UI/Service/Store)? New modules needed?
3657
+ 2. Library choices: New dependencies? Upgrades? Rationale?
3658
+ 3. Data flow: How does data travel from source to UI? State management changes?
3659
+ 4. Compatibility: Backward compatibility strategy? Migration needed?
3660
+ 5. Testability: Are there injection points / mock seams for testing?
3661
+
3662
+ No detailed implementation here \u2014 the design doc handles that.
3663
+ -->
3664
+
3665
+ {{approach}}
3666
+
3667
+ ---
3668
+
3669
+ ## Must-haves
3670
+
3671
+ <!--
3672
+ 3-7 observable, verifiable must-have behaviors.
3673
+ Each must be a concrete statement \u2014 no ambiguity.
3674
+ Reviewers should be able to judge pass/fail using these conditions.
3675
+
3676
+ Format: "MUST <condition>" or "SHALL <condition>"
3677
+ - Observable: visible on screen, checkable via CLI, assertable in tests
3678
+ - Verifiable: reviewer can confirm via action/command
3679
+ -->
3680
+
3681
+ {{must-haves}}
3682
+
3683
+ ---
3684
+
3685
+ ## Non-goals
3686
+
3687
+ <!--
3688
+ Explicit non-goals to prevent reviewers from asking "why wasn't X done?"
3689
+ Different from Out of scope (not in this change's scope).
3690
+ Non-goals are specific targets that might be incorrectly assumed to be in scope.
3691
+ Example:
3692
+ - Not pursuing Android list performance in this change
3693
+ - Not changing the existing pagination logic
3694
+ - Not adding new UI component library dependencies
3695
+ -->
3696
+
3697
+ {{non-goals}}
3698
+ `;
3699
+ var DESIGN_TEMPLATE = `# Design: {{name}}
3700
+
3701
+ > This document is the Change Design \u2014 written after proposal approval, describing how to implement. Each section has fill-in guidance. After this document, proceed to task breakdown.
3702
+
3703
+ ---
3704
+
3705
+ ## Context & Goals
3706
+
3707
+ <!--
3708
+ 1. Briefly describe context \u2014 what constraints exist?
3709
+ 2. Core design goals (no more than 3)
3710
+ 3. Must align with proposal Intent and Must-haves
3711
+ -->
3712
+
3713
+ {{background-and-goals}}
3714
+
3715
+ ---
3716
+
3717
+ ## Technical Approach
3718
+
3719
+ ### Architecture Diagram
3720
+
3721
+ <!--
3722
+ ASCII art showing module/component relationships:
3723
+ - New modules vs. existing modules
3724
+ - Data flow direction (arrows)
3725
+ - File/module boundaries
3726
+ Annotate: [NEW], [MODIFIED], [EXISTING]
3727
+ -->
3728
+
3729
+ \`\`\`text
3730
+ {{architecture-diagram}}
3731
+ \`\`\`
3732
+
3733
+ ### Core Data Structures
3734
+
3735
+ <!--
3736
+ Key types/interfaces/data structures introduced or modified by this design.
3737
+ Use TypeScript interface format. Brief description per type.
3738
+ -->
3739
+
3740
+ {{data-structures}}
3741
+
3742
+ ### Data Flow
3743
+
3744
+ <!--
3745
+ Step-by-step description of data flow from trigger to effect.
3746
+ Example:
3747
+ 1. User scrolls list \u2192 FlatList fires onScroll
3748
+ 2. OptimizedList reads itemHeight config \u2192 enables getItemLayout
3749
+ 3. Layout engine skips dynamic measurement \u2192 uses fixed row height
3750
+ 4. useScrollPerformance samples FPS every 500ms
3751
+ 5. FPS data \u2192 Performance Reporter \u2192 backend
3752
+ -->
3753
+
3754
+ {{data-flow}}
3755
+
3756
+ ### Interface Design
3757
+
3758
+ <!--
3759
+ Public API signatures exposed by this design:
3760
+ - Function/method names
3761
+ - Parameter lists (name + type + description)
3762
+ - Return types
3763
+ - sync/async
3764
+ -->
3765
+
3766
+ {{api-signatures}}
3767
+
3768
+ ---
3769
+
3770
+ ## File Manifest
3771
+
3772
+ <!--
3773
+ All files to create or modify, organized as a table.
3774
+ -->
3775
+
3776
+ | File Path | Description | Action |
3777
+ |-----------|-------------|--------|
3778
+ | \`{{file-path-1}}\` | {{description}} | Create |
3779
+ | \`{{file-path-2}}\` | {{description}} | Modify |
3780
+
3781
+ ---
3782
+
3783
+ ## Test Strategy
3784
+
3785
+ ### Unit Tests
3786
+ - <!-- Which modules need unit tests? What needs mocking? -->
3787
+
3788
+ ### Integration Tests
3789
+ - <!-- Which flows need integration tests? What fixtures needed? -->
3790
+
3791
+ ### TDD Tasks
3792
+ - <!-- List type:behavior tasks requiring RED\u2192GREEN\u2192REFACTOR -->
3793
+
3794
+ ---
3795
+
3796
+ ## Alternatives
3797
+
3798
+ <!--
3799
+ Evaluated but rejected approaches, with rationale.
3800
+ -->
3801
+
3802
+ | Approach | Pros | Cons | Rejection Reason |
3803
+ |----------|------|------|-----------------|
3804
+ | {{alt-name-1}} | {{pros}} | {{cons}} | {{reason}} |
3805
+ | {{alt-name-2}} | {{pros}} | {{cons}} | {{reason}} |
3806
+
3807
+ ---
3808
+
3809
+ ## Risk Assessment
3810
+
3811
+ | Risk | Probability | Impact | Mitigation |
3812
+ |------|------------|--------|-----------|
3813
+ | {{risk-1}} | {{probability}} | {{impact}} | {{mitigation}} |
3814
+ | {{risk-2}} | {{probability}} | {{impact}} | {{mitigation}} |
3815
+ `;
3816
+ var TASKS_TEMPLATE = `# Tasks: {{name}}
3817
+
3818
+ > This document breaks the design into executable tasks grouped by wave. Each task includes description, files, acceptance criteria, optional depends_on and spec_ref. type:behavior tasks must include RED test descriptions (GIVEN/WHEN/THEN format).
3819
+
3820
+ ---
3821
+
3822
+ ## TDD Type Annotations
3823
+
3824
+ | type | Meaning | TDD Protocol |
3825
+ |------|---------|-------------|
3826
+ | \`behavior\` | Business behavior \u2014 implement a concrete, observable/assertable feature | **RED\u2192GREEN\u2192REFACTOR** (mandatory: test first \u2192 implement \u2192 refactor) |
3827
+ | \`config\` | Configuration \u2014 env vars, CI/CD, lint, tsconfig, etc. | Direct implementation, no TDD |
3828
+ | \`refactor\` | Refactoring \u2014 improve internal structure without changing behavior | Verify tests pass \u2192 refactor \u2192 verify again |
3829
+ | \`docs\` | Documentation \u2014 README, API docs, comments | Direct implementation, no TDD |
3830
+ | \`scaffolding\` | Skeleton code \u2014 new module shells, directory structure, templates | Direct implementation, no TDD |
3831
+
3832
+ > **Rule**: If a task's core output is "a behavior" (user-perceptible or test-assertable), use \`behavior\`. If it's just "file exists" or "config takes effect", use \`config\`/\`scaffolding\`.
3833
+
3834
+ ---
3835
+
3836
+ ## Wave 1: {{wave-1-theme}}
3837
+
3838
+ <!--
3839
+ A wave is an independently verifiable unit of work. Tasks within a wave may have dependencies but the wave is self-contained.
3840
+ Each wave completion enables verification (tsc + test pass).
3841
+ -->
3842
+
3843
+ - [ ] task-{{id-1}}: [type:{{type}}] {{title}}
3844
+ - **description**: {{What to do, approach, files/APIs to reference}}
3845
+ - **files**: {{comma-separated file paths}}
3846
+ - **acceptance**: {{observable, assertable acceptance criteria}}
3847
+ - **depends_on**: [task-{{id-x}}] <!-- optional: predecessor -->
3848
+ - **spec_ref**: specs/{{domain}}/spec.md <!-- optional: linked spec -->
3849
+ {{if behavior}}
3850
+ - ***RED test***:
3851
+ \`\`\`
3852
+ GIVEN {{precondition}}
3853
+ WHEN {{trigger action}}
3854
+ THEN {{expected result}}
3855
+ \`\`\`
3856
+ {{/if}}
3857
+
3858
+ ---
3859
+
3860
+ ## Wave 2: {{wave-2-theme}}
3861
+
3862
+ - [ ] task-{{id-3}}: [type:{{type}}] {{title}}
3863
+ - **description**: {{What to do}}
3864
+ - **files**: {{file paths}}
3865
+ - **acceptance**: {{acceptance criteria}}
3866
+ - **depends_on**: [task-{{id-1}}] <!-- optional -->
3867
+ {{if behavior}}
3868
+ - ***RED test***:
3869
+ \`\`\`
3870
+ GIVEN {{precondition}}
3871
+ WHEN {{trigger action}}
3872
+ THEN {{expected result}}
3873
+ \`\`\`
3874
+ {{/if}}
3875
+
3876
+ ---
3877
+
3878
+ ## Verification
3879
+
3880
+ - [ ] \`tsc --noEmit\` passes (or equivalent type check)
3881
+ - [ ] \`vitest run\` all test suites pass
3882
+ - [ ] Each wave's acceptance criteria confirmed (manual or automated)
3883
+ - [ ] New code passes lint check
3884
+ - [ ] No new type errors or warnings introduced
3885
+ `;
3886
+ var CONTEXT_TEMPLATE = `# Context: {{name}}
3887
+
3888
+ > Phase implementation decisions document. Captures architecture decisions, interface contracts, and implementation constraints for this phase. Written during the discuss phase.
3889
+
3890
+ ---
3891
+
3892
+ ## Phase Goals
3893
+ <!-- What does this phase deliver? -->
3894
+
3895
+ {{phase-goals}}
3896
+
3897
+ ---
3898
+
3899
+ ## Architecture Decisions
3900
+
3901
+ <!-- Numbered decisions: D1, D2, ... Each decision records what was chosen and why. -->
3902
+
3903
+ ### D1: {{decision-title}}
3904
+ - **Decision**: {{what we decided}}
3905
+ - **Rationale**: {{why}}
3906
+ - **Alternatives considered**: {{what else we evaluated}}
3907
+
3908
+ ### D2: {{decision-title}}
3909
+ - **Decision**: {{what we decided}}
3910
+ - **Rationale**: {{why}}
3911
+ - **Alternatives considered**: {{what else we evaluated}}
3912
+
3913
+ ---
3914
+
3915
+ ## Interface Contracts
3916
+
3917
+ <!-- Key APIs and data models for this phase. -->
3918
+
3919
+ {{interface-contracts}}
3920
+
3921
+ ---
3922
+
3923
+ ## Implementation Constraints
3924
+
3925
+ <!-- Technical limits and boundaries for this phase. -->
3926
+
3927
+ {{constraints}}
3928
+
3929
+ ---
3930
+
3931
+ ## Change Split Plan
3932
+
3933
+ <!-- Preliminary breakdown of this phase into changes. -->
3934
+
3935
+ {{change-split-plan}}
3936
+
3937
+ ---
3938
+
3939
+ ## Non-Goals
3940
+
3941
+ <!-- Explicitly excluded from this phase. -->
3942
+
3943
+ {{non-goals}}
3944
+ `;
3945
+ var RESEARCH_TEMPLATE = `# Research: {{name}}
3946
+
3947
+ > Technical research document. Compares alternatives, assesses feasibility, and produces recommendations.
3948
+
3949
+ ---
3950
+
3951
+ ## Research Scope
3952
+
3953
+ {{scope}}
3954
+
3955
+ ---
3956
+
3957
+ ## Candidate Comparison
3958
+
3959
+ | Criterion | Option A: {{name-a}} | Option B: {{name-b}} | Option C: {{name-c}} |
3960
+ |-----------|---------------------|---------------------|---------------------|
3961
+ | {{criterion-1}} | {{score}} | {{score}} | {{score}} |
3962
+ | {{criterion-2}} | {{score}} | {{score}} | {{score}} |
3963
+
3964
+ ---
3965
+
3966
+ ## Recommendation
3967
+
3968
+ **Recommended**: {{recommended-option}}
3969
+
3970
+ **Rationale**: {{rationale}}
3971
+
3972
+ ---
3973
+
3974
+ ## Risk Assessment
3975
+
3976
+ | Risk | Likelihood | Impact | Mitigation |
3977
+ |------|-----------|--------|-----------|
3978
+ | {{risk-1}} | {{likelihood}} | {{impact}} | {{mitigation}} |
3979
+
3980
+ ---
3981
+
3982
+ ## Open Questions
3983
+
3984
+ - {{question-1}}
3985
+ - {{question-2}}
3986
+ `;
3987
+ var SUMMARY_TEMPLATE = `# Summary: {{name}}
3988
+
3989
+ > Change completion summary. Generated after all waves are complete.
3990
+
3991
+ ---
3992
+
3993
+ ## Intent Recap
3994
+ {{intent}}
3995
+
3996
+ ## Files Changed
3997
+ | File | Action | Lines |
3998
+ |------|--------|-------|
3999
+ | {{file-1}} | {{action}} | {{lines}} |
4000
+
4001
+ ## Key Decisions
4002
+ - {{decision-1}}
4003
+ - {{decision-2}}
4004
+
4005
+ ## Verification
4006
+ - [ ] All tests pass
4007
+ - [ ] Type check passes
4008
+ - [ ] Delta-specs covered
4009
+ `;
4010
+ var VERIFICATION_TEMPLATE = `# Verification: {{name}}
4011
+
4012
+ > Goal-backward verification report. Confirms the change delivers what it promised.
4013
+
4014
+ ---
4015
+
4016
+ ## Status: {{status}}
4017
+
4018
+ <!-- passed | gaps_found | human_needed -->
4019
+
4020
+ ## Delta-Spec Coverage
4021
+
4022
+ | Spec Item | Test Coverage | Status |
4023
+ |-----------|--------------|--------|
4024
+ | {{spec-item-1}} | {{test}} | {{status}} |
4025
+
4026
+ ## TDD Commit Integrity
4027
+
4028
+ | Task | RED | GREEN | REFACTOR | Status |
4029
+ |------|-----|-------|----------|--------|
4030
+ | {{task-1}} | {{commit}} | {{commit}} | {{commit}} | {{status}} |
4031
+
4032
+ ## Test Suite
4033
+
4034
+ - Total: {{total}}
4035
+ - Passed: {{passed}}
4036
+ - Failed: {{failed}}
4037
+ - Skipped: {{skipped}}
4038
+
4039
+ ## Findings
4040
+
4041
+ {{findings}}
4042
+ `;
4043
+ var SPEC_REVIEW_TEMPLATE = `# Spec Review: {{name}}
4044
+
4045
+ > Specification compliance review. Cross-references delta-spec SHALL/MUST constraints against implementation.
4046
+
4047
+ ---
4048
+
4049
+ ## Overall: {{verdict}}
4050
+
4051
+ <!-- PASS / FAIL / NEEDS_REVISION -->
4052
+
4053
+ ## Constraint Checklist
4054
+
4055
+ | # | Constraint | Location | Status | Evidence |
4056
+ |---|-----------|----------|--------|----------|
4057
+ | 1 | {{constraint}} | {{file:line}} | PASS / FAIL / N/A | {{note}} |
4058
+
4059
+ ## Edge Case Coverage
4060
+
4061
+ | Edge Case | Covered? | Evidence |
4062
+ |-----------|---------|----------|
4063
+ | {{edge-case}} | {{yes/no}} | {{note}} |
4064
+
4065
+ ## Findings
4066
+
4067
+ {{findings}}
4068
+ `;
4069
+ var QUALITY_REVIEW_TEMPLATE = `# Quality Review: {{name}}
4070
+
4071
+ > Code quality audit. Checks for bugs, security issues, conventions, and common AI mistakes.
4072
+
4073
+ ---
4074
+
4075
+ ## Overall: {{verdict}}
4076
+
4077
+ <!-- PASS / FAIL / NEEDS_REVISION -->
4078
+
4079
+ ## Issues
4080
+
4081
+ | # | Severity | Category | Location | Description |
4082
+ |---|----------|----------|----------|-------------|
4083
+ | 1 | BLOCKER / MAJOR / MINOR / INFO | {{category}} | {{file:line}} | {{description}} |
4084
+
4085
+ ## Convention Compliance
4086
+
4087
+ | Rule | Status | Note |
4088
+ |------|--------|------|
4089
+ | {{rule}} | {{status}} | {{note}} |
4090
+
4091
+ ## Findings
4092
+
4093
+ {{findings}}
4094
+ `;
4095
+ var GOAL_REVIEW_TEMPLATE = `# Goal Review: {{name}}
4096
+
4097
+ > Goal achievement review. Cross-references proposal.md goals and must_haves against implementation.
4098
+
4099
+ ---
4100
+
4101
+ ## Overall: {{verdict}}
4102
+
4103
+ <!-- PASS / FAIL / NEEDS_REVISION -->
4104
+
4105
+ ## Goal Checklist
4106
+
4107
+ | # | Goal / Must-have | Status | Evidence |
4108
+ |---|-----------------|--------|----------|
4109
+ | 1 | {{goal}} | ACHIEVED / PARTIAL / NOT_ACHIEVED | {{note}} |
4110
+
4111
+ ## Completeness Assessment
4112
+
4113
+ {{assessment}}
4114
+
4115
+ ## Findings
4116
+
4117
+ {{findings}}
4118
+ `;
4119
+ var CHANGE_SUMMARY_TEMPLATE = `# Change Summary: {{name}}
4120
+
4121
+ > Auto-generated summary after all waves complete.
4122
+
4123
+ ---
4124
+
4125
+ ## Intent
4126
+ {{intent}}
4127
+
4128
+ ## Output Files
4129
+ | File | Action |
4130
+ |------|--------|
4131
+ | {{file}} | {{action}} |
4132
+
4133
+ ## Key Decisions
4134
+ - {{decision}}
4135
+
4136
+ ## Verification Results
4137
+ - Type check: {{typecheck}}
4138
+ - Tests: {{tests}}
4139
+ - Lint: {{lint}}
4140
+ `;
4141
+ var CODEBASE_STACK_TEMPLATE = `# Tech Stack: {{name}}
4142
+
4143
+ > Codebase analysis \u2014 technology stack identified from brownfield scan.
4144
+
4145
+ ---
4146
+
4147
+ ## Runtime
4148
+ - **Language**: {{language}}
4149
+ - **Version**: {{version}}
4150
+ - **Package manager**: {{package-manager}}
4151
+
4152
+ ## Frameworks & Libraries
4153
+ | Dependency | Version | Purpose |
4154
+ |-----------|---------|--------|
4155
+ | {{dep-1}} | {{version}} | {{purpose}} |
4156
+
4157
+ ## Build & Tooling
4158
+ - **Bundler**: {{bundler}}
4159
+ - **Test runner**: {{test-runner}}
4160
+ - **Linter**: {{linter}}
4161
+ - **Formatter**: {{formatter}}
4162
+
4163
+ ## Infrastructure
4164
+ - **CI/CD**: {{ci-cd}}
4165
+ - **Deployment**: {{deployment}}
4166
+ - **Database**: {{database}}
4167
+ `;
4168
+ var CODEBASE_ARCHITECTURE_TEMPLATE = `# Architecture: {{name}}
4169
+
4170
+ > Codebase analysis \u2014 architecture patterns and module structure identified from brownfield scan.
4171
+
4172
+ ---
4173
+
4174
+ ## Module Map
4175
+
4176
+ \`\`\`text
4177
+ {{module-map}}
4178
+ \`\`\`
4179
+
4180
+ ## Architectural Patterns
4181
+ | Pattern | Where | Notes |
4182
+ |---------|-------|-------|
4183
+ | {{pattern-1}} | {{location}} | {{notes}} |
4184
+
4185
+ ## Key Abstractions
4186
+ - **{{abstraction-1}}**: {{description}}
4187
+
4188
+ ## Data Flow
4189
+ {{data-flow}}
4190
+ `;
4191
+ var CODEBASE_CONVENTIONS_TEMPLATE = `# Conventions: {{name}}
4192
+
4193
+ > Codebase analysis \u2014 coding conventions identified from brownfield scan.
4194
+
4195
+ ---
4196
+
4197
+ ## Naming
4198
+ - **Files**: {{file-naming}}
4199
+ - **Variables**: {{var-naming}}
4200
+ - **Functions**: {{func-naming}}
4201
+ - **Types/Interfaces**: {{type-naming}}
4202
+
4203
+ ## Code Style
4204
+ - **Indentation**: {{indent}}
4205
+ - **Quotes**: {{quotes}}
4206
+ - **Semicolons**: {{semicolons}}
4207
+ - **Max line length**: {{max-line}}
4208
+
4209
+ ## Import Style
4210
+ - **Pattern**: {{import-pattern}}
4211
+ - **Path aliases**: {{aliases}}
4212
+
4213
+ ## Testing Conventions
4214
+ - **Test location**: {{test-location}}
4215
+ - **Test naming**: {{test-naming}}
4216
+ - **Test framework patterns**: {{test-patterns}}
4217
+ `;
4218
+ var CODEBASE_PITFALLS_TEMPLATE = `# Pitfalls: {{name}}
4219
+
4220
+ > Codebase analysis \u2014 known pitfalls, anti-patterns, and technical debt identified from brownfield scan.
4221
+
4222
+ ---
4223
+
4224
+ ## Anti-Patterns
4225
+ | Pattern | Location | Risk |
4226
+ |---------|----------|------|
4227
+ | {{anti-1}} | {{location}} | {{risk}} |
4228
+
4229
+ ## Technical Debt
4230
+ | Item | Impact | Effort to Fix |
4231
+ |------|--------|--------------|
4232
+ | {{debt-1}} | {{impact}} | {{effort}} |
4233
+
4234
+ ## Risky Areas
4235
+ | Area | Why Risky | Mitigation |
4236
+ |------|----------|-----------|
4237
+ | {{area-1}} | {{reason}} | {{mitigation}} |
4238
+
4239
+ ## Dependencies at Risk
4240
+ | Dependency | Version | Latest | Risk |
4241
+ |-----------|---------|--------|------|
4242
+ | {{dep-1}} | {{version}} | {{latest}} | {{risk}} |
4243
+ `;
4244
+ var PHASE_RESEARCH_TEMPLATE = `# Phase Research: {{name}}
4245
+
4246
+ > Implementation path investigation for a specific phase.
4247
+
4248
+ ---
4249
+
4250
+ ## Research Scope
4251
+ {{scope}}
4252
+
4253
+ ## Recommended Approach
4254
+ **Recommendation**: {{recommendation}}
4255
+
4256
+ **Rationale**: {{rationale}}
4257
+
4258
+ ## Alternatives Considered
4259
+ | Approach | Pros | Cons | Verdict |
4260
+ |----------|------|------|--------|
4261
+ | {{alt-1}} | {{pros}} | {{cons}} | {{verdict}} |
4262
+
4263
+ ## Known Pitfalls
4264
+ - {{pitfall-1}}
4265
+ - {{pitfall-2}}
4266
+
4267
+ ## TDD Implications
4268
+ - {{tdd-note-1}}
4269
+ `;
4270
+ var ARTIFACT_TEMPLATES = {
4271
+ proposal: PROPOSAL_TEMPLATE,
4272
+ design: DESIGN_TEMPLATE,
4273
+ tasks: TASKS_TEMPLATE,
4274
+ context: CONTEXT_TEMPLATE,
4275
+ research: RESEARCH_TEMPLATE,
4276
+ summary: SUMMARY_TEMPLATE,
4277
+ verification: VERIFICATION_TEMPLATE,
4278
+ "spec-review": SPEC_REVIEW_TEMPLATE,
4279
+ "quality-review": QUALITY_REVIEW_TEMPLATE,
4280
+ "goal-review": GOAL_REVIEW_TEMPLATE,
4281
+ "change-summary": CHANGE_SUMMARY_TEMPLATE,
4282
+ // Codebase analysis (brownfield — produced by specwf-codebase-mapper)
4283
+ "codebase-stack": CODEBASE_STACK_TEMPLATE,
4284
+ "codebase-architecture": CODEBASE_ARCHITECTURE_TEMPLATE,
4285
+ "codebase-conventions": CODEBASE_CONVENTIONS_TEMPLATE,
4286
+ "codebase-pitfalls": CODEBASE_PITFALLS_TEMPLATE,
4287
+ // Phase research (produced by specwf-phase-researcher)
4288
+ "phase-research": PHASE_RESEARCH_TEMPLATE
4289
+ };
4290
+ var TEMPLATE_IDS = Object.keys(ARTIFACT_TEMPLATES);
4291
+
4292
+ // src/commands/specwf-template.ts
4293
+ function register9(program2) {
4294
+ program2.command("template <type>").description(`Generate template file (${TEMPLATE_IDS.join("|")})`).option("--name <name>", "change name", "my-change").option("--dir <path>", "target directory (default specwf/changes/<name>/)").action(templateHandler);
4295
+ }
4296
+ function templateHandler(type, options) {
4297
+ const template = ARTIFACT_TEMPLATES[type];
4298
+ if (!template) {
4299
+ console.error(`Unknown template type: ${type}. Available: ${TEMPLATE_IDS.join(", ")}`);
4300
+ process.exit(1);
4301
+ }
4302
+ let content = template;
4303
+ const name = options.name;
4304
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4305
+ content = content.replace(/\{\{name\}\}/g, name);
4306
+ content = content.replace(/\{\{date\}\}/g, date);
4307
+ const filenames = {
4308
+ proposal: "proposal.md",
4309
+ design: "design.md",
4310
+ tasks: "tasks.md",
4311
+ context: "context.md",
4312
+ research: "research.md",
4313
+ summary: "summary.md",
4314
+ verification: "verification.md",
4315
+ "spec-review": "spec-review.md",
4316
+ "quality-review": "quality-review.md",
4317
+ "goal-review": "goal-review.md",
4318
+ "change-summary": "change-summary.md",
4319
+ "codebase-stack": "codebase/stack.md",
4320
+ "codebase-architecture": "codebase/architecture.md",
4321
+ "codebase-conventions": "codebase/conventions.md",
4322
+ "codebase-pitfalls": "codebase/pitfalls.md",
4323
+ "phase-research": "phase-research.md"
4324
+ };
4325
+ const filename = filenames[type] ?? `${type}.md`;
4326
+ let targetDir;
4327
+ if (options.dir) {
4328
+ targetDir = options.dir.startsWith("/") ? options.dir : join17(process.cwd(), options.dir);
4329
+ } else {
4330
+ targetDir = join17(process.cwd(), "specwf", "changes", name);
4331
+ }
4332
+ mkdirSync7(targetDir, { recursive: true });
4333
+ const fullPath = join17(targetDir, filename);
4334
+ const fullDir = dirname2(fullPath);
4335
+ if (fullDir !== targetDir) {
4336
+ mkdirSync7(fullDir, { recursive: true });
4337
+ }
4338
+ writeFileSync9(fullPath, content, "utf-8");
4339
+ console.log(`\u2713 Created ${fullPath}`);
4340
+ }
4341
+
4342
+ // src/commands/specwf-change.ts
4343
+ import { join as join18 } from "path";
4344
+ import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync10 } from "fs";
4345
+ function register10(program2) {
4346
+ const cmd = program2.command("change").description("Manage changes (create/list)");
4347
+ cmd.command("new <name>").description("Create a change. Default: fast path (all artifacts). Use --full for full plan cycle.").option("--dir <path>", "specwf directory", "specwf").option("--full", "Full cycle: proposal only, goes through plan phase").option("--intent <text>", "one-line intent for the proposal").action(newChange);
4348
+ cmd.action(() => {
4349
+ console.log("Usage: specwf change new <name> [--full] [--intent <text>]");
4350
+ });
4351
+ }
4352
+ function newChange(name, options) {
4353
+ const specwfDir = join18(process.cwd(), options.dir);
4354
+ const changeDir = createAdhocChangeDir(specwfDir, name);
4355
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4356
+ if (options.full) {
4357
+ const content = (ARTIFACT_TEMPLATES["proposal"] ?? `# Proposal: ${name}
4358
+ `).replace(/\{\{name\}\}/g, name).replace(/\{\{date\}\}/g, date);
4359
+ writeFileSync10(join18(changeDir, "proposal.md"), content, "utf-8");
4360
+ console.log(`\u2713 Created change: changes/${name}/ (full cycle)`);
4361
+ console.log(` proposal.md \u2014 fill in, then plan \u2192 apply \u2192 review \u2192 verify \u2192 archive`);
4362
+ updateState(specwfDir, (state) => {
4363
+ state.adhoc.push({ name, status: "proposal", depends_on: [] });
4364
+ });
4365
+ } else {
4366
+ const templates = {
4367
+ "proposal.md": (ARTIFACT_TEMPLATES["proposal"] ?? `# Proposal: ${name}
4368
+ `).replace(/\{\{name\}\}/g, name).replace(/\{\{date\}\}/g, date).replace("{{intent}}", options.intent || "{{intent}}"),
4369
+ "design.md": (ARTIFACT_TEMPLATES["design"] ?? `# Design: ${name}
4370
+ `).replace(/\{\{name\}\}/g, name).replace(/\{\{date\}\}/g, date),
4371
+ "tasks.md": (ARTIFACT_TEMPLATES["tasks"] ?? `# Tasks: ${name}
4372
+ `).replace(/\{\{name\}\}/g, name).replace(/\{\{date\}\}/g, date)
4373
+ };
4374
+ for (const [filename, content] of Object.entries(templates)) {
4375
+ writeFileSync10(join18(changeDir, filename), content, "utf-8");
4376
+ }
4377
+ const specsDir = join18(changeDir, "specs", name);
4378
+ mkdirSync8(specsDir, { recursive: true });
4379
+ writeFileSync10(
4380
+ join18(specsDir, "spec.md"),
4381
+ `# ${name} \u2014 Delta Spec
4382
+
4383
+ ## ADDED Requirements
4384
+
4385
+ ### Requirement: <name>
4386
+
4387
+ The system SHALL <behavior>.
4388
+
4389
+ #### Scenario: <name>
4390
+ - **GIVEN** <precondition>
4391
+ - **WHEN** <trigger>
4392
+ - **THEN** <expected outcome>
4393
+ `,
4394
+ "utf-8"
4395
+ );
4396
+ console.log(`\u2713 Created change: changes/${name}/ (fast path)`);
4397
+ console.log(` proposal.md \u2014 fill in intent, scope, must-haves`);
4398
+ console.log(` design.md \u2014 fill in technical approach`);
4399
+ console.log(` tasks.md \u2014 fill in implementation tasks`);
4400
+ console.log(` specs/${name}/spec.md \u2014 fill in behavioral contracts`);
4401
+ updateState(specwfDir, (state) => {
4402
+ state.adhoc.push({ name, status: "planning", depends_on: [] });
4403
+ });
4404
+ }
4405
+ console.log(`\u2713 state.md updated`);
4406
+ console.log("");
4407
+ console.log(`\u2192 Next: fill in artifacts, then \`specwf continue change ${name}\``);
4408
+ }
4409
+
4410
+ // src/cli.ts
4411
+ var __dirname2 = dirname3(fileURLToPath2(import.meta.url));
4412
+ var pkg = JSON.parse(readFileSync12(join19(__dirname2, "..", "package.json"), "utf-8"));
4413
+ var version = pkg.version;
4414
+ program.name("specwf").description("\u89C4\u683C\u9A71\u52A8\u5F00\u53D1\u5DE5\u4F5C\u6D41 \u2014 spec-driven development workflow").version(version);
4415
+ register(program);
4416
+ register2(program);
4417
+ register3(program);
4418
+ register4(program);
4419
+ register5(program);
4420
+ register6(program);
4421
+ register7(program);
4422
+ register8(program);
4423
+ register9(program);
4424
+ register10(program);
4425
+ program.parse(process.argv);