sillyspec 3.28.3 → 3.28.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-auto/SKILL.md +54 -54
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -114
- package/.claude/skills/sillyspec-continue/SKILL.md +17 -17
- package/.claude/skills/sillyspec-execute/SKILL.md +222 -222
- package/.claude/skills/sillyspec-knowledge/SKILL.md +40 -40
- package/.claude/skills/sillyspec-plan/SKILL.md +156 -156
- package/.claude/skills/sillyspec-propose/SKILL.md +16 -16
- package/.claude/skills/sillyspec-resume/SKILL.md +21 -21
- package/.claude/skills/sillyspec-state/SKILL.md +25 -25
- package/.claude/skills/sillyspec-workspace/SKILL.md +33 -33
- package/AGENTS.md +24 -0
- package/INSTRUCTIONS.md +12 -0
- package/package.json +1 -1
- package/src/archive-delta.js +82 -0
- package/src/change-delete.js +2 -1
- package/src/change-list.js +7 -2
- package/src/change-risk-profile.js +77 -6
- package/src/config-cat.js +12 -1
- package/src/constants.js +3 -1
- package/src/docs-check.js +34 -0
- package/src/doctor-diagnostics.js +441 -5
- package/src/foreign-declared.js +53 -2
- package/src/index.js +181 -6
- package/src/module-impact.js +77 -12
- package/src/plan-adopt-waves.js +13 -5
- package/src/progress/change-registry.js +16 -0
- package/src/progress.js +62 -1
- package/src/quick-recommend.js +6 -1
- package/src/quicklog.js +10 -2
- package/src/run/command.js +30 -9
- package/src/run/complete-handlers.js +152 -6
- package/src/run/complete.js +45 -0
- package/src/run/gates.js +94 -6
- package/src/run/prompt.js +96 -5
- package/src/run/shared.js +74 -6
- package/src/run/stage.js +35 -0
- package/src/runtime-hygiene.js +84 -0
- package/src/spec-sync.js +63 -8
- package/src/stage-contract.js +83 -3
- package/src/stage-review.js +3 -0
- package/src/stages/archive.js +4 -4
- package/src/stages/brainstorm.js +2 -8
- package/src/stages/doctor.js +41 -515
- package/src/stages/plan-postcheck.js +191 -7
- package/src/stages/plan.js +5 -26
- package/src/stages/quick.js +11 -11
- package/src/stages/verify.js +10 -6
- package/src/sync-noise.js +158 -0
- package/src/sync.js +42 -17
- package/src/task-review.js +25 -1
- package/src/verify-facts-schema.js +141 -0
- package/src/verify-postcheck.js +257 -19
- package/src/verify-probes.js +142 -13
- package/src/workflow.js +6 -0
- package/src/worktree-apply.js +51 -13
- package/src/worktree.js +5 -1
package/src/worktree-apply.js
CHANGED
|
@@ -217,23 +217,31 @@ function getBlobHashMap(cwd, treeish, files) {
|
|
|
217
217
|
* 跨仓文件不在主仓 worktree 故永不进 diff);跨仓 task 走 no-op(D-009),其 allowed_paths 仅
|
|
218
218
|
* 作 Map 切片返回,供上游(assess / 跨仓 review 校验)按 repo 取用。
|
|
219
219
|
*
|
|
220
|
+
* **specBase 解析(2026-09-08 用户反馈:平台模式 apply allowlist 读本地空目录整批 BLOCKED)**:
|
|
221
|
+
* 优先级 opts.specBase(调用方传 platformOpts.specRoot)> 平台指针(.sillyspec-platform.json
|
|
222
|
+
* 静默读,不触发 resolvePlatformSpecDir 的守卫 warn)> 本地 projectRoot/.sillyspec。平台模式下
|
|
223
|
+
* changes/ 实体在 specRoot,本地目录常为空——旧硬编码 CHANGES_REL 恒读空 → allow 集恒空 →
|
|
224
|
+
* Gate1 整批误拦(sync.js BUG-01 同族)。
|
|
225
|
+
*
|
|
220
226
|
* @param {string} projectRoot - 主仓库根
|
|
221
227
|
* @param {string} changeName - 变更名
|
|
228
|
+
* @param {{ specBase?: string }} [opts] specBase 显式传入(平台模式必传 specRoot)
|
|
222
229
|
* @returns {Map<string, Set<string>>} repoKey → 并集清单 Set(无 design 清单且无 task 卡片时为空 Map)
|
|
223
230
|
*/
|
|
224
|
-
export function resolveApplyAllowSet(projectRoot, changeName) {
|
|
231
|
+
export function resolveApplyAllowSet(projectRoot, changeName, opts = {}) {
|
|
225
232
|
const repoMap = new Map();
|
|
226
233
|
const getOrCreate = (key) => {
|
|
227
234
|
if (!repoMap.has(key)) repoMap.set(key, new Set());
|
|
228
235
|
return repoMap.get(key);
|
|
229
236
|
};
|
|
230
|
-
|
|
237
|
+
const specBase = opts.specBase || _silentPointerSpecRoot(projectRoot) || join(projectRoot, '.sillyspec');
|
|
238
|
+
// design §6 清单归属 main(清单路径相对主仓根;跨仓 task 的 allowed_paths 由 task 卡 repo 切片)
|
|
231
239
|
const mainSet = getOrCreate('main');
|
|
232
240
|
parseFileChangeList(
|
|
233
|
-
join(
|
|
241
|
+
join(specBase, 'changes', changeName, 'design.md'),
|
|
234
242
|
{ keepSillyspecDocs: true }
|
|
235
243
|
).forEach(p => mainSet.add(p));
|
|
236
|
-
const tasksDir = join(
|
|
244
|
+
const tasksDir = join(specBase, 'changes', changeName, 'tasks');
|
|
237
245
|
if (existsSync(tasksDir)) {
|
|
238
246
|
for (const tf of readdirSync(tasksDir).filter(f => /^task-\d+\.md$/.test(f))) {
|
|
239
247
|
const content = readFileSync(join(tasksDir, tf), 'utf8');
|
|
@@ -246,6 +254,18 @@ export function resolveApplyAllowSet(projectRoot, changeName) {
|
|
|
246
254
|
return repoMap;
|
|
247
255
|
}
|
|
248
256
|
|
|
257
|
+
// 平台指针静默读(worktree-apply 内部用):只读 <projectRoot>/.sillyspec-platform.json 的
|
|
258
|
+
// specRoot 字段,不做存在性/自指/temp 守卫、不打 warn——allowlist 只需一个「读得到就用」的
|
|
259
|
+
// 基准目录,坏指针/缺字段落回本地(与 sync-noise.js bindSyncNoiseFromCwd 同款零副作用风格)。
|
|
260
|
+
function _silentPointerSpecRoot(projectRoot) {
|
|
261
|
+
try {
|
|
262
|
+
const p = join(projectRoot, '.sillyspec-platform.json');
|
|
263
|
+
if (!existsSync(p)) return null;
|
|
264
|
+
const s = JSON.parse(readFileSync(p, 'utf8').replace(/^\uFEFF/, ''));
|
|
265
|
+
return s && typeof s.specRoot === 'string' && s.specRoot ? s.specRoot : null;
|
|
266
|
+
} catch { return null; }
|
|
267
|
+
}
|
|
268
|
+
|
|
249
269
|
/**
|
|
250
270
|
* 收集最新 execute run 各 task review.json 的 changedFiles 声明 → Map<repoKey, string[]>。
|
|
251
271
|
*
|
|
@@ -257,14 +277,23 @@ export function resolveApplyAllowSet(projectRoot, changeName) {
|
|
|
257
277
|
* collectExecuteChangedFiles 一致:change 戳归属 run、.sillyspec//meta.json 过滤、
|
|
258
278
|
* review.repo 切片(跨仓声明不进 main 集)。读不到 run/review → 空 Map(fail-closed 回退旧行为)。
|
|
259
279
|
*
|
|
280
|
+
* runtimeRoot 解析(2026-09-08 平台模式修复,与 resolveApplyAllowSet 的 specBase 同族):
|
|
281
|
+
* 优先级 opts.runtimeRoot(调用方传 resolveRuntimeRoot(platformOpts, specBase) 产物)>
|
|
282
|
+
* 指针 specRoot/.runtime(静默读)> 本地 projectRoot/.sillyspec/.runtime。平台模式
|
|
283
|
+
* execute-runs 在平台 runtime root,旧硬编码本地恒空 → 第三源恒缺失。
|
|
284
|
+
*
|
|
260
285
|
* @param {string} projectRoot - 主仓库根
|
|
261
286
|
* @param {string} changeName - 变更名
|
|
287
|
+
* @param {{ runtimeRoot?: string }} [opts]
|
|
262
288
|
* @returns {Map<string, string[]>}
|
|
263
289
|
*/
|
|
264
|
-
export function collectReviewDeclaredFiles(projectRoot, changeName) {
|
|
290
|
+
export function collectReviewDeclaredFiles(projectRoot, changeName, opts = {}) {
|
|
265
291
|
const byRepo = new Map();
|
|
266
292
|
try {
|
|
267
|
-
const
|
|
293
|
+
const ptrSpec = _silentPointerSpecRoot(projectRoot);
|
|
294
|
+
const runtimeRoot = opts.runtimeRoot
|
|
295
|
+
|| (ptrSpec ? join(ptrSpec, '.runtime') : null)
|
|
296
|
+
|| join(projectRoot, '.sillyspec', '.runtime');
|
|
268
297
|
const runId = resolveLatestExecuteRunIdWithTasks({ runtimeRoot, changeName });
|
|
269
298
|
if (!runId) return byRepo;
|
|
270
299
|
const runTasksDir = join(runtimeRoot, 'execute-runs', runId, 'tasks');
|
|
@@ -504,10 +533,11 @@ function applyCrossRepoWorktrees(changeName, projectRoot, ctx, { checkOnly = fal
|
|
|
504
533
|
continue;
|
|
505
534
|
}
|
|
506
535
|
|
|
507
|
-
// 清单校验(per-repo 切片 + review 声明切片并入,与主仓 Gate1/3b
|
|
508
|
-
|
|
536
|
+
// 清单校验(per-repo 切片 + review 声明切片并入,与主仓 Gate1/3b 同语义)。
|
|
537
|
+
// specBase 显式传(平台模式 changes/ 在 specRoot;本地模式与旧硬编码同值零回归)。
|
|
538
|
+
const allowMap = resolveApplyAllowSet(projectRoot, changeName, { specBase });
|
|
509
539
|
let allowSet = allowMap.get(repoKey) || new Set();
|
|
510
|
-
const reviewDeclared = collectReviewDeclaredFiles(projectRoot, changeName).get(repoKey);
|
|
540
|
+
const reviewDeclared = collectReviewDeclaredFiles(projectRoot, changeName, { runtimeRoot: join(specBase, '.runtime') }).get(repoKey);
|
|
511
541
|
if (reviewDeclared) {
|
|
512
542
|
const merged = new Set([...allowSet]);
|
|
513
543
|
for (const f of reviewDeclared) {
|
|
@@ -754,15 +784,21 @@ export function applyWorktree(changeName, { cwd, checkOnly = false, merge = fals
|
|
|
754
784
|
}
|
|
755
785
|
|
|
756
786
|
// --- 3. 解析 apply 文件清单(design §6 清单 ∪ plan TaskCard allowed_paths,execute 复盘 c) ---
|
|
787
|
+
// allowlist 基准目录(2026-09-08 平台模式修复):ctx.platformOpts.specRoot 优先(execute
|
|
788
|
+
// ctx 透传),否则 resolveApplyAllowSet 内部静默读平台指针兜底,最后本地——旧硬编码在
|
|
789
|
+
// 平台模式读本地空 changes/ → allow 集恒空 → Gate1 整批误拦。
|
|
790
|
+
const allowSpecBase = (ctx && ctx.platformOpts && ctx.platformOpts.specRoot) || undefined;
|
|
791
|
+
const allowRuntimeRoot = (ctx && ctx.platformOpts && ctx.platformOpts.runtimeRoot)
|
|
792
|
+
|| (allowSpecBase ? join(allowSpecBase, '.runtime') : undefined);
|
|
757
793
|
// resolveApplyAllowSet 返回 Map<repo, Set>(task-05 跨仓切片);主仓 apply 只消费 main 仓 Set
|
|
758
794
|
// (主仓 worktree diff 只含主仓文件,跨仓文件在跨仓仓不进主仓 worktree diff)。跨仓 allowed_paths
|
|
759
795
|
// 仅作 Map 切片返回供上游用,主流程不消费(跨仓 apply=no-op,D-009)。
|
|
760
|
-
const allowMap = resolveApplyAllowSet(projectRoot, changeName);
|
|
796
|
+
const allowMap = resolveApplyAllowSet(projectRoot, changeName, { specBase: allowSpecBase });
|
|
761
797
|
// --- 3b. review.json 声明偏差文件并入(坑 apply-undeclared-deviation-block)---
|
|
762
798
|
// 第三源:reviewer 声明的 changedFiles(已过 Task Review Gate git 证据交叉校验)——执行期
|
|
763
799
|
// 有据越界(facade 转发/名单测试)不再逼回改 design.md。各 repo 切片各自并入;仅靠 review
|
|
764
800
|
// 放行的文件记审计 warning(result.reviewAdmittedFiles),完全越界文件仍拦(Gate1 在扩展后判)。
|
|
765
|
-
const reviewDeclaredByRepo = collectReviewDeclaredFiles(projectRoot, changeName);
|
|
801
|
+
const reviewDeclaredByRepo = collectReviewDeclaredFiles(projectRoot, changeName, { runtimeRoot: allowRuntimeRoot });
|
|
766
802
|
const reviewAdmittedFiles = [];
|
|
767
803
|
for (const [repoKey, files] of reviewDeclaredByRepo) {
|
|
768
804
|
const repoSet = allowMap.get(repoKey);
|
|
@@ -1693,13 +1729,15 @@ export function assessApplyRisk(changeName, { cwd } = {}) {
|
|
|
1693
1729
|
|
|
1694
1730
|
// design §6 标记为「顺带修复」的文件(坑 worktree-execute-apply-friction 坑1):合规修预存债,
|
|
1695
1731
|
// 不属任何 task 边界,assess 豁免 allowed_paths 严格校验(降级 warning),避免被迫 cherry-pick 绕过。
|
|
1696
|
-
|
|
1732
|
+
// specBase 走指针/本地解析(2026-09-08 平台模式修复,与 resolveApplyAllowSet 同族)。
|
|
1733
|
+
const assessSpecBase = _silentPointerSpecRoot(projectRoot) || join(projectRoot, '.sillyspec');
|
|
1734
|
+
const designPath = join(assessSpecBase, 'changes', changeName, 'design.md');
|
|
1697
1735
|
const incidentalSet = new Set(
|
|
1698
1736
|
parseFileChangeListDetailed(designPath).filter(e => e.incidental).map(e => e.path)
|
|
1699
1737
|
);
|
|
1700
1738
|
// review.json 声明偏差文件(坑 apply-undeclared-deviation-block):与顺带修复同等待遇——
|
|
1701
1739
|
// reviewer 声明(已过 git 证据校验)的执行期偏差豁免 allowed_paths 严格校验,降 warning 注明来源。
|
|
1702
|
-
const reviewDeclaredSet = new Set(collectReviewDeclaredFiles(projectRoot, changeName).get('main') || []);
|
|
1740
|
+
const reviewDeclaredSet = new Set(collectReviewDeclaredFiles(projectRoot, changeName, { runtimeRoot: join(assessSpecBase, '.runtime') }).get('main') || []);
|
|
1703
1741
|
|
|
1704
1742
|
// 检查 2: 变更在 allowed_paths 内(仅在 TaskCard 存在时);顺带修复/review 声明文件豁免。
|
|
1705
1743
|
// 匹配换 pathMatches(与 Gate1/plan-postcheck 同语义容差),消除原字面前缀弱匹配漂移。
|
package/src/worktree.js
CHANGED
|
@@ -203,7 +203,11 @@ function printSyncReport(diag, baseHash, changeName) {
|
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
function parseJSON(raw) {
|
|
206
|
-
|
|
206
|
+
// BOM 容错(2026-09-08 用户反馈:meta.json 带 UTF-8 BOM 被当损坏 → apply 报
|
|
207
|
+
// 「meta.json 不存在或已损坏」、幽灵判定误清):Windows 编辑器(记事本/部分 PowerShell
|
|
208
|
+
// 重定向)写 JSON 常带 \uFEFF 前缀,JSON.parse 对首字符 BOM 直接抛。剥掉再 parse。
|
|
209
|
+
const s = typeof raw === 'string' ? raw.replace(/^\uFEFF/, '') : raw
|
|
210
|
+
try { return JSON.parse(s); } catch { return null; }
|
|
207
211
|
}
|
|
208
212
|
|
|
209
213
|
/**
|