sillyspec 3.22.7 → 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 +1 -1
- package/src/run.js +73 -1
- package/src/stages/brainstorm-auto.js +1 -1
- package/src/stages/brainstorm.js +1 -1
- package/src/stages/quick.js +14 -14
package/package.json
CHANGED
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} <新名称>`)
|
|
@@ -1571,6 +1582,25 @@ export async function runCommand(args, cwd, specDir = null) {
|
|
|
1571
1582
|
}
|
|
1572
1583
|
}
|
|
1573
1584
|
|
|
1585
|
+
// quick 启动(非 --done):reset steps + 生成 quickRunId,避免多会话共享 progress.default.quick
|
|
1586
|
+
// 继承并行会话的 step1(会话 B 复用会话 A 的 ql,本会话改动无 ql 记录)。C-实用方案:
|
|
1587
|
+
// 每次 quick 启动 reset steps(逻辑隔离,本会话从 step1 重跑建独立 ql)+ 写 current-quick-run-id。
|
|
1588
|
+
if (stageName === 'quick' && !isDone && !isStatus && !isSkip && !isReset && !isReopen) {
|
|
1589
|
+
const qStage = progress.stages?.quick
|
|
1590
|
+
if (qStage?.steps?.length) {
|
|
1591
|
+
qStage.steps = qStage.steps.map(s => ({ ...s, status: 'pending', completedAt: null }))
|
|
1592
|
+
qStage.status = 'in-progress'
|
|
1593
|
+
}
|
|
1594
|
+
try {
|
|
1595
|
+
const now = new Date()
|
|
1596
|
+
const pad = (n) => String(n).padStart(2, '0')
|
|
1597
|
+
const quickRunId = `quick-${now.getFullYear()}-${pad(now.getMonth()+1)}-${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`
|
|
1598
|
+
const runtimeRoot = platformOpts.runtimeRoot || join(specRoot, '.runtime')
|
|
1599
|
+
mkdirSync(runtimeRoot, { recursive: true })
|
|
1600
|
+
writeFileSync(join(runtimeRoot, 'current-quick-run-id'), quickRunId)
|
|
1601
|
+
} catch {}
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1574
1604
|
// 确保步骤已初始化
|
|
1575
1605
|
const changed = await ensureStageSteps(progress, stageName, cwd, specRoot)
|
|
1576
1606
|
if (changed && effectiveChange) {
|
|
@@ -2336,6 +2366,48 @@ async function continueStep(pm, progress, stageName, cwd, answer, options = {})
|
|
|
2336
2366
|
console.warn(`🔗 Worktree: check failed — ${e.message}`);
|
|
2337
2367
|
}
|
|
2338
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
|
+
}
|
|
2339
2411
|
return { stageCompleted: true, currentIdx, nextPendingIdx: -1 }
|
|
2340
2412
|
}
|
|
2341
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 + 共享规范 + 子项目上下文
|
package/src/stages/brainstorm.js
CHANGED
|
@@ -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 句话)
|
package/src/stages/quick.js
CHANGED
|
@@ -11,23 +11,23 @@ export const definition = {
|
|
|
11
11
|
### 操作
|
|
12
12
|
1. 检查关联变更(\`<linked-changes>\`,逗号分隔的变更名列表;显示「(无)」= 不关联变更),确定记录方式
|
|
13
13
|
2. 理解任务:模糊则问一个问题确认
|
|
14
|
-
3. 加载项目信息:\`cat
|
|
15
|
-
4. 加载上下文:\`cat
|
|
16
|
-
5. 加载本地配置:\`cat
|
|
17
|
-
6. 若有关联变更,加载每个变更的设计文档:\`cat
|
|
18
|
-
7. 如有需要,查询知识库:\`cat
|
|
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. 读取
|
|
21
|
+
8. 读取 \`{SPEC_ROOT}/docs/<project>/modules/_module-map.yaml\`(不存在则跳过以下步骤)
|
|
22
22
|
9. 根据任务描述初步判断可能涉及的模块
|
|
23
|
-
10. 读取匹配到的
|
|
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. 创建/追加
|
|
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>\`:在
|
|
45
|
+
4. 对**每个**变更 \`<c>\`:在 \`{SPEC_ROOT}/changes/<c>/tasks.md\` 追加一条未勾选 task:
|
|
46
46
|
\`- [ ] <ql-ID> <一句话任务描述>\`(复用步骤 A 生成的同一个 ql-ID)
|
|
47
|
-
- 若 \`changes/<c>/\` 目录或 tasks.md 不存在,先 \`mkdir -p
|
|
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 检测到
|
|
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. 如果发现项目特有的坑,追加到
|
|
93
|
+
5. 如果发现项目特有的坑,追加到 \`{SPEC_ROOT}/knowledge/uncategorized.md\`
|
|
94
94
|
6. 任务比预期复杂 → 建议用完整流程
|
|
95
95
|
|
|
96
96
|
### 模块文档同步
|
|
97
|
-
7. 读取
|
|
97
|
+
7. 读取 \`{SPEC_ROOT}/docs/<project>/modules/_module-map.yaml\`(不存在则跳过以下步骤)
|
|
98
98
|
8. 对比本次修改的文件(\`git diff --name-only HEAD\`)与模块映射
|
|
99
99
|
9. 如果命中模块 → 直接同步模块文档:
|
|
100
|
-
- 读取对应的
|
|
100
|
+
- 读取对应的 \`{SPEC_ROOT}/docs/<project>/modules/<module>.md\`(如不存在则新建)
|
|
101
101
|
- 根据本次改动内容更新模块文档(正文描述当前状态,底部变更索引追加本次 ql-ID)
|
|
102
102
|
- 变更索引格式:\`- ql-YYYYMMDD-NNN-XXXX | <一句话描述>\`
|
|
103
103
|
- 写入模块文档
|