@zhuan-ai/zhuanspec 2.0.0 → 2.1.1

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 (33) hide show
  1. package/README.zh.md +1 -1
  2. package/dist/cli/index.js +1 -1
  3. package/dist/commands/artifact-workflow.js +22 -2
  4. package/dist/commands/validate.d.ts +5 -0
  5. package/dist/commands/validate.js +65 -3
  6. package/dist/core/skill-discovery.d.ts +2 -2
  7. package/dist/core/skill-discovery.js +16 -3
  8. package/dist/core/task-graph/index.d.ts +2 -0
  9. package/dist/core/task-graph/index.js +2 -0
  10. package/dist/core/task-graph/mermaid-renderer.d.ts +22 -0
  11. package/dist/core/task-graph/mermaid-renderer.js +128 -0
  12. package/dist/core/templates/agents-template.d.ts +1 -1
  13. package/dist/core/templates/agents-template.js +60 -37
  14. package/dist/core/templates/skill-templates.js +42 -0
  15. package/dist/core/templates/slash-command-templates.js +25 -2
  16. package/dist/core/templates/tasks-template.d.ts +23 -0
  17. package/dist/core/templates/tasks-template.js +79 -0
  18. package/dist/core/templates/tdd-tasks-template.d.ts +24 -0
  19. package/dist/core/templates/tdd-tasks-template.js +116 -0
  20. package/dist/core/validation/strict-rules.d.ts +60 -0
  21. package/dist/core/validation/strict-rules.js +287 -0
  22. package/dist/core/validation/types.d.ts +10 -0
  23. package/dist/core/validation/validator.d.ts +5 -0
  24. package/dist/core/validation/validator.js +103 -1
  25. package/package.json +1 -1
  26. package/schemas/spec-driven/schema.yaml +13 -13
  27. package/schemas/spec-driven/templates/design.md +6 -6
  28. package/schemas/spec-driven/templates/proposal.md +7 -7
  29. package/schemas/spec-driven/templates/spec.md +4 -4
  30. package/schemas/tdd/schema.yaml +107 -107
  31. package/schemas/tdd/templates/implementation.md +5 -5
  32. package/schemas/tdd/templates/spec.md +6 -6
  33. package/schemas/tdd/templates/test.md +7 -7
