cc-devflow 4.2.0 → 4.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 (119) hide show
  1. package/.claude/commands/flow/CLAUDE.md +0 -4
  2. package/.claude/docs/examples/design-inspiration-pool.md +59 -0
  3. package/.claude/docs/examples/ui-prototype-constitution-checklist.md +75 -0
  4. package/.claude/docs/implementation-summary-v7.md +449 -0
  5. package/.claude/docs/spec-format-guide.md +349 -0
  6. package/.claude/docs/state-consolidation-design.md +323 -0
  7. package/.claude/docs/templates/ARCHITECTURE_TEMPLATE.md +85 -386
  8. package/.claude/docs/templates/DESIGN_TEMPLATE.md +157 -0
  9. package/.claude/docs/templates/PROPOSAL_TEMPLATE.md +91 -0
  10. package/.claude/docs/templates/SPEC_TEMPLATE_DELTA.md +139 -0
  11. package/.claude/docs/templates/SPEC_TEMPLATE_PROJECT.md +93 -0
  12. package/.claude/docs/templates/STYLE_TEMPLATE.md +114 -901
  13. package/.claude/docs/templates/UI_PROTOTYPE_TEMPLATE.md +143 -1205
  14. package/.claude/hooks/inject-agent-context.ts +9 -9
  15. package/.claude/scripts/.claude/commands/flow/export-openspec.md +221 -0
  16. package/.claude/scripts/.claude/commands/flow/import-openspec.md +171 -0
  17. package/.claude/scripts/__tests__/openspec.test.js +212 -0
  18. package/.claude/scripts/delta-parser.ts +112 -2
  19. package/.claude/scripts/export-openspec.js +222 -0
  20. package/.claude/scripts/import-openspec.js +272 -0
  21. package/.claude/scripts/validate-scope.sh +200 -0
  22. package/.claude/skills/{workflow/flow-init → flow-init}/SKILL.md +25 -4
  23. package/.claude/skills/{workflow/flow-release → flow-release}/SKILL.md +14 -3
  24. package/.claude/skills/{workflow/flow-spec → flow-spec}/SKILL.md +30 -2
  25. package/.claude/skills/utility/npm-release/CLAUDE.md +55 -0
  26. package/.claude/skills/utility/npm-release/SKILL.md +111 -46
  27. package/.claude/skills/utility/npm-release/references/version-decision-guide.md +134 -0
  28. package/.claude/skills/utility/npm-release/scripts/atomic-version-bump.sh +95 -0
  29. package/.claude/skills/utility/npm-release/scripts/validate-version-sync.sh +82 -0
  30. package/.claude/skills/utility/npm-release/scripts/version-decision-tree.sh +44 -0
  31. package/.claude/tsc-cache/70d2fc6d-2936-429b-b529-429f1aae8c88/affected-repos.txt +1 -0
  32. package/.claude/tsc-cache/70d2fc6d-2936-429b-b529-429f1aae8c88/edited-files.log +2 -0
  33. package/CHANGELOG.md +40 -0
  34. package/README.md +2 -1
  35. package/README.zh-CN.md +2 -1
  36. package/docs/v4.3.0-migration-guide.md +276 -0
  37. package/lib/harness/CLAUDE.md +5 -4
  38. package/lib/harness/__tests__/planner.tdd.test.js +125 -0
  39. package/lib/harness/index.js +4 -2
  40. package/lib/harness/operations/dispatch.js +13 -0
  41. package/lib/harness/operations/plan.js +55 -1
  42. package/lib/harness/operations/release.js +87 -0
  43. package/lib/harness/operations/verify.js +14 -0
  44. package/lib/harness/planner.js +131 -0
  45. package/lib/harness/query.js +126 -0
  46. package/lib/harness/schemas.js +22 -1
  47. package/package.json +1 -1
  48. package/.claude/commands/flow/checklist.md +0 -18
  49. package/.claude/commands/flow/clarify.md +0 -18
  50. package/.claude/commands/flow/new.md +0 -23
  51. package/.claude/commands/flow/quality.md +0 -21
  52. package/.claude/docs/templates/EPIC_TEMPLATE.md +0 -805
  53. package/.claude/docs/templates/PRD_TEMPLATE.md +0 -562
  54. package/.claude/docs/templates/TASKS_TEMPLATE.md +0 -523
  55. package/.claude/docs/templates/TECH_DESIGN_TEMPLATE.md +0 -1019
  56. package/.claude/skills/workflow/CLAUDE.md +0 -24
  57. /package/.claude/skills/{domain/attention-refresh → attention-refresh}/SKILL.md +0 -0
  58. /package/.claude/skills/{domain/brainstorming → brainstorming}/SKILL.md +0 -0
  59. /package/.claude/skills/{guardrail/constitution-guardian → constitution-guardian}/SKILL.md +0 -0
  60. /package/.claude/skills/{utility/constitution-quick-ref → constitution-quick-ref}/SKILL.md +0 -0
  61. /package/.claude/skills/{domain/debugging → debugging}/SKILL.md +0 -0
  62. /package/.claude/skills/{utility/file-standards → file-standards}/SKILL.md +0 -0
  63. /package/.claude/skills/{domain/finishing-branch → finishing-branch}/SKILL.md +0 -0
  64. /package/.claude/skills/{workflow/flow-dev → flow-dev}/CLAUDE.md +0 -0
  65. /package/.claude/skills/{workflow/flow-dev → flow-dev}/SKILL.md +0 -0
  66. /package/.claude/skills/{workflow/flow-dev → flow-dev}/assets/IMPLEMENTATION_PLAN_TEMPLATE.md +0 -0
  67. /package/.claude/skills/{workflow/flow-dev → flow-dev}/context.jsonl +0 -0
  68. /package/.claude/skills/{workflow/flow-dev → flow-dev}/dev-implementer.jsonl +0 -0
  69. /package/.claude/skills/{workflow/flow-dev → flow-dev}/scripts/entry-gate.sh +0 -0
  70. /package/.claude/skills/{workflow/flow-dev → flow-dev}/scripts/exit-gate.sh +0 -0
  71. /package/.claude/skills/{workflow/flow-dev → flow-dev}/scripts/task-orchestrator.sh +0 -0
  72. /package/.claude/skills/{workflow/flow-fix → flow-fix}/SKILL.md +0 -0
  73. /package/.claude/skills/{workflow/flow-fix → flow-fix}/context.jsonl +0 -0
  74. /package/.claude/skills/{workflow/flow-fix → flow-fix}/references/bug-analyzer.md +0 -0
  75. /package/.claude/skills/{workflow/flow-init → flow-init}/assets/BRAINSTORM_TEMPLATE.md +0 -0
  76. /package/.claude/skills/{workflow/flow-init → flow-init}/assets/INIT_FLOW_TEMPLATE.md +0 -0
  77. /package/.claude/skills/{workflow/flow-init → flow-init}/assets/RESEARCH_TEMPLATE.md +0 -0
  78. /package/.claude/skills/{workflow/flow-init → flow-init}/context.jsonl +0 -0
  79. /package/.claude/skills/{workflow/flow-init → flow-init}/references/flow-researcher.md +0 -0
  80. /package/.claude/skills/{workflow/flow-init → flow-init}/scripts/check-prerequisites.sh +0 -0
  81. /package/.claude/skills/{workflow/flow-init → flow-init}/scripts/consolidate-research.sh +0 -0
  82. /package/.claude/skills/{workflow/flow-init → flow-init}/scripts/create-requirement.sh +0 -0
  83. /package/.claude/skills/{workflow/flow-init → flow-init}/scripts/generate-research-tasks.sh +0 -0
  84. /package/.claude/skills/{workflow/flow-init → flow-init}/scripts/populate-research-tasks.sh +0 -0
  85. /package/.claude/skills/{workflow/flow-init → flow-init}/scripts/validate-research.sh +0 -0
  86. /package/.claude/skills/{workflow/flow-quality → flow-quality}/SKILL.md +0 -0
  87. /package/.claude/skills/{workflow/flow-quality → flow-quality}/context.jsonl +0 -0
  88. /package/.claude/skills/{workflow/flow-quality → flow-quality}/references/code-quality-reviewer.md +0 -0
  89. /package/.claude/skills/{workflow/flow-quality → flow-quality}/references/qa-tester.md +0 -0
  90. /package/.claude/skills/{workflow/flow-quality → flow-quality}/references/security-reviewer.md +0 -0
  91. /package/.claude/skills/{workflow/flow-quality → flow-quality}/references/spec-reviewer.md +0 -0
  92. /package/.claude/skills/{workflow/flow-release → flow-release}/context.jsonl +0 -0
  93. /package/.claude/skills/{workflow/flow-release → flow-release}/references/release-manager.md +0 -0
  94. /package/.claude/skills/{workflow/flow-spec → flow-spec}/CLAUDE.md +0 -0
  95. /package/.claude/skills/{workflow/flow-spec → flow-spec}/context.jsonl +0 -0
  96. /package/.claude/skills/{workflow/flow-spec → flow-spec}/scripts/entry-gate.sh +0 -0
  97. /package/.claude/skills/{workflow/flow-spec → flow-spec}/scripts/exit-gate.sh +0 -0
  98. /package/.claude/skills/{workflow/flow-spec → flow-spec}/scripts/parallel-orchestrator.sh +0 -0
  99. /package/.claude/skills/{workflow/flow-spec → flow-spec}/scripts/team-communication.sh +0 -0
  100. /package/.claude/skills/{workflow/flow-spec → flow-spec}/scripts/team-init.sh +0 -0
  101. /package/.claude/skills/{workflow/flow-spec → flow-spec}/scripts/test-team-mode.sh +0 -0
  102. /package/.claude/skills/{workflow/flow-spec → flow-spec}/team-config.json +0 -0
  103. /package/.claude/skills/{workflow/flow-verify → flow-verify}/CLAUDE.md +0 -0
  104. /package/.claude/skills/{workflow/flow-verify → flow-verify}/SKILL.md +0 -0
  105. /package/.claude/skills/{workflow/flow-verify → flow-verify}/context.jsonl +0 -0
  106. /package/.claude/skills/{utility/fractal-docs → fractal-docs}/SKILL.md +0 -0
  107. /package/.claude/skills/{utility/journey-checker → journey-checker}/SKILL.md +0 -0
  108. /package/.claude/skills/{utility/journey-checker → journey-checker}/pressure-scenarios.md +0 -0
  109. /package/.claude/skills/{domain/receiving-review → receiving-review}/SKILL.md +0 -0
  110. /package/.claude/skills/{utility/skill-creator → skill-creator}/LICENSE.txt +0 -0
  111. /package/.claude/skills/{utility/skill-creator → skill-creator}/SKILL.md +0 -0
  112. /package/.claude/skills/{utility/skill-creator → skill-creator}/references/output-patterns.md +0 -0
  113. /package/.claude/skills/{utility/skill-creator → skill-creator}/references/workflows.md +0 -0
  114. /package/.claude/skills/{utility/skill-creator → skill-creator}/scripts/init_skill.py +0 -0
  115. /package/.claude/skills/{utility/skill-creator → skill-creator}/scripts/package_skill.py +0 -0
  116. /package/.claude/skills/{utility/skill-creator → skill-creator}/scripts/quick_validate.py +0 -0
  117. /package/.claude/skills/{domain/tdd → tdd}/SKILL.md +0 -0
  118. /package/.claude/skills/{guardrail/tdd-enforcer → tdd-enforcer}/SKILL.md +0 -0
  119. /package/.claude/skills/{domain/verification → verification}/SKILL.md +0 -0
