@luckychen1993/pm-mcp 0.1.2 → 0.1.4
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 +11 -5
- package/dist/acceptance-tools.js +6 -6
- package/dist/audit-tools.js +14 -13
- package/dist/audit.js +10 -5
- package/dist/governance-tools.js +11 -11
- package/dist/idempotency.js +427 -0
- package/dist/index-store.js +163 -94
- package/dist/index.js +28 -17
- package/dist/knowledge-tools.js +5 -5
- package/dist/license.js +7 -4
- package/dist/paths.js +35 -0
- package/dist/project-tools.js +4 -4
- package/dist/search.js +2 -2
- package/dist/security.js +15 -6
- package/dist/setup.js +1 -1
- package/dist/store.js +185 -35
- package/dist/task-tools.js +2 -2
- package/dist/tool-base.js +33 -10
- package/dist/tools.js +1 -1
- package/dist/watcher-coordinator.js +182 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@ AI 编码项目的典型失控(本工具逐一给出机制):
|
|
|
25
25
|
| 调试过程随会话消失 | `log_debug` 调试知识账(症状/根因/修法/验证) |
|
|
26
26
|
| 长任务崩溃、上下文丢失 | 任务步骤清单 + `checkpoint` 断点存档,新会话从恢复点续做 |
|
|
27
27
|
| 团队协作与知识传递 | Git 项目中共享 `.pm/` + 会话署名 + `onboard` 入职简报工作流 |
|
|
28
|
+
| 多 Agent 重复读写 | 实际重叠的同参读跨进程合并;写工具支持业务幂等键、瞬时自动去重与故障不确定态 fail-closed |
|
|
28
29
|
| token 猛烧 | 三段式定位 + 读类工具输出硬预算 + 文件用途索引 |
|
|
29
30
|
| 法律知识产权 | `audit_license` 依赖许可证/copyleft 冲突 + 来源登记(provenance) |
|
|
30
31
|
| 测试投机(删测试、skip 凑绿) | 禁用/蒸发/空测试检测 + 功能测试背书占比 + done 必附验证 |
|
|
@@ -52,7 +53,7 @@ npx -y @luckychen1993/pm-mcp@latest setup --dry-run
|
|
|
52
53
|
Codex 的本地客户端共享同一份 MCP 配置。复制执行:
|
|
53
54
|
|
|
54
55
|
```bash
|
|
55
|
-
codex mcp add pm-mcp -- npx -y @luckychen1993/pm-mcp@0.1.
|
|
56
|
+
codex mcp add pm-mcp -- npx -y @luckychen1993/pm-mcp@0.1.4
|
|
56
57
|
```
|
|
57
58
|
|
|
58
59
|
重启客户端后可用 `codex mcp list` 检查。该命令遵循 [OpenAI 官方 MCP CLI 格式](https://learn.chatgpt.com/docs/extend/mcp?surface=cli)。
|
|
@@ -60,7 +61,7 @@ codex mcp add pm-mcp -- npx -y @luckychen1993/pm-mcp@0.1.2
|
|
|
60
61
|
#### Claude Code
|
|
61
62
|
|
|
62
63
|
```bash
|
|
63
|
-
claude mcp add pm-mcp --scope user -- npx -y @luckychen1993/pm-mcp@0.1.
|
|
64
|
+
claude mcp add pm-mcp --scope user -- npx -y @luckychen1993/pm-mcp@0.1.4
|
|
64
65
|
```
|
|
65
66
|
|
|
66
67
|
#### ZCode、Cursor、VS Code(Windows 备用脚本)
|
|
@@ -68,7 +69,7 @@ claude mcp add pm-mcp --scope user -- npx -y @luckychen1993/pm-mcp@0.1.2
|
|
|
68
69
|
将 `<client>` 替换为 `zcode`、`cursor` 或 `vscode`,整行复制到 PowerShell:
|
|
69
70
|
|
|
70
71
|
```powershell
|
|
71
|
-
$u='https://raw.githubusercontent.com/happy520ai/pm-mcp/v0.1.
|
|
72
|
+
$u='https://raw.githubusercontent.com/happy520ai/pm-mcp/v0.1.4/install.ps1'; $f=Join-Path $env:TEMP 'install-pm-mcp.ps1'; Invoke-WebRequest $u -OutFile $f; & $f -Client <client>
|
|
72
73
|
```
|
|
73
74
|
|
|
74
75
|
脚本只写对应客户端的 MCP 配置;已有 JSON 配置会先生成带时间戳的备份。建议执行前先打开 [`install.ps1`](install.ps1) 审阅。ZCode 默认写入 `$ZCODE_HOME/cli/config.json` 或 `~/.zcode/cli/config.json`,Cursor 默认写入 `~/.cursor/mcp.json`;也可用 `-ConfigPath` 指定路径。
|
|
@@ -80,7 +81,7 @@ $u='https://raw.githubusercontent.com/happy520ai/pm-mcp/v0.1.2/install.ps1'; $f=
|
|
|
80
81
|
"mcpServers": {
|
|
81
82
|
"pm-mcp": {
|
|
82
83
|
"command": "npx",
|
|
83
|
-
"args": ["-y", "@luckychen1993/pm-mcp@0.1.
|
|
84
|
+
"args": ["-y", "@luckychen1993/pm-mcp@0.1.4"],
|
|
84
85
|
"env": {}
|
|
85
86
|
}
|
|
86
87
|
}
|
|
@@ -153,6 +154,10 @@ snapshot_codebase + audit_structure 对账;audit_security 安全体检;audit
|
|
|
153
154
|
|
|
154
155
|
Resources:`pm://dashboard`、`pm://roadmap`、`pm://tasks`、`pm://changelog`、`pm://architecture`、`pm://portfolio`、`pm://acceptance`。
|
|
155
156
|
|
|
157
|
+
所有写工具都接受可选 `idempotency_key`。同一业务的多个 Agent 应传同一个稳定键(例如 `T-123:add-login-task`):同键同参数只执行一次并复用首次结果,同键不同参数直接拒绝。即使没有显式键,完全相同的并发写也会在短窗口内自动合并;完全相同的并行读会等待首个执行者并复用结果。
|
|
158
|
+
|
|
159
|
+
并发保证有明确边界:正常运行的同键写只落一次,follower 最多等待 3 秒复用首次结果;仍未完成时以 MCP error 返回 pending,不把占位冒充成功。若进程在业务写入后、幂等完成记录前被强杀,pm-mcp 会把该键标为 `uncertain` 并禁止自动重放。它提供的是 **at-most-once + 不确定时 fail-closed**,不是跨多个 JSON 文件的事务型 exactly-once;核对目标账本后才能决定是否换新键。v0.1.3 的标准全局幂等记录会在首次复用时迁移到项目运行态,旧文件保留以便回滚。读合并只覆盖业务处理实际重叠的在途请求,完成结果不会继续缓存,避免外部代码刚变化却返回旧内容。每个项目由 SQLite 生命周期选举一个 watcher leader,其余进程低频待命;leader 被强杀后由一个 standby 接管。结构、安全、许可证与快照等权威审计强制重读内容,不把 `fs.watch` 或 mtime/size 缓存的最终一致性冒充强一致性。
|
|
160
|
+
|
|
156
161
|
## 数据模型(被管理项目内的 `.pm/`;JSON/Markdown 账本可 git diff,`index.db` 是忽略并可重建的缓存)
|
|
157
162
|
|
|
158
163
|
```
|
|
@@ -175,7 +180,7 @@ PROJECT.md # 仓库根,自动生成的仪表盘(勿手改)
|
|
|
175
180
|
changelog.md # 自动生成
|
|
176
181
|
```
|
|
177
182
|
|
|
178
|
-
全局注册表:`~/.pm-mcp/registry.json
|
|
183
|
+
全局注册表:`~/.pm-mcp/registry.json`。跨进程读合并、写幂等、恢复锁与 watcher 选举运行态位于被管理项目的 `.pm/.runtime/`,由 `.pm/.gitignore` 排除;因此路径别名、不同客户端或不同 `PM_MCP_HOME` 仍共享同一协调域,但运行态不会进入 Git。
|
|
179
184
|
|
|
180
185
|
## 跨文件、跨模块、跨语言治理
|
|
181
186
|
|
|
@@ -252,6 +257,7 @@ npm run benchmark:volume -- --size-gib 20 --file-bytes 1048576 --full-audit --te
|
|
|
252
257
|
- **许可证审计**覆盖直接+传递 Node 依赖(npm 嵌套与 pnpm 布局);语言单元虽可跨生态发现,但许可证深度仍不是所有生态的通用供应链图,也不替代法务意见。
|
|
253
258
|
- **测试背书占比**是代理指标(功能是否挂了真实存在的测试文件),不是行覆盖率。
|
|
254
259
|
- **token 消耗**本身是客户端的账,工具用「足迹/产出比」做空转代理指标。
|
|
260
|
+
- 幂等层能避免重复计算、重复落账并缩短重复调用输出,但无法返还模型在发起 MCP 调用前已经消耗的推理 token;客户端/Agent 编排仍应避免无意义扇出。
|
|
255
261
|
- 结构/安全扫描忽略内置目录(node_modules/.git/dist/build/venv 等)与常见二进制/锁文件,尚不通用解析 `.gitignore`;`search_code` 的 rg 后端会解析。目录深度上限 64 层(超限计数上报,不静默)。
|
|
256
262
|
- **仓库提供 fail-closed 巡检命令**;定时器、托管 CI 与会话 hooks 属部署层,必须在相应环境单独配置和监控。
|
|
257
263
|
- 语义层面无法强制的(笔记内容是否属实、AI 是否真跑了测试)依赖 git 对账与审计交叉验证,不承诺语义级防伪。
|
package/dist/acceptance-tools.js
CHANGED
|
@@ -7,7 +7,7 @@ import { acceptanceBaselineFingerprint, evaluateAcceptance } from "./acceptance-
|
|
|
7
7
|
import { writeAcceptanceReport } from "./acceptance-report.js";
|
|
8
8
|
import { requireInitialized } from "./paths.js";
|
|
9
9
|
import { loadJson, saveJson, withLedgerLock } from "./store.js";
|
|
10
|
-
import {
|
|
10
|
+
import { toolI, toolR } from "./tool-base.js";
|
|
11
11
|
import { refreshDerived } from "./dashboard.js";
|
|
12
12
|
const IdSchema = z.string().trim().min(1).max(120).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/);
|
|
13
13
|
const SemVerSchema = z.string().regex(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/);
|
|
@@ -269,11 +269,11 @@ export function evaluateAcceptanceFile(root, input) {
|
|
|
269
269
|
return { report, ...files };
|
|
270
270
|
}
|
|
271
271
|
export function registerAcceptanceTools(server, root) {
|
|
272
|
-
|
|
272
|
+
toolR(server, root, "list_acceptance_baselines", "列出版本化验收基线、审批状态、当前 fingerprint 和规模。", {}, () => {
|
|
273
273
|
requireInitialized(root);
|
|
274
274
|
return JSON.stringify(listAcceptanceBaselines(root), null, 2);
|
|
275
275
|
});
|
|
276
|
-
|
|
276
|
+
toolR(server, root, "get_acceptance_baseline", "读取一个验收基线;默认返回摘要,full=true 返回严格 JSON。", {
|
|
277
277
|
baseline_id: IdSchema,
|
|
278
278
|
baseline_version: SemVerSchema,
|
|
279
279
|
full: z.boolean().optional(),
|
|
@@ -284,14 +284,14 @@ export function registerAcceptanceTools(server, root) {
|
|
|
284
284
|
return JSON.stringify(baseline, null, 2);
|
|
285
285
|
return JSON.stringify(listAcceptanceBaselines(root).find((item) => item.id === args.baseline_id && item.version === args.baseline_version), null, 2);
|
|
286
286
|
});
|
|
287
|
-
|
|
287
|
+
toolI(server, root, "save_acceptance_baseline_draft", "从项目内 .pm/acceptance 下的严格 JSON 保存 draft 基线;批准后的同版本不可覆盖。", {
|
|
288
288
|
baseline_file: JsonPathSchema,
|
|
289
289
|
}, (args) => {
|
|
290
290
|
requireInitialized(root);
|
|
291
291
|
const baseline = saveDraftAcceptanceBaselineFile(root, args.baseline_file);
|
|
292
292
|
return `✅ draft ${baseline.baseline_id}@${baseline.baseline_version} 已保存;fingerprint=${acceptanceBaselineFingerprint(baseline)}`;
|
|
293
293
|
});
|
|
294
|
-
|
|
294
|
+
toolI(server, root, "approve_acceptance_baseline", "按当前 SHA-256 fingerprint 原子批准基线;fingerprint 变化即拒绝,批准后修改必须升版。", {
|
|
295
295
|
baseline_id: IdSchema,
|
|
296
296
|
baseline_version: SemVerSchema,
|
|
297
297
|
expected_fingerprint_sha256: Sha256Schema,
|
|
@@ -303,7 +303,7 @@ export function registerAcceptanceTools(server, root) {
|
|
|
303
303
|
const approved = approveAcceptanceBaseline(root, args);
|
|
304
304
|
return `✅ baseline ${approved.baseline_id}@${approved.baseline_version} 已批准;approved fingerprint=${acceptanceBaselineFingerprint(approved)}`;
|
|
305
305
|
});
|
|
306
|
-
|
|
306
|
+
toolI(server, root, "evaluate_acceptance", "读取项目内 .pm/acceptance evaluation JSON,逐项复算证据 SHA-256,再生成不可手填、不可覆盖的正式报告。", {
|
|
307
307
|
baseline_id: IdSchema,
|
|
308
308
|
baseline_version: SemVerSchema,
|
|
309
309
|
evaluation_file: JsonPathSchema,
|
package/dist/audit-tools.js
CHANGED
|
@@ -5,26 +5,27 @@ import { refreshDerived } from "./dashboard.js";
|
|
|
5
5
|
import { auditLicense } from "./license.js";
|
|
6
6
|
import { isInitialized, requireInitialized } from "./paths.js";
|
|
7
7
|
import { listRegistry } from "./registry.js";
|
|
8
|
-
import { auditSecurity, listFindings, resolveFinding } from "./security.js";
|
|
9
|
-
import { budgetLines,
|
|
8
|
+
import { auditSecurity, listFindings, prepareSecurityAudit, resolveFinding } from "./security.js";
|
|
9
|
+
import { budgetLines, toolR, toolW } from "./tool-base.js";
|
|
10
|
+
import { walkRefresh } from "./index-store.js";
|
|
10
11
|
export function registerAuditTools(server, root) {
|
|
11
12
|
toolW(server, root, "snapshot_codebase", "给代码结构拍快照(文件数/行数/目录分布/测试数/skip 标记/依赖清单)。之后 audit_structure 会与上次快照 diff。建议每个会话或每个里程碑结束时拍一次。", {}, () => {
|
|
12
13
|
requireInitialized(root);
|
|
13
14
|
const { snapshot, summary } = snapshotCodebase(root);
|
|
14
15
|
refreshDerived(root);
|
|
15
16
|
return [...summary, `下次 audit_structure 将以此为基线(${snapshot.file})。`].join("\n");
|
|
16
|
-
});
|
|
17
|
-
|
|
17
|
+
}, () => { requireInitialized(root); walkRefresh(root, { forceContent: true }); });
|
|
18
|
+
toolR(server, root, "audit_structure", "八项结构对账(定期做):①增长与新增依赖 ②漂移对账(功能↔文件,防幻觉)③债务与重构配额 ④churn 热点 ⑤复杂度预算 ⑥索引覆盖率 ⑦足迹/产出 ⑧测试健康(禁用/蒸发/空测试/背书占比)。", {}, () => {
|
|
18
19
|
requireInitialized(root);
|
|
19
|
-
return auditStructure(root, budgetLines(root));
|
|
20
|
+
return auditStructure(root, budgetLines(root), true);
|
|
20
21
|
});
|
|
21
|
-
toolW(server, root, "audit_security", "安全体检(只扫描本地、不联网,结果写入安全台账):密钥泄露 / 危险模式(eval、SQL 拼接、禁用证书校验等)/ 依赖风险。修复后重扫自动关闭;接受风险必须留理由。", {}, () => {
|
|
22
|
+
toolW(server, root, "audit_security", "安全体检(只扫描本地、不联网,结果写入安全台账):密钥泄露 / 危险模式(eval、SQL 拼接、禁用证书校验等)/ 依赖风险。修复后重扫自动关闭;接受风险必须留理由。", {}, (_args, prepared) => {
|
|
22
23
|
requireInitialized(root);
|
|
23
|
-
const report = auditSecurity(root);
|
|
24
|
+
const report = auditSecurity(root, prepared);
|
|
24
25
|
refreshDerived(root);
|
|
25
26
|
return report.text.join("\n");
|
|
26
|
-
});
|
|
27
|
-
|
|
27
|
+
}, () => { requireInitialized(root); return prepareSecurityAudit(root); });
|
|
28
|
+
toolR(server, root, "list_findings", "查看安全台账(可按状态过滤)。", { status: z.enum(["open", "fixed", "accepted"]).optional() }, (args) => {
|
|
28
29
|
requireInitialized(root);
|
|
29
30
|
const list = listFindings(root, args.status);
|
|
30
31
|
if (list.length === 0)
|
|
@@ -42,13 +43,13 @@ export function registerAuditTools(server, root) {
|
|
|
42
43
|
refreshDerived(root);
|
|
43
44
|
return `✅ ${f.id} 已置为 ${f.status}${f.note ? `(${f.note})` : ""}。`;
|
|
44
45
|
});
|
|
45
|
-
|
|
46
|
+
toolR(server, root, "audit_license", "许可证审计(法律账):依赖许可证清单与 copyleft 冲突、源码中的 GPL 家族许可证头、LICENSE 文件检查、来源登记清单。离线启发式,不替代法务。", {}, () => {
|
|
46
47
|
requireInitialized(root);
|
|
47
|
-
return auditLicense(root, budgetLines(root));
|
|
48
|
+
return auditLicense(root, budgetLines(root), true);
|
|
48
49
|
});
|
|
49
50
|
}
|
|
50
|
-
export function registerRegistryTools(server) {
|
|
51
|
-
|
|
51
|
+
export function registerRegistryTools(server, root) {
|
|
52
|
+
toolR(server, root, "list_projects", "列出本机所有被 pm-mcp 管理的项目(全局注册表)。", {}, () => {
|
|
52
53
|
const list = listRegistry();
|
|
53
54
|
if (list.length === 0)
|
|
54
55
|
return "(注册表为空。在任何项目里 init_project 后会自动登记。)";
|
package/dist/audit.js
CHANGED
|
@@ -5,7 +5,7 @@ import { loadFeatures, loadFileNotes, loadProject, loadRoadmap, loadSessions, lo
|
|
|
5
5
|
import { decisionsDir, pmPath, snapshotsDir } from "./paths.js";
|
|
6
6
|
import { normSep, foldLines } from "./budget.js";
|
|
7
7
|
import { readDirectDeps } from "./scan.js";
|
|
8
|
-
import { aggregates, ensureFresh, getIndex, getMeta } from "./index-store.js";
|
|
8
|
+
import { aggregates, ensureFresh, freshness, getIndex, getMeta, walkRefresh } from "./index-store.js";
|
|
9
9
|
import { quotaWarnings, milestoneStats } from "./roadmap.js";
|
|
10
10
|
import { blastRadiusWarning, churnStats, debtAging, footprint } from "./health.js";
|
|
11
11
|
import { now } from "./types.js";
|
|
@@ -21,8 +21,11 @@ export function annotatedCount(root, db) {
|
|
|
21
21
|
}
|
|
22
22
|
return n;
|
|
23
23
|
}
|
|
24
|
-
export function snapshotCodebase(root) {
|
|
25
|
-
|
|
24
|
+
export function snapshotCodebase(root, forceFresh = false, indexPrepared = false) {
|
|
25
|
+
if (forceFresh)
|
|
26
|
+
walkRefresh(root, { forceContent: true });
|
|
27
|
+
else if (!indexPrepared)
|
|
28
|
+
ensureFresh(root);
|
|
26
29
|
const db = getIndex(root);
|
|
27
30
|
const agg = aggregates(db);
|
|
28
31
|
const annotated = annotatedCount(root, db);
|
|
@@ -165,13 +168,15 @@ export function gitAudit(root) {
|
|
|
165
168
|
return out;
|
|
166
169
|
}
|
|
167
170
|
/** 完整结构对账:九项报告 */
|
|
168
|
-
export function auditStructure(root, maxLines = 150) {
|
|
171
|
+
export function auditStructure(root, maxLines = 150, forceFresh = false, indexPrepared = false) {
|
|
169
172
|
const project = loadProject(root);
|
|
170
173
|
const { features } = loadFeatures(root);
|
|
171
174
|
const { tasks } = loadTasks(root);
|
|
172
175
|
const { milestones } = loadRoadmap(root);
|
|
173
176
|
const { sessions } = loadSessions(root);
|
|
174
|
-
const fresh =
|
|
177
|
+
const fresh = forceFresh
|
|
178
|
+
? { used: "walk", freshness: (walkRefresh(root, { forceContent: true }), freshness(root)) }
|
|
179
|
+
: indexPrepared ? { used: "walk", freshness: freshness(root) } : ensureFresh(root);
|
|
175
180
|
const db = getIndex(root);
|
|
176
181
|
const agg = aggregates(db);
|
|
177
182
|
const deps = readDirectDeps(root).deps;
|
package/dist/governance-tools.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { budgetLines,
|
|
3
|
+
import { budgetLines, toolI, toolR, toolW } from "./tool-base.js";
|
|
4
4
|
import { foldLines } from "./budget.js";
|
|
5
5
|
import { refreshDerived } from "./dashboard.js";
|
|
6
6
|
import { requireInitialized } from "./paths.js";
|
|
@@ -64,13 +64,13 @@ function qualityLines(commands) {
|
|
|
64
64
|
return commands.map((command, index) => `${index + 1}. [${command.kind}] (${command.cwd}) ${command.command} ${command.args.join(" ")}`);
|
|
65
65
|
}
|
|
66
66
|
export function registerGovernanceTools(server, root) {
|
|
67
|
-
|
|
67
|
+
toolI(server, root, "init_governance", "为旧项目初始化 .pm/governance.json;新项目 init_project 已自动创建。", {}, () => {
|
|
68
68
|
requireInitialized(root);
|
|
69
69
|
const governance = initGovernance(root, {});
|
|
70
70
|
refreshDerived(root);
|
|
71
71
|
return `✅ 治理模型已初始化(schema v${governance.schema_version})。下一步用 upsert_module 声明模块、owner 与语言。`;
|
|
72
72
|
});
|
|
73
|
-
|
|
73
|
+
toolR(server, root, "get_governance", "查看结构化模块、owner、公开接口、仓库依赖与强制策略。", {}, () => {
|
|
74
74
|
requireInitialized(root);
|
|
75
75
|
return compactGovernance(loadGovernance(root));
|
|
76
76
|
});
|
|
@@ -140,7 +140,7 @@ export function registerGovernanceTools(server, root) {
|
|
|
140
140
|
refreshDerived(root);
|
|
141
141
|
return `✅ 治理策略已更新:coverage>=${governance.policies.minimum_coverage_pct}% · semantic>=${governance.policies.minimum_semantic_assurance} · regex-fallback=${governance.policies.fail_on_semantic_fallback ? "forbidden" : "allowed"}`;
|
|
142
142
|
});
|
|
143
|
-
|
|
143
|
+
toolR(server, root, "discover_languages", "递归发现 Node/Python/Go/Rust/JVM/.NET 单元及其受控质量命令,不执行命令。", {}, () => {
|
|
144
144
|
requireInitialized(root);
|
|
145
145
|
const units = discoverProjectUnits(root);
|
|
146
146
|
const coverage = assessQualityCoverage(units);
|
|
@@ -151,15 +151,15 @@ export function registerGovernanceTools(server, root) {
|
|
|
151
151
|
lines.push(`- 🚩 无质量命令: ${coverage.withoutCommands.join(", ")}`);
|
|
152
152
|
return foldLines(lines, { maxLines: budgetLines(root), hint: "按模块拆分治理 root" });
|
|
153
153
|
});
|
|
154
|
-
|
|
154
|
+
toolR(server, root, "audit_governance", "审计跨文件/模块/语言语义覆盖、owner、公开接口、依赖边界、循环、unresolved 与质量矩阵覆盖。", {}, () => {
|
|
155
155
|
requireInitialized(root);
|
|
156
156
|
return auditGovernance(root, budgetLines(root)).report;
|
|
157
157
|
});
|
|
158
|
-
|
|
158
|
+
toolR(server, root, "list_semantic_evidence", "列出已登记的语言原生 AST、编译器或运行时语义证据;损坏文档会使整次读取失败。", {}, () => {
|
|
159
159
|
requireInitialized(root);
|
|
160
160
|
return JSON.stringify(listSemanticEvidence(root), null, 2);
|
|
161
161
|
});
|
|
162
|
-
|
|
162
|
+
toolI(server, root, "save_semantic_evidence", "保存一份绑定源码 SHA-256 的语言原生 AST/编译器/运行时证据;过期摘要在治理审计中严格失败。", {
|
|
163
163
|
id: z.string().trim().min(1).max(128),
|
|
164
164
|
document: SemanticEvidenceDocumentSchema,
|
|
165
165
|
}, (args) => {
|
|
@@ -167,7 +167,7 @@ export function registerGovernanceTools(server, root) {
|
|
|
167
167
|
const saved = saveSemanticEvidence(root, args.id, args.document);
|
|
168
168
|
return `✅ semantic evidence ${args.id} 已保存:${saved.file} · ${saved.analyzer.id} · ${saved.analyzer.assurance} · ${saved.status}`;
|
|
169
169
|
});
|
|
170
|
-
|
|
170
|
+
toolR(server, root, "impact_analysis", "从变更文件沿 file/module 反向依赖闭包计算受影响文件与模块。", {
|
|
171
171
|
files: z.array(z.string().min(1)).min(1).max(100),
|
|
172
172
|
}, (args) => {
|
|
173
173
|
requireInitialized(root);
|
|
@@ -180,7 +180,7 @@ export function registerGovernanceTools(server, root) {
|
|
|
180
180
|
...(impact.unknownChangedFiles.length ? [`⚠️ 未进入语义图: ${impact.unknownChangedFiles.join(", ")}`] : []),
|
|
181
181
|
], { maxLines: budgetLines(root), hint: "缩小 files 输入或按模块运行" });
|
|
182
182
|
});
|
|
183
|
-
|
|
183
|
+
toolR(server, root, "plan_quality_matrix", "生成跨语言真实质量命令计划;只规划,不启动进程。", {
|
|
184
184
|
unit: z.string().optional(),
|
|
185
185
|
kinds: z.array(z.enum(["test", "build", "lint", "typecheck", "coverage", "security"])).optional(),
|
|
186
186
|
}, (args) => {
|
|
@@ -192,7 +192,7 @@ export function registerGovernanceTools(server, root) {
|
|
|
192
192
|
...(plan.coverage.withoutCommands.length ? [`🚩 无命令单元: ${plan.coverage.withoutCommands.join(", ")}`] : []),
|
|
193
193
|
], { maxLines: budgetLines(root), hint: "用 unit/kinds 过滤;真实执行需 run_quality_matrix confirm_execute=true" });
|
|
194
194
|
});
|
|
195
|
-
|
|
195
|
+
toolI(server, root, "run_quality_matrix", "显式执行跨语言质量矩阵。只运行受支持适配器生成的 argv,shell=false;缺工具/超时/非零均失败。", {
|
|
196
196
|
confirm_execute: z.literal(true).describe("必须显式为 true;该操作会运行目标项目构建/测试命令"),
|
|
197
197
|
unit: z.string().optional(),
|
|
198
198
|
kinds: z.array(z.enum(["test", "build", "lint", "typecheck", "coverage", "security"])).optional(),
|
|
@@ -219,7 +219,7 @@ export function registerGovernanceTools(server, root) {
|
|
|
219
219
|
lines.push(`- [${item.status}] ${item.command.kind} ${item.command.cwd} (${item.durationMs}ms${item.exitCode === null ? "" : `, exit ${item.exitCode}`})`);
|
|
220
220
|
return foldLines(lines, { maxLines: budgetLines(root), hint: "查看 .pm/quality-runs 结构化摘要(不落原始输出)" });
|
|
221
221
|
});
|
|
222
|
-
|
|
222
|
+
toolR(server, root, "get_portfolio", "聚合当前项目或全局注册项目的阶段、里程碑、债务、安全、语言、跨仓依赖/版本/cycle;加载失败不能假绿。", {
|
|
223
223
|
current_only: z.boolean().optional(),
|
|
224
224
|
}, (args) => {
|
|
225
225
|
requireInitialized(root);
|