@oevery/rsp 3.1.0-beta.5 → 3.2.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/README.zh-CN.md CHANGED
@@ -2,372 +2,83 @@
2
2
 
3
3
  [English](./README.md) | 简体中文
4
4
 
5
- **面向人类与 AI Agent 的仓库原生工程工作流。**
5
+ **面向人类与 AI 智能体的仓库原生工程工作流。**
6
6
 
7
- RSP 将模糊意图逐步转化为边界明确、经过实现、审查和验证的软件变更,同时让项目知识可以长期维护、让未完成工作可以可靠恢复。十一个可组合 Skills 共同覆盖工程旅程,但不引入隐藏 workflow state,也不取代项目自己的文件、工具和权限边界。
8
-
9
- 这套工作流建立在轻量的 **Rules、Specs、Plans** artifact foundation 之上。
7
+ RSP 把模糊意图转化为经过塑造、实现、审查和验证的软件变更,同时让项目知识可持久化、工作可恢复。它使用普通仓库文件——规则、规格和计划——而不是隐藏的工作流状态。
10
8
 
11
9
  ## RSP 帮助你完成什么
12
10
 
13
- - 将模糊工作塑造成一个可执行 Change,或一组彼此独立的小切片。
14
- - 在实现前,根据仓库证据解决关键设计问题。
15
- - 将原因不明的失败交给 diagnosis,将普通且证据充分的修改交给 implementation,仅将明确要求或具有具体风险的 test-first 工作交给 TDD。
16
- - 按明确范围审查变更、处理已接受的 findings,并重新运行 fresh verification。
17
- - 将稳定事实、作用域指令、长期理由和完成历史分别保存在正确的 owner 中。
18
- - 为显式且已确认的 release operation 准备或校准发布文档;只有重大协调需要持久 owner 时才使用 Release Change。
19
-
20
- ## 工作流如何组合
11
+ - 把模糊工作塑造成一个可执行 Change。
12
+ - 在实现前根据仓库证据解决设计问题。
13
+ - 把诊断、实现、TDD、审查和发布工作路由给明确的能力。
14
+ - 把当前事实、长期理由、作用域指令和完成历史保存在正确的归属位置。
15
+ - 保持权限可见:RSP 不会推断提交、发布、部署或批准权限。
21
16
 
22
17
  ```text
23
- 意图
24
- → Shape
25
- → 按需 Design
26
- → Diagnose | TDD | Implement
27
- → Review → Address accepted findings
28
- → 显式且确认后的 Release Docs
29
- → Durable Review → Archive
18
+ 意图 → 塑造 → 必要时设计 → 诊断 | TDD | 实现
19
+ → 审查 → 持久化审查 → 归档
30
20
  ```
31
21
 
32
- RSP 根据 selected Change、仓库证据、验证结果和 blockers 推导下一步动作。每个能力都把结果返回已有的项目或 RSP owner。RSP 不会自行推断修改代码、继续 Git 操作、commit、publish、deploy 或 approve 的权限。
33
-
34
- ## 快速开始
35
-
36
- ```bash
37
- npx -y @oevery/rsp@3.1.0-beta.5 init
38
- npx -y @oevery/rsp@3.1.0-beta.5 doctor
39
- ```
22
+ ## 五分钟入门
40
23
 
41
- 推荐启动流程:
24
+ RSP 3.1 需要 Node.js 22 或更高版本。使用当前稳定版本:
42
25
 
