sillyspec 3.25.8 → 3.25.11
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/skills/sillyspec-auto/SKILL.md +8 -13
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +8 -6
- package/.claude/skills/sillyspec-continue/SKILL.md +3 -3
- package/.claude/skills/sillyspec-doctor/SKILL.md +1 -1
- package/.claude/skills/sillyspec-execute/SKILL.md +10 -4
- package/.claude/skills/sillyspec-plan/SKILL.md +1 -1
- package/.claude/skills/sillyspec-verify/SKILL.md +1 -1
- package/README.md +9 -0
- package/package.json +2 -2
- package/src/change-list.js +11 -8
- package/src/change-risk-profile.js +22 -0
- package/src/db.js +160 -159
- package/src/dispatch/backends/local-agent.js +152 -0
- package/src/dispatch/backends/sillyhub-mcp.js +242 -0
- package/src/dispatch/probe.js +208 -0
- package/src/dispatch/strategy.js +121 -0
- package/src/doctor-diagnostics.js +19 -25
- package/src/fs-atomic.js +69 -69
- package/src/git-helper.js +66 -0
- package/src/hooks/worktree-guard.js +64 -55
- package/src/index.js +215 -34
- package/src/init.js +7 -14
- package/src/machine-interface.js +54 -19
- package/src/migrate.js +14 -4
- package/src/modules.js +16 -5
- package/src/progress/change-registry.js +72 -70
- package/src/progress/consistency-doctor.js +71 -14
- package/src/progress/shared.js +6 -3
- package/src/progress/stage-machine.js +61 -65
- package/src/progress/step-store.js +62 -67
- package/src/progress.js +155 -233
- package/src/quicklog.js +13 -5
- package/src/run/command.js +65 -37
- package/src/run/complete-handlers.js +1005 -889
- package/src/run/complete.js +115 -208
- package/src/run/concurrent-detect.js +132 -0
- package/src/run/gates.js +289 -17
- package/src/run/prompt.js +22 -68
- package/src/run/quick-audit.js +10 -9
- package/src/run/shared.js +648 -600
- package/src/run/stage.js +43 -24
- package/src/scan-postcheck.js +9 -31
- package/src/sillyhub-mcp/client.js +389 -0
- package/src/spec-dir-typo.js +71 -0
- package/src/stage-contract-engine.js +22 -1
- package/src/stage-contract-spec.js +11 -4
- package/src/stage-contract.js +26 -3
- package/src/stage-review.js +13 -9
- package/src/stages/archive.js +4 -6
- package/src/stages/brainstorm-auto.js +26 -20
- package/src/stages/brainstorm.js +29 -5
- package/src/stages/cmd-existence.js +145 -0
- package/src/stages/doctor.js +9 -6
- package/src/stages/execute.js +61 -20
- package/src/stages/plan-postcheck.js +152 -0
- package/src/stages/plan.js +16 -31
- package/src/stages/quick.js +1 -1
- package/src/stages/scan.js +9 -3
- package/src/stages/status.js +0 -1
- package/src/stages/verify.js +12 -7
- package/src/sync.js +4 -4
- package/src/task-review.js +30 -1
- package/src/verify-postcheck.js +933 -862
- package/src/version.js +19 -0
- package/src/worktree-apply.js +85 -68
- package/src/worktree-deps.js +89 -5
- package/src/worktree.js +213 -111
- package/templates/prompts/taskcard-rules.md +27 -0
- package/templates/prompts/verify-probes.md +3 -3
- package/.claude/skills/sillyspec-propose/SKILL.md +0 -87
- package/src/stages/propose.js +0 -181
package/src/stages/scan.js
CHANGED
|
@@ -38,6 +38,9 @@ export const definition = {
|
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
name: '构建扫描项目列表',
|
|
41
|
+
conditionalWait: true,
|
|
42
|
+
waitReason: '等待用户确认扫描项目列表',
|
|
43
|
+
waitOptions: ['全部重新扫描', '只补缺失', '跳过'],
|
|
41
44
|
prompt: `确定本次要扫描的项目列表。
|
|
42
45
|
|
|
43
46
|
### 操作
|
|
@@ -139,7 +142,7 @@ export const definition = {
|
|
|
139
142
|
对每个项目分别执行(将 \`<project>\` 替换为实际项目名)
|
|
140
143
|
4. 如果检查报告有失败项,按报告中的角色和文件重试失败的部分
|
|
141
144
|
|
|
142
|
-
### 覆盖保护
|
|
145
|
+
### 覆盖保护
|
|
143
146
|
- **scan_depth: quick 的浅层文档允许覆盖升级**:读取旧 frontmatter 时,若旧文档含 scan_depth: quick(由 --quick 快速接入生成的浅层版本),即使其 source_commit 与当前 HEAD 一致、updated_at 未变,也允许覆盖重写——深度扫描的目的就是把它升级为完整文档。
|
|
144
147
|
- 生成每份 scan 文档时,frontmatter 必须包含:
|
|
145
148
|
\`\`\`yaml
|
|
@@ -330,6 +333,9 @@ _module-map.yaml 生成结果(已存在/已生成/模块列表)`,
|
|
|
330
333
|
{
|
|
331
334
|
name: '生成模块卡片文档',
|
|
332
335
|
perProject: true,
|
|
336
|
+
conditionalWait: true,
|
|
337
|
+
waitReason: '检测到已有模块文档覆盖风险',
|
|
338
|
+
waitOptions: ['只生成缺失', '全部重新生成', '跳过'],
|
|
333
339
|
prompt: `根据当前项目的 \`_module-map.yaml\` 生成模块卡片文档。
|
|
334
340
|
|
|
335
341
|
### ⚠️ 重要:模块卡片只负责人类语义说明
|
|
@@ -350,7 +356,7 @@ _module-map.yaml 生成结果(已存在/已生成/模块列表)`,
|
|
|
350
356
|
### 生成方法(子代理并行,只针对用户选中的模块)
|
|
351
357
|
**你必须为每个模块启动独立子代理执行,不要自己写文档。**
|
|
352
358
|
|
|
353
|
-
每个子代理的 prompt(**主 agent
|
|
359
|
+
每个子代理的 prompt(**主 agent 启动前必须拼入**):
|
|
354
360
|
- 模块名和路径(从 _module-map.yaml 读取)
|
|
355
361
|
- 环境探测结果摘要(构建工具、语言框架)
|
|
356
362
|
- scan 文档关键信息摘要(ARCHITECTURE.md 的技术栈、CONVENTIONS.md 的代码风格要点,如已生成)
|
|
@@ -526,7 +532,7 @@ INDEX.md 维护索引,格式(每行:关键词1|关键词2 → [条目名](
|
|
|
526
532
|
3. 自检门控:ARCHITECTURE(技术栈+Schema摘要)、CONVENTIONS(隐形规则+代码风格)、STRUCTURE(目录结构)、INTEGRATIONS(外部依赖)、TESTING(测试现状)、CONCERNS(技术债务)、PROJECT(项目概览)
|
|
527
533
|
4. 检查 flows/ 和 glossary.md 是否已生成(如有)
|
|
528
534
|
5. 清理:\`rm -f {DOCS_ROOT}/scan/_env-detect.md\`
|
|
529
|
-
6. 如果非平台模式:\`git add
|
|
535
|
+
6. 如果非平台模式:\`git add {DOCS_ROOT}/ {KNOWLEDGE_ROOT}/\` — 暂存本次扫描产物(scan 文档 / 模块文档 / flows / glossary / knowledge;勿用 .sillyspec/ 整目录——会裹挟 changes/ 下其他活跃变更;不要 commit,由用户通过统一提交工具处理)。如果平台模式:跳过 git add(specRoot 不在 sourceRoot 的 git repo 内)。
|
|
530
536
|
|
|
531
537
|
### ⛔ 路径合规检查(平台模式下必须执行)
|
|
532
538
|
7. 确认所有文档都写入 \`{DOCS_ROOT}/\`(spec-root 下),**而非源码目录下的 .sillyspec/**
|
package/src/stages/status.js
CHANGED
|
@@ -28,7 +28,6 @@ export const definition = {
|
|
|
28
28
|
1. \`ls .sillyspec/changes/ 2>/dev/null | grep -v archive\`
|
|
29
29
|
2. 对每个进行中的变更:检查 proposal.md ✅/❌、design.md ✅/❌、requirements.md ✅/❌、tasks.md — X/Y 完成
|
|
30
30
|
3. \`ls .sillyspec/changes/archive/ 2>/dev/null | wc -l\`
|
|
31
|
-
4. \`cat .sillyspec/HANDOFF.json 2>/dev/null\`
|
|
32
31
|
|
|
33
32
|
### 输出
|
|
34
33
|
变更状态列表`,
|
package/src/stages/verify.js
CHANGED
|
@@ -31,6 +31,9 @@ export const definition = {
|
|
|
31
31
|
- 运行测试命令(不修改源码)
|
|
32
32
|
- 运行 lint 命令(不自动修复)
|
|
33
33
|
|
|
34
|
+
### 长测试/构建执行铁律
|
|
35
|
+
- 长测试/构建/lint 命令必须**前台同步执行**,禁止 run_in_background:true / & / nohup / disown——后台任务易被会话生命周期回收导致中断无果
|
|
36
|
+
|
|
34
37
|
如果发现文件缺失或异常,**只报告问题,不尝试修复**。
|
|
35
38
|
`,
|
|
36
39
|
|
|
@@ -72,16 +75,18 @@ export const definition = {
|
|
|
72
75
|
### Execute Evidence 传递检查
|
|
73
76
|
7. 检查 verify-required-evidence.json 是否存在(由 execute 阶段 Task Review Gate 写入)
|
|
74
77
|
- 路径:变更目录下的 verify-required-evidence.json
|
|
75
|
-
-
|
|
78
|
+
- 文件 schema:\`{ items: [{ task, verdict, evidence: string[] }] }\`——顶层是 \`items\`,每项的 \`evidence\` 是字符串数组(**不是** \`requiredEvidence\` 键;旧 prompt 写 requiredEvidence 是字段名错配,照读必落空)
|
|
79
|
+
- 如果存在 → 逐项读取 \`items\`,对每个 cannot_verify 任务逐条核对其 \`evidence\` 数组是否已满足
|
|
76
80
|
- 每条 evidence 必须在 verify-result.md 中给出明确结论(satisfied / missing / partial)
|
|
77
81
|
- 如果有任何 evidence 为 missing → verify 结论不能为 PASS
|
|
78
82
|
- 如果文件不存在 → 表示 execute 阶段无 cannot_verify 任务,正常继续
|
|
83
|
+
- CLI 会 advisory 复核每个 cannot_verify 任务是否在 verify-result.md 体现(未体现仅 warn 不阻断归档;evidence 是否真满足由你诚实判定,CLI 不替你语义判定)
|
|
79
84
|
|
|
80
85
|
### 模块文档加载
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
8. 读取 \`.sillyspec/docs/<project>/modules/_module-map.yaml\`(不存在则跳过以下步骤)
|
|
87
|
+
9. 根据 design.md 的文件变更清单匹配 _module-map.yaml 中的模块
|
|
88
|
+
10. 读取匹配到的 \`.sillyspec/docs/<project>/modules/<module>.md\`
|
|
89
|
+
11. **检查模块索引可信度**:如果相关模块的 needs_review 为 true,提示"该模块索引可能不可信,需要回看模块卡片或源码"
|
|
85
90
|
|
|
86
91
|
### 输出
|
|
87
92
|
文件加载确认清单(含 decisions.md 当前版本/未决项状态、模块文档 + 索引可信度)`,
|
|
@@ -130,7 +135,7 @@ export const definition = {
|
|
|
130
135
|
4. API 设计是否符合
|
|
131
136
|
5. **Reverse Sync 检查**:如果发现实现合理但 design.md 未覆盖,先更新 design.md 补充遗漏
|
|
132
137
|
6. **模块文档一致性检查**:如果在"加载规范并锚定"步骤中加载了模块文档,检查实现是否符合模块文档描述的当前设计(特别关注接口签名、数据流、依赖关系)。不符合时标记 ⚠️(不阻断,模块文档可能未及时更新)
|
|
133
|
-
7. **决策链路检查**:如果存在 decisions.md,输出 D-xxx@vN → FR-xxx → task-xx → evidence
|
|
138
|
+
7. **决策链路检查**:如果存在 decisions.md,输出 D-xxx@vN → FR-xxx → task-xx → evidence 的追踪矩阵;缺失项必须列为风险(CLI 只校验每个 D-xxx@vN ID 字面出现在 verify-result.md,warning 不阻断;矩阵的 D→FR→task→evidence 映射完整性供人类追溯,CLI 不校验——是否真覆盖由你诚实判定)
|
|
134
139
|
|
|
135
140
|
### 输出
|
|
136
141
|
探针报告 + 设计一致性检查结果 + 模块文档一致性检查结果 + 决策追踪矩阵(如有)`,
|
|
@@ -212,7 +217,7 @@ PASS / PASS WITH NOTES / FAIL
|
|
|
212
217
|
- 测试覆盖:...
|
|
213
218
|
- 决策追踪覆盖:...
|
|
214
219
|
- API 契约对账:...
|
|
215
|
-
- 代码删除对账:声明修改/新增却被整文件删除 = 高风险;清单未列出的删除 = 未声明(CLI 会用 git diff --name-status HEAD
|
|
220
|
+
- 代码删除对账:声明修改/新增却被整文件删除 = 高风险;清单未列出的删除 = 未声明(CLI 会用 git diff --name-status HEAD advisory 复核,仅 warn 不阻断——是否 FAIL blocker 由你诚实判定)
|
|
216
221
|
|
|
217
222
|
## 决策追踪矩阵(如存在 decisions.md)
|
|
218
223
|
| 决策 ID | FR | Task | Evidence | 状态 |
|
package/src/sync.js
CHANGED
|
@@ -221,7 +221,7 @@ export class SyncManager {
|
|
|
221
221
|
try {
|
|
222
222
|
const { ProgressManager } = await import('./progress.js');
|
|
223
223
|
const pm = new ProgressManager({ specDir: safePlatformSpecDir(this.cwd) });
|
|
224
|
-
progressData =
|
|
224
|
+
progressData = pm.read(this.cwd, changeName);
|
|
225
225
|
} catch (err) {
|
|
226
226
|
console.warn(`[sync] 读取 progress 失败 (${changeName}): ${err.message}`);
|
|
227
227
|
return { synced: 0, errors: [`读取 progress 失败: ${err.message}`] };
|
|
@@ -246,7 +246,7 @@ export class SyncManager {
|
|
|
246
246
|
try {
|
|
247
247
|
const { ProgressManager } = await import('./progress.js');
|
|
248
248
|
const pm = new ProgressManager({ specDir: safePlatformSpecDir(this.cwd) });
|
|
249
|
-
|
|
249
|
+
pm._updatePlatformLastSync(this.cwd, changeName);
|
|
250
250
|
} catch (err) {
|
|
251
251
|
console.warn(`[sync] 更新 platform_last_sync 失败: ${err.message}`);
|
|
252
252
|
}
|
|
@@ -347,7 +347,7 @@ export class SyncManager {
|
|
|
347
347
|
try {
|
|
348
348
|
const { ProgressManager } = await import('./progress.js');
|
|
349
349
|
const pm = new ProgressManager({ specDir: safePlatformSpecDir(this.cwd) });
|
|
350
|
-
|
|
350
|
+
pm._updateApprovalStatus(this.cwd, changeName, result.status, result.reason);
|
|
351
351
|
} catch (err) {
|
|
352
352
|
console.warn(`[sync] 更新本地审批状态失败: ${err.message}`);
|
|
353
353
|
}
|
|
@@ -477,7 +477,7 @@ async function _submitApproval(cwd, changeName, decision, reason = null) {
|
|
|
477
477
|
try {
|
|
478
478
|
const { ProgressManager } = await import('./progress.js');
|
|
479
479
|
const pm = new ProgressManager({ specDir: safePlatformSpecDir(cwd) });
|
|
480
|
-
|
|
480
|
+
pm._updateApprovalStatus(cwd, changeName, decision, reason);
|
|
481
481
|
} catch (err) {
|
|
482
482
|
console.warn(`[sync] 更新本地审批状态失败 (${changeName}): ${err.message}`);
|
|
483
483
|
}
|
package/src/task-review.js
CHANGED
|
@@ -15,6 +15,7 @@ import { pathMatches } from './change-list.js'
|
|
|
15
15
|
import { parseAllowedPaths } from './stages/plan-postcheck.js'
|
|
16
16
|
import { resolveVerifyChangedFiles } from './verify-postcheck.js'
|
|
17
17
|
import { WorktreeManager } from './worktree.js'
|
|
18
|
+
import { resolveRuntimeRoot } from './run/shared.js'
|
|
18
19
|
|
|
19
20
|
// ── review.json schema version ──
|
|
20
21
|
export const REVIEW_SCHEMA_VERSION = 1
|
|
@@ -599,6 +600,34 @@ export function generateExecuteRunId() {
|
|
|
599
600
|
return `exec-${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`
|
|
600
601
|
}
|
|
601
602
|
|
|
603
|
+
/**
|
|
604
|
+
* 解析最新 execute run id:优先 current-execute-run-id-<changeName> marker;
|
|
605
|
+
* marker 缺失/为空则扫描 execute-runs/ 下子目录、取 mtime 最新的(尽力定位已有 run)。
|
|
606
|
+
*
|
|
607
|
+
* 与 getLatestStageReviewRunId 的目录扫描兜底同语义——避免 marker 丢失(cleanup/并行/手删)
|
|
608
|
+
* 而 agent 已用旧 runId 落了 execute-runs/<旧ID>/ 时,调用方直接 generate 新 ID 找不到旧 review、
|
|
609
|
+
* 误判缺 review.json(gates.js execute 完成门坑)。返回 null 表示既无 marker 也无既有 run 目录。
|
|
610
|
+
*
|
|
611
|
+
* @param {{ runtimeRoot: string, changeName: string }} opts
|
|
612
|
+
* @returns {string|null} runId(exec- 前缀),无则 null
|
|
613
|
+
*/
|
|
614
|
+
export function resolveLatestExecuteRunId({ runtimeRoot, changeName }) {
|
|
615
|
+
const marker = join(runtimeRoot, `current-execute-run-id-${changeName}`)
|
|
616
|
+
try { if (existsSync(marker)) { const c = readFileSync(marker, 'utf8').trim(); if (c) return c } } catch {}
|
|
617
|
+
try {
|
|
618
|
+
const runsDir = join(runtimeRoot, 'execute-runs')
|
|
619
|
+
if (existsSync(runsDir)) {
|
|
620
|
+
const entries = readdirSync(runsDir)
|
|
621
|
+
.map(e => ({ e, p: join(runsDir, e) }))
|
|
622
|
+
.filter(x => { try { return statSync(x.p).isDirectory() } catch { return false } })
|
|
623
|
+
.map(x => ({ e: x.e, mtime: statSync(x.p).mtimeMs }))
|
|
624
|
+
.sort((a, b) => b.mtime - a.mtime)
|
|
625
|
+
if (entries.length > 0) return entries[0].e
|
|
626
|
+
}
|
|
627
|
+
} catch {}
|
|
628
|
+
return null
|
|
629
|
+
}
|
|
630
|
+
|
|
602
631
|
/**
|
|
603
632
|
* worktree execute「主 agent 直接实现」模式收尾兜底:per-task review.json 缺失时,
|
|
604
633
|
* 据 git diff base..head 按 task allowed_paths 归属,自动落盘 cannot_verify 草稿。
|
|
@@ -628,7 +657,7 @@ export function generateExecuteRunId() {
|
|
|
628
657
|
*/
|
|
629
658
|
export async function generateTaskReviewDrafts({ changeName, cwd, platformOpts = {} }) {
|
|
630
659
|
const specBase = platformOpts.specRoot || join(cwd, '.sillyspec')
|
|
631
|
-
const runtimeRoot = platformOpts
|
|
660
|
+
const runtimeRoot = resolveRuntimeRoot(platformOpts, specBase)
|
|
632
661
|
const changeDir = join(specBase, 'changes', changeName)
|
|
633
662
|
const tasksDir = join(changeDir, 'tasks')
|
|
634
663
|
|