@@ -474,6 +474,95 @@ function escapeRegex(str: string): string {
474
474
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
475
475
  }
476
476
 
477
+ // ============================================================================
478
+ // Version Bump
479
+ // ============================================================================
480
+
481
+ function bumpVersion(currentVersion: string, deltaBlocks: DeltaBlock[]): string {
482
+ const [major, minor, patch] = currentVersion.split('.').map(Number);
483
+
484
+ // REMOVED → MAJOR +1 (破坏性变更)
485
+ if (deltaBlocks.some(b => b.type === 'REMOVED')) {
486
+ return `${major + 1}.0.0`;
487
+ }
488
+
489
+ // ADDED → MINOR +1 (新功能)
490
+ if (deltaBlocks.some(b => b.type === 'ADDED')) {
491
+ return `${major}.${minor + 1}.0`;
492
+ }
493
+
494
+ // MODIFIED/RENAMED → PATCH +1 (修复/改进)
495
+ return `${major}.${minor}.${patch + 1}`;
496
+ }
497
+
498
+ // ============================================================================
499
+ // Merge to Project-Level spec.md
500
+ // ============================================================================
501
+
502
+ export function mergeDeltaToMainSpec(
503
+ mainSpecPath: string,
504
+ deltaSpecPath: string
505
+ ): { success: boolean; newVersion: string; error?: string } {
506
+ const fs = require('fs');
507
+
508
+ try {
509
+ // 读取文件
510
+ const mainSpecContent = fs.readFileSync(mainSpecPath, 'utf-8');
511
+ const deltaSpecContent = fs.readFileSync(deltaSpecPath, 'utf-8');
512
+
513
+ // 解析 frontmatter
514
+ const { metadata: mainMeta, body: mainBody } = extractYamlFrontmatter(mainSpecContent);
515
+ const { metadata: deltaMeta } = extractYamlFrontmatter(deltaSpecContent);
516
+
517
+ // 验证模块匹配
518
+ if (mainMeta.module !== deltaMeta.module) {
519
+ return {
520
+ success: false,
521
+ newVersion: mainMeta.version || '1.0.0',
522
+ error: `Module mismatch: main=${mainMeta.module}, delta=${deltaMeta.module}`
523
+ };
524
+ }
525
+
526
+ // 解析 Delta blocks
527
+ const deltaBlocks = parseDelta(deltaSpecContent);
528
+
529
+ if (deltaBlocks.length === 0) {
530
+ return {
531
+ success: false,
532
+ newVersion: mainMeta.version || '1.0.0',
533
+ error: 'No delta blocks found'
534
+ };
535
+ }
536
+
537
+ // 应用 Delta
538
+ const result = applyDelta(mainSpecContent, deltaBlocks);
539
+
540
+ // 更新版本号和时间戳
541
+ const newVersion = bumpVersion(mainMeta.version || '1.0.0', deltaBlocks);
542
+ const updatedMeta = {
543
+ ...mainMeta,
544
+ version: newVersion,
545
+ updated_at: new Date().toISOString()
546
+ };
547
+
548
+ // 重新组装 frontmatter
549
+ const yamlLines = Object.entries(updatedMeta).map(([k, v]) => `${k}: "${v}"`);
550
+ const newContent = `---\n${yamlLines.join('\n')}\n---\n${result.split('---\n')[2] || result}`;
551
+
552
+ // 写回文件
553
+ fs.writeFileSync(mainSpecPath, newContent, 'utf-8');
554
+
555
+ return { success: true, newVersion };
556
+
557
+ } catch (error) {
558
+ return {
559
+ success: false,
560
+ newVersion: '',
561
+ error: error instanceof Error ? error.message : String(error)
562
+ };
563
+ }
564
+ }
565
+
477
566
  // ============================================================================
