claude-sdlc 1.0.6 → 1.0.7
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/lib/installer.js +80 -95
- package/package.json +1 -1
- package/template/.claude/commands/checkpoint.md +4 -4
- package/template/.claude/commands/phase.md +7 -7
- package/template/.claude/commands/review.md +3 -3
- package/template/.claude/commands/status.md +2 -2
- package/template/.claude/hooks/check-phase-test.sh +6 -18
- package/template/.claude/hooks/check-phase-write.sh +8 -22
- package/template/.claude/project-state.md +42 -0
- package/template/.claude/rules/01-lifecycle-phases.md +6 -6
- package/template/.claude/rules/05-anti-amnesia.md +16 -15
- package/template/.claude/settings.json +3 -3
- package/template/CLAUDE.md +6 -47
package/lib/installer.js
CHANGED
|
@@ -74,11 +74,11 @@ function resultBanner(text, ok = true) {
|
|
|
74
74
|
|
|
75
75
|
// ── 工具函数 ──────────────────────────────────────────
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return group.
|
|
77
|
+
/**
|
|
78
|
+
* 获取 hook 组的 matcher 标识(用于按 matcher 匹配升级,而非按内容去重)
|
|
79
|
+
*/
|
|
80
|
+
function hookMatcherKey(group) {
|
|
81
|
+
return group.matcher || '';
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
function mergeSettings(existing, template) {
|
|
@@ -92,16 +92,36 @@ function mergeSettings(existing, template) {
|
|
|
92
92
|
|
|
93
93
|
for (const type of hookTypes) {
|
|
94
94
|
const existingHooks = result.hooks?.[type] || [];
|
|
95
|
-
const
|
|
95
|
+
const templateHooks = template.hooks?.[type] || [];
|
|
96
|
+
|
|
97
|
+
// 按 matcher 建立模板 hook 索引
|
|
98
|
+
const templateByMatcher = new Map();
|
|
99
|
+
for (const hook of templateHooks) {
|
|
100
|
+
templateByMatcher.set(hookMatcherKey(hook), hook);
|
|
101
|
+
}
|
|
96
102
|
|
|
97
|
-
const merged = [
|
|
98
|
-
const
|
|
103
|
+
const merged = [];
|
|
104
|
+
const processedMatchers = new Set();
|
|
99
105
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
106
|
+
// 遍历已有 hooks:模板有同 matcher → 替换为模板版本;模板没有 → 保留(用户自定义)
|
|
107
|
+
for (const hook of existingHooks) {
|
|
108
|
+
const mk = hookMatcherKey(hook);
|
|
109
|
+
if (processedMatchers.has(mk)) continue;
|
|
110
|
+
processedMatchers.add(mk);
|
|
111
|
+
|
|
112
|
+
if (templateByMatcher.has(mk)) {
|
|
113
|
+
merged.push(JSON.parse(JSON.stringify(templateByMatcher.get(mk))));
|
|
114
|
+
} else {
|
|
103
115
|
merged.push(hook);
|
|
104
|
-
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 追加模板中新增的 hooks(已有中没有的 matcher)
|
|
120
|
+
for (const hook of templateHooks) {
|
|
121
|
+
const mk = hookMatcherKey(hook);
|
|
122
|
+
if (!processedMatchers.has(mk)) {
|
|
123
|
+
processedMatchers.add(mk);
|
|
124
|
+
merged.push(JSON.parse(JSON.stringify(hook)));
|
|
105
125
|
}
|
|
106
126
|
}
|
|
107
127
|
|
|
@@ -112,46 +132,6 @@ function mergeSettings(existing, template) {
|
|
|
112
132
|
return result;
|
|
113
133
|
}
|
|
114
134
|
|
|
115
|
-
/**
|
|
116
|
-
* 从 CLAUDE.md 中提取 YAML 状态块(```yaml ... ``` 包裹的 SDLC 项目状态)
|
|
117
|
-
*/
|
|
118
|
-
function extractYamlState(content) {
|
|
119
|
-
const match = content.match(/```yaml\n# === SDLC 项目状态 ===\n[\s\S]*?```/);
|
|
120
|
-
return match ? match[0] : null;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* 判断 YAML 状态块是否有实际项目数据(非空白模板)
|
|
125
|
-
*/
|
|
126
|
-
function hasActiveState(yamlBlock) {
|
|
127
|
-
if (!yamlBlock) return false;
|
|
128
|
-
// 检查 current_phase 是否不是 P0
|
|
129
|
-
if (/current_phase:\s*P[1-6]/.test(yamlBlock)) return true;
|
|
130
|
-
// 检查 task_description 是否非空
|
|
131
|
-
if (/task_description:\s*"[^"]+"/.test(yamlBlock)) return true;
|
|
132
|
-
// 检查 modified_files 是否有内容
|
|
133
|
-
if (/modified_files:\s*\n\s*-/.test(yamlBlock)) return true;
|
|
134
|
-
// 检查 prd 是否有实际条目(非注释)
|
|
135
|
-
if (/prd:\s*\n\s*-\s*id:/.test(yamlBlock)) return true;
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* 合并 CLAUDE.md — 更新模板指令,保留用户的 YAML 状态块
|
|
141
|
-
*/
|
|
142
|
-
function mergeClaudeMd(existingContent, templateContent) {
|
|
143
|
-
const existingState = extractYamlState(existingContent);
|
|
144
|
-
const templateState = extractYamlState(templateContent);
|
|
145
|
-
|
|
146
|
-
if (!existingState || !templateState) {
|
|
147
|
-
// 格式不匹配,无法合并,返回模板
|
|
148
|
-
return templateContent;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// 用旧的状态块替换新模板中的空白状态块
|
|
152
|
-
return templateContent.replace(templateState, existingState);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
135
|
function copyFilesQuiet(srcDir, destDir, ext) {
|
|
156
136
|
if (!fs.existsSync(srcDir)) return [];
|
|
157
137
|
const files = fs.readdirSync(srcDir).filter(f => f.endsWith(ext));
|
|
@@ -165,7 +145,7 @@ function copyFilesQuiet(srcDir, destDir, ext) {
|
|
|
165
145
|
|
|
166
146
|
function install(targetDir) {
|
|
167
147
|
const templateDir = path.join(__dirname, '..', 'template');
|
|
168
|
-
const STEPS =
|
|
148
|
+
const STEPS = 7;
|
|
169
149
|
|
|
170
150
|
// 验证
|
|
171
151
|
if (!fs.existsSync(targetDir)) {
|
|
@@ -191,31 +171,16 @@ function install(targetDir) {
|
|
|
191
171
|
console.log(` ${SYM.arrow} 目标 ${BOLD}${targetDir}${RESET}`);
|
|
192
172
|
blank();
|
|
193
173
|
|
|
194
|
-
// Step 1: CLAUDE.md
|
|
174
|
+
// Step 1: CLAUDE.md(纯控制指令,升级时直接替换为最新版本)
|
|
195
175
|
step(1, STEPS, '安装核心控制文件');
|
|
196
176
|
const targetClaudeMd = path.join(targetDir, 'CLAUDE.md');
|
|
197
177
|
const templateClaudeMd = path.join(templateDir, 'CLAUDE.md');
|
|
198
178
|
const templateContent = fs.readFileSync(templateClaudeMd, 'utf-8');
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (hasActiveState(existingState)) {
|
|
205
|
-
// 有活跃项目状态 → 更新指令模板,保留状态块
|
|
206
|
-
const merged = mergeClaudeMd(existingContent, templateContent);
|
|
207
|
-
fs.writeFileSync(targetClaudeMd, merged, 'utf-8');
|
|
208
|
-
fileLog(SYM.check, `CLAUDE.md ${DIM}(升级模板,保留项目状态)${RESET}`);
|
|
209
|
-
} else {
|
|
210
|
-
// 状态为空(P0)→ 直接覆盖
|
|
211
|
-
fs.writeFileSync(targetClaudeMd, templateContent, 'utf-8');
|
|
212
|
-
fileLog(SYM.check, 'CLAUDE.md');
|
|
213
|
-
}
|
|
214
|
-
} else {
|
|
215
|
-
// 首次安装
|
|
216
|
-
fs.writeFileSync(targetClaudeMd, templateContent, 'utf-8');
|
|
217
|
-
fileLog(SYM.check, 'CLAUDE.md');
|
|
218
|
-
}
|
|
179
|
+
const isUpgrade = fs.existsSync(targetClaudeMd);
|
|
180
|
+
fs.writeFileSync(targetClaudeMd, templateContent, 'utf-8');
|
|
181
|
+
fileLog(SYM.check, isUpgrade
|
|
182
|
+
? `CLAUDE.md ${DIM}(已更新至最新版本)${RESET}`
|
|
183
|
+
: 'CLAUDE.md');
|
|
219
184
|
|
|
220
185
|
// Step 2: 目录结构
|
|
221
186
|
step(2, STEPS, '创建目录结构');
|
|
@@ -225,8 +190,19 @@ function install(targetDir) {
|
|
|
225
190
|
fileLog(SYM.check, `.claude/${dir}/`);
|
|
226
191
|
}
|
|
227
192
|
|
|
228
|
-
// Step 3:
|
|
229
|
-
step(3, STEPS, '
|
|
193
|
+
// Step 3: project-state.md(项目状态文件 — 已有则跳过,保护用户数据)
|
|
194
|
+
step(3, STEPS, '初始化项目状态文件');
|
|
195
|
+
const targetState = path.join(targetDir, '.claude', 'project-state.md');
|
|
196
|
+
const templateState = path.join(templateDir, '.claude', 'project-state.md');
|
|
197
|
+
if (fs.existsSync(targetState)) {
|
|
198
|
+
fileLog(SYM.check, `.claude/project-state.md ${DIM}(已有状态,跳过保护)${RESET}`);
|
|
199
|
+
} else {
|
|
200
|
+
fs.copyFileSync(templateState, targetState);
|
|
201
|
+
fileLog(SYM.check, '.claude/project-state.md');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Step 4: 规则文件
|
|
205
|
+
step(4, STEPS, '安装规则文件');
|
|
230
206
|
const rules = copyFilesQuiet(
|
|
231
207
|
path.join(templateDir, '.claude', 'rules'),
|
|
232
208
|
path.join(targetDir, '.claude', 'rules'),
|
|
@@ -234,8 +210,8 @@ function install(targetDir) {
|
|
|
234
210
|
);
|
|
235
211
|
fileLog(SYM.check, `${rules.length} 个规则文件`);
|
|
236
212
|
|
|
237
|
-
// Step
|
|
238
|
-
step(
|
|
213
|
+
// Step 5: Hook 脚本
|
|
214
|
+
step(5, STEPS, '安装 Hook 脚本');
|
|
239
215
|
const hooksSrcDir = path.join(templateDir, '.claude', 'hooks');
|
|
240
216
|
const hooksDestDir = path.join(targetDir, '.claude', 'hooks');
|
|
241
217
|
let hookCount = 0;
|
|
@@ -250,8 +226,8 @@ function install(targetDir) {
|
|
|
250
226
|
}
|
|
251
227
|
fileLog(SYM.check, `${hookCount} 个 Hook 脚本`);
|
|
252
228
|
|
|
253
|
-
// Step
|
|
254
|
-
step(
|
|
229
|
+
// Step 6: 斜杠命令
|
|
230
|
+
step(6, STEPS, '安装斜杠命令');
|
|
255
231
|
const cmds = copyFilesQuiet(
|
|
256
232
|
path.join(templateDir, '.claude', 'commands'),
|
|
257
233
|
path.join(targetDir, '.claude', 'commands'),
|
|
@@ -259,8 +235,8 @@ function install(targetDir) {
|
|
|
259
235
|
);
|
|
260
236
|
fileLog(SYM.check, `${cmds.length} 个命令 — ${DIM}/phase /status /checkpoint /review${RESET}`);
|
|
261
237
|
|
|
262
|
-
// Step
|
|
263
|
-
step(
|
|
238
|
+
// Step 7: settings.json
|
|
239
|
+
step(7, STEPS, '配置 Hooks');
|
|
264
240
|
const targetSettings = path.join(targetDir, '.claude', 'settings.json');
|
|
265
241
|
const sourceSettings = path.join(templateDir, '.claude', 'settings.json');
|
|
266
242
|
|
|
@@ -284,11 +260,12 @@ function install(targetDir) {
|
|
|
284
260
|
resultBanner('安装完成');
|
|
285
261
|
|
|
286
262
|
// 文件树
|
|
287
|
-
console.log(` ${DIM}已安装 ${rules.length + hookCount + cmds.length +
|
|
263
|
+
console.log(` ${DIM}已安装 ${rules.length + hookCount + cmds.length + 3} 个文件:${RESET}`);
|
|
288
264
|
blank();
|
|
289
265
|
console.log(` ${BOLD}${path.basename(targetDir)}/${RESET}`);
|
|
290
|
-
console.log(` ${SYM.tee} CLAUDE.md ${DIM}
|
|
266
|
+
console.log(` ${SYM.tee} CLAUDE.md ${DIM}核心控制文件(升级时自动更新)${RESET}`);
|
|
291
267
|
console.log(` ${SYM.corner} ${BOLD}.claude/${RESET}`);
|
|
268
|
+
console.log(` ${SYM.tee} project-state.md ${DIM}项目状态(升级时保留)${RESET}`);
|
|
292
269
|
console.log(` ${SYM.tee} settings.json ${DIM}Hooks 配置${RESET}`);
|
|
293
270
|
console.log(` ${SYM.tee} ${BOLD}rules/${RESET} ${DIM}${rules.length} 个规则 (自动加载)${RESET}`);
|
|
294
271
|
console.log(` ${SYM.tee} ${BOLD}hooks/${RESET} ${DIM}${hookCount} 个拦截脚本${RESET}`);
|
|
@@ -329,29 +306,37 @@ function uninstall(targetDir) {
|
|
|
329
306
|
console.log(` ${SYM.arrow} 目标 ${BOLD}${targetDir}${RESET}`);
|
|
330
307
|
blank();
|
|
331
308
|
|
|
332
|
-
|
|
309
|
+
let removed = 0;
|
|
333
310
|
|
|
334
311
|
// CLAUDE.md
|
|
335
312
|
const claudeMd = path.join(targetDir, 'CLAUDE.md');
|
|
336
313
|
if (fs.existsSync(claudeMd)) {
|
|
337
314
|
fs.unlinkSync(claudeMd);
|
|
338
|
-
removed
|
|
315
|
+
removed++;
|
|
339
316
|
fileLog(SYM.check, `${DIM}删除${RESET} CLAUDE.md`);
|
|
340
317
|
}
|
|
341
318
|
|
|
342
|
-
// .
|
|
319
|
+
// project-state.md
|
|
320
|
+
const stateMd = path.join(targetDir, '.claude', 'project-state.md');
|
|
321
|
+
if (fs.existsSync(stateMd)) {
|
|
322
|
+
fs.unlinkSync(stateMd);
|
|
323
|
+
removed++;
|
|
324
|
+
fileLog(SYM.check, `${DIM}删除${RESET} .claude/project-state.md`);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// .claude 子目录(全部删除)
|
|
343
328
|
const removeDirs = ['rules', 'hooks', 'commands', 'reviews'];
|
|
344
329
|
for (const dir of removeDirs) {
|
|
345
330
|
const dirPath = path.join(targetDir, '.claude', dir);
|
|
346
331
|
if (fs.existsSync(dirPath)) {
|
|
347
332
|
const count = fs.readdirSync(dirPath).length;
|
|
348
333
|
fs.rmSync(dirPath, { recursive: true });
|
|
349
|
-
removed
|
|
334
|
+
removed++;
|
|
350
335
|
fileLog(SYM.check, `${DIM}删除${RESET} .claude/${dir}/ ${DIM}(${count} 个文件)${RESET}`);
|
|
351
336
|
}
|
|
352
337
|
}
|
|
353
338
|
|
|
354
|
-
// settings.json
|
|
339
|
+
// settings.json — 仅清理 SDLC hooks,保留用户其他配置
|
|
355
340
|
const settingsPath = path.join(targetDir, '.claude', 'settings.json');
|
|
356
341
|
if (fs.existsSync(settingsPath)) {
|
|
357
342
|
try {
|
|
@@ -364,16 +349,16 @@ function uninstall(targetDir) {
|
|
|
364
349
|
}
|
|
365
350
|
if (Object.keys(settings).length === 0) {
|
|
366
351
|
fs.unlinkSync(settingsPath);
|
|
367
|
-
removed
|
|
352
|
+
removed++;
|
|
368
353
|
fileLog(SYM.check, `${DIM}删除${RESET} .claude/settings.json`);
|
|
369
354
|
} else {
|
|
370
355
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
|
|
371
|
-
removed
|
|
356
|
+
removed++;
|
|
372
357
|
fileLog(SYM.check, `${DIM}清理${RESET} settings.json ${DIM}(仅移除 hooks,保留其他配置)${RESET}`);
|
|
373
358
|
}
|
|
374
359
|
} catch (_) {
|
|
375
360
|
fs.unlinkSync(settingsPath);
|
|
376
|
-
removed
|
|
361
|
+
removed++;
|
|
377
362
|
fileLog(SYM.check, `${DIM}删除${RESET} .claude/settings.json`);
|
|
378
363
|
}
|
|
379
364
|
}
|
|
@@ -388,8 +373,8 @@ function uninstall(targetDir) {
|
|
|
388
373
|
}
|
|
389
374
|
}
|
|
390
375
|
|
|
391
|
-
if (removed
|
|
392
|
-
resultBanner(`卸载完成 — 已清理 ${removed
|
|
376
|
+
if (removed > 0) {
|
|
377
|
+
resultBanner(`卸载完成 — 已清理 ${removed} 项`);
|
|
393
378
|
console.log(` ${DIM}重新安装:npx claude-sdlc${RESET}`);
|
|
394
379
|
} else {
|
|
395
380
|
blank();
|
|
@@ -398,4 +383,4 @@ function uninstall(targetDir) {
|
|
|
398
383
|
blank();
|
|
399
384
|
}
|
|
400
385
|
|
|
401
|
-
module.exports = { install, uninstall, mergeSettings
|
|
386
|
+
module.exports = { install, uninstall, mergeSettings };
|
package/package.json
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
## 执行逻辑
|
|
10
10
|
|
|
11
11
|
1. **收集当前状态信息**:
|
|
12
|
-
- 读取
|
|
13
|
-
- 读取
|
|
12
|
+
- 读取 .claude/project-state.md 中的 `current_phase`
|
|
13
|
+
- 读取 .claude/project-state.md 中的 `task_description`
|
|
14
14
|
- 执行 `git status`(如果是 git 仓库)获取文件变更状态
|
|
15
15
|
- 执行 `git diff --stat`(如果是 git 仓库)获取变更统计
|
|
16
16
|
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
========================
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
3. **更新
|
|
39
|
+
3. **更新 .claude/project-state.md**:
|
|
40
40
|
- 确保 `modified_files` 列表是最新的
|
|
41
41
|
- 确保 `todo_items` 列表是最新的
|
|
42
42
|
- 更新 `last_updated` 时间戳
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
```
|
|
47
47
|
✅ 检查点已保存。
|
|
48
48
|
|
|
49
|
-
如果后续发生上下文压缩(compaction),可以从
|
|
49
|
+
如果后续发生上下文压缩(compaction),可以从 .claude/project-state.md 恢复以下状态:
|
|
50
50
|
- 阶段:{阶段}
|
|
51
51
|
- 任务:{任务描述}
|
|
52
52
|
- 已修改 {n} 个文件
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
根据参数执行以下操作:
|
|
12
12
|
|
|
13
13
|
### 无参数 或 `status`
|
|
14
|
-
1. 读取
|
|
14
|
+
1. 读取 .claude/project-state.md 中的 `current_phase` 值
|
|
15
15
|
2. 输出当前阶段信息:
|
|
16
16
|
- 阶段编号和名称
|
|
17
17
|
- 该阶段允许的操作
|
|
@@ -52,9 +52,9 @@ Step 3: 审查通过?
|
|
|
52
52
|
- 自动执行当前阶段对应的 `/review`
|
|
53
53
|
- 输出审查报告
|
|
54
54
|
5. **审查通过**:
|
|
55
|
-
- 将 `current_phase` 更新为下一阶段
|
|
56
|
-
- 更新 `phase_history` 记录(含审查结果摘要)
|
|
57
|
-
- 更新 `last_updated` 时间戳
|
|
55
|
+
- 将 .claude/project-state.md 的 `current_phase` 更新为下一阶段
|
|
56
|
+
- 更新 .claude/project-state.md 的 `phase_history` 记录(含审查结果摘要)
|
|
57
|
+
- 更新 .claude/project-state.md 的 `last_updated` 时间戳
|
|
58
58
|
- **P1→P2、P2→P3**:输出新阶段的入口信息(P2→P3 时提示"进入自动驱动模式")
|
|
59
59
|
- **P3→P4、P4→P5、P5→P6**:不输出冗余信息,立即开始下一阶段工作
|
|
60
60
|
- **P6 完成**:输出交付摘要报告
|
|
@@ -67,9 +67,9 @@ Step 3: 审查通过?
|
|
|
67
67
|
1. 读取当前阶段
|
|
68
68
|
2. 如果已是 P0/P1,提示无法继续回退
|
|
69
69
|
3. 要求提供回退原因
|
|
70
|
-
4. 更新 `current_phase` 为上一阶段
|
|
71
|
-
5. 更新 `phase_history` 记录(含回退原因)
|
|
72
|
-
6. 更新 `last_updated` 时间戳
|
|
70
|
+
4. 更新 .claude/project-state.md 的 `current_phase` 为上一阶段
|
|
71
|
+
5. 更新 .claude/project-state.md 的 `phase_history` 记录(含回退原因)
|
|
72
|
+
6. 更新 .claude/project-state.md 的 `last_updated` 时间戳
|
|
73
73
|
7. 输出回退后的阶段信息
|
|
74
74
|
8. 注意:回退后上一阶段的审查状态重置,再次推进时需重新审查
|
|
75
75
|
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
|
|
36
36
|
### 1. 自动识别当前阶段
|
|
37
37
|
|
|
38
|
-
读取
|
|
38
|
+
读取 .claude/project-state.md 中的 `current_phase`,根据阶段选择对应的审查清单。
|
|
39
39
|
|
|
40
40
|
### 2. 确定审查范围
|
|
41
41
|
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
- [ ] **影响分析**:受影响的文件和模块是否已识别完整
|
|
58
58
|
- [ ] **风险识别**:技术风险和业务风险是否已列出
|
|
59
59
|
- [ ] **用户已确认**:PRD 是否已经用户明确确认
|
|
60
|
-
- [ ] **已写入
|
|
60
|
+
- [ ] **已写入 .claude/project-state.md**:PRD 是否已写入 `prd` 字段
|
|
61
61
|
|
|
62
62
|
### 输出格式
|
|
63
63
|
```
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
## P3 编码实现 — 代码审查
|
|
142
142
|
|
|
143
143
|
### 审查范围
|
|
144
|
-
- 审查
|
|
144
|
+
- 审查 .claude/project-state.md 中 `modified_files` 列表里的所有文件
|
|
145
145
|
- 如指定了文件路径,仅审查该文件
|
|
146
146
|
|
|
147
147
|
### 工具执行(P3 审查前必须运行)
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
执行深度自检并生成全面的项目状态报告:
|
|
12
12
|
|
|
13
|
-
### 1. 读取
|
|
13
|
+
### 1. 读取 .claude/project-state.md 状态
|
|
14
14
|
- `current_phase` — 当前阶段
|
|
15
15
|
- `task_description` — 任务描述
|
|
16
16
|
- `started_at` / `last_updated` — 时间信息
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
### 4. 同步更新
|
|
77
|
-
- 如果发现
|
|
77
|
+
- 如果发现 .claude/project-state.md 中的信息不是最新,同步更新
|
|
78
78
|
- 更新 `last_updated` 时间戳
|
|
79
79
|
|
|
80
80
|
---
|
|
@@ -22,30 +22,18 @@ if [ -z "$COMMAND" ]; then
|
|
|
22
22
|
fi
|
|
23
23
|
|
|
24
24
|
# ==============================
|
|
25
|
-
#
|
|
25
|
+
# 读取项目状态
|
|
26
26
|
# ==============================
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
echo "$dir/CLAUDE.md"
|
|
33
|
-
return 0
|
|
34
|
-
fi
|
|
35
|
-
dir=$(dirname "$dir")
|
|
36
|
-
done
|
|
37
|
-
return 1
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
CLAUDE_MD=$(find_claude_md 2>/dev/null) || true
|
|
41
|
-
|
|
42
|
-
# 如果找不到 CLAUDE.md,默认放行(容错)
|
|
43
|
-
if [ -z "$CLAUDE_MD" ]; then
|
|
28
|
+
STATE_FILE="${CLAUDE_PROJECT_DIR:-.}/.claude/project-state.md"
|
|
29
|
+
|
|
30
|
+
# 如果找不到状态文件,默认放行(容错)
|
|
31
|
+
if [ ! -f "$STATE_FILE" ]; then
|
|
44
32
|
exit 0
|
|
45
33
|
fi
|
|
46
34
|
|
|
47
35
|
# 读取当前阶段 — 兼容 macOS(不使用 grep -oP)
|
|
48
|
-
CURRENT_PHASE=$(sed -n 's/^current_phase:[[:space:]]*\([^[:space:]#]*\).*/\1/p' "$
|
|
36
|
+
CURRENT_PHASE=$(sed -n 's/^current_phase:[[:space:]]*\([^[:space:]#]*\).*/\1/p' "$STATE_FILE" 2>/dev/null | head -1)
|
|
49
37
|
|
|
50
38
|
if [ -z "$CURRENT_PHASE" ]; then
|
|
51
39
|
exit 0
|
|
@@ -23,43 +23,29 @@ if [ -z "$FILE_PATH" ]; then
|
|
|
23
23
|
fi
|
|
24
24
|
|
|
25
25
|
# 文档/配置类文件扩展名 — 任何阶段都允许写入
|
|
26
|
-
# 注意:.html 和 .css 是代码文件(Web 项目),不在白名单中
|
|
27
26
|
DOC_EXTENSIONS='\.(md|txt|json|yaml|yml|toml|ini|cfg|conf|gitignore|editorconfig|prettierrc|eslintrc|csv|xml|svg|lock|log|env|env\..*)$'
|
|
28
27
|
|
|
29
28
|
if echo "$FILE_PATH" | grep -qiE "$DOC_EXTENSIONS"; then
|
|
30
29
|
exit 0
|
|
31
30
|
fi
|
|
32
31
|
|
|
33
|
-
#
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
echo "$dir/CLAUDE.md"
|
|
39
|
-
return 0
|
|
40
|
-
fi
|
|
41
|
-
dir=$(dirname "$dir")
|
|
42
|
-
done
|
|
43
|
-
return 1
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
CLAUDE_MD=$(find_claude_md 2>/dev/null) || true
|
|
47
|
-
|
|
48
|
-
# 如果找不到 CLAUDE.md,默认放行(容错)
|
|
49
|
-
if [ -z "$CLAUDE_MD" ]; then
|
|
32
|
+
# 读取项目状态文件
|
|
33
|
+
STATE_FILE="${CLAUDE_PROJECT_DIR:-.}/.claude/project-state.md"
|
|
34
|
+
|
|
35
|
+
# 如果找不到状态文件,默认放行(容错)
|
|
36
|
+
if [ ! -f "$STATE_FILE" ]; then
|
|
50
37
|
exit 0
|
|
51
38
|
fi
|
|
52
39
|
|
|
53
|
-
# 读取当前阶段 — 兼容 macOS (BSD
|
|
54
|
-
#
|
|
55
|
-
CURRENT_PHASE=$(sed -n 's/^current_phase:[[:space:]]*\([^[:space:]#]*\).*/\1/p' "$CLAUDE_MD" 2>/dev/null | head -1)
|
|
40
|
+
# 读取当前阶段 — 兼容 macOS (BSD sed)
|
|
41
|
+
CURRENT_PHASE=$(sed -n 's/^current_phase:[[:space:]]*\([^[:space:]#]*\).*/\1/p' "$STATE_FILE" 2>/dev/null | head -1)
|
|
56
42
|
|
|
57
43
|
# 如果无法确定阶段,默认放行(容错)
|
|
58
44
|
if [ -z "$CURRENT_PHASE" ]; then
|
|
59
45
|
exit 0
|
|
60
46
|
fi
|
|
61
47
|
|
|
62
|
-
# 提取阶段编号(P0→0, P1→1,
|
|
48
|
+
# 提取阶段编号(P0→0, P1→1, ...)
|
|
63
49
|
PHASE_NUM=$(echo "$CURRENT_PHASE" | sed 's/[^0-9]//g' 2>/dev/null)
|
|
64
50
|
|
|
65
51
|
if [ -z "$PHASE_NUM" ]; then
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# SDLC 项目状态(活文档 — 持续更新)
|
|
2
|
+
|
|
3
|
+
> **COMPACTION 保护区域:压缩时必须保留。每次变更后立即更新。**
|
|
4
|
+
> 本文件是项目状态唯一存储位置,通过 CLAUDE.md 的 @import 自动加载。
|
|
5
|
+
> 升级 claude-sdlc 时本文件不会被覆盖。
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
# === SDLC 项目状态 ===
|
|
9
|
+
current_phase: P0 # P0=未开始, P1=需求, P2=设计, P3=编码, P4=测试, P5=审查, P6=部署
|
|
10
|
+
task_description: ""
|
|
11
|
+
started_at: ""
|
|
12
|
+
last_updated: ""
|
|
13
|
+
|
|
14
|
+
# PRD — 用户确认的需求清单(P1 产出,P2-P6 唯一依据)
|
|
15
|
+
prd:
|
|
16
|
+
# - id: R1
|
|
17
|
+
# description: "需求描述"
|
|
18
|
+
# acceptance_criteria: "验收标准"
|
|
19
|
+
|
|
20
|
+
architecture_decisions: []
|
|
21
|
+
modified_files: []
|
|
22
|
+
todo_items: []
|
|
23
|
+
review_retry_count: 0 # 自动驱动审查重试计数,阶段推进后重置
|
|
24
|
+
phase_history: []
|
|
25
|
+
key_context: "" # compaction 后恢复用
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 状态更新时机
|
|
29
|
+
|
|
30
|
+
| 事件 | 更新字段 |
|
|
31
|
+
|------|---------|
|
|
32
|
+
| 新任务 | `current_phase`→P1, `task_description`, `started_at` |
|
|
33
|
+
| PRD 确认 | `prd`(编号化需求列表,此后为唯一依据) |
|
|
34
|
+
| 阶段推进 | `current_phase`, `phase_history`, `review_retry_count`→0, `last_updated` |
|
|
35
|
+
| 文件修改 | `modified_files`(追加路径), `last_updated` |
|
|
36
|
+
| 架构决策 | `architecture_decisions`, `last_updated` |
|
|
37
|
+
| 审查重试 | `review_retry_count`+1, `last_updated` |
|
|
38
|
+
| 即将压缩 | 所有字段确认最新, `key_context`(写入当前工作摘要) |
|
|
39
|
+
|
|
40
|
+
# Compact Instructions
|
|
41
|
+
|
|
42
|
+
压缩时必须保留:(1) 本文件路径和存在性 (2) 上方 YAML 块完整内容 (3) 当前阶段和任务 (4) 已修改文件列表 (5) 架构决策 (6) 用户最近指令。恢复后:P1/P2 等待用户确认继续,P3-P6 自动恢复自动驱动继续完成。
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
|
|
34
34
|
### PRD 确认流程
|
|
35
35
|
1. Claude 整理 PRD 初稿后,**必须向用户展示完整的需求清单**
|
|
36
|
-
2. 用户确认后,将 PRD 写入
|
|
36
|
+
2. 用户确认后,将 PRD 写入 .claude/project-state.md 的 `prd` 字段
|
|
37
37
|
3. **PRD 一旦写入即锁定**,后续阶段以此为唯一依据
|
|
38
38
|
4. 如需修改 PRD,必须回到 P1 重新获得用户确认
|
|
39
39
|
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
- [ ] 技术可行性 — 在当前技术栈下可实现
|
|
47
47
|
- [ ] 影响分析 — 受影响的文件和模块已识别
|
|
48
48
|
- [ ] **用户已确认** — PRD 经用户明确确认
|
|
49
|
-
- [ ] **PRD 已写入
|
|
49
|
+
- [ ] **PRD 已写入 .claude/project-state.md** — `prd` 字段已填写
|
|
50
50
|
|
|
51
51
|
### 退出条件
|
|
52
52
|
- PRD 已整理完成并经用户确认
|
|
53
|
-
- PRD 已写入
|
|
53
|
+
- PRD 已写入 .claude/project-state.md 的 `prd` 字段
|
|
54
54
|
- 影响范围已明确
|
|
55
55
|
- **需求审查已通过**(`/review`)
|
|
56
56
|
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
|
|
70
70
|
### 入口条件
|
|
71
71
|
- P1 需求分析已完成(含需求审查通过)
|
|
72
|
-
- PRD 已获用户确认并写入
|
|
72
|
+
- PRD 已获用户确认并写入 .claude/project-state.md
|
|
73
73
|
|
|
74
74
|
### 阶段活动
|
|
75
75
|
- **【最新技术与设计调研 — 必须首先执行】**:
|
|
@@ -145,7 +145,7 @@
|
|
|
145
145
|
3. 确保使用当前推荐的 API,不使用已废弃(deprecated)的方法或过时写法
|
|
146
146
|
- **严格按 P2 设计方案编写代码,不添加 PRD 之外的功能**
|
|
147
147
|
- 遵循编码规范(见 02-coding-standards.md)
|
|
148
|
-
- 每完成一个逻辑单元,更新
|
|
148
|
+
- 每完成一个逻辑单元,更新 .claude/project-state.md 中的 modified_files
|
|
149
149
|
- 记录实现中的关键决策
|
|
150
150
|
- **如发现 PRD 有遗漏或设计需调整,停止编码,回退到 P1/P2 与用户确认**
|
|
151
151
|
- **多 Agent 并行编码**(如有独立模块):按设计方案拆分独立模块,用 Task 工具并行派发子 Agent 编码(见 07-parallel-agents.md)
|
|
@@ -159,7 +159,7 @@
|
|
|
159
159
|
### 必需产出物
|
|
160
160
|
- [ ] 功能代码实现(**仅限 PRD 范围**)
|
|
161
161
|
- [ ] 代码符合编码规范
|
|
162
|
-
- [ ]
|
|
162
|
+
- [ ] .claude/project-state.md 中 modified_files 已更新
|
|
163
163
|
- [ ] 关键实现决策已记录
|
|
164
164
|
|
|
165
165
|
### 阶段审查(代码审查)
|
|
@@ -10,9 +10,9 @@ Claude Code 存在以下可能导致规范失效的场景:
|
|
|
10
10
|
|
|
11
11
|
| 场景 | 风险 | 防御机制 |
|
|
12
12
|
|------|------|---------|
|
|
13
|
-
| 新会话启动 | Claude 不知道有规范 | CLAUDE.md + rules/ 自动加载 |
|
|
13
|
+
| 新会话启动 | Claude 不知道有规范 | CLAUDE.md + rules/ + project-state.md 自动加载 |
|
|
14
14
|
| 长对话漂移 | Claude 渐渐忘记规范 | Stop hook 每次回复后强制自检 |
|
|
15
|
-
| Context Compaction | 早期对话被压缩丢弃 | PreCompact hook 保存状态 →
|
|
15
|
+
| Context Compaction | 早期对话被压缩丢弃 | PreCompact hook 保存状态 → .claude/project-state.md 重新加载恢复 |
|
|
16
16
|
| 用户催促跳过 | Claude 放弃规范直接执行 | Hooks 硬拦截违规操作 |
|
|
17
17
|
| 新任务覆盖 | 上一任务状态残留 | 新任务检测 → 自动重置 |
|
|
18
18
|
| Claude 自行判断不需要 | Claude 认为简单任务不需要走流程 | CLAUDE.md 明确"所有开发任务必须" |
|
|
@@ -23,6 +23,7 @@ Claude Code 存在以下可能导致规范失效的场景:
|
|
|
23
23
|
|
|
24
24
|
### 第一层:CLAUDE.md 启动指令(主动)
|
|
25
25
|
- CLAUDE.md 在每次会话开始和 compaction 后自动加载
|
|
26
|
+
- CLAUDE.md 通过 @import 引用 `.claude/project-state.md`,项目状态存储在该文件中
|
|
26
27
|
- **「启动指令」要求 Claude 读到该文件时立即执行状态检查**
|
|
27
28
|
- 这确保 Claude 不是被动接收规范,而是主动执行初始化
|
|
28
29
|
- **这是最关键的防线**
|
|
@@ -42,20 +43,20 @@ Claude Code 存在以下可能导致规范失效的场景:
|
|
|
42
43
|
- 这是**不依赖 Claude 自觉性的被动安全网**
|
|
43
44
|
|
|
44
45
|
### 第四层:PostToolUse Hook — 状态同步(主动)
|
|
45
|
-
- 每次 Write/Edit 操作后,prompt hook 提醒 Claude 更新
|
|
46
|
-
- 确保 modified_files
|
|
46
|
+
- 每次 Write/Edit 操作后,prompt hook 提醒 Claude 更新 .claude/project-state.md
|
|
47
|
+
- 确保 modified_files 列表实时最新(状态存储在 .claude/project-state.md 中)
|
|
47
48
|
- 为 compaction 后的恢复提供准确的文件清单
|
|
48
49
|
|
|
49
50
|
### 第五层:Stop Hook — 回复后审查(主动)
|
|
50
51
|
- 每次回复完成后触发
|
|
51
|
-
- **强制 Claude 用 Read 工具重新读取
|
|
52
|
+
- **强制 Claude 用 Read 工具重新读取 .claude/project-state.md**,而非依赖记忆
|
|
52
53
|
- 检查阶段合规性、状态最新性
|
|
53
54
|
- 发现偏离时立即纠正
|
|
54
55
|
|
|
55
56
|
### 第六层:PreCompact Hook — 压缩前保存(主动)
|
|
56
57
|
- **prompt 类型**,直接指令 Claude 在压缩前保存所有状态
|
|
57
|
-
- 要求 Claude 用 Read 读取
|
|
58
|
-
- 特别要求写入 key_context
|
|
58
|
+
- 要求 Claude 用 Read 读取 .claude/project-state.md 确认状态,用 Edit 更新缺失信息
|
|
59
|
+
- 特别要求写入 key_context 摘要到 .claude/project-state.md,确保恢复时有足够上下文
|
|
59
60
|
|
|
60
61
|
### 第七层:用户命令(按需)
|
|
61
62
|
- `/status` — 随时查看完整状态,触发深度自检
|
|
@@ -73,7 +74,7 @@ Claude Code 存在以下可能导致规范失效的场景:
|
|
|
73
74
|
1. Claude Code 启动 → 自动加载 CLAUDE.md + rules/
|
|
74
75
|
2. CLAUDE.md「启动指令」执行 → 检查 current_phase = P0
|
|
75
76
|
3. Claude 识别到"实现"是开发请求 → 自动进入 P1
|
|
76
|
-
4. 更新
|
|
77
|
+
4. 更新 .claude/project-state.md: current_phase=P1, task_description="实现登录功能"
|
|
77
78
|
5. 开始需求分析 → 整理 PRD → 向用户展示 PRD 等待确认
|
|
78
79
|
6. 用户确认 PRD → 自动审查 → 进入 P2 → 设计方案 → 向用户展示等待确认
|
|
79
80
|
7. 用户确认设计 → 自动审查 → 进入自动驱动模式
|
|
@@ -85,7 +86,7 @@ Claude Code 存在以下可能导致规范失效的场景:
|
|
|
85
86
|
|
|
86
87
|
```
|
|
87
88
|
1. 当前在 P3 自动驱动中
|
|
88
|
-
2. PreCompact hook 触发 → Claude 被指令保存状态到
|
|
89
|
+
2. PreCompact hook 触发 → Claude 被指令保存状态到 .claude/project-state.md
|
|
89
90
|
3. Compaction 执行 → 对话历史被压缩
|
|
90
91
|
4. CLAUDE.md 重新加载 → 「启动指令」重新执行
|
|
91
92
|
5. Claude 读取 current_phase=P3 → 知道正在自动驱动的编码阶段
|
|
@@ -98,7 +99,7 @@ Claude Code 存在以下可能导致规范失效的场景:
|
|
|
98
99
|
```
|
|
99
100
|
1. Claude 调用 Write 工具写 .py 文件
|
|
100
101
|
2. PreToolUse hook (check-phase-write.sh) 执行
|
|
101
|
-
3. 读取
|
|
102
|
+
3. 读取 .claude/project-state.md: current_phase=P1
|
|
102
103
|
4. P1 < P3 → 输出拦截信息,exit 2
|
|
103
104
|
5. Claude 收到拦截信息 → 向用户说明不能在 P1 写代码
|
|
104
105
|
6. 继续需求分析工作
|
|
@@ -153,13 +154,13 @@ Claude Code 存在以下可能导致规范失效的场景:
|
|
|
153
154
|
**每次回复前执行(内部 4 步):**
|
|
154
155
|
|
|
155
156
|
```
|
|
156
|
-
1. 【阶段】我当前在哪个阶段?→ 检查
|
|
157
|
+
1. 【阶段】我当前在哪个阶段?→ 检查 .claude/project-state.md 的 current_phase
|
|
157
158
|
2. 【PRD】我接下来要做的事,对应 PRD 中的哪条需求?→ 如果对应不上,不做
|
|
158
159
|
3. 【合规】我要执行的操作在当前阶段允许吗?→ 对照允许列表
|
|
159
|
-
4.
|
|
160
|
+
4. 【状态】.claude/project-state.md 的状态是最新的吗?→ 如有变更需更新
|
|
160
161
|
```
|
|
161
162
|
|
|
162
|
-
**疑问时的行为:不猜测,用 Read 工具重新读取
|
|
163
|
+
**疑问时的行为:不猜测,用 Read 工具重新读取 .claude/project-state.md。**
|
|
163
164
|
|
|
164
165
|
---
|
|
165
166
|
|
|
@@ -173,7 +174,7 @@ Claude Code 存在以下可能导致规范失效的场景:
|
|
|
173
174
|
- Stop hook 提醒时
|
|
174
175
|
|
|
175
176
|
```
|
|
176
|
-
1. 用 Read 工具读取
|
|
177
|
+
1. 用 Read 工具读取 .claude/project-state.md 的 YAML 状态
|
|
177
178
|
2. 确认 current_phase 值
|
|
178
179
|
3. 确认 task_description 值
|
|
179
180
|
4. 回顾 modified_files 列表 — 是否有遗漏
|
|
@@ -188,7 +189,7 @@ Claude Code 存在以下可能导致规范失效的场景:
|
|
|
188
189
|
## 6. 异常恢复流程
|
|
189
190
|
|
|
190
191
|
### 自动恢复(优先)
|
|
191
|
-
1. 用 Read 工具读取
|
|
192
|
+
1. 用 Read 工具读取 .claude/project-state.md 获取最新状态
|
|
192
193
|
2. 根据 modified_files 列表,用 Read 工具读取已修改的文件以恢复上下文
|
|
193
194
|
3. 向用户报告恢复结果
|
|
194
195
|
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"hooks": [
|
|
28
28
|
{
|
|
29
29
|
"type": "command",
|
|
30
|
-
"command": "echo '{\"decision\":\"block\",\"reason\":\"SDLC: File modified. Use Edit to update
|
|
30
|
+
"command": "echo '{\"decision\":\"block\",\"reason\":\"SDLC: File modified. Use Edit to update .claude/project-state.md: append the modified file path to modified_files (if not already listed) and update last_updated. This is mandatory, do not skip.\"}'"
|
|
31
31
|
}
|
|
32
32
|
]
|
|
33
33
|
}
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"hooks": [
|
|
38
38
|
{
|
|
39
39
|
"type": "prompt",
|
|
40
|
-
"prompt": "You are evaluating whether Claude should stop working. Context: $ARGUMENTS\n\nIMPORTANT: First check if stop_hook_active is true in the context above. If it is true, respond immediately with {\"ok\": true} to prevent infinite loops.\n\nOtherwise, check the conversation transcript and determine:\n1. Is there an active SDLC phase (P1-P6) with remaining work?\n2. For P3/P4/P5 auto-drive phases: has the /review been executed and passed?\n3. Are there uncompleted PRD requirements?\n4. Has
|
|
40
|
+
"prompt": "You are evaluating whether Claude should stop working. Context: $ARGUMENTS\n\nIMPORTANT: First check if stop_hook_active is true in the context above. If it is true, respond immediately with {\"ok\": true} to prevent infinite loops.\n\nOtherwise, check the conversation transcript and determine:\n1. Is there an active SDLC phase (P1-P6) with remaining work?\n2. For P3/P4/P5 auto-drive phases: has the /review been executed and passed?\n3. Are there uncompleted PRD requirements?\n4. Has .claude/project-state.md YAML state been updated after the latest changes?\n\nRespond with JSON: {\"ok\": true} if all tasks are complete and Claude can stop, or {\"ok\": false, \"reason\": \"SDLC self-check: [describe what still needs to be done, e.g. run /review, update .claude/project-state.md YAML, continue to next phase, etc.]\"} if Claude should continue working.",
|
|
41
41
|
"timeout": 30
|
|
42
42
|
}
|
|
43
43
|
]
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"hooks": [
|
|
49
49
|
{
|
|
50
50
|
"type": "prompt",
|
|
51
|
-
"prompt": "You are evaluating a conversation before context compaction. Context: $ARGUMENTS\n\nCheck if
|
|
51
|
+
"prompt": "You are evaluating a conversation before context compaction. Context: $ARGUMENTS\n\nCheck if .claude/project-state.md YAML state block has been updated with latest values for: current_phase, task_description, prd, modified_files, architecture_decisions, todo_items, key_context.\n\nRespond with JSON: {\"ok\": true} if state is saved, or {\"ok\": false, \"reason\": \"Before compaction: update .claude/project-state.md YAML - save current_phase, task_description, modified_files, and write key_context summary of current work and next steps.\"}",
|
|
52
52
|
"timeout": 30
|
|
53
53
|
}
|
|
54
54
|
]
|
package/template/CLAUDE.md
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
> 详细规则见 `.claude/rules/`(自动加载)。可用命令:`/phase`、`/status`、`/checkpoint`、`/review`。
|
|
4
4
|
|
|
5
|
+
@.claude/project-state.md
|
|
6
|
+
|
|
5
7
|
## 启动指令(加载时立即执行)
|
|
6
8
|
|
|
7
|
-
1.
|
|
9
|
+
1. 用 Read 工具读取 `.claude/project-state.md` 获取 `current_phase` 值
|
|
8
10
|
2. **P1-P6**:向用户简要报告状态(阶段、任务、已修改文件数),继续工作
|
|
9
11
|
3. **P0 或空**:等待用户提出任务
|
|
10
12
|
4. 所有后续操作遵守 SDLC 规范
|
|
@@ -66,52 +68,9 @@ P3/P4/P5 阶段有独立模块时,用 Task 工具并行派发子 Agent 提高
|
|
|
66
68
|
|
|
67
69
|
### 每次回复前 4 步自检
|
|
68
70
|
|
|
69
|
-
1. **阶段** — `current_phase`
|
|
71
|
+
1. **阶段** — `current_phase` 是什么?(读 `.claude/project-state.md`)
|
|
70
72
|
2. **PRD** — 要做的事对应 PRD 哪条?对应不上则不做
|
|
71
73
|
3. **合规** — 操作在当前阶段允许吗?
|
|
72
|
-
4. **状态** — 变更后
|
|
73
|
-
|
|
74
|
-
有疑问 → 用 Read 重读本文件,不依赖记忆。详见 `.claude/rules/05-anti-amnesia.md`。
|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## 当前项目状态(活文档 — 持续更新)
|
|
79
|
-
|
|
80
|
-
> **COMPACTION 保护区域:压缩时必须保留。每次变更后立即更新。**
|
|
81
|
-
|
|
82
|
-
```yaml
|
|
83
|
-
# === SDLC 项目状态 ===
|
|
84
|
-
current_phase: P0 # P0=未开始, P1=需求, P2=设计, P3=编码, P4=测试, P5=审查, P6=部署
|
|
85
|
-
task_description: ""
|
|
86
|
-
started_at: ""
|
|
87
|
-
last_updated: ""
|
|
88
|
-
|
|
89
|
-
# PRD — 用户确认的需求清单(P1 产出,P2-P6 唯一依据)
|
|
90
|
-
prd:
|
|
91
|
-
# - id: R1
|
|
92
|
-
# description: "需求描述"
|
|
93
|
-
# acceptance_criteria: "验收标准"
|
|
94
|
-
|
|
95
|
-
architecture_decisions: []
|
|
96
|
-
modified_files: []
|
|
97
|
-
todo_items: []
|
|
98
|
-
review_retry_count: 0 # 自动驱动审查重试计数,阶段推进后重置
|
|
99
|
-
phase_history: []
|
|
100
|
-
key_context: "" # compaction 后恢复用
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### 状态更新时机
|
|
104
|
-
|
|
105
|
-
| 事件 | 更新字段 |
|
|
106
|
-
|------|---------|
|
|
107
|
-
| 新任务 | `current_phase`→P1, `task_description`, `started_at` |
|
|
108
|
-
| PRD 确认 | `prd`(编号化需求列表,此后为唯一依据) |
|
|
109
|
-
| 阶段推进 | `current_phase`, `phase_history`, `review_retry_count`→0, `last_updated` |
|
|
110
|
-
| 文件修改 | `modified_files`(追加路径), `last_updated` |
|
|
111
|
-
| 架构决策 | `architecture_decisions`, `last_updated` |
|
|
112
|
-
| 审查重试 | `review_retry_count`+1, `last_updated` |
|
|
113
|
-
| 即将压缩 | 所有字段确认最新, `key_context`(写入当前工作摘要) |
|
|
114
|
-
|
|
115
|
-
# Compact Instructions
|
|
74
|
+
4. **状态** — 变更后 `.claude/project-state.md` 更新了吗?
|
|
116
75
|
|
|
117
|
-
|
|
76
|
+
有疑问 → 用 Read 重读 `.claude/project-state.md`,不依赖记忆。详见 `.claude/rules/05-anti-amnesia.md`。
|