sillyspec 3.27.2 → 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/.claude/skills/sillyspec-execute/SKILL.md +8 -0
- package/.claude/skills/sillyspec-quick/SKILL.md +2 -1
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-archive/SKILL.md +87 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-auto/SKILL.md +93 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-commit/SKILL.md +67 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-continue/SKILL.md +47 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-doctor/SKILL.md +76 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-execute/SKILL.md +214 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-explore/SKILL.md +102 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-export/SKILL.md +57 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-init/SKILL.md +177 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-knowledge/SKILL.md +270 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-plan/SKILL.md +156 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-quick/SKILL.md +116 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-resume/SKILL.md +70 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-scan/SKILL.md +79 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-state/SKILL.md +64 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-status/SKILL.md +54 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-verify/SKILL.md +87 -0
- package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-workspace/SKILL.md +175 -0
- package/.claude/worktrees/sillyspec-data-model-research/.gitattributes +2 -0
- package/.claude/worktrees/sillyspec-data-model-research/.husky/pre-push +20 -0
- package/.claude/worktrees/sillyspec-data-model-research/README.md +203 -0
- package/.claude/worktrees/sillyspec-data-model-research/SKILL.md +91 -0
- package/.claude/worktrees/sillyspec-data-model-research/bin/sillyspec.js +2 -0
- package/.claude/worktrees/sillyspec-data-model-research/logo.jpg +0 -0
- package/.claude/worktrees/sillyspec-data-model-research/package-lock.json +977 -0
- package/.claude/worktrees/sillyspec-data-model-research/package.json +39 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/favicon.jpg +0 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/index.html +16 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/logo.jpg +0 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/prototype-dashboard.html +836 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/prototype-overview.html +256 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/package.json +25 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/executor.js +95 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/index.js +679 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/parser.js +526 -0
- package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/watcher.js +357 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/agent-session-log.js +803 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/change-list.js +290 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/change-risk-profile.js +270 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/check-primitives.js +65 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/classify-change.js +109 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/commit-suggest.js +202 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/config-cat.js +88 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/config-schema.js +328 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/constants.js +97 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/contract-matrix.js +480 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/datetime.js +20 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/db-engine.js +55 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/db.js +381 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/decision-distill.js +517 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/backends/local-agent.js +155 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/backends/sillyhub-mcp.js +245 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/probe.js +246 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/strategy.js +121 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/docs-check.js +825 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/docs-debt.js +393 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/docs-gate.js +107 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/doctor-diagnostics.js +1099 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/endpoint-extractor.js +420 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/foreign-declared.js +78 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/fs-atomic.js +73 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/git-helper.js +150 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/hooks/claude-pre-tool-use.cjs +130 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/hooks/worktree-guard.js +892 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/index.js +2798 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/init.js +626 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/knowledge-match.js +231 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/local-detect.js +128 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/local-register.js +104 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/machine-interface.js +543 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/migrate.js +127 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/module-impact.js +166 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/module-resolve.js +222 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/modules.js +580 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/change-registry.js +336 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/consistency-doctor.js +355 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/shared.js +36 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/stage-machine.js +594 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/progress/step-store.js +265 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/progress.js +1304 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/quick-recommend.js +115 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/quicklog.js +852 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/review-tier.js +77 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/command.js +1595 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/complete-handlers.js +1552 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/complete.js +1259 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/concurrent-detect.js +156 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/gates.js +1084 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/multi-repo-context.js +208 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/next.js +128 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/prompt.js +843 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/quick-audit.js +186 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/scan-profile.js +269 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/shared.js +1562 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run/stage.js +584 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/run.js +23 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/scan-diff.js +388 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/scan-postcheck.js +512 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/scan-staleness.js +95 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/setup.js +414 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/sillyhub-mcp/client.js +597 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/sillyhub-mcp/config.js +62 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/spec-dir-typo.js +50 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/spec-sync.js +358 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract-engine.js +171 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract-spec.js +550 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract.js +889 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stage-review.js +597 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/archive.js +199 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/brainstorm-auto.js +236 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/brainstorm.js +617 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/cmd-existence.js +145 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/doctor.js +540 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/execute.js +1275 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/explore.js +43 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/index.js +29 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/knowledge.js +514 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/plan-postcheck.js +1344 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/plan.js +650 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/quick.js +167 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/scan.js +608 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/status.js +64 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/stages/verify.js +246 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/sync.js +1470 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/task-review.js +1498 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/taskcard.js +210 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/verify-postcheck.js +1718 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/verify-probes.js +277 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/version.js +19 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/workflow.js +600 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/workspace.js +125 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/worktree-apply.js +1446 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/worktree-deps.js +576 -0
- package/.claude/worktrees/sillyspec-data-model-research/src/worktree.js +1715 -0
- package/.claude/worktrees/sillyspec-data-model-research/templates/claude-instruction.md +23 -0
- package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/taskcard-rules.md +28 -0
- package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/testcase-design.md +12 -0
- package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/verify-probes.md +32 -0
- package/.claude/worktrees/sillyspec-data-model-research/templates/workflows/archive-impact.yaml +79 -0
- package/.claude/worktrees/sillyspec-data-model-research/templates/workflows/scan-docs.yaml +132 -0
- 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/eval/README.md +86 -0
- package/eval/eval.config.json +28 -0
- package/eval/lib/adapters/cli.mjs +63 -0
- package/eval/lib/adapters/dryrun.mjs +27 -0
- package/eval/lib/config.mjs +82 -0
- package/eval/lib/db.mjs +76 -0
- package/eval/lib/report.mjs +120 -0
- package/eval/lib/runner.mjs +157 -0
- package/eval/lib/tasks.mjs +73 -0
- package/eval/lib/util.mjs +107 -0
- package/eval/lib/workdir.mjs +30 -0
- package/eval/run.mjs +83 -0
- package/eval/tasks/001-slugify/files/package.json +6 -0
- package/eval/tasks/001-slugify/solution/src/slugify.mjs +26 -0
- package/eval/tasks/001-slugify/task.json +6 -0
- package/eval/tasks/001-slugify/verify.mjs +28 -0
- package/eval/tasks/002-each-day/files/package.json +6 -0
- package/eval/tasks/002-each-day/files/src/dates.mjs +4 -0
- package/eval/tasks/002-each-day/solution/src/dates.mjs +30 -0
- package/eval/tasks/002-each-day/task.json +6 -0
- package/eval/tasks/002-each-day/verify.mjs +31 -0
- package/eval/tasks/003-word-report/files/data/notes.txt +5 -0
- package/eval/tasks/003-word-report/files/package.json +6 -0
- package/eval/tasks/003-word-report/solution/bin/report.mjs +36 -0
- package/eval/tasks/003-word-report/task.json +6 -0
- package/eval/tasks/003-word-report/verify.mjs +38 -0
- package/package.json +4 -2
- package/src/agent-session-log.js +45 -5
- package/src/change-risk-profile.js +83 -14
- package/src/config-schema.js +18 -3
- package/src/contract-matrix.js +90 -29
- package/src/datetime.js +20 -0
- package/src/decision-distill.js +517 -0
- package/src/dispatch/backends/local-agent.js +4 -4
- package/src/docs-check.js +227 -0
- package/src/docs-debt.js +187 -24
- package/src/doctor-diagnostics.js +102 -2
- package/src/endpoint-extractor.js +41 -52
- package/src/foreign-declared.js +155 -0
- package/src/index.js +95 -13
- package/src/init.js +9 -7
- package/src/knowledge-match.js +107 -6
- package/src/local-register.js +7 -1
- package/src/module-resolve.js +12 -1
- package/src/modules.js +2 -1
- package/src/plan-adopt-waves.js +126 -0
- package/src/quicklog.js +50 -4
- package/src/run/command.js +124 -44
- package/src/run/complete-handlers.js +48 -19
- package/src/run/complete.js +5 -2
- package/src/run/gates.js +104 -22
- package/src/run/prompt.js +84 -42
- package/src/run/quick-audit.js +11 -0
- package/src/run/shared.js +152 -10
- package/src/run/stage.js +8 -5
- package/src/run.js +2 -2
- package/src/scan-postcheck.js +30 -17
- package/src/stage-contract-spec.js +3 -2
- package/src/stage-contract.js +68 -4
- package/src/stages/archive.js +53 -12
- package/src/stages/brainstorm-auto.js +50 -1
- package/src/stages/brainstorm.js +13 -0
- package/src/stages/doctor.js +157 -1
- package/src/stages/execute.js +32 -6
- package/src/stages/plan-postcheck.js +76 -17
- package/src/stages/plan.js +9 -4
- package/src/stages/quick.js +6 -1
- package/src/stages/verify.js +32 -15
- package/src/sync.js +41 -3
- package/src/task-review.js +2 -2
- package/src/taskcard-placeholders.js +21 -0
- package/src/taskcard.js +43 -1
- package/src/verify-postcheck.js +315 -13
- package/src/verify-probes.js +73 -10
- package/src/worktree-apply.js +569 -227
- package/src/worktree-deps.js +150 -1
- package/src/worktree.js +285 -77
- package/templates/prompts/taskcard-rules.md +2 -1
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SillyHub MCP 后端派发指令模板(task-dispatcher 抽象层)— task-04
|
|
3
|
+
*
|
|
4
|
+
* 定位(D-007):dispatcher **不是 JS 执行体**。SillyHub 的 MCP tool(create_mission /
|
|
5
|
+
* dispatch_worker / list_workers / report_progress)只有 agent 能调,CLI(Node)进程调不了。
|
|
6
|
+
* 所以本文件 = **派发指令模板生成器**:生成注入 execute prompt 的「派发指令文本」,告诉
|
|
7
|
+
* agent 如何经 SillyHub MCP tool 创建 mission / 派 worker / 轮询终态 / kill lease / 回收;
|
|
8
|
+
* 实际 MCP tool 调用由 agent 执行,本模块不调任何 tool,也不直接 import client.js。
|
|
9
|
+
*
|
|
10
|
+
* 路径 A 探测翻真(task-11,D-005 + R-04):`isPathASupported()` 读 probe.js 预热的
|
|
11
|
+
* tools/list 探测缓存——`dispatch_worker.inputSchema.properties` 含 `worktree_path` 与
|
|
12
|
+
* `worker_prompt` → true;任一缺失 / 未预热 / 探测失败 → false(保守回退 Local,**绝不硬试路径A**)。
|
|
13
|
+
* env 备选(spike-01 不通过时):`process.env.SILLYHUB_PATH_A==='1'` → true(手动标记 daemon 侧
|
|
14
|
+
* 已落地)。探测预热住 probe.js `probeSillyHub`(daemon 可达后 best-effort 调 `client.listTools()`),
|
|
15
|
+
* 本模块只缓存探测结果 + 同步读——保持 `isPathASupported()` sync 签名不改 strategy.js /
|
|
16
|
+
* execute.js `getDispatchMode` 调用点(探测 = async,读缓存 = sync,解耦)。
|
|
17
|
+
*
|
|
18
|
+
* 铁律:
|
|
19
|
+
* - converge_mission 不出现在任何指令文本(D-004,SillySpec 自己 apply);JSDoc 仅作开发者锚点。
|
|
20
|
+
* - worker 不 git commit,改动留 worktree 工作区(D-003@v2 第三处 UB-1,worker_prompt 覆写)。
|
|
21
|
+
* - 不直接调 client,只生成指令由 agent 执行 MCP tool 调用(探测由 probe.js 调 client,本模块仅读缓存)。
|
|
22
|
+
* - 路径 A 检测保守:不支持即回退,不硬试。
|
|
23
|
+
* - 兼容 Win/Linux/macOS:本文件无路径拼接 / 无平台分支,纯模板生成。
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 路径 A 未落地时 strategy.js 拼到降级提示里的原因说明。
|
|
28
|
+
*
|
|
29
|
+
* 属指令文本(会注入 prompt),故不含 converge_mission 字面(D-004)。strategy 在探测到
|
|
30
|
+
* isPathASupported()=false 时把此串拼进降级提示,引导 agent 回退 Local。
|
|
31
|
+
*/
|
|
32
|
+
export const PATH_A_DOWNGRADE_REASON =
|
|
33
|
+
'路径A 跨仓未落地:dispatch_worker 不支持 worktree_path / render_worker_prompt 仍硬编码 commit,本次派发回退 Local'
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* SillyHub 后端结果回收约定(worker 终态 → review.json,D-004 + UB-1)。
|
|
37
|
+
*
|
|
38
|
+
* 与 Local 后端回收同构(R-07 屏蔽后端差异):worker 终态后 SillySpec 主体对 worktree
|
|
39
|
+
* **工作区**做 git diff 写 review.json。差异在 worker 侧产出形态——SillyHub worker **绝不
|
|
40
|
+
* git commit**(路径A 第三处 UB-1,worker_prompt 已覆写覆盖 daemon 默认 commit 行为),改动
|
|
41
|
+
* 全留工作区;合并回主干由 SillySpec 自己 apply(worktree-apply 全门控保留),**不调 SillyHub
|
|
42
|
+
* 合并 tool**(D-004)。
|
|
43
|
+
*
|
|
44
|
+
* 抽成独立常量供 strategy.js 在拼接降级 / 回收段时复用,避免 Local 与 SillyHub 两份回收文案
|
|
45
|
+
* 重复维护。含 `<runId>` 占位符,使用方按实际 contract.runId 替换(见 renderSillyHubInstruction)。
|
|
46
|
+
*/
|
|
47
|
+
export const SILLYHUB_RECYCLE_RULE = `### 回收约定(worker 终态 → review.json,SillySpec 自己 apply)
|
|
48
|
+
|
|
49
|
+
worker 终态后,**回收不依赖 worker git commit,也不调 SillyHub 合并 tool**:SillySpec 主体
|
|
50
|
+
(你,调度 agent)对 worktree **工作区**做 git diff,按既有 task-review 契约写 review.json;
|
|
51
|
+
合并回主干由 SillySpec 自己 apply(worktree-apply / Review Gate / assess 全门控保留)。
|
|
52
|
+
|
|
53
|
+
1. worker 终态(completed / failed / killed)后,读取该 task 在 worktree 工作区的 git diff
|
|
54
|
+
(task 派发 → 终态的工作区变更;worker 未 commit 故全在工作区,不依赖 worker 自报)
|
|
55
|
+
2. 对照 plan.md 与 tasks/task-XX.md 检查实现(不信任 worker 自报,只看当前 task 的 diff)
|
|
56
|
+
3. 写入 review.json:\`.runtime/execute-runs/<runId>/tasks/task-XX/review.json\`
|
|
57
|
+
4. review.json 写入成功后,该 task 的派发回收完成;合并走 SillySpec apply,不调 SillyHub 合并 tool
|
|
58
|
+
|
|
59
|
+
review.json 复用既有 task-review 契约(必填字段:schemaVersion / task / base / head /
|
|
60
|
+
changedFiles / specVerdict / qualityVerdict / reviewerNotes / requiredEvidence),不发明
|
|
61
|
+
新契约。回收屏蔽后端差异:无论 Local 还是 SillyHub worker,SillySpec 都自己 diff worktree
|
|
62
|
+
工作区写 review.json,再自己 apply(D-004)。`
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 路径A 探测结果缓存(task-11):probe.js `probeSillyHub` 预热 → `isPathASupported` 同步读。
|
|
66
|
+
*
|
|
67
|
+
* 保持 `isPathASupported` sync 签名(strategy.js / execute.js `getDispatchMode` 同步调,不改
|
|
68
|
+
* 调用点):探测是 async(probe.js 调 client.listTools),读缓存是 sync。未预热(probe 未跑 /
|
|
69
|
+
* 探测失败)→ 默认 false(保守 R-04,不硬试路径A)。模块级单例,进程内有效;测试用
|
|
70
|
+
* `clearPathAProbeCache` 隔离。
|
|
71
|
+
*/
|
|
72
|
+
let _pathAProbe = { probed: false, supported: false };
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 写路径A 探测结果(probe.js `probeSillyHub` 在 daemon 可达后预热调用)。
|
|
76
|
+
* @param {boolean} supported - dispatch_worker schema 含 worktree_path + worker_prompt
|
|
77
|
+
*/
|
|
78
|
+
export function setPathAProbeResult(supported) {
|
|
79
|
+
_pathAProbe = { probed: true, supported: !!supported };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 清空路径A 探测缓存(测试隔离用;生产中进程重启 / 不需要 TTL,probe 每次可达都重预热带新值)。
|
|
84
|
+
*/
|
|
85
|
+
export function clearPathAProbeCache() {
|
|
86
|
+
_pathAProbe = { probed: false, supported: false };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 路径 A 是否已落地(dispatch_worker 支持 caller worktree + worker_prompt 覆写)。
|
|
91
|
+
*
|
|
92
|
+
* task-11 翻真(D-005 + R-04):读 probe.js 预热的探测结果(probeSillyHub 经 `tools/list` 查
|
|
93
|
+
* `dispatch_worker.inputSchema.properties` 含 `worktree_path` 与 `worker_prompt`)。保持 sync
|
|
94
|
+
* 签名(strategy.js / execute.js `getDispatchMode` 同步调),探测预热在 probe 流程。
|
|
95
|
+
*
|
|
96
|
+
* 判定顺序(保守,**绝不硬试路径A**):
|
|
97
|
+
* 1. **env 强制开启**:`process.env.SILLYHUB_PATH_A==='1'` → true(spike-01 不通过 / tools/list
|
|
98
|
+
* 不可达时的备选,手动标记 daemon 侧路径A 已落地;优先级最高,让探测不可信时也能启用)
|
|
99
|
+
* 2. 否则读探测缓存:probe 预热且 dispatch_worker schema 全命中(worktree_path + worker_prompt)→ true
|
|
100
|
+
* 3. 未预热 / 探测失败 / schema 缺字段 → false(保守回退 Local,R-04)
|
|
101
|
+
*
|
|
102
|
+
* @returns {boolean} 路径A 可用 → true;否则 false(保守不硬试,R-04)
|
|
103
|
+
*/
|
|
104
|
+
export function isPathASupported() {
|
|
105
|
+
// 1. env 强制开启(spike-01 备选):优先级最高,探测不可信 / 不可达时手动启用
|
|
106
|
+
if (process.env.SILLYHUB_PATH_A === '1') return true;
|
|
107
|
+
// 2. 读 probe 预热的 schema 探测缓存(未预热 → supported=false,保守 R-04 不硬试)
|
|
108
|
+
return _pathAProbe.supported;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @typedef {import('./local-agent.js').DispatchContract} DispatchContract
|
|
113
|
+
* @see design.md「接口定义」DispatchContract:brief / worktreePath / branch / allowedPaths /
|
|
114
|
+
* readOnly / modelHint / agentProfileHint / runId / missionId(与 task-03 Local 后端同构)
|
|
115
|
+
*/
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 渲染 SillyHub 后端派发指令文本(路径 A 落地后由 strategy.js 注入 execute prompt)。
|
|
119
|
+
*
|
|
120
|
+
* 纯模板生成器:不校验 contract 完整性、不调任何 MCP tool / client,只把 contract 字段拼成
|
|
121
|
+
* 可注入 execute prompt 的中文指令字符串。contract 缺字段时回退到醒目占位符(`<未提供 ...>`),
|
|
122
|
+
* 让 agent 在 prompt 里一眼看到派发前必须补齐的参数,而非静默产出错位指令。
|
|
123
|
+
*
|
|
124
|
+
* 指令含四要点(D-008 / D-003@v2 / UB-6 / D-004):
|
|
125
|
+
* 1. **一 Wave 一 mission**:agent 先调 create_mission(objective/change_id/budget_usd per Wave),
|
|
126
|
+
* Wave 内 task→worker 并行 dispatch,Wave 间 mission 串行
|
|
127
|
+
* 2. **dispatch_worker 参数**:mission_id/objective/worktree_path/branch/read_only/model/
|
|
128
|
+
* agent_profile_id/worker_prompt(覆写:worker 不 git commit)
|
|
129
|
+
* 3. **终态轮询**:agent 轮询 list_workers,per-worker 超时 → report_progress 标记 + kill lease
|
|
130
|
+
* 防双写 + fallback Local 重派
|
|
131
|
+
* 4. **回收约定**:worker 不 commit,SillySpec 自己 diff worktree 写 review.json,不调合并 tool
|
|
132
|
+
*
|
|
133
|
+
* @param {DispatchContract} [contract] - 派发契约(见 design.md DispatchContract 接口定义)
|
|
134
|
+
* @returns {string} 注入 execute prompt 的 SillyHub 后端派发指令文本
|
|
135
|
+
*/
|
|
136
|
+
export function renderSillyHubInstruction(contract) {
|
|
137
|
+
const c = contract || {}
|
|
138
|
+
const worktreePath = c.worktreePath
|
|
139
|
+
const branch = c.branch
|
|
140
|
+
const allowedPaths = Array.isArray(c.allowedPaths) ? c.allowedPaths : null
|
|
141
|
+
const readOnly = !!c.readOnly
|
|
142
|
+
const brief = c.brief
|
|
143
|
+
const runId = c.runId
|
|
144
|
+
const modelHint = c.modelHint
|
|
145
|
+
const agentProfileHint = c.agentProfileHint
|
|
146
|
+
const missionId = c.missionId
|
|
147
|
+
|
|
148
|
+
// 回收约定:替换 <runId> 占位符;runId 缺失保留占位符让 agent 警觉
|
|
149
|
+
const recycleRule = SILLYHUB_RECYCLE_RULE.replaceAll(
|
|
150
|
+
'<runId>',
|
|
151
|
+
runId ? String(runId) : '<未提供 runId>'
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
// 各字段缺省醒目占位(派发前必须补齐的参数一眼可见)
|
|
155
|
+
const worktreeDisplay = worktreePath
|
|
156
|
+
? String(worktreePath)
|
|
157
|
+
: '<未提供 contract.worktreePath — 派发前必须补齐,否则 worker cwd 错位>'
|
|
158
|
+
const branchDisplay = branch
|
|
159
|
+
? String(branch)
|
|
160
|
+
: '<未提供 contract.branch — 派发前必须补齐 worktree 当前分支>'
|
|
161
|
+
const briefDisplay = brief
|
|
162
|
+
? String(brief)
|
|
163
|
+
: '<未提供 contract.brief — 派发前必须补齐任务目标与蓝图路径>'
|
|
164
|
+
const missionDisplay = missionId
|
|
165
|
+
? String(missionId)
|
|
166
|
+
: '<本 Wave mission_id — 先调 create_mission 拿到后填入>'
|
|
167
|
+
|
|
168
|
+
const allowedLine = allowedPaths && allowedPaths.length
|
|
169
|
+
? `- 本次任务边界(allowedPaths):${allowedPaths.map(p => `\`${p}\``).join(' / ')}(SillySpec 侧 assess/apply 校验,SillyHub 不物化)`
|
|
170
|
+
: '- 本次任务边界(allowedPaths):未提供,按蓝图 tasks/task-XX.md 的 allowed_paths 为准(SillySpec 侧校验)'
|
|
171
|
+
const modelLine = modelHint
|
|
172
|
+
? `- \`model\`:\`${modelHint}\`(来自 tasks.md [model:xxx],映射到 SillyHub model)`
|
|
173
|
+
: '- `model`:未提供(contract.modelHint 缺),沿用 daemon 默认模型'
|
|
174
|
+
const profileLine = agentProfileHint
|
|
175
|
+
? `- \`agent_profile_id\`:\`${agentProfileHint}\`(来自 contract.agentProfileHint)`
|
|
176
|
+
: '- `agent_profile_id`:未提供(contract.agentProfileHint 缺),沿用 daemon 默认 profile'
|
|
177
|
+
|
|
178
|
+
const modeLine = readOnly
|
|
179
|
+
? '**只读模式(read_only=true)**:worker 只读 / 审查 / 写 review.json,不改源码(注:第一波 execute task 用写模式,acceptance 强制 tier=self,R-03)'
|
|
180
|
+
: '**写模式(read_only=false)**:worker 按蓝图实现源码(TDD),但**绝不 git commit**(见下方 worker_prompt 覆写)'
|
|
181
|
+
|
|
182
|
+
return `### 派发后端:SillyHub MCP — 路径 A 落地后启用
|
|
183
|
+
|
|
184
|
+
本指令用 **SillyHub MCP tool** 派 worker 执行任务(路径 A 落地后由 strategy.js 注入;当前
|
|
185
|
+
\`isPathASupported()\` 返回 false,strategy 不会调本指令,而是降级提示 + 回退 Local)。
|
|
186
|
+
|
|
187
|
+
**你的角色是调度者 + 审查者,不要自己写代码。** 实际 MCP tool 调用由你(调度 agent)执行:
|
|
188
|
+
创建 mission → 派 worker → 轮询终态 → kill 异常 lease → 回收 diff。
|
|
189
|
+
|
|
190
|
+
### 一 Wave 一 mission(D-008)
|
|
191
|
+
|
|
192
|
+
1. **Wave 开始先建 mission**:调 \`create_mission\` tool 为本 Wave 创建独立 mission
|
|
193
|
+
- 参数:\`objective\`(本 Wave 目标)/ \`change_id\`(SillySpec change id)/ \`budget_usd\`(per Wave 预算)
|
|
194
|
+
- 返回 \`mission_id\`,记下供本 Wave 内所有 task→worker 复用:${missionDisplay}
|
|
195
|
+
2. **Wave 内 task → worker 并行 dispatch**:同 Wave 多 task 各调一次 \`dispatch_worker\`,共用本 Wave 的 mission_id(保 Wave 内并行)
|
|
196
|
+
3. **Wave 间 mission 串行**:上一 Wave 全 worker 终态 + 回收完成后,下一 Wave 起新 mission(不把全 Wave 塞一个 mission,否则失串行)
|
|
197
|
+
|
|
198
|
+
### dispatch_worker 参数(每个 task 调一次)
|
|
199
|
+
|
|
200
|
+
调 \`dispatch_worker\` tool,参数(snake_case 对齐 SillyHub schema;对照 client.js \`dispatchWorker\` 签名):
|
|
201
|
+
- \`mission_id\`:${missionDisplay}
|
|
202
|
+
- \`objective\`:${briefDisplay}
|
|
203
|
+
- \`worktree_path\`:\`${worktreeDisplay}\`(路径A:worker cwd = SillySpec worktree,D-002)
|
|
204
|
+
- \`branch\`:\`${branchDisplay}\`(路径A:worktree 当前分支)
|
|
205
|
+
- \`read_only\`:\`${readOnly}\`
|
|
206
|
+
${modelLine}
|
|
207
|
+
${profileLine}
|
|
208
|
+
- \`worker_prompt\`:**必须覆写**(见下方,覆盖 daemon 默认 render_worker_prompt 的 git commit 行为)
|
|
209
|
+
|
|
210
|
+
${modeLine}
|
|
211
|
+
${allowedLine}
|
|
212
|
+
|
|
213
|
+
### worker_prompt 覆写(D-003@v2 第三处,UB-1 关键)
|
|
214
|
+
|
|
215
|
+
dispatch_worker 的 \`worker_prompt\` 参数必须传以下覆写文本(路径A 第三处:daemon 默认
|
|
216
|
+
\`render_worker_prompt\` 硬编码 \`git add -A && git commit\`,会污染 sillyspec/<change> 分支历史
|
|
217
|
+
+ 撞 D-004;用 worker_prompt 覆写让 caller 控制 commit 行为):
|
|
218
|
+
|
|
219
|
+
\`\`\`
|
|
220
|
+
你是 SillySpec execute 的 worker,在指定 worktree 干活。铁律:
|
|
221
|
+
1. 按 objective + 蓝图(tasks/task-XX.md / design.md)实现任务(TDD:先读后写 → 写测试 → 写实现)
|
|
222
|
+
2. **绝不执行 git add / git commit / git push**:改动留在工作区(staged 或 unstaged 都行),
|
|
223
|
+
交 SillySpec 主体 git diff 回收(D-004)
|
|
224
|
+
3. 不超出 allowedPaths,不碰其他 task 的文件
|
|
225
|
+
4. 完成后输出简短自报(改了哪些文件 / 跑了哪些测试 / 是否存疑),不自报 review.json(SillySpec 自己写)
|
|
226
|
+
5. 写测试时遵循下方「测试用例设计」规则:
|
|
227
|
+
|
|
228
|
+
{{include: testcase-design}}
|
|
229
|
+
\`\`\`
|
|
230
|
+
|
|
231
|
+
### 终态轮询 + 超时 kill lease(UB-6)
|
|
232
|
+
|
|
233
|
+
dispatch 后在当前 step 内主动轮询,不阻塞等待:
|
|
234
|
+
|
|
235
|
+
1. **轮询 \`list_workers\`**:间隔默认 15s,可配 local.yaml \`dispatch.poll_interval_ms\`
|
|
236
|
+
- 入参 \`mission_id\`;关注每个 worker 的 status(pending → running → completed/failed)
|
|
237
|
+
2. **per-worker 超时**:默认可配 local.yaml \`dispatch.worker_timeout_ms\`;超时 → 三步兜底:
|
|
238
|
+
- a. **标记**:调 \`report_progress\` tool(worker_id + 超时说明)记录该 worker 超时
|
|
239
|
+
- b. **kill lease 防双写**:调 \`report_progress\` tool(worker_id + \`kill: true\` + marker)终止 lease
|
|
240
|
+
(等价 client.js \`killLease(workerId)\` 路径;SillyHub 当前无专用 kill tool,路径A 落地后升级为专用 kill)
|
|
241
|
+
- c. **fallback Local 重派**:该 task 改用本机 Agent tool 重派(调 \`sillyspec dispatch hint\` 走 Local 模板)
|
|
242
|
+
3. **worker 终态(completed / failed / killed)** → 进入下方回收约定
|
|
243
|
+
|
|
244
|
+
${recycleRule}`
|
|
245
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SillyHub 能力探测(D-005 双后端 fallback 的关键判定)
|
|
3
|
+
*
|
|
4
|
+
* 定位(design.md §Phase4 / §接口定义):probeSillyHub 是"能力探测",决定 dispatch 用 SillyHub
|
|
5
|
+
* 还是 Local 后端。无 MCP 配置 / daemon 不可达 / worktree 越界 → 返回 unavailable → 调用方全程
|
|
6
|
+
* Local(零回归)。纯 JS 可测(task-08 mock client 验三分支);不直接碰 lease,只感知 daemon 连通性。
|
|
7
|
+
*
|
|
8
|
+
* 铁律(design.md R-06 / task-01 constraints):
|
|
9
|
+
* - 探测失败保守 fallback(返回 unavailable),**绝不抛异常阻断 execute**(client 契约异常时再兜 try/catch)。
|
|
10
|
+
* - TTL 可配(local.yaml dispatch.probe_ttl_ms),不硬编码。
|
|
11
|
+
* - 无 MCP 配置时同步快速路径返回,**不发网络**(零回归的关键)。
|
|
12
|
+
* - 负面结果 TTL 缓存(daemon 抖动期免反复探测,R-06);正面结果不缓存(让 daemon 恢复/退化及时反映)。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
16
|
+
import { join, relative, isAbsolute, sep } from 'node:path';
|
|
17
|
+
import jsYaml from 'js-yaml';
|
|
18
|
+
import { SillyHubMcpClient } from '../sillyhub-mcp/client.js';
|
|
19
|
+
import { readMcpConfig } from '../sillyhub-mcp/config.js';
|
|
20
|
+
import { setPathAProbeResult } from './backends/sillyhub-mcp.js';
|
|
21
|
+
|
|
22
|
+
/** 默认负面缓存 TTL(毫秒)。可被 local.yaml dispatch.probe_ttl_ms 或 ttlMs 参数覆盖。 */
|
|
23
|
+
export const DEFAULT_PROBE_TTL_MS = 60_000;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* HUB-12a:probe 总超时。链路最坏 initialize(10s) + tools/call(10s) + tools/list(10s) 串行,
|
|
27
|
+
* 慢/挂 daemon 下此前要 ~30-50s 才降级,阻塞 execute。默认 25s 截断(可达 daemon 毫秒级
|
|
28
|
+
* 响应,不受影响);opts.totalTimeoutMs 可覆盖。超时按 daemon-unreachable 语义进负面缓存。
|
|
29
|
+
*/
|
|
30
|
+
const DEFAULT_PROBE_TOTAL_TIMEOUT_MS = 25_000;
|
|
31
|
+
|
|
32
|
+
// 负面缓存:configFingerprint → { result: {available,reason}, expiresAt }
|
|
33
|
+
// 仅缓存网络派生的 unavailable 结果(daemon-unreachable),避免 daemon 抖动期反复探测(R-06)。
|
|
34
|
+
// no-config 由 env 同步派生(不入缓存);worktree-outside-root 依赖路径入参(每次重算,不入缓存)。
|
|
35
|
+
const negativeCache = new Map();
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 读 local.yaml 的 dispatch.probe_ttl_ms(best-effort,绝不抛)。
|
|
39
|
+
* 文件不存在 / 解析失败 / 无 dispatch 段 / 非正数 → 返回 undefined(调用方降级默认值)。
|
|
40
|
+
* 读法参考 src/sync.js readLocalYaml 的 best-effort 风格,但用 js-yaml 解析嵌套段(更稳健)。
|
|
41
|
+
*/
|
|
42
|
+
function readProbeTtlFromLocalYaml(cwd) {
|
|
43
|
+
try {
|
|
44
|
+
const p = join(cwd, '.sillyspec', 'local.yaml');
|
|
45
|
+
if (!existsSync(p)) return undefined;
|
|
46
|
+
const doc = jsYaml.load(readFileSync(p, 'utf8'));
|
|
47
|
+
if (!doc || typeof doc !== 'object') return undefined;
|
|
48
|
+
const dispatch = doc.dispatch;
|
|
49
|
+
if (!dispatch || typeof dispatch !== 'object') return undefined;
|
|
50
|
+
const v = dispatch.probe_ttl_ms;
|
|
51
|
+
return typeof v === 'number' && v > 0 ? v : undefined;
|
|
52
|
+
} catch {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 解析生效 TTL。优先级:显式 ttlMs 参数 > local.yaml dispatch.probe_ttl_ms > 默认常量。
|
|
59
|
+
*/
|
|
60
|
+
function resolveTtl(ttlMs, cwd) {
|
|
61
|
+
if (typeof ttlMs === 'number' && ttlMs > 0) return ttlMs;
|
|
62
|
+
const fromYaml = readProbeTtlFromLocalYaml(cwd);
|
|
63
|
+
if (typeof fromYaml === 'number') return fromYaml;
|
|
64
|
+
return DEFAULT_PROBE_TTL_MS;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 配置指纹(负面缓存 key)。用 local.yaml mcp 段或 env 的 URL 标识 daemon 身份;
|
|
69
|
+
* token 不入 key(避免敏感信息驻留缓存,保密语义不变)。token 轮换后最迟 TTL 内自动失效重探。
|
|
70
|
+
*/
|
|
71
|
+
function configFingerprint(cwd) {
|
|
72
|
+
return readMcpConfig(cwd)?.url || '';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 判断 worktreePath 是否在 rootPath 目录内(含相等,R-08)。
|
|
77
|
+
* 段级 '..' 精确匹配:兼容跨盘符(Windows 不同盘 → rel 绝对路径 → 越界)与字面 '..' 前缀目录名
|
|
78
|
+
* (如 '..foo' 目录不会被误判越界,区别于粗放的 startsWith('..') 写法)。
|
|
79
|
+
*/
|
|
80
|
+
function isWithinRoot(rootPath, worktreePath) {
|
|
81
|
+
const rel = relative(rootPath, worktreePath);
|
|
82
|
+
if (rel === '') return true; // worktree === root,边界视为在内
|
|
83
|
+
if (isAbsolute(rel)) return false; // 跨盘符(Windows)→ rel 绝对路径 → 越界
|
|
84
|
+
const segments = rel.split(sep);
|
|
85
|
+
return !segments.some((s) => s === '..');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 从 tools/list 返回的 tools 数组探测路径A 是否支持(task-11,D-005)。
|
|
90
|
+
*
|
|
91
|
+
* 命中条件(全中才 true,任一缺失 → false,保守 R-04):
|
|
92
|
+
* - 找到 `name === 'dispatch_worker'` 的 tool
|
|
93
|
+
* - 其 `inputSchema.properties` 同时声明 `worktree_path` 与 `worker_prompt`(caller-worktree +
|
|
94
|
+
* prompt 覆写两处 daemon 侧落地标志,design §7.3)
|
|
95
|
+
*
|
|
96
|
+
* 纯函数(不调网络),独立可测(task-11 三分支:含字段→true / 缺字段→false / 异常输入→false)。
|
|
97
|
+
* @param {Array<{name:string, inputSchema?:object}>|null} tools - client.listTools() 返回的 tools 数组
|
|
98
|
+
* @returns {boolean} 路径A schema 全命中 → true;否则 false
|
|
99
|
+
*/
|
|
100
|
+
export function detectPathAFromTools(tools) {
|
|
101
|
+
if (!Array.isArray(tools)) return false;
|
|
102
|
+
const dw = tools.find((t) => t && t.name === 'dispatch_worker');
|
|
103
|
+
if (!dw) return false;
|
|
104
|
+
const props = dw.inputSchema && dw.inputSchema.properties;
|
|
105
|
+
if (!props || typeof props !== 'object') return false;
|
|
106
|
+
return Boolean(props.worktree_path) && Boolean(props.worker_prompt);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 能力探测:决定 dispatch 是否可用 SillyHub 后端。
|
|
111
|
+
*
|
|
112
|
+
* 决策顺序(design.md §Phase4 / task-01 implementation):
|
|
113
|
+
* 1. env 快速路径(无网络):缺 URL/TOKEN → no-config
|
|
114
|
+
* 2. 负面缓存命中且未过 TTL → 直接返回缓存(无网络,R-06)
|
|
115
|
+
* 3. 连通性探测 client.probeDaemon():false → 缓存 daemon-unreachable 并返回
|
|
116
|
+
* 3.5 路径A schema 预热 + root_path 获取(HUB-12a 合并):daemon 可达后**一次**
|
|
117
|
+
* tools/list 同时喂 detectPathAFromTools 预热与 root_path 越界校验(此前
|
|
118
|
+
* listTools + getRootPath 各发一次同 method 请求)
|
|
119
|
+
* 4. worktreePath 越界校验(R-08 best-effort)
|
|
120
|
+
* 5. 全通过 → available=true(不缓存正面结果)
|
|
121
|
+
*
|
|
122
|
+
* 整链路受 totalTimeoutMs 总超时保护(HUB-12a):超时按 daemon-unreachable 进负面缓存。
|
|
123
|
+
*
|
|
124
|
+
* @param {object} [opts]
|
|
125
|
+
* @param {object} [opts.client] - SillyHubMcpClient 实例;缺省 new SillyHubMcpClient()(经 readMcpConfig 读 local.yaml mcp 段 + env)
|
|
126
|
+
* @param {string} [opts.worktreePath] - SillySpec worktree 绝对路径(root_path 校验用)
|
|
127
|
+
* @param {string} [opts.rootPath] - daemon ws.root_path;未传(undefined)时 best-effort 从 daemon
|
|
128
|
+
* 拿(task-12 #5),拿不到则跳过越界校验(不判 unavailable)
|
|
129
|
+
* @param {number} [opts.ttlMs] - 负面缓存 TTL 覆盖(优先于 local.yaml)
|
|
130
|
+
* @param {number} [opts.totalTimeoutMs] - 总超时覆盖(默认 25s,HUB-12a)
|
|
131
|
+
* @returns {Promise<{ available: boolean, reason?: string }>}
|
|
132
|
+
*/
|
|
133
|
+
export async function probeSillyHub({ client, worktreePath, rootPath, ttlMs, totalTimeoutMs } = {}) {
|
|
134
|
+
// 1. 同步快速路径:无 local.yaml mcp 段且 env 缺凭据即 unavailable(不发网络,零回归关键)。
|
|
135
|
+
// readMcpConfig 纯 fs + env 读(best-effort 不抛不发网络),返回 null = 两源都缺 → no-config。
|
|
136
|
+
if (!readMcpConfig(process.cwd())) {
|
|
137
|
+
return { available: false, reason: 'no-config' };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 2. 负面缓存命中(未过 TTL)→ 直接返回,不发网络(R-06 抖动期免反复探测)
|
|
141
|
+
const fp = configFingerprint(process.cwd());
|
|
142
|
+
const cached = negativeCache.get(fp);
|
|
143
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
144
|
+
return cached.result;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const cli = client || new SillyHubMcpClient({ cwd: process.cwd() });
|
|
148
|
+
const cacheNegative = (reason) => {
|
|
149
|
+
const result = { available: false, reason };
|
|
150
|
+
const ttl = resolveTtl(ttlMs, process.cwd());
|
|
151
|
+
negativeCache.set(fp, { result, expiresAt: Date.now() + ttl });
|
|
152
|
+
return result;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// HUB-12a:总超时兜底——慢/挂 daemon 不拖 execute。超时后后台请求由 client 自身
|
|
156
|
+
// 10s 请求超时自然终结(有界),probe 侧按不可达降级
|
|
157
|
+
const budgetMs = typeof totalTimeoutMs === 'number' && totalTimeoutMs > 0
|
|
158
|
+
? totalTimeoutMs
|
|
159
|
+
: DEFAULT_PROBE_TOTAL_TIMEOUT_MS;
|
|
160
|
+
|
|
161
|
+
const probeBody = (async () => {
|
|
162
|
+
// 3. 连通性探测:client.probeDaemon 自身 best-effort 不抛;再兜一层 try/catch,
|
|
163
|
+
// 即便 client 契约异常抛出也保守判 unavailable(铁律:绝不抛穿 execute)
|
|
164
|
+
let reachable;
|
|
165
|
+
try {
|
|
166
|
+
reachable = await cli.probeDaemon();
|
|
167
|
+
} catch (err) {
|
|
168
|
+
const msg = err && err.message ? err.message : String(err);
|
|
169
|
+
console.warn(`[dispatch/probe] probeDaemon 抛异常(保守判 unavailable): ${msg}`);
|
|
170
|
+
reachable = false;
|
|
171
|
+
}
|
|
172
|
+
if (!reachable) return cacheNegative('daemon-unreachable');
|
|
173
|
+
|
|
174
|
+
// 3.5+4 合并(HUB-12a):一次 tools/list 同时拿 tools 数组(路径A schema 预热)与
|
|
175
|
+
// root_path(越界校验)。旧 client 无 listToolsWithMeta 时回退 listTools +
|
|
176
|
+
// getRootPath 各一次(兼容旧 mock)
|
|
177
|
+
let tools = null;
|
|
178
|
+
let toolsListRoot = undefined;
|
|
179
|
+
try {
|
|
180
|
+
if (typeof cli.listToolsWithMeta === 'function') {
|
|
181
|
+
const raw = await cli.listToolsWithMeta();
|
|
182
|
+
if (raw) {
|
|
183
|
+
if (Array.isArray(raw)) tools = raw;
|
|
184
|
+
else if (Array.isArray(raw.tools)) tools = raw.tools;
|
|
185
|
+
if (raw && typeof raw === 'object' && typeof raw.root_path === 'string' && raw.root_path) {
|
|
186
|
+
toolsListRoot = raw.root_path;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} else if (typeof cli.listTools === 'function') {
|
|
190
|
+
tools = await cli.listTools();
|
|
191
|
+
}
|
|
192
|
+
} catch (err) {
|
|
193
|
+
const msg = err && err.message ? err.message : String(err);
|
|
194
|
+
console.warn(`[dispatch/probe] tools/list 异常(路径A 保守判 false): ${msg}`);
|
|
195
|
+
tools = null;
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
setPathAProbeResult(detectPathAFromTools(tools));
|
|
199
|
+
} catch (err) {
|
|
200
|
+
const msg = err && err.message ? err.message : String(err);
|
|
201
|
+
console.warn(`[dispatch/probe] 路径A schema 探测异常(保守判 false): ${msg}`);
|
|
202
|
+
setPathAProbeResult(false);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (worktreePath) {
|
|
206
|
+
let effectiveRoot = rootPath;
|
|
207
|
+
if (effectiveRoot === undefined) {
|
|
208
|
+
effectiveRoot = toolsListRoot !== undefined ? toolsListRoot : null;
|
|
209
|
+
if (effectiveRoot === null && typeof cli.getRootPath === 'function') {
|
|
210
|
+
try {
|
|
211
|
+
effectiveRoot = await cli.getRootPath();
|
|
212
|
+
} catch (err) {
|
|
213
|
+
const msg = err && err.message ? err.message : String(err);
|
|
214
|
+
console.warn(`[dispatch/probe] rootPath best-effort 拿取异常(跳过越界校验): ${msg}`);
|
|
215
|
+
effectiveRoot = null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (effectiveRoot && !isWithinRoot(effectiveRoot, worktreePath)) {
|
|
220
|
+
return { available: false, reason: 'worktree-outside-root' };
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// 5. 全通过:不缓存正面结果,下次重新探测(让 daemon 恢复/退化及时反映)
|
|
225
|
+
return { available: true };
|
|
226
|
+
})();
|
|
227
|
+
|
|
228
|
+
let timeoutId = null;
|
|
229
|
+
let raced;
|
|
230
|
+
try {
|
|
231
|
+
raced = await Promise.race([
|
|
232
|
+
probeBody,
|
|
233
|
+
new Promise((resolve) => { timeoutId = setTimeout(() => resolve(null), budgetMs) }),
|
|
234
|
+
]);
|
|
235
|
+
} finally {
|
|
236
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
237
|
+
}
|
|
238
|
+
return raced || cacheNegative('probe-timeout');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* 清空负面缓存(测试用;生产中 TTL 自然过期即可)。
|
|
243
|
+
*/
|
|
244
|
+
export function clearProbeCache() {
|
|
245
|
+
negativeCache.clear();
|
|
246
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 派发策略生成器(task-dispatcher 抽象层)— task-02
|
|
3
|
+
*
|
|
4
|
+
* 定位(D-007 + UB-2):dispatcher **不是 JS 执行体**。本文件 = **派发策略生成器**:
|
|
5
|
+
* 依据 probe 结果选择后端(sillyhub / local),并把对应后端的派发指令模板组合成可注入
|
|
6
|
+
* execute prompt 的「派发指令文本」,告诉 agent 用哪个后端、调什么 tool、传什么参数、
|
|
7
|
+
* 怎么轮询与回收。**实际 tool 调用(本机 Agent tool / SillyHub MCP tool)由 agent 执行,
|
|
8
|
+
* 本模块不 import client、不调任何 tool。**
|
|
9
|
+
*
|
|
10
|
+
* backend 选择(acceptance #1/#2,**由 probe.available 驱动,不由 isPathASupported 驱动**):
|
|
11
|
+
* - `probe.available === true` → backend = `'sillyhub'`(指令走 SillyHub MCP 模板)
|
|
12
|
+
* - 否则(false / undefined / probe 为 null)→ backend = `'local'`(与现状行为一致,零回归)
|
|
13
|
+
*
|
|
14
|
+
* 路径 A 降级(D-003@v2):`isPathASupported()` **不改 backend 标签**(仍由 probe 驱动),
|
|
15
|
+
* 而是影响 SillyHub 指令的**文本内容**:path A 未支持时(当前 stub 恒 false),在 SillyHub
|
|
16
|
+
* 主指令后附加降级提示段(PATH_A_DOWNGRADE_REASON + per-worker 回退 Local 说明),并始终追加
|
|
17
|
+
* Local 兜底指令全文,让 agent 据此 per-worker 回退本机 Agent tool,不硬试 MCP 路径。
|
|
18
|
+
*
|
|
19
|
+
* 铁律(R-09 / UB-7 / D-004):
|
|
20
|
+
* - 纯函数:除 import 的模板生成器外无副作用,易测(task-08 mock probe 验两分支)。
|
|
21
|
+
* - 指令必须明确**单后端**(backend 字段 + 文本只描述选定后端 + 兜底),避免 agent 误执行。
|
|
22
|
+
* - allowedPaths 不物化到 SillyHub(仅写入指令供 SillySpec 侧 assess/apply 校验)——
|
|
23
|
+
* allowedPaths 由各后端模板在指令文本里声明,本策略不额外处理。
|
|
24
|
+
* - 兼容 Win/Linux/macOS:无路径拼接 / 无平台分支,纯模板组合。
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { renderLocalInstruction } from './backends/local-agent.js';
|
|
28
|
+
import {
|
|
29
|
+
isPathASupported,
|
|
30
|
+
renderSillyHubInstruction,
|
|
31
|
+
PATH_A_DOWNGRADE_REASON,
|
|
32
|
+
} from './backends/sillyhub-mcp.js';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {import('./backends/local-agent.js').DispatchContract} DispatchContract
|
|
36
|
+
* @see design.md「接口定义」DispatchContract:brief / worktreePath / branch / allowedPaths /
|
|
37
|
+
* readOnly / modelHint / agentProfileHint / runId / missionId
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @typedef {object} DispatchInstruction
|
|
42
|
+
* @property {string} instruction - 注入 execute prompt 的派发指令文本(含选定后端 + 兜底)
|
|
43
|
+
* @property {'sillyhub' | 'local'} backend - 选定后端标签(由 probe.available 驱动)
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 生成派发指令(注入 execute prompt,agent 据此执行派发)。
|
|
48
|
+
*
|
|
49
|
+
* 决策(D-007 + task-02 acceptance):
|
|
50
|
+
* 1. 解析 probe:`available = !!(probe && probe.available)`;probe 为 null/undefined/缺
|
|
51
|
+
* available 均视为 unavailable → local(零回归)。
|
|
52
|
+
* 2. **backend = available ? 'sillyhub' : 'local'**(acceptance #1/#2,由 probe 驱动)。
|
|
53
|
+
* 3. contract 兜底为 `{}`(renderLocalInstruction / renderSillyHubInstruction 内部已对
|
|
54
|
+
* 缺字段回退醒目占位符,不会崩)。
|
|
55
|
+
* 4. backend==='sillyhub':
|
|
56
|
+
* - 主指令 = renderSillyHubInstruction(contract)(含轮询间隔 / kill lease 防双写 / 回收约定)
|
|
57
|
+
* - 若 !isPathASupported():追加降级提示段(PATH_A_DOWNGRADE_REASON + per-worker 回退 Local)
|
|
58
|
+
* - **始终追加 Local 兜底指令全文**(implementation #4,保证可回退)
|
|
59
|
+
* 5. backend==='local':主指令 = renderLocalInstruction(contract)(与现状行为一致)
|
|
60
|
+
*
|
|
61
|
+
* 注:renderLocalInstruction / renderSillyHubInstruction 内部已自行替换
|
|
62
|
+
* LOCAL_RECYCLE_RULE / SILLYHUB_RECYCLE_RULE 的 `<runId>` 占位符,本策略不重复替换;
|
|
63
|
+
* 仅当额外引用含占位符的常量时才需替换,当前实现未额外引用,故无需处理。
|
|
64
|
+
*
|
|
65
|
+
* @param {DispatchContract} [contract] - 派发契约(缺省视为空对象,模板内部回退占位符)
|
|
66
|
+
* @param {{ available?: boolean, reason?: string } | null} [probe] - probeSillyHub() 的结果
|
|
67
|
+
* @returns {DispatchInstruction} `{ instruction, backend }`
|
|
68
|
+
*/
|
|
69
|
+
export function renderDispatchInstruction(contract, probe) {
|
|
70
|
+
// 1. 解析 probe:null/undefined/缺 available 均视为 unavailable → local(零回归)
|
|
71
|
+
const available = !!(probe && probe.available);
|
|
72
|
+
|
|
73
|
+
// 2. backend 由 probe.available 驱动(acceptance #1/#2,不由 isPathASupported 驱动)
|
|
74
|
+
const backend = available ? 'sillyhub' : 'local';
|
|
75
|
+
|
|
76
|
+
// 3. contract 兜底(模板内部对缺字段已有醒目占位符处理,不会崩)
|
|
77
|
+
const c = contract || {};
|
|
78
|
+
|
|
79
|
+
if (backend === 'sillyhub') {
|
|
80
|
+
// SillyHub 主指令:含一 Wave 一 mission / dispatch_worker 参数 / 终态轮询间隔 / kill lease
|
|
81
|
+
// 防双写 / 回收约定(acceptance #3 轮询间隔与 kill lease 来自此模板)
|
|
82
|
+
const sillyHubInstruction = renderSillyHubInstruction(c);
|
|
83
|
+
|
|
84
|
+
// path A 未支持时的降级提示段(不改 backend 标签,仅追加文本,引导 agent per-worker 回退 Local)
|
|
85
|
+
const pathADowngrade = isPathASupported()
|
|
86
|
+
? ''
|
|
87
|
+
: [
|
|
88
|
+
'',
|
|
89
|
+
'---',
|
|
90
|
+
'',
|
|
91
|
+
'### 路径 A 降级提示(per-worker 回退 Local)',
|
|
92
|
+
'',
|
|
93
|
+
`${PATH_A_DOWNGRADE_REASON}`,
|
|
94
|
+
'',
|
|
95
|
+
'本次派发 backend 标记仍为 `sillyhub`(由 probe 可达驱动,acceptance 要求不改标签),',
|
|
96
|
+
'但实际执行将回退 Local:agent 调 dispatch_worker 传 worktree_path 会被拒/忽略,',
|
|
97
|
+
'请 per-worker 回退本机 Agent tool(见下方 Local 兜底),不要硬试 MCP 路径。',
|
|
98
|
+
'',
|
|
99
|
+
].join('\n');
|
|
100
|
+
|
|
101
|
+
// 始终追加 Local 兜底指令全文(implementation #4「始终附加 Local 兜底」):
|
|
102
|
+
// 保证 SillyHub 路径任一步异常(dispatch 被拒 / 轮询超时 kill 后 fallback / path A 未落地)
|
|
103
|
+
// 时 agent 可立即改用本机 Agent tool 重派,零回归。
|
|
104
|
+
const localFallback = [
|
|
105
|
+
'',
|
|
106
|
+
'---',
|
|
107
|
+
'',
|
|
108
|
+
'### 兜底:Local(本机 Agent tool)— SillyHub 路径异常时回退',
|
|
109
|
+
'',
|
|
110
|
+
'当上方 SillyHub MCP 路径任一步失败,立即改用本机 Agent tool 重派该 task。',
|
|
111
|
+
'Local 后端派发指令全文如下(workdir 强制 = contract.worktreePath):',
|
|
112
|
+
'',
|
|
113
|
+
renderLocalInstruction(c),
|
|
114
|
+
].join('\n');
|
|
115
|
+
|
|
116
|
+
return { instruction: sillyHubInstruction + pathADowngrade + localFallback, backend };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// backend === 'local':与现状行为一致(acceptance #2),probe 本就 unavailable,无需额外降级提示
|
|
120
|
+
return { instruction: renderLocalInstruction(c), backend };
|
|
121
|
+
}
|