@zhuan-ai/zhuanspec 2.2.4 → 2.4.8

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 (103) hide show
  1. package/bin/zhuanspec-hook.js +3 -0
  2. package/dist/cli/hooks.d.ts +14 -0
  3. package/dist/cli/hooks.js +465 -0
  4. package/dist/cli/index.js +100 -0
  5. package/dist/commands/artifact-workflow.js +15 -34
  6. package/dist/commands/design.d.ts +42 -0
  7. package/dist/commands/design.js +337 -0
  8. package/dist/commands/progress.d.ts +32 -0
  9. package/dist/commands/progress.js +278 -0
  10. package/dist/commands/review.d.ts +32 -0
  11. package/dist/commands/review.js +472 -0
  12. package/dist/commands/validate.d.ts +14 -0
  13. package/dist/commands/validate.js +161 -10
  14. package/dist/core/archive.d.ts +1 -0
  15. package/dist/core/archive.js +45 -3
  16. package/dist/core/completions/command-registry.js +67 -0
  17. package/dist/core/configurators/slash/amazon-q.js +32 -2
  18. package/dist/core/configurators/slash/antigravity.js +8 -2
  19. package/dist/core/configurators/slash/auggie.js +16 -1
  20. package/dist/core/configurators/slash/base.js +1 -1
  21. package/dist/core/configurators/slash/claude.d.ts +4 -0
  22. package/dist/core/configurators/slash/claude.js +56 -1
  23. package/dist/core/configurators/slash/cline.js +8 -2
  24. package/dist/core/configurators/slash/codebuddy.js +22 -1
  25. package/dist/core/configurators/slash/codex.js +21 -0
  26. package/dist/core/configurators/slash/costrict.js +15 -0
  27. package/dist/core/configurators/slash/crush.js +22 -1
  28. package/dist/core/configurators/slash/cursor.js +22 -1
  29. package/dist/core/configurators/slash/factory.js +16 -1
  30. package/dist/core/configurators/slash/gemini.js +8 -2
  31. package/dist/core/configurators/slash/github-copilot.js +19 -1
  32. package/dist/core/configurators/slash/iflow.js +22 -1
  33. package/dist/core/configurators/slash/kilocode.js +4 -1
  34. package/dist/core/configurators/slash/opencode.js +27 -0
  35. package/dist/core/configurators/slash/qoder.d.ts +4 -0
  36. package/dist/core/configurators/slash/qoder.js +59 -1
  37. package/dist/core/configurators/slash/qwen.js +8 -2
  38. package/dist/core/configurators/slash/roocode.js +8 -2
  39. package/dist/core/configurators/slash/windsurf.js +8 -2
  40. package/dist/core/dashboard/metrics.d.ts +33 -0
  41. package/dist/core/dashboard/metrics.js +114 -0
  42. package/dist/core/hooks/collect-knowledge.d.ts +16 -0
  43. package/dist/core/hooks/collect-knowledge.js +203 -0
  44. package/dist/core/hooks/context-load-hook.d.ts +25 -0
  45. package/dist/core/hooks/context-load-hook.js +159 -0
  46. package/dist/core/hooks/deviation-check.d.ts +27 -0
  47. package/dist/core/hooks/deviation-check.js +403 -0
  48. package/dist/core/hooks/deviation-handler.d.ts +43 -0
  49. package/dist/core/hooks/deviation-handler.js +98 -0
  50. package/dist/core/hooks/init.d.ts +14 -0
  51. package/dist/core/hooks/init.js +244 -0
  52. package/dist/core/hooks/notify-milestone.d.ts +14 -0
  53. package/dist/core/hooks/notify-milestone.js +170 -0
  54. package/dist/core/hooks/post-apply.d.ts +29 -0
  55. package/dist/core/hooks/post-apply.js +173 -0
  56. package/dist/core/hooks/post-archive.d.ts +7 -0
  57. package/dist/core/hooks/post-archive.js +208 -0
  58. package/dist/core/hooks/pre-apply.d.ts +34 -0
  59. package/dist/core/hooks/pre-apply.js +139 -0
  60. package/dist/core/hooks/pre-archive.d.ts +7 -0
  61. package/dist/core/hooks/pre-archive.js +50 -0
  62. package/dist/core/hooks/record-progress.d.ts +49 -0
  63. package/dist/core/hooks/record-progress.js +494 -0
  64. package/dist/core/hooks/review-hooks.d.ts +89 -0
  65. package/dist/core/hooks/review-hooks.js +345 -0
  66. package/dist/core/hooks/review-orchestrator.d.ts +40 -0
  67. package/dist/core/hooks/review-orchestrator.js +146 -0
  68. package/dist/core/hooks/summarize.d.ts +15 -0
  69. package/dist/core/hooks/summarize.js +282 -0
  70. package/dist/core/hooks/user-input-hook.d.ts +25 -0
  71. package/dist/core/hooks/user-input-hook.js +179 -0
  72. package/dist/core/init.d.ts +8 -0
  73. package/dist/core/init.js +251 -23
  74. package/dist/core/parsers/requirement-blocks.js +13 -10
  75. package/dist/core/templates/agents-template.d.ts +1 -1
  76. package/dist/core/templates/agents-template.js +510 -243
  77. package/dist/core/templates/index.d.ts +1 -0
  78. package/dist/core/templates/index.js +1 -0
  79. package/dist/core/templates/skill-templates.js +46 -152
  80. package/dist/core/templates/slash-command-templates.d.ts +1 -1
  81. package/dist/core/templates/slash-command-templates.js +352 -20
  82. package/dist/core/templates/tasks-template.d.ts +7 -0
  83. package/dist/core/templates/tasks-template.js +130 -24
  84. package/dist/core/templates/tdd-tasks-template.d.ts +3 -0
  85. package/dist/core/templates/tdd-tasks-template.js +91 -38
  86. package/dist/core/templates/test-cases-template.d.ts +41 -0
  87. package/dist/core/templates/test-cases-template.js +128 -0
  88. package/dist/core/validation/strict-rules.d.ts +44 -5
  89. package/dist/core/validation/strict-rules.js +302 -8
  90. package/dist/core/validation/validator.js +52 -2
  91. package/dist/core/view.d.ts +1 -0
  92. package/dist/core/view.js +60 -2
  93. package/dist/mcp/index.d.ts +28 -0
  94. package/dist/mcp/index.js +31 -0
  95. package/dist/utils/file-system.d.ts +1 -0
  96. package/dist/utils/file-system.js +11 -0
  97. package/dist/utils/item-discovery.js +24 -2
  98. package/dist/utils/phase-utils.d.ts +36 -0
  99. package/dist/utils/phase-utils.js +117 -0
  100. package/package.json +22 -23
  101. package/schemas/spec-driven/schema.yaml +45 -31
  102. package/schemas/spec-driven/templates/spec.md +142 -5
  103. package/schemas/spec-driven/templates/tasks.md +73 -9
