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
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { readdir, readFile } from "fs/promises"
|
|
2
|
+
import { basename, join, relative } from "path"
|
|
3
|
+
|
|
4
|
+
export type SchemaView = "business" | "add" | "all"
|
|
5
|
+
export type SchemaTopologyMode = "single" | "split"
|
|
6
|
+
export type SchemaBoundary = "shared" | "business" | "add"
|
|
7
|
+
|
|
8
|
+
export interface PrismaDeclaration {
|
|
9
|
+
kind: "model" | "enum"
|
|
10
|
+
name: string
|
|
11
|
+
sourcePath: string
|
|
12
|
+
sourceFile: string
|
|
13
|
+
boundary: SchemaBoundary
|
|
14
|
+
body: string
|
|
15
|
+
fieldCount: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PrismaSchemaFile {
|
|
19
|
+
path: string
|
|
20
|
+
file: string
|
|
21
|
+
boundary: SchemaBoundary
|
|
22
|
+
declarations: PrismaDeclaration[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface PrismaSchemaTopology {
|
|
26
|
+
mode: SchemaTopologyMode
|
|
27
|
+
view: SchemaView
|
|
28
|
+
schemaRoot: string
|
|
29
|
+
files: PrismaSchemaFile[]
|
|
30
|
+
models: PrismaDeclaration[]
|
|
31
|
+
enums: PrismaDeclaration[]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function extractBlock(source: string, braceIndex: number): string {
|
|
35
|
+
let depth = 1
|
|
36
|
+
let cursor = braceIndex + 1
|
|
37
|
+
while (depth > 0 && cursor < source.length) {
|
|
38
|
+
if (source[cursor] === "{") depth += 1
|
|
39
|
+
if (source[cursor] === "}") depth -= 1
|
|
40
|
+
cursor += 1
|
|
41
|
+
}
|
|
42
|
+
if (depth !== 0) throw new Error("Prisma declaration 花括号未闭合")
|
|
43
|
+
return source.slice(braceIndex, cursor)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function parsePrismaDeclarations(
|
|
47
|
+
source: string,
|
|
48
|
+
sourcePath: string,
|
|
49
|
+
boundary: SchemaBoundary,
|
|
50
|
+
): PrismaDeclaration[] {
|
|
51
|
+
const declarations: PrismaDeclaration[] = []
|
|
52
|
+
const declarationRegex = /^\s*(model|enum)\s+(\w+)\s*\{/gm
|
|
53
|
+
let match: RegExpExecArray | null
|
|
54
|
+
while ((match = declarationRegex.exec(source)) !== null) {
|
|
55
|
+
const braceIndex = source.indexOf("{", match.index)
|
|
56
|
+
const body = extractBlock(source, braceIndex)
|
|
57
|
+
const kind = match[1] as "model" | "enum"
|
|
58
|
+
declarations.push({
|
|
59
|
+
kind,
|
|
60
|
+
name: match[2],
|
|
61
|
+
sourcePath,
|
|
62
|
+
sourceFile: basename(sourcePath),
|
|
63
|
+
boundary,
|
|
64
|
+
body,
|
|
65
|
+
fieldCount: kind === "model"
|
|
66
|
+
? body.split("\n").filter(line => {
|
|
67
|
+
const trimmed = line.trim()
|
|
68
|
+
return trimmed.length > 0 && trimmed !== "{" && trimmed !== "}" && !trimmed.startsWith("//") && !trimmed.startsWith("@@")
|
|
69
|
+
}).length
|
|
70
|
+
: 0,
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
return declarations
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function boundaryFor(file: string, mode: SchemaTopologyMode): SchemaBoundary {
|
|
77
|
+
if (mode === "single") return "shared"
|
|
78
|
+
return file === "add.prisma" ? "add" : "business"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function includedInView(file: string, view: SchemaView): boolean {
|
|
82
|
+
if (view === "all") return true
|
|
83
|
+
return view === "add" ? file === "add.prisma" : file !== "add.prisma"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function loadPrismaSchemaTopology(input: {
|
|
87
|
+
projectRoot: string
|
|
88
|
+
splitDatabase: boolean
|
|
89
|
+
view?: SchemaView
|
|
90
|
+
}): Promise<PrismaSchemaTopology> {
|
|
91
|
+
const schemaRoot = join(input.projectRoot, "prisma")
|
|
92
|
+
const mode: SchemaTopologyMode = input.splitDatabase ? "split" : "single"
|
|
93
|
+
const view = input.view ?? "all"
|
|
94
|
+
let names: string[]
|
|
95
|
+
try {
|
|
96
|
+
names = (await readdir(schemaRoot)).filter(file => file.endsWith(".prisma")).sort()
|
|
97
|
+
} catch {
|
|
98
|
+
throw new Error(`未找到 Prisma schema 目录: ${schemaRoot}`)
|
|
99
|
+
}
|
|
100
|
+
if (names.length === 0) throw new Error(`Prisma schema 目录中没有 *.prisma: ${schemaRoot}`)
|
|
101
|
+
|
|
102
|
+
const selected = names.filter(file => includedInView(file, view))
|
|
103
|
+
if (selected.length === 0) {
|
|
104
|
+
throw new Error(`Schema view=${view} 没有可扫描文件;mode=${mode};全部文件=${names.join(", ")}`)
|
|
105
|
+
}
|
|
106
|
+
const files: PrismaSchemaFile[] = []
|
|
107
|
+
for (const file of selected) {
|
|
108
|
+
const path = join(schemaRoot, file)
|
|
109
|
+
const boundary = boundaryFor(file, mode)
|
|
110
|
+
const source = await readFile(path, "utf8")
|
|
111
|
+
files.push({ path, file, boundary, declarations: parsePrismaDeclarations(source, path, boundary) })
|
|
112
|
+
}
|
|
113
|
+
const declarations = files.flatMap(file => file.declarations)
|
|
114
|
+
const models = declarations.filter(item => item.kind === "model")
|
|
115
|
+
const enums = declarations.filter(item => item.kind === "enum")
|
|
116
|
+
if (models.length === 0) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
`Schema 诊断: 检测到 ${files.length} 个 Prisma 文件但 view=${view} 解析为 0 个模型;` +
|
|
119
|
+
`mode=${mode};扫描=${files.map(file => relative(input.projectRoot, file.path)).join(", ")}`,
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const byName = new Map<string, PrismaDeclaration[]>()
|
|
124
|
+
for (const model of models) {
|
|
125
|
+
const key = model.name.toLowerCase()
|
|
126
|
+
byName.set(key, [...(byName.get(key) ?? []), model])
|
|
127
|
+
}
|
|
128
|
+
const conflicts = [...byName.entries()].filter(([, entries]) => entries.length > 1)
|
|
129
|
+
if (conflicts.length > 0) {
|
|
130
|
+
const details = conflicts.map(([name, entries]) =>
|
|
131
|
+
`${name}: ${entries.map(entry => `${relative(input.projectRoot, entry.sourcePath)}[${entry.boundary}]`).join(", ")}`,
|
|
132
|
+
)
|
|
133
|
+
throw new Error(`Schema 模型冲突(fail closed): ${details.join("; ")}`)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return { mode, view, schemaRoot, files, models, enums }
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function formatSchemaTopology(topology: PrismaSchemaTopology, projectRoot: string): string {
|
|
140
|
+
const lines = [
|
|
141
|
+
"=== Prisma Schema 拓扑 ===",
|
|
142
|
+
`mode: ${topology.mode}`,
|
|
143
|
+
`view: ${topology.view}`,
|
|
144
|
+
`databaseBoundaries: ${topology.mode === "single" ? "shared (business + ADD 同库)" : "business, add (分库;all 仅汇总展示,不代表可跨库 join)"}`,
|
|
145
|
+
`scannedFiles: ${topology.files.length}`,
|
|
146
|
+
]
|
|
147
|
+
for (const file of topology.files) {
|
|
148
|
+
lines.push(` - ${relative(projectRoot, file.path)} [${file.boundary}] models=${file.declarations.filter(item => item.kind === "model").length}`)
|
|
149
|
+
}
|
|
150
|
+
lines.push("", `模型 (${topology.models.length} 个):`)
|
|
151
|
+
for (const model of topology.models) {
|
|
152
|
+
lines.push(` ${model.name} (${model.fieldCount} 字段) — ${relative(projectRoot, model.sourcePath)} [${model.boundary}]`)
|
|
153
|
+
}
|
|
154
|
+
if (topology.enums.length > 0) {
|
|
155
|
+
lines.push("", `枚举 (${topology.enums.length} 个):`)
|
|
156
|
+
for (const item of topology.enums) lines.push(` ${item.name} — ${relative(projectRoot, item.sourcePath)} [${item.boundary}]`)
|
|
157
|
+
}
|
|
158
|
+
return lines.join("\n")
|
|
159
|
+
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import * as z from "zod/v4"
|
|
2
|
+
import { createHash } from "crypto"
|
|
2
3
|
import type { ToolRegistrar } from "./registrar.js"
|
|
3
4
|
import { textResponse, errorResponse } from "../shared/response.js"
|
|
4
5
|
import { prisma } from "../shared/prisma.js"
|
|
5
|
-
import { PROJECT_ID, PROJECT_ROOT } from "../shared/env.js"
|
|
6
|
+
import { PROJECT_ID, PROJECT_ROOT, getRuntimeContext } from "../shared/env.js"
|
|
6
7
|
import type { AddUserRow, DevOperationRow } from "../shared/db-types.js"
|
|
7
8
|
import { AddUserRowSchema, DevOperationRowSchema, validatedDelegate } from "../shared/db-types.js"
|
|
8
9
|
|
|
9
10
|
export function registerAuditTools(server: ToolRegistrar) {
|
|
11
|
+
const runtimeContext = getRuntimeContext()
|
|
10
12
|
|
|
11
13
|
// 无类型边界单点(zod 托管):动态 client → 运行期校验的泛型委托
|
|
12
14
|
const devDb = validatedDelegate<DevOperationRow>(prisma.devOperation, DevOperationRowSchema, "DevOperation")
|
|
@@ -30,7 +32,10 @@ export function registerAuditTools(server: ToolRegistrar) {
|
|
|
30
32
|
}, async (args: Record<string, string | number | undefined>, _ctx: unknown) => {
|
|
31
33
|
try {
|
|
32
34
|
const { targetType, action, targetId, planKeyword, keyword, sinceMinutes, limit = 20 } = args
|
|
33
|
-
const where: Record<string, unknown> = {
|
|
35
|
+
const where: Record<string, unknown> = {
|
|
36
|
+
projectKey: runtimeContext.projectKey,
|
|
37
|
+
producerAdapterKey: runtimeContext.adapterKey,
|
|
38
|
+
}
|
|
34
39
|
if (sinceMinutes !== undefined) where.createdAt = { gte: new Date(Date.now() - Number(sinceMinutes) * 60 * 1000) }
|
|
35
40
|
if (targetType) where.targetType = s(targetType); if (action) where.action = s(action); if (targetId) where.targetId = s(targetId); if (planKeyword) where.planKeyword = s(planKeyword)
|
|
36
41
|
const kw = s(keyword)
|
|
@@ -44,6 +49,7 @@ export function registerAuditTools(server: ToolRegistrar) {
|
|
|
44
49
|
const lines = [`=== 开发操作审计日志 (条件: ${fl.length > 0 ? fl.join(", ") : "无过滤条件(最近全部)"}) ===`, `服务项目: ${PROJECT_ID} (${PROJECT_ROOT})`, `共 ${logs.length} 条记录`]
|
|
45
50
|
for (let i = 0; i < logs.length; i++) {
|
|
46
51
|
const l = logs[i]; lines.push(`[${i+1}] ${l.createdAt.toISOString()}`, ` action: ${l.action} | targetType: ${l.targetType} | targetId: ${l.targetId || "(无)"}`)
|
|
52
|
+
lines.push(` beforeState: ${JSON.stringify(l.beforeState)}`, ` afterState: ${JSON.stringify(l.afterState)}`)
|
|
47
53
|
if (l.reason) lines.push(` reason: ${l.reason}`); if (l.planKeyword) lines.push(` planKeyword: ${l.planKeyword}`)
|
|
48
54
|
}
|
|
49
55
|
if (planKeyword) { lines.push("=== Plan 分组 ===", `planKeyword=${planKeyword} 的操作链:`); for (const l of logs) lines.push(` ${l.createdAt.toISOString().slice(11,19)} ${l.action}`) }
|
|
@@ -60,22 +66,58 @@ export function registerAuditTools(server: ToolRegistrar) {
|
|
|
60
66
|
targetType: z.string().describe("目标类型: 'API_ROUTE', 'COMPONENT', 'SCHEMA', 'RULE', 'DOC', 'PLAN', 'SPEC', 'SKILL', 'AGENT' 等"),
|
|
61
67
|
targetId: z.string().optional().describe("目标标识(相对路径)"),
|
|
62
68
|
planKeyword: z.string().optional().describe("关联 Plan 的关键词"),
|
|
63
|
-
beforeState: z.string().describe("
|
|
64
|
-
afterState: z.string().describe("
|
|
69
|
+
beforeState: z.string().describe("操作前的状态(非 null JSON 对象或数组字符串)。必填,用于审计回溯。"),
|
|
70
|
+
afterState: z.string().describe("操作后的状态(非 null JSON 对象或数组字符串)。必填,用于审计回溯。"),
|
|
65
71
|
reason: z.string().optional().describe("操作原因"),
|
|
72
|
+
operationKey: z.string().optional().describe("调用方提供的幂等键;未提供时按本次结构化操作内容计算"),
|
|
66
73
|
}),
|
|
67
74
|
}, async (args: Record<string, string | number | undefined>, _ctx: unknown) => {
|
|
68
75
|
try {
|
|
69
|
-
const { action, targetType, targetId, planKeyword, beforeState, afterState, reason } = args
|
|
76
|
+
const { action, targetType, targetId, planKeyword, beforeState, afterState, reason, operationKey } = args
|
|
70
77
|
const tId = s(targetId)
|
|
71
78
|
const pathWarnings: string[] = []
|
|
72
79
|
if (tId && (tId.startsWith("/") || /^[A-Z]:\\/.test(tId))) pathWarnings.push(`⚠️ targetId 使用了绝对路径: "${tId}"。请使用相对路径。`)
|
|
80
|
+
if (!s(beforeState).trim() || !s(afterState).trim()) return errorResponse("beforeState/afterState 必须提供非空 JSON 字符串。")
|
|
73
81
|
let parsedBefore: unknown, parsedAfter: unknown
|
|
74
82
|
try { if (beforeState) parsedBefore = JSON.parse(s(beforeState)); if (afterState) parsedAfter = JSON.parse(s(afterState)) } catch { return errorResponse("beforeState/afterState 必须是有效的 JSON 字符串。") }
|
|
83
|
+
const isStructuredState = (value: unknown): value is Record<string, unknown> | unknown[] => typeof value === "object" && value !== null
|
|
84
|
+
if (!isStructuredState(parsedBefore) || !isStructuredState(parsedAfter)) return errorResponse("beforeState/afterState 必须是非 null 的 JSON 对象或数组。")
|
|
75
85
|
let systemUser = await userDb.findUnique({ where: { username: "ai-assistant" }, select: { id: true } })
|
|
76
86
|
if (!systemUser) systemUser = await userDb.create({ data: { id: "ai-assistant", username: "ai-assistant", email: "ai-assistant@internal" } })
|
|
77
|
-
const
|
|
78
|
-
|
|
87
|
+
const operationPayload = {
|
|
88
|
+
projectKey: runtimeContext.projectKey,
|
|
89
|
+
producerAdapterKey: runtimeContext.adapterKey,
|
|
90
|
+
action: s(action),
|
|
91
|
+
targetType: s(targetType),
|
|
92
|
+
targetId: tId || "unknown",
|
|
93
|
+
planKeyword: s(planKeyword) || "unknown",
|
|
94
|
+
beforeState: parsedBefore,
|
|
95
|
+
afterState: parsedAfter,
|
|
96
|
+
reason: s(reason) || null,
|
|
97
|
+
}
|
|
98
|
+
const resolvedOperationKey = s(operationKey).trim() || createHash("sha256")
|
|
99
|
+
.update(JSON.stringify(operationPayload))
|
|
100
|
+
.digest("hex")
|
|
101
|
+
const scopedData: Partial<DevOperationRow> = {
|
|
102
|
+
...operationPayload,
|
|
103
|
+
userId: systemUser.id,
|
|
104
|
+
contextId: runtimeContext.contextId,
|
|
105
|
+
toolName: "record_dev_operation",
|
|
106
|
+
operationKey: resolvedOperationKey,
|
|
107
|
+
}
|
|
108
|
+
const log = await devDb.upsert({
|
|
109
|
+
where: {
|
|
110
|
+
projectKey_producerAdapterKey_toolName_operationKey: {
|
|
111
|
+
projectKey: runtimeContext.projectKey,
|
|
112
|
+
producerAdapterKey: runtimeContext.adapterKey,
|
|
113
|
+
toolName: "record_dev_operation",
|
|
114
|
+
operationKey: resolvedOperationKey,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
create: scopedData,
|
|
118
|
+
update: {},
|
|
119
|
+
})
|
|
120
|
+
const lines = [`✅ 开发操作已记录`, ` 落库项目: ${PROJECT_ID} (${PROJECT_ROOT})`, ` ID: ${log.id}`, ` action: ${s(action)}`, ` targetType: ${s(targetType)}`, ` targetId: ${tId || "unknown"}`, ` planKeyword: ${s(planKeyword) || "unknown"}`, ` beforeState: ${JSON.stringify(log.beforeState)}`, ` afterState: ${JSON.stringify(log.afterState)}`, ` createdAt: ${log.createdAt.toISOString()}`]
|
|
79
121
|
if (pathWarnings.length > 0) { lines.push(""); lines.push(...pathWarnings) }
|
|
80
122
|
lines.push("", `📋 落库回查(必须执行):`, tId ? ` query_audit_logs({ targetId: "${tId}" }) — 确认本条记录已写入` : "")
|
|
81
123
|
return textResponse(lines.join("\n"))
|
|
@@ -6,13 +6,23 @@ import { stat } from "fs/promises"
|
|
|
6
6
|
import { existsSync } from "fs"
|
|
7
7
|
import { join, relative } from "path"
|
|
8
8
|
import { textResponse, errorResponse } from "../shared/response.js"
|
|
9
|
-
import { readFileSafe
|
|
9
|
+
import { readFileSafe } from "../shared/fs.js"
|
|
10
10
|
import { PROJECT_ROOT, MAGIC_DIR } from "../shared/fs.js"
|
|
11
11
|
import { prisma } from "../shared/prisma.js"
|
|
12
|
-
import type { PlanRow } from "../shared/db-types.js"
|
|
13
|
-
import { PlanRowSchema, validatedDelegate } from "../shared/db-types.js"
|
|
12
|
+
import type { PlanRow, ReviewRow } from "../shared/db-types.js"
|
|
13
|
+
import { PlanRowSchema, ReviewRowSchema, validatedDelegate } from "../shared/db-types.js"
|
|
14
|
+
import { getRuntimeContext } from "../shared/env.js"
|
|
15
|
+
import { resolvePlanStatus } from "../shared/plan-lifecycle.js"
|
|
16
|
+
import { createPrismaPlanStatusStore } from "../shared/plan-status-store.js"
|
|
17
|
+
import { assertPathInRuntimeScope } from "../shared/runtime-context.js"
|
|
18
|
+
import {
|
|
19
|
+
formatSchemaTopology,
|
|
20
|
+
loadPrismaSchemaTopology,
|
|
21
|
+
type SchemaView,
|
|
22
|
+
} from "../shared/schema-topology.js"
|
|
14
23
|
|
|
15
24
|
export function registerContextTools(server: ToolRegistrar) {
|
|
25
|
+
const runtimeContext = getRuntimeContext()
|
|
16
26
|
|
|
17
27
|
// ===== get_project_context (L166-430) =====
|
|
18
28
|
server.registerTool(
|
|
@@ -91,88 +101,73 @@ export function registerContextTools(server: ToolRegistrar) {
|
|
|
91
101
|
|
|
92
102
|
if (scope === "all" || scope === "structure" || scope === "add-state") {
|
|
93
103
|
parts.push("=== ADD 工作流状态 ===")
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const specsDir = join(PROJECT_ROOT, MAGIC_DIR, "specs")
|
|
104
|
+
parts.push(`contextId: ${runtimeContext.contextId}`)
|
|
105
|
+
parts.push("source: database")
|
|
97
106
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const indicators = ["add-route", "specs/", "handoff", "Task Group", "perExpertTopK", "agri_tech", "ChromaCollectionManager", "迁移路线图", "8 个 Expert", "冗余策略"]
|
|
143
|
-
let hit = 0
|
|
144
|
-
for (const ind of indicators) { if (planContent.toLowerCase().includes(ind.toLowerCase())) hit++ }
|
|
145
|
-
reviewBackflowRate = Math.min(100, Math.round((hit / Math.max(reviewP0Count + reviewP1Count, 1)) * 100))
|
|
146
|
-
}
|
|
147
|
-
parts.push(""); parts.push(`关联 Review: ${matchingReview}`)
|
|
148
|
-
parts.push(` P0 问题: ${reviewP0Count} 个, P1 问题: ${reviewP1Count} 个`)
|
|
149
|
-
parts.push(` 回流状态: 约 ${reviewBackflowRate}%`)
|
|
150
|
-
parts.push(reviewBackflowRate < 70 ? " ⚠️ Review 结论未充分回流至 Plan — 需执行 0.6.5 卡位" : " ✅ Review 结论基本回流至 Plan")
|
|
151
|
-
} else { parts.push(""); parts.push("关联 Review: 无(该 Plan 尚未生成方案评审)") }
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (activePlan) {
|
|
155
|
-
const planKeyword = activePlan.replace(/-plan-v\d+\.md$/, "")
|
|
156
|
-
const specDirs = existsSync(specsDir) ? await readdir(specsDir) : []
|
|
157
|
-
const matchingSpec = specDirs.find(d => d.toLowerCase().includes(planKeyword.toLowerCase()))
|
|
107
|
+
const planDb = validatedDelegate<PlanRow>(prisma.planRecord, PlanRowSchema, "PlanRecord")
|
|
108
|
+
const reviewDb = validatedDelegate<ReviewRow>(prisma.reviewRecord, ReviewRowSchema, "ReviewRecord")
|
|
109
|
+
const resolution = await resolvePlanStatus(
|
|
110
|
+
createPrismaPlanStatusStore(prisma),
|
|
111
|
+
runtimeContext,
|
|
112
|
+
{ activeOnly: true },
|
|
113
|
+
)
|
|
114
|
+
let activePlan: PlanRow | null = null
|
|
115
|
+
let reviewRows: ReviewRow[] = []
|
|
116
|
+
if (resolution.availability === "STATUS_UNAVAILABLE") {
|
|
117
|
+
parts.push(`状态: ⛔ STATUS_UNAVAILABLE — ${resolution.reason}`)
|
|
118
|
+
parts.push("裁决: fail closed;未回退 Plan/Handoff/add-route 文件猜测")
|
|
119
|
+
} else if (resolution.planName === null) {
|
|
120
|
+
parts.push("活跃 Plan: 无(DB lifecycle 无 ACTIVE/BLOCKED)")
|
|
121
|
+
} else {
|
|
122
|
+
activePlan = await planDb.findFirst({
|
|
123
|
+
where: {
|
|
124
|
+
projectKey: runtimeContext.projectKey,
|
|
125
|
+
adapterKey: runtimeContext.adapterKey,
|
|
126
|
+
planName: resolution.planName,
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
if (!activePlan) throw new Error(`resolver 返回 Plan 后记录消失: ${resolution.planName}`)
|
|
130
|
+
assertPathInRuntimeScope(runtimeContext, activePlan.planPath)
|
|
131
|
+
parts.push(`活跃 Plan: ${activePlan.planName}`)
|
|
132
|
+
parts.push(` lifecycle: ${resolution.lifecycle} | revision: ${resolution.revision}`)
|
|
133
|
+
parts.push(` approval: ${resolution.approvalStatus ?? "—"}`)
|
|
134
|
+
parts.push(` tasks: ${resolution.progress.doneTasks}/${resolution.progress.totalTasks}`)
|
|
135
|
+
parts.push(` Plan: ${activePlan.planPath}`)
|
|
136
|
+
parts.push(` add-route: ${activePlan.addRoutePath && existsSync(activePlan.addRoutePath) ? `✅ ${activePlan.addRoutePath}` : "❌ 缺失"}`)
|
|
137
|
+
parts.push(" handoff: 不参与 active 裁决(仅 Step 8 交接产物)")
|
|
138
|
+
parts.push("")
|
|
139
|
+
parts.push("Specs:")
|
|
140
|
+
parts.push(` spec.md: ${activePlan.specPath && existsSync(activePlan.specPath) ? "✅" : "❌"}`)
|
|
141
|
+
parts.push(` tasks.md: ${activePlan.tasksPath && existsSync(activePlan.tasksPath) ? "✅" : "❌"}`)
|
|
142
|
+
parts.push(` checklist.md: ${activePlan.checklistPath && existsSync(activePlan.checklistPath) ? "✅" : "❌"}`)
|
|
143
|
+
reviewRows = await reviewDb.findMany({
|
|
144
|
+
where: {
|
|
145
|
+
projectKey: runtimeContext.projectKey,
|
|
146
|
+
adapterKey: runtimeContext.adapterKey,
|
|
147
|
+
planName: activePlan.planName,
|
|
148
|
+
},
|
|
149
|
+
orderBy: { updatedAt: "desc" },
|
|
150
|
+
})
|
|
158
151
|
parts.push("")
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
} else { parts.push("Specs: ❌ 缺失") }
|
|
152
|
+
if (reviewRows.length === 0) {
|
|
153
|
+
parts.push("关联 Review: 无")
|
|
154
|
+
} else {
|
|
155
|
+
parts.push(`关联 Review: ${reviewRows.length} 条(DB scoped)`)
|
|
156
|
+
for (const review of reviewRows) {
|
|
157
|
+
parts.push(` ${review.type}: P0=${review.p0Count}, P1=${review.p1Count}, backflow=${review.backflowRate}`)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
168
160
|
}
|
|
169
161
|
|
|
170
162
|
parts.push(""); parts.push("=== 悬空 Plan 对账(记录在、文件缺失) ===")
|
|
171
|
-
// 孤儿可见性:create_hitl 预置行被中断 / 放弃未清理 → 开局即提示,不静默累积
|
|
172
163
|
try {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
164
|
+
const allPlans = await planDb.findMany({
|
|
165
|
+
where: {
|
|
166
|
+
projectKey: runtimeContext.projectKey,
|
|
167
|
+
adapterKey: runtimeContext.adapterKey,
|
|
168
|
+
},
|
|
169
|
+
orderBy: { createdAt: "desc" },
|
|
170
|
+
})
|
|
176
171
|
const orphans = allPlans.filter(p => !existsSync(p.planPath))
|
|
177
172
|
if (orphans.length > 0) {
|
|
178
173
|
const cutoff = Date.now() - 14 * 86400000
|
|
@@ -185,20 +180,19 @@ export function registerContextTools(server: ToolRegistrar) {
|
|
|
185
180
|
|
|
186
181
|
parts.push(""); parts.push("=== 待执行 ADD 操作(按流程顺序) ===")
|
|
187
182
|
const todoItems: string[] = []
|
|
188
|
-
if (
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
183
|
+
if (resolution.availability === "STATUS_UNAVAILABLE") {
|
|
184
|
+
todoItems.push("1. [阻断] 恢复 DB/MCP resolver;禁止按文件继续")
|
|
185
|
+
} else if (!activePlan) { todoItems.push("1. [未开始] 用户提出需求 → 生成 Plan + HITL") }
|
|
186
|
+
else if (resolution.planName !== null) {
|
|
187
|
+
if (resolution.approvalStatus !== "TONGYI") todoItems.push("1. [HITL] 完成当前 Plan 审批")
|
|
188
|
+
if (reviewRows.length === 0) todoItems.push("2. [Step 0] 生成并追踪 Plan Review")
|
|
189
|
+
if (reviewRows.some(review => review.p0Count + review.p1Count > 0 && review.backflowRate === 0)) {
|
|
190
|
+
todoItems.push("3. [0.6.5] Review P0/P1 回流至 Plan")
|
|
194
191
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (
|
|
198
|
-
|
|
199
|
-
const sd = existsSync(specsDir) ? await readdir(specsDir) : []
|
|
200
|
-
if (!sd.some(d => d.toLowerCase().includes(planKw.toLowerCase())) && reviewBackflowRate >= 70) { todoItems.push("3. [Step 0] 生成 Specs 三元组") }
|
|
201
|
-
if (todoItems.length === 0) { todoItems.push("✅ ADD 就绪 — check_dps ≥ {{dpsPass}} 后可进入 Step 1") }
|
|
192
|
+
if (!activePlan.addRoutePath || !existsSync(activePlan.addRoutePath)) todoItems.push("4. [Step 0.5] 生成 add-route")
|
|
193
|
+
if (!activePlan.specPath || !activePlan.tasksPath || !activePlan.checklistPath) todoItems.push("5. [Step 0] 补齐 Specs 三元组")
|
|
194
|
+
if (activePlan.doneTasks < activePlan.totalTasks) todoItems.push(`6. [实施] 继续未完成 Task(${activePlan.doneTasks}/${activePlan.totalTasks})`)
|
|
195
|
+
if (todoItems.length === 0) todoItems.push("✅ DB lifecycle 与文档链已就绪,可进入 Review/closure")
|
|
202
196
|
}
|
|
203
197
|
for (const item of todoItems) parts.push(item)
|
|
204
198
|
parts.push(""); parts.push("快速指令: 说「执行 add-paradigm Step 0」进入文档先行流程")
|
|
@@ -243,39 +237,41 @@ export function registerContextTools(server: ToolRegistrar) {
|
|
|
243
237
|
server.registerTool(
|
|
244
238
|
"get_db_schema",
|
|
245
239
|
{
|
|
246
|
-
description: "获取 Prisma
|
|
247
|
-
inputSchema: z.object({
|
|
240
|
+
description: "获取 topology-aware Prisma Schema。兼容单库多文件 schema folder 与 ADD_DATABASE_URL 分库;返回扫描文件、数据库边界、模型来源和诊断。",
|
|
241
|
+
inputSchema: z.object({
|
|
242
|
+
model: z.string().optional().describe("可选模型或枚举名称(不区分大小写)"),
|
|
243
|
+
view: z.enum(["business", "add", "all"]).optional().default("all").describe("Schema 视图;分库时 all 仅分组汇总,不表示可跨库 join"),
|
|
244
|
+
}),
|
|
248
245
|
},
|
|
249
|
-
async (args: { model?: string }) => {
|
|
246
|
+
async (args: { model?: string; view?: SchemaView }) => {
|
|
250
247
|
try {
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
248
|
+
const topology = await loadPrismaSchemaTopology({
|
|
249
|
+
projectRoot: PROJECT_ROOT,
|
|
250
|
+
splitDatabase: Boolean(process.env.ADD_DATABASE_URL?.trim()),
|
|
251
|
+
view: args?.view ?? "all",
|
|
252
|
+
})
|
|
253
|
+
const requested = args?.model?.trim().toLowerCase()
|
|
254
|
+
if (requested) {
|
|
255
|
+
const declaration = [...topology.models, ...topology.enums]
|
|
256
|
+
.find(item => item.name.toLowerCase() === requested)
|
|
257
|
+
if (!declaration) {
|
|
258
|
+
return errorResponse(`未在 view=${topology.view} 找到模型或枚举: ${args.model}。扫描: ${topology.files.map(file => file.file).join(", ")}`)
|
|
261
259
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
260
|
+
return textResponse([
|
|
261
|
+
`=== ${declaration.kind === "model" ? "Model" : "Enum"}: ${declaration.name} ===`,
|
|
262
|
+
`mode: ${topology.mode}`,
|
|
263
|
+
`view: ${topology.view}`,
|
|
264
|
+
`boundary: ${declaration.boundary}`,
|
|
265
|
+
`source: ${relative(PROJECT_ROOT, declaration.sourcePath)}`,
|
|
266
|
+
"",
|
|
267
|
+
`${declaration.kind} ${declaration.name} ${declaration.body}`,
|
|
268
|
+
].join("\n"))
|
|
271
269
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
parts.push("", '提示: 指定 model 参数获取完整字段定义,例如: get_db_schema({ model: "User" })')
|
|
278
|
-
return textResponse(parts.join("\n"))
|
|
270
|
+
return textResponse([
|
|
271
|
+
formatSchemaTopology(topology, PROJECT_ROOT),
|
|
272
|
+
"",
|
|
273
|
+
'提示: 指定 model/view 获取完整定义,例如 get_db_schema({ model: "PlanRecord", view: "add" })',
|
|
274
|
+
].join("\n"))
|
|
279
275
|
} catch (error) { return errorResponse(`获取 Schema 失败: ${error instanceof Error ? error.message : String(error)}`) }
|
|
280
276
|
}
|
|
281
277
|
)
|