sillyspec 3.22.8 → 3.22.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sillyspec",
3
- "version": "3.22.8",
3
+ "version": "3.22.9",
4
4
  "description": "SillySpec CLI — 流程状态机,让 AI 严格按步骤来",
5
5
  "icon": "logo.jpg",
6
6
  "homepage": "https://sillyspec.ppdmq.top/",
package/src/run.js CHANGED
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { basename, join, resolve, dirname, relative, isAbsolute } from 'path'
8
8
  import { existsSync, readdirSync, mkdirSync, writeFileSync, appendFileSync, readFileSync, rmSync, statSync } from 'fs'
9
+ import { randomBytes } from 'crypto'
9
10
  import { createRequire } from 'module'
10
11
  const require = createRequire(import.meta.url)
11
12
  import { ProgressManager } from './progress.js'
@@ -776,6 +777,16 @@ async function outputStep(stageName, stepIndex, steps, cwd, changeName, dbProjec
776
777
  platformDirectives.push(`workspace_id: ${platformOpts.workspaceId}`)
777
778
  }
778
779
  promptText = platformDirectives.join('\n') + '\n\n' + promptText
780
+ } else {
781
+ // 常规模式(无平台 specRoot):占位符替换为 cwd/.sillyspec 下对应路径
782
+ // 让用 {SPEC_ROOT}/{DOCS_ROOT} 等占位符的 prompt(如 quick/scan)在常规模式也写到正确位置
783
+ const projectName = dbProjectName || basename(cwd)
784
+ const specSillyspec = join(cwd, '.sillyspec')
785
+ promptText = promptText.replace(/\{SPEC_ROOT\}/g, specSillyspec)
786
+ promptText = promptText.replace(/\{DOCS_ROOT\}/g, join(specSillyspec, 'docs', projectName))
787
+ promptText = promptText.replace(/\{PROJECTS_ROOT\}/g, join(specSillyspec, 'projects'))
788
+ promptText = promptText.replace(/\{WORKFLOWS_ROOT\}/g, join(specSillyspec, 'workflows'))
789
+ promptText = promptText.replace(/\{KNOWLEDGE_ROOT\}/g, join(specSillyspec, 'knowledge'))
779
790
  }
780
791
 
781
792
  // 注入 scanProfile 硬约束指令
@@ -1466,7 +1477,7 @@ export async function runCommand(args, cwd, specDir = null) {
1466
1477
  process.exit(1)
1467
1478
  }
1468
1479
  const date = new Date().toISOString().slice(0, 10)
1469
- const autoName = `${date}-new-change`
1480
+ const autoName = `${date}-new-change-${randomBytes(4).toString('hex')}`
1470
1481
  console.log(`🔄 自动创建变更:${autoName}`)
1471
1482
  console.log(` 提示:可以用 --change <名称> 指定自定义变更名`)
1472
1483
  console.log(` 或事后重命名:sillyspec change-rename ${autoName} <新名称>`)
@@ -2355,6 +2366,48 @@ async function continueStep(pm, progress, stageName, cwd, answer, options = {})
2355
2366
  console.warn(`🔗 Worktree: check failed — ${e.message}`);
2356
2367
  }
2357
2368
  }