@@ -475,25 +475,17 @@ async function generateApplyInstructions(projectRoot, changeName, schemaName) {
475
475
  }
476
476
  else if (tracksFile && remaining === 0 && total > 0) {
477
477
  state = 'all_done';
478
- instruction = `All tasks are complete! This change is ready for code review or archiving.
478
+ instruction = `All tasks are complete. Review should auto-run once after apply completion.
479
479
 
480
- **Next Step: Code Review (Optional)**
480
+ **Required Next Steps**
481
481
 
482
- When all tasks are complete, you should:
483
- 1. Use the **AskUserQuestion tool** to ask the user: "所有任务已完成。是否进行代码审查?" (or "All tasks are complete. Would you like to perform a code review?")
484
- 2. Provide options:
485
- - "是,进行代码审查" / "Yes, perform code review"
486
- - "跳过,直接归档" / "Skip, proceed to archive"
487
- 3. Wait for user selection before proceeding.
482
+ 1. Verify auto review report exists (\`metrics/review.json\`, \`metrics/tests.json\`)
483
+ 2. Ensure unit tests pass in the review report
484
+ 3. Ensure critical issues = 0
485
+ 4. If auto review failed/missing, run \`zhuanspec review ${changeName}\` manually
486
+ 5. Then continue to \`zhuanspec archive ${changeName}\`
488
487
 
489
- If the user selects code review, review the implemented code for:
490
- - Code standards and conventions compliance
491
- - Logic correctness
492
- - Performance issues
493
- - Security concerns
494
- - Best practices adherence
495
-
496
- See the apply command template for detailed code review workflow instructions.`;
488
+ Archive may be blocked if review/tests are missing or failed.`;
497
489
  }
498
490
  else if (!tracksFile) {
499
491
  // No tracking file (e.g., TDD schema) - ready to apply
@@ -596,27 +588,16 @@ function printApplyInstructionsText(instructions) {
596
588
  // Code review prompt for all_done state
597
589
  if (state === 'all_done') {
598
590
  console.log();
599
- console.log('### 📋 Code Review (Optional)');
591
+ console.log('### 📋 Code Review (Auto + Required)');
600
592
  console.log();
601
593
  console.log('All tasks are complete. You should now:');
602
594
  console.log();
603
- console.log('1. **Prompt for code review** using the **AskUserQuestion tool**:');
604
- console.log(' - Ask: "所有任务已完成。是否进行代码审查?" (or "All tasks are complete. Would you like to perform a code review?")');
605
- console.log(' - Provide options:');
606
- console.log(' "是,进行代码审查" / "Yes, perform code review"');
607
- console.log(' "跳过,直接归档" / "Skip, proceed to archive"');
608
- console.log(' - Wait for user selection before proceeding');
609
- console.log();
610
- console.log('2. **If user selects code review**, review the implemented code for:');
611
- console.log(' - Code standards and conventions compliance');
612
- console.log(' - Logic correctness');
613
- console.log(' - Performance issues');
614
- console.log(' - Security concerns');
615
- console.log(' - Best practices adherence');
616
- console.log();
617
- console.log('3. **After code review** (or if skipped), proceed to archive workflow.');
618
- console.log();
619
- console.log('See the apply command template for detailed code review workflow instructions.');
595
+ console.log('1. Auto review is triggered once when all apply tasks complete');
596
+ console.log('2. Confirm `metrics/review.json` and `metrics/tests.json` exist');
597
+ console.log('3. Ensure review report is PASS with no critical issues');
598
+ console.log('4. Ensure unit tests pass');
599
+ console.log('5. If auto review failed/missing, run `zhuanspec review <change-id>` manually');
600
+ console.log('6. Then proceed to archive workflow');
620
601
  }
621
602
  }
622
603
  async function templatesCommand(options) {
@@ -0,0 +1,42 @@
1
+ export declare class DesignCommand {
2
+ execute(changeName?: string, options?: {
3
+ force?: boolean;
4
+ initTemplate?: boolean;
5
+ change?: string;
6
+ desc?: string;
7
+ dashenPageId?: string;
8
+ dashenUrl?: string;
9
+ output?: string;
10
+ validate?: boolean;
11
+ autoFix?: boolean;
12
+ createChange?: boolean;
13
+ }): Promise<void>;
14
+ private writeTemplateToChange;
15
+ private resolveDesignContext;
16
+ private resolveOutputPath;
17
+ private buildSkillRequestDoc;
18
+ private bindDesignSourceToChange;
19
+ private selectChangeInteractively;
20
+ /**
21
+ * Validate design.md for required sections and optionally auto-fix
22
+ */
23
+ private validateDesign;
24
+ /**
25
+ * Auto-fix design content by adding missing section skeletons
26
+ */
27
+ private autoFixDesign;
28
+ /**
29
+ * Generate a kebab-case change name from design context
30
+ */
31
+ private generateChangeName;
32
+ /**
33
+ * Create a new change directory for techDesign phase
34
+ * - Creates change directory using createChange()
35
+ * - Creates .tech-design marker file
36
+ * - Initializes progress.json with phase=techDesign
37
+ * - Creates design.md skeleton
38
+ * - Creates doc/ request file
39
+ */
40
+ private createTechDesignChange;
41
+ }
42
+ //# sourceMappingURL=design.d.ts.map
@@ -0,0 +1,337 @@
1
+ import path from 'path';
2
+ import { promises as fs } from 'fs';
3
+ import { FileSystemUtils } from '../utils/file-system.js';
4
+ import { createChange } from '../utils/change-utils.js';
5
+ import { initializeProgress } from '../core/hooks/record-progress.js';
6
+ const DEFAULT_DESIGN_TEMPLATE = `## 背景
7
+ [背景、约束、利益相关者]
8
+
9
+ ## 目标 / 非目标
10
+ - Goals: [...]
11
+ - Non-Goals: [...]
12
+
13
+ ## 决策
14
+ - Decision: [内容和原因]
15
+ - Alternatives considered: [选项 + 理由]
16
+
17
+ ## 风险 / 权衡
18
+ - [风险] -> 缓解措施
19
+
20
+ ## 迁移计划
21
+ [步骤、回滚]
22
+
23
+ ## 待解决问题
24
+ - [...]
25
+ `;
26
+ export class DesignCommand {
27
+ async execute(changeName, options) {
28
+ const boundChange = options?.change?.trim() || changeName?.trim() || undefined;
29
+ // Create new change directory mode (techDesign phase)
30
+ if (options?.createChange && !boundChange) {
31
+ const designContext = await this.resolveDesignContext({
32
+ desc: options?.desc,
33
+ dashenPageId: options?.dashenPageId,
34
+ dashenUrl: options?.dashenUrl,
35
+ });
36
+ const newChangeName = await this.generateChangeName(designContext);
37
+ await this.createTechDesignChange(newChangeName, designContext);
38
+ return;
39
+ }
40
+ // Backward-compatible mode: generate empty design template in change directory.
41
+ if (options?.initTemplate) {
42
+ const targetChange = boundChange || (process.stdin.isTTY ? await this.selectChangeInteractively() : undefined);
43
+ if (!targetChange) {
44
+ throw new Error('No change selected. Usage: zhuanspec techDesign <change-id> --init-template');
45
+ }
46
+ await this.writeTemplateToChange(targetChange, !!options?.force);
47
+ return;
48
+ }
49
+ // Validation mode: validate existing design.md
50
+ if (options?.validate) {
51
+ const targetChange = boundChange || (process.stdin.isTTY ? await this.selectChangeInteractively() : undefined);
52
+ if (!targetChange) {
53
+ throw new Error('No change selected. Usage: zhuanspec techDesign <change-id> --validate');
54
+ }
55
+ await this.validateDesign(targetChange, !!options?.autoFix);
56
+ return;
57
+ }
58
+ const designContext = await this.resolveDesignContext({
59
+ desc: options?.desc,
60
+ dashenPageId: options?.dashenPageId,
61
+ dashenUrl: options?.dashenUrl,
62
+ });
63
+ if (!designContext.desc && !designContext.dashenPageId && !designContext.dashenUrl) {
64
+ throw new Error('Missing design source. Provide --desc / --dashen-page-id / --dashen-url to generate technical design.');
65
+ }
66
+ const outputPath = await this.resolveOutputPath(options?.output, boundChange);
67
+ const exists = await FileSystemUtils.fileExists(outputPath);
68
+ if (exists && !options?.force) {
69
+ throw new Error(`Output file already exists: ${outputPath}. Use --force to overwrite.`);
70
+ }
71
+ const content = this.buildSkillRequestDoc(designContext, outputPath, boundChange);
72
+ await FileSystemUtils.writeFile(outputPath, content);
73
+ console.log(`Design generation request created: ${outputPath}`);
74
+ console.log('Next step: run generate-tech-spec-md-skill in agent mode with this request.');
75
+ if (boundChange) {
76
+ await this.bindDesignSourceToChange(boundChange, outputPath, !!options?.force);
77
+ }
78
+ }
79
+ async writeTemplateToChange(changeName, force) {
80
+ const changeDir = path.join(process.cwd(), 'zhuanspec', 'changes', changeName);
81
+ if (!(await FileSystemUtils.directoryExists(changeDir))) {
82
+ throw new Error(`Change '${changeName}' not found in zhuanspec/changes`);
83
+ }
84
+ const designPath = path.join(changeDir, 'design.md');
85
+ const exists = await FileSystemUtils.fileExists(designPath);
86
+ if (exists && !force) {
87
+ console.log(`design.md already exists: ${designPath}`);
88
+ return;
89
+ }
90
+ await FileSystemUtils.writeFile(designPath, DEFAULT_DESIGN_TEMPLATE);
91
+ console.log(`Design template generated: ${designPath}`);
92
+ }
93
+ async resolveDesignContext(input) {
94
+ const result = {
95
+ desc: input.desc?.trim() ?? '',
96
+ dashenPageId: input.dashenPageId?.trim() ?? '',
97
+ dashenUrl: input.dashenUrl?.trim() ?? '',
98
+ };
99
+ if (result.desc || result.dashenPageId || result.dashenUrl || !process.stdin.isTTY) {
100
+ return result;
101
+ }
102
+ const { input: askInput } = await import('@inquirer/prompts');
103
+ const source = await askInput({
104
+ message: '请输入需求描述(或留空后继续输入 Dashen 信息)',
105
+ default: '',
106
+ });
107
+ result.desc = source.trim();
108
+ return result;
109
+ }
110
+ async resolveOutputPath(output, boundChange) {
111
+ if (output?.trim()) {
112
+ return path.isAbsolute(output) ? output : path.join(process.cwd(), output);
113
+ }
114
+ const docDir = path.join(process.cwd(), 'doc');
115
+ await FileSystemUtils.createDirectory(docDir);
116
+ const stamp = new Date().toISOString().slice(0, 10);
117
+ const fileName = boundChange
118
+ ? `${stamp}-${boundChange}-技术方案生成请求.md`
119
+ : `${stamp}-技术方案生成请求.md`;
120
+ return path.join(docDir, fileName);
121
+ }
122
+ buildSkillRequestDoc(context, outputPath, boundChange) {
123
+ return [
124
+ '# 技术方案生成请求',
125
+ '',
126
+ '## 目标',
127
+ '使用 `generate-tech-spec-md-skill` 基于以下输入生成完整技术方案文档。',
128
+ '',
129
+ '## 输入',
130
+ `- 需求描述: ${context.desc || '(未提供)'}`,
131
+ `- Dashen Page ID: ${context.dashenPageId || '(未提供)'}`,
132
+ `- Dashen URL: ${context.dashenUrl || '(未提供)'}`,
133
+ `- 绑定变更: ${boundChange || '(无,独立于提案阶段)'}`,
134
+ '',
135
+ '## 生成要求',
136
+ '- 使用 Dashen 模板优先,本地模板兜底',
137
+ '- 输出完整技术方案(含 Mermaid)',
138
+ '- 覆盖需求功能点并附带校验结果与修复记录',
139
+ '',
140
+ '## 输出建议',
141
+ `- 当前请求文件: ${outputPath}`,
142
+ '- 技术方案目标文件: doc/[项目名称]技术方案.md',
143
+ '',
144
+ ].join('\n');
145
+ }
146
+ async bindDesignSourceToChange(changeName, outputPath, force) {
147
+ const changeDir = path.join(process.cwd(), 'zhuanspec', 'changes', changeName);
148
+ if (!(await FileSystemUtils.directoryExists(changeDir))) {
149
+ throw new Error(`Change '${changeName}' not found in zhuanspec/changes`);
150
+ }
151
+ const designPath = path.join(changeDir, 'design.md');
152
+ const exists = await FileSystemUtils.fileExists(designPath);
153
+ if (exists && !force) {
154
+ console.log(`Skip binding to ${designPath} because it already exists. Use --force to overwrite binding.`);
155
+ return;
156
+ }
157
+ const relative = path.relative(changeDir, outputPath).replaceAll('\\', '/');
158
+ const binding = `# Design Source\n\n- source: \`${relative}\`\n- mode: generated-before-proposal\n`;
159
+ await FileSystemUtils.writeFile(designPath, binding);
160
+ console.log(`Bound design source to change: ${designPath}`);
161
+ }
162
+ async selectChangeInteractively() {
163
+ const changesDir = path.join(process.cwd(), 'zhuanspec', 'changes');
164
+ let entries = [];
165
+ try {
166
+ entries = await fs.readdir(changesDir, { withFileTypes: true });
167
+ }
168
+ catch {
169
+ throw new Error("No zhuanspec changes directory found. Run 'zhuanspec init' first.");
170
+ }
171
+ const changes = entries
172
+ .filter((entry) => entry.isDirectory() && entry.name !== 'archive' && !entry.name.startsWith('.'))
173
+ .map((entry) => entry.name)
174
+ .sort((a, b) => a.localeCompare(b));
175
+ if (changes.length === 0) {
176
+ throw new Error('No active changes found. Create one first with zhuanspec new change <name>.');
177
+ }
178
+ if (!process.stdin.isTTY) {
179
+ throw new Error(`Missing change name. Available changes: ${changes.join(', ')}`);
180
+ }
181
+ const { select } = await import('@inquirer/prompts');
182
+ try {
183
+ return await select({
184
+ message: '选择要生成 design 的变更',
185
+ choices: changes.map((id) => ({ name: id, value: id })),
186
+ });
187
+ }
188
+ catch {
189
+ return null;
190
+ }
191
+ }
192
+ /**
193
+ * Validate design.md for required sections and optionally auto-fix
194
+ */
195
+ async validateDesign(changeName, autoFix) {
196
+ const changeDir = path.join(process.cwd(), 'zhuanspec', 'changes', changeName);
197
+ const designPath = path.join(changeDir, 'design.md');
198
+ if (!await FileSystemUtils.fileExists(designPath)) {
199
+ throw new Error(`design.md not found in change '${changeName}'`);
200
+ }
201
+ const content = await FileSystemUtils.readFile(designPath);
202
+ const issues = [];
203
+ // Check required sections: 背景/目标/决策/风险
204
+ const requiredSections = [
205
+ { pattern: /^##\s+(背景|Background)/im, name: '背景/Background', skeleton: '## 背景\n\n[请补充背景、约束、利益相关者]\n' },
206
+ { pattern: /^##\s+(目标|Goals|非目标|Non-Goals)/im, name: '目标/Goals', skeleton: '## 目标 / 非目标\n\n- Goals: [...]\n- Non-Goals: [...]\n' },
207
+ { pattern: /^##\s+(决策|Decision)/im, name: '决策/Decision', skeleton: '## 决策\n\n- Decision: [内容和原因]\n- Alternatives considered: [选项 + 理由]\n' },
208
+ { pattern: /^##\s+(风险|权衡|Risks)/im, name: '风险/Risks', skeleton: '## 风险 / 权衡\n\n- [风险] -> 缓解措施\n' },
209
+ ];
210
+ for (const section of requiredSections) {
211
+ if (!section.pattern.test(content)) {
212
+ issues.push({
213
+ type: 'missing-section',
214
+ section: section.name,
215
+ skeleton: section.skeleton,
216
+ });
217
+ }
218
+ }
219
+ // Check if content has placeholder markers (needs completion)
220
+ const placeholderPatterns = [
221
+ /\[请补充[^\]]*\]/g,
222
+ /\[\.\.\.\]/g,
223
+ /\[待[^\]]*\]/g,
224
+ ];
225
+ const placeholderCount = placeholderPatterns.reduce((count, pattern) => {
226
+ const matches = content.match(pattern) || [];
227
+ return count + matches.length;
228
+ }, 0);
229
+ if (placeholderCount > 5) {
230
+ issues.push({
231
+ type: 'placeholders-excessive',
232
+ count: placeholderCount,
233
+ });
234
+ }
235
+ // Report validation results
236
+ if (issues.length === 0) {
237
+ console.log(`✓ Design validation passed for ${changeName}`);
238
+ return;
239
+ }
240
+ console.log(`✗ Design validation failed for ${changeName}:`);
241
+ for (const issue of issues) {
242
+ if (issue.type === 'missing-section') {
243
+ console.log(` - Missing section: ${issue.section}`);
244
+ }
245
+ else if (issue.type === 'placeholders-excessive') {
246
+ console.log(` - Excessive placeholders: ${issue.count} placeholders detected`);
247
+ }
248
+ }
249
+ // Auto-fix missing sections
250
+ if (autoFix) {
251
+ const fixedContent = this.autoFixDesign(content, issues);
252
+ await FileSystemUtils.writeFile(designPath, fixedContent);
253
+ console.log(`✓ Auto-fixed ${issues.filter(i => i.type === 'missing-section').length} missing sections`);
254
+ }
255
+ else {
256
+ console.log('Tip: Use --auto-fix to add missing section skeletons');
257
+ throw new Error('Design validation failed');
258
+ }
259
+ }
260
+ /**
261
+ * Auto-fix design content by adding missing section skeletons
262
+ */
263
+ autoFixDesign(content, issues) {
264
+ let result = content;
265
+ for (const issue of issues) {
266
+ if (issue.type === 'missing-section' && issue.skeleton) {
267
+ // Add skeleton at end of document
268
+ result = result.trimEnd() + '\n\n' + issue.skeleton;
269
+ }
270
+ }
271
+ return result;
272
+ }
273
+ /**
274
+ * Generate a kebab-case change name from design context
275
+ */
276
+ async generateChangeName(context) {
277
+ let base;
278
+ if (context.desc) {
279
+ base = context.desc;
280
+ }
281
+ else if (context.dashenPageId) {
282
+ base = `tech-design-${context.dashenPageId}`;
283
+ }
284
+ else if (context.dashenUrl) {
285
+ // Extract page ID from URL if possible
286
+ const match = context.dashenUrl.match(/pages\/(\d+)/);
287
+ base = match ? `tech-design-${match[1]}` : `tech-design-${Date.now()}`;
288
+ }
289
+ else {
290
+ base = `tech-design-${Date.now()}`;
291
+ }
292
+ // Convert to kebab-case
293
+ const kebabName = base
294
+ .toLowerCase()
295
+ .replace(/[^\w\s-]/g, '') // Remove non-word chars except spaces and hyphens
296
+ .replace(/\s+/g, '-') // Replace spaces with hyphens
297
+ .replace(/-+/g, '-') // Replace multiple hyphens with single
298
+ .slice(0, 40) // Limit length
299
+ .replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
300
+ // Ensure unique name
301
+ const changesDir = path.join(process.cwd(), 'zhuanspec', 'changes');
302
+ if (await FileSystemUtils.directoryExists(path.join(changesDir, kebabName))) {
303
+ return `${kebabName}-${Date.now()}`;
304
+ }
305
+ return kebabName;
306
+ }
307
+ /**
308
+ * Create a new change directory for techDesign phase
309
+ * - Creates change directory using createChange()
310
+ * - Creates .tech-design marker file
311
+ * - Initializes progress.json with phase=techDesign
312
+ * - Creates design.md skeleton
313
+ * - Creates doc/ request file
314
+ */
315
+ async createTechDesignChange(changeName, context) {
316
+ const projectRoot = process.cwd();
317
+ // 1. Create change directory
318
+ await createChange(projectRoot, changeName, { schema: 'spec-driven' });
319
+ const changeDir = path.join(projectRoot, 'zhuanspec', 'changes', changeName);
320
+ // 2. Initialize progress.json with phase=techDesign
321
+ await initializeProgress(changeName, 'techDesign');
322
+ // 3. Create .tech-design marker file
323
+ await FileSystemUtils.writeFile(path.join(changeDir, '.tech-design'), `created: ${new Date().toISOString()}\nsource: ${context.desc || context.dashenPageId || 'manual'}\n`);
324
+ // 4. Create design.md skeleton
325
+ await FileSystemUtils.writeFile(path.join(changeDir, 'design.md'), DEFAULT_DESIGN_TEMPLATE);
326
+ // 5. Create doc/ request file (keep compatibility)
327
+ const docDir = path.join(projectRoot, 'doc');
328
+ await FileSystemUtils.createDirectory(docDir);
329
+ const requestPath = path.join(docDir, `${changeName}-技术方案生成请求.md`);
330
+ await FileSystemUtils.writeFile(requestPath, this.buildSkillRequestDoc(context, requestPath, changeName));
331
+ console.log(`\n✓ TechDesign change created: zhuanspec/changes/${changeName}/`);
332
+ console.log(`✓ Phase: techDesign (run /zhuanspec:proposal to continue)`);
333
+ console.log(`✓ Design skeleton: ${changeDir}/design.md`);
334
+ console.log(`✓ Request file: ${requestPath}\n`);
335
+ }
336
+ }
337
+ //# sourceMappingURL=design.js.map
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Progress Command - Display current execution status
3
+ *
4
+ * This command reads progress.json and tasks.md to show:
5
+ * - Current phase and wave status
6
+ * - Task completion percentage with visual progress bar
7
+ * - Estimated remaining time
8
+ */
9
+ export declare class ProgressCommand {
10
+ /**
11
+ * Show progress for a specific change
12
+ * Usage: zhuanspec progress show <change-id>
13
+ */
14
+ show(changeId: string, options?: {
15
+ json?: boolean;
16
+ }): Promise<void>;
17
+ execute(options?: {
18
+ json?: boolean;
19
+ }): Promise<void>;
20
+ private findMostRecentChange;
21
+ private parseTasks;
22
+ private computeWaves;
23
+ private outputJson;
24
+ private outputFormatted;
25
+ private generateProgressBar;
26
+ /**
27
+ * Set phase for a change
28
+ * Usage: zhuanspec progress set-phase <change-id> <phase>
29
+ */
30
+ setPhase(changeId: string, phase: string): Promise<void>;
31
+ }
32
+ //# sourceMappingURL=progress.d.ts.map