@@ -0,0 +1,287 @@
1
+ /**
2
+ * Strict Validation Rules
3
+ *
4
+ * Implementation of three strict validation rules for `zhuanspec validate --strict`:
5
+ * 1. pre-clarification-completed: Check Pre-Clarification Log section
6
+ * 2. skill-tags-valid: Validate @skill tags against known skills
7
+ * 3. task-ordering-by-wave: Verify task ordering follows wave structure
8
+ */
9
+ import { generateExecutionPlan } from '../task-graph/execution-planner.js';
10
+ // Patterns for detecting unresolved markers
11
+ const UNRESOLVED_MARKERS = /\b(TODO|TBD|PENDING|\?)\b/i;
12
+ const HTML_COMMENT_PATTERN = /<!--[\s\S]*?-->/g;
13
+ const PRE_CLARIFICATION_SECTION_PATTERN = /^##\s+Pre-Clarification\s+Log\s*$/im;
14
+ const VALID_COMPLETION_MARKERS = [
15
+ /no\s+clarification\s+needed/i,
16
+ /status:\s*completed/i,
17
+ ];
18
+ // Skill mapping section header pattern
19
+ const SKILL_MAPPING_SECTION_PATTERN = /^##\s+Skill\s+Mapping\s*$/im;
20
+ // Table data row pattern (starts with |, not a separator row like |---|---|)
21
+ const TABLE_DATA_ROW_PATTERN = /^\|(?![-:|\s]+\|$)[^\n]+\|/gm;
22
+ const SKILL_TAG_PATTERN = /@skill:([^\s@]+)/g;
23
+ // Wave header pattern
24
+ const WAVE_HEADER_PATTERN = /^###\s+Wave\s+(\d+)/im;
25
+ /**
26
+ * Rule 1: pre-clarification-completed
27
+ *
28
+ * Checks that tasks.md has a valid "## Pre-Clarification Log" section.
29
+ * Valid content includes:
30
+ * - Actual Q&A record text
31
+ * - "No clarification needed"
32
+ * - "Status: COMPLETED"
33
+ * Invalid:
34
+ * - Section missing
35
+ * - Section empty (whitespace only)
36
+ * - Only HTML comments
37
+ * - Contains "TODO", "TBD", "PENDING", "?" unresolved markers
38
+ */
39
+ export function checkPreClarification(tasksContent) {
40
+ const result = {
41
+ ruleId: 'pre-clarification-completed',
42
+ ruleName: 'Pre-clarification completed',
43
+ passed: false,
44
+ errors: [],
45
+ warnings: [],
46
+ };
47
+ // Check if section exists
48
+ const sectionMatch = tasksContent.match(PRE_CLARIFICATION_SECTION_PATTERN);
49
+ if (!sectionMatch) {
50
+ result.errors.push('Pre-Clarification Log section is missing');
51
+ return result;
52
+ }
53
+ // Extract section content (from header to next ## or end)
54
+ const sectionStartIndex = sectionMatch.index + sectionMatch[0].length;
55
+ const nextSectionMatch = tasksContent.slice(sectionStartIndex).match(/^##\s+/m);
56
+ const sectionEndIndex = nextSectionMatch
57
+ ? sectionStartIndex + nextSectionMatch.index
58
+ : tasksContent.length;
59
+ let sectionContent = tasksContent.slice(sectionStartIndex, sectionEndIndex);
60
+ // Remove HTML comments for content analysis
61
+ const contentWithoutComments = sectionContent.replace(HTML_COMMENT_PATTERN, '').trim();
62
+ // Check if section is empty
63
+ if (!contentWithoutComments) {
64
+ result.errors.push('Pre-Clarification Log section is empty or contains only HTML comments');
65
+ return result;
66
+ }
67
+ // Check for unresolved markers
68
+ if (UNRESOLVED_MARKERS.test(contentWithoutComments)) {
69
+ result.errors.push('Pre-Clarification Log contains unresolved markers (TODO, TBD, PENDING, or ?)');
70
+ return result;
71
+ }
72
+ // Check for valid completion markers or actual content
73
+ const hasValidCompletionMarker = VALID_COMPLETION_MARKERS.some(pattern => pattern.test(contentWithoutComments));
74
+ const hasSubstantiveContent = contentWithoutComments.length > 20; // More than just a few words
75
+ if (hasValidCompletionMarker || hasSubstantiveContent) {
76
+ result.passed = true;
77
+ }
78
+ else {
79
+ result.errors.push('Pre-Clarification Log does not contain valid completion markers or substantive Q&A content');
80
+ }
81
+ return result;
82
+ }
83
+ /**
84
+ * Rule 2: skill-tags-valid
85
+ *
86
+ * Validates:
87
+ * a. Skill Mapping table exists with at least one data row
88
+ * b. Each task has @skill:xxx tag or @skill:none
89
+ * c. @skill:none tasks should have a reason in description
90
+ * d. All @skill:xxx names must exist in knownSkillNames
91
+ * e. Comma-separated multi-skills are validated individually
92
+ */
93
+ export function checkSkillTagsValid(tasksContent, parsedTasks, knownSkillNames) {
94
+ const result = {
95
+ ruleId: 'skill-tags-valid',
96
+ ruleName: 'Skill tags valid',
97
+ passed: false,
98
+ errors: [],
99
+ warnings: [],
100
+ };
101
+ // a. Check Skill Mapping section exists with table data
102
+ const sectionMatch = tasksContent.match(SKILL_MAPPING_SECTION_PATTERN);
103
+ if (!sectionMatch) {
104
+ result.warnings.push('Skill Mapping section not found');
105
+ }
106
+ else {
107
+ // Extract section content (from header to next ## or end)
108
+ const sectionStartIndex = sectionMatch.index + sectionMatch[0].length;
109
+ const nextSectionMatch = tasksContent.slice(sectionStartIndex).match(/^##\s+/m);
110
+ const sectionEndIndex = nextSectionMatch
111
+ ? sectionStartIndex + nextSectionMatch.index
112
+ : tasksContent.length;
113
+ const sectionContent = tasksContent.slice(sectionStartIndex, sectionEndIndex);
114
+ // Check for table data rows (lines starting with | that are not separator rows)
115
+ const tableRows = sectionContent.match(TABLE_DATA_ROW_PATTERN);
116
+ // Filter out header rows and separator rows
117
+ const dataRows = tableRows?.filter(row => {
118
+ const trimmed = row.trim();
119
+ // Skip separator rows like |---|---| or |:---|---:|
120
+ if (/^\|[-:\s|]+\|$/.test(trimmed))
121
+ return false;
122
+ // Skip header rows (first non-separator row is header)
123
+ return true;
124
+ });
125
+ // Need at least 2 rows (header + 1 data row)
126
+ if (!dataRows || dataRows.length < 2) {
127
+ result.warnings.push('Skill Mapping table has no data rows');
128
+ }
129
+ }
130
+ // If no tasks, consider it valid
131
+ if (parsedTasks.length === 0) {
132
+ result.passed = true;
133
+ return result;
134
+ }
135
+ // b, c, d, e. Validate each task's skill tags
136
+ const knownSkillSet = new Set(knownSkillNames.map(s => s.toLowerCase()));
137
+ for (const task of parsedTasks) {
138
+ // b. Check if task has skill tag
139
+ if (task.skills.length === 0) {
140
+ result.errors.push(`Task ${task.id}: missing @skill tag`);
141
+ continue;
142
+ }
143
+ // Validate each skill
144
+ for (const skill of task.skills) {
145
+ const skillLower = skill.toLowerCase();
146
+ // c. @skill:none should have a reason
147
+ if (skillLower === 'none') {
148
+ // Check if description mentions why no skill needed
149
+ const descLower = task.description.toLowerCase();
150
+ const hasReason = descLower.includes('manual') ||
151
+ descLower.includes('review') ||
152
+ descLower.includes('document') ||
153
+ descLower.includes('config') ||
154
+ descLower.includes('test') ||
155
+ descLower.includes('verify') ||
156
+ task.description.length > 30; // Substantial description is acceptable
157
+ if (!hasReason) {
158
+ result.warnings.push(`Task ${task.id}: @skill:none should have a reason in description`);
159
+ }
160
+ continue;
161
+ }
162
+ // d. Validate skill exists in known skills
163
+ if (knownSkillNames.length > 0 && !knownSkillSet.has(skillLower)) {
164
+ const availableSkills = knownSkillNames.length <= 10
165
+ ? knownSkillNames.join(', ')
166
+ : `${knownSkillNames.slice(0, 10).join(', ')}... (${knownSkillNames.length} total)`;
167
+ result.errors.push(`Task ${task.id}: @skill:${skill} is not a known skill. Available skills: ${availableSkills}`);
168
+ }
169
+ }
170
+ }
171
+ // Passed if no errors
172
+ result.passed = result.errors.length === 0;
173
+ return result;
174
+ }
175
+ /**
176
+ * Rule 3: task-ordering-by-wave
177
+ *
178
+ * Validates:
179
+ * - Tasks are ordered by wave (wave 1 tasks before wave 2, etc.)
180
+ * - Wave headers "### Wave N" match actual computed wave numbers
181
+ * - Tasks.md must have Wave headers
182
+ */
183
+ export function checkTaskOrderingByWave(tasksContent, parsedTasks) {
184
+ const result = {
185
+ ruleId: 'task-ordering-by-wave',
186
+ ruleName: 'Task ordering by wave',
187
+ passed: false,
188
+ errors: [],
189
+ warnings: [],
190
+ };
191
+ // If no tasks, consider it valid
192
+ if (parsedTasks.length === 0) {
193
+ result.passed = true;
194
+ return result;
195
+ }
196
+ // Check for wave headers
197
+ const waveHeaders = [];
198
+ const waveHeaderRegex = /^###\s+Wave\s+(\d+)/gim;
199
+ let match;
200
+ while ((match = waveHeaderRegex.exec(tasksContent)) !== null) {
201
+ waveHeaders.push({
202
+ wave: parseInt(match[1], 10),
203
+ position: match.index,
204
+ });
205
+ }
206
+ if (waveHeaders.length === 0) {
207
+ result.errors.push('No Wave headers found in tasks.md. Expected format: "### Wave N"');
208
+ return result;
209
+ }
210
+ // Generate execution plan to get actual waves
211
+ const executionPlan = generateExecutionPlan(parsedTasks);
212
+ if (executionPlan.hasCycle) {
213
+ result.errors.push('Task dependency graph has cycles, cannot validate wave ordering');
214
+ return result;
215
+ }
216
+ // Build a map of task ID to actual wave number
217
+ const taskToWave = new Map();
218
+ for (const wave of executionPlan.waves) {
219
+ for (const task of wave.tasks) {
220
+ taskToWave.set(task.id, wave.wave);
221
+ }
222
+ }
223
+ // Find task positions in content
224
+ const taskPositions = [];
225
+ for (const task of parsedTasks) {
226
+ // Find task line in content
227
+ const taskPattern = new RegExp(`-\\s+\\[[ xX]\\]\\s+${task.id.replace('.', '\\.')}\\s`, 'gm');
228
+ const taskMatch = taskPattern.exec(tasksContent);
229
+ if (taskMatch) {
230
+ const actualWave = taskToWave.get(task.id);
231
+ if (actualWave !== undefined) {
232
+ taskPositions.push({
233
+ id: task.id,
234
+ position: taskMatch.index,
235
+ actualWave,
236
+ });
237
+ }
238
+ }
239
+ }
240
+ // Sort by position in file
241
+ taskPositions.sort((a, b) => a.position - b.position);
242
+ // Check ordering: all tasks of wave N should appear before tasks of wave N+1
243
+ let lastWave = 0;
244
+ for (const task of taskPositions) {
245
+ if (task.actualWave < lastWave) {
246
+ result.errors.push(`Task ${task.id} (wave ${task.actualWave}) appears after tasks of wave ${lastWave}`);
247
+ }
248
+ lastWave = Math.max(lastWave, task.actualWave);
249
+ }
250
+ // Validate wave headers match actual wave numbers
251
+ const actualWaveNumbers = new Set(executionPlan.waves.map(w => w.wave));
252
+ const declaredWaveNumbers = new Set(waveHeaders.map(h => h.wave));
253
+ for (const declared of declaredWaveNumbers) {
254
+ if (!actualWaveNumbers.has(declared)) {
255
+ result.warnings.push(`Wave header "### Wave ${declared}" does not match any computed wave`);
256
+ }
257
+ }
258
+ for (const actual of actualWaveNumbers) {
259
+ if (!declaredWaveNumbers.has(actual)) {
260
+ result.errors.push(`Missing Wave header for computed wave ${actual}`);
261
+ }
262
+ }
263
+ // Check wave header ordering
264
+ for (let i = 1; i < waveHeaders.length; i++) {
265
+ if (waveHeaders[i].wave <= waveHeaders[i - 1].wave) {
266
+ result.errors.push(`Wave headers are not in ascending order: Wave ${waveHeaders[i - 1].wave} followed by Wave ${waveHeaders[i].wave}`);
267
+ }
268
+ }
269
+ result.passed = result.errors.length === 0;
270
+ return result;
271
+ }
272
+ /**
273
+ * Main entry: Run all strict validation rules
274
+ */
275
+ export async function runStrictValidation(tasksContent, parsedTasks, knownSkillNames) {
276
+ const checks = [
277
+ checkPreClarification(tasksContent),
278
+ checkSkillTagsValid(tasksContent, parsedTasks, knownSkillNames),
279
+ checkTaskOrderingByWave(tasksContent, parsedTasks),
280
+ ];
281
+ const allPassed = checks.every(check => check.passed);
282
+ return {
283
+ checks,
284
+ allPassed,
285
+ };
286
+ }
287
+ //# sourceMappingURL=strict-rules.js.map
@@ -6,6 +6,13 @@ export interface ValidationIssue {
6
6
  line?: number;
7
7
  column?: number;
8
8
  }
9
+ export interface StrictCheckSummary {
10
+ ruleId: string;
11
+ ruleName: string;
12
+ passed: boolean;
13
+ errors: string[];
14
+ warnings: string[];
15
+ }
9
16
  export interface ValidationReport {
10
17
  valid: boolean;
11
18
  issues: ValidationIssue[];
@@ -14,5 +21,8 @@ export interface ValidationReport {
14
21
  warnings: number;
15
22
  info: number;
16
23
  };
24
+ strictChecks?: StrictCheckSummary[];
25
+ /** Mermaid diagram content if task graph analysis succeeded */
26
+ mermaidDiagram?: string;
17
27
  }
18
28
  //# sourceMappingURL=types.d.ts.map
@@ -29,5 +29,10 @@ export declare class Validator {
29
29
  private containsShallOrMust;
30
30
  private countScenarios;
31
31
  private formatSectionList;
32
+ /**
33
+ * Find project root directory by walking up from changeDir.
34
+ * Looks for common project markers like package.json, .git, or skills directory.
35
+ */
36
+ private findProjectRoot;
32
37
  }
33
38
  //# sourceMappingURL=validator.d.ts.map
@@ -6,6 +6,12 @@ import { ChangeParser } from '../parsers/change-parser.js';
6
6
  import { MIN_PURPOSE_LENGTH, MAX_REQUIREMENT_TEXT_LENGTH, VALIDATION_MESSAGES } from './constants.js';
7
7
  import { parseDeltaSpec, normalizeRequirementName } from '../parsers/requirement-blocks.js';
8
8
  import { FileSystemUtils } from '../../utils/file-system.js';
9
+ import { runStrictValidation } from './strict-rules.js';
10
+ import { discoverSkills } from '../skill-discovery.js';
11
+ import { existsSync } from 'fs';
12
+ import { parseTasks } from '../task-graph/task-parser.js';
13
+ import { generateExecutionPlan } from '../task-graph/execution-planner.js';
14
+ import { renderMermaidDiagram } from '../task-graph/mermaid-renderer.js';
9
15
  export class Validator {
10
16
  strictMode;
11
17
  constructor(strictMode = false) {
@@ -253,7 +259,84 @@ export class Validator {
253
259
  if (totalDeltas === 0) {
254
260
  issues.push({ level: 'ERROR', path: 'file', message: this.enrichTopLevelError('change', VALIDATION_MESSAGES.CHANGE_NO_DELTAS) });
255
261
  }
256
- return this.createReport(issues);
262
+ // Strict mode validation for tasks.md
263
+ let strictChecks;
264
+ if (this.strictMode) {
265
+ const tasksFile = path.join(changeDir, 'tasks.md');
266
+ let tasksContent;
267
+ try {
268
+ tasksContent = await fs.readFile(tasksFile, 'utf-8');
269
+ }
270
+ catch {
271
+ // tasks.md not found - skip strict validation for tasks
272
+ }
273
+ if (tasksContent) {
274
+ const parsedTasks = parseTasks(tasksContent);
275
+ // Get known skill names
276
+ let knownSkillNames = [];
277
+ try {
278
+ const projectRoot = this.findProjectRoot(changeDir);
279
+ const skills = discoverSkills(projectRoot);
280
+ knownSkillNames = skills.map(s => s.name);
281
+ }
282
+ catch {
283
+ // skill discovery failed - add warning and continue without skill name validation
284
+ issues.push({
285
+ level: 'WARNING',
286
+ path: 'tasks.md',
287
+ message: 'Could not discover skills for strict validation — skill name check skipped',
288
+ });
289
+ }
290
+ // Run strict validation
291
+ const strictResult = await runStrictValidation(tasksContent, parsedTasks, knownSkillNames);
292
+ strictChecks = strictResult.checks;
293
+ // Merge strict errors into main issues
294
+ for (const check of strictResult.checks) {
295
+ for (const error of check.errors) {
296
+ issues.push({
297
+ level: 'ERROR',
298
+ path: 'tasks.md',
299
+ message: `[${check.ruleName}] ${error}`,
300
+ });
301
+ }
302
+ for (const warning of check.warnings) {
303
+ issues.push({
304
+ level: 'WARNING',
305
+ path: 'tasks.md',
306
+ message: `[${check.ruleName}] ${warning}`,
307
+ });
308
+ }
309
+ }
310
+ }
311
+ }
312
+ // Generate Mermaid diagram if tasks.md exists and has tasks
313
+ let mermaidDiagram;
314
+ const tasksFile = path.join(changeDir, 'tasks.md');
315
+ let tasksContentForMermaid;
316
+ try {
317
+ tasksContentForMermaid = await fs.readFile(tasksFile, 'utf-8');
318
+ }
319
+ catch {
320
+ // tasks.md not found - skip Mermaid generation
321
+ }
322
+ if (tasksContentForMermaid) {
323
+ const parsedTasksForMermaid = parseTasks(tasksContentForMermaid);
324
+ if (parsedTasksForMermaid.length > 0) {
325
+ const executionPlan = generateExecutionPlan(parsedTasksForMermaid);
326
+ // Only generate Mermaid diagram if execution plan is valid (no cycles)
327
+ if (!executionPlan.hasCycle && executionPlan.waves.length > 0) {
328
+ mermaidDiagram = renderMermaidDiagram(executionPlan, parsedTasksForMermaid);
329
+ }
330
+ }
331
+ }
332
+ const report = this.createReport(issues);
333
+ if (strictChecks) {
334
+ report.strictChecks = strictChecks;
335
+ }
336
+ if (mermaidDiagram) {
337
+ report.mermaidDiagram = mermaidDiagram;
338
+ }
339
+ return report;
257
340
  }
258
341
  convertZodErrors(error) {
259
342
  return error.issues.map(err => {
@@ -405,5 +488,24 @@ export class Validator {
405
488
  const last = sections[sections.length - 1];
406
489
  return `${head.join(', ')} and ${last}`;
407
490
  }
491
+ /**
492
+ * Find project root directory by walking up from changeDir.
493
+ * Looks for common project markers like package.json, .git, or skills directory.
494
+ */
495
+ findProjectRoot(changeDir) {
496
+ let current = path.resolve(changeDir);
497
+ const root = path.parse(current).root;
498
+ while (current !== root) {
499
+ // Check for project root markers
500
+ if (existsSync(path.join(current, 'package.json')) ||
501
+ existsSync(path.join(current, '.git')) ||
502
+ existsSync(path.join(current, 'skills'))) {
503
+ return current;
504
+ }
505
+ current = path.dirname(current);
506
+ }
507
+ // Fallback to process.cwd() if no markers found
508
+ return process.cwd();
509
+ }
408
510
  }
409
511
  //# sourceMappingURL=validator.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhuan-ai/zhuanspec",
3
- "version": "2.0.0",
3
+ "version": "2.1.1",
4
4
  "description": "AI-native system for spec-driven development",
5
5
  "keywords": [
6
6
  "zhuanspec",
@@ -44,19 +44,19 @@ artifacts:
44
44
  创建建立此变更原因的提案文档。
45
45
 
46
46
  章节:
47
- - **Why**:关于问题或机会的 1-2 句话。它解决了什么问题?为什么是现在?
48
- - **What Changes**:变更的要点列表。具体说明新功能、修改或移除。用 **BREAKING** 标记破坏性更改。
49
- - **Capabilities**:确定将创建或修改哪些规范:
50
- - **New Capabilities**:列出正在引入的功能。每个都会成为新的 `specs/<name>/spec.md`。使用 kebab-case 名称(例如,`user-auth`、`data-export`)。
51
- - **Modified Capabilities**:列出其 REQUIREMENTS 正在更改的现有功能。仅当规范级行为更改时(不仅仅是实现细节)才包含。每个都需要一个增量规范文件。检查 `openspec/specs/` 以获取现有规范名称。如果没有要求更改,则留空。
52
- - **Skill Mapping**:运行 `zhuanspec skills list` 发现可用 skill,基于 skill description 进行语义匹配:
47
+ - **变更原因**:关于问题或机会的 1-2 句话。它解决了什么问题?为什么是现在?
48
+ - **变更内容**:变更的要点列表。具体说明新功能、修改或移除。用 **BREAKING** 标记破坏性更改。
49
+ - **功能范围**:确定将创建或修改哪些规范:
50
+ - **新增功能**:列出正在引入的功能。每个都会成为新的 `specs/<name>/spec.md`。使用 kebab-case 名称(例如,`user-auth`、`data-export`)。
51
+ - **修改功能**:列出其 REQUIREMENTS 正在更改的现有功能。仅当规范级行为更改时(不仅仅是实现细节)才包含。每个都需要一个增量规范文件。检查 `openspec/specs/` 以获取现有规范名称。如果没有要求更改,则留空。
52
+ - **Skill 映射**:运行 `zhuanspec skills list` 发现可用 skill,基于 skill description 进行语义匹配:
53
53
  1. 仔细阅读每个 skill 的 description,理解其具体功能和适用场景
54
54
  2. 只有当实现区域的实际功能与 skill description 明确匹配时才关联
55
55
  3. 简单的代码修改(如枚举值增删)应匹配通用编码规范 skill,而非架构级 skill
56
56
  4. 避免仅因模块名称或文件路径中的关键词而错误匹配
57
57
  5. 在表格中说明匹配理由,确保映射合理性
58
58
  此映射将在 tasks.md 中用于 @skill 标注。如果没有匹配 skill 则留空。
59
- - **Impact**:受影响的代码、API、依赖项或系统。
59
+ - **影响范围**:受影响的代码、API、依赖项或系统。
60
60
 
61
61
  重要提示:Capabilities 部分至关重要。它在提案和规范阶段之间创建契约。在填写之前,请研究现有规范。
62
62
  此处列出的每个功能都需要一个相应的规范文件。
@@ -134,12 +134,12 @@ artifacts:
134
134
  - 在编码之前从技术决策中受益的模糊性
135
135
 
136
136
  章节:
137
- - **Context**:背景、当前状态、约束、利益相关者
138
- - **Goals / Non-Goals**:此设计实现的内容和明确排除的内容
139
- - **Decisions**:关键的技术选择及其理由(为什么选择 X 而不是 Y?)。包括为每个决策考虑的替代方案。
140
- - **Risks / Trade-offs**:已知的限制,可能出错的事情。格式:[Risk] → Mitigation
141
- - **Migration Plan**:部署步骤、回滚策略(如果适用)
142
- - **Open Questions**:待解决的决定或未知数
137
+ - **背景**:背景、当前状态、约束、利益相关者
138
+ - **目标 / 非目标**:此设计实现的内容和明确排除的内容
139
+ - **决策**:关键的技术选择及其理由(为什么选择 X 而不是 Y?)。包括为每个决策考虑的替代方案。
140
+ - **风险 / 权衡**:已知的限制,可能出错的事情。格式:[风险] → 缓解措施
141
+ - **迁移计划**:部署步骤、回滚策略(如果适用)
142
+ - **待解决问题**:待解决的决定或未知数
143
143
 
144
144
  专注于架构和方法,而不是逐行实现。
145
145
  参考提案了解动机,参考规范了解要求。
@@ -1,19 +1,19 @@
1
- ## Context
1
+ ## 背景
2
2
 
3
3
  <!-- 背景和当前状态 -->
4
4
 
5
- ## Goals / Non-Goals
5
+ ## 目标 / 非目标
6
6
 
7
- **Goals:**
7
+ **目标:**
8
8
  <!-- 此设计旨在实现的目标 -->
9
9
 
10
- **Non-Goals:**
10
+ **非目标:**
11
11
  <!-- 明确不在范围内的内容 -->
12
12
 
13
- ## Decisions
13
+ ## 决策
14
14
 
15
15
  <!-- 关键设计决策和理由 -->
16
16
 
17
- ## Risks / Trade-offs
17
+ ## 风险 / 权衡
18
18
 
19
19
  <!-- 已知风险和权衡 -->
@@ -1,24 +1,24 @@
1
- ## Why
1
+ ## 变更原因
2
2
 
3
3
  <!-- 解释此变更的动机。它解决了什么问题?为什么是现在? -->
4
4
 
5
- ## What Changes
5
+ ## 变更内容
6
6
 
7
7
  <!-- 描述将要更改的内容。具体说明新功能、修改或移除。 -->
8
8
 
9
- ## Capabilities
9
+ ## 功能范围
10
10
 
11
- ### New Capabilities
11
+ ### 新增功能
12
12
  <!-- 正在引入的功能。将 <name> 替换为 kebab-case 标识符(例如,user-auth、data-export、api-rate-limiting)。每个都会创建 specs/<name>/spec.md -->
13
13
  - `<name>`: <此功能涵盖内容的简要描述>
14
14
 
15
- ### Modified Capabilities
15
+ ### 修改功能
16
16
  <!-- 其 REQUIREMENTS 正在更改的现有功能(不仅仅是实现)。
17
17
  仅当规范级行为更改时,才在此处列出。每个都需要一个增量规范文件。
18
18
  使用 zhuanspec/specs/ 中的现有规范名称。如果没有要求更改,则留空。 -->
19
19
  - `<existing-name>`: <正在更改的要求>
20
20
 
21
- ## Skill Mapping
21
+ ## Skill 映射
22
22
 
23
23
  <!-- 重要:运行 `zhuanspec skills list` 查看可用 skill,基于 skill description 进行语义匹配。
24
24
  匹配原则:
@@ -38,6 +38,6 @@
38
38
  |---------|---------|-----------|---------|
39
39
  | <!-- 模块/文件 --> | <!-- 具体功能 --> | <!-- skill 名称 --> | <!-- 为什么匹配 --> |
40
40
 
41
- ## Impact
41
+ ## 影响范围
42
42
 
43
43
  <!-- 受影响的代码、API、依赖项、系统 -->
@@ -1,8 +1,8 @@
1
1
  ## ADDED Requirements
2
2
 
3
- ### Requirement: <!-- 要求名称 -->
3
+ ### 需求:<!-- 要求名称 -->
4
4
  <!-- 要求文本 -->
5
5
 
6
- #### Scenario: <!-- 场景名称 -->
7
- - **WHEN** <!-- 条件 -->
8
- - **THEN** <!-- 预期结果 -->
6
+ #### 场景:<!-- 场景名称 -->
7
+ - **当** <!-- 条件 -->
8
+ - **则** <!-- 预期结果 -->