43
26
  ```bash
44
- npx -y @oevery/rsp@3.1.0-beta.5 init --with-project-setup
27
+ npx -y @oevery/rsp@latest init --with-project-setup
45
28
  # 填写 .rsp/changes/project-setup.md
46
29
  # 填写 .rsp/specs/design.md
47
- npx -y @oevery/rsp@3.1.0-beta.5 doctor
48
- ```
49
-
50
- ## Artifact foundation
51
-
52
- `Reliable Software Practice` 表达产品承诺;`Rules、Specs、Plans` 描述让这套工作流保持仓库原生的轻量 artifact model:
53
-
54
- - nearest `AGENTS.md` 存放有作用域的项目或模块操作指令。
55
- - `rsp-rules.md` 是最小、tool-agnostic 的 fallback protocol。
56
- - `specs/` 存放项目级 source-of-truth 文档。
57
- - `changes/` 以单文件格式存放 open work。
58
- - `focus.d/` 用空标记文件镜像当前聚焦的 changes。
59
- - `archives/` 存放已完成工作。
60
-
61
- ```text
62
- .rsp/
63
- ├── rsp-rules.md # 最小 fallback protocol
64
- ├── specs/
65
- │ ├── 00-index.md # 自动生成的直接子项导航
66
- │ ├── design.md
67
- │ ├── decisions/ # 默认的权威 Decision Records;不进入 Specs 索引
68
- │ └── <domain>/
69
- │ ├── 00-index.md # 自动生成的直接子项导航
70
- │ └── <spec>.md
71
- ├── changes/
72
- │ ├── <name>.md
73
- │ └── <group>/
74
- │ ├── 00-brief.md
75
- │ └── <change>.md
76
- ├── focus.d/
77
- │ └── <name>
78
- └── archives/
79
- └── YYYY-MM-DD_<change>.md
80
- ```
81
-
82
- ## 概念
83
-
84
- - `specs/` 描述长期稳定的项目事实和当前认可的设计。
85
- - Decision Records 描述难以逆转选择的长期理由、备选方案、取舍和后果;默认位于 `.rsp/specs/decisions/`,也可以配置唯一的外部权威路径。
86
- - `changes/` 描述 open work,包括 feature、fix、refactor、docs、ops 和 research。
87
- - 每个 change 始终是单个 Markdown 文件,并包含 proposal、spec、design、tasks、verification 与 blockers 等明确 section。
88
- - Change 保持为“当前计划与最终证据”的收敛快照:选择足以证明结果的最小验证,只保留具有长期价值的新测试,并让临时 probe 和执行流水留在持久产物之外。持久正文描述真实的领域、系统、用户或操作员;只有 AI 或 agent 确实是产品参与者或约束时才提及它们。
89
- - 精确 blocker 行 `- requires \`<change-work-ref>\`: <reason>` 表示依赖另一个可执行 Change。自由文本 blocker 保持为外部阻塞,RSP 不会从 prose 猜测依赖边;前置 Change 归档后依赖会自动解除。
90
- - Change 名称只能是扁平的 `<change>` 或一层直接子项 `<group>/<change>`;递归 work 目录会被拒绝。
91
- - Change Group 是可选且唯一的复合 work 形态。其不可执行、不可 focus 的逻辑身份 `<group>/brief` 在磁盘上存储为 `<group>/00-brief.md`,为至少两个直接子 Change 统一拥有目标、共享约束、slice 声明、整体完成条件、durable outcomes 和 blockers。
92
- - 必须先创建 Group 再创建子 Change。每个 grouped Change 都必须由 brief 声明,独立 focus、独立归档,并将 brief 纳入上下文。没有 focus 时,`status` 按 Brief 声明顺序和当前 blockers 推荐第一个可执行 slice。`status`、`check` 和 `doctor` 从文件事实推导 Group 健康度和完成度,不持久化额外状态;`rsp group close` 只归档已完成的 brief。已归档的 Group 身份不能重新打开。
93
- - Group Brief blocker 会作为外部 blocker 派生到其直接子 Change,但不会生成猜测的依赖边。
94
- - 同一个 work 身份不能同时由 Markdown 文件和目录占用。
95
- - `rsp status`、`rsp check` 和 `rsp doctor` 使用同一套完整 work-tree 与依赖事实检查。`status` 无需 graph 文件即可派生带原因的精确依赖边、ready work、blockers 和稳定 waves;`check` 与 `doctor` 会拒绝不完整的 archive inspection、格式错误或缺失的依赖目标、自依赖与循环。`changes/` 根目录必须存在且是真实目录,已有的 focus/archive 根目录和 group 前缀也必须是真实目录;非法目录、非 Markdown 条目、符号链接、缺失或不可读的当前 work、不完整读取和身份冲突都会成为可见错误。`status` 会返回非零状态,而不是隐藏非法 work。
96
- - `rsp init`、`rsp update`、`rsp add spec` 和生成索引使用相同的 no-follow managed-path 检查。archive discovery 只接受扁平 archive 文件或一层真实 group 目录;递归组织的 Specs 只接受真实目录和普通文件。
97
- - 项目 `AGENTS.md`、focus marker、fallback/config、生成索引和 placeholder 等最终 managed file 也必须是普通文件;RSP 会在读写前拒绝静态 symlink 目标。
98
- - 完成后的 change 会移动到 `archives/`。稳定当前事实写入 Specs;长期理由写入 Decision Records;稳定的作用域操作指令写入 nearest project-owned `AGENTS.md`。
99
- - 不要把任务历史、排障笔记或一次性实现上下文提升到 Specs、Decision Records 或项目指令。
100
- - Change `Spec` 中的 delta 标记仅为规划辅助。`rsp archive` 不会自动将它们提升到 Specs 或 Decision Records。
101
- - `rsp check` 会执行 deterministic 的卫生检查。它会对未完成的模板占位符和未解决的 clarification 标记发出 warning,但这些 warning 不会替代 durable update 的语义判断。
102
-
103
- ## 文件所有权
104
-
105
- - `AGENTS.md`:只有 `<!-- rsp:begin --> ... <!-- rsp:end -->` 受管块由 RSP 维护。
106
- - `.rsp/specs/**/00-index.md`:为根目录及含内容的 Spec 目录生成直接子项导航。使用 `rsp update` 对齐全部本地索引;`rsp add spec` 只刷新受影响的目录链。
107
- - `.rsp/specs/design.md`:由 `rsp init` 创建,之后由项目维护。
108
- - `.rsp/specs/decisions/`:默认的权威 Decision Record 目录;只有 Host Project 已有外部 ADR 目录时才配置 `decisions.path`。
109
- - `.rsp/rsp-rules.md`:生成的最小 fallback protocol;可用时优先加载 `rsp` skill。
110
- - 将项目级边界放在 `.rsp/specs/design.md`;通过最近的 `00-index.md` 发现归属于最小领域 Spec 的成组、可复用事实。
111
- - 所有 `00-index.md` 都只是生成导航,不能作为可编辑的当前事实或理由 owner。每个索引只列直接子 Spec 与含内容的直接子目录,并排除 Decision Records。
112
- - 将稳定且有作用域的工作流、验证和本地运行指令放在 nearest project-owned `AGENTS.md` 的非受管区域。
113
-
114
- ## Decision Record 路径
115
-
116
- 默认权威目录是 `.rsp/specs/decisions/`。如果 Host Project 已在其他位置维护 ADR,只配置一个项目相对的外部路径:
117
-
118
- ```yaml
119
- decisions:
120
- path: docs/adr
121
- ```
122
-
123
- 该路径不能是绝对路径、不能逃逸 Host Project,也不能指向其他 `.rsp/` 核心位置。`rsp init` 和 `rsp update` 会在写入受管文件前校验路由并确保目录存在,`rsp show` 与 `rsp ready` 会拒绝不安全路由,`rsp doctor` 检查目录可读性和迁移健康;这些命令都不会创建 Decision Record。切换到外部路径不会自动迁移默认目录中的记录;在旧 `.rsp/specs/decisions/*.md` 被迁移或明确删除前,`rsp doctor` 会持续报告问题。
124
-
125
- ## AGENTS 接入
126
-
127
- 受管块示例:
128
-
129
- ```md
130
- <!-- rsp:begin -->
131
- ## RSP Entry
132
-
133
- RSP tracks current work, stable specs, and archives under `.rsp/`.
134
-
135
- Read in order:
136
- 1. Nearest `AGENTS.md` for project or module instructions.
137
- 2. Root `CONTEXT-MAP.md` if present, then the relevant nearest `CONTEXT.md`.
138
- 3. The `rsp` skill; if unavailable, read `.rsp/rsp-rules.md` as the fallback protocol.
139
- 4. `.rsp/focus.d/`; for grouped work read the sibling Group Brief, then the explicitly selected focused Change.
140
- 5. Only the relevant Specs and Decision Records under the configured authoritative path.
141
-
142
- If `.rsp/focus.d/` is empty and the user has not provided a concrete task, ask what to work on or suggest `npx -y @oevery/rsp create <name>` for tracked work.
143
- Do not treat `.rsp/specs/` or `.rsp/changes/` as replacements for nearest `AGENTS.md` or `CONTEXT.md`.
144
- <!-- rsp:end -->
145
- ```
146
-
147
- `rsp init --agents-mode <mode>`:
148
-
149
- - `managed`:在需要时创建 `AGENTS.md`,并插入或更新受管块。
150
- - `print`:正常初始化,并额外打印最终的 `AGENTS.md` 内容。
151
-
152
- ## Skills
153
-
154
- RSP 发布十一个默认、宿主无关、按需加载的生命周期 Skills,以及一个需要显式安装的项目级审计 Skill:
155
-
156
- | Skill | 职责 |
157
- |---|---|
158
- | `rsp` | 派生下一动作,指导 setup、durable review 与 archive 判断。 |
159
- | `rsp-shape` | 在不实现的前提下塑造一个可执行 Change 或合理的 shallow Group。 |
160
- | `rsp-design` | 解决一个受跟踪的 domain、module/seam 或 reversible-exploration 问题。 |
161
- | `rsp-implement` | 实现一个 selected ready Change,并提供 fresh verification。 |
162
- | `rsp-diagnose` | 修正前确认原因,或如实返回 unresolved diagnosis。 |
163
- | `rsp-tdd` | 让一个清晰行为经过 RED、GREEN 与安全的 REFACTOR。 |
164
- | `rsp-review` | 对固定的 Code、Document 或 mixed comparison 做只读审查。 |
165
- | `rsp-resolve-findings` | 处置固定 findings,修正 accepted 项,验证并请求复审。 |
166
- | `rsp-commit` | 使用与仓库一致的结构化消息创建一个已授权、精确范围的本地提交。 |
167
- | `rsp-release-docs` | 起草、审计、定稿或校准基于证据的发布表面。 |
168
- | `rsp-manage` | 继续一个显式请求或项目启用且符合条件的 ready Change 或 shallow Group。 |
169
- | `rsp-structural-audit`(可选) | 只读审计一个有界仓库或子树,发现有证据支撑的结构风险。 |
170
-
171
- 每个生命周期 Skill 都把结果返回现有项目或 RSP owner。只读的 Pre-Change Design 与 `rsp-structural-audit` 可以把一个有界结果直接返回用户,而不虚构 artifact owner。套件不引入隐藏 workflow state 或递归 Skill 编排。任何 Skill 都不会推断 commit、push、publication、deployment、approval 或 human-acceptance 权限。
172
-
173
- 响应语言归用户和会话所有;产物与 commit message 语言才是项目持久表面。项目 `.rsp/config.yaml` 绝不选择响应语言:响应正文依次遵循当前明确请求、用户/宿主个人指令和会话语言。项目可以设置共享的 `language.default`,并通过 `language.artifacts` 或 `language.commit` 分别覆盖。新产物正文依次遵循明确产物语言、有效产物语言、项目产物指令和会话语言;已有产物除非明确授权翻译,否则保持现有语言。Commit 正文依次遵循当前明确的 commit 语言、有效 commit 语言、仓库 commit 规则和清晰的近期非 merge 历史。配置变化不会重写历史。CLI 生成的脚手架只包含 canonical 结构 token、中立的 `<…>` 占位符和用户原样输入,不维护 locale 字典,也不会静默回退为英文正文;Core 与 discipline Skills 再按授权语言填写自然语言内容。RSP 的 canonical headings、路径、命令、标识符、Conventional Commit type/scope、trailers 和机器消费值保持不变;WorkRef 遵循安全 Unicode 身份契约。
174
-
175
- ```yaml
176
- language:
177
- default: en
178
- ```
179
-
180
- 配置了 `language` mapping 时,`default` 必填并同时为产物和 commit 提供持久默认值;可选的 `artifacts` 与 `commit` 只覆盖对应表面。`response` 被明确禁止,因为共享仓库不应限制每位协作者的会话语言。完全省略 `language` 时,两项持久有效值都保持未设置。普通 status 显示持久语言值,status JSON 则始终把 `language.artifacts` 与 `language.commit` 投影为规范化语言标签或 `null`。语言配置不会授予 stage、commit、push 或 publication 权限。
181
-
182
- 按证据组合套件:Shape 确定 owner;Design 返回一个关键问题;Core 在 Diagnose、TDD 与 Implement 之间选择;Review 保持只读;Resolve Findings 修正 accepted findings 并请求复审;Core 在 archive 前执行 durable decision。显式 release operation 在 identity 或 range 已确认时,可以不创建 Release Change 就进入 Release Docs;只有重大决策、协调、恢复、blocker 或 acceptance 需要持久 owner 时才创建。Manage 是可选能力:它接受一个 selected ready Change 或 shallow Group,并要求独立调度、长时 continuation 或恢复需求;小型或紧耦合工作保持直接执行。项目可以保持显式启用,也可以让 Core 自动选择符合条件的 managed work。
183
-
184
- ### Managed automation policy
185
-
186
- 在 `.rsp/config.yaml` 中分别配置自动选择与本地收尾:
187
-
188
- ```yaml
189
- manage:
190
- activation: auto
191
- closeout: lifecycle
192
- ```
193
-
194
- `activation` 可取 `explicit` 或 `auto`。`auto` 只授权 Core 为符合条件且已经请求完成或继续的工作选择 Manage;它不授予 planning、product mutation、lifecycle、Git 或外部权限。`closeout` 可取:
195
-
196
- - `manual`:archive 与 commit 都不会自动执行。
197
- - `lifecycle`:durable review 后可以 archive,但 commit 仍需要独立权限。
198
- - `local`:允许 lifecycle closeout 和有独立依据的 recovery checkpoint;lifecycle closeout 后,符合条件且 clean、已验证的非小型终态边界会调用一次 `rsp-commit`,小型终态仍不提交。
199
-
200
- Change 粒度由一个可观察结果及其共享的 acceptance、verification、review、archive 和 rollback 边界决定,不规定 Git commit 数量。每次 `rsp-commit` 调用仍只创建一个本地 commit。
201
-
202
- 新的 `rsp init` config template 使用上面的 `auto` 加 `lifecycle` policy。如果省略 `manage`,RSP 则为兼容既有显式 Manage 行为解析为 `activation: explicit` 与 `closeout: local`。普通 `rsp status` 和 `rsp status --json` 顶层的 `manage` 对象都会显示解析后的值。nearest scoped restriction 与 host enforcement 可以缩小该配置上限。RSP 有意不提供 `full` preset:push、tag、publication、deployment、approval 与 human acceptance 始终保持显式且属于外部边界。
203
-
204
- 人类应从本 README 开始;agent 应遵循 nearest `AGENTS.md`,优先加载 `skills/rsp/SKILL.md`,仅在 Skill 不可用时读取 `.rsp/rsp-rules.md`。
205
-
206
- 如果文档中写的是 `rsp <command>`,默认前提是你的环境里已经能直接运行 `rsp`。进行 opt-in beta 评估时,应固定精确 prerelease 身份,例如 `npx -y @oevery/rsp@3.1.0-beta.5 <command>`;stable 用户仍可使用不带版本的入口获取 npm `latest`。
207
-
208
- 将当前精确包内置的默认生命周期 Skill 套件安装到当前项目:
209
-
210
- ```bash
211
- rsp skills install --dry-run
212
- rsp skills install
30
+ npx -y @oevery/rsp@latest doctor
213
31
  ```
