superpowers-zh 1.3.0 → 1.5.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.
@@ -76,8 +76,10 @@ function decodeFrame(buffer) {
76
76
  const PORT = process.env.BRAINSTORM_PORT || (49152 + Math.floor(Math.random() * 16383));
77
77
  const HOST = process.env.BRAINSTORM_HOST || '127.0.0.1';
78
78
  const URL_HOST = process.env.BRAINSTORM_URL_HOST || (HOST === '127.0.0.1' ? 'localhost' : HOST);
79
- const SCREEN_DIR = process.env.BRAINSTORM_DIR || '/tmp/brainstorm';
80
- const OWNER_PID = process.env.BRAINSTORM_OWNER_PID ? Number(process.env.BRAINSTORM_OWNER_PID) : null;
79
+ const SESSION_DIR = process.env.BRAINSTORM_DIR || '/tmp/brainstorm';
80
+ const CONTENT_DIR = path.join(SESSION_DIR, 'content');
81
+ const STATE_DIR = path.join(SESSION_DIR, 'state');
82
+ let ownerPid = process.env.BRAINSTORM_OWNER_PID ? Number(process.env.BRAINSTORM_OWNER_PID) : null;
81
83
 
82
84
  const MIME_TYPES = {
83
85
  '.html': 'text/html', '.css': 'text/css', '.js': 'application/javascript',
@@ -112,10 +114,10 @@ function wrapInFrame(content) {
112
114
  }
113
115
 
114
116
  function getNewestScreen() {
115
- const files = fs.readdirSync(SCREEN_DIR)
117
+ const files = fs.readdirSync(CONTENT_DIR)
116
118
  .filter(f => f.endsWith('.html'))
117
119
  .map(f => {
118
- const fp = path.join(SCREEN_DIR, f);
120
+ const fp = path.join(CONTENT_DIR, f);
119
121
  return { path: fp, mtime: fs.statSync(fp).mtime.getTime() };
120
122
  })
121
123
  .sort((a, b) => b.mtime - a.mtime);
@@ -142,7 +144,7 @@ function handleRequest(req, res) {
142
144
  res.end(html);
143
145
  } else if (req.method === 'GET' && req.url.startsWith('/files/')) {
144
146
  const fileName = req.url.slice(7);
145
- const filePath = path.join(SCREEN_DIR, path.basename(fileName));
147
+ const filePath = path.join(CONTENT_DIR, path.basename(fileName));
146
148
  if (!fs.existsSync(filePath)) {
147
149
  res.writeHead(404);
148
150
  res.end('Not found');
@@ -230,7 +232,7 @@ function handleMessage(text) {
230
232
  touchActivity();
231
233
  console.log(JSON.stringify({ source: 'user-event', ...event }));
232
234
  if (event.choice) {
233
- const eventsFile = path.join(SCREEN_DIR, '.events');
235
+ const eventsFile = path.join(STATE_DIR, 'events');
234
236
  fs.appendFileSync(eventsFile, JSON.stringify(event) + '\n');
235
237
  }
236
238
  }
@@ -258,32 +260,33 @@ const debounceTimers = new Map();
258
260
  // ========== Server Startup ==========
259
261
 
260
262
  function startServer() {
261
- if (!fs.existsSync(SCREEN_DIR)) fs.mkdirSync(SCREEN_DIR, { recursive: true });
263
+ if (!fs.existsSync(CONTENT_DIR)) fs.mkdirSync(CONTENT_DIR, { recursive: true });
264
+ if (!fs.existsSync(STATE_DIR)) fs.mkdirSync(STATE_DIR, { recursive: true });
262
265
 
263
266
  // Track known files to distinguish new screens from updates.
264
267
  // macOS fs.watch reports 'rename' for both new files and overwrites,
265
268
  // so we can't rely on eventType alone.
266
269
  const knownFiles = new Set(
267
- fs.readdirSync(SCREEN_DIR).filter(f => f.endsWith('.html'))
270
+ fs.readdirSync(CONTENT_DIR).filter(f => f.endsWith('.html'))
268
271
  );
269
272
 
270
273
  const server = http.createServer(handleRequest);
271
274
  server.on('upgrade', handleUpgrade);
272
275
 
273
- const watcher = fs.watch(SCREEN_DIR, (eventType, filename) => {
276
+ const watcher = fs.watch(CONTENT_DIR, (eventType, filename) => {
274
277
  if (!filename || !filename.endsWith('.html')) return;
275
278
 
276
279
  if (debounceTimers.has(filename)) clearTimeout(debounceTimers.get(filename));
277
280
  debounceTimers.set(filename, setTimeout(() => {
278
281
  debounceTimers.delete(filename);
279
- const filePath = path.join(SCREEN_DIR, filename);
282
+ const filePath = path.join(CONTENT_DIR, filename);
280
283
 
281
284
  if (!fs.existsSync(filePath)) return; // file was deleted
282
285
  touchActivity();
283
286
 
284
287
  if (!knownFiles.has(filename)) {
285
288
  knownFiles.add(filename);
286
- const eventsFile = path.join(SCREEN_DIR, '.events');
289
+ const eventsFile = path.join(STATE_DIR, 'events');
287
290
  if (fs.existsSync(eventsFile)) fs.unlinkSync(eventsFile);
288
291
  console.log(JSON.stringify({ type: 'screen-added', file: filePath }));
289
292
  } else {
@@ -297,10 +300,10 @@ function startServer() {
297
300
 
298
301
  function shutdown(reason) {
299
302
  console.log(JSON.stringify({ type: 'server-stopped', reason }));
300
- const infoFile = path.join(SCREEN_DIR, '.server-info');
303
+ const infoFile = path.join(STATE_DIR, 'server-info');
301
304
  if (fs.existsSync(infoFile)) fs.unlinkSync(infoFile);
302
305
  fs.writeFileSync(
303
- path.join(SCREEN_DIR, '.server-stopped'),
306
+ path.join(STATE_DIR, 'server-stopped'),
304
307
  JSON.stringify({ reason, timestamp: Date.now() }) + '\n'
305
308
  );
306
309
  watcher.close();
@@ -309,8 +312,8 @@ function startServer() {
309
312
  }
310
313
 
311
314
  function ownerAlive() {
312
- if (!OWNER_PID) return true;
313
- try { process.kill(OWNER_PID, 0); return true; } catch (e) { return false; }
315
+ if (!ownerPid) return true;
316
+ try { process.kill(ownerPid, 0); return true; } catch (e) { return e.code === 'EPERM'; }
314
317
  }
315
318
 
316
319
  // Check every 60s: exit if owner process died or idle for 30 minutes
@@ -320,14 +323,27 @@ function startServer() {
320
323
  }, 60 * 1000);
321
324
  lifecycleCheck.unref();
322
325
 
326
+ // Validate owner PID at startup. If it's already dead, the PID resolution
327
+ // was wrong (common on WSL, Tailscale SSH, and cross-user scenarios).
328
+ // Disable monitoring and rely on the idle timeout instead.
329
+ if (ownerPid) {
330
+ try { process.kill(ownerPid, 0); }
331
+ catch (e) {
332
+ if (e.code !== 'EPERM') {
333
+ console.log(JSON.stringify({ type: 'owner-pid-invalid', pid: ownerPid, reason: 'dead at startup' }));
334
+ ownerPid = null;
335
+ }
336
+ }
337
+ }
338
+
323
339
  server.listen(PORT, HOST, () => {
324
340
  const info = JSON.stringify({
325
341
  type: 'server-started', port: Number(PORT), host: HOST,
326
342
  url_host: URL_HOST, url: 'http://' + URL_HOST + ':' + PORT,
327
- screen_dir: SCREEN_DIR
343
+ screen_dir: CONTENT_DIR, state_dir: STATE_DIR
328
344
  });
329
345
  console.log(info);
330
- fs.writeFileSync(path.join(SCREEN_DIR, '.server-info'), info + '\n');
346
+ fs.writeFileSync(path.join(STATE_DIR, 'server-info'), info + '\n');
331
347
  });
332
348
  }
333
349
 
@@ -78,16 +78,17 @@ fi
78
78
  SESSION_ID="$$-$(date +%s)"
79
79
 
80
80
  if [[ -n "$PROJECT_DIR" ]]; then
81
- SCREEN_DIR="${PROJECT_DIR}/.superpowers/brainstorm/${SESSION_ID}"
81
+ SESSION_DIR="${PROJECT_DIR}/.superpowers/brainstorm/${SESSION_ID}"
82
82
  else
83
- SCREEN_DIR="/tmp/brainstorm-${SESSION_ID}"
83
+ SESSION_DIR="/tmp/brainstorm-${SESSION_ID}"
84
84
  fi
85
85
 
86
- PID_FILE="${SCREEN_DIR}/.server.pid"
87
- LOG_FILE="${SCREEN_DIR}/.server.log"
86
+ STATE_DIR="${SESSION_DIR}/state"
87
+ PID_FILE="${STATE_DIR}/server.pid"
88
+ LOG_FILE="${STATE_DIR}/server.log"
88
89
 
89
- # Create fresh session directory
90
- mkdir -p "$SCREEN_DIR"
90
+ # Create fresh session directory with content and state peers
91
+ mkdir -p "${SESSION_DIR}/content" "$STATE_DIR"
91
92
 
92
93
  # Kill any existing server
93
94
  if [[ -f "$PID_FILE" ]]; then
@@ -106,22 +107,16 @@ if [[ -z "$OWNER_PID" || "$OWNER_PID" == "1" ]]; then
106
107
  OWNER_PID="$PPID"
107
108
  fi
108
109
 
109
- # On Windows/MSYS2, the MSYS2 PID namespace is invisible to Node.js.
110
- # Skip owner-PID monitoring — the 30-minute idle timeout prevents orphans.
111
- case "${OSTYPE:-}" in
112
- msys*|cygwin*|mingw*) OWNER_PID="" ;;
113
- esac
114
-
115
110
  # Foreground mode for environments that reap detached/background processes.
116
111
  if [[ "$FOREGROUND" == "true" ]]; then
117
112
  echo "$$" > "$PID_FILE"
118
- env BRAINSTORM_DIR="$SCREEN_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs
113
+ env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs
119
114
  exit $?
120
115
  fi
121
116
 
122
117
  # Start server, capturing output to log file
123
118
  # Use nohup to survive shell exit; disown to remove from job table
124
- nohup env BRAINSTORM_DIR="$SCREEN_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs > "$LOG_FILE" 2>&1 &
119
+ nohup env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs > "$LOG_FILE" 2>&1 &
125
120
  SERVER_PID=$!
126
121
  disown "$SERVER_PID" 2>/dev/null
127
122
  echo "$SERVER_PID" > "$PID_FILE"
@@ -1,19 +1,20 @@
1
1
  #!/usr/bin/env bash
2
2
  # Stop the brainstorm server and clean up
3
- # Usage: stop-server.sh <screen_dir>
3
+ # Usage: stop-server.sh <session_dir>
4
4
  #
5
5
  # Kills the server process. Only deletes session directory if it's
6
6
  # under /tmp (ephemeral). Persistent directories (.superpowers/) are
7
7
  # kept so mockups can be reviewed later.
8
8
 
9
- SCREEN_DIR="$1"
9
+ SESSION_DIR="$1"
10
10
 
11
- if [[ -z "$SCREEN_DIR" ]]; then
12
- echo '{"error": "Usage: stop-server.sh <screen_dir>"}'
11
+ if [[ -z "$SESSION_DIR" ]]; then
12
+ echo '{"error": "Usage: stop-server.sh <session_dir>"}'
13
13
  exit 1
14
14
  fi
15
15
 
16
- PID_FILE="${SCREEN_DIR}/.server.pid"
16
+ STATE_DIR="${SESSION_DIR}/state"
17
+ PID_FILE="${STATE_DIR}/server.pid"
17
18
 
18
19
  if [[ -f "$PID_FILE" ]]; then
19
20
  pid=$(cat "$PID_FILE")
@@ -42,11 +43,11 @@ if [[ -f "$PID_FILE" ]]; then
42
43
  exit 1
43
44
  fi
44
45
 
45
- rm -f "$PID_FILE" "${SCREEN_DIR}/.server.log"
46
+ rm -f "$PID_FILE" "${STATE_DIR}/server.log"
46
47
 
47
48
  # Only delete ephemeral /tmp directories
48
- if [[ "$SCREEN_DIR" == /tmp/* ]]; then
49
- rm -rf "$SCREEN_DIR"
49
+ if [[ "$SESSION_DIR" == /tmp/* ]]; then
50
+ rm -rf "$SESSION_DIR"
50
51
  fi
51
52
 
52
53
  echo '{"status": "stopped"}'
@@ -9,7 +9,7 @@ description: 当实现完成、所有测试通过、需要决定如何集成工
9
9
 
10
10
  通过提供清晰的选项并执行所选工作流来引导开发工作的收尾。
11
11
 
12
- **核心原则:** 验证测试 → 展示选项 → 执行选择 → 清理。
12
+ **核心原则:** 验证测试 → 检测环境 → 展示选项 → 执行选择 → 清理。
13
13
 
14
14
  **开始时宣布:** "我正在使用 finishing-a-development-branch 技能来完成这项工作。"
15
15
 
@@ -25,6 +25,7 @@ npm test / cargo test / pytest / go test ./...
25
25
  ```
26
26
 
27
27
  **如果测试失败:**
28
+
28
29
  ```
29
30
  测试失败(<N> 个失败)。必须先修复才能继续:
30
31
 
@@ -37,7 +38,24 @@ npm test / cargo test / pytest / go test ./...
37
38
 
38
39
  **如果测试通过:** 继续步骤 2。
39
40
 
40
- ### 步骤 2:确定基础分支
41
+ ### 步骤 2:检测环境
42
+
43
+ **在展示选项之前,先确定工作区状态:**
44
+
45
+ ```bash
46
+ GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
47
+ GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
48
+ ```
49
+
50
+ 这决定了展示哪种菜单、以及清理方式:
51
+
52
+ | 状态 | 菜单 | 清理 |
53
+ |------|------|------|
54
+ | `GIT_DIR == GIT_COMMON`(普通仓库) | 标准 4 个选项 | 无 worktree 可清理 |
55
+ | `GIT_DIR != GIT_COMMON`,命名分支 | 标准 4 个选项 | 按来源判断(见步骤 6) |
56
+ | `GIT_DIR != GIT_COMMON`,分离 HEAD | 收敛 3 个选项(无合并) | 无清理(由外部管理) |
57
+
58
+ ### 步骤 3:确定基础分支
41
59
 
42
60
  ```bash
43
61
  # 尝试常见的基础分支
@@ -46,9 +64,9 @@ git merge-base HEAD main 2>/dev/null || git merge-base HEAD master 2>/dev/null
46
64
 
47
65
  或者询问:"这个分支是从 main 分出来的——对吗?"
48
66
 
49
- ### 步骤 3:展示选项
67
+ ### 步骤 4:展示选项
50
68
 
51
- 展示以下 4 个选项:
69
+ **普通仓库和命名分支 worktree —— 准确展示以下 4 个选项:**
52
70
 
53
71
  ```
54
72
  实现已完成。你想怎么做?
@@ -61,30 +79,45 @@ git merge-base HEAD main 2>/dev/null || git merge-base HEAD master 2>/dev/null
61
79
  选哪个?
62
80
  ```
63
81
 
64
- **不要添加解释** - 保持选项简洁。
82
+ **分离 HEAD —— 准确展示以下 3 个选项:**
83
+
84
+ ```
85
+ 实现已完成。你在分离 HEAD 上(由外部管理的工作区)。
86
+
87
+ 1. 作为新分支推送并创建 Pull Request
88
+ 2. 保持现状(我稍后处理)
89
+ 3. 丢弃这项工作
90
+
91
+ 选哪个?
92
+ ```
93
+
94
+ **不要添加解释** —— 保持选项简洁。
65
95
 
66
- ### 步骤 4:执行选择
96
+ ### 步骤 5:执行选择
67
97
 
68
98
  #### 选项 1:本地合并
69
99
 
70
100
  ```bash
71
- # 切换到基础分支
72
- git checkout <base-branch>
101
+ # 切到主仓库根目录,保证 CWD 安全
102
+ MAIN_ROOT=$(git -C "$(git rev-parse --git-common-dir)/.." rev-parse --show-toplevel)
103
+ cd "$MAIN_ROOT"
73
104
 
74
- # 拉取最新代码
105
+ # 先合并 —— 在删除任何东西之前先验证合并成功
106
+ git checkout <base-branch>
75
107
  git pull
76
-
77
- # 合并功能分支
78
108
  git merge <feature-branch>
79
109
 
80
110
  # 在合并结果上验证测试
81
111
  <test command>
82
112
 
83
- # 如果测试通过
84
- git branch -d <feature-branch>
113
+ # 合并成功之后再:清理 worktree(步骤 6),然后删除分支
85
114
  ```
86
115
 
87
- 然后:清理工作树(步骤 5)
116
+ 然后:清理 worktree(步骤 6),再删除分支:
117
+
118
+ ```bash
119
+ git branch -d <feature-branch>
120
+ ```
88
121
 
89
122
  #### 选项 2:推送并创建 PR
90
123
 
@@ -103,7 +136,7 @@ EOF
103
136
  )"
104
137
  ```
105
138
 
106
- 然后:清理工作树(步骤 5)
139
+ **不要清理 worktree** —— 用户在 PR 反馈迭代时还需要它存活。
107
140
 
108
141
  #### 选项 3:保持现状
109
142
 
@@ -114,6 +147,7 @@ EOF
114
147
  #### 选项 4:丢弃
115
148
 
116
149
  **先确认:**
150
+
117
151
  ```
118
152
  这将永久删除:
119
153
  - 分支 <name>
@@ -126,28 +160,40 @@ EOF
126
160
  等待精确的确认。
127
161
 
128
162
  确认后:
163
+
129
164
  ```bash
130
- git checkout <base-branch>
131
- git branch -D <feature-branch>
165
+ MAIN_ROOT=$(git -C "$(git rev-parse --git-common-dir)/.." rev-parse --show-toplevel)
166
+ cd "$MAIN_ROOT"
132
167
  ```
133
168
 
134
- 然后:清理工作树(步骤 5)
169
+ 然后:清理 worktree(步骤 6),再强制删除分支:
170
+
171
+ ```bash
172
+ git branch -D <feature-branch>
173
+ ```
135
174
 
136
- ### 步骤 5:清理工作树
175
+ ### 步骤 6:清理工作区
137
176
 
138
- **对于选项 1、2、4:**
177
+ **只对选项 1 和 4 执行。** 选项 2 和 3 始终保留 worktree。
139
178
 
140
- 检查是否在工作树中:
141
179
  ```bash
142
- git worktree list | grep $(git branch --show-current)
180
+ GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
181
+ GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
182
+ WORKTREE_PATH=$(git rev-parse --show-toplevel)
143
183
  ```
144
184
 
145
- 如果是:
185
+ **如果 `GIT_DIR == GIT_COMMON`:** 普通仓库,无 worktree 可清理。结束。
186
+
187
+ **如果 worktree 路径在 `.worktrees/`、`worktrees/` 或 `~/.config/superpowers/worktrees/` 之下:** 这是 Superpowers 创建的 worktree —— 我们负责清理。
188
+
146
189
  ```bash
147
- git worktree remove <worktree-path>
190
+ MAIN_ROOT=$(git -C "$(git rev-parse --git-common-dir)/.." rev-parse --show-toplevel)
191
+ cd "$MAIN_ROOT"
192
+ git worktree remove "$WORKTREE_PATH"
193
+ git worktree prune # 自愈:清理任何过期的注册记录
148
194
  ```
149
195
 
150
- **对于选项 3:** 保留工作树。
196
+ **否则:** 这个工作区由宿主环境(harness)管理。**不要**移除它。如果你的平台提供了工作区退出工具,用它。否则原样保留工作区。
151
197
 
152
198
  ## 快速参考
153
199
 
@@ -161,40 +207,69 @@ git worktree remove <worktree-path>
161
207
  ## 常见错误
162
208
 
163
209
  **跳过测试验证**
210
+
164
211
  - **问题:** 合并损坏的代码、创建失败的 PR
165
212
  - **修复:** 在提供选项前始终验证测试
166
213
 
167
214
  **开放式问题**
215
+
168
216
  - **问题:** "接下来该做什么?" → 含糊不清
169
- - **修复:** 准确展示 4 个结构化选项
217
+ - **修复:** 准确展示 4 个结构化选项(分离 HEAD 时是 3 个)
218
+
219
+ **为选项 2 清理 worktree**
170
220
 
171
- **自动清理工作树**
172
- - **问题:** 在可能还需要工作树时就删除了(选项 2、3)
221
+ - **问题:** 删掉用户 PR 迭代还需要的 worktree
173
222
  - **修复:** 只在选项 1 和 4 时清理
174
223
 
224
+ **先删分支再删 worktree**
225
+
226
+ - **问题:** `git branch -d` 失败,因为 worktree 还引用着该分支
227
+ - **修复:** 先合并,再删 worktree,最后删分支
228
+
229
+ **在 worktree 内部跑 `git worktree remove`**
230
+
231
+ - **问题:** 当 CWD 在被删除的 worktree 内时,命令静默失败
232
+ - **修复:** 跑 `git worktree remove` 前先 `cd` 到主仓库根目录
233
+
234
+ **清理 harness 拥有的 worktree**
235
+
236
+ - **问题:** 移除 harness 创建的 worktree 会造成幻影状态
237
+ - **修复:** 只清理 `.worktrees/`、`worktrees/` 或 `~/.config/superpowers/worktrees/` 下的 worktree
238
+
175
239
  **丢弃时不确认**
240
+
176
241
  - **问题:** 意外删除工作成果
177
- - **修复:** 要求输入 "discard" 确认
242
+ - **修复:** 要求输入 'discard' 确认
178
243
 
179
244
  ## 红线
180
245
 
181
246
  **绝不:**
247
+
182
248
  - 在测试失败时继续
183
- - 合并前不验证测试结果
249
+ - 合并前不验证合并结果上的测试
184
250
  - 不确认就删除工作成果
185
251
  - 未经明确请求就强制推送
252
+ - 在确认合并成功之前移除 worktree
253
+ - 清理不是你创建的 worktree(按来源判断)
254
+ - 在 worktree 内部跑 `git worktree remove`
186
255
 
187
256
  **始终:**
257
+
188
258
  - 在提供选项前验证测试
189
- - 准确展示 4 个选项
259
+ - 展示菜单前检测环境
260
+ - 准确展示 4 个选项(分离 HEAD 时是 3 个)
190
261
  - 选项 4 要求输入确认
191
- - 只在选项 1 和 4 时清理工作树
262
+ - 只在选项 1 和 4 时清理 worktree
263
+ - 移除 worktree 前 `cd` 到主仓库根目录
264
+ - 移除后跑 `git worktree prune`
192
265
 
193
266
  ## 集成
194
267
 
195
268
  **被以下技能调用:**
269
+
196
270
  - **subagent-driven-development**(步骤 7)- 所有任务完成后
197
271
  - **executing-plans**(步骤 5)- 所有批次完成后
198
272
 
199
273
  **配合使用:**
274
+
200
275
  - **using-git-worktrees** - 清理由该技能创建的工作树
@@ -5,7 +5,7 @@ description: 完成任务、实现重要功能或合并前使用,用于验证
5
5
 
6
6
  # 请求代码审查
7
7
 
8
- 派遣 superpowers:code-reviewer 子代理来在问题扩散之前发现它们。审查者获得的是精心组织的评估上下文——绝不是你的会话历史。这样可以让审查者专注于工作成果而非你的思考过程,同时保留你自己的上下文以便继续工作。
8
+ 派遣代码审查子代理,在问题扩散之前发现它们。审查者获得的是精心组织的评估上下文——绝不是你的会话历史。这样可以让审查者专注于工作成果而非你的思考过程,同时保留你自己的上下文以便继续工作。
9
9
 
10
10
  **核心原则:** 早审查,勤审查。
11
11
 
@@ -29,16 +29,15 @@ BASE_SHA=$(git rev-parse HEAD~1) # 或 origin/main
29
29
  HEAD_SHA=$(git rev-parse HEAD)
30
30
  ```
31
31
 
32
- **2. 派遣 code-reviewer 子代理:**
32
+ **2. 派遣代码审查子代理:**
33
33
 
34
- 使用 Task 工具,指定 superpowers:code-reviewer 类型,填写 `code-reviewer.md` 中的模板
34
+ 使用 Task 工具,指定 `general-purpose` 类型,填写 `code-reviewer.md` 中的模板
35
35
 
36
36
  **占位符说明:**
37
- - `{WHAT_WAS_IMPLEMENTED}` - 你刚完成的内容
37
+ - `{DESCRIPTION}` - 你刚完成的内容简要说明
38
38
  - `{PLAN_OR_REQUIREMENTS}` - 预期功能
39
39
  - `{BASE_SHA}` - 起始提交
40
40
  - `{HEAD_SHA}` - 结束提交
41
- - `{DESCRIPTION}` - 简要说明
42
41
 
43
42
  **3. 处理反馈:**
44
43
  - Critical 问题立即修复
@@ -56,12 +55,11 @@ HEAD_SHA=$(git rev-parse HEAD)
56
55
  BASE_SHA=$(git log --oneline | grep "Task 1" | head -1 | awk '{print $1}')
57
56
  HEAD_SHA=$(git rev-parse HEAD)
58
57
 
59
- [派遣 superpowers:code-reviewer 子代理]
60
- WHAT_WAS_IMPLEMENTED: 会话索引的验证和修复功能
58
+ [派遣代码审查子代理]
59
+ DESCRIPTION: 添加了 verifyIndex() 和 repairIndex(),支持 4 种问题类型
61
60
  PLAN_OR_REQUIREMENTS: docs/superpowers/plans/deployment-plan.md 中的任务 2
62
61
  BASE_SHA: a7981ec
63
62
  HEAD_SHA: 3df7661
64
- DESCRIPTION: 添加了 verifyIndex() 和 repairIndex(),支持 4 种问题类型
65
63
 
66
64
  [子代理返回]:
67
65
  优点:架构清晰,测试真实
@@ -82,8 +80,8 @@ HEAD_SHA=$(git rev-parse HEAD)
82
80
  - 修复后再进入下一个任务
83
81
 
84
82
  **执行计划:**
85
- - 每批(3 个任务)后审查
86
- - 获取反馈,修复,继续
83
+ - 每个任务完成后或在自然 checkpoint 审查
84
+ - 获取反馈,应用,继续
87
85
 
88
86
  **临时开发:**
89
87
  - 合并前审查