openspec-cn 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +153 -0
- package/bin/openspec.js +3 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +480 -0
- package/dist/commands/change.d.ts +35 -0
- package/dist/commands/change.js +277 -0
- package/dist/commands/completion.d.ts +72 -0
- package/dist/commands/completion.js +257 -0
- package/dist/commands/config.d.ts +8 -0
- package/dist/commands/config.js +198 -0
- package/dist/commands/feedback.d.ts +9 -0
- package/dist/commands/feedback.js +183 -0
- package/dist/commands/schema.d.ts +6 -0
- package/dist/commands/schema.js +869 -0
- package/dist/commands/show.d.ts +14 -0
- package/dist/commands/show.js +132 -0
- package/dist/commands/spec.d.ts +15 -0
- package/dist/commands/spec.js +225 -0
- package/dist/commands/validate.d.ts +24 -0
- package/dist/commands/validate.js +294 -0
- package/dist/commands/workflow/index.d.ts +17 -0
- package/dist/commands/workflow/index.js +12 -0
- package/dist/commands/workflow/instructions.d.ts +29 -0
- package/dist/commands/workflow/instructions.js +381 -0
- package/dist/commands/workflow/new-change.d.ts +11 -0
- package/dist/commands/workflow/new-change.js +44 -0
- package/dist/commands/workflow/schemas.d.ts +10 -0
- package/dist/commands/workflow/schemas.js +34 -0
- package/dist/commands/workflow/shared.d.ts +52 -0
- package/dist/commands/workflow/shared.js +111 -0
- package/dist/commands/workflow/status.d.ts +14 -0
- package/dist/commands/workflow/status.js +58 -0
- package/dist/commands/workflow/templates.d.ts +16 -0
- package/dist/commands/workflow/templates.js +68 -0
- package/dist/core/archive.d.ts +11 -0
- package/dist/core/archive.js +280 -0
- package/dist/core/artifact-graph/graph.d.ts +56 -0
- package/dist/core/artifact-graph/graph.js +141 -0
- package/dist/core/artifact-graph/index.d.ts +7 -0
- package/dist/core/artifact-graph/index.js +13 -0
- package/dist/core/artifact-graph/instruction-loader.d.ts +143 -0
- package/dist/core/artifact-graph/instruction-loader.js +214 -0
- package/dist/core/artifact-graph/resolver.d.ts +81 -0
- package/dist/core/artifact-graph/resolver.js +257 -0
- package/dist/core/artifact-graph/schema.d.ts +13 -0
- package/dist/core/artifact-graph/schema.js +108 -0
- package/dist/core/artifact-graph/state.d.ts +12 -0
- package/dist/core/artifact-graph/state.js +54 -0
- package/dist/core/artifact-graph/types.d.ts +45 -0
- package/dist/core/artifact-graph/types.js +43 -0
- package/dist/core/command-generation/adapters/amazon-q.d.ts +13 -0
- package/dist/core/command-generation/adapters/amazon-q.js +26 -0
- package/dist/core/command-generation/adapters/antigravity.d.ts +13 -0
- package/dist/core/command-generation/adapters/antigravity.js +26 -0
- package/dist/core/command-generation/adapters/auggie.d.ts +13 -0
- package/dist/core/command-generation/adapters/auggie.js +27 -0
- package/dist/core/command-generation/adapters/claude.d.ts +13 -0
- package/dist/core/command-generation/adapters/claude.js +50 -0
- package/dist/core/command-generation/adapters/cline.d.ts +14 -0
- package/dist/core/command-generation/adapters/cline.js +27 -0
- package/dist/core/command-generation/adapters/codebuddy.d.ts +13 -0
- package/dist/core/command-generation/adapters/codebuddy.js +28 -0
- package/dist/core/command-generation/adapters/codex.d.ts +13 -0
- package/dist/core/command-generation/adapters/codex.js +27 -0
- package/dist/core/command-generation/adapters/continue.d.ts +13 -0
- package/dist/core/command-generation/adapters/continue.js +28 -0
- package/dist/core/command-generation/adapters/costrict.d.ts +13 -0
- package/dist/core/command-generation/adapters/costrict.js +27 -0
- package/dist/core/command-generation/adapters/crush.d.ts +13 -0
- package/dist/core/command-generation/adapters/crush.js +30 -0
- package/dist/core/command-generation/adapters/cursor.d.ts +14 -0
- package/dist/core/command-generation/adapters/cursor.js +44 -0
- package/dist/core/command-generation/adapters/factory.d.ts +13 -0
- package/dist/core/command-generation/adapters/factory.js +27 -0
- package/dist/core/command-generation/adapters/gemini.d.ts +13 -0
- package/dist/core/command-generation/adapters/gemini.js +26 -0
- package/dist/core/command-generation/adapters/github-copilot.d.ts +13 -0
- package/dist/core/command-generation/adapters/github-copilot.js +26 -0
- package/dist/core/command-generation/adapters/iflow.d.ts +13 -0
- package/dist/core/command-generation/adapters/iflow.js +29 -0
- package/dist/core/command-generation/adapters/index.d.ts +27 -0
- package/dist/core/command-generation/adapters/index.js +27 -0
- package/dist/core/command-generation/adapters/kilocode.d.ts +14 -0
- package/dist/core/command-generation/adapters/kilocode.js +23 -0
- package/dist/core/command-generation/adapters/opencode.d.ts +13 -0
- package/dist/core/command-generation/adapters/opencode.js +26 -0
- package/dist/core/command-generation/adapters/qoder.d.ts +13 -0
- package/dist/core/command-generation/adapters/qoder.js +30 -0
- package/dist/core/command-generation/adapters/qwen.d.ts +13 -0
- package/dist/core/command-generation/adapters/qwen.js +26 -0
- package/dist/core/command-generation/adapters/roocode.d.ts +14 -0
- package/dist/core/command-generation/adapters/roocode.js +27 -0
- package/dist/core/command-generation/adapters/windsurf.d.ts +14 -0
- package/dist/core/command-generation/adapters/windsurf.js +51 -0
- package/dist/core/command-generation/generator.d.ts +21 -0
- package/dist/core/command-generation/generator.js +27 -0
- package/dist/core/command-generation/index.d.ts +22 -0
- package/dist/core/command-generation/index.js +24 -0
- package/dist/core/command-generation/registry.d.ts +36 -0
- package/dist/core/command-generation/registry.js +88 -0
- package/dist/core/command-generation/types.d.ts +55 -0
- package/dist/core/command-generation/types.js +8 -0
- package/dist/core/completions/command-registry.d.ts +7 -0
- package/dist/core/completions/command-registry.js +456 -0
- package/dist/core/completions/completion-provider.d.ts +60 -0
- package/dist/core/completions/completion-provider.js +102 -0
- package/dist/core/completions/factory.d.ts +64 -0
- package/dist/core/completions/factory.js +75 -0
- package/dist/core/completions/generators/bash-generator.d.ts +32 -0
- package/dist/core/completions/generators/bash-generator.js +174 -0
- package/dist/core/completions/generators/fish-generator.d.ts +32 -0
- package/dist/core/completions/generators/fish-generator.js +157 -0
- package/dist/core/completions/generators/powershell-generator.d.ts +33 -0
- package/dist/core/completions/generators/powershell-generator.js +207 -0
- package/dist/core/completions/generators/zsh-generator.d.ts +44 -0
- package/dist/core/completions/generators/zsh-generator.js +250 -0
- package/dist/core/completions/installers/bash-installer.d.ts +87 -0
- package/dist/core/completions/installers/bash-installer.js +318 -0
- package/dist/core/completions/installers/fish-installer.d.ts +43 -0
- package/dist/core/completions/installers/fish-installer.js +143 -0
- package/dist/core/completions/installers/powershell-installer.d.ts +88 -0
- package/dist/core/completions/installers/powershell-installer.js +327 -0
- package/dist/core/completions/installers/zsh-installer.d.ts +125 -0
- package/dist/core/completions/installers/zsh-installer.js +449 -0
- package/dist/core/completions/templates/bash-templates.d.ts +6 -0
- package/dist/core/completions/templates/bash-templates.js +24 -0
- package/dist/core/completions/templates/fish-templates.d.ts +7 -0
- package/dist/core/completions/templates/fish-templates.js +39 -0
- package/dist/core/completions/templates/powershell-templates.d.ts +6 -0
- package/dist/core/completions/templates/powershell-templates.js +25 -0
- package/dist/core/completions/templates/zsh-templates.d.ts +6 -0
- package/dist/core/completions/templates/zsh-templates.js +36 -0
- package/dist/core/completions/types.d.ts +79 -0
- package/dist/core/completions/types.js +2 -0
- package/dist/core/config-prompts.d.ts +9 -0
- package/dist/core/config-prompts.js +34 -0
- package/dist/core/config-schema.d.ts +76 -0
- package/dist/core/config-schema.js +200 -0
- package/dist/core/config.d.ts +17 -0
- package/dist/core/config.js +30 -0
- package/dist/core/converters/json-converter.d.ts +6 -0
- package/dist/core/converters/json-converter.js +51 -0
- package/dist/core/global-config.d.ts +39 -0
- package/dist/core/global-config.js +115 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +3 -0
- package/dist/core/init.d.ts +32 -0
- package/dist/core/init.js +433 -0
- package/dist/core/legacy-cleanup.d.ts +162 -0
- package/dist/core/legacy-cleanup.js +501 -0
- package/dist/core/list.d.ts +9 -0
- package/dist/core/list.js +171 -0
- package/dist/core/parsers/change-parser.d.ts +13 -0
- package/dist/core/parsers/change-parser.js +193 -0
- package/dist/core/parsers/markdown-parser.d.ts +22 -0
- package/dist/core/parsers/markdown-parser.js +187 -0
- package/dist/core/parsers/requirement-blocks.d.ts +37 -0
- package/dist/core/parsers/requirement-blocks.js +201 -0
- package/dist/core/project-config.d.ts +64 -0
- package/dist/core/project-config.js +223 -0
- package/dist/core/schemas/base.schema.d.ts +13 -0
- package/dist/core/schemas/base.schema.js +13 -0
- package/dist/core/schemas/change.schema.d.ts +73 -0
- package/dist/core/schemas/change.schema.js +31 -0
- package/dist/core/schemas/index.d.ts +4 -0
- package/dist/core/schemas/index.js +4 -0
- package/dist/core/schemas/spec.schema.d.ts +18 -0
- package/dist/core/schemas/spec.schema.js +15 -0
- package/dist/core/shared/index.d.ts +8 -0
- package/dist/core/shared/index.js +8 -0
- package/dist/core/shared/skill-generation.d.ts +41 -0
- package/dist/core/shared/skill-generation.js +74 -0
- package/dist/core/shared/tool-detection.d.ts +66 -0
- package/dist/core/shared/tool-detection.js +140 -0
- package/dist/core/specs-apply.d.ts +73 -0
- package/dist/core/specs-apply.js +384 -0
- package/dist/core/styles/palette.d.ts +7 -0
- package/dist/core/styles/palette.js +8 -0
- package/dist/core/templates/index.d.ts +8 -0
- package/dist/core/templates/index.js +9 -0
- package/dist/core/templates/skill-templates.d.ts +112 -0
- package/dist/core/templates/skill-templates.js +2893 -0
- package/dist/core/update.d.ts +42 -0
- package/dist/core/update.js +306 -0
- package/dist/core/validation/constants.d.ts +34 -0
- package/dist/core/validation/constants.js +40 -0
- package/dist/core/validation/types.d.ts +18 -0
- package/dist/core/validation/types.js +2 -0
- package/dist/core/validation/validator.d.ts +33 -0
- package/dist/core/validation/validator.js +409 -0
- package/dist/core/view.d.ts +8 -0
- package/dist/core/view.js +168 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/prompts/searchable-multi-select.d.ts +27 -0
- package/dist/prompts/searchable-multi-select.js +149 -0
- package/dist/telemetry/config.d.ts +32 -0
- package/dist/telemetry/config.js +68 -0
- package/dist/telemetry/index.d.ts +31 -0
- package/dist/telemetry/index.js +145 -0
- package/dist/ui/ascii-patterns.d.ts +16 -0
- package/dist/ui/ascii-patterns.js +133 -0
- package/dist/ui/welcome-screen.d.ts +10 -0
- package/dist/ui/welcome-screen.js +146 -0
- package/dist/utils/change-metadata.d.ts +51 -0
- package/dist/utils/change-metadata.js +147 -0
- package/dist/utils/change-utils.d.ts +62 -0
- package/dist/utils/change-utils.js +121 -0
- package/dist/utils/file-system.d.ts +36 -0
- package/dist/utils/file-system.js +281 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/interactive.d.ts +18 -0
- package/dist/utils/interactive.js +21 -0
- package/dist/utils/item-discovery.d.ts +4 -0
- package/dist/utils/item-discovery.js +72 -0
- package/dist/utils/match.d.ts +3 -0
- package/dist/utils/match.js +22 -0
- package/dist/utils/shell-detection.d.ts +20 -0
- package/dist/utils/shell-detection.js +41 -0
- package/dist/utils/task-progress.d.ts +8 -0
- package/dist/utils/task-progress.js +36 -0
- package/package.json +84 -0
- package/schemas/spec-driven/schema.yaml +148 -0
- package/schemas/spec-driven/templates/design.md +19 -0
- package/schemas/spec-driven/templates/proposal.md +23 -0
- package/schemas/spec-driven/templates/spec.md +8 -0
- package/schemas/spec-driven/templates/tasks.md +9 -0
- package/schemas/tdd/schema.yaml +213 -0
- package/schemas/tdd/templates/docs.md +15 -0
- package/schemas/tdd/templates/implementation.md +11 -0
- package/schemas/tdd/templates/spec.md +11 -0
- package/schemas/tdd/templates/test.md +11 -0
- package/scripts/postinstall.js +147 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
name: spec-driven
|
|
2
|
+
version: 1
|
|
3
|
+
description: 默认 OpenSpec 工作流 - proposal → specs → design → tasks
|
|
4
|
+
artifacts:
|
|
5
|
+
- id: proposal
|
|
6
|
+
generates: proposal.md
|
|
7
|
+
description: 概述变更的初始提案文档
|
|
8
|
+
template: proposal.md
|
|
9
|
+
instruction: |
|
|
10
|
+
创建提案文档,说明本次变更为何需要。
|
|
11
|
+
|
|
12
|
+
Sections:
|
|
13
|
+
- **Why**: 用 1-2 句话描述问题或机会。它解决什么问题?为何现在?
|
|
14
|
+
- **What Changes**: 变更清单(项目符号)。明确说明新增能力、修改或移除的内容。破坏性变更用 **BREAKING** 标记。
|
|
15
|
+
- **Capabilities**: 标明将创建或修改哪些规范:
|
|
16
|
+
- **New Capabilities**: 列出引入的新能力。每项生成 `specs/<name>/spec.md`。使用 kebab-case 命名(例如 `user-auth`、`data-export`)。
|
|
17
|
+
- **Modified Capabilities**: 列出 REQUIREMENTS 有变化的既有能力。仅在规范层面的行为变化时包含(不含纯实现细节)。每项需要一个增量规范文件。参考 `openspec/specs/` 中已有名称。若无需求变化则留空。
|
|
18
|
+
- **Impact**: 影响到的代码、API、依赖或系统。
|
|
19
|
+
|
|
20
|
+
IMPORTANT: Capabilities 部分至关重要,它建立了
|
|
21
|
+
proposal 与 specs 阶段的契约。在填写前先研究现有规范。
|
|
22
|
+
这里列出的每个能力都需要对应的规范文件。
|
|
23
|
+
|
|
24
|
+
保持简洁(1-2 页)。聚焦“为什么”,而非“如何实现”——
|
|
25
|
+
实现细节应放在 design.md。
|
|
26
|
+
|
|
27
|
+
这是基础文档——specs、design 与 tasks 都建立在它之上。
|
|
28
|
+
requires: []
|
|
29
|
+
|
|
30
|
+
- id: specs
|
|
31
|
+
generates: "specs/**/*.md"
|
|
32
|
+
description: 变更的详细规范
|
|
33
|
+
template: spec.md
|
|
34
|
+
instruction: |
|
|
35
|
+
创建规范文件,定义系统应做什么(WHAT)。
|
|
36
|
+
|
|
37
|
+
每个能力/功能领域创建一个规范文件:`specs/<name>/spec.md`。
|
|
38
|
+
|
|
39
|
+
增量操作(使用 ## 标题):
|
|
40
|
+
- **ADDED Requirements**: New capabilities
|
|
41
|
+
- **MODIFIED Requirements**: Changed behavior - MUST include full updated content
|
|
42
|
+
- **REMOVED Requirements**: Deprecated features - MUST include **Reason** and **Migration**
|
|
43
|
+
- **RENAMED Requirements**: Name changes only - use FROM:/TO: format
|
|
44
|
+
|
|
45
|
+
需求格式:
|
|
46
|
+
- 每条需求:`### Requirement: <name>`,后接描述
|
|
47
|
+
- 规范性需求使用 SHALL/MUST(避免 should/may)
|
|
48
|
+
- 每个场景:`#### Scenario: <name>`,使用 WHEN/THEN 格式
|
|
49
|
+
- **CRITICAL**: 场景必须使用 4 个井号(`####`)。使用 3 个井号或列表会静默失败。
|
|
50
|
+
- 每条需求必须至少包含一个场景。
|
|
51
|
+
|
|
52
|
+
MODIFIED 需求流程:
|
|
53
|
+
1. 在 openspec/specs/<capability>/spec.md 中定位现有需求
|
|
54
|
+
2. 复制完整需求块(从 `### Requirement:` 到所有场景)
|
|
55
|
+
3. 粘贴到 `## MODIFIED Requirements` 下并编辑为新行为
|
|
56
|
+
4. 确保标题文本完全一致(忽略空白)
|
|
57
|
+
|
|
58
|
+
常见误区:用 MODIFIED 只写部分内容会在归档时丢失细节。
|
|
59
|
+
如果只是新增关注点而不改变现有行为,请改用 ADDED。
|
|
60
|
+
|
|
61
|
+
Example:
|
|
62
|
+
```
|
|
63
|
+
## ADDED Requirements
|
|
64
|
+
|
|
65
|
+
### Requirement: User can export data
|
|
66
|
+
The system SHALL allow users to export their data in CSV format.
|
|
67
|
+
|
|
68
|
+
#### Scenario: Successful export
|
|
69
|
+
- **WHEN** user clicks "Export" button
|
|
70
|
+
- **THEN** system downloads a CSV file with all user data
|
|
71
|
+
|
|
72
|
+
## REMOVED Requirements
|
|
73
|
+
|
|
74
|
+
### Requirement: Legacy export
|
|
75
|
+
**Reason**: Replaced by new export system
|
|
76
|
+
**Migration**: Use new export endpoint at /api/v2/export
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
规范应可测试——每个场景都是潜在测试用例。
|
|
80
|
+
requires:
|
|
81
|
+
- proposal
|
|
82
|
+
|
|
83
|
+
- id: design
|
|
84
|
+
generates: design.md
|
|
85
|
+
description: 包含实现细节的技术设计文档
|
|
86
|
+
template: design.md
|
|
87
|
+
instruction: |
|
|
88
|
+
创建设计文档,说明如何实现本次变更(HOW)。
|
|
89
|
+
|
|
90
|
+
何时需要 design.md(满足任一项才创建):
|
|
91
|
+
- 跨模块/跨服务变更或新的架构模式
|
|
92
|
+
- 新的外部依赖或显著的数据模型变更
|
|
93
|
+
- 安全、性能或迁移复杂度较高
|
|
94
|
+
- 存在需要先做技术决策的歧义
|
|
95
|
+
|
|
96
|
+
Sections:
|
|
97
|
+
- **Context**: 背景、现状、约束与相关方
|
|
98
|
+
- **Goals / Non-Goals**: 设计要达成的目标与明确排除的范围
|
|
99
|
+
- **Decisions**: 关键技术选择及其理由(为何选 X 而非 Y)。每项决策需包含考虑过的替代方案。
|
|
100
|
+
- **Risks / Trade-offs**: 已知限制与可能出错之处。格式:[风险] → 缓解措施
|
|
101
|
+
- **Migration Plan**: 上线步骤与回滚策略(如适用)
|
|
102
|
+
- **Open Questions**: 待解决的决策或未知点
|
|
103
|
+
|
|
104
|
+
聚焦架构与方案,而非逐行实现细节。
|
|
105
|
+
动机参考 proposal,需求参考 specs。
|
|
106
|
+
|
|
107
|
+
优秀的设计文档应解释技术决策背后的“why”。
|
|
108
|
+
requires:
|
|
109
|
+
- proposal
|
|
110
|
+
|
|
111
|
+
- id: tasks
|
|
112
|
+
generates: tasks.md
|
|
113
|
+
description: 从规格与设计中拆解出的实现任务
|
|
114
|
+
template: tasks.md
|
|
115
|
+
instruction: |
|
|
116
|
+
创建任务列表,拆解实现工作。
|
|
117
|
+
|
|
118
|
+
Guidelines:
|
|
119
|
+
- 使用 ## 编号标题分组相关任务
|
|
120
|
+
- 每项任务为复选框:- [ ] X.Y 任务描述
|
|
121
|
+
- 任务应足够小,便于一次会话内完成
|
|
122
|
+
- 按依赖顺序排列任务(先做哪些)
|
|
123
|
+
|
|
124
|
+
Example:
|
|
125
|
+
```
|
|
126
|
+
## 1. Setup
|
|
127
|
+
|
|
128
|
+
- [ ] 1.1 Create new module structure
|
|
129
|
+
- [ ] 1.2 Add dependencies to package.json
|
|
130
|
+
|
|
131
|
+
## 2. Core Implementation
|
|
132
|
+
|
|
133
|
+
- [ ] 2.1 Implement data export function
|
|
134
|
+
- [ ] 2.2 Add CSV formatting utilities
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
依据 specs 确定要做什么,依据 design 确定如何做。
|
|
138
|
+
每项任务应可验证——完成与否清晰可判断。
|
|
139
|
+
requires:
|
|
140
|
+
- specs
|
|
141
|
+
- design
|
|
142
|
+
|
|
143
|
+
apply:
|
|
144
|
+
requires: [tasks]
|
|
145
|
+
tracks: tasks.md
|
|
146
|
+
instruction: |
|
|
147
|
+
阅读上下文文件,逐项完成待办任务,完成后立即标记。
|
|
148
|
+
若遇到阻塞或需要澄清,暂停并说明情况。
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## Why
|
|
2
|
+
|
|
3
|
+
<!-- 说明本次变更的动机。它解决什么问题?为何现在要做? -->
|
|
4
|
+
|
|
5
|
+
## What Changes
|
|
6
|
+
|
|
7
|
+
<!-- 描述将发生的变化。明确说明新增能力、修改或移除的内容。 -->
|
|
8
|
+
|
|
9
|
+
## Capabilities
|
|
10
|
+
|
|
11
|
+
### New Capabilities
|
|
12
|
+
<!-- 引入的新能力。将 <name> 替换为 kebab-case 标识符(例如 user-auth、data-export、api-rate-limiting)。每项都会生成 specs/<name>/spec.md -->
|
|
13
|
+
- `<name>`: <此能力涵盖内容的简要说明>
|
|
14
|
+
|
|
15
|
+
### Modified Capabilities
|
|
16
|
+
<!-- 需求发生变化的既有能力(不仅是实现变化)。
|
|
17
|
+
仅在规范层面的行为发生变化时列出。每项都需要一个增量规范文件。
|
|
18
|
+
使用 openspec/specs/ 中已有的规范名称。若无需求变化则留空。 -->
|
|
19
|
+
- `<existing-name>`: <变更的需求是什么>
|
|
20
|
+
|
|
21
|
+
## Impact
|
|
22
|
+
|
|
23
|
+
<!-- 影响到的代码、API、依赖或系统 -->
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
name: tdd
|
|
2
|
+
version: 1
|
|
3
|
+
description: 测试驱动开发工作流 - tests → implementation → docs
|
|
4
|
+
artifacts:
|
|
5
|
+
- id: spec
|
|
6
|
+
generates: spec.md
|
|
7
|
+
description: 定义需求的功能规范
|
|
8
|
+
template: spec.md
|
|
9
|
+
instruction: |
|
|
10
|
+
创建功能规范,定义要构建的内容(WHAT)。
|
|
11
|
+
|
|
12
|
+
Sections:
|
|
13
|
+
- **Feature**: 功能名称与高层描述(目的与用户价值)
|
|
14
|
+
- **Requirements**: 具体需求列表,规范性表述使用 SHALL/MUST
|
|
15
|
+
- **Acceptance Criteria**: 采用 WHEN/THEN 格式的可测试标准
|
|
16
|
+
|
|
17
|
+
需求格式:
|
|
18
|
+
- 每条需求应具体且可测试
|
|
19
|
+
- 验收标准使用 `#### Scenario: <name>` 并采用 WHEN/THEN 格式
|
|
20
|
+
- 明确列出边界情况与错误场景
|
|
21
|
+
- 每条需求必须至少包含一个场景
|
|
22
|
+
|
|
23
|
+
Example:
|
|
24
|
+
```
|
|
25
|
+
## Feature: User Authentication
|
|
26
|
+
|
|
27
|
+
Users can securely log into the application.
|
|
28
|
+
|
|
29
|
+
## Requirements
|
|
30
|
+
|
|
31
|
+
### Requirement: Password validation
|
|
32
|
+
The system SHALL validate passwords meet minimum security requirements.
|
|
33
|
+
|
|
34
|
+
#### Scenario: Valid password accepted
|
|
35
|
+
- **WHEN** password has 8+ chars, uppercase, lowercase, and number
|
|
36
|
+
- **THEN** password is accepted
|
|
37
|
+
|
|
38
|
+
#### Scenario: Weak password rejected
|
|
39
|
+
- **WHEN** password is less than 8 characters
|
|
40
|
+
- **THEN** system displays "Password too short" error
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
此规范驱动测试编写——每个场景都会成为一个测试用例。
|
|
44
|
+
requires: []
|
|
45
|
+
|
|
46
|
+
- id: tests
|
|
47
|
+
generates: "tests/*.test.ts"
|
|
48
|
+
description: 在实现前编写的测试文件
|
|
49
|
+
template: test.md
|
|
50
|
+
instruction: |
|
|
51
|
+
在实现前编写测试(TDD 红阶段)。
|
|
52
|
+
|
|
53
|
+
文件命名:
|
|
54
|
+
- 测试文件命名为 `tests/<feature>.test.ts`
|
|
55
|
+
- 每个功能/能力对应一个测试文件
|
|
56
|
+
- 使用与规范一致的描述性名称
|
|
57
|
+
|
|
58
|
+
测试结构:
|
|
59
|
+
- 使用 Given/When/Then 格式,与规范场景保持一致
|
|
60
|
+
- 相关测试使用 `describe()` 分组
|
|
61
|
+
- 规范中的每个场景至少对应一个 `it()` 测试
|
|
62
|
+
|
|
63
|
+
覆盖要求:
|
|
64
|
+
- 覆盖规范中的每条需求
|
|
65
|
+
- 包含正常路径(成功案例)
|
|
66
|
+
- 包含边界情况(临界条件)
|
|
67
|
+
- 包含错误场景(非法输入、失败)
|
|
68
|
+
- 初始测试应失败(尚未实现)
|
|
69
|
+
|
|
70
|
+
Example:
|
|
71
|
+
```typescript
|
|
72
|
+
describe('Password validation', () => {
|
|
73
|
+
it('accepts valid password with all requirements', () => {
|
|
74
|
+
// GIVEN a password meeting all requirements
|
|
75
|
+
const password = 'SecurePass1';
|
|
76
|
+
// WHEN validating
|
|
77
|
+
const result = validatePassword(password);
|
|
78
|
+
// THEN it should be accepted
|
|
79
|
+
expect(result.valid).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('rejects password shorter than 8 characters', () => {
|
|
83
|
+
// GIVEN a short password
|
|
84
|
+
const password = 'Short1';
|
|
85
|
+
// WHEN validating
|
|
86
|
+
const result = validatePassword(password);
|
|
87
|
+
// THEN it should be rejected with message
|
|
88
|
+
expect(result.valid).toBe(false);
|
|
89
|
+
expect(result.error).toBe('Password too short');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
严格遵循规范需求——测试用于验证规范。
|
|
95
|
+
requires:
|
|
96
|
+
- spec
|
|
97
|
+
|
|
98
|
+
- id: implementation
|
|
99
|
+
generates: "src/*.ts"
|
|
100
|
+
description: 让测试通过的实现代码
|
|
101
|
+
template: implementation.md
|
|
102
|
+
instruction: |
|
|
103
|
+
实现功能以让测试通过(TDD 绿阶段)。
|
|
104
|
+
|
|
105
|
+
TDD 工作流:
|
|
106
|
+
1. 运行测试,确认失败(红)
|
|
107
|
+
2. 编写最少代码让一个测试通过
|
|
108
|
+
3. 运行测试,确认通过(绿)
|
|
109
|
+
4. 需要时重构,同时保持测试通过
|
|
110
|
+
5. 对下一个失败测试重复
|
|
111
|
+
|
|
112
|
+
实现准则:
|
|
113
|
+
- 为通过测试写最少代码——不多不少
|
|
114
|
+
- 频繁运行测试以验证进度
|
|
115
|
+
- 函数保持小而专注
|
|
116
|
+
- 使用清晰、可读的名称
|
|
117
|
+
|
|
118
|
+
代码组织:
|
|
119
|
+
- 在 `src/<feature>.ts` 中创建源文件
|
|
120
|
+
- 清晰导出公共 API
|
|
121
|
+
- 实现细节保持私有
|
|
122
|
+
- 为公共函数添加 JSDoc 注释
|
|
123
|
+
|
|
124
|
+
Example structure:
|
|
125
|
+
```typescript
|
|
126
|
+
/**
|
|
127
|
+
* Validates a password meets security requirements.
|
|
128
|
+
* @param password - The password to validate
|
|
129
|
+
* @returns Validation result with valid flag and optional error
|
|
130
|
+
*/
|
|
131
|
+
export function validatePassword(password: string): ValidationResult {
|
|
132
|
+
if (password.length < 8) {
|
|
133
|
+
return { valid: false, error: 'Password too short' };
|
|
134
|
+
}
|
|
135
|
+
// ... additional checks
|
|
136
|
+
return { valid: true };
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
不要过度设计——只实现测试要求的内容。
|
|
141
|
+
requires:
|
|
142
|
+
- tests
|
|
143
|
+
|
|
144
|
+
- id: docs
|
|
145
|
+
generates: "docs/*.md"
|
|
146
|
+
description: 已实现功能的文档
|
|
147
|
+
template: docs.md
|
|
148
|
+
instruction: |
|
|
149
|
+
为已实现的功能编写文档。
|
|
150
|
+
|
|
151
|
+
Sections:
|
|
152
|
+
- **Overview**: 功能说明与存在原因(1-2 段)
|
|
153
|
+
- **Getting Started**: 可立即使用的快速上手指南
|
|
154
|
+
- **Examples**: 展示常见用例的代码示例
|
|
155
|
+
- **Reference**: 详细 API 文档与配置选项
|
|
156
|
+
|
|
157
|
+
Guidelines:
|
|
158
|
+
- 面向用户,而非开发者
|
|
159
|
+
- 从最常见用例开始
|
|
160
|
+
- 提供可复制粘贴的代码示例
|
|
161
|
+
- 记录所有配置项及其默认值
|
|
162
|
+
- 说明限制、边界情况与注意事项
|
|
163
|
+
- 链接相关功能或规范
|
|
164
|
+
|
|
165
|
+
Example structure:
|
|
166
|
+
```markdown
|
|
167
|
+
## Overview
|
|
168
|
+
|
|
169
|
+
Password validation ensures user passwords meet security requirements
|
|
170
|
+
before account creation or password changes.
|
|
171
|
+
|
|
172
|
+
## Getting Started
|
|
173
|
+
|
|
174
|
+
Import and use the validation function:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import { validatePassword } from './password';
|
|
178
|
+
|
|
179
|
+
const result = validatePassword('MySecurePass1');
|
|
180
|
+
if (!result.valid) {
|
|
181
|
+
console.error(result.error);
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Examples
|
|
186
|
+
|
|
187
|
+
### Basic validation
|
|
188
|
+
...
|
|
189
|
+
|
|
190
|
+
### Custom error handling
|
|
191
|
+
...
|
|
192
|
+
|
|
193
|
+
## Reference
|
|
194
|
+
|
|
195
|
+
### validatePassword(password)
|
|
196
|
+
|
|
197
|
+
| Parameter | Type | Description |
|
|
198
|
+
|-----------|------|-------------|
|
|
199
|
+
| password | string | The password to validate |
|
|
200
|
+
|
|
201
|
+
**Returns**: `{ valid: boolean, error?: string }`
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
需求参考规范,细节参考实现。
|
|
205
|
+
requires:
|
|
206
|
+
- implementation
|
|
207
|
+
|
|
208
|
+
apply:
|
|
209
|
+
requires: [tests]
|
|
210
|
+
tracks: null
|
|
211
|
+
instruction: |
|
|
212
|
+
运行测试查看失败项。编写最少代码逐一通过。
|
|
213
|
+
重构时保持测试通过。
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Postinstall script for auto-installing shell completions
|
|
5
|
+
*
|
|
6
|
+
* This script runs automatically after npm install unless:
|
|
7
|
+
* - CI=true environment variable is set
|
|
8
|
+
* - OPENSPEC_NO_COMPLETIONS=1 environment variable is set
|
|
9
|
+
* - dist/ directory doesn't exist (dev setup scenario)
|
|
10
|
+
*
|
|
11
|
+
* The script never fails npm install - all errors are caught and handled gracefully.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { promises as fs } from 'fs';
|
|
15
|
+
import path from 'path';
|
|
16
|
+
import { fileURLToPath } from 'url';
|
|
17
|
+
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = path.dirname(__filename);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Check if we should skip installation
|
|
23
|
+
*/
|
|
24
|
+
function shouldSkipInstallation() {
|
|
25
|
+
// Skip in CI environments
|
|
26
|
+
if (process.env.CI === 'true' || process.env.CI === '1') {
|
|
27
|
+
return { skip: true, reason: 'CI environment detected' };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Skip if user opted out
|
|
31
|
+
if (process.env.OPENSPEC_NO_COMPLETIONS === '1') {
|
|
32
|
+
return { skip: true, reason: 'OPENSPEC_NO_COMPLETIONS=1 set' };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return { skip: false };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Check if dist/ directory exists
|
|
40
|
+
*/
|
|
41
|
+
async function distExists() {
|
|
42
|
+
const distPath = path.join(__dirname, '..', 'dist');
|
|
43
|
+
try {
|
|
44
|
+
const stat = await fs.stat(distPath);
|
|
45
|
+
return stat.isDirectory();
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Detect the user's shell
|
|
53
|
+
*/
|
|
54
|
+
async function detectShell() {
|
|
55
|
+
try {
|
|
56
|
+
const { detectShell } = await import('../dist/utils/shell-detection.js');
|
|
57
|
+
const result = detectShell();
|
|
58
|
+
return result.shell;
|
|
59
|
+
} catch (error) {
|
|
60
|
+
// Fail silently if detection module doesn't exist
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Install completions for the detected shell
|
|
67
|
+
*/
|
|
68
|
+
async function installCompletions(shell) {
|
|
69
|
+
try {
|
|
70
|
+
const { CompletionFactory } = await import('../dist/core/completions/factory.js');
|
|
71
|
+
const { COMMAND_REGISTRY } = await import('../dist/core/completions/command-registry.js');
|
|
72
|
+
|
|
73
|
+
// Check if shell is supported
|
|
74
|
+
if (!CompletionFactory.isSupported(shell)) {
|
|
75
|
+
console.log(`\nTip: Run 'openspec completion install' for shell completions`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Generate completion script
|
|
80
|
+
const generator = CompletionFactory.createGenerator(shell);
|
|
81
|
+
const script = generator.generate(COMMAND_REGISTRY);
|
|
82
|
+
|
|
83
|
+
// Install completion script
|
|
84
|
+
const installer = CompletionFactory.createInstaller(shell);
|
|
85
|
+
const result = await installer.install(script);
|
|
86
|
+
|
|
87
|
+
if (result.success) {
|
|
88
|
+
// Show success message based on installation type
|
|
89
|
+
if (result.isOhMyZsh) {
|
|
90
|
+
console.log(`✓ Shell completions installed`);
|
|
91
|
+
console.log(` Restart shell: exec zsh`);
|
|
92
|
+
} else if (result.zshrcConfigured) {
|
|
93
|
+
console.log(`✓ Shell completions installed and configured`);
|
|
94
|
+
console.log(` Restart shell: exec zsh`);
|
|
95
|
+
} else {
|
|
96
|
+
console.log(`✓ Shell completions installed to ~/.zsh/completions/`);
|
|
97
|
+
console.log(` Add to ~/.zshrc: fpath=(~/.zsh/completions $fpath)`);
|
|
98
|
+
console.log(` Then: exec zsh`);
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
// Installation failed, show tip for manual install
|
|
102
|
+
console.log(`\nTip: Run 'openspec completion install' for shell completions`);
|
|
103
|
+
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
// Fail gracefully - show tip for manual install
|
|
106
|
+
console.log(`\nTip: Run 'openspec completion install' for shell completions`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Main function
|
|
112
|
+
*/
|
|
113
|
+
async function main() {
|
|
114
|
+
try {
|
|
115
|
+
// Check if we should skip
|
|
116
|
+
const skipCheck = shouldSkipInstallation();
|
|
117
|
+
if (skipCheck.skip) {
|
|
118
|
+
// Silent skip - no output
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Check if dist/ exists (skip silently if not - expected during dev setup)
|
|
123
|
+
if (!(await distExists())) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Detect shell
|
|
128
|
+
const shell = await detectShell();
|
|
129
|
+
if (!shell) {
|
|
130
|
+
console.log(`\nTip: Run 'openspec completion install' for shell completions`);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Install completions
|
|
135
|
+
await installCompletions(shell);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
// Fail gracefully - never break npm install
|
|
138
|
+
// Show tip for manual install
|
|
139
|
+
console.log(`\nTip: Run 'openspec completion install' for shell completions`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Run main and handle any unhandled errors
|
|
144
|
+
main().catch(() => {
|
|
145
|
+
// Silent failure - never break npm install
|
|
146
|
+
process.exit(0);
|
|
147
|
+
});
|