OpenSpec — 项目学习指南
OpenSpec 是一个 AI 原生的规格驱动开发(Spec-Driven Development)CLI 工具,让开发者和 AI 编程助手在写代码之前先就"要构建什么"达成一致,从而提升 AI 辅助开发的可预测性与可维护性。
OpenSpec 项目学习指南
面向首次接触该项目的开发者,帮助你从零快速上手并深入理解 OpenSpec。
目录
1. 项目简介
一句话说明
OpenSpec 是一个 AI 原生的规格驱动开发(Spec-Driven Development)CLI 工具,让开发者和 AI 编程助手在写代码之前先就”要构建什么”达成一致,从而提升 AI 辅助开发的可预测性与可维护性。
核心功能列表
| 功能 | 说明 |
|---|---|
| 项目初始化 | openspec init — 在项目中创建规格目录结构,并为 20+ 种 AI 工具生成 Skill/Command 文件 |
| 变更管理 | 每个功能变更对应一个独立文件夹,包含提案、规格、设计、任务清单等制品(Artifact) |
| Delta 规格 | 用 ADDED / MODIFIED / REMOVED / RENAMED 四种操作描述规格变更,而非重写全文 |
| 规格归档 | openspec archive — 将 Delta 规格合并到主规格库,并将变更文件夹移入历史存档 |
| 规格验证 | openspec validate — 校验规格和变更的结构合法性,支持并发批量验证 |
| 工作流 Schema | 可自定义制品依赖图(如 proposal → specs → design → tasks),支持内置和项目级 Schema |
| 多工具集成 | 支持 Claude Code、Cursor、GitHub Copilot、Gemini CLI 等 20+ AI 工具的 Skill/Command 生成 |
| Shell 补全 | 为 bash / zsh / fish / powershell 生成命令行自动补全脚本 |
| 匿名遥测 | 仅收集命令名和版本号,可随时关闭 |
技术栈总览
| 类别 | 技术 | 版本 |
|---|---|---|
| 语言 | TypeScript | ^5.9.3 |
| 运行时 | Node.js | ≥ 20.19.0 |
| 包管理 | pnpm | — |
| CLI 框架 | commander | ^14.0.0 |
| 交互式提示 | @inquirer/prompts、@inquirer/core | ^7.8.0 / ^10.2.2 |
| 终端样式 | chalk | ^5.5.0 |
| 加载动画 | ora | ^8.2.0 |
| 文件 Glob | fast-glob | ^3.3.3 |
| YAML 解析 | yaml | ^2.8.2 |
| 数据校验 | zod | ^4.0.17 |
| 遥测 | posthog-node | ^5.20.0 |
| 测试框架 | vitest | ^3.2.4 |
| 模块系统 | ESM("type": "module") |
2. 目录结构说明
OpenSpec/
├── bin/ # CLI 可执行入口(构建产物)
│ └── openspec.js # npm 全局安装后的入口脚本
│
├── src/ # 源代码(TypeScript)
│ ├── cli/
│ │ └── index.ts # ★ CLI 主入口:注册所有命令,挂载 Hook
│ │
│ ├── commands/ # 命令层:每个文件对应一个顶层命令
│ │ ├── change.ts # openspec change(已废弃,向后兼容)
│ │ ├── completion.ts # openspec completion(Shell 补全管理)
│ │ ├── config.ts # openspec config(全局配置管理)
│ │ ├── feedback.ts # openspec feedback(提交反馈)
│ │ ├── schema.ts # openspec schema(Schema 管理)
│ │ ├── show.ts # openspec show(查看变更/规格详情)
│ │ ├── spec.ts # openspec spec(规格相关命令)
│ │ ├── validate.ts # openspec validate(验证命令)
│ │ └── workflow/ # 工作流命令组(status/instructions/templates 等)
│ │
│ ├── core/ # 核心业务逻辑层
│ │ ├── config.ts # AI 工具配置表(AI_TOOLS 常量)
│ │ ├── config-schema.ts # 全局配置的 Zod Schema 及工具函数
│ │ ├── global-config.ts # 全局配置读写(~/.config/openspec/config.json)
│ │ ├── project-config.ts # 项目配置读写(openspec/config.yaml)
│ │ ├── profiles.ts # 工作流 Profile 定义(core / custom)
│ │ ├── init.ts # openspec init 核心逻辑
│ │ ├── update.ts # openspec update 核心逻辑
│ │ ├── archive.ts # openspec archive 核心逻辑
│ │ ├── list.ts # openspec list 核心逻辑
│ │ ├── view.ts # openspec view(交互式仪表盘)
│ │ ├── specs-apply.ts # Delta 规格合并引擎(核心算法)
│ │ ├── migration.ts # 旧版本迁移逻辑
│ │ ├── legacy-cleanup.ts # 清理旧版遗留文件
│ │ ├── available-tools.ts # 检测项目中已安装的 AI 工具
│ │ ├── profile-sync-drift.ts # 检测 Profile 与实际文件的同步状态
│ │ │
│ │ ├── artifact-graph/ # 制品依赖图(DAG)解析与状态计算
│ │ ├── command-generation/ # 为各 AI 工具生成 Slash Command 文件
│ │ │ └── adapters/ # 各工具的适配器(Claude、Cursor、Gemini 等)
│ │ ├── completions/ # Shell 补全脚本生成与安装
│ │ ├── converters/ # 格式转换工具
│ │ ├── parsers/ # Markdown 解析器(规格、Delta 解析)
│ │ │ ├── markdown-parser.ts
│ │ │ └── requirement-blocks.ts # ★ Delta 规格解析核心
│ │ ├── schemas/ # 内置 Schema 定义(spec-driven 等)
│ │ ├── templates/ # 制品模板文件(proposal.md、tasks.md 等)
│ │ │ └── workflows/ # 各工作流的 Skill 模板
│ │ └── validation/ # 规格验证器
│ │ └── validator.ts # ★ 验证器核心
│ │
│ ├── prompts/
│ │ └── searchable-multi-select.ts # 自定义可搜索多选交互组件
│ │
│ ├── telemetry/ # 匿名遥测模块
│ │ ├── index.ts # 遥测主逻辑(PostHog)
│ │ └── config.ts # 遥测配置持久化
│ │
│ ├── ui/ # 终端 UI 组件
│ │ ├── ascii-patterns.ts # ASCII 艺术图案
│ │ └── welcome-screen.ts # 欢迎界面
│ │
│ └── utils/ # 通用工具函数
│ ├── file-system.ts # 文件系统操作封装
│ ├── item-discovery.ts # 发现项目中的变更/规格列表
│ ├── match.ts # 模糊匹配(用于错误提示)
│ ├── shell-detection.ts # 检测当前 Shell 类型
│ ├── task-progress.ts # 解析 tasks.md 中的任务完成进度
│ ├── change-utils.ts # 变更相关工具函数
│ └── command-references.ts # 命令引用解析
│
├── schemas/ # 内置工作流 Schema 定义(YAML)
│ └── spec-driven/
│ └── schema.yaml # 默认 Schema:proposal→specs→design→tasks
│
├── docs/ # 用户文档
│ ├── getting-started.md # 快速上手
│ ├── concepts.md # 核心概念(必读)
│ ├── cli.md # CLI 命令完整参考
│ ├── commands.md # AI Slash 命令参考
│ ├── workflows.md # 工作流模式
│ └── customization.md # 自定义 Schema 和配置
│
├── test/ # 测试文件(与 src/ 结构对应)
├── scripts/ # 构建辅助脚本
│ └── postinstall.js # npm install 后自动执行的脚本
├── build.js # 构建脚本(调用 tsc)
├── package.json # 项目元信息与依赖
└── tsconfig.json # TypeScript 编译配置
3. 架构设计
整体架构模式
OpenSpec 采用分层架构,从外到内依次为:
┌─────────────────────────────────────────────────────┐
│ CLI 入口层 │
│ src/cli/index.ts(Commander 注册) │
└──────────────────────┬──────────────────────────────┘
│ 调用
┌──────────────────────▼──────────────────────────────┐
│ 命令层(Commands) │
│ src/commands/*.ts — 参数解析、交互提示、错误处理 │
└──────────────────────┬──────────────────────────────┘
│ 调用
┌──────────────────────▼──────────────────────────────┐
│ 核心业务层(Core) │
│ src/core/*.ts — 业务逻辑、文件操作、算法 │
│ ├── 解析器(parsers/) │
│ ├── 验证器(validation/) │
│ ├── 制品图(artifact-graph/) │
│ └── 命令生成(command-generation/) │
└──────────────────────┬──────────────────────────────┘
│ 读写
┌──────────────────────▼──────────────────────────────┐
│ 文件系统(持久层) │
│ openspec/specs/、openspec/changes/、 │
│ ~/.config/openspec/config.json │
└─────────────────────────────────────────────────────┘
设计理念:
- 无数据库:所有状态以 Markdown 文件和 YAML 配置存储,人类可读,Git 友好
- 无服务端:纯本地 CLI,无需网络(遥测除外,且可关闭)
- 渐进式:核心 Profile 只有 4 个工作流,扩展 Profile 提供更多,用户按需选择
核心模块划分与职责
| 模块 | 文件 | 职责 |
|---|---|---|
| CLI 路由 | src/cli/index.ts | 注册命令、挂载全局 Hook(遥测、颜色控制) |
| 初始化 | src/core/init.ts | 创建目录结构、检测 AI 工具、生成 Skill/Command 文件 |
| 更新 | src/core/update.ts | 检测版本漂移、重新生成 Skill/Command 文件 |
| 归档 | src/core/archive.ts | 验证变更、合并 Delta 规格、移动文件夹 |
| Delta 合并 | src/core/specs-apply.ts | 解析 Delta 操作并应用到主规格,核心算法 |
| 验证 | src/core/validation/validator.ts | 校验规格和变更的 Markdown 结构 |
| 全局配置 | src/core/global-config.ts | 读写 ~/.config/openspec/config.json |
| 项目配置 | src/core/project-config.ts | 读写 openspec/config.yaml |
| Profile | src/core/profiles.ts | 定义 core/custom 两种 Profile 及其工作流集合 |
| 遥测 | src/telemetry/index.ts | 匿名上报命令名和版本(PostHog) |
模块间调用关系与数据流向
用户执行命令
│
▼
src/cli/index.ts
├── preAction Hook → 遥测(trackCommand)
├── 路由到对应 Command 类
└── postAction Hook → 遥测关闭(shutdown)
│
▼
Command 类(src/commands/)
├── 解析 CLI 参数
├── 交互式提示(@inquirer/prompts)
└── 调用 Core 层
│
▼
Core 层(src/core/)
├── 读取全局配置(global-config.ts)
├── 读取项目配置(project-config.ts)
├── 操作文件系统(utils/file-system.ts)
└── 输出结果到终端(chalk + ora)
4. 核心流程解析
4.1 应用启动流程
bin/openspec.js(npm 全局安装的入口)
│
└── import src/cli/index.ts
│
├── 创建 Commander program 实例
├── 注册全局选项(--no-color)
├── 注册 preAction Hook(遥测通知 + 命令追踪)
├── 注册 postAction Hook(遥测关闭)
├── 注册所有子命令(init / update / list / archive / validate ...)
└── program.parse() ← 解析 process.argv,触发对应命令
关键代码(src/cli/index.ts):
// 全局 Hook:每个命令执行前都会触发
program.hook('preAction', async (thisCommand, actionCommand) => {
// 1. 处理 --no-color 全局选项
if (opts.color === false) {
process.env.NO_COLOR = '1';
}
// 2. 首次运行时显示遥测通知
await maybeShowTelemetryNotice();
// 3. 追踪命令执行(仅命令名+版本,无参数内容)
const commandPath = getCommandPath(actionCommand); // e.g. "archive", "change:show"
await trackCommand(commandPath, version);
});
4.2 流程一:openspec init(项目初始化)
这是最核心的命令,完整流程如下:
openspec init
│
├── 1. 检测旧版遗留文件(legacy-cleanup.ts)
│ └── 若存在旧文件,提示用户确认清理
│
├── 2. 读取全局配置(global-config.ts)
│ └── 获取 profile(core/custom)和 delivery(both/skills/commands)
│
├── 3. 检测项目中已有的 AI 工具目录(available-tools.ts)
│ └── 扫描 .claude/、.cursor/、.github/ 等目录是否存在
│
├── 4. 工具选择(交互式多选 或 --tools 参数)
│ └── searchable-multi-select 组件
│
├── 5. 创建目录结构
│ openspec/specs/
│ openspec/changes/
│ openspec/changes/archive/
│
├── 6. 生成 Skill 和 Command 文件(核心步骤)
│ ├── 根据 profile 获取工作流列表(getProfileWorkflows)
│ ├── 获取 Skill 模板(getSkillTemplates)
│ ├── 获取 Command 内容(getCommandContents)
│ └── 对每个选中的 AI 工具:
│ ├── 写入 .claude/skills/opsx-propose/SKILL.md 等
│ └── 写入 .claude/commands/opsx-propose.md 等
│
├── 7. 创建 openspec/config.yaml(若不存在)
│
└── 8. 显示成功信息和下一步提示
关键代码(src/core/init.ts):
// 根据 profile 决定生成哪些工作流的 Skill
const profile: Profile = this.resolveProfileOverride() ?? globalConfig.profile ?? 'core';
const workflows = getProfileWorkflows(profile, globalConfig.workflows);
// core profile 返回: ['propose', 'explore', 'apply', 'archive']
const skillTemplates = shouldGenerateSkills ? getSkillTemplates(workflows) : [];
const commandContents = shouldGenerateCommands ? getCommandContents(workflows) : [];
4.3 流程二:openspec archive(变更归档)
归档是整个工作流的终点,也是 Delta 规格合并的触发点:
openspec archive add-dark-mode
│
├── 1. 验证变更(Validator.validateChangeDeltaSpecs)
│ └── 检查 specs/ 下的 Delta 规格格式是否合法
│
├── 2. 检查任务完成进度(getTaskProgressForChange)
│ └── 解析 tasks.md 中 [x] 和 [ ] 的数量
│ └── 若有未完成任务,提示用户确认
│
├── 3. 查找需要合并的规格(findSpecUpdates)
│ └── 扫描 openspec/changes/add-dark-mode/specs/ 目录
│
├── 4. 构建更新后的规格内容(buildUpdatedSpec)
│ ├── 解析 Delta 规格(parseDeltaSpec)
│ ├── 预验证:检查重复、冲突
│ └── 按顺序应用:RENAMED → REMOVED → MODIFIED → ADDED
│
├── 5. 验证合并后的规格(Validator.validateSpecContent)
│
├── 6. 写入更新后的主规格文件(writeUpdatedSpec)
│ └── openspec/specs/ui/spec.md(更新)
│
└── 7. 移动变更文件夹到归档目录
openspec/changes/add-dark-mode/
→ openspec/changes/archive/2025-01-24-add-dark-mode/
4.4 流程三:Delta 规格合并算法(specs-apply.ts)
这是项目最核心的算法,理解它是理解 OpenSpec 的关键:
// Delta 规格示例(openspec/changes/add-dark-mode/specs/ui/spec.md)
// ## ADDED Requirements
// ### Requirement: Theme Selection
// ...
// ## MODIFIED Requirements
// ### Requirement: Accessibility
// ...
// ## REMOVED Requirements
// ### Requirement: High Contrast Mode
合并步骤(顺序严格):
1. parseDeltaSpec(changeContent)
→ 解析出 { added: [], modified: [], removed: [], renamed: [] }
2. 预验证(Pre-validation)
→ 检查同一 section 内是否有重名需求
→ 检查跨 section 冲突(如同一需求既在 ADDED 又在 REMOVED)
3. 加载目标规格(targetContent)
→ 读取 openspec/specs/ui/spec.md
→ 若不存在则创建骨架文件
4. 提取需求块(extractRequirementsSection)
→ 解析 ## Requirements 下的所有 ### Requirement: xxx 块
→ 构建 Map<normalizedName, RequirementBlock>
5. 按顺序应用操作:
RENAMED → REMOVED → MODIFIED → ADDED
(顺序重要:先重命名再修改,避免找不到目标)
6. 重组规格文件
→ 保持原有顺序,新增的追加到末尾
→ 去除多余空行
4.5 流程四:openspec validate(规格验证)
openspec validate --all
│
├── 发现所有变更和规格(getActiveChangeIds / getSpecIds)
│
├── 构建并发任务队列(默认并发数 6,可通过 OPENSPEC_CONCURRENCY 调整)
│
└── 对每个条目:
├── 变更(change)→ validator.validateChangeDeltaSpecs(changeDir)
│ └── 检查 specs/ 下每个 spec.md 的 Delta 格式
└── 规格(spec)→ validator.validateSpec(file)
└── 检查 ## Purpose、## Requirements、### Requirement:、#### Scenario: 等结构
5. 关键设计与实现
5.1 设计模式
命令模式(Command Pattern)
每个 CLI 命令对应一个 Command 类(如 InitCommand、ArchiveCommand、ListCommand),封装了该命令的完整执行逻辑:
// src/core/archive.ts
export class ArchiveCommand {
async execute(changeName?: string, options = {}): Promise<void> {
// 完整的归档逻辑
}
}
适配器模式(Adapter Pattern)
src/core/command-generation/adapters/ 目录下为每种 AI 工具实现了适配器,统一接口生成不同格式的 Command 文件:
// 不同工具的 Command 文件路径格式不同
// Claude: .claude/commands/opsx-propose.md
// Cursor: .cursor/rules/opsx-propose.mdc
// 适配器屏蔽了这些差异
const adapter = CommandAdapterRegistry.get(tool.value);
const generatedCommands = generateCommands(commandContents, adapter);
策略模式(Strategy Pattern)
Profile 系统通过 getProfileWorkflows 函数,根据不同 Profile 返回不同的工作流集合:
// src/core/profiles.ts
export function getProfileWorkflows(profile: Profile, customWorkflows?: string[]) {
if (profile === 'custom') {
return customWorkflows ?? []; // 用户自定义工作流
}
return CORE_WORKFLOWS; // ['propose', 'explore', 'apply', 'archive']
}
5.2 数据模型
OpenSpec 没有数据库,所有数据以文件形式存储:
全局配置(~/.config/openspec/config.json):
{
"profile": "core",
"delivery": "both",
"workflows": [],
"featureFlags": {}
}
profile:"core"或"custom",决定安装哪些工作流delivery:"both"/"skills"/"commands",决定生成 Skill 文件还是 Command 文件workflows:custom profile 下用户选择的工作流列表
项目配置(openspec/config.yaml):
schema: spec-driven # 使用的工作流 Schema
context: | # 注入到所有制品指令中的项目上下文(可选,最大 50KB)
This is a React + TypeScript project...
rules: # 针对特定制品的额外规则(可选)
proposal:
- "Always include a rollback plan"
规格文件(openspec/specs/<domain>/spec.md):
# Auth Specification
## Purpose
...
## Requirements
### Requirement: User Authentication
The system SHALL issue a JWT token upon successful login.
#### Scenario: Valid credentials
- GIVEN a user with valid credentials
- WHEN the user submits login form
- THEN a JWT token is returned
变更文件夹(openspec/changes/<name>/):
add-dark-mode/
├── proposal.md # 提案:为什么做、做什么
├── design.md # 设计:怎么做
├── tasks.md # 任务清单:- [ ] 1.1 ...
└── specs/
└── ui/
└── spec.md # Delta 规格:ADDED/MODIFIED/REMOVED
5.3 配置读取的两层结构
OpenSpec 使用两层配置,职责分离:
全局配置(~/.config/openspec/config.json)
└── 控制 CLI 行为:profile、delivery、workflows
适用于该用户的所有项目
项目配置(openspec/config.yaml)
└── 控制项目内容:schema、context、rules
适用于当前项目,可提交到 Git
5.4 错误处理策略
- CLI 层:所有命令用
try/catch包裹,失败时用ora().fail()显示错误,process.exit(1)退出 - 遥测:所有遥测操作静默失败(
catch {}不抛出),确保遥测问题不影响 CLI 正常使用 - 配置解析:使用 Zod 的
safeParse()进行字段级别的弹性解析,部分字段无效时仍返回有效的部分配置,并打印console.warn - 文件操作:Windows 下
fs.rename可能因权限问题失败,archive.ts中实现了 copy-then-remove 的降级策略
// src/core/archive.ts — Windows 兼容的目录移动
async function moveDirectory(src: string, dest: string): Promise<void> {
try {
await fs.rename(src, dest);
} catch (err: any) {
if (err?.code === 'EPERM' || err?.code === 'EXDEV') {
// 降级:先复制再删除
await copyDirRecursive(src, dest);
await fs.rm(src, { recursive: true, force: true });
} else {
throw err;
}
}
}
5.5 遥测与隐私
遥测模块(src/telemetry/index.ts)采用隐私优先设计:
- 仅收集:命令名(如
"archive")+ 版本号(如"1.2.0") - 不收集:命令参数、文件路径、文件内容、用户信息
- 匿名 ID:随机 UUID,与用户身份无关
- 自动禁用:CI 环境(
CI=true)下自动关闭 - 关闭方式:
export OPENSPEC_TELEMETRY=0或export DO_NOT_TRACK=1
6. 环境搭建与运行
环境依赖
- Node.js ≥ 20.19.0(必须)
- pnpm(开发时使用,
npm install -g pnpm) - Git(可选,用于提交反馈)
作为用户安装使用
# 全局安装
npm install -g @fission-ai/openspec@latest
# 在你的项目中初始化
cd your-project
openspec init
# 查看所有命令
openspec --help
作为开发者本地运行
# 1. 克隆仓库
git clone https://github.com/Fission-AI/OpenSpec.git
cd OpenSpec
# 2. 安装依赖
pnpm install
# 3. 构建(TypeScript → JavaScript)
pnpm run build
# 4. 本地测试 CLI(构建后运行)
pnpm run dev:cli
# 5. 监听模式(修改代码后自动重新编译)
pnpm run dev
# 6. 运行测试
pnpm test
# 7. 运行测试(监听模式)
pnpm test:watch
# 8. 查看测试覆盖率
pnpm test:coverage
关键环境变量说明
| 变量 | 说明 | 默认值 |
|---|---|---|
OPENSPEC_TELEMETRY=0 | 关闭遥测 | 开启 |
DO_NOT_TRACK=1 | 关闭遥测(通用标准) | — |
CI=true | 自动关闭遥测(CI 环境) | — |
OPENSPEC_CONCURRENCY | 批量验证的并发数 | 6 |
NO_COLOR | 禁用终端颜色输出 | — |
EDITOR / VISUAL | openspec config edit 使用的编辑器 | — |
XDG_CONFIG_HOME | 全局配置目录(XDG 规范) | ~/.config |
XDG_DATA_HOME | 全局数据目录(XDG 规范) | ~/.local/share |
构建产物说明
pnpm run build
# 等价于:node build.js → 调用 tsc
# 输出到 dist/ 目录(TypeScript 编译为 JavaScript)
# bin/openspec.js 是入口,引用 dist/ 中的代码
7. 推荐学习路线
阶段一:入门(理解概念,约 1-2 小时)
目标:理解 OpenSpec 解决什么问题,以及核心概念
- 阅读
README.md— 了解项目定位和快速示例 - 阅读
docs/concepts.md— 必读,理解以下核心概念:- Spec(规格):描述系统行为的 Markdown 文件
- Change(变更):一个功能变更对应的文件夹
- Artifact(制品):变更文件夹中的各类文档(proposal、specs、design、tasks)
- Delta 规格:用 ADDED/MODIFIED/REMOVED 描述规格变化
- Archive(归档):将 Delta 合并到主规格,完成变更
- 阅读
docs/getting-started.md— 跟着示例走一遍完整流程 - 实际运行:在一个测试项目中执行
openspec init和openspec list
阶段二:进阶(理解代码架构,约 3-5 小时)
目标:理解代码如何实现这些概念
- CLI 入口:阅读
src/cli/index.ts- 理解 Commander 如何注册命令
- 理解
preAction/postActionHook 的作用
- 初始化流程:阅读
src/core/init.ts- 理解 AI 工具检测、Skill 生成的完整流程
- 关注
generateSkillsAndCommands方法
- Delta 合并算法:阅读
src/core/specs-apply.ts(最重要)- 理解
parseDeltaSpec如何解析 Delta 规格 - 理解
buildUpdatedSpec如何按顺序应用操作
- 理解
- 归档流程:阅读
src/core/archive.ts- 理解验证 → 合并 → 移动的完整流程
- 配置系统:阅读
src/core/global-config.ts和src/core/project-config.ts- 理解两层配置的职责划分
- 理解 XDG 规范的配置目录
阶段三:实践(动手修改,约 4-8 小时)
目标:通过实际修改验证理解
-
小任务 1:为
openspec list命令添加一个新的排序选项(如按字母倒序)- 涉及文件:
src/cli/index.ts(添加选项)、src/core/list.ts(实现逻辑)
- 涉及文件:
-
小任务 2:在
openspec validate输出中添加验证耗时统计- 涉及文件:
src/commands/validate.ts
- 涉及文件:
-
小任务 3:为一个新的 AI 工具添加适配器
- 参考
src/core/command-generation/adapters/下的现有适配器 - 在
src/core/config.ts的AI_TOOLS数组中添加新工具
- 参考
-
小任务 4:阅读并运行测试文件
test/core/archive.test.ts— 理解归档逻辑的测试方式test/core/validation.test.ts— 理解验证器的测试方式- 运行
pnpm test确认所有测试通过
8. 常见问题与注意事项
容易踩的坑
1. ESM 模块系统
项目使用 "type": "module"(纯 ESM),所有 import 语句必须带 .js 扩展名(即使源文件是 .ts):
// ✅ 正确
import { Validator } from '../core/validation/validator.js';
// ❌ 错误(会导致运行时找不到模块)
import { Validator } from '../core/validation/validator';
2. 动态 import 用于延迟加载
部分模块(如 @inquirer/prompts)使用动态 import() 延迟加载,避免非交互场景的启动开销:
// 只在需要交互时才加载
const { confirm } = await import('@inquirer/prompts');
3. Delta 规格的操作顺序
合并时操作顺序固定为 RENAMED → REMOVED → MODIFIED → ADDED,不可颠倒。如果先 MODIFIED 再 RENAMED,会因为找不到旧名称而报错。
4. openspec change 命令已废弃
openspec change show、openspec change list 等命令已废弃,应使用顶层命令:
openspec show替代openspec change showopenspec list替代openspec change listopenspec validate替代openspec change validate
5. Windows 下的文件移动
Windows 上 fs.rename 在目录非空时可能抛出 EPERM 错误,archive.ts 已有降级处理(copy + delete),但如果有杀毒软件或 IDE 占用文件,仍可能失败。
代码中的特殊约定
1. 命令路径格式
嵌套命令的路径用冒号分隔,如 change:show、completion:install,这是遥测追踪的格式:
// src/cli/index.ts
function getCommandPath(command: Command): string {
// 将 ['change', 'show'] 拼接为 'change:show'
return names.join(':') || 'openspec';
}
2. OPENSPEC_MARKERS 注释标记
配置文件中使用特殊 HTML 注释标记来标识 OpenSpec 管理的内容区域:
// src/core/config.ts
export const OPENSPEC_MARKERS = {
start: '<!-- OPENSPEC:START -->',
end: '<!-- OPENSPEC:END -->'
};
3. Skill 文件的 YAML Frontmatter
生成的 SKILL.md 文件包含 YAML Frontmatter,记录生成版本,用于 openspec update 时的版本漂移检测:
---
generatedBy: openspec@1.2.0
---
4. 规格名称规范化
需求名称在比较时会被规范化(normalizeRequirementName),转为小写并去除多余空格,因此 "User Authentication" 和 "user authentication" 被视为同一需求。
值得注意的技术债务
-
openspec change命令组:已标记为废弃(deprecated),但仍保留向后兼容。未来版本可能移除,新代码不应依赖这些命令。 -
src/core/index.ts注释:文件顶部有注释// Core OpenSpec logic will be implemented here,说明该文件目前只是导出聚合,核心逻辑分散在各子模块中,[待确认] 是否有计划统一整合。 -
项目配置缓存:
project-config.ts的注释中明确说明当前不做缓存(每次命令读 1-2 次文件,约 1-3ms),认为缓存带来的复杂度不值得。如果未来有高频读取场景,可能需要引入缓存。 -
openspec experimental命令:是openspec init的隐藏别名,已标记为废弃,仅为向后兼容保留。 -
并发验证的错误处理:
validate.ts中批量验证的错误捕获使用了getPlannedId/getPlannedType辅助函数来还原失败任务的 ID,逻辑较为脆弱,依赖队列索引与 ID 数组的对应关系。
本文档基于 OpenSpec v1.2.0 源码分析生成。如有疑问,欢迎参考 官方文档 或加入 Discord 社区。