478
567
  // CLI Interface
479
568
  // ============================================================================
@@ -485,8 +574,9 @@ if (require.main === module) {
485
574
  if (args.length < 1) {
486
575
  console.error('Usage: delta-parser.ts <command> [args]');
487
576
  console.error('Commands:');
488
- console.error(' parse <delta-file> Parse delta file and output JSON');
489
- console.error(' apply <prd-file> <delta-file> Apply delta to PRD and output result');
577
+ console.error(' parse <delta-file> Parse delta file and output JSON');
578
+ console.error(' apply <prd-file> <delta-file> Apply delta to PRD and output result');
579
+ console.error(' merge <main-spec> <delta-spec> Merge delta to project-level spec.md');
490
580
  process.exit(1);
491
581
  }
492
582
 
@@ -520,6 +610,26 @@ if (require.main === module) {
520
610
  break;
521
611
  }
522
612
 
613
+ case 'merge': {
614
+ const mainSpecPath = args[1];
615
+ const deltaSpecPath = args[2];
616
+ if (!mainSpecPath || !deltaSpecPath) {
617
+ console.error('Error: main-spec and delta-spec required');
618
+ process.exit(1);
619
+ }
620
+
621
+ const result = mergeDeltaToMainSpec(mainSpecPath, deltaSpecPath);
622
+
623
+ if (result.success) {
624
+ console.log(`✅ Delta merged successfully`);
625
+ console.log(`📦 New version: ${result.newVersion}`);
626
+ } else {
627
+ console.error(`❌ Merge failed: ${result.error}`);
628
+ process.exit(1);
629
+ }
630
+ break;
631
+ }
632
+
523
633
  default:
524
634
  console.error(`Unknown command: ${command}`);
525
635
  process.exit(1);
@@ -0,0 +1,222 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * [INPUT]: 依赖 CC-DevFlow spec.md 文件路径
4
+ * [OUTPUT]: 生成 OpenSpec 格式的 spec.md (纯 Requirements,无 CC-DevFlow 元数据)
5
+ * [POS]: OpenSpec 互操作层,被 /flow:export-openspec 命令调用
6
+ * [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+
12
+ /**
13
+ * 解析 CC-DevFlow spec.md 格式
14
+ */
15
+ function parseDevFlowSpec(content) {
16
+ const lines = content.split('\n');
17
+ const result = {
18
+ moduleName: '',
19
+ purpose: '',
20
+ requirements: []
21
+ };
22
+
23
+ let inFrontmatter = false;
24
+ let currentSection = null;
25
+ let currentRequirement = null;
26
+ let currentScenario = null;
27
+ let buffer = [];
28
+
29
+ for (let i = 0; i < lines.length; i++) {
30
+ const line = lines[i];
31
+
32
+ // 跳过 YAML frontmatter
33
+ if (line === '---') {
34
+ inFrontmatter = !inFrontmatter;
35
+ continue;
36
+ }
37
+ if (inFrontmatter) {
38
+ continue;
39
+ }
40
+
41
+ // # Module Name
42
+ if (line.startsWith('# ') && !result.moduleName) {
43
+ result.moduleName = line.substring(2).trim();
44
+ continue;
45
+ }
46
+
47
+ // ## Purpose
48
+ if (line.startsWith('## Purpose')) {
49
+ currentSection = 'purpose';
50
+ buffer = [];
51
+ continue;
52
+ }
53
+
54
+ // ## Requirements
55
+ if (line.startsWith('## Requirements')) {
56
+ if (currentSection === 'purpose') {
57
+ result.purpose = buffer.join('\n').trim();
58
+ }
59
+ currentSection = 'requirements';
60
+ buffer = [];
61
+ continue;
62
+ }
63
+
64
+ // ## Design (停止解析,OpenSpec 不需要)
65
+ if (line.startsWith('## Design') || line.startsWith('## Tasks') || line.startsWith('## Verification')) {
66
+ if (currentRequirement) {
67
+ result.requirements.push(currentRequirement);
68
+ currentRequirement = null;
69
+ }
70
+ break;
71
+ }
72
+
73
+ // ### Requirement: Name
74
+ if (line.startsWith('### Requirement:')) {
75
+ if (currentRequirement) {
76
+ result.requirements.push(currentRequirement);
77
+ }
78
+ currentRequirement = {
79
+ name: line.substring(16).trim(),
80
+ description: '',
81
+ scenarios: []
82
+ };
83
+ currentScenario = null;
84
+ buffer = [];
85
+ continue;
86
+ }
87
+
88
+ // #### Scenario: Case
89
+ if (line.startsWith('#### Scenario:')) {
90
+ if (currentRequirement && buffer.length > 0) {
91
+ currentRequirement.description = buffer.join('\n').trim();
92
+ buffer = [];
93
+ }
94
+ currentScenario = {
95
+ name: line.substring(14).trim(),
96
+ steps: []
97
+ };
98
+ continue;
99
+ }
100
+
101
+ // BDD steps
102
+ if (currentScenario && (line.startsWith('- GIVEN') || line.startsWith('- WHEN') || line.startsWith('- THEN') || line.startsWith('- AND'))) {
103
+ currentScenario.steps.push(line.substring(2).trim());
104
+ continue;
105
+ }
106
+
107
+ // 收集内容
108
+ if (currentSection === 'purpose' && line.trim()) {
109
+ buffer.push(line);
110
+ } else if (currentRequirement && !currentScenario && line.trim() && !line.startsWith('[NEEDS CLARIFICATION')) {
111
+ buffer.push(line);
112
+ }
113
+
114
+ // 场景结束
115
+ if (currentScenario && line.trim() === '' && currentScenario.steps.length > 0) {
116
+ currentRequirement.scenarios.push(currentScenario);
117
+ currentScenario = null;
118
+ }
119
+ }
120
+
121
+ // 处理最后的 requirement
122
+ if (currentRequirement) {
123
+ if (currentScenario && currentScenario.steps.length > 0) {
124
+ currentRequirement.scenarios.push(currentScenario);
125
+ }
126
+ if (buffer.length > 0) {
127
+ currentRequirement.description = buffer.join('\n').trim();
128
+ }
129
+ result.requirements.push(currentRequirement);
130
+ }
131
+
132
+ return result;
133
+ }
134
+
135
+ /**
136
+ * 转换为 OpenSpec 格式
137
+ */
138
+ function convertToOpenSpec(devflowData) {
139
+ let output = `# ${devflowData.moduleName}
140
+
141
+ ## Purpose
142
+
143
+ ${devflowData.purpose}
144
+
145
+ ## Requirements
146
+
147
+ `;
148
+
149
+ // 转换 Requirements
150
+ for (const req of devflowData.requirements) {
151
+ output += `### Requirement: ${req.name}\n`;
152
+
153
+ if (req.description) {
154
+ output += `${req.description}\n\n`;
155
+ }
156
+
157
+ // 转换 Scenarios
158
+ for (const scenario of req.scenarios) {
159
+ output += `#### Scenario: ${scenario.name}\n`;
160
+ for (const step of scenario.steps) {
161
+ output += `- ${step}\n`;
162
+ }
163
+ output += '\n';
164
+ }
165
+ }
166
+
167
+ return output;
168
+ }
169
+
170
+ /**
171
+ * 主函数
172
+ */
173
+ function exportOpenSpec(devflowSpecPath, outputPath) {
174
+ // 读取 CC-DevFlow spec.md
175
+ if (!fs.existsSync(devflowSpecPath)) {
176
+ throw new Error(`DevFlow spec.md not found: ${devflowSpecPath}`);
177
+ }
178
+
179
+ const content = fs.readFileSync(devflowSpecPath, 'utf-8');
180
+
181
+ // 解析 CC-DevFlow spec
182
+ const devflowData = parseDevFlowSpec(content);
183
+
184
+ // 转换为 OpenSpec 格式
185
+ const openspecContent = convertToOpenSpec(devflowData);
186
+
187
+ // 写入输出文件
188
+ const outputDir = path.dirname(outputPath);
189
+ if (!fs.existsSync(outputDir)) {
190
+ fs.mkdirSync(outputDir, { recursive: true });
191
+ }
192
+
193
+ fs.writeFileSync(outputPath, openspecContent, 'utf-8');
194
+
195
+ return {
196
+ success: true,
197
+ outputPath,
198
+ requirementsCount: devflowData.requirements.length
199
+ };
200
+ }
201
+
202
+ // CLI 接口
203
+ if (require.main === module) {
204
+ const args = process.argv.slice(2);
205
+
206
+ if (args.length < 2) {
207
+ console.error('Usage: export-openspec.js <devflow-spec-path> <output-path>');
208
+ process.exit(1);
209
+ }
210
+
211
+ const [devflowSpecPath, outputPath] = args;
212
+
213
+ try {
214
+ const result = exportOpenSpec(devflowSpecPath, outputPath);
215
+ console.log(JSON.stringify(result, null, 2));
216
+ } catch (error) {
217
+ console.error('Error:', error.message);
218
+ process.exit(1);
219
+ }
220
+ }
221
+
222
+ module.exports = { exportOpenSpec, parseDevFlowSpec, convertToOpenSpec };
@@ -0,0 +1,272 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * [INPUT]: 依赖 OpenSpec spec.md 文件路径,依赖 REQ-ID
4
+ * [OUTPUT]: 生成 CC-DevFlow 格式的 spec.md,自动补充 TDD 任务
5
+ * [POS]: OpenSpec 互操作层,被 /flow:import-openspec 命令调用
6
+ * [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+
12
+ /**
13
+ * 解析 OpenSpec spec.md 格式
14
+ *
15
+ * OpenSpec 格式:
16
+ * # Module Name
17
+ * ## Purpose
18
+ * ## Requirements
19
+ * ### Requirement: Name
20
+ * #### Scenario: Case
21
+ * - GIVEN ...
22
+ * - WHEN ...
23
+ * - THEN ...
24
+ */
25
+ function parseOpenSpecMarkdown(content) {
26
+ const lines = content.split('\n');
27
+ const result = {
28
+ moduleName: '',
29
+ purpose: '',
30
+ requirements: []
31
+ };
32
+
33
+ let currentSection = null;
34
+ let currentRequirement = null;
35
+ let currentScenario = null;
36
+ let buffer = [];
37
+
38
+ for (let i = 0; i < lines.length; i++) {
39
+ const line = lines[i];
40
+
41
+ // # Module Name
42
+ if (line.startsWith('# ') && !result.moduleName) {
43
+ result.moduleName = line.substring(2).trim();
44
+ continue;
45
+ }
46
+
47
+ // ## Purpose
48
+ if (line.startsWith('## Purpose')) {
49
+ currentSection = 'purpose';
50
+ buffer = [];
51
+ continue;
52
+ }
53
+
54
+ // ## Requirements
55
+ if (line.startsWith('## Requirements')) {
56
+ if (currentSection === 'purpose') {
57
+ result.purpose = buffer.join('\n').trim();
58
+ }
59
+ currentSection = 'requirements';
60
+ buffer = [];
61
+ continue;
62
+ }
63
+
64
+ // ### Requirement: Name
65
+ if (line.startsWith('### Requirement:')) {
66
+ if (currentRequirement) {
67
+ result.requirements.push(currentRequirement);
68
+ }
69
+ currentRequirement = {
70
+ name: line.substring(16).trim(),
71
+ description: '',
72
+ scenarios: []
73
+ };
74
+ currentScenario = null;
75
+ buffer = [];
76
+ continue;
77
+ }
78
+
79
+ // #### Scenario: Case
80
+ if (line.startsWith('#### Scenario:')) {
81
+ if (currentRequirement && buffer.length > 0) {
82
+ currentRequirement.description = buffer.join('\n').trim();
83
+ buffer = [];
84
+ }
85
+ currentScenario = {
86
+ name: line.substring(14).trim(),
87
+ steps: []
88
+ };
89
+ continue;
90
+ }
91
+
92
+ // BDD steps (GIVEN/WHEN/THEN)
93
+ if (currentScenario && (line.startsWith('- GIVEN') || line.startsWith('- WHEN') || line.startsWith('- THEN') || line.startsWith('- AND'))) {
94
+ currentScenario.steps.push(line.substring(2).trim());
95
+ continue;
96
+ }
97
+
98
+ // 收集内容
99
+ if (currentSection === 'purpose' && line.trim()) {
100
+ buffer.push(line);
101
+ } else if (currentRequirement && !currentScenario && line.trim()) {
102
+ buffer.push(line);
103
+ }
104
+
105
+ // 场景结束
106
+ if (currentScenario && line.trim() === '' && currentScenario.steps.length > 0) {
107
+ currentRequirement.scenarios.push(currentScenario);
108
+ currentScenario = null;
109
+ }
110
+ }
111
+
112
+ // 处理最后的 requirement
113
+ if (currentRequirement) {
114
+ if (currentScenario && currentScenario.steps.length > 0) {
115
+ currentRequirement.scenarios.push(currentScenario);
116
+ }
117
+ result.requirements.push(currentRequirement);
118
+ }
119
+
120
+ return result;
121
+ }
122
+
123
+ /**
124
+ * 转换为 CC-DevFlow spec.md 格式
125
+ */
126
+ function convertToDevFlowSpec(openspecData, reqId, title) {
127
+ const now = new Date().toISOString();
128
+
129
+ let output = `---
130
+ req_id: "${reqId}"
131
+ title: "${title}"
132
+ created_at: "${now}"
133
+ updated_at: "${now}"
134
+ version: "1.0.0"
135
+ status: "draft"
136
+ source: "openspec"
137
+ ---
138
+
139
+ # ${openspecData.moduleName}
140
+
141
+ ## Purpose
142
+
143
+ ${openspecData.purpose}
144
+
145
+ ## Requirements
146
+
147
+ `;
148
+
149
+ // 转换 Requirements
150
+ for (const req of openspecData.requirements) {
151
+ output += `### Requirement: ${req.name}\n\n`;
152
+
153
+ if (req.description) {
154
+ output += `${req.description}\n\n`;
155
+ }
156
+
157
+ // 转换 Scenarios (BDD 格式)
158
+ for (const scenario of req.scenarios) {
159
+ output += `#### Scenario: ${scenario.name}\n\n`;
160
+ for (const step of scenario.steps) {
161
+ output += `- ${step}\n`;
162
+ }
163
+ output += '\n';
164
+ }
165
+ }
166
+
167
+ // 添加 Design 部分 (空白,待填充)
168
+ output += `## Design
169
+
170
+ [NEEDS CLARIFICATION: 技术实现方案]
171
+
172
+ ### Architecture
173
+
174
+ [NEEDS CLARIFICATION: 架构设计]
175
+
176
+ ### Data Model
177
+
178
+ [NEEDS CLARIFICATION: 数据模型]
179
+
180
+ ### API Design
181
+
182
+ [NEEDS CLARIFICATION: API 设计]
183
+
184
+ ## Tasks
185
+
186
+ `;
187
+
188
+ // 自动生成 TDD 任务
189
+ let taskId = 1;
190
+ for (const req of openspecData.requirements) {
191
+ const featureName = req.name;
192
+
193
+ // TEST 任务
194
+ output += `- [ ] T${String(taskId).padStart(3, '0')} [TEST] ${featureName} - 测试\n`;
195
+ const testTaskId = taskId;
196
+ taskId++;
197
+
198
+ // IMPL 任务
199
+ output += `- [ ] T${String(taskId).padStart(3, '0')} [IMPL] ${featureName} - 实现 (dependsOn:T${String(testTaskId).padStart(3, '0')})\n`;
200
+ taskId++;
201
+ }
202
+
203
+ output += `
204
+ ## Verification
205
+
206
+ `;
207
+
208
+ // 生成验收标准
209
+ for (const req of openspecData.requirements) {
210
+ output += `- [ ] ${req.name}\n`;
211
+ for (const scenario of req.scenarios) {
212
+ output += ` - [ ] ${scenario.name}\n`;
213
+ }
214
+ }
215
+
216
+ return output;
217
+ }
218
+
219
+ /**
220
+ * 主函数
221
+ */
222
+ function importOpenSpec(openspecPath, reqId, title, outputPath) {
223
+ // 读取 OpenSpec 文件
224
+ if (!fs.existsSync(openspecPath)) {
225
+ throw new Error(`OpenSpec file not found: ${openspecPath}`);
226
+ }
227
+
228
+ const content = fs.readFileSync(openspecPath, 'utf-8');
229
+
230
+ // 解析 OpenSpec
231
+ const openspecData = parseOpenSpecMarkdown(content);
232
+
233
+ // 转换为 CC-DevFlow 格式
234
+ const devflowSpec = convertToDevFlowSpec(openspecData, reqId, title);
235
+
236
+ // 写入输出文件
237
+ const outputDir = path.dirname(outputPath);
238
+ if (!fs.existsSync(outputDir)) {
239
+ fs.mkdirSync(outputDir, { recursive: true });
240
+ }
241
+
242
+ fs.writeFileSync(outputPath, devflowSpec, 'utf-8');
243
+
244
+ return {
245
+ success: true,
246
+ outputPath,
247
+ requirementsCount: openspecData.requirements.length,
248
+ tasksCount: openspecData.requirements.length * 2 // TEST + IMPL
249
+ };
250
+ }
251
+
252
+ // CLI 接口
253
+ if (require.main === module) {
254
+ const args = process.argv.slice(2);
255
+
256
+ if (args.length < 4) {
257
+ console.error('Usage: import-openspec.js <openspec-path> <req-id> <title> <output-path>');
258
+ process.exit(1);
259
+ }
260
+
261
+ const [openspecPath, reqId, title, outputPath] = args;
262
+
263
+ try {
264
+ const result = importOpenSpec(openspecPath, reqId, title, outputPath);
265
+ console.log(JSON.stringify(result, null, 2));
266
+ } catch (error) {
267
+ console.error('Error:', error.message);
268
+ process.exit(1);
269
+ }
270
+ }
271
+
272
+ module.exports = { importOpenSpec, parseOpenSpecMarkdown, convertToDevFlowSpec };