add-coder 0.3.26 → 0.3.27
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 +36 -10
- package/dist/index.js +481 -209
- package/package.json +14 -4
- package/templates/.add-coder-src-hash.json +129 -106
- package/templates/adapters/claude/hooks/doc-format-guard.sh +112 -31
- package/templates/adapters/claude/hooks/lib/common.sh +46 -87
- package/templates/adapters/claude/hooks/lib/context-inject.sh +2 -2
- package/templates/adapters/claude/hooks/lib/notify.sh +6 -4
- package/templates/adapters/claude/hooks/lib/preload-templates.sh +27 -2
- package/templates/adapters/claude/hooks/lib/state-detect.sh +8 -112
- package/templates/adapters/claude/hooks/lib/vocabulary.sh +13 -7
- package/templates/adapters/claude/hooks/prompt-submit.sh +1 -1
- package/templates/adapters/claude/hooks/stop-check.sh +9 -0
- package/templates/adapters/codex/config.toml.example +12 -0
- package/templates/adapters/codex/hooks/doc-format-guard.sh +121 -39
- package/templates/adapters/codex/hooks/lib/common.sh +47 -99
- package/templates/adapters/codex/hooks/lib/context-inject.sh +4 -4
- package/templates/adapters/codex/hooks/lib/notify.sh +6 -4
- package/templates/adapters/codex/hooks/lib/preload-templates.sh +27 -6
- package/templates/adapters/codex/hooks/lib/state-detect.sh +5 -114
- package/templates/adapters/codex/hooks/lib/vocabulary.sh +3 -8
- package/templates/adapters/codex/hooks/notification.sh +3 -18
- package/templates/adapters/codex/hooks/post-tool-use.sh +24 -119
- package/templates/adapters/codex/hooks/pre-tool-use.sh +97 -184
- package/templates/adapters/codex/hooks/prompt-submit.sh +8 -3
- package/templates/adapters/codex/hooks/session-start.sh +19 -9
- package/templates/adapters/codex/hooks/stop-check.sh +44 -18
- package/templates/adapters/codex/hooks.json +67 -1
- package/templates/adapters/qoder/hooks/doc-format-guard.sh +119 -38
- package/templates/adapters/qoder/hooks/lib/common.sh +49 -87
- package/templates/adapters/qoder/hooks/lib/context-inject.sh +2 -2
- package/templates/adapters/qoder/hooks/lib/notify.sh +6 -4
- package/templates/adapters/qoder/hooks/lib/preload-templates.sh +27 -2
- package/templates/adapters/qoder/hooks/lib/state-detect.sh +8 -112
- package/templates/adapters/qoder/hooks/lib/vocabulary.sh +13 -7
- package/templates/adapters/qoder/hooks/prompt-submit.sh +1 -1
- package/templates/adapters/qoder/hooks/session-start.sh +1 -1
- package/templates/adapters/qoder/hooks/stop-check.sh +11 -0
- package/templates/adapters/trae/hooks/doc-format-guard.sh +120 -38
- package/templates/adapters/trae/hooks/lib/common.sh +46 -87
- package/templates/adapters/trae/hooks/lib/context-inject.sh +2 -2
- package/templates/adapters/trae/hooks/lib/notify.sh +5 -3
- package/templates/adapters/trae/hooks/lib/preload-templates.sh +27 -2
- package/templates/adapters/trae/hooks/lib/state-detect.sh +8 -115
- package/templates/adapters/trae/hooks/lib/vocabulary.sh +13 -7
- package/templates/adapters/trae/hooks/pre-tool-use.sh +14 -8
- package/templates/adapters/trae/hooks/stop-check.sh +9 -0
- package/templates/adapters/vscode/hooks/doc-format-guard.sh +112 -31
- package/templates/adapters/vscode/hooks/lib/common.sh +46 -87
- package/templates/adapters/vscode/hooks/lib/context-inject.sh +2 -2
- package/templates/adapters/vscode/hooks/lib/notify.sh +6 -4
- package/templates/adapters/vscode/hooks/lib/preload-templates.sh +27 -2
- package/templates/adapters/vscode/hooks/lib/state-detect.sh +8 -112
- package/templates/adapters/vscode/hooks/lib/vocabulary.sh +13 -7
- package/templates/adapters/vscode/hooks/notification.sh +3 -5
- package/templates/adapters/vscode/hooks/prompt-submit.sh +1 -1
- package/templates/adapters/vscode/hooks/stop-check.sh +9 -0
- package/templates/core/docs/ADD-governance-claude-code.md +1 -1
- package/templates/core/docs/ADD-governance-codex.md +1 -1
- package/templates/core/docs/ADD-governance-qoder-cn.md +1 -1
- package/templates/core/docs/ADD-governance-trae.md +1 -1
- package/templates/core/docs/ADD-governance-vscode-copilot.md +1 -1
- package/templates/core/hooks/doc-format-guard.sh +120 -38
- package/templates/core/hooks/lib/common.sh +46 -87
- package/templates/core/hooks/lib/context-inject.sh +2 -2
- package/templates/core/hooks/lib/notify.sh +5 -3
- package/templates/core/hooks/lib/preload-templates.sh +27 -2
- package/templates/core/hooks/lib/state-detect.sh +8 -115
- package/templates/core/hooks/lib/vocabulary.sh +13 -7
- package/templates/core/hooks/pre-tool-use.sh +14 -8
- package/templates/core/hooks/stop-check.sh +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 +75 -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 +140 -15
- 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
|
@@ -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
|
)
|
|
@@ -7,6 +7,7 @@ import { readdirRecursive, PROJECT_ROOT, MAGIC_DIR } from "../shared/fs.js"
|
|
|
7
7
|
import { prisma } from "../shared/prisma.js"
|
|
8
8
|
import type { CollabContractRow, PlanRow } from "../shared/db-types.js"
|
|
9
9
|
import { CollabContractRowSchema, PlanRowSchema, validatedDelegate } from "../shared/db-types.js"
|
|
10
|
+
import { getRuntimeContext } from "../shared/env.js"
|
|
10
11
|
|
|
11
12
|
const db = {
|
|
12
13
|
get collabContract() { return validatedDelegate<CollabContractRow>(prisma.collabContract, CollabContractRowSchema, "CollabContract") },
|
|
@@ -67,6 +68,7 @@ function parseContractDoc(content: string): ParsedContract {
|
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
export function registerContractTools(server: ToolRegistrar) {
|
|
71
|
+
const runtimeContext = getRuntimeContext()
|
|
70
72
|
|
|
71
73
|
// ===== contract_track =====
|
|
72
74
|
server.registerTool("contract_track", {
|
|
@@ -124,13 +126,30 @@ export function registerContractTools(server: ToolRegistrar) {
|
|
|
124
126
|
results.push(`⚠️ ${name} 缺少「总控 Plan:」声明,跳过`)
|
|
125
127
|
continue
|
|
126
128
|
}
|
|
127
|
-
const
|
|
129
|
+
const masterPlan = await db.plan.findFirst({
|
|
130
|
+
where: {
|
|
131
|
+
projectKey: runtimeContext.projectKey,
|
|
132
|
+
adapterKey: runtimeContext.adapterKey,
|
|
133
|
+
planName: masterPlanName,
|
|
134
|
+
},
|
|
135
|
+
})
|
|
136
|
+
if (!masterPlan) {
|
|
137
|
+
results.push(`⚠️ ${name} 当前 RuntimeContextKey 下不存在 Master Plan: ${masterPlanName}`)
|
|
138
|
+
continue
|
|
139
|
+
}
|
|
140
|
+
const existing = await db.collabContract.findFirst({
|
|
141
|
+
where: { projectKey: runtimeContext.projectKey, contractName: name },
|
|
142
|
+
})
|
|
128
143
|
const version = (existing?.version as number ?? 0) + 1
|
|
129
144
|
|
|
130
145
|
const data = {
|
|
131
146
|
contractName: name,
|
|
147
|
+
projectKey: runtimeContext.projectKey,
|
|
148
|
+
ownerAdapterKey: runtimeContext.adapterKey,
|
|
132
149
|
contractPath: path,
|
|
133
150
|
masterPlanName,
|
|
151
|
+
masterProjectKey: runtimeContext.projectKey,
|
|
152
|
+
masterAdapterKey: runtimeContext.adapterKey,
|
|
134
153
|
participants: parsed.participants as never,
|
|
135
154
|
abilityMatrix: parsed.abilityMatrix as never,
|
|
136
155
|
stages: parsed.stages as never,
|
|
@@ -142,7 +161,7 @@ export function registerContractTools(server: ToolRegistrar) {
|
|
|
142
161
|
}
|
|
143
162
|
|
|
144
163
|
if (existing) {
|
|
145
|
-
await db.collabContract.update({ where: {
|
|
164
|
+
await db.collabContract.update({ where: { id: existing.id }, data })
|
|
146
165
|
results.push(`📄 ${name} ✅ 已更新 (v${version})`)
|
|
147
166
|
} else {
|
|
148
167
|
await db.collabContract.create({ data })
|
|
@@ -152,8 +171,12 @@ export function registerContractTools(server: ToolRegistrar) {
|
|
|
152
171
|
// 若 masterPlan 存在,标记 PlanRecord 角色
|
|
153
172
|
if (masterPlanName) {
|
|
154
173
|
await db.plan.updateMany({
|
|
155
|
-
where: {
|
|
156
|
-
|
|
174
|
+
where: {
|
|
175
|
+
projectKey: runtimeContext.projectKey,
|
|
176
|
+
adapterKey: runtimeContext.adapterKey,
|
|
177
|
+
planName: masterPlanName,
|
|
178
|
+
},
|
|
179
|
+
data: { contractRole: "MASTER", contractName: name },
|
|
157
180
|
})
|
|
158
181
|
}
|
|
159
182
|
}
|
|
@@ -173,7 +196,9 @@ export function registerContractTools(server: ToolRegistrar) {
|
|
|
173
196
|
}, async (args: Record<string, unknown>) => {
|
|
174
197
|
try {
|
|
175
198
|
const { contractName } = args as { contractName: string }
|
|
176
|
-
const contract = await db.collabContract.findFirst({
|
|
199
|
+
const contract = await db.collabContract.findFirst({
|
|
200
|
+
where: { projectKey: runtimeContext.projectKey, contractName },
|
|
201
|
+
})
|
|
177
202
|
if (!contract) {
|
|
178
203
|
return textResponse(`📋 契约: 未跟踪\ncontractName: ${contractName}\n\n操作: contract_track({ contractName: "${contractName}" })`)
|
|
179
204
|
}
|
|
@@ -182,11 +207,11 @@ export function registerContractTools(server: ToolRegistrar) {
|
|
|
182
207
|
const boundaries = (contract.fileBoundaries as unknown[]) || []
|
|
183
208
|
const parallelCount = stages.filter((s) => (s as Record<string, unknown>).parallelism === "parallel").length
|
|
184
209
|
const serialCount = stages.filter((s) => (s as Record<string, unknown>).parallelism === "serial").length
|
|
185
|
-
const versionStr = String(contract.version
|
|
186
|
-
const statusStr = String(contract.status
|
|
187
|
-
const masterStr = String(contract.masterPlanName
|
|
210
|
+
const versionStr = String(contract.version ?? "?")
|
|
211
|
+
const statusStr = String(contract.status ?? "?")
|
|
212
|
+
const masterStr = String(contract.masterPlanName ?? "(未绑定)")
|
|
188
213
|
const depStr = contract.dependencyGraph ? "已声明" : "未声明"
|
|
189
|
-
const pathStr = String(contract.contractPath
|
|
214
|
+
const pathStr = String(contract.contractPath ?? "(无)")
|
|
190
215
|
return textResponse(
|
|
191
216
|
`📊 ${contractName}\n` +
|
|
192
217
|
`版本: v${versionStr} (${statusStr})\n` +
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
MAGIC_DIR,
|
|
18
18
|
} from "../../shared/fs.js";
|
|
19
19
|
import { prisma } from "../../shared/prisma.js";
|
|
20
|
+
import { getRuntimeContext } from "../../shared/env.js";
|
|
20
21
|
|
|
21
22
|
function scanCheckboxes(content: string) {
|
|
22
23
|
let t = 0,
|
|
@@ -61,6 +62,7 @@ function scanCheckboxes(content: string) {
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
export function registerCheckAddRouteStatus(server: ToolRegistrar) {
|
|
65
|
+
const runtimeContext = getRuntimeContext();
|
|
64
66
|
server.registerTool(
|
|
65
67
|
"check_add_route_status",
|
|
66
68
|
{
|
|
@@ -85,10 +87,13 @@ export function registerCheckAddRouteStatus(server: ToolRegistrar) {
|
|
|
85
87
|
prisma.auditLog as Record<string, (...a: unknown[]) => unknown>
|
|
86
88
|
).findMany({
|
|
87
89
|
where: {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
projectKey: runtimeContext.projectKey,
|
|
91
|
+
producerAdapterKey: runtimeContext.adapterKey,
|
|
92
|
+
// 按当前 Plan 精准定位:targetId 同时含 planKeyword 与 add-route(与文件匹配语义一致)
|
|
93
|
+
AND: [
|
|
94
|
+
{ targetId: { startsWith: `${MAGIC_DIR}/` } },
|
|
90
95
|
{ targetId: { contains: planKeyword, mode: "insensitive" } },
|
|
91
|
-
{
|
|
96
|
+
{ targetId: { contains: "add-route", mode: "insensitive" } },
|
|
92
97
|
],
|
|
93
98
|
},
|
|
94
99
|
orderBy: { createdAt: "desc" },
|
|
@@ -113,6 +118,48 @@ export function registerCheckAddRouteStatus(server: ToolRegistrar) {
|
|
|
113
118
|
} catch {
|
|
114
119
|
/* empty */
|
|
115
120
|
}
|
|
121
|
+
// 补查 DevOperation 表(record_dev_operation 写入通道;R10 修复,2026-08-12 用户拍板不另立 Plan,本 Plan 内闭环)
|
|
122
|
+
try {
|
|
123
|
+
const devLogs = await (
|
|
124
|
+
prisma.devOperation as Record<string, (...a: unknown[]) => unknown>
|
|
125
|
+
).findMany({
|
|
126
|
+
where: {
|
|
127
|
+
projectKey: runtimeContext.projectKey,
|
|
128
|
+
producerAdapterKey: runtimeContext.adapterKey,
|
|
129
|
+
// 按当前 Plan 精准定位(AND:planKeyword + add-route),命中集天然为当前 Plan 的 add-route 记录
|
|
130
|
+
AND: [
|
|
131
|
+
{ targetId: { startsWith: `${MAGIC_DIR}/` } },
|
|
132
|
+
{ targetId: { contains: planKeyword, mode: "insensitive" } },
|
|
133
|
+
{ targetId: { contains: "add-route", mode: "insensitive" } },
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
orderBy: { createdAt: "desc" },
|
|
137
|
+
take: 100,
|
|
138
|
+
}) as Array<{
|
|
139
|
+
targetId?: string;
|
|
140
|
+
reason?: string;
|
|
141
|
+
action: string;
|
|
142
|
+
createdAt: Date;
|
|
143
|
+
}>;
|
|
144
|
+
const pl = planKeyword.toLowerCase();
|
|
145
|
+
for (const l of devLogs) {
|
|
146
|
+
const ti = (l.targetId || "").toLowerCase();
|
|
147
|
+
const r = (l.reason || "").toLowerCase();
|
|
148
|
+
if (
|
|
149
|
+
(ti.includes("add-route") && ti.includes(pl)) ||
|
|
150
|
+
(r.includes("add-route") && r.includes(pl))
|
|
151
|
+
) {
|
|
152
|
+
auditHasRecord = true;
|
|
153
|
+
auditRecords.push({
|
|
154
|
+
action: l.action,
|
|
155
|
+
targetId: l.targetId || "unknown",
|
|
156
|
+
createdAt: l.createdAt,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
} catch {
|
|
161
|
+
/* empty */
|
|
162
|
+
}
|
|
116
163
|
let fileExists = false;
|
|
117
164
|
const matchedFiles: string[] = [];
|
|
118
165
|
try {
|