sillyspec 3.24.5 → 3.24.6
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-archive/SKILL.md +86 -86
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +1 -1
- package/.claude/skills/sillyspec-doctor/SKILL.md +1 -1
- package/.claude/skills/sillyspec-execute/SKILL.md +91 -91
- package/.claude/skills/sillyspec-explore/SKILL.md +96 -96
- package/.claude/skills/sillyspec-plan/SKILL.md +1 -1
- package/.claude/skills/sillyspec-propose/SKILL.md +1 -1
- package/.claude/skills/sillyspec-quick/SKILL.md +1 -1
- package/.claude/skills/sillyspec-scan/SKILL.md +74 -74
- package/.claude/skills/sillyspec-status/SKILL.md +55 -55
- package/.claude/skills/sillyspec-verify/SKILL.md +82 -82
- package/package.json +1 -1
- package/src/db.js +108 -11
- package/src/hooks/worktree-guard.js +37 -26
- package/src/run.js +27 -15
- package/src/stages/doctor.js +4 -1
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
|
|
12
12
|
import { existsSync, readFileSync, readdirSync } from 'fs'
|
|
13
13
|
import path from 'path'
|
|
14
|
+
import { execFileSync } from 'child_process'
|
|
15
|
+
import { createRequire } from 'module'
|
|
16
|
+
import { pathToFileURL } from 'url'
|
|
14
17
|
|
|
15
18
|
// ── 常量 ──
|
|
16
19
|
|
|
@@ -236,6 +239,35 @@ function readGateStatus(cwd) {
|
|
|
236
239
|
}
|
|
237
240
|
}
|
|
238
241
|
|
|
242
|
+
/**
|
|
243
|
+
* 同步只读查询 sillyspec.db 第一行第一列(不依赖外部 sqlite3 CLI)。
|
|
244
|
+
* sql.js 的 init 是 async,故用子进程跑(execFileSync 等待);sql.js 模块经 hook 进程的
|
|
245
|
+
* require.resolve 解析出绝对路径传入,避免子进程 cwd(项目目录)找不到 sql.js。
|
|
246
|
+
* 全平台一致(node 是 SillySpec 自身运行时)。返回字符串值或 null。
|
|
247
|
+
*/
|
|
248
|
+
function queryDbFirstCell(cwd, sql) {
|
|
249
|
+
const dbPath = path.join(cwd, '.sillyspec', '.runtime', 'sillyspec.db')
|
|
250
|
+
if (!existsSync(dbPath)) return null
|
|
251
|
+
let sqlJsPath
|
|
252
|
+
try { sqlJsPath = createRequire(import.meta.url).resolve('sql.js') } catch { return null }
|
|
253
|
+
// Windows 上 dynamic import 不接受裸 "C:\..." 路径,必须转 file:// URL(全平台合法)
|
|
254
|
+
const libUrl = pathToFileURL(sqlJsPath).href
|
|
255
|
+
const script =
|
|
256
|
+
"const fs=require('fs');" +
|
|
257
|
+
"const dbPath=" + JSON.stringify(dbPath) + ",sql=" + JSON.stringify(sql) + ",lib=" + JSON.stringify(libUrl) + ";" +
|
|
258
|
+
"if(!fs.existsSync(dbPath))process.exit(0);" +
|
|
259
|
+
"import(lib).then(async m=>{const SQL=await m.default();" +
|
|
260
|
+
"const db=new SQL.Database(fs.readFileSync(dbPath));const r=db.exec(sql);" +
|
|
261
|
+
"if(r.length&&r[0].values.length)process.stdout.write(String(r[0].values[0][0]??''));db.close()}).catch(()=>{});"
|
|
262
|
+
try {
|
|
263
|
+
return execFileSync(process.execPath, ['-e', script], {
|
|
264
|
+
encoding: 'utf8', timeout: 5000, stdio: ['ignore', 'pipe', 'pipe'],
|
|
265
|
+
}).trim() || null
|
|
266
|
+
} catch { return null }
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export { queryDbFirstCell as _queryDbFirstCellForTest };
|
|
270
|
+
|
|
239
271
|
/**
|
|
240
272
|
* 从 sillyspec.db 读取 currentStage
|
|
241
273
|
* 优先级:gate-status.json > sillyspec.db
|
|
@@ -247,19 +279,9 @@ function readCurrentStage(cwd) {
|
|
|
247
279
|
const gateStatus = readGateStatus(cwd)
|
|
248
280
|
if (gateStatus && gateStatus.stage) return gateStatus.stage
|
|
249
281
|
|
|
250
|
-
// 2. 从 sillyspec.db
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
try {
|
|
254
|
-
const { execSync } = require('child_process')
|
|
255
|
-
const result = execSync(
|
|
256
|
-
`sqlite3 "${dbPath}" "SELECT current_stage FROM changes WHERE status='active' AND current_stage IN ('execute','quick') ORDER BY last_active DESC LIMIT 1"`,
|
|
257
|
-
{ encoding: 'utf8', timeout: 2000 }
|
|
258
|
-
).trim()
|
|
259
|
-
return result || null
|
|
260
|
-
} catch { /* sqlite3 CLI 不可用或查询失败 */ }
|
|
261
|
-
|
|
262
|
-
return null
|
|
282
|
+
// 2. 从 sillyspec.db 读取(node + sql.js 子进程,不依赖外部 sqlite3 CLI——Windows 默认没有)
|
|
283
|
+
const v = queryDbFirstCell(cwd, "SELECT current_stage FROM changes WHERE status='active' AND current_stage IN ('execute','quick') ORDER BY last_active DESC LIMIT 1")
|
|
284
|
+
return v || null
|
|
263
285
|
}
|
|
264
286
|
|
|
265
287
|
/**
|
|
@@ -272,19 +294,8 @@ function isNoWorktreeMode(cwd) {
|
|
|
272
294
|
const gateStatus = readGateStatus(cwd)
|
|
273
295
|
if (gateStatus && gateStatus.noWorktree) return true
|
|
274
296
|
|
|
275
|
-
// 2. 从 sillyspec.db
|
|
276
|
-
|
|
277
|
-
if (!existsSync(dbPath)) return false
|
|
278
|
-
try {
|
|
279
|
-
const { execSync } = require('child_process')
|
|
280
|
-
const result = execSync(
|
|
281
|
-
`sqlite3 "${dbPath}" "SELECT no_worktree FROM changes WHERE status='active' AND current_stage IN ('execute','quick') LIMIT 1"`,
|
|
282
|
-
{ encoding: 'utf8', timeout: 2000 }
|
|
283
|
-
).trim()
|
|
284
|
-
return result === '1'
|
|
285
|
-
} catch { /* sqlite3 CLI 不可用或查询失败 */ }
|
|
286
|
-
|
|
287
|
-
return false
|
|
297
|
+
// 2. 从 sillyspec.db 读取(node + sql.js,同 readCurrentStage)
|
|
298
|
+
return queryDbFirstCell(cwd, "SELECT no_worktree FROM changes WHERE status='active' AND current_stage IN ('execute','quick') LIMIT 1") === '1'
|
|
288
299
|
}
|
|
289
300
|
|
|
290
301
|
/**
|
package/src/run.js
CHANGED
|
@@ -894,7 +894,12 @@ async function outputStep(stageName, stepIndex, steps, cwd, changeName, dbProjec
|
|
|
894
894
|
if (platformOpts.workspaceId) {
|
|
895
895
|
platformDirectives.push(`workspace_id: ${platformOpts.workspaceId}`)
|
|
896
896
|
}
|
|
897
|
-
|
|
897
|
+
// 平台 directives 仅 step0 前置(对齐 persona 仅 step0 策略):路径列表/Write 规则/
|
|
898
|
+
// 占位符映射一次建立即可;后续 step 靠 agent 跨步记忆 + 每步 changeDir header +
|
|
899
|
+
// footer 平台规则(下方 L1077+)维持写入约束,避免每步重复注入 ~500 tokens。
|
|
900
|
+
if (stepIndex === 0) {
|
|
901
|
+
promptText = platformDirectives.join('\n') + '\n\n' + promptText
|
|
902
|
+
}
|
|
898
903
|
} else {
|
|
899
904
|
// 常规模式(无平台 specRoot):占位符替换为 cwd/.sillyspec 下对应路径
|
|
900
905
|
// 让用 {SPEC_ROOT}/{DOCS_ROOT} 等占位符的 prompt(如 quick/scan)在常规模式也写到正确位置
|
|
@@ -1063,7 +1068,7 @@ async function outputStep(stageName, stepIndex, steps, cwd, changeName, dbProjec
|
|
|
1063
1068
|
|
|
1064
1069
|
console.log(promptText)
|
|
1065
1070
|
console.log(`\n### ⚠️ 铁律`)
|
|
1066
|
-
console.log('-
|
|
1071
|
+
console.log('- 文档优先:代码产出必须先有对应的设计/规范文档支撑。')
|
|
1067
1072
|
console.log('- 只做本步骤描述的操作,不得自行扩展或跳过')
|
|
1068
1073
|
console.log('- 不要回头修改已完成的步骤')
|
|
1069
1074
|
console.log('- 不要编造不存在的 CLI 子命令')
|
|
@@ -1347,7 +1352,10 @@ export async function runCommand(args, cwd, specDir = null) {
|
|
|
1347
1352
|
// ── cwd 纠正:向上查找真实项目根 ──
|
|
1348
1353
|
// 防止多 project 工作区中 cwd 停在子目录(如 backend/)时
|
|
1349
1354
|
// 状态写入子目录下误建的 .sillyspec,导致状态分裂
|
|
1350
|
-
if (!specDir) {
|
|
1355
|
+
if (!specDir && !existsSync(join(cwd, '.sillyspec-platform.json'))) {
|
|
1356
|
+
// 平台模式(cwd 有 .sillyspec-platform.json 指针)不做 cwd 纠正:指针已明确 specDir,
|
|
1357
|
+
// 向上找 .sillyspec 反而会撞到无关项目(如用户 home 的 .sillyspec),导致 --done 恢复
|
|
1358
|
+
// 读写 ~/.sillyspec-platform.json 出错的 specRoot。
|
|
1351
1359
|
const resolvedRoot = resolveSpecDir(cwd)
|
|
1352
1360
|
if (resolvedRoot && resolvedRoot !== join(cwd, '.sillyspec')) {
|
|
1353
1361
|
const realRoot = dirname(resolvedRoot)
|
|
@@ -2121,18 +2129,22 @@ async function runStage(pm, progress, stageName, cwd, changeName, skipApproval =
|
|
|
2121
2129
|
}
|
|
2122
2130
|
|
|
2123
2131
|
if (currentIdx === -1) {
|
|
2124
|
-
//
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2132
|
+
// 所有步骤已 completed/skipped,但阶段未盖到 completed —— 这是上次最后一步完成后、
|
|
2133
|
+
// stage 升级事务未提交的崩溃中间态(正常完成会被上方 stageStatus==='completed' 守卫拦下)。
|
|
2134
|
+
// 旧逻辑无条件清空 steps 为 pending 会丢掉已完成的进度且不可恢复。改为走正式
|
|
2135
|
+
// completeStage:产物齐则补盖完成戳,不齐则给出 actionable 提示,永不静默清空步骤。
|
|
2136
|
+
console.log(`\nℹ️ ${stageName} 所有步骤已完成,但阶段未标记完成(上次可能中断)。尝试补盖完成戳…`)
|
|
2137
|
+
await pm.completeStage(cwd, stageName, changeName)
|
|
2138
|
+
const after = await pm.read(cwd, changeName)
|
|
2139
|
+
if (after?.stages?.[stageName]?.status === 'completed') {
|
|
2140
|
+
console.log(` ✅ 已补盖完成戳。下一步: sillyspec run <下一阶段>,或 sillyspec run ${stageName} --status 查看。`)
|
|
2141
|
+
process.exit(0)
|
|
2142
|
+
}
|
|
2143
|
+
// completeStage 已打印产物校验失败的明细;这里只补充恢复指引
|
|
2144
|
+
console.error(`\n ⚠️ 已保留步骤进度(未清空)。修复产物后重跑,或:`)
|
|
2145
|
+
console.error(` - 重新开始: sillyspec run ${stageName} --reset`)
|
|
2146
|
+
console.error(` - 强制补盖: sillyspec progress complete-stage ${stageName}${changeName ? ` --change ${changeName}` : ''} --force`)
|
|
2147
|
+
process.exit(1)
|
|
2136
2148
|
}
|
|
2137
2149
|
|
|
2138
2150
|
// quick 阶段:记录 baselineFiles + 分配 ql-ID(CLI 接管 QUICKLOG 写入)
|
package/src/stages/doctor.js
CHANGED
|
@@ -12,6 +12,8 @@ export const definition = {
|
|
|
12
12
|
name: 'SillySpec 内部检查',
|
|
13
13
|
prompt: `运行 SillySpec 内部检查。逐项执行以下命令并汇总结果:
|
|
14
14
|
|
|
15
|
+
> ⚠️ Windows 兼容:下列 sqlite3 查询依赖外部 sqlite3 CLI(Windows 默认没有)。若某条 sqlite3 失败,改用 sillyspec 自身命令替代——sillyspec progress show 查看当前变更与各阶段状态(等价于查 changes/stages 表),sillyspec doctor --json 查看 db 健康与活跃变更列表。
|
|
16
|
+
|
|
15
17
|
### 1. 目录结构完整性
|
|
16
18
|
\`\`\`bash
|
|
17
19
|
# 检查 .sillyspec/ 及子目录
|
|
@@ -21,7 +23,8 @@ done
|
|
|
21
23
|
# 检查 sillyspec.db(SQLite 权威状态源)
|
|
22
24
|
DB_FILE='.sillyspec/.runtime/sillyspec.db'
|
|
23
25
|
[ -f "$DB_FILE" ] && echo "✅ sillyspec.db 存在" || echo "❌ sillyspec.db 不存在"
|
|
24
|
-
|
|
26
|
+
# 用 sillyspec 自身命令验证 db 可读(fallback 不依赖外部 sqlite3 CLI,兼容 Windows)
|
|
27
|
+
sqlite3 "$DB_FILE" "SELECT count(*) FROM project" 2>/dev/null && echo "✅ sillyspec.db 可查询" || (sillyspec progress show >/dev/null 2>&1 && echo "✅ sillyspec.db 可查询(经 sillyspec 命令)" || echo "⚠️ sillyspec.db 不可查询")
|
|
25
28
|
\`\`\`
|
|
26
29
|
|
|
27
30
|
### 2. 项目配置检查
|