gsd-lite 0.5.15 → 0.6.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/agents/debugger.md +11 -4
- package/agents/reviewer.md +12 -1
- package/commands/doctor.md +0 -1
- package/commands/prd.md +18 -1
- package/commands/resume.md +27 -21
- package/commands/start.md +8 -0
- package/hooks/gsd-auto-update.cjs +24 -22
- package/hooks/lib/gsd-finder.cjs +19 -3
- package/hooks/lib/semver-sort.cjs +20 -0
- package/install.js +30 -9
- package/package.json +1 -1
- package/references/execution-loop.md +1 -1
- package/references/review-classification.md +25 -6
- package/references/state-diagram.md +4 -3
- package/src/schema.js +53 -30
- package/src/tools/orchestrator/helpers.js +16 -9
- package/src/tools/orchestrator/resume.js +252 -188
- package/src/tools/state/constants.js +2 -0
- package/src/tools/state/crud.js +50 -44
- package/workflows/debugging.md +14 -11
- package/workflows/deviation-rules.md +4 -4
- package/workflows/review-cycle.md +19 -2
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"name": "gsd",
|
|
14
14
|
"source": "./",
|
|
15
15
|
"description": "AI orchestration tool — GSD management shell + Superpowers quality core. 5 commands, 4 agents, 5 workflows, MCP server, context monitoring.",
|
|
16
|
-
"version": "0.
|
|
16
|
+
"version": "0.6.0",
|
|
17
17
|
"keywords": [
|
|
18
18
|
"orchestration",
|
|
19
19
|
"mcp",
|
package/agents/debugger.md
CHANGED
|
@@ -55,8 +55,11 @@ Phase 3 假设测试:
|
|
|
55
55
|
3. 验证: 有效 → Phase 4 / 无效 → 新假设
|
|
56
56
|
|
|
57
57
|
Phase 4 修复方向建议:
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
调试器不直接写代码 — 返回 fix_direction + 测试用例描述,由 executor 实施。
|
|
59
|
+
1. 提出修复方案 (针对根因,不是症状) → 写入 `fix_direction`
|
|
60
|
+
2. 描述回归测试用例 (测什么、预期 vs 实际) → 供 executor 实现
|
|
61
|
+
- 不要自己写测试代码,你没有 Write 工具
|
|
62
|
+
- 用自然语言描述: 输入、操作步骤、预期结果、实际错误行为
|
|
60
63
|
3. 评估修复影响范围 (哪些下游可能受影响)
|
|
61
64
|
→ 3 次修复方向均被 executor 验证无效 → 停止。标记 architecture_concern: true。报告给编排器。
|
|
62
65
|
</four_phases>
|
|
@@ -66,6 +69,10 @@ Phase 4 修复方向建议:
|
|
|
66
69
|
{
|
|
67
70
|
"task_id": "2.3",
|
|
68
71
|
"outcome": "root_cause_found | fix_suggested | failed",
|
|
72
|
+
// outcome 判定:
|
|
73
|
+
// root_cause_found — 根因已确认,有明确的修复方向
|
|
74
|
+
// fix_suggested — 部分理解,建议一个尝试方向 (根因尚未完全确认)
|
|
75
|
+
// failed — 穷尽假设仍无法定位根因,或 fix_attempts >= 3
|
|
69
76
|
"root_cause": "Description of the identified root cause",
|
|
70
77
|
"evidence": [
|
|
71
78
|
{ "id": "ev:repro:error-xyz", "scope": "task:2.3", "command": "npm test", "exit_code": 1, "stdout": "...", "stderr": "...", "timestamp": "ISO8601" }
|
|
@@ -73,8 +80,8 @@ Phase 4 修复方向建议:
|
|
|
73
80
|
"hypothesis_tested": [
|
|
74
81
|
{ "hypothesis": "X causes Y", "result": "confirmed | rejected", "evidence": "non-empty string (required)" }
|
|
75
82
|
],
|
|
76
|
-
"fix_direction": "Suggested fix approach for executor",
|
|
77
|
-
"fix_attempts": 0,
|
|
83
|
+
"fix_direction": "Suggested fix approach for executor (include suggested test case description)",
|
|
84
|
+
"fix_attempts": 0, // 由编排器 dispatch context 中的 debug_context 传入,记录已尝试的修复方向次数
|
|
78
85
|
"blockers": [],
|
|
79
86
|
"architecture_concern": false
|
|
80
87
|
}
|
package/agents/reviewer.md
CHANGED
|
@@ -51,6 +51,14 @@ L1 普通编码任务 → executor 自审 + 阶段结束时批量 review
|
|
|
51
51
|
L2 关键任务 → 单任务独立 review
|
|
52
52
|
(涉及认证/支付/数据安全/核心架构的任务)
|
|
53
53
|
|
|
54
|
+
L3 最高风险任务 → 单任务独立 review + 人工确认
|
|
55
|
+
(auth/payment/security architecture 等最高风险任务)
|
|
56
|
+
- 与 L2 相同的双阶段审查流程,外加:
|
|
57
|
+
- 结果中必须包含 `requires_human_confirmation: true`
|
|
58
|
+
- review summary 必须明确列出安全影响 (security implications)
|
|
59
|
+
- 质量审查阶段必须检查 OWASP Top 10 相关问题
|
|
60
|
+
- 审查通过后 task 进入 `awaiting_user` 而非 `accepted`,需用户显式确认
|
|
61
|
+
|
|
54
62
|
判定规则按影响面,不按关键词猜测:
|
|
55
63
|
- 改 auth/payment/permission/public API/DB migration/core architecture → L2
|
|
56
64
|
- 纯 docs/comment/style/config 且无运行时语义变化 → L0
|
|
@@ -107,7 +115,9 @@ Minor = 建议修复 (命名/风格)
|
|
|
107
115
|
{
|
|
108
116
|
"scope": "task | phase",
|
|
109
117
|
"scope_id": "2.3 (task scope: string ID) | 2 (phase scope: number ID)",
|
|
110
|
-
"review_level": "L2 | L1-batch | L1",
|
|
118
|
+
"review_level": "L2 | L3 | L1-batch | L1",
|
|
119
|
+
"requires_human_confirmation": false, // L3 时必须为 true
|
|
120
|
+
"security_implications": [], // L3 时必须列出安全影响
|
|
111
121
|
"spec_passed": true,
|
|
112
122
|
"quality_passed": false,
|
|
113
123
|
"critical_issues": [
|
|
@@ -141,4 +151,5 @@ checkpoint commit ≠ accepted
|
|
|
141
151
|
L0: checkpoint commit = accepted
|
|
142
152
|
L1: checkpoint commit → phase batch review 通过 → accepted
|
|
143
153
|
L2: checkpoint commit → immediate independent review 通过 → accepted
|
|
154
|
+
L3: checkpoint commit → immediate independent review 通过 → awaiting_user → 用户确认 → accepted
|
|
144
155
|
</checkpoint_topology>
|
package/commands/doctor.md
CHANGED
package/commands/prd.md
CHANGED
|
@@ -17,13 +17,30 @@ argument-hint: File path to requirements doc, or inline description text
|
|
|
17
17
|
|
|
18
18
|
<process>
|
|
19
19
|
|
|
20
|
+
## STEP 0 — 已有项目检测
|
|
21
|
+
|
|
22
|
+
调用 `health` 工具(MCP tool 名称: health)。如果返回 state_exists=true 且项目未完成/未失败:
|
|
23
|
+
- 告知用户: "检测到进行中的 GSD 项目。"
|
|
24
|
+
- 显示当前项目状态 (项目名、当前阶段、workflow_mode)
|
|
25
|
+
- 提供选项:
|
|
26
|
+
- (a) 恢复现有项目 → 转到 `/gsd:resume`
|
|
27
|
+
- (b) 覆盖并重新开始 → 继续 STEP 1(现有 state.json 将被覆盖)
|
|
28
|
+
- (c) 取消
|
|
29
|
+
- 等待用户选择后再继续
|
|
30
|
+
|
|
31
|
+
如果无 state 或项目已完成/已失败 → 直接进入 STEP 1。
|
|
32
|
+
|
|
20
33
|
## STEP 1: 解析输入
|
|
21
34
|
|
|
22
35
|
判断 `$ARGUMENTS` 的类型:
|
|
23
36
|
|
|
24
37
|
**如果是文件路径** (包含 `/` 或 `.` 且文件存在):
|
|
25
38
|
- 使用 Read 工具读取文件内容
|
|
26
|
-
-
|
|
39
|
+
- 如果文件不存在:
|
|
40
|
+
- 告知用户文件不存在
|
|
41
|
+
- 提示常见原因: 路径拼写错误、当前工作目录不正确
|
|
42
|
+
- 建议: "请确认文件路径,或使用绝对路径重试。需要我帮你查找文件吗?"
|
|
43
|
+
- 停止
|
|
27
44
|
|
|
28
45
|
**如果是文本描述**:
|
|
29
46
|
- 直接作为需求描述使用
|
package/commands/resume.md
CHANGED
|
@@ -9,19 +9,20 @@ description: Resume project execution from saved state with workspace validation
|
|
|
9
9
|
|
|
10
10
|
<process>
|
|
11
11
|
|
|
12
|
-
## STEP 1:
|
|
12
|
+
## STEP 1: 调用 orchestrator-resume 获取状态摘要
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
14
|
+
调用 MCP tool `orchestrator-resume`,使用响应中的 `summary` 字段展示状态给用户:
|
|
15
|
+
- 如果响应为 error 且 message 包含 "No .gsd directory" → 告知用户 "未找到 GSD 项目状态,请先运行 /gsd:start 或 /gsd:prd",停止
|
|
16
|
+
- 如果响应为 error → 告知用户错误信息并停止
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
`summary` 字段包含:
|
|
19
19
|
- `workflow_mode` — 当前工作流状态
|
|
20
|
-
- `current_phase`
|
|
21
|
-
- `
|
|
22
|
-
- `
|
|
23
|
-
- `
|
|
24
|
-
|
|
20
|
+
- `current_phase` — 当前阶段 (格式: "N/M")
|
|
21
|
+
- `current_task` — 当前任务 (id + name)
|
|
22
|
+
- `phase_progress` — 阶段进度 (格式: "done/total")
|
|
23
|
+
- `recent_decisions` — 最近 2-3 个决策 (如有)
|
|
24
|
+
|
|
25
|
+
注意: 不需要单独读取 state.json,`orchestrator-resume` 的响应已包含所有需要展示的信息。
|
|
25
26
|
|
|
26
27
|
## STEP 2: 前置校验
|
|
27
28
|
|
|
@@ -55,10 +56,15 @@ description: Resume project execution from saved state with workspace validation
|
|
|
55
56
|
- 或 research.decision_index 中有条目的 expires_at 已过期
|
|
56
57
|
- → 覆写 `workflow_mode = research_refresh_needed`
|
|
57
58
|
|
|
58
|
-
5.
|
|
59
|
+
5. **Dirty-phase 回滚检测:**
|
|
60
|
+
- 检查已完成 phase 中是否有 `needs_revalidation` 状态的 task
|
|
61
|
+
- 如有 → 回滚 `current_phase` 到最早的 dirty phase
|
|
62
|
+
- → 覆写 `workflow_mode = executing_task`
|
|
63
|
+
|
|
64
|
+
6. **全部通过:**
|
|
59
65
|
- 保持原 `workflow_mode` 不变
|
|
60
66
|
|
|
61
|
-
校验顺序: 1→2→3→4,首个命中的覆写生效 (不累积)
|
|
67
|
+
校验顺序: 1→2→3→4→5,首个命中的覆写生效 (不累积)
|
|
62
68
|
</HARD-GATE>
|
|
63
69
|
|
|
64
70
|
## STEP 3: 按 workflow_mode 恢复
|
|
@@ -106,8 +112,8 @@ description: Resume project execution from saved state with workspace validation
|
|
|
106
112
|
### `awaiting_clear` — 继续自动执行
|
|
107
113
|
|
|
108
114
|
- 上下文已通过 /clear 清理
|
|
109
|
-
-
|
|
110
|
-
-
|
|
115
|
+
- 再次验证上下文健康度 ≥ 40%,不足则要求再次 /clear
|
|
116
|
+
- 验证通过后从 `current_phase` + `current_task` 恢复调度
|
|
111
117
|
|
|
112
118
|
---
|
|
113
119
|
|
|
@@ -208,17 +214,17 @@ description: Resume project execution from saved state with workspace validation
|
|
|
208
214
|
|
|
209
215
|
## STEP 4: 显示当前进度 + 下一动作
|
|
210
216
|
|
|
211
|
-
|
|
217
|
+
每次恢复后使用 `orchestrator-resume` 响应中的 `summary` 字段展示简要进度面板:
|
|
212
218
|
|
|
213
219
|
```
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
下一步: {根据
|
|
220
|
+
模式: {summary.workflow_mode}
|
|
221
|
+
进度: Phase {summary.current_phase} | Task {summary.phase_progress}
|
|
222
|
+
当前: {summary.current_task.id} — {summary.current_task.name}
|
|
223
|
+
决策: {summary.recent_decisions (如有)}
|
|
224
|
+
下一步: {根据 action 推导的下一动作}
|
|
219
225
|
```
|
|
220
226
|
|
|
221
|
-
|
|
227
|
+
注意: 所有展示数据直接取自 `summary` 字段,不需要额外读取 state.json。
|
|
222
228
|
|
|
223
229
|
## STEP 5: 自动执行循环
|
|
224
230
|
|
package/commands/start.md
CHANGED
|
@@ -58,3 +58,11 @@ argument-hint: Optional feature or project description
|
|
|
58
58
|
使用 Read 工具读取 `workflows/execution-flow.md`,严格按照其中 STEP 5-12 执行。
|
|
59
59
|
|
|
60
60
|
</process>
|
|
61
|
+
|
|
62
|
+
<EXTREMELY-IMPORTANT>
|
|
63
|
+
## 编排器纪律
|
|
64
|
+
- 只有编排器写 state.json,子代理不直接写
|
|
65
|
+
- 所有摘要/提示在展示时从 canonical fields 推导,不持久化 derived fields
|
|
66
|
+
- 子代理返回结构化 JSON,不解析自然语言
|
|
67
|
+
- 上下文 < 35% → 保存状态 + workflow_mode = awaiting_clear + 停止执行
|
|
68
|
+
</EXTREMELY-IMPORTANT>
|
|
@@ -7,6 +7,7 @@ const fs = require('node:fs');
|
|
|
7
7
|
const path = require('node:path');
|
|
8
8
|
const os = require('node:os');
|
|
9
9
|
const { execSync, spawnSync } = require('node:child_process');
|
|
10
|
+
const { semverSortComparator } = require('./lib/semver-sort.cjs');
|
|
10
11
|
|
|
11
12
|
// ── Configuration ──────────────────────────────────────────
|
|
12
13
|
const GITHUB_REPO = 'sdsrss/gsd-lite';
|
|
@@ -195,12 +196,13 @@ async function withFileLock(fn) {
|
|
|
195
196
|
}
|
|
196
197
|
}
|
|
197
198
|
|
|
199
|
+
if (!acquired) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
198
202
|
try {
|
|
199
203
|
return await fn();
|
|
200
204
|
} finally {
|
|
201
|
-
|
|
202
|
-
try { fs.rmSync(STATE_LOCK_FILE, { force: true }); } catch {}
|
|
203
|
-
}
|
|
205
|
+
try { fs.rmSync(STATE_LOCK_FILE, { force: true }); } catch {}
|
|
204
206
|
}
|
|
205
207
|
}
|
|
206
208
|
|
|
@@ -209,7 +211,7 @@ async function withFileLock(fn) {
|
|
|
209
211
|
// 1. PLUGIN_AUTO_UPDATE env set → recursive guard (auto-update already in progress)
|
|
210
212
|
// 2. Running from a git clone → dev mode (developer working on source)
|
|
211
213
|
function shouldSkipUpdateCheck() {
|
|
212
|
-
if (process.env.PLUGIN_AUTO_UPDATE) return true;
|
|
214
|
+
if (process.env.PLUGIN_AUTO_UPDATE === '1') return true;
|
|
213
215
|
return isDevMode();
|
|
214
216
|
}
|
|
215
217
|
|
|
@@ -297,15 +299,8 @@ async function fetchLatestRelease(token) {
|
|
|
297
299
|
}
|
|
298
300
|
|
|
299
301
|
// ── Version Comparison (semver) ────────────────────────────
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
const pb = b.split('.').map(Number);
|
|
303
|
-
for (let i = 0; i < 3; i++) {
|
|
304
|
-
if ((pa[i] || 0) > (pb[i] || 0)) return 1;
|
|
305
|
-
if ((pa[i] || 0) < (pb[i] || 0)) return -1;
|
|
306
|
-
}
|
|
307
|
-
return 0;
|
|
308
|
-
}
|
|
302
|
+
// Reuse shared comparator; callers only check sign (> 0, < 0, === 0)
|
|
303
|
+
const compareVersions = semverSortComparator;
|
|
309
304
|
|
|
310
305
|
function getCurrentVersion(mode = getInstallMode()) {
|
|
311
306
|
const candidates = mode === 'manual'
|
|
@@ -460,18 +455,25 @@ function pruneOldCacheVersions(cacheBase, keepCount = 3, verbose = false) {
|
|
|
460
455
|
.map(e => e.name);
|
|
461
456
|
if (entries.length <= keepCount) return;
|
|
462
457
|
|
|
463
|
-
// Sort by semver
|
|
464
|
-
const sorted = entries.slice().sort(
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
|
|
458
|
+
// Sort by semver using shared comparator
|
|
459
|
+
const sorted = entries.slice().sort(semverSortComparator);
|
|
460
|
+
|
|
461
|
+
// Detect versions with active processes to avoid disrupting running sessions
|
|
462
|
+
let activeVersions;
|
|
463
|
+
try {
|
|
464
|
+
const psOutput = spawnSync('ps', ['aux'], { stdio: 'pipe', timeout: 5000 });
|
|
465
|
+
const lines = (psOutput.stdout || '').toString();
|
|
466
|
+
activeVersions = new Set(
|
|
467
|
+
entries.filter(ver => lines.includes(`/cache/gsd/gsd/${ver}/`))
|
|
468
|
+
);
|
|
469
|
+
} catch { activeVersions = new Set(); }
|
|
472
470
|
|
|
473
471
|
const toRemove = sorted.slice(0, sorted.length - keepCount);
|
|
474
472
|
for (const ver of toRemove) {
|
|
473
|
+
if (activeVersions.has(ver)) {
|
|
474
|
+
if (verbose) console.log(` Skipped ${ver} (active process detected)`);
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
475
477
|
const verPath = path.join(cacheBase, ver);
|
|
476
478
|
fs.rmSync(verPath, { recursive: true, force: true });
|
|
477
479
|
if (verbose) console.log(` Pruned old cache: ${ver}`);
|
package/hooks/lib/gsd-finder.cjs
CHANGED
|
@@ -9,26 +9,42 @@
|
|
|
9
9
|
const fs = require('node:fs');
|
|
10
10
|
const path = require('node:path');
|
|
11
11
|
|
|
12
|
+
const _findCache = new Map();
|
|
13
|
+
|
|
12
14
|
/**
|
|
13
15
|
* Walk from startDir up to filesystem root looking for a .gsd directory
|
|
14
16
|
* that contains state.json. Returns the absolute path to .gsd or null.
|
|
17
|
+
* Results are cached per startDir (positive hits only — null is not cached
|
|
18
|
+
* so that a later-created .gsd directory can be discovered).
|
|
15
19
|
*/
|
|
16
20
|
function findGsdDir(startDir) {
|
|
21
|
+
if (_findCache.has(startDir)) return _findCache.get(startDir);
|
|
22
|
+
|
|
17
23
|
let dir = startDir;
|
|
18
24
|
while (true) {
|
|
19
25
|
const candidate = path.join(dir, '.gsd');
|
|
20
26
|
try {
|
|
21
27
|
if (fs.statSync(candidate).isDirectory()) {
|
|
22
28
|
// Only return if state.json exists (not just an empty .gsd dir)
|
|
23
|
-
if (fs.existsSync(path.join(candidate, 'state.json')))
|
|
29
|
+
if (fs.existsSync(path.join(candidate, 'state.json'))) {
|
|
30
|
+
_findCache.set(startDir, candidate);
|
|
31
|
+
return candidate;
|
|
32
|
+
}
|
|
24
33
|
}
|
|
25
34
|
} catch { /* skip */ }
|
|
26
35
|
const parent = path.dirname(dir);
|
|
27
|
-
if (parent === dir) return null;
|
|
36
|
+
if (parent === dir) return null; // Don't cache negative results
|
|
28
37
|
dir = parent;
|
|
29
38
|
}
|
|
30
39
|
}
|
|
31
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Clear the findGsdDir result cache. Useful for testing.
|
|
43
|
+
*/
|
|
44
|
+
function clearFindGsdDirCache() {
|
|
45
|
+
_findCache.clear();
|
|
46
|
+
}
|
|
47
|
+
|
|
32
48
|
/**
|
|
33
49
|
* Read and parse .gsd/state.json. Returns parsed object or null on any failure.
|
|
34
50
|
*/
|
|
@@ -81,4 +97,4 @@ function getProgress(state) {
|
|
|
81
97
|
};
|
|
82
98
|
}
|
|
83
99
|
|
|
84
|
-
module.exports = { findGsdDir, readState, getProgress };
|
|
100
|
+
module.exports = { findGsdDir, clearFindGsdDirCache, readState, getProgress };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Shared semver sort comparator for use by install.js and gsd-auto-update.cjs
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Compare two semver version strings (e.g. "1.2.3") for sorting.
|
|
6
|
+
* Returns negative if a < b, positive if a > b, 0 if equal.
|
|
7
|
+
* @param {string} a
|
|
8
|
+
* @param {string} b
|
|
9
|
+
* @returns {number}
|
|
10
|
+
*/
|
|
11
|
+
function semverSortComparator(a, b) {
|
|
12
|
+
const pa = a.split('.').map(Number);
|
|
13
|
+
const pb = b.split('.').map(Number);
|
|
14
|
+
for (let i = 0; i < 3; i++) {
|
|
15
|
+
if ((pa[i] || 0) !== (pb[i] || 0)) return (pa[i] || 0) - (pb[i] || 0);
|
|
16
|
+
}
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = { semverSortComparator };
|
package/install.js
CHANGED
|
@@ -6,8 +6,11 @@ import { join, dirname } from 'node:path';
|
|
|
6
6
|
import { homedir } from 'node:os';
|
|
7
7
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
8
8
|
import { execSync } from 'node:child_process';
|
|
9
|
+
import { createRequire } from 'node:module';
|
|
9
10
|
|
|
10
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const _require = createRequire(import.meta.url);
|
|
13
|
+
const { semverSortComparator } = _require('./hooks/lib/semver-sort.cjs');
|
|
11
14
|
const CLAUDE_DIR = process.env.CLAUDE_CONFIG_DIR || join(homedir(), '.claude');
|
|
12
15
|
const RUNTIME_DIR = join(CLAUDE_DIR, 'gsd');
|
|
13
16
|
const DRY_RUN = process.argv.includes('--dry-run');
|
|
@@ -116,8 +119,23 @@ export function main() {
|
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
// Reset managed runtime directory to avoid stale files on reinstall
|
|
122
|
+
// Preserve runtime/ subdirectory (update-state.json, update-notification.json)
|
|
119
123
|
if (!DRY_RUN && existsSync(RUNTIME_DIR)) {
|
|
124
|
+
const runtimeSubdir = join(RUNTIME_DIR, 'runtime');
|
|
125
|
+
const preserveRuntime = existsSync(runtimeSubdir);
|
|
126
|
+
let runtimeBackup;
|
|
127
|
+
if (preserveRuntime) {
|
|
128
|
+
runtimeBackup = join(RUNTIME_DIR, '..', '.gsd-runtime-backup');
|
|
129
|
+
try { cpSync(runtimeSubdir, runtimeBackup, { recursive: true }); } catch { runtimeBackup = null; }
|
|
130
|
+
}
|
|
120
131
|
rmSync(RUNTIME_DIR, { recursive: true, force: true });
|
|
132
|
+
if (runtimeBackup) {
|
|
133
|
+
try {
|
|
134
|
+
mkdirSync(join(RUNTIME_DIR, 'runtime'), { recursive: true });
|
|
135
|
+
cpSync(runtimeBackup, join(RUNTIME_DIR, 'runtime'), { recursive: true });
|
|
136
|
+
rmSync(runtimeBackup, { recursive: true, force: true });
|
|
137
|
+
} catch { /* best effort */ }
|
|
138
|
+
}
|
|
121
139
|
}
|
|
122
140
|
|
|
123
141
|
// 1. Commands
|
|
@@ -242,19 +260,22 @@ export function main() {
|
|
|
242
260
|
const entries = readdirSync(cacheBase, { withFileTypes: true })
|
|
243
261
|
.filter(e => e.isDirectory()).map(e => e.name);
|
|
244
262
|
if (entries.length > 3) {
|
|
245
|
-
const sorted = entries.slice().sort(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
|
|
263
|
+
const sorted = entries.slice().sort(semverSortComparator);
|
|
264
|
+
// Detect versions with active processes to avoid disrupting running sessions
|
|
265
|
+
let activeVersions;
|
|
266
|
+
try {
|
|
267
|
+
const psOut = execSync('ps aux', { stdio: 'pipe', timeout: 5000 }).toString();
|
|
268
|
+
activeVersions = new Set(entries.filter(v => psOut.includes(`/cache/gsd/gsd/${v}/`)));
|
|
269
|
+
} catch { activeVersions = new Set(); }
|
|
270
|
+
|
|
253
271
|
const toRemove = sorted.slice(0, sorted.length - 3);
|
|
272
|
+
let pruned = 0;
|
|
254
273
|
for (const ver of toRemove) {
|
|
274
|
+
if (activeVersions.has(ver)) continue; // skip versions with running processes
|
|
255
275
|
rmSync(join(cacheBase, ver), { recursive: true, force: true });
|
|
276
|
+
pruned++;
|
|
256
277
|
}
|
|
257
|
-
log(` ✓ Pruned ${
|
|
278
|
+
if (pruned > 0) log(` ✓ Pruned ${pruned} old cache version(s), kept latest 3`);
|
|
258
279
|
}
|
|
259
280
|
} catch { /* best effort */ }
|
|
260
281
|
}
|
package/package.json
CHANGED
|
@@ -81,7 +81,7 @@ executor 上下文传递协议 (orchestrator → executor):
|
|
|
81
81
|
**审查级别运行时重分类:**
|
|
82
82
|
- executor 报告 `contract_changed: true` + 涉及 auth/payment/public API → 自动升级为 L2
|
|
83
83
|
- executor 标注 `[LEVEL-UP]` → 编排器采纳
|
|
84
|
-
- 不主动降级 (安全优先)
|
|
84
|
+
- 不主动降级 (安全优先),L1 + high confidence + 有 evidence 且无测试失败 → L0 例外
|
|
85
85
|
|
|
86
86
|
### 11.6 — 处理 reviewer 结果
|
|
87
87
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
| L0 | 无运行时语义变化 (docs/config/style) | checkpoint 后直接 accepted |
|
|
8
8
|
| L1 | 普通编码任务 (默认) | phase 结束后批量审查 |
|
|
9
9
|
| L2 | 高风险 (auth/payment/public API/DB migration) | checkpoint 后立即独立审查 |
|
|
10
|
-
| L3 | 最高风险 (
|
|
10
|
+
| L3 | 最高风险 (auth/payment/security architecture) | checkpoint 后立即独立审查 + 人工确认 |
|
|
11
11
|
|
|
12
12
|
## 运行时重分类
|
|
13
13
|
|
|
@@ -21,7 +21,10 @@
|
|
|
21
21
|
/\b(auth|payment|security|public.?api|login|token|credential|session|oauth)\b/i
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
规则:
|
|
24
|
+
规则: L2/L3 只升不降。L1 在满足条件时可降为 L0:
|
|
25
|
+
- `confidence: 'high'` + `contract_changed: false`
|
|
26
|
+
- 有 evidence 且无测试失败
|
|
27
|
+
- 此时自审即可,无需独立审查
|
|
25
28
|
|
|
26
29
|
## 决策树
|
|
27
30
|
|
|
@@ -31,6 +34,7 @@ task.level 当前值?
|
|
|
31
34
|
└── L0 或 L1
|
|
32
35
|
├── executor decisions 含 [LEVEL-UP]? -> 升级为 L2
|
|
33
36
|
├── contract_changed: true + task.name 匹配敏感关键词? -> 升级为 L2
|
|
37
|
+
├── L1 + confidence: 'high' + !contract_changed + 有 evidence 且无测试失败? -> 降为 L0
|
|
34
38
|
└── 否 -> 保持当前级别
|
|
35
39
|
```
|
|
36
40
|
|
|
@@ -63,15 +67,30 @@ executor checkpointed
|
|
|
63
67
|
-> 批量审查所有 checkpointed task (排除 L0)
|
|
64
68
|
```
|
|
65
69
|
|
|
66
|
-
### L2
|
|
70
|
+
### L2 流程
|
|
67
71
|
|
|
68
72
|
```
|
|
69
73
|
executor checkpointed
|
|
70
|
-
-> handleExecutorResult 检测
|
|
74
|
+
-> handleExecutorResult 检测 reviewLevel === 'L2' && review_required !== false
|
|
71
75
|
-> 设置 current_review = { scope: 'task', scope_id: task.id, stage: 'spec' }
|
|
72
76
|
-> workflow_mode = 'reviewing_task'
|
|
73
|
-
-> 派发 reviewer (scope='task', review_level='L2'
|
|
74
|
-
->
|
|
77
|
+
-> 派发 reviewer (scope='task', review_level='L2')
|
|
78
|
+
-> 审查通过后 → accepted,释放下游依赖
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### L3 流程
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
executor checkpointed
|
|
85
|
+
-> handleExecutorResult 检测 reviewLevel === 'L3' && review_required !== false
|
|
86
|
+
-> 设置 current_review = { scope: 'task', scope_id: task.id, stage: 'spec' }
|
|
87
|
+
-> workflow_mode = 'reviewing_task'
|
|
88
|
+
-> 派发 reviewer (scope='task', review_level='L3')
|
|
89
|
+
-> reviewer 返回 requires_human_confirmation: true + security_implications
|
|
90
|
+
-> 审查通过后 → task 进入 awaiting_user (非 accepted)
|
|
91
|
+
-> 编排器向用户展示审查摘要 + 安全影响
|
|
92
|
+
-> 用户显式确认 → accepted,释放下游依赖
|
|
93
|
+
-> 用户拒绝 → 返工
|
|
75
94
|
```
|
|
76
95
|
|
|
77
96
|
## Reviewer 结果处理
|
|
@@ -62,7 +62,7 @@ stateDiagram-v2
|
|
|
62
62
|
| `reviewing` | `accepted`, `active` |
|
|
63
63
|
| `accepted` | *(终态,无后续转换)* |
|
|
64
64
|
| `blocked` | `active` |
|
|
65
|
-
| `failed` |
|
|
65
|
+
| `failed` | `active` (H-3: 用户显式恢复) |
|
|
66
66
|
|
|
67
67
|
### Mermaid 图
|
|
68
68
|
|
|
@@ -81,15 +81,16 @@ stateDiagram-v2
|
|
|
81
81
|
|
|
82
82
|
blocked --> active : 阻塞解除
|
|
83
83
|
|
|
84
|
+
failed --> active : H-3 用户显式恢复 (resume 提供 retry/skip/replan)
|
|
85
|
+
|
|
84
86
|
accepted --> [*]
|
|
85
|
-
failed --> [*]
|
|
86
87
|
```
|
|
87
88
|
|
|
88
89
|
### 关键路径说明
|
|
89
90
|
|
|
90
91
|
- **正常路径**: `pending -> active -> reviewing -> accepted`
|
|
91
92
|
- **返工路径**: `active -> reviewing -> active -> reviewing -> accepted` (最多循环)
|
|
92
|
-
- **失败路径**: `active -> failed` (
|
|
93
|
+
- **失败路径**: `active -> failed -> active` (H-3: 可通过 resume 恢复)
|
|
93
94
|
- **Phase 推进**: 当前 phase `accepted` 后,下一个 `pending` phase 自动转为 `active`
|
|
94
95
|
|
|
95
96
|
来源: `PHASE_LIFECYCLE` in `src/schema.js`
|