2369
+ // 阶段完成后明确下一步(agent 常卡:stageData completed 但不知要 run <下一阶段> 推进 currentStage)
2370
+ const nextStageHint = { brainstorm: 'plan', plan: 'execute', execute: 'verify', verify: 'archive' }[stageName]
2371
+ if (nextStageHint) {
2372
+ console.log(`\n👉 ${stageName} 已完成。下一步:sillyspec run ${nextStageHint}${changeName ? ` --change ${changeName}` : ''}`)
2373
+ if (stageName === 'execute') {
2374
+ console.log(` ⚠️ 若 worktree 改动还没 apply 到主工作区,先:sillyspec worktree apply ${changeName}`)
2375
+ console.log(` (apply 不需要先 commit,支持 working tree 未提交改动)`)
2376
+ // plan.md checkbox auto-check:execute 完成 + review.json pass → 自动勾选(治本,比警告可靠)
2377
+ try {
2378
+ const specBaseLc = platformOpts.specRoot || join(cwd, '.sillyspec')
2379
+ const changeDir = join(specBaseLc, 'changes', changeName)
2380
+ const planPath = join(changeDir, 'plan.md')
2381
+ const runtimeRoot = platformOpts.runtimeRoot || join(specBaseLc, '.runtime')
2382
+ const runIdFile = join(runtimeRoot, `current-execute-run-id-${changeName}`)
2383
+ if (existsSync(planPath) && existsSync(runIdFile)) {
2384
+ const executeRunId = readFileSync(runIdFile, 'utf8').trim()
2385
+ const planContent = readFileSync(planPath, 'utf8')
2386
+ const { readReview } = await import('./task-review.js')
2387
+ let checkedCount = 0
2388
+ let skippedCount = 0
2389
+ const updated = planContent.replace(/^(\s*[-*]\s*\[)\s(\]\s*task-\d+)/gim, (match, p1, p2) => {
2390
+ const taskNum = match.match(/task-(\d+)/)[1].padStart(2, '0')
2391
+ const reviewPath = join(runtimeRoot, 'execute-runs', executeRunId, 'tasks', `task-${taskNum}`, 'review.json')
2392
+ const r = readReview(reviewPath)
2393
+ if (r.ok && r.review?.specVerdict !== 'fail' && r.review?.qualityVerdict !== 'fail') {
2394
+ checkedCount++
2395
+ return `${p1}x${p2}` // 勾选
2396
+ }
2397
+ skippedCount++
2398
+ return match // 不勾
2399
+ })
2400
+ if (checkedCount > 0) {
2401
+ writeFileSync(planPath, updated)
2402
+ console.log(` ✅ 自动勾选 ${checkedCount} 个 task checkbox(基于 review.json pass)`)
2403
+ }
2404
+ if (skippedCount > 0) {
2405
+ console.warn(` ⚠️ ${skippedCount} 个 task 未勾(review.json 缺失/fail)→ archive 会拦。补 review 后重跑 execute --done 触发自动勾`)
2406
+ }
2407
+ }
2408
+ } catch {}
2409
+ }
2410
+ }
2358
2411
  return { stageCompleted: true, currentIdx, nextPendingIdx: -1 }
2359
2412
  }
2360
2413
 
