sillyspec 3.27.5 → 3.27.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/.worktrees/8d2d0392/.claude/skills/sillyspec-archive/SKILL.md +87 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-auto/SKILL.md +93 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-commit/SKILL.md +67 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-continue/SKILL.md +47 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-doctor/SKILL.md +76 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-execute/SKILL.md +214 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-explore/SKILL.md +102 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-export/SKILL.md +57 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-init/SKILL.md +177 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-knowledge/SKILL.md +270 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-plan/SKILL.md +156 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-propose/SKILL.md +87 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-quick/SKILL.md +116 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-resume/SKILL.md +70 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-scan/SKILL.md +79 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-state/SKILL.md +64 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-status/SKILL.md +54 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-verify/SKILL.md +87 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-workspace/SKILL.md +175 -0
- package/.worktrees/8d2d0392/.claude/skills/verify-per-user/SKILL.md +6 -0
- package/.worktrees/8d2d0392/.gitattributes +2 -0
- package/.worktrees/8d2d0392/.husky/pre-push +20 -0
- package/.worktrees/8d2d0392/README.md +203 -0
- package/.worktrees/8d2d0392/SKILL.md +91 -0
- package/.worktrees/8d2d0392/architecture-analysis.md +448 -0
- package/.worktrees/8d2d0392/bin/sillyspec.js +2 -0
- package/.worktrees/8d2d0392/eval/README.md +86 -0
- package/.worktrees/8d2d0392/eval/eval.config.json +28 -0
- package/.worktrees/8d2d0392/eval/lib/adapters/cli.mjs +63 -0
- package/.worktrees/8d2d0392/eval/lib/adapters/dryrun.mjs +27 -0
- package/.worktrees/8d2d0392/eval/lib/config.mjs +82 -0
- package/.worktrees/8d2d0392/eval/lib/db.mjs +76 -0
- package/.worktrees/8d2d0392/eval/lib/report.mjs +120 -0
- package/.worktrees/8d2d0392/eval/lib/runner.mjs +157 -0
- package/.worktrees/8d2d0392/eval/lib/tasks.mjs +73 -0
- package/.worktrees/8d2d0392/eval/lib/util.mjs +107 -0
- package/.worktrees/8d2d0392/eval/lib/workdir.mjs +30 -0
- package/.worktrees/8d2d0392/eval/run.mjs +83 -0
- package/.worktrees/8d2d0392/eval/tasks/001-slugify/files/package.json +6 -0
- package/.worktrees/8d2d0392/eval/tasks/001-slugify/solution/src/slugify.mjs +26 -0
- package/.worktrees/8d2d0392/eval/tasks/001-slugify/task.json +6 -0
- package/.worktrees/8d2d0392/eval/tasks/001-slugify/verify.mjs +28 -0
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/files/package.json +6 -0
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/files/src/dates.mjs +4 -0
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/solution/src/dates.mjs +30 -0
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/task.json +6 -0
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/verify.mjs +31 -0
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/files/data/notes.txt +5 -0
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/files/package.json +6 -0
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/solution/bin/report.mjs +36 -0
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/task.json +6 -0
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/verify.mjs +38 -0
- package/.worktrees/8d2d0392/logo.jpg +0 -0
- package/.worktrees/8d2d0392/package-lock.json +977 -0
- package/.worktrees/8d2d0392/package.json +41 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/favicon.jpg +0 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/index.html +16 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/logo.jpg +0 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/prototype-dashboard.html +836 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/prototype-overview.html +256 -0
- package/.worktrees/8d2d0392/packages/dashboard/package.json +25 -0
- package/.worktrees/8d2d0392/packages/dashboard/server/executor.js +95 -0
- package/.worktrees/8d2d0392/packages/dashboard/server/index.js +679 -0
- package/.worktrees/8d2d0392/packages/dashboard/server/parser.js +526 -0
- package/.worktrees/8d2d0392/packages/dashboard/server/watcher.js +357 -0
- package/.worktrees/8d2d0392/src/agent-session-log.js +803 -0
- package/.worktrees/8d2d0392/src/change-list.js +290 -0
- package/.worktrees/8d2d0392/src/change-risk-profile.js +339 -0
- package/.worktrees/8d2d0392/src/check-primitives.js +65 -0
- package/.worktrees/8d2d0392/src/classify-change.js +109 -0
- package/.worktrees/8d2d0392/src/commit-suggest.js +202 -0
- package/.worktrees/8d2d0392/src/config-cat.js +88 -0
- package/.worktrees/8d2d0392/src/config-schema.js +328 -0
- package/.worktrees/8d2d0392/src/constants.js +97 -0
- package/.worktrees/8d2d0392/src/contract-matrix.js +533 -0
- package/.worktrees/8d2d0392/src/datetime.js +20 -0
- package/.worktrees/8d2d0392/src/db-engine.js +55 -0
- package/.worktrees/8d2d0392/src/db.js +381 -0
- package/.worktrees/8d2d0392/src/decision-distill.js +517 -0
- package/.worktrees/8d2d0392/src/dispatch/backends/local-agent.js +155 -0
- package/.worktrees/8d2d0392/src/dispatch/backends/sillyhub-mcp.js +245 -0
- package/.worktrees/8d2d0392/src/dispatch/probe.js +246 -0
- package/.worktrees/8d2d0392/src/dispatch/strategy.js +121 -0
- package/.worktrees/8d2d0392/src/docs-check.js +825 -0
- package/.worktrees/8d2d0392/src/docs-debt.js +393 -0
- package/.worktrees/8d2d0392/src/docs-gate.js +107 -0
- package/.worktrees/8d2d0392/src/doctor-diagnostics.js +1099 -0
- package/.worktrees/8d2d0392/src/endpoint-extractor.js +409 -0
- package/.worktrees/8d2d0392/src/foreign-declared.js +78 -0
- package/.worktrees/8d2d0392/src/fs-atomic.js +73 -0
- package/.worktrees/8d2d0392/src/git-helper.js +150 -0
- package/.worktrees/8d2d0392/src/hooks/claude-pre-tool-use.cjs +130 -0
- package/.worktrees/8d2d0392/src/hooks/worktree-guard.js +892 -0
- package/.worktrees/8d2d0392/src/index.js +2854 -0
- package/.worktrees/8d2d0392/src/init.js +626 -0
- package/.worktrees/8d2d0392/src/knowledge-match.js +231 -0
- package/.worktrees/8d2d0392/src/local-detect.js +128 -0
- package/.worktrees/8d2d0392/src/local-register.js +104 -0
- package/.worktrees/8d2d0392/src/machine-interface.js +543 -0
- package/.worktrees/8d2d0392/src/migrate.js +127 -0
- package/.worktrees/8d2d0392/src/module-impact.js +166 -0
- package/.worktrees/8d2d0392/src/module-resolve.js +222 -0
- package/.worktrees/8d2d0392/src/modules.js +580 -0
- package/.worktrees/8d2d0392/src/plan-adopt-waves.js +126 -0
- package/.worktrees/8d2d0392/src/progress/change-registry.js +336 -0
- package/.worktrees/8d2d0392/src/progress/consistency-doctor.js +355 -0
- package/.worktrees/8d2d0392/src/progress/shared.js +36 -0
- package/.worktrees/8d2d0392/src/progress/stage-machine.js +594 -0
- package/.worktrees/8d2d0392/src/progress/step-store.js +265 -0
- package/.worktrees/8d2d0392/src/progress.js +1304 -0
- package/.worktrees/8d2d0392/src/quick-recommend.js +115 -0
- package/.worktrees/8d2d0392/src/quicklog.js +883 -0
- package/.worktrees/8d2d0392/src/review-tier.js +77 -0
- package/.worktrees/8d2d0392/src/run/command.js +1641 -0
- package/.worktrees/8d2d0392/src/run/complete-handlers.js +1552 -0
- package/.worktrees/8d2d0392/src/run/complete.js +1259 -0
- package/.worktrees/8d2d0392/src/run/concurrent-detect.js +156 -0
- package/.worktrees/8d2d0392/src/run/gates.js +1094 -0
- package/.worktrees/8d2d0392/src/run/multi-repo-context.js +208 -0
- package/.worktrees/8d2d0392/src/run/next.js +128 -0
- package/.worktrees/8d2d0392/src/run/prompt.js +843 -0
- package/.worktrees/8d2d0392/src/run/quick-audit.js +186 -0
- package/.worktrees/8d2d0392/src/run/scan-profile.js +269 -0
- package/.worktrees/8d2d0392/src/run/shared.js +1562 -0
- package/.worktrees/8d2d0392/src/run/stage.js +587 -0
- package/.worktrees/8d2d0392/src/run.js +23 -0
- package/.worktrees/8d2d0392/src/scan-diff.js +388 -0
- package/.worktrees/8d2d0392/src/scan-postcheck.js +524 -0
- package/.worktrees/8d2d0392/src/scan-staleness.js +95 -0
- package/.worktrees/8d2d0392/src/setup.js +414 -0
- package/.worktrees/8d2d0392/src/sillyhub-mcp/client.js +597 -0
- package/.worktrees/8d2d0392/src/sillyhub-mcp/config.js +62 -0
- package/.worktrees/8d2d0392/src/spec-dir-typo.js +50 -0
- package/.worktrees/8d2d0392/src/spec-sync.js +358 -0
- package/.worktrees/8d2d0392/src/stage-contract-engine.js +171 -0
- package/.worktrees/8d2d0392/src/stage-contract-spec.js +551 -0
- package/.worktrees/8d2d0392/src/stage-contract.js +948 -0
- package/.worktrees/8d2d0392/src/stage-review.js +597 -0
- package/.worktrees/8d2d0392/src/stages/archive.js +199 -0
- package/.worktrees/8d2d0392/src/stages/brainstorm-auto.js +285 -0
- package/.worktrees/8d2d0392/src/stages/brainstorm.js +624 -0
- package/.worktrees/8d2d0392/src/stages/cmd-existence.js +145 -0
- package/.worktrees/8d2d0392/src/stages/doctor.js +540 -0
- package/.worktrees/8d2d0392/src/stages/execute.js +1277 -0
- package/.worktrees/8d2d0392/src/stages/explore.js +43 -0
- package/.worktrees/8d2d0392/src/stages/index.js +29 -0
- package/.worktrees/8d2d0392/src/stages/knowledge.js +514 -0
- package/.worktrees/8d2d0392/src/stages/plan-postcheck.js +1398 -0
- package/.worktrees/8d2d0392/src/stages/plan.js +651 -0
- package/.worktrees/8d2d0392/src/stages/quick.js +167 -0
- package/.worktrees/8d2d0392/src/stages/scan.js +608 -0
- package/.worktrees/8d2d0392/src/stages/status.js +64 -0
- package/.worktrees/8d2d0392/src/stages/verify.js +246 -0
- package/.worktrees/8d2d0392/src/sync.js +1470 -0
- package/.worktrees/8d2d0392/src/task-review.js +1498 -0
- package/.worktrees/8d2d0392/src/taskcard-placeholders.js +21 -0
- package/.worktrees/8d2d0392/src/taskcard.js +217 -0
- package/.worktrees/8d2d0392/src/verify-postcheck.js +1718 -0
- package/.worktrees/8d2d0392/src/verify-probes.js +327 -0
- package/.worktrees/8d2d0392/src/version.js +19 -0
- package/.worktrees/8d2d0392/src/workflow.js +600 -0
- package/.worktrees/8d2d0392/src/workspace.js +125 -0
- package/.worktrees/8d2d0392/src/worktree-apply.js +1611 -0
- package/.worktrees/8d2d0392/src/worktree-deps.js +576 -0
- package/.worktrees/8d2d0392/src/worktree.js +1838 -0
- package/.worktrees/8d2d0392/templates/claude-instruction.md +23 -0
- package/.worktrees/8d2d0392/templates/prompts/taskcard-rules.md +29 -0
- package/.worktrees/8d2d0392/templates/prompts/testcase-design.md +12 -0
- package/.worktrees/8d2d0392/templates/prompts/verify-probes.md +32 -0
- package/.worktrees/8d2d0392/templates/workflows/archive-impact.yaml +79 -0
- package/.worktrees/8d2d0392/templates/workflows/scan-docs.yaml +132 -0
- package/architecture-analysis.md +448 -0
- package/package.json +1 -1
- package/src/contract-matrix.js +1 -1
- package/src/endpoint-extractor.js +41 -52
- package/src/foreign-declared.js +78 -1
- package/src/index.js +9 -7
- package/src/run/command.js +2 -1
- package/src/run/gates.js +17 -0
- package/src/run/stage.js +8 -5
- package/src/stage-contract-spec.js +1 -1
- package/src/stages/brainstorm-auto.js +43 -1
- package/src/stages/brainstorm.js +2 -0
- package/src/stages/plan.js +8 -4
- package/src/stages/verify.js +1 -1
- package/src/taskcard.js +33 -0
- package/src/verify-probes.js +27 -6
- package/src/worktree-apply.js +451 -286
- package/src/worktree-deps.js +93 -1
- package/src/worktree.js +84 -19
- package/templates/prompts/taskcard-rules.md +1 -1
package/src/worktree-deps.js
CHANGED
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
* 见 change 2026-06-28-worktree-deps-provision / D-005@v1, D-007@v1。
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { existsSync, readFileSync, realpathSync, lstatSync } from 'fs';
|
|
11
|
+
import { existsSync, readFileSync, realpathSync, lstatSync, readdirSync } from 'fs';
|
|
12
12
|
import { join, isAbsolute, relative, resolve as resolvePath, sep as pathSep } from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
13
14
|
import { execFileSync } from 'child_process';
|
|
14
15
|
import { createHash } from 'crypto';
|
|
15
16
|
|
|
@@ -574,3 +575,94 @@ export function checkDepsFreshness(meta, wtPath, mainCwd) {
|
|
|
574
575
|
wtHash, mainHash, metaLockHash,
|
|
575
576
|
};
|
|
576
577
|
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* editable install 越界探测(坑 worktree-editable-install-escape,2026-08-25 用户实证
|
|
581
|
+
* gen:types 坑):worktree 内 Python venv 的 editable install 指向 worktree 外(典型:
|
|
582
|
+
* 主仓 checkout 路径)时,gen:types / 后端命令 / pytest 静默加载 worktree 外的旧代码——
|
|
583
|
+
* 改动不生效且无任何报错,此前靠模块文档注意事项人工记忆,本函数把它 CLI 检查化
|
|
584
|
+
* (worktree doctor 调用)。
|
|
585
|
+
*
|
|
586
|
+
* 覆盖三种 editable 痕迹:
|
|
587
|
+
* 1. 路径型 .pth(setuptools 旧式):.pth 内容行即绝对路径
|
|
588
|
+
* 2. PEP 660 __editable__.<pkg>-*.pth + __editable___<pkg>_*_finder.py(MAPPING 表绝对路径)
|
|
589
|
+
* 3. *.dist-info/direct_url.json { dir_info.editable: true, url: file:///... }
|
|
590
|
+
* 判定统一为「目标路径 resolve 后不在 worktree 内」;venv/文件读取失败按无越界处理
|
|
591
|
+
* (doctor 体检语义,不阻断)。
|
|
592
|
+
*
|
|
593
|
+
* @param {string} wtPath worktree 根目录
|
|
594
|
+
* @returns {Array<{ pkg: string, target: string, via: string }>} 越界清单(空 = 干净或无 venv)
|
|
595
|
+
*/
|
|
596
|
+
export function detectEditableInstallEscape(wtPath) {
|
|
597
|
+
const offenders = [];
|
|
598
|
+
if (!wtPath || !existsSync(wtPath)) return offenders;
|
|
599
|
+
const wtRoot = resolvePath(wtPath);
|
|
600
|
+
const isEscape = (target) => {
|
|
601
|
+
if (!target || !isAbsolute(target)) return false;
|
|
602
|
+
const rel = relative(wtRoot, resolvePath(target));
|
|
603
|
+
return rel.startsWith('..') || isAbsolute(rel);
|
|
604
|
+
};
|
|
605
|
+
const seen = new Set();
|
|
606
|
+
const push = (pkg, target, via) => {
|
|
607
|
+
const key = `${pkg}|${target}`;
|
|
608
|
+
if (seen.has(key)) return;
|
|
609
|
+
seen.add(key);
|
|
610
|
+
offenders.push({ pkg, target, via });
|
|
611
|
+
};
|
|
612
|
+
|
|
613
|
+
for (const venvName of ['.venv', 'venv']) {
|
|
614
|
+
const venvDir = join(wtPath, venvName);
|
|
615
|
+
if (!existsSync(venvDir)) continue;
|
|
616
|
+
// site-packages 候选:Windows Lib/site-packages + POSIX lib/python*/site-packages
|
|
617
|
+
const spCandidates = [join(venvDir, 'Lib', 'site-packages')];
|
|
618
|
+
try {
|
|
619
|
+
for (const e of readdirSync(join(venvDir, 'lib'), { withFileTypes: true })) {
|
|
620
|
+
if (e.isDirectory() && e.name.toLowerCase().startsWith('python')) {
|
|
621
|
+
spCandidates.push(join(venvDir, 'lib', e.name, 'site-packages'));
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
} catch { /* 无 lib 目录(Windows 布局)跳过 */ }
|
|
625
|
+
for (const sp of spCandidates) {
|
|
626
|
+
if (!existsSync(sp)) continue;
|
|
627
|
+
let entries = [];
|
|
628
|
+
try { entries = readdirSync(sp, { withFileTypes: true }); } catch { continue; }
|
|
629
|
+
for (const e of entries) {
|
|
630
|
+
if (!e.isFile()) continue;
|
|
631
|
+
const fp = join(sp, e.name);
|
|
632
|
+
if (e.name.endsWith('.pth')) {
|
|
633
|
+
let lines = [];
|
|
634
|
+
try { lines = readFileSync(fp, 'utf8').split('\n'); } catch { continue; }
|
|
635
|
+
for (const raw of lines) {
|
|
636
|
+
const line = raw.trim();
|
|
637
|
+
if (!line || line.startsWith('#') || line.startsWith('import ')) continue;
|
|
638
|
+
if (isEscape(line)) push(e.name.replace(/\.pth$/, ''), line, '.pth');
|
|
639
|
+
}
|
|
640
|
+
} else if (/^__editable___.+_finder\.py$/.test(e.name)) {
|
|
641
|
+
let src = '';
|
|
642
|
+
try { src = readFileSync(fp, 'utf8'); } catch { continue; }
|
|
643
|
+
const mm = /MAPPING\s*=\s*\{([\s\S]*?)\}/.exec(src);
|
|
644
|
+
if (!mm) continue;
|
|
645
|
+
const pkg = e.name.replace(/^__editable___/, '').replace(/_finder\.py$/, '');
|
|
646
|
+
for (const pm of mm[1].matchAll(/'((?:[^'\\]|\\.)*)'/g)) {
|
|
647
|
+
const target = pm[1].replace(/\\\\/g, '\\');
|
|
648
|
+
if (isEscape(target)) push(pkg, target, 'pep660-finder');
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
// direct_url.json(pip/uv 现代 editable 标记)
|
|
653
|
+
for (const e of entries) {
|
|
654
|
+
if (!e.isDirectory() || !e.name.endsWith('.dist-info')) continue;
|
|
655
|
+
const du = join(sp, e.name, 'direct_url.json');
|
|
656
|
+
if (!existsSync(du)) continue;
|
|
657
|
+
try {
|
|
658
|
+
const meta = JSON.parse(readFileSync(du, 'utf8'));
|
|
659
|
+
if (meta?.dir_info?.editable && typeof meta.url === 'string' && meta.url.startsWith('file://')) {
|
|
660
|
+
const target = fileURLToPath(meta.url);
|
|
661
|
+
if (isEscape(target)) push(e.name.replace(/\.dist-info$/, ''), target, 'direct_url');
|
|
662
|
+
}
|
|
663
|
+
} catch { /* 解析失败跳过 */ }
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return offenders;
|
|
668
|
+
}
|
package/src/worktree.js
CHANGED
|
@@ -12,7 +12,7 @@ import { execFileSync } from 'child_process';
|
|
|
12
12
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync, readdirSync, statSync, lstatSync, readlinkSync, unlinkSync, copyFileSync } from 'fs';
|
|
13
13
|
import { join, resolve, dirname, relative, isAbsolute } from 'path';
|
|
14
14
|
import { createHash } from 'crypto';
|
|
15
|
-
import { provisionDeps, checkDepsFreshness } from './worktree-deps.js';
|
|
15
|
+
import { provisionDeps, checkDepsFreshness, detectEditableInstallEscape } from './worktree-deps.js';
|
|
16
16
|
import { writeAtomicSync } from './fs-atomic.js';
|
|
17
17
|
import { git, gitQuiet } from './git-helper.js';
|
|
18
18
|
|
|
@@ -416,7 +416,7 @@ export class WorktreeManager {
|
|
|
416
416
|
* @returns {{ branch: string, worktreePath: string, baseHash: string }}
|
|
417
417
|
* @throws {Error} worktree 已存在、git 不可用、changeName 为空
|
|
418
418
|
*/
|
|
419
|
-
create(changeName, { base } = {}) {
|
|
419
|
+
create(changeName, { base, adoptBranch = false } = {}) {
|
|
420
420
|
const name = validateChangeName(changeName);
|
|
421
421
|
const worktreePath = this.getWorktreePath(name);
|
|
422
422
|
const branch = BRANCH_PREFIX + name;
|
|
@@ -481,18 +481,29 @@ export class WorktreeManager {
|
|
|
481
481
|
try { safeRemoveWorktreeDir(worktreePath); } catch (unlinkErr) {
|
|
482
482
|
throw new Error(`幽灵 worktree 安全清理失败(已阻断,主仓 node_modules 未受影响):${unlinkErr.message}`)
|
|
483
483
|
}
|
|
484
|
-
// 同步清理 git worktree
|
|
485
|
-
//
|
|
484
|
+
// 同步清理 git worktree 注册(git 内部状态),否则后续 worktree add 会因「已注册」失败。
|
|
485
|
+
// 不再盲删同名分支(坑 worktree-user-branch-conflict,2026-08-24 用户反馈五期①:该分支
|
|
486
|
+
// 可能是用户有意创建的,无凭据区分遗留 vs 自建——统一交由下方分支决策菜单处置)。
|
|
486
487
|
try { gitQuiet(this.cwd, ['worktree', 'prune'], { timeout: 30000 }); } catch {}
|
|
487
|
-
try { gitQuiet(this.cwd, ['branch', '-D', branch]); } catch {}
|
|
488
488
|
} else {
|
|
489
489
|
throw new Error(`worktree already exists: ${name}. Run cleanup first.`);
|
|
490
490
|
}
|
|
491
491
|
}
|
|
492
492
|
|
|
493
|
-
// 2.
|
|
494
|
-
|
|
495
|
-
|
|
493
|
+
// 2. 检查分支是否已存在(坑 worktree-user-branch-conflict,2026-08-24 用户反馈五期①:
|
|
494
|
+
// 「用户要求在指定分支上做」时既有同名分支让 execute 直接死锁,旧报错只说 Run cleanup
|
|
495
|
+
// first、修复建议无条件推荐删分支——用户自建分支被引导误删。现给决策菜单;--adopt-branch
|
|
496
|
+
// 显式收编:检出该分支为 worktree 工作分支,分支现状作 baseline,仅后续新改动计入交付。)
|
|
497
|
+
const branchExists = !!gitQuiet(this.cwd, ['rev-parse', '--verify', `refs/heads/${branch}`]);
|
|
498
|
+
let adoptedBranch = false;
|
|
499
|
+
if (branchExists && !adoptBranch) {
|
|
500
|
+
throw new Error(
|
|
501
|
+
`分支已存在:${branch}(worktree create 需新建同名分支,直接冲突)。按实际情况三选一:\n` +
|
|
502
|
+
` ① 本变更的遗留分支(上次 execute 残留、内容已落地或作废):确认后 git branch -D ${branch} 再重跑;\n` +
|
|
503
|
+
` ② 该分支是你有意让本变更在其上做的(用户指定分支):sillyspec run execute --change ${name} --adopt-branch\n` +
|
|
504
|
+
` —— 收编为 worktree 工作分支,分支既有内容计入 baseline(仅后续新改动算交付 diff);\n` +
|
|
505
|
+
` ③ 换变更名重跑。`
|
|
506
|
+
);
|
|
496
507
|
}
|
|
497
508
|
|
|
498
509
|
// 3. 解析 base 分支
|
|
@@ -521,7 +532,17 @@ export class WorktreeManager {
|
|
|
521
532
|
|
|
522
533
|
// 5. 创建 worktree(含版本检测 + sandbox fallback)
|
|
523
534
|
try {
|
|
524
|
-
|
|
535
|
+
if (branchExists && adoptBranch) {
|
|
536
|
+
// --adopt-branch 收编(坑 worktree-user-branch-conflict):检出既有分支为工作分支(不 -b),
|
|
537
|
+
// 分支当前 HEAD 作 baseHash——分支存量内容归 baseline,仅子代理新改动计入交付 diff
|
|
538
|
+
// (与 dirty baseline overlay 同哲学)。baseBranch 仍记主仓侧锚(sync 诊断用)。
|
|
539
|
+
git(this.cwd, ['worktree', 'add', worktreePath, branch], { timeout: 120000 });
|
|
540
|
+
baseHash = git(this.cwd, ['rev-parse', branch]);
|
|
541
|
+
adoptedBranch = true;
|
|
542
|
+
console.log(`📌 已收编既有分支 ${branch} 为 worktree 工作分支(--adopt-branch):baseline = 分支 HEAD ${String(baseHash).slice(0, 8)},分支既有内容不计交付 diff`);
|
|
543
|
+
} else {
|
|
544
|
+
git(this.cwd, ['worktree', 'add', worktreePath, '-b', branch, baseHash], { timeout: 120000 });
|
|
545
|
+
}
|
|
525
546
|
} catch (e) {
|
|
526
547
|
const check = isGitWorktreeSupported(this.cwd);
|
|
527
548
|
if (!check.supported) {
|
|
@@ -582,8 +603,9 @@ export class WorktreeManager {
|
|
|
582
603
|
|
|
583
604
|
// 5.7 创建 baseline checkpoint(有 dirty baseline 时才创建)
|
|
584
605
|
let baselineCommit = null;
|
|
606
|
+
if (adoptedBranch) baselineCommit = baseHash; // adopt:分支 HEAD 恒作交付 diff 锚(无 dirty overlay 也要锚定)
|
|
585
607
|
if (baselineFiles.length > 0) {
|
|
586
|
-
baselineCommit = this._createBaselineCheckpoint(worktreePath, name);
|
|
608
|
+
baselineCommit = this._createBaselineCheckpoint(worktreePath, name, baselineFiles);
|
|
587
609
|
}
|
|
588
610
|
|
|
589
611
|
// 5.8 依赖供给(change 2026-06-28-worktree-deps-provision)
|
|
@@ -616,12 +638,13 @@ export class WorktreeManager {
|
|
|
616
638
|
depsLockHash: deps.depsLockHash || null,
|
|
617
639
|
depsCheckedAt: deps.depsCheckedAt || null,
|
|
618
640
|
...(deps.depsError ? { depsError: deps.depsError } : {}),
|
|
641
|
+
...(adoptedBranch ? { adoptedBranch: true } : {}),
|
|
619
642
|
};
|
|
620
643
|
|
|
621
644
|
const metaPath = join(worktreePath, META_FILE);
|
|
622
645
|
writeMetaAtomic(metaPath, meta);
|
|
623
646
|
|
|
624
|
-
return { branch, worktreePath, baseHash, mode: meta.mode, syncDiagnostic };
|
|
647
|
+
return { branch, worktreePath, baseHash, mode: meta.mode, syncDiagnostic, ...(adoptedBranch ? { adoptedBranch: true } : {}) };
|
|
625
648
|
}
|
|
626
649
|
|
|
627
650
|
/**
|
|
@@ -719,7 +742,7 @@ export class WorktreeManager {
|
|
|
719
742
|
|
|
720
743
|
let baselineCommit = null;
|
|
721
744
|
if (baselineFiles.length > 0) {
|
|
722
|
-
baselineCommit = this._createBaselineCheckpoint(this.cwd, name);
|
|
745
|
+
baselineCommit = this._createBaselineCheckpoint(this.cwd, name, baselineFiles);
|
|
723
746
|
}
|
|
724
747
|
|
|
725
748
|
const meta = {
|
|
@@ -1017,6 +1040,11 @@ export class WorktreeManager {
|
|
|
1017
1040
|
|
|
1018
1041
|
// 4. 删除分支(fail-closed:task review 引用可达性校验。force 也不绕过——force 语义 =
|
|
1019
1042
|
// 丢弃内容,不含丢弃审计链;确要删用 git branch -D 手动执行)
|
|
1043
|
+
// native-worktree 豁免(坑 worktree-user-branch-conflict,2026-08-24):该模式 meta.branch
|
|
1044
|
+
// 记的是用户自己的检出分支(非 sillyspec 所建),force 也不删——只清 sillyspec 侧注册。
|
|
1045
|
+
if (mode === 'native-worktree') {
|
|
1046
|
+
details.push(`branch kept: native-worktree 模式的 branch(${branch})是用户自己的检出分支,不删除`);
|
|
1047
|
+
} else
|
|
1020
1048
|
// 升级(坑 cleanup-branch-review-anchor-tag,2026-08-21 实证):分支被 review.json 引用
|
|
1021
1049
|
// 时不再只能手动保留——打 `sillyspec-audit/<branch>` 轻量 tag 锚定分支 tip(ref 前缀独立
|
|
1022
1050
|
// 于 sillyspec/* 分支族,doctor 孤儿分支扫描不误伤),commit 经 tag 保持可达(gc 安全),
|
|
@@ -1176,16 +1204,19 @@ export class WorktreeManager {
|
|
|
1176
1204
|
// 数据源不一致——并行会话的活跃变更(meta 已清/in-place/平台模式 meta 在别处)分支会被全局
|
|
1177
1205
|
// doctor --fix 误删。删分支前交叉核对:分支名 ∈ 活跃变更 → 保留(fixable:false 提示人工确认)。
|
|
1178
1206
|
// 先探 DB 文件存在再实例化 ProgressManager(坑7 读路径建库:_ensureDB 不存在会建库污染)。
|
|
1179
|
-
// null =
|
|
1180
|
-
//
|
|
1207
|
+
// null = 无法核对(读失败 或 无进度库——坑 worktree-user-branch-conflict 2026-08-24 收紧:
|
|
1208
|
+
// 无库时无法区分孤儿与用户自建分支,从「照删」改保守保留,git-only 便利让位于不误删);
|
|
1209
|
+
// Set = 进度库可读(空集=无活跃变更,非活跃孤儿仍可删)。
|
|
1181
1210
|
let activeChanges = null;
|
|
1211
|
+
let dbMissing = false;
|
|
1182
1212
|
try {
|
|
1183
1213
|
const specBaseOfWt = dirname(dirname(this.worktreeBase)); // <specBase>(.runtime/worktrees 上两级)
|
|
1184
1214
|
if (existsSync(join(specBaseOfWt, '.runtime', 'sillyspec.db'))) {
|
|
1185
1215
|
const { ProgressManager } = await import('./progress.js');
|
|
1186
1216
|
activeChanges = new Set(new ProgressManager({ specDir: specBaseOfWt }).listChanges(this.cwd));
|
|
1187
1217
|
} else {
|
|
1188
|
-
activeChanges =
|
|
1218
|
+
activeChanges = null;
|
|
1219
|
+
dbMissing = true;
|
|
1189
1220
|
}
|
|
1190
1221
|
} catch (e) {
|
|
1191
1222
|
console.warn(`⚠️ doctor 进度库读取失败,孤儿分支的活跃态交叉核对不可用(保守不自动删): ${e.message}`);
|
|
@@ -1296,6 +1327,17 @@ export class WorktreeManager {
|
|
|
1296
1327
|
(r.ok ? fixed : unfixable).push(r.msg);
|
|
1297
1328
|
}
|
|
1298
1329
|
}
|
|
1330
|
+
// editable install 越界检查(坑 worktree-editable-install-escape,2026-08-25 实证
|
|
1331
|
+
// gen:types 坑):worktree venv 的 editable install 指向 worktree 外(主仓)时,
|
|
1332
|
+
// gen:types/后端命令/pytest 静默加载旧代码零报错——此前靠模块文档人工记忆,内建检查化。
|
|
1333
|
+
// fixable:false(重装决策留给用户:uv sync / pip install -e . 需按项目方式选)。
|
|
1334
|
+
try {
|
|
1335
|
+
const escapes = detectEditableInstallEscape(wtPath);
|
|
1336
|
+
if (escapes.length > 0) {
|
|
1337
|
+
issues.push({ type: 'editable-install-escape', name, fixable: false,
|
|
1338
|
+
detail: `worktree venv 含 ${escapes.length} 个 editable install 指向 worktree 外(${escapes.slice(0, 5).map(e => `${e.pkg}→${e.target}`).join(', ')}${escapes.length > 5 ? ' 等' : ''})——gen:types/后端命令会静默加载 worktree 外旧代码。修复:在 worktree 内重装(uv sync / uv pip install -e .)后重跑生成命令` });
|
|
1339
|
+
}
|
|
1340
|
+
} catch { /* 探测失败不阻断 doctor */ }
|
|
1299
1341
|
}
|
|
1300
1342
|
}
|
|
1301
1343
|
|
|
@@ -1375,9 +1417,20 @@ export class WorktreeManager {
|
|
|
1375
1417
|
continue;
|
|
1376
1418
|
}
|
|
1377
1419
|
if (activeChanges === null) {
|
|
1378
|
-
//
|
|
1420
|
+
// 无法核对活跃态(读失败 或 无进度库):保守不自动删(宁可漏删不可误删——
|
|
1421
|
+
// 坑 worktree-user-branch-conflict:无库时该分支可能是用户自建的,无凭据区分)
|
|
1422
|
+
const why = dbMissing ? '无进度库,无法区分孤儿与用户自建分支' : '进度库不可读,无法交叉核对活跃态'
|
|
1423
|
+
issues.push({ type: 'orphan-branch', name, fixable: false,
|
|
1424
|
+
detail: `分支疑似残留(无对应 meta,且${why}): ${branch}——保守不自动删,人工确认后 git branch -D ${branch}` });
|
|
1425
|
+
continue;
|
|
1426
|
+
}
|
|
1427
|
+
// review 锚点复核(坑 worktree-user-branch-conflict):分支上有 task review base/head
|
|
1428
|
+
// 引用的 commit → 审计链在用,不自动删(与 cleanup 的分支删除审计保护同口径)
|
|
1429
|
+
let reviewRefs = [];
|
|
1430
|
+
try { reviewRefs = this._branchReviewReferences(branch); } catch { reviewRefs = [] }
|
|
1431
|
+
if (reviewRefs.length > 0) {
|
|
1379
1432
|
issues.push({ type: 'orphan-branch', name, fixable: false,
|
|
1380
|
-
detail:
|
|
1433
|
+
detail: `分支无对应 meta 但被 ${reviewRefs.length} 个 task review.json 的 base/head 引用(审计锚点)——保留。确要删除:先归档/迁移审计引用,或手动 git branch -D ${branch}` });
|
|
1381
1434
|
continue;
|
|
1382
1435
|
}
|
|
1383
1436
|
issues.push({ type: 'orphan-branch', name, detail: `分支残留(无对应 meta,且非活跃变更): ${branch}`, fixable: true });
|
|
@@ -1748,9 +1801,13 @@ export class WorktreeManager {
|
|
|
1748
1801
|
* 用于区分 "前置 dirty baseline" 和 "子代理新增改动"
|
|
1749
1802
|
* @param {string} worktreePath
|
|
1750
1803
|
* @param {string} changeName
|
|
1804
|
+
* @param {string[]} [baselineFiles] 主仓并行在途文件清单(_overlayBaseline 返回的 files,
|
|
1805
|
+
* 已排除 .sillyspec)——写入提交信息正文(坑 baseline-checkpoint-opaque-carriage,
|
|
1806
|
+
* 2026-08-24 用户反馈三期③:checkpoint 把主仓并行脏文件带进分支 diff,逐任务归因要靠
|
|
1807
|
+
* 人肉区分;清单入 message 后 `git log` 一眼可辨、可机械排除)
|
|
1751
1808
|
* @returns {string} commit hash
|
|
1752
1809
|
*/
|
|
1753
|
-
_createBaselineCheckpoint(worktreePath, changeName) {
|
|
1810
|
+
_createBaselineCheckpoint(worktreePath, changeName, baselineFiles = []) {
|
|
1754
1811
|
// 使用临时 git identity,避免用户未配置 user.name/user.email 导致失败
|
|
1755
1812
|
const env = {
|
|
1756
1813
|
GIT_AUTHOR_NAME: 'sillyspec',
|
|
@@ -1765,13 +1822,21 @@ export class WorktreeManager {
|
|
|
1765
1822
|
if (!status) {
|
|
1766
1823
|
return gitQuiet(worktreePath, ['rev-parse', 'HEAD']);
|
|
1767
1824
|
}
|
|
1825
|
+
// 提交信息正文列夹带文件(封顶 30 行,超出记总数)——清单与 meta.baselineFiles 同源
|
|
1826
|
+
const CAP = 30
|
|
1827
|
+
const files = Array.isArray(baselineFiles) ? baselineFiles.filter(Boolean) : []
|
|
1828
|
+
const body = files.length > 0
|
|
1829
|
+
? ['\n主仓并行在途文件(非本变更改动,逐任务归因时排除):',
|
|
1830
|
+
...files.slice(0, CAP).map(f => `- ${f}`),
|
|
1831
|
+
...(files.length > CAP ? [`…等共 ${files.length} 个`] : [])].join('\n')
|
|
1832
|
+
: ''
|
|
1768
1833
|
// --no-verify:baseline 是锚点不是交付物,只是把主仓库 dirty 文件快照到 worktree
|
|
1769
1834
|
// 分支上以便区分「前置 baseline」与「子代理新增改动」。它不该触发项目 pre-commit
|
|
1770
1835
|
// hook(如 ruff format),否则主仓库 dirty 文件中任一不达标的会被 hook reformat
|
|
1771
1836
|
// 致 commit 失败 → worktree 创建失败 → execute 无法启动。
|
|
1772
1837
|
execFileSync(
|
|
1773
1838
|
'git',
|
|
1774
|
-
['commit', '--no-verify', '-m', `sillyspec: baseline checkpoint for ${changeName}`],
|
|
1839
|
+
['commit', '--no-verify', '-m', `sillyspec: baseline checkpoint for ${changeName}${body}`],
|
|
1775
1840
|
{ cwd: worktreePath, encoding: 'utf8', stdio: ['pipe','pipe','pipe'], env }
|
|
1776
1841
|
);
|
|
1777
1842
|
const hash = git(worktreePath, ['rev-parse', 'HEAD']);
|
|
@@ -26,4 +26,4 @@ TaskCard 格式规则(必须严格遵守):
|
|
|
26
26
|
- **规范约定字段**(应填但不硬校验,缺失不阻断完成、只影响规范性):author、created_at、priority、depends_on、blocks
|
|
27
27
|
- allowed_paths 非空(至少一个真实源文件路径;回归类 task 无源码改动时填被验证的关键入口文件)
|
|
28
28
|
- acceptance 与 verify 都在 frontmatter 内(漏写会被 plan-postcheck 报「缺少验收标准」/「缺少验证步骤」)
|
|
29
|
-
-
|
|
29
|
+
- **生成方式**:由主 agent 一次性 `sillyspec taskcard <change-name> --all` 预生成全部安全骨架(LF 行尾 + frontmatter 必闭合 + 九个硬校验字段齐全 + author/created_at 自动填;幂等,已存在跳过)。**子代理不要并行跑 taskcard CLI**(2026-08-25 实证撞进度库 SQLite 锁),只往骨架里填语义内容(LF 行尾 + frontmatter 必闭合 + 九个硬校验字段齐全 + author/created_at 自动填),再往骨架里填语义内容——**勿用 Write tool 整文件手写**(CRLF / 漏闭合 `---` / 漏字段的根源,postcheck 报错大多由此来);已存在的卡命令会跳过不覆盖,局部修改用 Edit 定点改
|