sillyspec 3.26.2 → 3.26.4
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-execute/SKILL.md +38 -0
- package/.claude/skills/sillyspec-plan/SKILL.md +48 -0
- package/.claude/skills/sillyspec-quick/SKILL.md +13 -1
- package/package.json +1 -1
- package/src/db.js +11 -0
- package/src/dispatch/backends/local-agent.js +3 -0
- package/src/dispatch/backends/sillyhub-mcp.js +3 -0
- package/src/index.js +55 -3
- package/src/machine-interface.js +11 -4
- package/src/run/command.js +9 -0
- package/src/run/complete.js +27 -4
- package/src/run/gates.js +26 -14
- package/src/run/multi-repo-context.js +206 -0
- package/src/run/prompt.js +6 -0
- package/src/run/shared.js +170 -2
- package/src/stage-review.js +1 -0
- package/src/stages/brainstorm.js +22 -20
- package/src/stages/execute.js +188 -7
- package/src/stages/plan-postcheck.js +266 -11
- package/src/stages/quick.js +3 -1
- package/src/stages/verify.js +1 -1
- package/src/sync.js +25 -2
- package/src/task-review.js +135 -25
- package/src/verify-postcheck.js +242 -11
- package/src/worktree-apply.js +137 -9
- package/templates/prompts/testcase-design.md +12 -0
- package/templates/prompts/verify-probes.md +1 -0
- package/templates/skills/sillyspec-onboard/SKILL.md +92 -0
- package/.sillyspec-platform-cleaned +0 -1
- package/.sillyspec-platform.json +0 -7
package/src/worktree-apply.js
CHANGED
|
@@ -18,8 +18,9 @@ import { join, resolve } from 'path';
|
|
|
18
18
|
import { tmpdir } from 'os';
|
|
19
19
|
import { WorktreeManager } from './worktree.js';
|
|
20
20
|
import { parseFileChangeList, parseFileChangeListDetailed, pathMatches } from './change-list.js';
|
|
21
|
-
import { parseAllowedPaths } from './stages/plan-postcheck.js';
|
|
21
|
+
import { parseAllowedPaths, parseRepo } from './stages/plan-postcheck.js';
|
|
22
22
|
import { git, gitQuiet } from './git-helper.js';
|
|
23
|
+
import { resolveLatestExecuteRunId, readReview } from './task-review.js';
|
|
23
24
|
|
|
24
25
|
const CHANGES_REL = '.sillyspec/changes';
|
|
25
26
|
|
|
@@ -191,36 +192,145 @@ function getBlobHashMap(cwd, treeish, files) {
|
|
|
191
192
|
* 两 gate 口径不一致)。union 后两源并集为准;plan 已过 validateDesignFileCoverage 单向校验(design ⊆
|
|
192
193
|
* plan),union 不会放开 design/plan 之外的任意文件(仍拦完全越界文件)。
|
|
193
194
|
*
|
|
195
|
+
* **跨仓切片(task-05 / D-009 / design §6 A4)**:返回 `Map<repoKey, Set<path>>`,按 task 卡片
|
|
196
|
+
* `repo:` 字段把 allowed_paths 归属到对应 repo;design §6 清单归属 'main'(清单文件相对主仓根)。
|
|
197
|
+
* 单仓 change(task 卡无 repo:)→ 所有 allowed_paths 归 'main' → `{main: Set}` 退化零回归
|
|
198
|
+
* (GOAL-2)。allowed_paths 基准=各 repo 自身根(跨仓 task 卡片 allowed_paths 相对跨仓仓根,design §7.2)。
|
|
199
|
+
*
|
|
200
|
+
* 主流程(applyWorktree A5 主仓 apply)只消费 main 仓的 Set(主仓 worktree diff 只含主仓文件,
|
|
201
|
+
* 跨仓文件不在主仓 worktree 故永不进 diff);跨仓 task 走 no-op(D-009),其 allowed_paths 仅
|
|
202
|
+
* 作 Map 切片返回,供上游(assess / 跨仓 review 校验)按 repo 取用。
|
|
203
|
+
*
|
|
194
204
|
* @param {string} projectRoot - 主仓库根
|
|
195
205
|
* @param {string} changeName - 变更名
|
|
196
|
-
* @returns {Set<string
|
|
206
|
+
* @returns {Map<string, Set<string>>} repoKey → 并集清单 Set(无 design 清单且无 task 卡片时为空 Map)
|
|
197
207
|
*/
|
|
198
208
|
export function resolveApplyAllowSet(projectRoot, changeName) {
|
|
199
|
-
const
|
|
209
|
+
const repoMap = new Map();
|
|
210
|
+
const getOrCreate = (key) => {
|
|
211
|
+
if (!repoMap.has(key)) repoMap.set(key, new Set());
|
|
212
|
+
return repoMap.get(key);
|
|
213
|
+
};
|
|
214
|
+
// design §6 清单归属 main(清单路径相对主仓根;跨仓 task 的 allowed_paths 由 task 卡片 repo 切片)
|
|
215
|
+
const mainSet = getOrCreate('main');
|
|
216
|
+
parseFileChangeList(
|
|
217
|
+
join(projectRoot, CHANGES_REL, changeName, 'design.md'),
|
|
218
|
+
{ keepSillyspecDocs: true }
|
|
219
|
+
).forEach(p => mainSet.add(p));
|
|
200
220
|
const tasksDir = join(projectRoot, CHANGES_REL, changeName, 'tasks');
|
|
201
221
|
if (existsSync(tasksDir)) {
|
|
202
222
|
for (const tf of readdirSync(tasksDir).filter(f => /^task-\d+\.md$/.test(f))) {
|
|
203
|
-
|
|
223
|
+
const content = readFileSync(join(tasksDir, tf), 'utf8');
|
|
224
|
+
// task 卡 repo: 字段切片(缺省='main',design §7.2);跨仓 task 的 allowed_paths 相对跨仓仓根。
|
|
225
|
+
const repoKey = parseRepo(content) || 'main';
|
|
226
|
+
const repoSet = getOrCreate(repoKey);
|
|
227
|
+
for (const p of parseAllowedPaths(content)) repoSet.add(p);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return repoMap;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* 跨仓 task apply = no-op 校验(task-05 / D-009 / design §6 A3 G1)。
|
|
235
|
+
*
|
|
236
|
+
* 跨仓 task 的 commit 已由子代理直接落跨仓仓主干(NG-3),apply 阶段无 patch 可打(跨仓仓无
|
|
237
|
+
* worktree/meta/分支——A5 patch 路径不可复用)。本函数对每个跨仓 repo 的 task review.head
|
|
238
|
+
* 校验是否为该跨仓仓真实 commit(约束①+② 保险,R-05:子代理漏 commit / head 伪造 → 这里拦)。
|
|
239
|
+
*
|
|
240
|
+
* 不调 wm.cleanup(跨仓仓无主仓 worktree 可清——wm.cleanup 只作用主仓 worktree,跨仓仓主干工作区
|
|
241
|
+
* 永不进主仓 worktree 生命周期,无需也无法 cleanup)。这是 D-009 no-op 的关键:跨仓 commit 已落主干,
|
|
242
|
+
* apply 只校验不复用 patch、不 cleanup。
|
|
243
|
+
*
|
|
244
|
+
* review.json 物理在主仓 execute-runs(D-003),含 repo + head 字段(design §7.4)。本函数读主仓
|
|
245
|
+
* execute-runs 取最新 runId 的 reviews,按 review.repo 切片到对应跨仓 entry 校验 head。
|
|
246
|
+
*
|
|
247
|
+
* 无 review.json(execute 未走完 / 单 task --done 中途)→ 记 warning 不阻断(apply 可在 review 落盘前
|
|
248
|
+
* 被调用,如 worktree apply --check-only 预检;真阻断由 Task Review Gate 负责,这里不重复)。
|
|
249
|
+
*
|
|
250
|
+
* @param {object} ctx - MultiRepoContext(design §7.1)
|
|
251
|
+
* @param {string} projectRoot - 主仓根(读 .sillyspec/.runtime/execute-runs)
|
|
252
|
+
* @param {string} changeName
|
|
253
|
+
* @returns {{ errors: string[], warnings: string[], validated: Array<{repo:string, task:string, head:string}> }}
|
|
254
|
+
*/
|
|
255
|
+
function validateCrossRepoNoOp(ctx, projectRoot, changeName) {
|
|
256
|
+
const errors = [];
|
|
257
|
+
const warnings = [];
|
|
258
|
+
const validated = [];
|
|
259
|
+
if (!ctx || typeof ctx.hasCrossRepo !== 'function' || !ctx.hasCrossRepo()) {
|
|
260
|
+
return { errors, warnings, validated }; // 单仓退化:无跨仓,no-op 无事可做(零回归)
|
|
261
|
+
}
|
|
262
|
+
// review.json 物理在主仓 .runtime/execute-runs(D-003)。runtimeRoot = <projectRoot>/.sillyspec/.runtime
|
|
263
|
+
const runtimeRoot = join(projectRoot, '.sillyspec', '.runtime');
|
|
264
|
+
const runId = resolveLatestExecuteRunId({ runtimeRoot, changeName });
|
|
265
|
+
if (!runId) {
|
|
266
|
+
// 无 execute run → 无 review 可校验。不阻断(Task Review Gate 负责拦截 review 缺失),
|
|
267
|
+
// 仅 warn 提示跨仓 head 校验被跳过(约束①保险在此场景未触发,但功能不崩)。
|
|
268
|
+
warnings.push(`跨仓 task no-op 校验跳过:未找到 execute runId(${runtimeRoot}),无法读取跨仓 review.head。跨仓 commit 校验交由 Task Review Gate 负责。`);
|
|
269
|
+
return { errors, warnings, validated };
|
|
270
|
+
}
|
|
271
|
+
const tasksDir = join(runtimeRoot, 'execute-runs', runId, 'tasks');
|
|
272
|
+
if (!existsSync(tasksDir)) {
|
|
273
|
+
warnings.push(`跨仓 task no-op 校验跳过:execute run ${runId} 无 tasks/ 目录。`);
|
|
274
|
+
return { errors, warnings, validated };
|
|
275
|
+
}
|
|
276
|
+
// 遍历 ctx.repos,只对跨仓 entry(isMain=false)校验。每 entry 读所有 review.json 取 repo 匹配的 head。
|
|
277
|
+
for (const [repoKey, entry] of ctx.repos) {
|
|
278
|
+
if (entry.isMain) continue; // 主仓走 A5,不在此 no-op
|
|
279
|
+
// 扫 tasks/ 下 review.json,取 repo === repoKey 的 head 校验
|
|
280
|
+
let taskEntries = [];
|
|
281
|
+
try { taskEntries = readdirSync(tasksDir).filter(f => /^task-\d+$/.test(f)); } catch { taskEntries = []; }
|
|
282
|
+
for (const taskId of taskEntries) {
|
|
283
|
+
const reviewPath = join(tasksDir, taskId, 'review.json');
|
|
284
|
+
if (!existsSync(reviewPath)) continue;
|
|
285
|
+
const r = readReview(reviewPath);
|
|
286
|
+
// readReview 走 validateReviewSchema(task-03 扩展 repo 字段后兼容);review 不完整则跳过(不在此拦)
|
|
287
|
+
if (!r.ok || !r.review) continue;
|
|
288
|
+
const reviewRepo = r.review.repo || 'main'; // schemaVersion=1 无 repo 视 main(design §9 兼容)
|
|
289
|
+
if (reviewRepo !== repoKey) continue;
|
|
290
|
+
const head = r.review.head;
|
|
291
|
+
if (!head) continue; // schema 校验已挡缺 head,防御性跳过
|
|
292
|
+
// 校验 head 是该跨仓仓真实 commit:git cat-file -e HEAD~0 / rev-parse 校验存在
|
|
293
|
+
// 用 cat-file -e <head> 校验 commit 存在(rev-parse 对缩写/不存在都非零退出)
|
|
294
|
+
const exists = gitQuiet(entry.gitDir, ['cat-file', '-e', head]);
|
|
295
|
+
if (exists === null) {
|
|
296
|
+
errors.push(
|
|
297
|
+
`跨仓 task no-op 校验失败:task ${taskId}(repo: ${repoKey})的 review.head ${head} ` +
|
|
298
|
+
`不是跨仓仓 ${entry.gitDir} 的真实 commit(git cat-file -e 失败)。` +
|
|
299
|
+
`跨仓 task 的 commit 必须由子代理直接落跨仓仓主干(D-009),apply 为 no-op 不复用 patch,` +
|
|
300
|
+
`故 head 真实性是跨仓改动落地的唯一保险(约束①+②,R-05)。请检查子代理是否漏 commit 或 head 锡点漂移。`
|
|
301
|
+
);
|
|
302
|
+
} else {
|
|
303
|
+
validated.push({ repo: repoKey, task: taskId, head });
|
|
304
|
+
}
|
|
204
305
|
}
|
|
205
306
|
}
|
|
206
|
-
return
|
|
307
|
+
return { errors, warnings, validated };
|
|
207
308
|
}
|
|
208
309
|
|
|
209
310
|
/**
|
|
210
311
|
* apply worktree 变更到主工作区
|
|
211
312
|
*
|
|
313
|
+
* **跨仓 task no-op(task-05 / D-009 / design §6 A3/A5)**:当传 ctx 且 ctx.hasCrossRepo() 时,
|
|
314
|
+
* 跨仓 task 的 commit 已由子代理直接落跨仓仓主干(NG-3 跨仓不经主仓 worktree),apply 阶段对跨仓
|
|
315
|
+
* task = no-op(无 patch 可打,跨仓仓无 worktree/meta/分支——A5 patch 路径不可复用)。只校验跨仓
|
|
316
|
+
* task review.head 是跨仓仓真实 commit(约束①+② 保险,R-05)+ 不调 wm.cleanup(跨仓仓无主仓 worktree
|
|
317
|
+
* 可清,wm.cleanup 只作用于主仓 worktree)。主仓 task 走原 A5 完整 apply 不动(GOAL-2 单仓零回归)。
|
|
318
|
+
*
|
|
212
319
|
* @param {string} changeName - 变更名
|
|
213
|
-
* @param {{ cwd?: string, checkOnly?: boolean }} opts
|
|
320
|
+
* @param {{ cwd?: string, checkOnly?: boolean, merge?: boolean, ctx?: object }} opts
|
|
321
|
+
* - ctx:可选 MultiRepoContext(design §7.1)。缺省=单仓退化(仅主仓 apply,零行为变化,GOAL-2)。
|
|
322
|
+
* 提供 ctx 且含跨仓 entry 时,触发跨仓 no-op 校验(校验 review.head 真实 + 不 cleanup 跨仓)。
|
|
214
323
|
* @returns {{
|
|
215
324
|
* ok: boolean,
|
|
216
325
|
* changedFiles: string[],
|
|
217
326
|
* extraFiles: string[],
|
|
218
327
|
* hashMismatchFiles: string[],
|
|
219
328
|
* patchPath: string|null,
|
|
220
|
-
* errors: string[]
|
|
329
|
+
* errors: string[],
|
|
330
|
+
* crossRepoValidated?: Array<{ repo: string, head: string }>
|
|
221
331
|
* }}
|
|
222
332
|
*/
|
|
223
|
-
export function applyWorktree(changeName, { cwd, checkOnly = false, merge = false } = {}) {
|
|
333
|
+
export function applyWorktree(changeName, { cwd, checkOnly = false, merge = false, ctx = null } = {}) {
|
|
224
334
|
const projectRoot = cwd || process.cwd();
|
|
225
335
|
const wm = new WorktreeManager({ cwd: projectRoot });
|
|
226
336
|
const meta = wm.getMeta(changeName);
|
|
@@ -234,8 +344,22 @@ export function applyWorktree(changeName, { cwd, checkOnly = false, merge = fals
|
|
|
234
344
|
patchPath: null,
|
|
235
345
|
errors: [],
|
|
236
346
|
merged: false,
|
|
347
|
+
crossRepoValidated: [],
|
|
237
348
|
};
|
|
238
349
|
|
|
350
|
+
// --- 0. 跨仓 task no-op 校验(task-05 / D-009) ---
|
|
351
|
+
// 跨仓 task 的 commit 已落跨仓仓主干,apply = no-op(无 patch)。仅校验 review.head 是跨仓仓真实
|
|
352
|
+
// commit + 不 cleanup(wm.cleanup 只作用主仓 worktree)。校验失败 → 推 errors 阻断 apply(R-05)。
|
|
353
|
+
// 单仓 ctx 缺省 / 无跨仓 entry → validateCrossRepoNoOp 直接返回空(零回归)。
|
|
354
|
+
if (ctx) {
|
|
355
|
+
const cross = validateCrossRepoNoOp(ctx, projectRoot, changeName);
|
|
356
|
+
for (const e of cross.errors) result.errors.push(e);
|
|
357
|
+
if (cross.warnings.length > 0) result.warnings = (result.warnings || []).concat(cross.warnings);
|
|
358
|
+
result.crossRepoValidated = cross.validated;
|
|
359
|
+
// 跨仓 head 校验失败 = 跨仓改动未真落地,apply 不可继续(即使主仓 patch 能跑,跨仓 task 实际未完成)
|
|
360
|
+
if (cross.errors.length > 0) return result;
|
|
361
|
+
}
|
|
362
|
+
|
|
239
363
|
// --- 1. 校验 worktree 存在 + meta.json 有效 ---
|
|
240
364
|
if (!meta) {
|
|
241
365
|
result.errors.push(`worktree not found: ${changeName}。meta.json 不存在或已损坏。`);
|
|
@@ -297,7 +421,11 @@ export function applyWorktree(changeName, { cwd, checkOnly = false, merge = fals
|
|
|
297
421
|
}
|
|
298
422
|
|
|
299
423
|
// --- 3. 解析 apply 文件清单(design §6 清单 ∪ plan TaskCard allowed_paths,execute 复盘 c) ---
|
|
300
|
-
|
|
424
|
+
// resolveApplyAllowSet 返回 Map<repo, Set>(task-05 跨仓切片);主仓 apply 只消费 main 仓 Set
|
|
425
|
+
// (主仓 worktree diff 只含主仓文件,跨仓文件在跨仓仓不进主仓 worktree diff)。跨仓 allowed_paths
|
|
426
|
+
// 仅作 Map 切片返回供上游用,主流程不消费(跨仓 apply=no-op,D-009)。
|
|
427
|
+
const allowMap = resolveApplyAllowSet(projectRoot, changeName);
|
|
428
|
+
const allowSet = allowMap.get('main') || new Set();
|
|
301
429
|
const hasAllowList = allowSet.size > 0;
|
|
302
430
|
|
|
303
431
|
// --- 3.5 主干已提交推进检测:hashMismatch 前移(Grill P0 修复,design §step 顺序修正)---
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
### 测试用例设计(写测试时按此检查)
|
|
2
|
+
|
|
3
|
+
写测试是为了拦住回归,不是"有测试文件"。
|
|
4
|
+
**基础习惯照做、不展开**:FIRST(快/独立/可重复/自校验/及时)、金字塔分层(单元为主/集成少量/E2E 只冒烟)、AAA 结构、测试名描述场景、测试数据轻量构造(Object Mother/Builder)、覆盖率不追数字。
|
|
5
|
+
|
|
6
|
+
逐条对照检查:
|
|
7
|
+
1. **覆盖**:正常路径 + 边界 + 异常/非法输入,不只写业务正例;单测全绿 ≠ 集成正确。
|
|
8
|
+
2. **断言**:断言真实输出/副作用,禁止只"不抛错"的空断言;不 mock 被测方法自身。
|
|
9
|
+
3. **行为 vs 实现**:走公开 API 测行为;重构后功能不变,测试不应失败。
|
|
10
|
+
4. **契约与回归**:覆盖本次改动会破坏的既有行为与相邻模块接口;改公共签名/文案/常量时同步修复受影响既有测试并跑全量,禁止删断言凑绿。
|
|
11
|
+
5. **时间敏感分支**:超时/重试/并发分支别真等——超时参数做成可注入,测试传毫秒级极小值或注入可控时钟推进,再断言超时后的行为(走哪个分支/返回什么/资源是否回收)。
|
|
12
|
+
6. **隔离确定性**:独立 fixture、可重复跑,不依赖共享状态/执行顺序。
|
|
@@ -21,6 +21,7 @@ grep -rl "<关键词>" <源码目录>/ --include="*.java" --include="*.js" --inc
|
|
|
21
21
|
2. 对每个 task,**递归**检查对应模块目录及其**所有子目录**下是否存在测试文件(*test*、*spec*、*Test*、*Spec*)——包括模块内 co-located 的 tests/ 目录(如 `backend/app/modules/*/tests/`),**不要只看顶层 `tests/`**,否则会把测试就在模块内部的项目误判为“无测试”
|
|
22
22
|
3. 没有测试文件的 task 标记为 ⚠️ 缺少测试
|
|
23
23
|
4. **集成盲区提示**:测试文件存在 ≠ 集成正确——路由/layout 守卫重定向、跨模块装配这类集成 bug 组件单测覆盖不到,只有集成/冒烟/E2E 才暴露。对路由/layout/跨进程装配敏感的 task,额外检查是否有集成冒烟覆盖;无则标 ⚠️ 集成层未验证(需人工或部署确认)
|
|
24
|
+
5. **断言有效性抽查(与 execute「测试用例设计」闭环)**:测试文件存在 ≠ 测试有效。对最能代表本次变更行为的 2-3 个核心测试做抽查:① 断言验证真实输出/副作用,不是只"不抛错"的空断言、只测 getter/setter;② 覆盖边界/异常分支,不只业务正例;③ 走公开 API 测行为,不测实现细节(重构后功能不变测试不应失败)。不达标标 ⚠️ 断言薄弱或无效(advisory:CLI 不阻断,是否 FAIL 由你诚实判定;execute 已按「测试用例设计」写的,这里只抽查核验,不重审全量)
|
|
24
25
|
|
|
25
26
|
**探针 4:决策追踪覆盖探针(如存在 decisions.md)**
|
|
26
27
|
1. 从 decisions.md 提取所有当前版本 D-xxx@vN
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sillyspec-onboard
|
|
3
|
+
description: 为未安装 SillySpec 的全新项目接入规范驱动开发工作流——引导安装 CLI、初始化接入、建立项目库,并把「用 SillySpec 走流程」确立为项目默认工作方式。适合用户说"给这个项目接上 sillyspec"或新项目开工前确认流程。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## 交互规范
|
|
7
|
+
|
|
8
|
+
**当需要用户从多个选项中做出选择时,必须使用 Claude Code 内置的 AskUserQuestion 工具,将选项以参数传入。** 涉及是否全局安装 CLI / 是否建立项目库等决策,让用户亲手拍板,不替用户做决定。
|
|
9
|
+
|
|
10
|
+
## 何时使用
|
|
11
|
+
|
|
12
|
+
- 目标项目**没有安装/未接入** SillySpec(判断:项目根无 `.sillyspec/` 目录;或 `sillyspec --version` 命令不可用)
|
|
13
|
+
- 用户想让 SillySpec 成为该项目的开发流程控制器
|
|
14
|
+
|
|
15
|
+
## 铁律
|
|
16
|
+
|
|
17
|
+
- **必须用 exec 工具(shell)执行 CLI,不编造命令**;命中的命令固定为下方公开 CLI,不自行发明子命令
|
|
18
|
+
- 本 skill 只做**接入动作**(安装/初始化/验证),不写业务代码、不改项目源码
|
|
19
|
+
- 全程在主工作区根执行 CLI,不进入 worktree
|
|
20
|
+
- 每个决策点用 AskUserQuestion 让用户拍板,不静默代选
|
|
21
|
+
|
|
22
|
+
## 核心流程
|
|
23
|
+
|
|
24
|
+
### Step 0: 判定接入状态
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# 项目根是否已有 .sillyspec/(已接入)
|
|
28
|
+
ls -d .sillyspec 2>/dev/null
|
|
29
|
+
# CLI 是否可用
|
|
30
|
+
sillyspec --version 2>/dev/null
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
- 已接入(`.sillyspec/` 存在)→ 不重复接入,提示用户项目已用 SillySpec 管理,退出。
|
|
34
|
+
- 未接入但 CLI 已装 → 跳到 Step 2。
|
|
35
|
+
- 都无 → 继续 Step 1。
|
|
36
|
+
|
|
37
|
+
### Step 1: 引导安装 CLI(全局)
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install -g sillyspec
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- 若当前环境沙箱/权限导致 `npm -g` 失败,提示用户用 `! npm install -g sillyspec` 手动执行(输出直达会话)。
|
|
44
|
+
- 装完验证:`sillyspec --version` 可输出版本号。
|
|
45
|
+
|
|
46
|
+
### Step 2: 初始化接入项目
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
sillyspec init
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`init` 会自动:创建 `.sillyspec/`(项目注册、文档中心、知识库、运行时进度库)、检测项目中的 AI 工具(Claude Code 等)、给 `CLAUDE.md` 注入 SillySpec 受管段(若项目已有文件)或生成完整指引模板(若没有)、安装对应命令模板/MCP 工具。
|
|
53
|
+
|
|
54
|
+
- 新项目为空目录时建议走 `sillyspec run scan`(见 Step 3)补齐项目库;已有代码但未接入的存量项目,先确认项目状态再决定是否 scan。
|
|
55
|
+
- 验证产物:
|
|
56
|
+
```bash
|
|
57
|
+
ls .sillyspec/projects/ # 项目注册表
|
|
58
|
+
grep -c "SillySpec" CLAUDE.md # 受管段/指引已注入(>0)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Step 3: 建立项目库(知识基石)
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
sillyspec run scan
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
把项目的代码风格(CONVENTIONS.md)、架构(ARCHITECTURE.md)沉淀为后续 agent 的检索知识。这是「让后续 agent 不瞎猜」的关键一步,新项目建议立即做。
|
|
68
|
+
|
|
69
|
+
### Step 4: 价值认知锚 —— 让后续 agent 愿意用
|
|
70
|
+
|
|
71
|
+
接入完成后,**让"用 SillySpec 走流程"成为项目默认工作方式**靠的是已落盘的载体,不是本 skill(skill 是一次性的):
|
|
72
|
+
|
|
73
|
+
1. 确认价值锚已就位:
|
|
74
|
+
- `CLAUDE.md` 含 SillySpec 段(Step 2 已验证)——agent 每次会话读取项目说明书时都会看到「禁止绕过流程 / 新功能走 brainstorm→plan→execute→verify→archive / 小修复走 quick」。
|
|
75
|
+
- `.claude/skills/` 含 SillySpec 持续 skill(init 已复制 plan/quick/brainstorm 等)——agent 在开发任务中 `/plan`、`/quick` 即走流程。
|
|
76
|
+
- `.sillyspec/` 进度库已建——进度自动落盘,多 agent 并发不互相覆盖。
|
|
77
|
+
|
|
78
|
+
2. 向用户/后续 agent 说明价值(一句话口径,复用 init 注入的陈述,不另造文案):
|
|
79
|
+
> SillySpec 是给 Agent 调用的 CLI 流程控制器:你通过 CLI 告诉它"我在哪",它告诉你"下一步做什么";你执行步骤,它校验产出、推进状态,人类只在关键决策点介入审批。多 agent 同时操作代码时进度统一落盘、不丢不回滚。
|
|
80
|
+
|
|
81
|
+
3. 给后续 agent 一个「开工首查」示例:
|
|
82
|
+
```bash
|
|
83
|
+
sillyspec progress show # 看项目当前阶段/进行中变更
|
|
84
|
+
sillyspec run <stage> # 续跑当前阶段(brainstorm/plan/execute/verify/archive)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## 分发与安装(本 skill 是引导型)
|
|
88
|
+
|
|
89
|
+
本 skill 是「接入引导」,只在新项目接入时用一次,**不应随项目复制进 `.claude/skills/` 长期留存**。使用方式二选一:
|
|
90
|
+
|
|
91
|
+
- **项目级一次性**:复制本 `SKILL.md` 到目标项目 `.claude/skills/sillyspec-onboard/SKILL.md`,接入完成后删除。
|
|
92
|
+
- **全局可用**:复制到 `~/.claude/skills/sillyspec-onboard/SKILL.md`(或 `~/.config/claude/skills/`,随 Claude Code 版本),任何新项目都能呼起 `/sillyspec-onboard`。
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2026-08-10T17:08:53.237Z
|