@@ -22,7 +22,7 @@ export const definition = {
22
22
  ### 操作
23
23
  1. 运行 \`sillyspec progress show\`,确认 currentStage 为 "brainstorm"
24
24
  2. 如果未初始化,提示先运行 sillyspec init
25
- 3. **检查变更名称**:如果当前变更名是自动生成的(如 \`2026-06-02-new-change\`),直接重命名为有意义名称,然后运行 \`sillyspec change-rename <旧名> <新名>\`
25
+ 3. **检查变更名称**:如果当前变更名是自动生成的(如 \`2026-06-02-new-change-a3f2b7c1\`),直接重命名为有意义名称,然后运行 \`sillyspec change-rename <旧名> <新名>\`
26
26
 
27
27
  ### 加载上下文
28
28
  4. 读取 CODEBASE-OVERVIEW.md + 共享规范 + 子项目上下文
@@ -12,7 +12,7 @@ export const definition = {
12
12
  2. 确认 currentStage 为 "brainstorm"
13
13
  3. 如果有进行中的 brainstorm,提示选择继续或重新开始
14
14
  4. 如果未初始化,提示先运行 sillyspec init
15
- 5. **检查变更名称是否有意义**:如果当前变更名是自动生成的(如 \`2026-06-02-new-change\`),询问用户确认实际变更名,然后运行 \`sillyspec change-rename <旧名> <新名>\` 重命名
15
+ 5. **检查变更名称是否有意义**:如果当前变更名是自动生成的(如 \`2026-06-02-new-change-a3f2b7c1\`),询问用户确认实际变更名,然后运行 \`sillyspec change-rename <旧名> <新名>\` 重命名
16
16
 
17
17
  ### 输出
18
18
  当前状态摘要(1-2 句话)
@@ -11,23 +11,23 @@ export const definition = {
11
11
  ### 操作
12
12
  1. 检查关联变更(\`<linked-changes>\`,逗号分隔的变更名列表;显示「(无)」= 不关联变更),确定记录方式
13
13
  2. 理解任务:模糊则问一个问题确认
14
- 3. 加载项目信息:\`cat .sillyspec/projects/*.yaml 2>/dev/null\`(了解项目结构和技术栈)
15
- 4. 加载上下文:\`cat .sillyspec/docs/<project>/scan/CONVENTIONS.md 2>/dev/null\`
16
- 5. 加载本地配置:\`cat .sillyspec/local.yaml 2>/dev/null\`(构建命令、测试命令、环境变量等)
17
- 6. 若有关联变更,加载每个变更的设计文档:\`cat .sillyspec/changes/<c>/design.md 2>/dev/null\`(理解设计意图)
18
- 7. 如有需要,查询知识库:\`cat .sillyspec/knowledge/INDEX.md 2>/dev/null\`
14
+ 3. 加载项目信息:\`cat {SPEC_ROOT}/projects/*.yaml 2>/dev/null\`(了解项目结构和技术栈)
15
+ 4. 加载上下文:\`cat {SPEC_ROOT}/docs/<project>/scan/CONVENTIONS.md 2>/dev/null\`
16
+ 5. 加载本地配置:\`cat {SPEC_ROOT}/local.yaml 2>/dev/null\`(构建命令、测试命令、环境变量等)
17
+ 6. 若有关联变更,加载每个变更的设计文档:\`cat {SPEC_ROOT}/changes/<c>/design.md 2>/dev/null\`(理解设计意图)
18
+ 7. 如有需要,查询知识库:\`cat {SPEC_ROOT}/knowledge/INDEX.md 2>/dev/null\`
19
19
 
20
20
  ### 模块文档加载
21
- 8. 读取 \`.sillyspec/docs/<project>/modules/_module-map.yaml\`(不存在则跳过以下步骤)
21
+ 8. 读取 \`{SPEC_ROOT}/docs/<project>/modules/_module-map.yaml\`(不存在则跳过以下步骤)
22
22
  9. 根据任务描述初步判断可能涉及的模块
23
- 10. 读取匹配到的 \`.sillyspec/docs/<project>/modules/<module>.md\`
23
+ 10. 读取匹配到的 \`{SPEC_ROOT}/docs/<project>/modules/<module>.md\`
24
24
 
25
25
  ### 创建任务记录(⛔ 此步骤不能跳过,没有 quicklog 记录 = 未完成)
26
26
  理解完任务后,**必须**立即创建记录文件,再输出任何其他内容:
27
27
 
28
28
  **A. 始终创建 QUICKLOG(无论是否关联变更)**
29
29
  1. 使用预注入的 git 用户名:\`<git-user>\`
30
- 2. 创建/追加 \`.sillyspec/quicklog/QUICKLOG-\`<git-user>\`.md\`,写入:
30
+ 2. 创建/追加 \`{SPEC_ROOT}/quicklog/QUICKLOG-\`<git-user>\`.md\`,写入:
31
31
  \`\`\`
32
32
  ## ql-<YYYYMMDD>-<NNN>-<XXXX> | <now-datetime> | <一句话任务描述>
33
33
  状态:进行中
@@ -42,13 +42,13 @@ export const definition = {
42
42
 
43
43
  **B. 若关联变更(\`<linked-changes>\` 不为「(无)」)**
44
44
  3. 解析 \`<linked-changes>\`(逗号分隔的变更名列表,可能有多个 = 一次 quick 同时归属多个变更)
45
- 4. 对**每个**变更 \`<c>\`:在 \`.sillyspec/changes/<c>/tasks.md\` 追加一条未勾选 task:
45
+ 4. 对**每个**变更 \`<c>\`:在 \`{SPEC_ROOT}/changes/<c>/tasks.md\` 追加一条未勾选 task:
46
46
  \`- [ ] <ql-ID> <一句话任务描述>\`(复用步骤 A 生成的同一个 ql-ID)
47
- - 若 \`changes/<c>/\` 目录或 tasks.md 不存在,先 \`mkdir -p .sillyspec/changes/<c>\` 再创建 tasks.md
47
+ - 若 \`changes/<c>/\` 目录或 tasks.md 不存在,先 \`mkdir -p {SPEC_ROOT}/changes/<c>\` 再创建 tasks.md
48
48
  - 同一个 ql-ID 出现在 QUICKLOG 和所有关联变更的 tasks.md 中,便于交叉引用
49
49
  - \`<linked-changes>\` 为「(无)」时跳过本步,仅保留 QUICKLOG
50
50
 
51
- 这样 Gate 检测到 \`.sillyspec/\` 下有变更,就不会拦截后续的代码修改。
51
+ 这样 Gate 检测到 \`{SPEC_ROOT}/\` 下有变更,就不会拦截后续的代码修改。
52
52
 
53
53
  ### 输出
54
54
  quicklog 已创建(必须放在输出的第一行确认)+ 任务理解 + 上下文摘要
@@ -90,14 +90,14 @@ quicklog 已创建(必须放在输出的第一行确认)+ 任务理解 + 上
90
90
  - QUICKLOG:找到对应 ql-ID 的条目,将「状态:进行中」改为「状态:已完成」,补充实际改动文件和结果摘要(**始终做**,无论是否关联变更)
91
91
  - 关联变更:对 Step 1 在每个 \`changes/<c>/tasks.md\` 写入的 ql-ID task,将其 checkbox 由 \`- [ ]\` 勾选为 \`- [x]\`;若 Step 1 未写入(用户事后才决定关联),则按 Step 1 的格式补写并直接勾选
92
92
  4. QUICKLOG 轮转:超过 500 行则重命名为 \`QUICKLOG-<USER>-YYYY-MM-DD.md\`(日期取最后一条记录的日期)。新文件从空开始,ql-ID 需扫描同目录所有 QUICKLOG 文件中当天最大序号 +1
93
- 5. 如果发现项目特有的坑,追加到 \`.sillyspec/knowledge/uncategorized.md\`
93
+ 5. 如果发现项目特有的坑,追加到 \`{SPEC_ROOT}/knowledge/uncategorized.md\`
94
94
  6. 任务比预期复杂 → 建议用完整流程
95
95
 
96
96
  ### 模块文档同步
97
- 7. 读取 \`.sillyspec/docs/<project>/modules/_module-map.yaml\`(不存在则跳过以下步骤)
97
+ 7. 读取 \`{SPEC_ROOT}/docs/<project>/modules/_module-map.yaml\`(不存在则跳过以下步骤)
98
98
  8. 对比本次修改的文件(\`git diff --name-only HEAD\`)与模块映射
99
99
  9. 如果命中模块 → 直接同步模块文档:
100
- - 读取对应的 \`.sillyspec/docs/<project>/modules/<module>.md\`(如不存在则新建)
100
+ - 读取对应的 \`{SPEC_ROOT}/docs/<project>/modules/<module>.md\`(如不存在则新建)
101
101
  - 根据本次改动内容更新模块文档(正文描述当前状态,底部变更索引追加本次 ql-ID)
102
102
  - 变更索引格式:\`- ql-YYYYMMDD-NNN-XXXX | <一句话描述>\`
103
103
  - 写入模块文档