add-coder 0.3.26 → 0.3.28
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/README.md +282 -155
- package/dist/index.js +481 -209
- package/package.json +15 -4
- package/templates/.add-coder-src-hash.json +186 -185
- package/templates/adapters/claude/hooks/doc-format-guard.ts +32 -0
- package/templates/adapters/claude/hooks/lib/claude-env.ts +7 -0
- package/templates/adapters/claude/hooks/notification.ts +24 -0
- package/templates/adapters/claude/hooks/permission-denied.ts +33 -0
- package/templates/adapters/claude/hooks/permission-gate.ts +12 -0
- package/templates/adapters/claude/hooks/post-tool-failure.ts +4 -0
- package/templates/adapters/claude/hooks/post-tool-use.ts +17 -0
- package/templates/adapters/claude/hooks/pre-compact.ts +10 -0
- package/templates/adapters/claude/hooks/pre-tool-use.ts +81 -0
- package/templates/adapters/claude/hooks/prompt-submit.ts +82 -0
- package/templates/adapters/claude/hooks/review-checklist.ts +4 -0
- package/templates/adapters/claude/hooks/session-end.ts +10 -0
- package/templates/adapters/claude/hooks/session-start.ts +24 -0
- package/templates/adapters/claude/hooks/stop-check.ts +15 -0
- package/templates/adapters/claude/hooks/stop-failure.ts +34 -0
- package/templates/adapters/claude/hooks/subagent-guard.ts +25 -0
- package/templates/adapters/claude/hooks/subagent-stop.ts +12 -0
- package/templates/adapters/claude/settings.json +13 -13
- package/templates/adapters/codex/config.toml.example +12 -0
- package/templates/adapters/codex/hooks/doc-format-guard.ts +30 -0
- package/templates/adapters/codex/hooks/notification.ts +26 -0
- package/templates/adapters/codex/hooks/permission-gate.ts +15 -0
- package/templates/adapters/codex/hooks/post-tool-failure.ts +14 -0
- package/templates/adapters/codex/hooks/post-tool-use.ts +85 -0
- package/templates/adapters/codex/hooks/pre-compact.ts +20 -0
- package/templates/adapters/codex/hooks/pre-tool-use.ts +141 -0
- package/templates/adapters/codex/hooks/prompt-submit.ts +38 -0
- package/templates/adapters/codex/hooks/review-checklist.ts +5 -0
- package/templates/adapters/codex/hooks/session-end.ts +19 -0
- package/templates/adapters/codex/hooks/session-start.ts +45 -0
- package/templates/adapters/codex/hooks/stop-check.ts +59 -0
- package/templates/adapters/codex/hooks/subagent-guard.ts +22 -0
- package/templates/adapters/codex/hooks/subagent-stop.ts +20 -0
- package/templates/adapters/codex/hooks.json +67 -1
- package/templates/adapters/qoder/hooks/doc-format-guard.ts +34 -0
- package/templates/adapters/qoder/hooks/lib/context-inject.ts +61 -0
- package/templates/adapters/qoder/hooks/lib/qoder-env.ts +23 -0
- package/templates/adapters/qoder/hooks/lib/review-checklist.ts +5 -0
- package/templates/adapters/qoder/hooks/notification.ts +34 -0
- package/templates/adapters/qoder/hooks/permission-gate.ts +26 -0
- package/templates/adapters/qoder/hooks/post-tool-failure.ts +30 -0
- package/templates/adapters/qoder/hooks/post-tool-use.ts +32 -0
- package/templates/adapters/qoder/hooks/pre-compact.ts +16 -0
- package/templates/adapters/qoder/hooks/pre-tool-use.ts +112 -0
- package/templates/adapters/qoder/hooks/prompt-submit.ts +66 -0
- package/templates/adapters/qoder/hooks/review-checklist.ts +24 -0
- package/templates/adapters/qoder/hooks/session-end.ts +27 -0
- package/templates/adapters/qoder/hooks/session-start.ts +83 -0
- package/templates/adapters/qoder/hooks/stop-check.ts +69 -0
- package/templates/adapters/qoder/hooks/subagent-guard.ts +16 -0
- package/templates/adapters/qoder/hooks/subagent-stop.ts +33 -0
- package/templates/adapters/qoder/settings.json +13 -13
- package/templates/adapters/trae/hooks/doc-format-guard.ts +30 -0
- package/templates/adapters/trae/hooks/notification.ts +22 -0
- package/templates/adapters/trae/hooks/permission-gate.ts +15 -0
- package/templates/adapters/trae/hooks/post-tool-failure.ts +14 -0
- package/templates/adapters/trae/hooks/post-tool-use.ts +24 -0
- package/templates/adapters/trae/hooks/pre-compact.ts +15 -0
- package/templates/adapters/trae/hooks/pre-tool-use.ts +40 -0
- package/templates/adapters/trae/hooks/prompt-submit.ts +29 -0
- package/templates/adapters/trae/hooks/review-checklist.ts +5 -0
- package/templates/adapters/trae/hooks/session-end.ts +16 -0
- package/templates/adapters/trae/hooks/session-start.ts +15 -0
- package/templates/adapters/trae/hooks/stop-check.ts +20 -0
- package/templates/adapters/trae/hooks/subagent-guard.ts +22 -0
- package/templates/adapters/trae/hooks/subagent-stop.ts +15 -0
- package/templates/adapters/trae/hooks.json +1 -1
- package/templates/adapters/vscode/hooks/doc-format-guard.ts +27 -0
- package/templates/adapters/vscode/hooks/notification.ts +22 -0
- package/templates/adapters/vscode/hooks/permission-gate.ts +15 -0
- package/templates/adapters/vscode/hooks/post-tool-failure.ts +32 -0
- package/templates/adapters/vscode/hooks/post-tool-use.ts +25 -0
- package/templates/adapters/vscode/hooks/pre-compact.ts +15 -0
- package/templates/adapters/vscode/hooks/pre-tool-use.ts +78 -0
- package/templates/adapters/vscode/hooks/prompt-submit.ts +74 -0
- package/templates/adapters/vscode/hooks/review-checklist.ts +5 -0
- package/templates/adapters/vscode/hooks/session-end.ts +16 -0
- package/templates/adapters/vscode/hooks/session-start.ts +24 -0
- package/templates/adapters/vscode/hooks/stop-check.ts +21 -0
- package/templates/adapters/vscode/hooks/subagent-guard.ts +21 -0
- package/templates/adapters/vscode/hooks/subagent-stop.ts +15 -0
- package/templates/core/docs/ADD-governance-claude-code.md +4 -4
- package/templates/core/docs/ADD-governance-codex.md +6 -6
- package/templates/core/docs/ADD-governance-qoder-cn.md +5 -5
- package/templates/core/docs/ADD-governance-trae.md +3 -3
- package/templates/core/docs/ADD-governance-vscode-copilot.md +22 -23
- package/templates/core/governance/audit-bridge.ts +47 -0
- package/templates/core/governance/common.ts +299 -0
- package/templates/core/governance/context-inject.ts +53 -0
- package/templates/core/governance/doc-format-guard.ts +440 -0
- package/templates/core/governance/notification-router.ts +62 -0
- package/templates/core/governance/notify.ts +66 -0
- package/templates/core/governance/permission-gate-router.ts +42 -0
- package/templates/core/governance/post-tool-failure-router.ts +56 -0
- package/templates/core/governance/post-tool-router.ts +212 -0
- package/templates/core/governance/pre-compact-guard.ts +41 -0
- package/templates/core/governance/pre-tool-guard.ts +309 -0
- package/templates/core/governance/preload-templates.ts +241 -0
- package/templates/core/governance/prompt-router.ts +222 -0
- package/templates/core/governance/review-checklist-guard.ts +186 -0
- package/templates/core/governance/rules.ts +720 -0
- package/templates/core/governance/session-end.ts +88 -0
- package/templates/core/governance/session-start-guard.ts +99 -0
- package/templates/core/governance/state-detect.ts +6 -0
- package/templates/core/governance/stop-router.ts +140 -0
- package/templates/core/governance/subagent-guard-router.ts +35 -0
- package/templates/core/governance/subagent-stop-router.ts +93 -0
- package/templates/core/governance/subagent-stop.ts +75 -0
- package/templates/core/governance/vocabulary.ts +109 -0
- package/templates/core/hooks/doc-format-guard.ts +21 -0
- package/templates/core/hooks/notification.ts +11 -0
- package/templates/core/hooks/permission-gate.ts +7 -0
- package/templates/core/hooks/post-tool-failure.ts +7 -0
- package/templates/core/hooks/post-tool-use.ts +23 -0
- package/templates/core/hooks/pre-compact.ts +9 -0
- package/templates/core/hooks/pre-tool-use.ts +30 -0
- package/templates/core/hooks/prompt-submit.ts +11 -0
- package/templates/core/hooks/review-checklist.ts +4 -0
- package/templates/core/hooks/session-end.ts +10 -0
- package/templates/core/hooks/session-start.ts +9 -0
- package/templates/core/hooks/stop-check.ts +14 -0
- package/templates/core/hooks/subagent-guard.ts +8 -0
- package/templates/core/hooks/subagent-stop.ts +9 -0
- package/templates/core/prisma/add.prisma +140 -82
- package/templates/core/rules/project_rules.md +27 -24
- package/templates/core/scripts/db-ensure.sh +8 -0
- package/templates/core/scripts/gen-plan-index.sh +4 -15
- package/templates/core/scripts/mcp-server/notifications/hitl.ts +9 -1
- package/templates/core/scripts/mcp-server/notifications/hook.ts +25 -9
- package/templates/core/scripts/mcp-server/resources/hitl-approval-widget.ts +34 -0
- package/templates/core/scripts/mcp-server/resources/hook-events-report.ts +14 -2
- package/templates/core/scripts/mcp-server/resources/index.ts +2 -0
- package/templates/core/scripts/mcp-server/sampling/review.ts +25 -10
- package/templates/core/scripts/mcp-server/shared/db-types.ts +33 -8
- package/templates/core/scripts/mcp-server/shared/dps-scoring.strategy.ts +1 -1
- package/templates/core/scripts/mcp-server/shared/env.ts +28 -8
- package/templates/core/scripts/mcp-server/shared/fs.ts +3 -0
- package/templates/core/scripts/mcp-server/shared/hitl-interaction.strategy.ts +2 -3
- package/templates/core/scripts/mcp-server/shared/hitl-lifecycle-mutation.ts +129 -0
- package/templates/core/scripts/mcp-server/shared/hitl-proposal-mutation.ts +98 -0
- package/templates/core/scripts/mcp-server/shared/hitl-ui.ts +3 -0
- package/templates/core/scripts/mcp-server/shared/plan-lifecycle-events.ts +62 -0
- package/templates/core/scripts/mcp-server/shared/plan-lifecycle-mutation.ts +78 -0
- package/templates/core/scripts/mcp-server/shared/plan-lifecycle-subscriber.ts +145 -0
- package/templates/core/scripts/mcp-server/shared/plan-lifecycle.ts +153 -0
- package/templates/core/scripts/mcp-server/shared/plan-round-events.ts +56 -0
- package/templates/core/scripts/mcp-server/shared/plan-round-mutation.ts +143 -0
- package/templates/core/scripts/mcp-server/shared/plan-round-store.ts +47 -0
- package/templates/core/scripts/mcp-server/shared/plan-round-subscriber.ts +99 -0
- package/templates/core/scripts/mcp-server/shared/plan-status-store.ts +37 -0
- package/templates/core/scripts/mcp-server/shared/plan-tracking-mutation.ts +71 -0
- package/templates/core/scripts/mcp-server/shared/prisma.ts +12 -6
- package/templates/core/scripts/mcp-server/shared/project-root-strategy.ts +1 -2
- package/templates/core/scripts/mcp-server/shared/quant-recheck.strategy.ts +16 -0
- package/templates/core/scripts/mcp-server/shared/redact.ts +28 -0
- package/templates/core/scripts/mcp-server/shared/response.ts +3 -2
- package/templates/core/scripts/mcp-server/shared/runtime-context.ts +77 -0
- package/templates/core/scripts/mcp-server/shared/schema-topology.ts +159 -0
- package/templates/core/scripts/mcp-server/tools/audit.ts +49 -7
- package/templates/core/scripts/mcp-server/tools/context.ts +117 -121
- package/templates/core/scripts/mcp-server/tools/contract.ts +34 -9
- package/templates/core/scripts/mcp-server/tools/gateway/check_add_route_status.ts +50 -3
- package/templates/core/scripts/mcp-server/tools/gateway/check_doc_similarity.ts +218 -0
- package/templates/core/scripts/mcp-server/tools/gateway/check_dps.ts +22 -7
- package/templates/core/scripts/mcp-server/tools/gateway/check_rahs.ts +86 -4
- package/templates/core/scripts/mcp-server/tools/gateway/check_spec_sync.ts +19 -6
- package/templates/core/scripts/mcp-server/tools/gateway/index.ts +2 -0
- package/templates/core/scripts/mcp-server/tools/hitl.ts +160 -75
- package/templates/core/scripts/mcp-server/tools/hook-event-report.ts +9 -2
- package/templates/core/scripts/mcp-server/tools/index.ts +69 -3
- package/templates/core/scripts/mcp-server/tools/plan.ts +152 -17
- package/templates/core/scripts/mcp-server/tools/review.ts +44 -15
- package/templates/core/scripts/mcp-server.ts +60 -1
- package/templates/core/scripts/plan-status-bridge.ts +26 -0
- package/templates/core/skills/add-paradigm/SKILL.md +2 -2
- package/templates/core/templates/add-route-template.schema.json +24 -56
- package/templates/core/templates/collab-contract-template.md +1 -1
- package/templates/core/templates/handoff-multi-round-template.schema.json +48 -0
- package/templates/core/templates/handoff-single-round-template.schema.json +92 -0
- package/templates/core/templates/handoff-single-round.schema.json +1 -1
- package/templates/core/templates/hitl-approval-widget.html +222 -37
- package/templates/core/templates/spec-template.schema.json +9 -46
- package/templates/core/templates/standard-plan-template.schema.json +1 -1
- package/templates/core/templates/tasks-template.schema.json +13 -54
- package/templates/adapters/claude/hooks/doc-format-guard.sh +0 -202
- package/templates/adapters/claude/hooks/lib/common.sh +0 -261
- package/templates/adapters/claude/hooks/lib/context-inject.sh +0 -96
- package/templates/adapters/claude/hooks/lib/notify.sh +0 -34
- package/templates/adapters/claude/hooks/lib/preload-templates.sh +0 -187
- package/templates/adapters/claude/hooks/lib/session-end.sh +0 -76
- package/templates/adapters/claude/hooks/lib/state-detect.sh +0 -117
- package/templates/adapters/claude/hooks/lib/subagent-stop.sh +0 -90
- package/templates/adapters/claude/hooks/lib/vocabulary.sh +0 -56
- package/templates/adapters/claude/hooks/notification.sh +0 -45
- package/templates/adapters/claude/hooks/permission-denied.sh +0 -42
- package/templates/adapters/claude/hooks/permission-gate.sh +0 -18
- package/templates/adapters/claude/hooks/post-tool-failure.sh +0 -10
- package/templates/adapters/claude/hooks/post-tool-use.sh +0 -35
- package/templates/adapters/claude/hooks/pre-compact.sh +0 -37
- package/templates/adapters/claude/hooks/pre-tool-use.sh +0 -153
- package/templates/adapters/claude/hooks/prompt-submit.sh +0 -99
- package/templates/adapters/claude/hooks/review-checklist.sh +0 -10
- package/templates/adapters/claude/hooks/session-end.sh +0 -21
- package/templates/adapters/claude/hooks/session-start.sh +0 -36
- package/templates/adapters/claude/hooks/stop-check.sh +0 -45
- package/templates/adapters/claude/hooks/stop-failure.sh +0 -44
- package/templates/adapters/claude/hooks/subagent-guard.sh +0 -36
- package/templates/adapters/claude/hooks/subagent-stop.sh +0 -20
- package/templates/adapters/codex/hooks/doc-format-guard.sh +0 -261
- package/templates/adapters/codex/hooks/lib/common.sh +0 -261
- package/templates/adapters/codex/hooks/lib/context-inject.sh +0 -96
- package/templates/adapters/codex/hooks/lib/notify.sh +0 -34
- package/templates/adapters/codex/hooks/lib/preload-templates.sh +0 -187
- package/templates/adapters/codex/hooks/lib/session-end.sh +0 -76
- package/templates/adapters/codex/hooks/lib/state-detect.sh +0 -117
- package/templates/adapters/codex/hooks/lib/subagent-stop.sh +0 -90
- package/templates/adapters/codex/hooks/lib/vocabulary.sh +0 -56
- package/templates/adapters/codex/hooks/notification.sh +0 -45
- package/templates/adapters/codex/hooks/permission-gate.sh +0 -18
- package/templates/adapters/codex/hooks/post-tool-failure.sh +0 -10
- package/templates/adapters/codex/hooks/post-tool-use.sh +0 -138
- package/templates/adapters/codex/hooks/pre-compact.sh +0 -37
- package/templates/adapters/codex/hooks/pre-tool-use.sh +0 -217
- package/templates/adapters/codex/hooks/prompt-submit.sh +0 -87
- package/templates/adapters/codex/hooks/review-checklist.sh +0 -10
- package/templates/adapters/codex/hooks/session-end.sh +0 -38
- package/templates/adapters/codex/hooks/session-start.sh +0 -53
- package/templates/adapters/codex/hooks/stop-check.sh +0 -45
- package/templates/adapters/codex/hooks/subagent-guard.sh +0 -15
- package/templates/adapters/codex/hooks/subagent-stop.sh +0 -47
- package/templates/adapters/qoder/hooks/doc-format-guard.sh +0 -260
- package/templates/adapters/qoder/hooks/lib/common.sh +0 -261
- package/templates/adapters/qoder/hooks/lib/context-inject.sh +0 -96
- package/templates/adapters/qoder/hooks/lib/notify.sh +0 -34
- package/templates/adapters/qoder/hooks/lib/preload-templates.sh +0 -187
- package/templates/adapters/qoder/hooks/lib/session-end.sh +0 -76
- package/templates/adapters/qoder/hooks/lib/state-detect.sh +0 -117
- package/templates/adapters/qoder/hooks/lib/subagent-stop.sh +0 -90
- package/templates/adapters/qoder/hooks/lib/vocabulary.sh +0 -56
- package/templates/adapters/qoder/hooks/notification.sh +0 -25
- package/templates/adapters/qoder/hooks/permission-gate.sh +0 -8
- package/templates/adapters/qoder/hooks/post-tool-failure.sh +0 -8
- package/templates/adapters/qoder/hooks/post-tool-use.sh +0 -121
- package/templates/adapters/qoder/hooks/pre-compact.sh +0 -37
- package/templates/adapters/qoder/hooks/pre-tool-use.sh +0 -154
- package/templates/adapters/qoder/hooks/prompt-submit.sh +0 -96
- package/templates/adapters/qoder/hooks/review-checklist.sh +0 -157
- package/templates/adapters/qoder/hooks/session-end.sh +0 -24
- package/templates/adapters/qoder/hooks/session-start.sh +0 -45
- package/templates/adapters/qoder/hooks/stop-check.sh +0 -48
- package/templates/adapters/qoder/hooks/subagent-guard.sh +0 -27
- package/templates/adapters/qoder/hooks/subagent-stop.sh +0 -26
- package/templates/adapters/trae/hooks/doc-format-guard.sh +0 -261
- package/templates/adapters/trae/hooks/lib/common.sh +0 -261
- package/templates/adapters/trae/hooks/lib/context-inject.sh +0 -96
- package/templates/adapters/trae/hooks/lib/notify.sh +0 -34
- package/templates/adapters/trae/hooks/lib/preload-templates.sh +0 -187
- package/templates/adapters/trae/hooks/lib/session-end.sh +0 -76
- package/templates/adapters/trae/hooks/lib/state-detect.sh +0 -117
- package/templates/adapters/trae/hooks/lib/subagent-stop.sh +0 -90
- package/templates/adapters/trae/hooks/lib/vocabulary.sh +0 -56
- package/templates/adapters/trae/hooks/notification.sh +0 -45
- package/templates/adapters/trae/hooks/permission-gate.sh +0 -18
- package/templates/adapters/trae/hooks/post-tool-failure.sh +0 -10
- package/templates/adapters/trae/hooks/post-tool-use.sh +0 -138
- package/templates/adapters/trae/hooks/pre-compact.sh +0 -37
- package/templates/adapters/trae/hooks/pre-tool-use.sh +0 -217
- package/templates/adapters/trae/hooks/prompt-submit.sh +0 -87
- package/templates/adapters/trae/hooks/review-checklist.sh +0 -10
- package/templates/adapters/trae/hooks/session-end.sh +0 -38
- package/templates/adapters/trae/hooks/session-start.sh +0 -53
- package/templates/adapters/trae/hooks/stop-check.sh +0 -45
- package/templates/adapters/trae/hooks/subagent-guard.sh +0 -15
- package/templates/adapters/trae/hooks/subagent-stop.sh +0 -47
- package/templates/adapters/vscode/hooks/doc-format-guard.sh +0 -201
- package/templates/adapters/vscode/hooks/lib/common.sh +0 -261
- package/templates/adapters/vscode/hooks/lib/context-inject.sh +0 -96
- package/templates/adapters/vscode/hooks/lib/notify.sh +0 -34
- package/templates/adapters/vscode/hooks/lib/preload-templates.sh +0 -187
- package/templates/adapters/vscode/hooks/lib/session-end.sh +0 -76
- package/templates/adapters/vscode/hooks/lib/state-detect.sh +0 -117
- package/templates/adapters/vscode/hooks/lib/subagent-stop.sh +0 -90
- package/templates/adapters/vscode/hooks/lib/vocabulary.sh +0 -56
- package/templates/adapters/vscode/hooks/notification.sh +0 -45
- package/templates/adapters/vscode/hooks/permission-gate.sh +0 -18
- package/templates/adapters/vscode/hooks/post-tool-failure.sh +0 -28
- package/templates/adapters/vscode/hooks/post-tool-use.sh +0 -35
- package/templates/adapters/vscode/hooks/pre-compact.sh +0 -37
- package/templates/adapters/vscode/hooks/pre-tool-use.sh +0 -153
- package/templates/adapters/vscode/hooks/prompt-submit.sh +0 -99
- package/templates/adapters/vscode/hooks/review-checklist.sh +0 -10
- package/templates/adapters/vscode/hooks/session-end.sh +0 -13
- package/templates/adapters/vscode/hooks/session-start.sh +0 -35
- package/templates/adapters/vscode/hooks/stop-check.sh +0 -53
- package/templates/adapters/vscode/hooks/subagent-guard.sh +0 -37
- package/templates/adapters/vscode/hooks/subagent-stop.sh +0 -12
- package/templates/core/hooks/doc-format-guard.sh +0 -261
- package/templates/core/hooks/lib/common.sh +0 -261
- package/templates/core/hooks/lib/context-inject.sh +0 -96
- package/templates/core/hooks/lib/notify.sh +0 -34
- package/templates/core/hooks/lib/preload-templates.sh +0 -187
- package/templates/core/hooks/lib/session-end.sh +0 -76
- package/templates/core/hooks/lib/state-detect.sh +0 -117
- package/templates/core/hooks/lib/subagent-stop.sh +0 -90
- package/templates/core/hooks/lib/vocabulary.sh +0 -56
- package/templates/core/hooks/notification.sh +0 -45
- package/templates/core/hooks/permission-gate.sh +0 -18
- package/templates/core/hooks/post-tool-failure.sh +0 -10
- package/templates/core/hooks/post-tool-use.sh +0 -138
- package/templates/core/hooks/pre-compact.sh +0 -37
- package/templates/core/hooks/pre-tool-use.sh +0 -217
- package/templates/core/hooks/prompt-submit.sh +0 -87
- package/templates/core/hooks/review-checklist.sh +0 -10
- package/templates/core/hooks/session-end.sh +0 -38
- package/templates/core/hooks/session-start.sh +0 -53
- package/templates/core/hooks/stop-check.sh +0 -45
- package/templates/core/hooks/subagent-guard.sh +0 -15
- package/templates/core/hooks/subagent-stop.sh +0 -47
- package/templates/shared/hooks-lib/common.sh +0 -22
|
@@ -7,6 +7,19 @@ import { readFileSafe, readdirRecursive, PROJECT_ROOT, MAGIC_DIR } from "../shar
|
|
|
7
7
|
import { prisma } from "../shared/prisma.js"
|
|
8
8
|
import type { PlanRow } from "../shared/db-types.js"
|
|
9
9
|
import { PlanRowSchema, validatedDelegate } from "../shared/db-types.js"
|
|
10
|
+
import { getRuntimeContext } from "../shared/env.js"
|
|
11
|
+
import { assertPathInRuntimeScope } from "../shared/runtime-context.js"
|
|
12
|
+
import { resolvePlanStatus } from "../shared/plan-lifecycle.js"
|
|
13
|
+
import { createPrismaPlanStatusStore } from "../shared/plan-status-store.js"
|
|
14
|
+
import {
|
|
15
|
+
trackPlanAndPublish,
|
|
16
|
+
type PlanTrackingDatabase,
|
|
17
|
+
} from "../shared/plan-tracking-mutation.js"
|
|
18
|
+
import {
|
|
19
|
+
closePlanRoundAndPublish,
|
|
20
|
+
type PlanRoundDatabase,
|
|
21
|
+
} from "../shared/plan-round-mutation.js"
|
|
22
|
+
import { queryPlanRounds, type PlanRoundReadClient } from "../shared/plan-round-store.js"
|
|
10
23
|
|
|
11
24
|
// 无类型边界单点(zod 托管):动态加载的 prisma client 在此一次性转为运行期校验的泛型委托
|
|
12
25
|
const db = {
|
|
@@ -14,6 +27,14 @@ const db = {
|
|
|
14
27
|
}
|
|
15
28
|
|
|
16
29
|
export function registerPlanTools(server: ToolRegistrar) {
|
|
30
|
+
const runtimeContext = getRuntimeContext()
|
|
31
|
+
const statusStore = createPrismaPlanStatusStore(prisma)
|
|
32
|
+
const parseStructuredState = (value: string, field: string): Record<string, unknown> | unknown[] => {
|
|
33
|
+
let parsed: unknown
|
|
34
|
+
try { parsed = JSON.parse(value) } catch { throw new Error(`${field} 必须是有效 JSON`) }
|
|
35
|
+
if (parsed === null || typeof parsed !== "object") throw new Error(`${field} 必须是非 null JSON 对象或数组`)
|
|
36
|
+
return parsed as Record<string, unknown> | unknown[]
|
|
37
|
+
}
|
|
17
38
|
|
|
18
39
|
// ===== plan_track =====
|
|
19
40
|
server.registerTool("plan_track", {
|
|
@@ -24,7 +45,7 @@ export function registerPlanTools(server: ToolRegistrar) {
|
|
|
24
45
|
}),
|
|
25
46
|
}, async (args: Record<string, unknown>) => {
|
|
26
47
|
try {
|
|
27
|
-
const { planName
|
|
48
|
+
const { planName } = args as { planName?: string; scanAll?: boolean }
|
|
28
49
|
const pn = typeof planName === "string" ? planName : ""
|
|
29
50
|
const plansDir = join(PROJECT_ROOT, MAGIC_DIR, "plans")
|
|
30
51
|
const specsDir = join(PROJECT_ROOT, MAGIC_DIR, "specs")
|
|
@@ -32,7 +53,7 @@ export function registerPlanTools(server: ToolRegistrar) {
|
|
|
32
53
|
|
|
33
54
|
if (!existsSync(plansDir)) return errorResponse(`plans 目录不存在: ${plansDir}`)
|
|
34
55
|
|
|
35
|
-
|
|
56
|
+
const targets: { name: string; path: string; keyword: string }[] = []
|
|
36
57
|
const newPlans: string[] = []
|
|
37
58
|
const allFiles = await readdirRecursive(plansDir)
|
|
38
59
|
// 排除 .hitl.md(HITL 提案不是 Plan 本体,避免 plan_track 误扫为独立 Plan)
|
|
@@ -54,8 +75,18 @@ export function registerPlanTools(server: ToolRegistrar) {
|
|
|
54
75
|
// 提取 planKeyword(关键词: xxx)
|
|
55
76
|
const kwMatch = content.match(/关键词[::]\s*(.+)/) || content.match(/planKeyword[::]\s*"?([^"\n]+)"?/)
|
|
56
77
|
const keyword = kwMatch?.[1]?.trim() || t.name
|
|
57
|
-
// 定位 spec dir
|
|
58
|
-
|
|
78
|
+
// 定位 spec dir(目录名可能带 -vN 版本后缀:优先带版本匹配,再回退不带版本,
|
|
79
|
+
// 最后回退解析 Plan §六 关联文档 Spec 路径真源)[Task 1.8 修复]
|
|
80
|
+
const planBase = basename(t.name).replace(/-plan-v\d+$/, "")
|
|
81
|
+
const versionSuffix = /-plan-(v\d+)$/.exec(basename(t.name))?.[1] ?? ""
|
|
82
|
+
const specCandidates = versionSuffix
|
|
83
|
+
? [`${planBase}-${versionSuffix}`, planBase]
|
|
84
|
+
: [planBase]
|
|
85
|
+
const specRef = content.match(/specs\/([^/`\s]+)/)
|
|
86
|
+
if (specRef) specCandidates.push(specRef[1])
|
|
87
|
+
const specDirName =
|
|
88
|
+
specCandidates.find((d) => existsSync(join(specsDir, d))) ??
|
|
89
|
+
specCandidates[0]
|
|
59
90
|
const tasksPath = join(specsDir, specDirName, "tasks.md")
|
|
60
91
|
const checklistPath = join(specsDir, specDirName, "checklist.md")
|
|
61
92
|
const specPath = join(specsDir, specDirName, "spec.md")
|
|
@@ -84,7 +115,6 @@ export function registerPlanTools(server: ToolRegistrar) {
|
|
|
84
115
|
)
|
|
85
116
|
const addRoutePath = addRouteFile ? join(plansDir, addRouteFile) : undefined
|
|
86
117
|
// upsert PlanRecord
|
|
87
|
-
const existing = await db.plan.findFirst({ where: { planName: t.name } })
|
|
88
118
|
const data: Partial<PlanRow> = {
|
|
89
119
|
planPath: t.path,
|
|
90
120
|
specPath: existsSync(specPath) ? specPath : undefined,
|
|
@@ -94,11 +124,16 @@ export function registerPlanTools(server: ToolRegistrar) {
|
|
|
94
124
|
totalTasks, doneTasks, checklistT, checklistTDone, checklistR,
|
|
95
125
|
planKeyword: keyword,
|
|
96
126
|
}
|
|
97
|
-
|
|
98
|
-
|
|
127
|
+
assertPathInRuntimeScope(runtimeContext, t.path)
|
|
128
|
+
const tracked = await trackPlanAndPublish(prisma as unknown as PlanTrackingDatabase, {
|
|
129
|
+
context: runtimeContext,
|
|
130
|
+
planName: t.name,
|
|
131
|
+
planPath: t.path,
|
|
132
|
+
projection: data,
|
|
133
|
+
})
|
|
134
|
+
if (!tracked.created) {
|
|
99
135
|
results.push(` ✅ 已更新 (totalTasks=${totalTasks}, done=${doneTasks})` + (addRoutePath ? `, addRoute` : ``))
|
|
100
136
|
} else {
|
|
101
|
-
await db.plan.create({ data: { ...data, planName: t.name, planPath: t.path } })
|
|
102
137
|
results.push(` ✅ 已创建 (totalTasks=${totalTasks}, done=${doneTasks})` + (addRoutePath ? `, addRoute` : ``))
|
|
103
138
|
newPlans.push(t.name.replace(/-plan-v\d+$/, ""))
|
|
104
139
|
}
|
|
@@ -106,7 +141,13 @@ export function registerPlanTools(server: ToolRegistrar) {
|
|
|
106
141
|
// ── 孤儿对账(全量扫描时):PlanRecord 存在但 planPath 文件缺失 → 悬空清单。
|
|
107
142
|
// 来源:create_hitl 预置行被中断 / 用户放弃未清理。允许暂时存在,不允许不可见。 ──
|
|
108
143
|
if (!pn) {
|
|
109
|
-
const allPlans = await db.plan.findMany({
|
|
144
|
+
const allPlans = await db.plan.findMany({
|
|
145
|
+
where: {
|
|
146
|
+
projectKey: runtimeContext.projectKey,
|
|
147
|
+
adapterKey: runtimeContext.adapterKey,
|
|
148
|
+
},
|
|
149
|
+
orderBy: { createdAt: "desc" },
|
|
150
|
+
})
|
|
110
151
|
const orphans = allPlans.filter(p => !existsSync(p.planPath))
|
|
111
152
|
if (orphans.length > 0) {
|
|
112
153
|
results.push("", `⚠️ 悬空 Plan(记录在、文件缺失)— 续写或清理:`)
|
|
@@ -137,17 +178,35 @@ export function registerPlanTools(server: ToolRegistrar) {
|
|
|
137
178
|
}, async (args: Record<string, unknown>) => {
|
|
138
179
|
try {
|
|
139
180
|
const { planName } = args as { planName: string }
|
|
140
|
-
const
|
|
141
|
-
if (
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
181
|
+
const resolution = await resolvePlanStatus(statusStore, runtimeContext, planName)
|
|
182
|
+
if (resolution.availability === "STATUS_UNAVAILABLE") {
|
|
183
|
+
return errorResponse(`Plan status 不可用(禁止按文件猜测): ${resolution.reason}`)
|
|
184
|
+
}
|
|
185
|
+
if (resolution.planName === null) {
|
|
186
|
+
return textResponse(`📋 Plan: 未跟踪\nplanName: ${planName}\nsource: database\ncontextId: ${runtimeContext.contextId}\n\n操作: plan_track({ planName: "${planName}" })`)
|
|
187
|
+
}
|
|
188
|
+
const plan = await db.plan.findFirst({
|
|
189
|
+
where: {
|
|
190
|
+
projectKey: runtimeContext.projectKey,
|
|
191
|
+
adapterKey: runtimeContext.adapterKey,
|
|
192
|
+
planName,
|
|
193
|
+
},
|
|
194
|
+
})
|
|
195
|
+
if (!plan) return errorResponse(`Plan status 解析后记录消失,请重试: ${planName}`)
|
|
196
|
+
const t = resolution.progress.totalTasks
|
|
197
|
+
const d = resolution.progress.doneTasks
|
|
198
|
+
const ct = resolution.progress.checklistT
|
|
199
|
+
const ctd = resolution.progress.checklistTDone
|
|
146
200
|
const cr = plan.checklistR || 0
|
|
147
201
|
const taskPct = t > 0 ? Math.round((d / t) * 100) : 0
|
|
148
202
|
const lines = [
|
|
149
203
|
`📊 ${planName}`,
|
|
150
204
|
``,
|
|
205
|
+
`lifecycle: ${resolution.lifecycle} (${resolution.isActive ? "active" : "inactive"})`,
|
|
206
|
+
`approval: ${resolution.approvalStatus ?? "—"}`,
|
|
207
|
+
`revision: ${resolution.revision}`,
|
|
208
|
+
`source: database`,
|
|
209
|
+
`contextId: ${runtimeContext.contextId}`,
|
|
151
210
|
`tasks.md: ${d}/${t} (${taskPct}%) ${taskPct >= 100 ? "✅" : d > 0 ? "🔄" : "⬜"}`,
|
|
152
211
|
`checklist: [T] ${ctd}/${ct} | [R] ${cr} 项`,
|
|
153
212
|
`planKeyword: ${plan.planKeyword || "—"}`,
|
|
@@ -156,6 +215,7 @@ export function registerPlanTools(server: ToolRegistrar) {
|
|
|
156
215
|
// P3 #5 契约角色展示(PlanRecord 已设置时)
|
|
157
216
|
plan.contractRole ? `contractRole: ${plan.contractRole}` : null,
|
|
158
217
|
plan.contractName ? `contractName: ${plan.contractName}` : null,
|
|
218
|
+
`machine: ${JSON.stringify(resolution)}`,
|
|
159
219
|
].filter(Boolean)
|
|
160
220
|
return textResponse(lines.join("\n"))
|
|
161
221
|
} catch (e) {
|
|
@@ -172,9 +232,16 @@ export function registerPlanTools(server: ToolRegistrar) {
|
|
|
172
232
|
}, async (args: Record<string, unknown>) => {
|
|
173
233
|
try {
|
|
174
234
|
const { planName } = args as { planName: string }
|
|
175
|
-
const plan = await db.plan.findFirst({
|
|
235
|
+
const plan = await db.plan.findFirst({
|
|
236
|
+
where: {
|
|
237
|
+
projectKey: runtimeContext.projectKey,
|
|
238
|
+
adapterKey: runtimeContext.adapterKey,
|
|
239
|
+
planName,
|
|
240
|
+
},
|
|
241
|
+
})
|
|
176
242
|
if (!plan) return errorResponse(`PlanRecord 未找到: ${planName}`)
|
|
177
243
|
const planPath = plan.planPath
|
|
244
|
+
assertPathInRuntimeScope(runtimeContext, planPath)
|
|
178
245
|
if (!existsSync(planPath)) return errorResponse(`Plan 文件不存在: ${planPath}`)
|
|
179
246
|
const content = readFileSync(planPath, "utf-8")
|
|
180
247
|
const t = plan.totalTasks || 0
|
|
@@ -195,7 +262,6 @@ export function registerPlanTools(server: ToolRegistrar) {
|
|
|
195
262
|
].join("\n")
|
|
196
263
|
// 用 indexOf 安全替换(不用跨行正则)
|
|
197
264
|
const marker = "## 📊 Plan 进度快照"
|
|
198
|
-
const markerEnd = "\n## 📊 Plan 进度快照"
|
|
199
265
|
// 找到现有快照区块的结束位置
|
|
200
266
|
const startIdx = content.indexOf(marker)
|
|
201
267
|
let newContent: string
|
|
@@ -217,4 +283,73 @@ export function registerPlanTools(server: ToolRegistrar) {
|
|
|
217
283
|
}
|
|
218
284
|
})
|
|
219
285
|
|
|
286
|
+
// ===== plan_round_close =====
|
|
287
|
+
server.registerTool("plan_round_close", {
|
|
288
|
+
description: "关闭一个逻辑 PlanRound:在当前 RuntimeContextKey 内幂等写入 ROUND_CLOSED,并于同一 Prisma transaction 发布 PostgreSQL 唤醒通知。不会关闭整个 Plan,也不会修改 lifecycle/revision。",
|
|
289
|
+
inputSchema: z.object({
|
|
290
|
+
planName: z.string().min(1),
|
|
291
|
+
round: z.number().int().positive(),
|
|
292
|
+
beforeState: z.string().describe("非 null JSON 对象或数组字符串"),
|
|
293
|
+
afterState: z.string().describe("非 null JSON 对象或数组字符串,包含本轮产出摘要"),
|
|
294
|
+
reason: z.string().optional(),
|
|
295
|
+
operationKey: z.string().optional(),
|
|
296
|
+
}),
|
|
297
|
+
}, async (args: Record<string, unknown>) => {
|
|
298
|
+
try {
|
|
299
|
+
const input = args as { planName: string; round: number; beforeState: string; afterState: string; reason?: string; operationKey?: string }
|
|
300
|
+
const result = await closePlanRoundAndPublish(prisma as unknown as PlanRoundDatabase, {
|
|
301
|
+
context: runtimeContext,
|
|
302
|
+
planName: input.planName,
|
|
303
|
+
round: input.round,
|
|
304
|
+
beforeState: parseStructuredState(input.beforeState, "beforeState"),
|
|
305
|
+
afterState: parseStructuredState(input.afterState, "afterState"),
|
|
306
|
+
reason: input.reason,
|
|
307
|
+
operationKey: input.operationKey,
|
|
308
|
+
})
|
|
309
|
+
return textResponse([
|
|
310
|
+
"✅ PlanRound 已关闭",
|
|
311
|
+
`planName: ${result.plan.planName}`,
|
|
312
|
+
`round: ${input.round}`,
|
|
313
|
+
`operationId: ${result.operation.id}`,
|
|
314
|
+
`targetId: ${result.operation.targetId}`,
|
|
315
|
+
`lifecycle: ${result.plan.lifecycle} (未修改)`,
|
|
316
|
+
`revision: ${result.plan.revision} (未修改)`,
|
|
317
|
+
"source: database",
|
|
318
|
+
].join("\n"))
|
|
319
|
+
} catch (e) {
|
|
320
|
+
return errorResponse(`plan_round_close 失败: ${e instanceof Error ? e.message : String(e)}`)
|
|
321
|
+
}
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
// ===== plan_round_status =====
|
|
325
|
+
server.registerTool("plan_round_status", {
|
|
326
|
+
description: "查询当前 RuntimeContextKey 下的 ROUND_CLOSED 记录。数据库是权威状态;通知仅负责唤醒订阅者调用本查询。",
|
|
327
|
+
inputSchema: z.object({
|
|
328
|
+
planName: z.string().min(1),
|
|
329
|
+
round: z.number().int().positive().optional(),
|
|
330
|
+
}),
|
|
331
|
+
}, async (args: Record<string, unknown>) => {
|
|
332
|
+
try {
|
|
333
|
+
const input = args as { planName: string; round?: number }
|
|
334
|
+
const snapshot = await queryPlanRounds(prisma as unknown as PlanRoundReadClient, {
|
|
335
|
+
context: runtimeContext,
|
|
336
|
+
planName: input.planName,
|
|
337
|
+
round: input.round,
|
|
338
|
+
})
|
|
339
|
+
if (!snapshot.planId) return textResponse(`📋 PlanRound: 未找到 scoped Plan\nplanName: ${input.planName}\ncontextId: ${runtimeContext.contextId}`)
|
|
340
|
+
const lines = [
|
|
341
|
+
`📋 PlanRound: ${snapshot.planName}`,
|
|
342
|
+
`contextId: ${runtimeContext.contextId}`,
|
|
343
|
+
`source: database`,
|
|
344
|
+
`records: ${snapshot.rounds.length}`,
|
|
345
|
+
]
|
|
346
|
+
for (const record of snapshot.rounds) {
|
|
347
|
+
lines.push(`- ${record.targetId} | ${record.createdAt.toISOString()} | operationId=${record.id}`)
|
|
348
|
+
}
|
|
349
|
+
return textResponse(lines.join("\n"))
|
|
350
|
+
} catch (e) {
|
|
351
|
+
return errorResponse(`plan_round_status 失败: ${e instanceof Error ? e.message : String(e)}`)
|
|
352
|
+
}
|
|
353
|
+
})
|
|
354
|
+
|
|
220
355
|
}
|
|
@@ -7,6 +7,8 @@ import { readFileSafe, readdirRecursive, PROJECT_ROOT, MAGIC_DIR } from "../shar
|
|
|
7
7
|
import { prisma } from "../shared/prisma.js"
|
|
8
8
|
import type { PlanRow, ReviewRow } from "../shared/db-types.js"
|
|
9
9
|
import { PlanRowSchema, ReviewRowSchema, validatedDelegate } from "../shared/db-types.js"
|
|
10
|
+
import { getRuntimeContext } from "../shared/env.js"
|
|
11
|
+
import { assertPathInRuntimeScope } from "../shared/runtime-context.js"
|
|
10
12
|
|
|
11
13
|
const db = {
|
|
12
14
|
get review() { return validatedDelegate<ReviewRow>(prisma.reviewRecord, ReviewRowSchema, "ReviewRecord") },
|
|
@@ -34,6 +36,7 @@ function countIssueType(content: string): { p0: number; p1: number; backflow: nu
|
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
export function registerReviewTools(server: ToolRegistrar) {
|
|
39
|
+
const runtimeContext = getRuntimeContext()
|
|
37
40
|
|
|
38
41
|
// ===== review_track =====
|
|
39
42
|
server.registerTool("review_track", {
|
|
@@ -44,7 +47,7 @@ export function registerReviewTools(server: ToolRegistrar) {
|
|
|
44
47
|
}),
|
|
45
48
|
}, async (args: Record<string, unknown>) => {
|
|
46
49
|
try {
|
|
47
|
-
const { planName
|
|
50
|
+
const { planName } = args as { planName?: string; scanAll?: boolean }
|
|
48
51
|
const pn = typeof planName === "string" ? planName : ""
|
|
49
52
|
const reviewsDir = join(PROJECT_ROOT, MAGIC_DIR, "reviews")
|
|
50
53
|
if (!existsSync(reviewsDir)) return errorResponse(`reviews 目录不存在: ${reviewsDir}`)
|
|
@@ -57,7 +60,7 @@ export function registerReviewTools(server: ToolRegistrar) {
|
|
|
57
60
|
const fullPath = join(reviewsDir, rf)
|
|
58
61
|
// 从文件名推导 planName
|
|
59
62
|
// 格式: {plan-name}-review-v1.md 或 {plan-name}-review-implementation.md
|
|
60
|
-
|
|
63
|
+
const derivedPlan = basename(rf, ".md")
|
|
61
64
|
.replace(/-review-.*$/, "")
|
|
62
65
|
.replace(/-implementation.*$/, "")
|
|
63
66
|
.replace(/-runtime.*$/, "")
|
|
@@ -69,7 +72,11 @@ export function registerReviewTools(server: ToolRegistrar) {
|
|
|
69
72
|
// 而 derivedPlan 为 {planPrefix}(从 review 文件名中去掉了 -review-v{n})
|
|
70
73
|
// 需要用 contains 匹配,而不是 exact match
|
|
71
74
|
const planMatch = await db.plan.findFirst({
|
|
72
|
-
where: {
|
|
75
|
+
where: {
|
|
76
|
+
projectKey: runtimeContext.projectKey,
|
|
77
|
+
adapterKey: runtimeContext.adapterKey,
|
|
78
|
+
planName: { contains: derivedPlan },
|
|
79
|
+
},
|
|
73
80
|
})
|
|
74
81
|
const actualPlanName = planMatch
|
|
75
82
|
? planMatch.planName
|
|
@@ -81,7 +88,12 @@ export function registerReviewTools(server: ToolRegistrar) {
|
|
|
81
88
|
const rtype = detectReviewType(rf)
|
|
82
89
|
const stats = countIssueType(content)
|
|
83
90
|
const record = await db.review.findFirst({
|
|
84
|
-
where: {
|
|
91
|
+
where: {
|
|
92
|
+
projectKey: runtimeContext.projectKey,
|
|
93
|
+
adapterKey: runtimeContext.adapterKey,
|
|
94
|
+
planName: actualPlanName,
|
|
95
|
+
type: rtype,
|
|
96
|
+
},
|
|
85
97
|
})
|
|
86
98
|
|
|
87
99
|
const data = {
|
|
@@ -95,7 +107,13 @@ export function registerReviewTools(server: ToolRegistrar) {
|
|
|
95
107
|
results.push(`🔄 ${rf} (${rtype}) P0=${stats.p0} P1=${stats.p1}`)
|
|
96
108
|
} else {
|
|
97
109
|
await db.review.create({
|
|
98
|
-
data: {
|
|
110
|
+
data: {
|
|
111
|
+
projectKey: runtimeContext.projectKey,
|
|
112
|
+
adapterKey: runtimeContext.adapterKey,
|
|
113
|
+
planName: actualPlanName,
|
|
114
|
+
type: rtype,
|
|
115
|
+
...data,
|
|
116
|
+
},
|
|
99
117
|
})
|
|
100
118
|
results.push(`✅ ${rf} (${rtype}) P0=${stats.p0} P1=${stats.p1}`)
|
|
101
119
|
}
|
|
@@ -118,7 +136,11 @@ export function registerReviewTools(server: ToolRegistrar) {
|
|
|
118
136
|
try {
|
|
119
137
|
const { planName } = args as { planName: string }
|
|
120
138
|
const rows = await db.review.findMany({
|
|
121
|
-
where: {
|
|
139
|
+
where: {
|
|
140
|
+
projectKey: runtimeContext.projectKey,
|
|
141
|
+
adapterKey: runtimeContext.adapterKey,
|
|
142
|
+
planName,
|
|
143
|
+
},
|
|
122
144
|
orderBy: { type: "asc" },
|
|
123
145
|
})
|
|
124
146
|
|
|
@@ -130,9 +152,9 @@ export function registerReviewTools(server: ToolRegistrar) {
|
|
|
130
152
|
|
|
131
153
|
const lines = [`📝 Review: ${planName}`, ``]
|
|
132
154
|
for (const r of rows) {
|
|
133
|
-
const p0 = r.p0Count
|
|
134
|
-
const p1 = r.p1Count
|
|
135
|
-
const br = r.backflowRate
|
|
155
|
+
const p0 = r.p0Count || 0
|
|
156
|
+
const p1 = r.p1Count || 0
|
|
157
|
+
const br = r.backflowRate || 0
|
|
136
158
|
const total = p0 + p1
|
|
137
159
|
lines.push(
|
|
138
160
|
` [${r.type}] P0=${p0} P1=${p1} (合计 ${total}) 回流=${br}`,
|
|
@@ -140,8 +162,8 @@ export function registerReviewTools(server: ToolRegistrar) {
|
|
|
140
162
|
)
|
|
141
163
|
}
|
|
142
164
|
// 汇总
|
|
143
|
-
const tP0 = rows.reduce((s: number, r) => s + (r.p0Count
|
|
144
|
-
const tP1 = rows.reduce((s: number, r) => s + (r.p1Count
|
|
165
|
+
const tP0 = rows.reduce((s: number, r) => s + (r.p0Count || 0), 0)
|
|
166
|
+
const tP1 = rows.reduce((s: number, r) => s + (r.p1Count || 0), 0)
|
|
145
167
|
lines.push(``, `总计: P0=${tP0} P1=${tP1}`)
|
|
146
168
|
return textResponse(lines.join("\n"))
|
|
147
169
|
} catch (e) {
|
|
@@ -158,7 +180,13 @@ export function registerReviewTools(server: ToolRegistrar) {
|
|
|
158
180
|
}, async (args: Record<string, unknown>) => {
|
|
159
181
|
try {
|
|
160
182
|
const { planName } = args as { planName: string }
|
|
161
|
-
const rows = await db.review.findMany({
|
|
183
|
+
const rows = await db.review.findMany({
|
|
184
|
+
where: {
|
|
185
|
+
projectKey: runtimeContext.projectKey,
|
|
186
|
+
adapterKey: runtimeContext.adapterKey,
|
|
187
|
+
planName,
|
|
188
|
+
},
|
|
189
|
+
})
|
|
162
190
|
|
|
163
191
|
if (!rows.length) return errorResponse(`无 ReviewRecord: ${planName}`)
|
|
164
192
|
|
|
@@ -169,10 +197,11 @@ export function registerReviewTools(server: ToolRegistrar) {
|
|
|
169
197
|
results.push(`⚠️ 文件不存在: ${rp || r.type}`)
|
|
170
198
|
continue
|
|
171
199
|
}
|
|
200
|
+
assertPathInRuntimeScope(runtimeContext, rp)
|
|
172
201
|
const content = readFileSync(rp, "utf-8")
|
|
173
|
-
const p0 = r.p0Count
|
|
174
|
-
const p1 = r.p1Count
|
|
175
|
-
const br = r.backflowRate
|
|
202
|
+
const p0 = r.p0Count || 0
|
|
203
|
+
const p1 = r.p1Count || 0
|
|
204
|
+
const br = r.backflowRate || 0
|
|
176
205
|
|
|
177
206
|
const metaLines = [
|
|
178
207
|
"<!-- REVIEW_META",
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/server"
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"
|
|
3
3
|
import { registerAll } from "./mcp-server/index.js"
|
|
4
|
+
import { redact } from "./mcp-server/shared/redact.js"
|
|
5
|
+
import { DATABASE_URL, getRuntimeContext } from "./mcp-server/shared/env.js"
|
|
6
|
+
import { prisma } from "./mcp-server/shared/prisma.js"
|
|
7
|
+
import { resolvePlanStatus } from "./mcp-server/shared/plan-lifecycle.js"
|
|
8
|
+
import { createPrismaPlanStatusStore } from "./mcp-server/shared/plan-status-store.js"
|
|
9
|
+
import {
|
|
10
|
+
createPgLifecycleListenClient,
|
|
11
|
+
PlanLifecycleSubscriber,
|
|
12
|
+
} from "./mcp-server/shared/plan-lifecycle-subscriber.js"
|
|
13
|
+
import { PlanRoundSubscriber } from "./mcp-server/shared/plan-round-subscriber.js"
|
|
14
|
+
import { queryPlanRounds, type PlanRoundReadClient } from "./mcp-server/shared/plan-round-store.js"
|
|
4
15
|
|
|
5
16
|
async function main() {
|
|
6
17
|
const server = new McpServer(
|
|
@@ -10,10 +21,58 @@ async function main() {
|
|
|
10
21
|
registerAll(server)
|
|
11
22
|
const transport = new StdioServerTransport()
|
|
12
23
|
await server.connect(transport)
|
|
24
|
+
let lifecycleSubscriber: PlanLifecycleSubscriber | undefined
|
|
25
|
+
let planRoundSubscriber: PlanRoundSubscriber | undefined
|
|
26
|
+
if (/^postgres(ql)?:\/\//.test(DATABASE_URL)) {
|
|
27
|
+
const context = getRuntimeContext()
|
|
28
|
+
const store = createPrismaPlanStatusStore(prisma)
|
|
29
|
+
lifecycleSubscriber = new PlanLifecycleSubscriber({
|
|
30
|
+
context,
|
|
31
|
+
clientFactory: () => createPgLifecycleListenClient(DATABASE_URL),
|
|
32
|
+
resolveStatus: ({ context: subscriberContext, planId }) => resolvePlanStatus(
|
|
33
|
+
store,
|
|
34
|
+
subscriberContext,
|
|
35
|
+
planId ? { planId } : { activeOnly: true },
|
|
36
|
+
),
|
|
37
|
+
onSnapshot: (snapshot, envelope) => {
|
|
38
|
+
const trigger = envelope ? `notify:${envelope.eventId}` : "initial"
|
|
39
|
+
console.error(`[ADD-MCP] lifecycle pull (${trigger}) ${JSON.stringify(snapshot)}`)
|
|
40
|
+
},
|
|
41
|
+
onError: (error) => console.error(`[ADD-MCP] lifecycle subscriber: ${redact(error.message)}`),
|
|
42
|
+
})
|
|
43
|
+
await lifecycleSubscriber.start().catch((error: unknown) => {
|
|
44
|
+
console.error(`[ADD-MCP] lifecycle initial pull failed: ${redact(error instanceof Error ? error.message : String(error))}`)
|
|
45
|
+
})
|
|
46
|
+
planRoundSubscriber = new PlanRoundSubscriber({
|
|
47
|
+
context,
|
|
48
|
+
clientFactory: () => createPgLifecycleListenClient(DATABASE_URL),
|
|
49
|
+
queryRounds: ({ context: subscriberContext, planId }) => queryPlanRounds(
|
|
50
|
+
prisma as unknown as PlanRoundReadClient,
|
|
51
|
+
{ context: subscriberContext, planId },
|
|
52
|
+
),
|
|
53
|
+
onSnapshot: (snapshot, envelope) => {
|
|
54
|
+
const trigger = envelope ? `notify:${envelope.eventId}` : "initial"
|
|
55
|
+
console.error(`[ADD-MCP] PlanRound pull (${trigger}) ${JSON.stringify({
|
|
56
|
+
contextId: snapshot.context.contextId,
|
|
57
|
+
planId: snapshot.planId,
|
|
58
|
+
planName: snapshot.planName,
|
|
59
|
+
records: snapshot.rounds.length,
|
|
60
|
+
})}`)
|
|
61
|
+
},
|
|
62
|
+
onError: (error) => console.error(`[ADD-MCP] PlanRound subscriber: ${redact(error.message)}`),
|
|
63
|
+
})
|
|
64
|
+
await planRoundSubscriber.start().catch((error: unknown) => {
|
|
65
|
+
console.error(`[ADD-MCP] PlanRound initial pull failed: ${redact(error instanceof Error ? error.message : String(error))}`)
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
process.stdin.once("end", () => {
|
|
69
|
+
void lifecycleSubscriber?.stop()
|
|
70
|
+
void planRoundSubscriber?.stop()
|
|
71
|
+
})
|
|
13
72
|
console.error("[ADD-MCP] add-dev-tools MCP server started on stdio")
|
|
14
73
|
}
|
|
15
74
|
|
|
16
75
|
main().catch((error) => {
|
|
17
|
-
console.error("[ADD-MCP] Fatal error:", error)
|
|
76
|
+
console.error("[ADD-MCP] Fatal error:", redact(error instanceof Error ? error.message : String(error)))
|
|
18
77
|
process.exit(1)
|
|
19
78
|
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { getRuntimeContext } from "./mcp-server/shared/env.js"
|
|
3
|
+
import { prisma } from "./mcp-server/shared/prisma.js"
|
|
4
|
+
import { resolvePlanStatus } from "./mcp-server/shared/plan-lifecycle.js"
|
|
5
|
+
import { createPrismaPlanStatusStore } from "./mcp-server/shared/plan-status-store.js"
|
|
6
|
+
|
|
7
|
+
async function main(): Promise<void> {
|
|
8
|
+
const context = getRuntimeContext()
|
|
9
|
+
const store = createPrismaPlanStatusStore(prisma)
|
|
10
|
+
const snapshot = await resolvePlanStatus(store, context, { activeOnly: true })
|
|
11
|
+
process.stdout.write(`${JSON.stringify(snapshot)}\n`)
|
|
12
|
+
if (snapshot.availability === "STATUS_UNAVAILABLE") process.exitCode = 3
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
main()
|
|
16
|
+
.catch((error: unknown) => {
|
|
17
|
+
process.stderr.write(`[ADD plan-status-bridge] ${error instanceof Error ? error.message : String(error)}\n`)
|
|
18
|
+
process.exitCode = 3
|
|
19
|
+
})
|
|
20
|
+
.finally(async () => {
|
|
21
|
+
const runtimeClient = prisma as unknown as Record<string, unknown>
|
|
22
|
+
const disconnect = runtimeClient.$disconnect
|
|
23
|
+
if (typeof disconnect === "function") {
|
|
24
|
+
await Reflect.apply(disconnect, prisma, []).catch(() => undefined)
|
|
25
|
+
}
|
|
26
|
+
})
|
|
@@ -379,7 +379,7 @@ Plan 级闭包: {业务功能描述}
|
|
|
379
379
|
|
|
380
380
|
**活跃判定规则**:
|
|
381
381
|
|
|
382
|
-
1.
|
|
382
|
+
1. **判定输入**:当前 adapter 自己的 `{{magicDir}}/plans/`。各 adapter 的 Plan/Spec/Route/Review/HITL 相互独立,禁止跨 adapter fallback
|
|
383
383
|
2. **索引优先**:先读 `plans/index.md` 匹配 planKeyword → 无匹配才 glob `*-plan-v*.md`
|
|
384
384
|
3. **活跃定义**:读 add-route 文件 → 存在 `[ ]` 未勾选的 Step 产出项 = 活跃(进行中);全部 `[x]` = 已收敛(不活跃)
|
|
385
385
|
4. **多 Plan 冲突**:多个 Plan 均有 `[ ]` → 取 add-route 文件最近修改时间(`mtime`)最大的 Plan
|
|
@@ -863,7 +863,7 @@ check_add_route_completeness({ planKeyword: "<Plan 核心关键词>" })
|
|
|
863
863
|
|
|
864
864
|
### 3.5.3 生成实现审查文档
|
|
865
865
|
|
|
866
|
-
|
|
866
|
+
读取 `review-implementation-template.md`,将实现审查发现整理为 `dimensions`,调用 `create_hitl({ planName: "{name}-review-implementation", type: "PLAN_REVIEW", dimensions })`。`create_hitl` 自动生成 `*.hitl.md` 审批提案并写入 HITL 记录;人类审核后调用 `update_hitl` 完成拍板。通过 `status_hitl` 确认状态为 `TONGYI` 后,方可逐条展开详细分析并写入 `{{magicDir}}/reviews/`;若为 `BOHUI`,修订发现后调用 `create_hitl` 新建 round。
|
|
867
867
|
|
|
868
868
|
### 3.5.4 生成运行时审查文档(所有 [T] 项通过后)
|
|
869
869
|
|
|
@@ -1,58 +1,61 @@
|
|
|
1
1
|
{
|
|
2
|
+
"_generated": true,
|
|
3
|
+
"_generated_source": "guard-rules.toml",
|
|
2
4
|
"template": "add-route-template.md",
|
|
3
|
-
"shared_by": [
|
|
4
|
-
"add-route-template.md",
|
|
5
|
-
"add-route-template-heavyweight.md"
|
|
6
|
-
],
|
|
7
5
|
"sections": [
|
|
8
6
|
{
|
|
9
7
|
"id": "step0",
|
|
10
|
-
"
|
|
11
|
-
"
|
|
8
|
+
"required": true,
|
|
9
|
+
"heading": "## Step 0"
|
|
12
10
|
},
|
|
13
11
|
{
|
|
14
12
|
"id": "step0_plan_track",
|
|
15
|
-
"pattern": "plan_track\\(\\{ planName:",
|
|
16
13
|
"required": true,
|
|
14
|
+
"anchor": "plan_track",
|
|
17
15
|
"within": "## Step 0",
|
|
16
|
+
"pattern": "plan_track\\(\\{ planName:",
|
|
18
17
|
"description": "Step 0 产出必须包含 plan_track 落库步骤"
|
|
19
18
|
},
|
|
20
19
|
{
|
|
21
20
|
"id": "step1",
|
|
22
|
-
"
|
|
23
|
-
"
|
|
21
|
+
"required": true,
|
|
22
|
+
"heading": "## Step 1"
|
|
24
23
|
},
|
|
25
24
|
{
|
|
26
25
|
"id": "step2",
|
|
27
|
-
"
|
|
28
|
-
"
|
|
26
|
+
"required": true,
|
|
27
|
+
"heading": "## Step 2"
|
|
29
28
|
},
|
|
30
29
|
{
|
|
31
30
|
"id": "step3",
|
|
32
|
-
"
|
|
33
|
-
"
|
|
31
|
+
"required": true,
|
|
32
|
+
"heading": "## Step 3"
|
|
34
33
|
},
|
|
35
34
|
{
|
|
36
35
|
"id": "step3_5",
|
|
37
|
-
"
|
|
38
|
-
"
|
|
36
|
+
"required": false,
|
|
37
|
+
"heading": "## Step 3.5"
|
|
39
38
|
},
|
|
40
39
|
{
|
|
41
40
|
"id": "step4",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
41
|
+
"required": true,
|
|
42
|
+
"heading": "## Step 4"
|
|
44
43
|
},
|
|
45
44
|
{
|
|
46
45
|
"id": "step5",
|
|
47
|
-
"
|
|
48
|
-
"
|
|
46
|
+
"required": true,
|
|
47
|
+
"heading": "## Step 5"
|
|
49
48
|
},
|
|
50
49
|
{
|
|
51
50
|
"id": "step8",
|
|
52
|
-
"
|
|
53
|
-
"
|
|
51
|
+
"required": true,
|
|
52
|
+
"heading": "## Step 8"
|
|
54
53
|
}
|
|
55
54
|
],
|
|
55
|
+
"shared_by": [
|
|
56
|
+
"add-route-template.md",
|
|
57
|
+
"add-route-template-heavyweight.md"
|
|
58
|
+
],
|
|
56
59
|
"placeholders": [
|
|
57
60
|
"{需求域名}",
|
|
58
61
|
"{核心内容}",
|
|
@@ -71,38 +74,3 @@
|
|
|
71
74
|
"阶段"
|
|
72
75
|
]
|
|
73
76
|
}
|
|
74
|
-
{
|
|
75
|
-
"template": "add-route-template.md",
|
|
76
|
-
"shared_by": [
|
|
77
|
-
"add-route-template.md",
|
|
78
|
-
"add-route-template-heavyweight.md"
|
|
79
|
-
],
|
|
80
|
-
"sections": [
|
|
81
|
-
{
|
|
82
|
-
"id": "step0",
|
|
83
|
-
"heading": "## Step 0",
|
|
84
|
-
"required": true
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
"id": "step3",
|
|
88
|
-
"heading": "## Step 3",
|
|
89
|
-
"required": true
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
"id": "step8",
|
|
93
|
-
"heading": "## Step 8",
|
|
94
|
-
"required": true
|
|
95
|
-
}
|
|
96
|
-
],
|
|
97
|
-
"placeholders": [
|
|
98
|
-
"{需求域名}",
|
|
99
|
-
"{核心内容}",
|
|
100
|
-
"{版本号}"
|
|
101
|
-
],
|
|
102
|
-
"forbidden_terms": [
|
|
103
|
-
"Phase",
|
|
104
|
-
"Round",
|
|
105
|
-
"轮次",
|
|
106
|
-
"阶段"
|
|
107
|
-
]
|
|
108
|
-
}
|
|
@@ -169,7 +169,7 @@
|
|
|
169
169
|
| 发起时机 | 契约新建 / 契约重大变更(参与者增减、边界重划、触发条件变更) |
|
|
170
170
|
| round 机制 | 复用 HitlRecord.round(同一契约多次变更递增 round) |
|
|
171
171
|
| 与子 Plan 审批关系 | 契约审批是**总控级**(planName=总控 Plan);子 Plan 审批独立进行(互不阻塞) |
|
|
172
|
-
| 哨兵 |
|
|
172
|
+
| 哨兵 | `{{magicDir}}/hitl/.tongyi-{contractName}`(复用当前 adapter 的哨兵机制) |
|
|
173
173
|
| 审计 | 契约审批记录入 HitlRecord(type=COLLAB_CONTRACT)→ query_audit_logs 可查 |
|
|
174
174
|
|
|
175
175
|
---
|