214
32
 
215
- 在 dual TTY 中,`rsp skills` 会打开交互式管理器:默认套件保持选中并锁定,可在确认一次原子安装前选择可选的项目级 Skill。脚本、重定向终端和 CI 使用确定性的发现与安装命令:
33
+ 然后创建并聚焦一个受跟踪变更:
216
34
 
217
35
  ```bash
218
- rsp skills list
219
- rsp skills list --json
36
+ rsp create improve-login "让登录失败信息可供用户采取行动"
37
+ rsp focus improve-login
38
+ rsp show --focused
220
39
  ```
221
40
 
222
- 该命令会预检十一个默认 package-owned targets,保留无关的 `.agents/skills` 条目(包括可选 Skills),并且只有显式传入 `--force` 才会替换内容不同的已选目录。按精确名称安装可选的项目级审计 Skill:
223
-
224
- 升级仍包含 `rsp-address-review` 或 `rsp-codebase-audit` 的安装时,其替代项分别是 `rsp-resolve-findings` 和 `rsp-structural-audit`。安装器会在变更前停止,直到 `--force` 显式授权事务性移除这个过时的 package-owned 目录;`--dry-run --force` 可同时预览移除与安装。
41
+ 遵循最近的 `AGENTS.md`,在工作推进时同步当前聚焦的 Change,运行最新的项目检查,判断是否需要持久化更新,并仅在满足验收条件后归档。
225
42
 
226
- ```bash
227
- rsp skills install rsp-structural-audit --dry-run
228
- rsp skills install rsp-structural-audit
229
- ```
43
+ [阅读完整入门指南](./docs/site/zh-CN/getting-started.md)。
230
44
 
231
- 两种形式都从调用 `rsp` 的同一个包安装,因此 beta 评估可以固定精确 npm 身份:
232
-
233
- ```bash
234
- npx -y @oevery/rsp@3.1.0-beta.5 skills install --dry-run
235
- npx -y @oevery/rsp@3.1.0-beta.5 skills install
236
- ```
237
-
238
- 精确 prerelease 身份可以避免依赖持续移动的 dist-tag。`rsp update` 只刷新由 RSP 管理的项目文件;刷新 package-owned Skill 套件需要单独运行 `rsp skills install`。
239
-
240
- ## 从 2.x 迁移
241
-
242
- 版本 3 仅使用 `.rsp/rsp-rules.md` 作为运行时 fallback,并从 RSP 模型中移除了 project rules:
243
-
244
- 完整的兼容性、恢复与验证说明见 [3.0 migration guide](https://github.com/oevery/rsp/blob/v3.0.0/docs/migrations/3.0.md)。
245
-
246
- 1. 升级 RSP CLI。
247
- 2. 运行 `rsp update`,生成 canonical fallback 并删除旧的 `.rsp/rules/rsp-rules.md` 生成文件。
248
- 3. 如果 `.rsp/rules/` 仍然存在,只把残留内容中稳定且有作用域的指令迁入 nearest project-owned `AGENTS.md`,然后删除旧条目。
249
- 4. 将深于 `.rsp/changes/<group>/<change>.md` 的 work path 扁平化,并手动解决 `.rsp/changes/<name>.md` 与 `.rsp/changes/<name>/` 的身份冲突。
250
- 5. 运行 `rsp doctor`,处理所有剩余迁移问题。
251
-
252
- 仓库维护者可以把外部 skill 和 workflow 来源作为离线评审输入进行跟踪。该工具有意不进入发布包;具体用法见[源码仓库维护指南](https://github.com/oevery/rsp/blob/main/docs/upstreams.md)。
253
-
254
- ## 工作模型
45
+ ## 产物模型
255
46
 
256
47
  ```text
257
- open → archived
48
+ .rsp/
49
+ ├── rsp-rules.md # 最小后备协议
50
+ ├── specs/ # 持久化的当前事实
51
+ ├── changes/ # 未完成工作
52
+ ├── focus.d/ # 选择当前工作的空标记文件
53
+ └── archives/ # 已完成历史
258
54
  ```
259
55
 
260
- 各目录职责应保持单一且明确:
261
-
262
- - `changes/`:open changes
263
- - `focus.d/`:当前聚焦的 changes
264
- - `archives/`:完成后的历史
56
+ 一个 Change 是包含规范的 Proposal、Spec、Design、Tasks、Verify 和 Blockers 章节的单个 Markdown 文件。稳定事实属于 Specs,长期理由属于 Decision Records,稳定且有作用域的操作指令属于最近的项目自有 `AGENTS.md`。
265
57
 
266
- 在 `open` 阶段,常见活动包括:
58
+ ## 文档
267
59
 
268
- - `create`:创建并界定一个 change。
269
- - `focus` / `unfocus`:调整当前聚焦的 open change。
270
- - 直接编辑 change 文件,填写 section、勾选 tasks、记录设计决策。
271
- - 在归档前判断是否需要 durable updates。
60
+ - [开始使用](./docs/site/zh-CN/getting-started.md)
61
+ - [核心概念与产物归属](./docs/site/zh-CN/concepts.md)
62
+ - [日常工作流](./docs/site/zh-CN/guides/daily-workflow.md)
63
+ - [Skills 与受管工作](./docs/site/zh-CN/guides/skills.md)
64
+ - [配置参考](./docs/site/zh-CN/reference/configuration.md)
65
+ - [CLI 参考](./docs/site/zh-CN/reference/cli.md)
66
+ - [3.0 迁移指南](./docs/migrations/3.0.md)与 [3.1 迁移指南](./docs/migrations/3.1.md)
67
+ - [发布说明](./docs/releases/3.2.0.md)
68
+ - [设计哲学](./docs/maintainers/design-philosophy.md)与[维护者上游研究](./docs/maintainers/upstreams.md)
272
69
 
273
- 实现过程中,应保持 change 文件与实际工作同步:代码完成后勾选对应的 `## Tasks`,在 `## Verify` 中记录实际运行的检查;如果实现发现设计不成立,应先更新 `## Design` 再继续。
70
+ 在本地运行文档:
274
71
 
275
- `archive` 会把已完成的工作归档到历史。`archive` 不会阻塞,只会给出 warning,最终判断留给 agent 或人工。
276
-
277
- agent 应只把 `focus.d/` 中列出的 change 视为当前工作。`changes/` 中未聚焦的文件仍然是 open,但除非用户明确要求或重新 `focus`,否则不应被当作当前目标。
278
-
279
- durable review 包含两个独立语义判断:是否更新当前事实或作用域指令,以及是否需要记录长期理由的 Decision Record;两者都由 RSP skill 或人工 reviewer 完成。
280
-
281
- `rsp ready` 和 `rsp show` 会暴露 deterministic readiness 与 semantic-review 信号,但不会把确定性通过直接变成 archive 动作。deterministic readiness 来自 checkbox、blocker 和 scenario;durable update 判断和 advisory archive 建议仍由 Core 或人工负责。
282
-
283
- `rsp ready --json` 和 `rsp show --json` 会提供 `durableReview.factDecisions`、`rationaleDecisions`、`factCandidateTargets` 和唯一的 `decisionRecordsPath`。这只是路由指导;RSP 不会虚构文件名或自动提升 Change 内容。
284
-
285
- ## 推荐工作流
286
-
287
- 新项目:
288
-
289
- 1. `npx -y @oevery/rsp@3.1.0-beta.5 init`
290
- 2. 优先使用 `npx -y @oevery/rsp@3.1.0-beta.5 init --with-project-setup`,或手动执行 `rsp create project-setup`
291
- 3. 填写 `.rsp/specs/design.md`
292
- 4. 仅在需要新的长期项目文档时使用 `rsp add spec <name>`
293
- 5. 将长期理由写入配置的 Decision Record 目录,将稳定的作用域操作指令写入 nearest project-owned `AGENTS.md`
294
- 6. 对需要跟踪的 open work,使用 `rsp create <name>` 开始
295
- 7. 如果要让某个已有 open change 成为当前工作,使用 `rsp focus <name>`
296
- 8. 如果要将某个 change 移出当前焦点集合,使用 `rsp unfocus <name>`
297
- 9. 直接编辑 change 文件并完成实现、勾选 tasks
298
- 10. 使用 RSP skill 或人工 review 判断是否需要 durable updates
299
- 11. Core 建议 archive 后,在最终 Git 交付前运行 `rsp archive <name>`;重新检查完整工作树,并单独取得 Git 权限
300
-
301
- 已有复杂 `AGENTS.md` 的项目:
302
-
303
- 1. `npx -y @oevery/rsp@3.1.0-beta.5 init`
304
- 2. 保持受管块尽量薄
305
- 3. 将项目级边界与导航放在 `.rsp/specs/design.md`,把成组的长期事实放入最小领域 Spec
306
- 4. 只有需要额外的 durable current-fact 文档时才使用 `rsp add spec <name>`
307
-
308
- AI 协助接入:
309
-
310
- 1. `npx -y @oevery/rsp@3.1.0-beta.5 init --agents-mode print --with-project-setup`
311
- 2. 保持受管块原样,只在需要时调整周围由人维护的内容
312
- 3. 让 AI 审阅并填写 `.rsp/changes/project-setup.md`
313
- 4. 让 AI 填写 `.rsp/specs/design.md`
314
- 5. 运行 `rsp doctor`
315
-
316
- ## CLI
317
-
318
- ```text
319
- rsp init --agents-mode <mode> 搭建 .rsp/,并确保 AGENTS.md 含有 RSP 入口块
320
- rsp init --with-project-setup 同时创建 .rsp/changes/project-setup.md
321
- rsp update 刷新受管项目文件,不更新 packaged Skills
322
- rsp ui [--lang auto|en|zh-CN] 打开只读交互式仪表盘
323
- rsp skills 在 dual TTY 中打开交互式项目 Skill 管理器
324
- rsp skills list [--json] 列出内置 Skills 及其精确项目安装状态
325
- rsp skills install [name] [--dry-run] [--force]
326
- 将默认十一个 Skills 或一个精确可选 Skill 安装到 .agents/skills
327
- rsp add spec <name> 创建 .rsp/specs/<name>.md 并重建 specs 索引
328
- rsp create <name> [summary] 创建 .rsp/changes/<name>.md;可加 --lite 使用更短模板
329
- rsp group create <name> [goal] 创建不进入 focus 的 .rsp/changes/<name>/00-brief.md
330
- rsp group close <name> 所有子 Change 归档后关闭并归档 Group Brief
331
- rsp focus <name> 将一个 open change 标记为当前聚焦
332
- rsp unfocus <name> 将一个 open change 移出当前聚焦集合
333
- rsp archive <name> 归档到 .rsp/archives/
334
- rsp archive --dry-run <name> 已弃用的 rsp ready 兼容入口;不会移动 change
335
- rsp ready <name> [--json [--compact]] [--verbose]
336
- canonical 只读归档就绪投影
337
- rsp show <name|--focused> [--json [--compact]] [--verbose]
338
- 显示 change 上下文,带就绪信号和上下文路径
339
- rsp history [--limit <n>] [--since <date>] [--until <date>] [--kind <kind>] [--group <group>] [--search <text>] [--json [--compact]]
340
- 列出有界的已归档 Change 摘要(默认 20,最大 100)
341
- rsp history <work-ref> [--json [--compact]]
342
- 显示一个精确已归档 Change 的有界证据详情
343
- rsp status [--focused|--blocked|--stale <days>] [--json [--compact]] [--verbose]
344
- 查看项目状态与派生依赖计划,并支持当前聚焦相关的轻量筛选
345
- rsp check [--focused] [--json [--compact]] [--verbose]
346
- 校验 change 文件,并对 template/scenario 结构做轻量 lint
347
- rsp doctor [--fix] [--json [--compact]] [--verbose]
348
- 检查接入健康和常见问题
72
+ ```bash
73
+ pnpm docs:dev
74
+ pnpm docs:check
75
+ pnpm docs:build
349
76
  ```
350
77
 
351
- 操作优先使用 `skills/rsp/SKILL.md`;skill 不可用时使用 `.rsp/rsp-rules.md` 作为最小 fallback protocol。
352
-
353
- 在真实交互式终端中,裸 `rsp` 会打开与 `rsp ui` 相同的只读仪表盘。CI、管道、重定向流和 `TERM=dumb` 继续使用静态命令输出;人类快照使用 `rsp status`,自动化使用 `rsp status --json`。仪表盘不会创建、聚焦或归档工作。
354
-
355
- 仪表盘快捷键:`Tab` 按 Changes/Groups/History 顺序切换,方向键或 `j`/`k` 移动,`/` 筛选当前 scope,`Enter` 打开全宽详情,`r` 刷新当前 scope,`?` 显示帮助,`q`、`Ctrl-C` 或顶层 `Esc` 退出。History 首次进入时才加载默认有界的近期结果,只有选中记录并按 `Enter` 后才按唯一 archive path 加载结构化详情;更早的记录使用 `rsp history` 过滤查询。可设置 `RSP_UI_LANG=en|zh-CN` 或传入 `rsp ui --lang`;只有仪表盘自有标签会本地化。该 UI 设置不会本地化 CLI help、纯文本输出、JSON、路径、命令、Skills 或已有产物。WorkRef 可使用安全 Unicode 身份契约,新产物正文则遵循上文的项目语言策略。
356
-
357
- 当没有 focused change 时,`rsp status` 和 `rsp show --focused --json` 会输出 `nextActions`,但不会自动猜测哪个 open change 是当前工作。
358
-
359
- 人类可读的 `rsp status` 会把执行指引渲染成依赖森林:父节点依赖其子节点,共享前置依赖以引用方式显示而不重复展开,`Next action` 直接指出当前可执行的 Change。
360
-
361
- `rsp status --json` 会在 `plan.nodes`、`plan.ready`、`plan.edges`、`plan.blocked` 和 `plan.waves` 中返回同一份依赖图。节点会区分过滤器选中的 Change 与仅用于解释的前置依赖上下文;过滤后的计划会保留解释结果所需的传递前置依赖闭包。由于同一个前置依赖可能被多个 Change 共享,JSON 保持扁平图结构而不嵌套 children。每条边都读作“`change` requires `requires`”。这些内容是派生的导航事实,不是执行授权或持久化 workflow state。
362
-
363
- `rsp history` 直接检查权威 archive 文件;系统不再生成或读取 Archive Index。列表按 archive date 降序、WorkRef、source path 排序,在 1–100 的结果上限前应用 inclusive date、精确 kind、精确 Group 以及 WorkRef/summary 的大小写不敏感字面搜索。详情只返回有界 summary、scenario/checkbox counts 与结构化 Tasks/Verify/Blockers evidence,不返回 raw Markdown;重复 WorkRef、不可读或格式错误的 archive 会 fail closed。`status` archive trend 与 TUI History 使用同一份已验证 inspection。
364
-
365
- 产生 JSON 的 `status`、`show`、`ready`、`check`、`doctor` 和 `history` 都支持 `--json --compact`:内容与普通 `--json` 解析结果相同,但序列化为一行并以 LF 结尾。`--compact` 必须与 `--json` 同时使用,其他命令会在执行行为前拒绝它。
366
-
367
- `rsp create --lite` 是用于显式跟踪小 change 的短模板;简单的当前会话任务默认不应创建 RSP change,除非确实需要跟踪。
78
+ 网站只是本仓库 Markdown 的展示层。它提供双语导航、本地搜索和页面目录,不引入内容数据库或运行时服务。
368
79
 
369
- `rsp doctor --fix` 只执行安全 deterministic 修复。它的 JSON `fixed` 条目表示实际写入的文件系统变化;健康项目会返回 `fixed: []`,human 输出会说明无需安全修复。
80
+ ## 平台支持
370
81
 
371
- ## 工具无关
82
+ RSP 是与工具无关的文件约定,适用于任何能读取项目文件的助手或编辑器。人类从这里开始;AI 智能体遵循最近的 `AGENTS.md`,优先加载 `skills/rsp/SKILL.md`,仅在 Skill 不可用时使用 `.rsp/rsp-rules.md` 后备协议。
372
83
 
373
- `.rsp/` 是纯文件约定,适用于 Kilo Code、Cursor、Claude Code、Cline、GitHub Copilot 或任何能读取项目文件的 AI 助手。RSP 3.1 需要 Node.js 22+;从 3.0 升级时须先更新 Node.js 再安装。
84
+ RSP 使用 [MIT 许可证](./LICENSE)。
@@ -5,7 +5,7 @@ import {
5
5
  inspectManagedDirectory,
6
6
  inspectManagedFileTree,
7
7
  resolveManagedDirectoryChain
8
- } from "./chunk-UVYEXKYS.mjs";
8
+ } from "./chunk-SEGJLWRE.mjs";
9
9
 
10
10
  // src/commands/skills.ts
11
11
  import { randomUUID } from "crypto";
@@ -165,7 +165,7 @@ function printPackagedSkillInventory(inventory) {
165
165
  console.log(` ${inventory.package.name}@${inventory.package.version}`);
166
166
  console.log(` target: ${inventory.target}`);
167
167
  for (const group of [
168
- { heading: "Default lifecycle Skills", kind: "default" },
168
+ { heading: "Default suite Skills", kind: "default" },
169
169
  { heading: "Optional project Skills", kind: "optional" }
170
170
  ]) {
171
171
  console.log("");
@@ -651,8 +651,7 @@ function getChangeTemplateByKind(kind) {
651
651
  why: placeholder,
652
652
  scope: placeholder,
653
653
  nonGoals: placeholder,
654
- specSection: `<!-- ${placeholder} -->
655
- ### ${delta}
654
+ specSection: `### ${delta}
656
655
  - Requirement: ${placeholder}
657
656
  - ${placeholder}`,
658
657
  acceptanceSection: `#### Scenario: ${placeholder}
@@ -680,7 +679,7 @@ RSP tracks current work, stable specs, and archives under \`.rsp/\`.
680
679
  Read in order:
681
680
  1. Nearest \`AGENTS.md\` for project or module instructions.
682
681
  2. Root \`CONTEXT-MAP.md\` if present, then the relevant nearest \`CONTEXT.md\`.
683
- 3. The \`rsp\` skill; if unavailable, read \`.rsp/rsp-rules.md\` as the fallback protocol.
682
+ 3. Use the project \`rsp\` Skill at \`.agents/skills/rsp/SKILL.md\`; hosts may load it through Skill discovery or read it directly. Only when it is absent or cannot be used, read \`.rsp/rsp-rules.md\` as the fallback protocol.
684
683
  4. \`.rsp/focus.d/\`; for grouped work read the sibling Group Brief, then the explicitly selected focused Change.
685
684
  5. Only the relevant Specs and Decision Records under the configured authoritative path.
686
685
 
@@ -29,7 +29,7 @@ import {
29
29
  resolveLanguagePolicy,
30
30
  resolveManagePolicy,
31
31
  toErrorMessage
32
- } from "./chunk-UVYEXKYS.mjs";
32
+ } from "./chunk-SEGJLWRE.mjs";
33
33
 
34
34
  // src/status/inspect.ts
35
35
  import { readFile as readFile5, stat } from "fs/promises";
@@ -664,6 +664,43 @@ function normalizeLogicalPath2(path) {
664
664
  return path.split("\\").join("/");
665
665
  }
666
666
 
667
+ // src/core/work-summary.ts
668
+ var PLACEHOLDER_SUMMARY = "<\u2026>";
669
+ function extractChangeSummary(content, document = parseRspDocument(content, CHANGE_DOCUMENT_SCHEMA), options = {}) {
670
+ try {
671
+ const frontmatter = parseFrontmatter(content);
672
+ const frontmatterSummary = meaningfulSummary(frontmatter?.summary, options);
673
+ if (frontmatterSummary)
674
+ return frontmatterSummary;
675
+ } catch {
676
+ }
677
+ const proposalLines = getDocumentSectionBody(document, "proposal").split("\n").map((line) => line.trim());
678
+ for (const field of ["Outcome", "Summary"]) {
679
+ const prefix = `- ${field}:`;
680
+ for (const line of proposalLines) {
681
+ const summary = line.startsWith(prefix) ? meaningfulSummary(line.slice(prefix.length), options) : null;
682
+ if (summary)
683
+ return summary;
684
+ }
685
+ }
686
+ return null;
687
+ }
688
+ function extractGroupSummary(content, document = parseRspDocument(content, GROUP_BRIEF_DOCUMENT_SCHEMA)) {
689
+ for (const line of getDocumentSectionBody(document, "goal").split("\n")) {
690
+ const trimmed = line.trim();
691
+ const summary = trimmed.startsWith("- ") || trimmed.startsWith("* ") ? meaningfulSummary(trimmed.slice(2)) : null;
692
+ if (summary)
693
+ return summary;
694
+ }
695
+ return null;
696
+ }
697
+ function meaningfulSummary(value, options = {}) {
698
+ if (typeof value !== "string")
699
+ return null;
700
+ const summary = value.trim();
701
+ return summary && (options.preservePlaceholder || summary !== PLACEHOLDER_SUMMARY) ? summary : null;
702
+ }
703
+
667
704
  // src/core/change-group.ts
668
705
  var GROUP_BRIEF_SECTIONS = getCanonicalSectionHeadings(GROUP_BRIEF_DOCUMENT_SCHEMA);
669
706
  var GROUP_REOPEN_COMPLETION_PREFIX = "Resolve reopened concern from";
@@ -859,6 +896,7 @@ async function inspectChangeGroups(options = {}) {
859
896
  const readyToClose = diagnostics.every((diagnostic) => diagnostic.change !== brief.group || diagnostic.severity !== "error") && slices.length >= 2 && slices.every((slice) => slice.state === "archived") && parsed.completion.total > 0 && parsed.completion.done === parsed.completion.total && !parsed.blockers;
860
897
  groups.push({
861
898
  name: brief.group,
899
+ summary: extractGroupSummary(content),
862
900
  path: normalizeLogicalPath(brief.path),
863
901
  slices,
864
902
  completion: parsed.completion,
@@ -1195,7 +1233,7 @@ function parseArchiveEntry(sourcePath, path, archivesDir, content) {
1195
1233
  if (!isCanonicalExecutableWorkRef(workRef) || expectedGroup !== archiveGroup || !matchesArchiveStem(archiveStem, expectedBase)) {
1196
1234
  return { diagnostic: errorDiagnostic("archive_identity_mismatch", path, `archived Change identity ${workRef} does not match its archive path`) };
1197
1235
  }
1198
- const rawSummary = extractSummary(content, changeDocument);
1236
+ const rawSummary = extractChangeSummary(content, changeDocument, { preservePlaceholder: true });
1199
1237
  if (!rawSummary)
1200
1238
  return { diagnostic: errorDiagnostic("archive_summary_missing", path, "archived Change must contain frontmatter summary, Proposal Outcome, or Proposal Summary") };
1201
1239
  const summary = boundText(rawSummary);
@@ -1214,18 +1252,6 @@ function parseArchiveEntry(sourcePath, path, archivesDir, content) {
1214
1252
  }
1215
1253
  };
1216
1254
  }
1217
- function extractSummary(content, document = parseRspDocument(content, CHANGE_DOCUMENT_SCHEMA)) {
1218
- const frontmatter = parseFrontmatter(content);
1219
- if (typeof frontmatter?.summary === "string" && frontmatter.summary.trim())
1220
- return frontmatter.summary.trim();
1221
- const proposalLines = getDocumentSectionBody(document, "proposal").split("\n").map((line) => line.trim());
1222
- for (const prefix of ["- Outcome:", "- Summary:"]) {
1223
- const line = proposalLines.find((candidate) => candidate.startsWith(prefix));
1224
- if (line && line.slice(prefix.length).trim())
1225
- return line.slice(prefix.length).trim();
1226
- }
1227
- return "";
1228
- }
1229
1255
  function boundText(value) {
1230
1256
  const codePoints = [...value];
1231
1257
  return codePoints.length > HISTORY_MAX_TEXT_CODE_POINTS ? { value: codePoints.slice(0, HISTORY_MAX_TEXT_CODE_POINTS).join(""), truncated: true } : { value, truncated: false };
@@ -1707,6 +1733,7 @@ async function inspectProjectStatus(options = {}) {
1707
1733
  for (const name of allNames) {
1708
1734
  const path = changeMap.get(name);
1709
1735
  let kind = "\u2014";
1736
+ let summary = null;
1710
1737
  let title = name;
1711
1738
  let blockerEntries = [];
1712
1739
  let readiness = emptyReadiness(false);
@@ -1721,6 +1748,7 @@ async function inspectProjectStatus(options = {}) {
1721
1748
  try {
1722
1749
  const content = await readFile5(path, "utf-8");
1723
1750
  title = extractChangeTitle(content) ?? name;
1751
+ summary = extractChangeSummary(content);
1724
1752
  blockerEntries = extractBlockerEntries(content);
1725
1753
  try {
1726
1754
  const frontmatter = parseFrontmatter(content);
@@ -1815,6 +1843,7 @@ async function inspectProjectStatus(options = {}) {
1815
1843
  records.push({
1816
1844
  output: {
1817
1845
  name,
1846
+ summary,
1818
1847
  kind,
1819
1848
  progress: { done, total },
1820
1849
  ageDays,
package/dist/cli.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  installPackagedSkills,
5
5
  printPackagedSkillInventory,
6
6
  printSkillInstallResult
7
- } from "./chunk-3X2V6IEW.mjs";
7
+ } from "./chunk-JYDEJEM4.mjs";
8
8
  import {
9
9
  ArchiveHistoryError,
10
10
  GROUP_BRIEF_FILENAME,
@@ -37,7 +37,7 @@ import {
37
37
  resolveWorkRef,
38
38
  selectArchiveHistoryRecord,
39
39
  selectArchivedGroupBrief
40
- } from "./chunk-ZKZVWJQ7.mjs";
40
+ } from "./chunk-ZORUJTVS.mjs";
41
41
  import {
42
42
  ARCHIVES_DIR,
43
43
  CHANGES_DIR,
@@ -101,7 +101,7 @@ import {
101
101
  validateDecisionRecordsFilesystemPath,
102
102
  walkMarkdownFiles,
103
103
  writeManagedFile
104
- } from "./chunk-UVYEXKYS.mjs";
104
+ } from "./chunk-SEGJLWRE.mjs";
105
105
 
106
106
  // src/cli.ts
107
107
  import { resolve } from "path";
@@ -2836,6 +2836,8 @@ function printStatusPlain(view) {
2836
2836
  for (const { output: record, progressKnown } of view.records) {
2837
2837
  const state = [record.isFocused ? "focused" : "open", record.isBlocked ? "blocked" : null].filter(Boolean).join(" \xB7 ");
2838
2838
  console.log(` ${pc.bold("Change:")} ${record.isBlocked ? pc.yellow(record.name) : record.name}`);
2839
+ if (record.summary)
2840
+ console.log(` ${pc.dim(`Summary: ${record.summary}`)}`);
2839
2841
  console.log(` ${state} \xB7 ${record.kind} \xB7 age ${record.ageDays ?? "\u2014"}d \xB7 progress ${progressKnown ? `${record.progress.done}/${record.progress.total}` : "\u2014"}`);
2840
2842
  }
2841
2843
  } else {
@@ -2851,6 +2853,8 @@ function printStatusPlain(view) {
2851
2853
  const nameDisplay = record.isBlocked ? pc.yellow(namePadded) : namePadded;
2852
2854
  const ageDisplay = record.isFocused ? pc.cyan(agePadded) : agePadded;
2853
2855
  console.log(` ${marker}${nameDisplay} ${kindPadded} ${ageDisplay} ${progressPadded}`);
2856
+ if (record.summary)
2857
+ console.log(` ${pc.dim(`Summary: ${record.summary}`)}`);
2854
2858
  }
2855
2859
  }
2856
2860
  console.log();
@@ -2867,6 +2871,8 @@ function printChangeGroups(groups) {
2867
2871
  const readiness = group.readyToClose ? pc.green("ready to close") : pc.dim("not ready");
2868
2872
  const blocked = group.blockers ? ` ${pc.yellow("blocked")}` : "";
2869
2873
  console.log(` ${group.name}: ${archived}/${group.slices.length} archived, completion ${group.completion.done}/${group.completion.total}, ${readiness}${blocked}`);
2874
+ if (group.summary)
2875
+ console.log(` ${pc.dim(`Summary: ${group.summary}`)}`);
2870
2876
  }
2871
2877
  console.log();
2872
2878
  }
@@ -3607,7 +3613,7 @@ async function runCli(rawArgs = process.argv.slice(2)) {
3607
3613
  if (shouldLaunchSkillsUi(rawArgs, terminalEnvironment)) {
3608
3614
  const { resolveUiLocale } = await import("./locale-HT44UIUP.mjs");
3609
3615
  const locale = resolveUiLocale("auto", process.env.RSP_UI_LANG, process.env.LC_ALL ?? process.env.LC_MESSAGES ?? process.env.LANG ?? new Intl.DateTimeFormat().resolvedOptions().locale);
3610
- const { runSkillsTui } = await import("./entry-BKEJDNOQ.mjs");
3616
+ const { runSkillsTui } = await import("./entry-RG6HREUN.mjs");
3611
3617
  const selection = await runSkillsTui(locale);
3612
3618
  if (selection.kind === "confirmed") {
3613
3619
  const result = await installPackagedSkills({ names: selection.names, force: selection.force });
@@ -3625,7 +3631,7 @@ async function runCli(rawArgs = process.argv.slice(2)) {
3625
3631
  } else {
3626
3632
  const { resolveUiLocale } = await import("./locale-HT44UIUP.mjs");
3627
3633
  const locale = resolveUiLocale(lang, process.env.RSP_UI_LANG, process.env.LC_ALL ?? process.env.LC_MESSAGES ?? process.env.LANG ?? new Intl.DateTimeFormat().resolvedOptions().locale);
3628
- const { runTui } = await import("./entry-ETZGIFKP.mjs");
3634
+ const { runTui } = await import("./entry-I22OUIW5.mjs");
3629
3635
  const exitCode = await runTui(locale);
3630
3636
  if (exitCode !== 0)
3631
3637
  process.exitCode = exitCode;