mcp-probe-kit 3.0.8 → 3.0.10
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 +15 -4
- package/build/lib/__tests__/gitnexus-bridge.unit.test.d.ts +1 -0
- package/build/lib/__tests__/gitnexus-bridge.unit.test.js +86 -0
- package/build/lib/gitnexus-bridge.d.ts +23 -0
- package/build/lib/gitnexus-bridge.js +518 -22
- package/build/schemas/code-analysis-tools.d.ts +9 -1
- package/build/schemas/code-analysis-tools.js +9 -1
- package/build/schemas/git-tools.d.ts +1 -1
- package/build/schemas/git-tools.js +1 -1
- package/build/schemas/index.d.ts +23 -3
- package/build/schemas/orchestration-tools.d.ts +13 -1
- package/build/schemas/orchestration-tools.js +13 -1
- package/build/schemas/output/core-tools.d.ts +130 -1
- package/build/schemas/output/core-tools.js +71 -1
- package/build/schemas/output/index.d.ts +2 -2
- package/build/schemas/output/index.js +2 -2
- package/build/schemas/output/project-tools.d.ts +13 -0
- package/build/schemas/output/project-tools.js +9 -0
- package/build/schemas/structured-output.d.ts +358 -5
- package/build/schemas/structured-output.js +169 -5
- package/build/tools/__tests__/code_insight.unit.test.js +81 -1
- package/build/tools/__tests__/fix_bug.unit.test.d.ts +1 -0
- package/build/tools/__tests__/fix_bug.unit.test.js +31 -0
- package/build/tools/__tests__/gencommit.unit.test.d.ts +1 -0
- package/build/tools/__tests__/gencommit.unit.test.js +41 -0
- package/build/tools/__tests__/init_project_context.unit.test.d.ts +1 -0
- package/build/tools/__tests__/init_project_context.unit.test.js +63 -0
- package/build/tools/__tests__/start_bugfix.unit.test.js +10 -0
- package/build/tools/__tests__/start_feature.unit.test.js +10 -0
- package/build/tools/code_insight.d.ts +10 -0
- package/build/tools/code_insight.js +156 -3
- package/build/tools/fix_bug.d.ts +3 -3
- package/build/tools/fix_bug.js +297 -312
- package/build/tools/gencommit.js +144 -123
- package/build/tools/init_project_context.js +211 -53
- package/build/tools/start_bugfix.js +170 -70
- package/build/tools/start_feature.js +79 -25
- package/docs/data/tools.js +33 -31
- package/docs/i18n/all-tools/en.json +9 -9
- package/docs/i18n/all-tools/ja.json +9 -9
- package/docs/i18n/all-tools/ko.json +9 -9
- package/docs/i18n/all-tools/zh-CN.json +9 -9
- package/docs/i18n/en.json +535 -518
- package/docs/i18n/ja.json +533 -516
- package/docs/i18n/ko.json +535 -518
- package/docs/i18n/zh-CN.json +535 -518
- package/docs/index.html +5 -4
- package/docs/pages/all-tools.html +3 -2
- package/docs/pages/examples.html +58 -31
- package/docs/pages/getting-started.html +3 -3
- package/docs/pages/migration.html +19 -10
- package/package.json +1 -1
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { describe, expect, test } from "vitest";
|
|
2
|
-
import { codeInsight } from "../code_insight.js";
|
|
2
|
+
import { codeInsight, resolveCodeInsightQuery } from "../code_insight.js";
|
|
3
|
+
import * as os from "node:os";
|
|
4
|
+
import * as fs from "node:fs";
|
|
5
|
+
import * as path from "node:path";
|
|
3
6
|
describe("code_insight 单元测试", () => {
|
|
4
7
|
test("mode 非法时返回错误", async () => {
|
|
5
8
|
const result = await codeInsight({
|
|
@@ -32,4 +35,81 @@ describe("code_insight 单元测试", () => {
|
|
|
32
35
|
}
|
|
33
36
|
}
|
|
34
37
|
});
|
|
38
|
+
test("当前目录为家目录时提示传入 project_root", async () => {
|
|
39
|
+
const prev = process.env.MCP_ENABLE_GITNEXUS_BRIDGE;
|
|
40
|
+
process.env.MCP_ENABLE_GITNEXUS_BRIDGE = "1";
|
|
41
|
+
try {
|
|
42
|
+
const result = await codeInsight({
|
|
43
|
+
mode: "query",
|
|
44
|
+
query: "authentication middleware",
|
|
45
|
+
project_root: os.homedir(),
|
|
46
|
+
});
|
|
47
|
+
expect(result.isError).toBe(false);
|
|
48
|
+
expect("structuredContent" in result).toBe(true);
|
|
49
|
+
const structured = result.structuredContent;
|
|
50
|
+
expect(structured.status).toBe("degraded");
|
|
51
|
+
if (structured.warnings.includes("bridge_disabled")) {
|
|
52
|
+
expect(structured.summary).toMatch(/bridge/i);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
expect(structured.warnings).toContain("project_root_required");
|
|
56
|
+
expect(structured.summary).toMatch(/project_root/i);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
if (prev === undefined) {
|
|
61
|
+
delete process.env.MCP_ENABLE_GITNEXUS_BRIDGE;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
process.env.MCP_ENABLE_GITNEXUS_BRIDGE = prev;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
test("auto 模式在缺少 query/target 时使用项目概览默认查询", () => {
|
|
69
|
+
const resolved = resolveCodeInsightQuery({
|
|
70
|
+
mode: "auto",
|
|
71
|
+
query: "",
|
|
72
|
+
target: "",
|
|
73
|
+
input: "",
|
|
74
|
+
});
|
|
75
|
+
expect(resolved.finalTarget).toBe("");
|
|
76
|
+
expect(resolved.finalQuery).toMatch(/核心流程/);
|
|
77
|
+
expect(resolved.finalQuery).toMatch(/依赖关系/);
|
|
78
|
+
});
|
|
79
|
+
test("返回 docs 保存指引而不直接代写文件", async () => {
|
|
80
|
+
const prev = process.env.MCP_ENABLE_GITNEXUS_BRIDGE;
|
|
81
|
+
process.env.MCP_ENABLE_GITNEXUS_BRIDGE = "0";
|
|
82
|
+
const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), "code-insight-docs-"));
|
|
83
|
+
try {
|
|
84
|
+
const result = await codeInsight({
|
|
85
|
+
mode: "auto",
|
|
86
|
+
project_root: projectRoot,
|
|
87
|
+
});
|
|
88
|
+
expect(result.isError).toBe(false);
|
|
89
|
+
const text = String(result.content?.[0]?.text || "");
|
|
90
|
+
const structured = result.structuredContent;
|
|
91
|
+
expect(text).toMatch(/delegated plan/);
|
|
92
|
+
expect(text).toMatch(/不要只口头总结而不写文件/);
|
|
93
|
+
expect(text).toMatch(/docs\/graph-insights\/latest\.md/);
|
|
94
|
+
expect(text).toMatch(/docs\/project-context\.md/);
|
|
95
|
+
expect(structured.projectDocs.latestMarkdownFilePath).toContain("/docs/graph-insights/latest.md");
|
|
96
|
+
expect(structured.projectDocs.archiveMarkdownFilePath).toContain("/docs/graph-insights/");
|
|
97
|
+
expect(structured.projectDocs.projectContextFilePath).toContain("/docs/project-context.md");
|
|
98
|
+
expect(structured.projectDocs.navigationSnippet).toMatch(/代码图谱洞察/);
|
|
99
|
+
expect(structured.plan.mode).toBe("delegated");
|
|
100
|
+
expect(structured.plan.steps[0].outputs[0]).toContain("/docs/project-context.md");
|
|
101
|
+
const updateIndexStep = structured.plan.steps.find((step) => step.id === "update-project-context-index");
|
|
102
|
+
expect(updateIndexStep.note).toMatch(/代码图谱洞察/);
|
|
103
|
+
expect(fs.existsSync(path.join(projectRoot, "docs", "graph-insights", "latest.md"))).toBe(false);
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
107
|
+
if (prev === undefined) {
|
|
108
|
+
delete process.env.MCP_ENABLE_GITNEXUS_BRIDGE;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
process.env.MCP_ENABLE_GITNEXUS_BRIDGE = prev;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
});
|
|
35
115
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { fixBug } from "../fix_bug.js";
|
|
3
|
+
describe("fix_bug 单元测试", () => {
|
|
4
|
+
test("返回 TBP8 结构化输出", async () => {
|
|
5
|
+
const result = await fixBug({
|
|
6
|
+
error_message: "登录提交后页面白屏",
|
|
7
|
+
stack_trace: "TypeError: Cannot read property 'token' of undefined at src/auth/login.ts:45:12",
|
|
8
|
+
expected_behavior: "提示错误并保持页面可交互",
|
|
9
|
+
actual_behavior: "页面白屏,无法继续操作",
|
|
10
|
+
});
|
|
11
|
+
expect(result.isError).toBe(false);
|
|
12
|
+
expect("structuredContent" in result).toBe(true);
|
|
13
|
+
const structured = result.structuredContent;
|
|
14
|
+
expect(structured.analysisMode).toBe("tbp8");
|
|
15
|
+
expect(structured.tbp.phenomenon).toMatch(/页面白屏|登录提交/);
|
|
16
|
+
expect(Array.isArray(structured.tbp.timeline)).toBe(true);
|
|
17
|
+
expect(structured.tbp.timeline.length).toBeGreaterThan(0);
|
|
18
|
+
expect(Array.isArray(structured.tbp.repair)).toBe(true);
|
|
19
|
+
expect(structured.affectedFiles).toContain("src/auth/login.ts");
|
|
20
|
+
});
|
|
21
|
+
test("支持 code_context 并进入证据链", async () => {
|
|
22
|
+
const result = await fixBug({
|
|
23
|
+
error_message: "接口返回成功但前端没生效",
|
|
24
|
+
code_context: "图谱摘要: auth flow\n图谱线索: query: src/modules/record/index.js",
|
|
25
|
+
});
|
|
26
|
+
expect(result.isError).toBe(false);
|
|
27
|
+
const structured = result.structuredContent;
|
|
28
|
+
expect(structured.tbp.evidence.some((item) => item.source === "code_context")).toBe(true);
|
|
29
|
+
expect(structured.fixPlan.steps[0]).toMatch(/TBP-1/);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { gencommit } from "../gencommit.js";
|
|
3
|
+
describe("gencommit 单元测试", () => {
|
|
4
|
+
test("提供变更内容时返回指导文档而不是最终 commit 草稿", async () => {
|
|
5
|
+
const result = await gencommit({
|
|
6
|
+
changes: "修复登录时 token 为空导致的报错\nsrc/auth/login.ts\nsrc/auth/session.ts",
|
|
7
|
+
});
|
|
8
|
+
expect(result.isError).toBe(false);
|
|
9
|
+
expect("structuredContent" in result).toBe(true);
|
|
10
|
+
const structured = result.structuredContent;
|
|
11
|
+
expect(structured.mode).toBe("guidance");
|
|
12
|
+
expect(structured.status).toBe("ready");
|
|
13
|
+
expect(structured.hasChanges).toBe(true);
|
|
14
|
+
expect(structured.steps[2]).toMatch(/不要再调用 gencommit/);
|
|
15
|
+
expect(structured.nextAction).toMatch(/最终 commit message/);
|
|
16
|
+
expect(result.content[0].text).toMatch(/本工具返回的是说明文档/);
|
|
17
|
+
expect(result._meta?.note).toMatch(/固定提交规范说明/);
|
|
18
|
+
});
|
|
19
|
+
test("缺少变更内容时返回非空补充指引", async () => {
|
|
20
|
+
const result = await gencommit({});
|
|
21
|
+
expect(result.isError).toBe(false);
|
|
22
|
+
expect("structuredContent" in result).toBe(true);
|
|
23
|
+
const structured = result.structuredContent;
|
|
24
|
+
expect(structured.mode).toBe("guidance");
|
|
25
|
+
expect(structured.status).toBe("needs_changes");
|
|
26
|
+
expect(structured.hasChanges).toBe(false);
|
|
27
|
+
expect(structured.nextAction).toMatch(/先补充变更内容/);
|
|
28
|
+
expect(result.content[0].text).toMatch(/git diff|git diff --staged/);
|
|
29
|
+
});
|
|
30
|
+
test("结构化输出包含规则、类型列表和模板", async () => {
|
|
31
|
+
const result = await gencommit({
|
|
32
|
+
changes: "更新 README 中关于 code_insight 的使用说明",
|
|
33
|
+
});
|
|
34
|
+
const structured = result.structuredContent;
|
|
35
|
+
expect(Array.isArray(structured.rules)).toBe(true);
|
|
36
|
+
expect(structured.rules).toContain("不要再次调用 gencommit");
|
|
37
|
+
expect(Array.isArray(structured.allowedTypes)).toBe(true);
|
|
38
|
+
expect(structured.allowedTypes.some((item) => item.type === "feat")).toBe(true);
|
|
39
|
+
expect(structured.outputTemplate).toMatch(/<type>: <emoji> <subject>/);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { describe, expect, test } from 'vitest';
|
|
5
|
+
import { initProjectContext } from '../init_project_context.js';
|
|
6
|
+
describe('init_project_context 单元测试', () => {
|
|
7
|
+
test('返回 delegated plan,并预置 code_insight 图谱初始化步骤', async () => {
|
|
8
|
+
const result = await initProjectContext({
|
|
9
|
+
docs_dir: 'docs',
|
|
10
|
+
project_root: 'E:/workspace/github/mcp-probe-kit',
|
|
11
|
+
});
|
|
12
|
+
expect(result.isError).toBeFalsy();
|
|
13
|
+
expect('structuredContent' in result).toBe(true);
|
|
14
|
+
if (!('structuredContent' in result)) {
|
|
15
|
+
throw new Error('structuredContent 缺失');
|
|
16
|
+
}
|
|
17
|
+
const structured = result.structuredContent;
|
|
18
|
+
expect(structured.documentation.some((item) => item.path === 'docs/graph-insights/latest.md')).toBe(true);
|
|
19
|
+
expect(structured.documentation.some((item) => item.path === 'docs/graph-insights/latest.json')).toBe(true);
|
|
20
|
+
expect(structured.metadata?.graphDocs?.latestMarkdownFilePath).toContain('/docs/graph-insights/latest.md');
|
|
21
|
+
const plan = structured.metadata?.plan;
|
|
22
|
+
expect(plan?.mode).toBe('delegated');
|
|
23
|
+
expect(plan.steps.map((step) => step.id)).toEqual([
|
|
24
|
+
'write-project-context',
|
|
25
|
+
'bootstrap-code-insight',
|
|
26
|
+
'persist-graph-docs',
|
|
27
|
+
]);
|
|
28
|
+
expect(plan.steps[1].action).toMatch(/code_insight/);
|
|
29
|
+
});
|
|
30
|
+
test('输出文本包含 graph-insights 入口和 delegated plan', async () => {
|
|
31
|
+
const result = await initProjectContext({
|
|
32
|
+
docs_dir: 'docs',
|
|
33
|
+
project_root: 'E:/workspace/github/mcp-probe-kit',
|
|
34
|
+
});
|
|
35
|
+
const text = result.content[0].text;
|
|
36
|
+
expect(text).toMatch(/graph-insights\/latest\.md/);
|
|
37
|
+
expect(text).toMatch(/delegated plan/);
|
|
38
|
+
expect(text).toMatch(/code_insight/);
|
|
39
|
+
});
|
|
40
|
+
test('已存在 project-context.md 时不再规划重写上下文文档', async () => {
|
|
41
|
+
const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mcp-probe-kit-context-'));
|
|
42
|
+
fs.mkdirSync(path.join(projectRoot, 'docs'), { recursive: true });
|
|
43
|
+
fs.writeFileSync(path.join(projectRoot, 'docs', 'project-context.md'), '# existing context\n', 'utf8');
|
|
44
|
+
const result = await initProjectContext({
|
|
45
|
+
docs_dir: 'docs',
|
|
46
|
+
project_root: projectRoot,
|
|
47
|
+
});
|
|
48
|
+
expect(result.isError).toBeFalsy();
|
|
49
|
+
expect('structuredContent' in result).toBe(true);
|
|
50
|
+
if (!('structuredContent' in result)) {
|
|
51
|
+
throw new Error('structuredContent 缺失');
|
|
52
|
+
}
|
|
53
|
+
const structured = result.structuredContent;
|
|
54
|
+
expect(structured.metadata?.projectContextExists).toBe(true);
|
|
55
|
+
expect(structured.metadata?.plan?.steps.map((step) => step.id)).toEqual([
|
|
56
|
+
'bootstrap-code-insight',
|
|
57
|
+
'persist-graph-docs',
|
|
58
|
+
]);
|
|
59
|
+
const text = result.content[0].text;
|
|
60
|
+
expect(text).toMatch(/已存在(将保留,不覆盖)/);
|
|
61
|
+
expect(text).toMatch(/不要重写/);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -24,9 +24,19 @@ describe('start_bugfix 单元测试', () => {
|
|
|
24
24
|
expect(plan).toBeTruthy();
|
|
25
25
|
expect(plan.mode).toBe('delegated');
|
|
26
26
|
expect(Array.isArray(plan.steps)).toBe(true);
|
|
27
|
+
expect(structured.analysisMode).toBe('tbp8');
|
|
28
|
+
expect(structured.tbp.rootCauseStatement).toMatch(/A \+ B|因果句|待形成/);
|
|
27
29
|
const fixStep = plan.steps.find((step) => step.tool === 'fix_bug');
|
|
28
30
|
expect(fixStep).toBeTruthy();
|
|
29
31
|
expect(fixStep.args.error_message).toBe('TypeError: Cannot read property');
|
|
32
|
+
expect(fixStep.args.analysis_mode).toBe('tbp8');
|
|
33
|
+
const contextStep = plan.steps.find((step) => step.tool === 'init_project_context');
|
|
34
|
+
expect(contextStep.outputs).toContain('docs/graph-insights/latest.md');
|
|
35
|
+
expect(contextStep.outputs).toContain('docs/graph-insights/latest.json');
|
|
36
|
+
expect(contextStep.when).toMatch(/graph-insights\/latest\.md/);
|
|
37
|
+
expect(contextStep.note).toMatch(/兼容老项目|补齐/);
|
|
38
|
+
expect(structured?.metadata?.graphDocs?.latestMarkdownPath).toBe('docs/graph-insights/latest.md');
|
|
39
|
+
expect(structured?.metadata?.graphContext?.summary).toMatch(/GitNexus|图谱|降级/);
|
|
30
40
|
});
|
|
31
41
|
test('loop 模式返回需求循环结构', async () => {
|
|
32
42
|
const result = await startBugfix({
|
|
@@ -30,9 +30,16 @@ describe('start_feature 单元测试', () => {
|
|
|
30
30
|
expect(tools).toContain('init_project_context');
|
|
31
31
|
expect(tools).toContain('add_feature');
|
|
32
32
|
expect(tools).toContain('estimate');
|
|
33
|
+
const contextStep = plan.steps.find((step) => step.tool === 'init_project_context');
|
|
34
|
+
expect(contextStep.outputs).toContain('docs/graph-insights/latest.md');
|
|
35
|
+
expect(contextStep.outputs).toContain('docs/graph-insights/latest.json');
|
|
36
|
+
expect(contextStep.when).toMatch(/graph-insights\/latest\.md/);
|
|
37
|
+
expect(contextStep.note).toMatch(/兼容老项目|补齐/);
|
|
33
38
|
const specStep = plan.steps.find((step) => step.tool === 'add_feature');
|
|
34
39
|
expect(specStep.args.feature_name).toBe('user-auth');
|
|
35
40
|
expect(specStep.outputs).toContain('docs/specs/user-auth/requirements.md');
|
|
41
|
+
expect(structured?.metadata?.graphDocs?.latestMarkdownPath).toBe('docs/graph-insights/latest.md');
|
|
42
|
+
expect(structured?.metadata?.graphContext?.summary).toMatch(/GitNexus|图谱|降级/);
|
|
36
43
|
});
|
|
37
44
|
test('输出文本包含执行计划与关键工具名', async () => {
|
|
38
45
|
const result = await startFeature({
|
|
@@ -43,6 +50,9 @@ describe('start_feature 单元测试', () => {
|
|
|
43
50
|
expect(text).toMatch(/执行计划/);
|
|
44
51
|
expect(text).toMatch(/add_feature/);
|
|
45
52
|
expect(text).toMatch(/estimate/);
|
|
53
|
+
expect(text).toMatch(/graph-insights\/latest\.md/);
|
|
54
|
+
expect(text).toMatch(/任务级收敛/);
|
|
55
|
+
expect(text).toMatch(/project-context\.md/);
|
|
46
56
|
});
|
|
47
57
|
test('template_profile 应该透传到 add_feature 计划', async () => {
|
|
48
58
|
const result = await startFeature({
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
+
import { type CodeInsightMode } from "../lib/gitnexus-bridge.js";
|
|
1
2
|
import { type ToolExecutionContext } from "../lib/tool-execution-context.js";
|
|
3
|
+
export declare function resolveCodeInsightQuery(input: {
|
|
4
|
+
mode: CodeInsightMode;
|
|
5
|
+
query: string;
|
|
6
|
+
target: string;
|
|
7
|
+
input: string;
|
|
8
|
+
}): {
|
|
9
|
+
finalQuery: string;
|
|
10
|
+
finalTarget: string;
|
|
11
|
+
};
|
|
2
12
|
export declare function codeInsight(args: any, context?: ToolExecutionContext): Promise<import("../lib/response.js").ToolResponse | {
|
|
3
13
|
content: {
|
|
4
14
|
type: string;
|
|
@@ -1,9 +1,51 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
1
2
|
import { parseArgs, getString, getNumber, getBoolean } from "../utils/parseArgs.js";
|
|
2
3
|
import { okStructured } from "../lib/response.js";
|
|
4
|
+
import { renderOrchestrationHeader } from "../lib/orchestration-guidance.js";
|
|
3
5
|
import { runCodeInsightBridge, } from "../lib/gitnexus-bridge.js";
|
|
4
6
|
import { throwIfAborted, } from "../lib/tool-execution-context.js";
|
|
5
7
|
const ALLOWED_MODES = new Set(["auto", "query", "context", "impact"]);
|
|
6
8
|
const ALLOWED_DIRECTIONS = new Set(["upstream", "downstream"]);
|
|
9
|
+
const DEFAULT_AUTO_QUERY = "项目整体架构 核心流程 关键模块 依赖关系 入口点";
|
|
10
|
+
function toPosixPath(value) {
|
|
11
|
+
return value.replace(/\\/g, "/");
|
|
12
|
+
}
|
|
13
|
+
function makeSafeSegment(value) {
|
|
14
|
+
return (value || "code-insight")
|
|
15
|
+
.toLowerCase()
|
|
16
|
+
.replace(/[^a-z0-9._-]+/g, "-")
|
|
17
|
+
.replace(/-+/g, "-")
|
|
18
|
+
.replace(/^-|-$/g, "")
|
|
19
|
+
.slice(0, 48) || "code-insight";
|
|
20
|
+
}
|
|
21
|
+
function buildProjectDocsOutputs(input) {
|
|
22
|
+
const projectRoot = input.projectRoot?.trim();
|
|
23
|
+
if (!projectRoot) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const docsRoot = path.join(path.resolve(projectRoot), input.docsDirName || "docs", "graph-insights");
|
|
27
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
28
|
+
const suffix = makeSafeSegment(input.structured.summary || input.mode);
|
|
29
|
+
const baseName = `${timestamp}-${input.mode}-${suffix}`;
|
|
30
|
+
return {
|
|
31
|
+
markdownFilePath: toPosixPath(path.join(docsRoot, `${baseName}.md`)),
|
|
32
|
+
jsonFilePath: toPosixPath(path.join(docsRoot, `${baseName}.json`)),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function renderPlanSteps(steps) {
|
|
36
|
+
return steps
|
|
37
|
+
.map((step, index) => {
|
|
38
|
+
const lines = [`${index + 1}. ${step.action}`];
|
|
39
|
+
if (step.outputs && step.outputs.length > 0) {
|
|
40
|
+
lines.push(` 输出: ${step.outputs.join(", ")}`);
|
|
41
|
+
}
|
|
42
|
+
if (step.note) {
|
|
43
|
+
lines.push(` 说明: ${step.note}`);
|
|
44
|
+
}
|
|
45
|
+
return lines.join("\n");
|
|
46
|
+
})
|
|
47
|
+
.join("\n");
|
|
48
|
+
}
|
|
7
49
|
function normalizeMode(value) {
|
|
8
50
|
const mode = (value || "auto").trim().toLowerCase();
|
|
9
51
|
if (!ALLOWED_MODES.has(mode)) {
|
|
@@ -34,6 +76,13 @@ function summarizeExecutions(executions) {
|
|
|
34
76
|
})
|
|
35
77
|
.join("\n");
|
|
36
78
|
}
|
|
79
|
+
export function resolveCodeInsightQuery(input) {
|
|
80
|
+
const finalTarget = input.target || ((input.mode === "context" || input.mode === "impact") ? input.input : "");
|
|
81
|
+
const finalQuery = input.query
|
|
82
|
+
|| (input.mode === "query" ? input.input : "")
|
|
83
|
+
|| (input.mode === "auto" && !finalTarget ? DEFAULT_AUTO_QUERY : "");
|
|
84
|
+
return { finalQuery, finalTarget };
|
|
85
|
+
}
|
|
37
86
|
export async function codeInsight(args, context) {
|
|
38
87
|
try {
|
|
39
88
|
throwIfAborted(context?.signal, "code_insight 已取消");
|
|
@@ -55,6 +104,8 @@ export async function codeInsight(args, context) {
|
|
|
55
104
|
query: ["q", "keyword", "关键词"],
|
|
56
105
|
target: ["symbol", "name", "目标符号"],
|
|
57
106
|
repo: ["repository", "仓库"],
|
|
107
|
+
project_root: ["projectRoot", "project_path", "path", "dir", "directory", "项目路径", "项目根目录"],
|
|
108
|
+
docs_dir: ["docsDir", "docs", "文档目录"],
|
|
58
109
|
goal: ["目的", "目标"],
|
|
59
110
|
task_context: ["context", "taskContext", "任务上下文"],
|
|
60
111
|
direction: ["dir", "方向"],
|
|
@@ -66,20 +117,27 @@ export async function codeInsight(args, context) {
|
|
|
66
117
|
const query = getString(parsedArgs.query);
|
|
67
118
|
const target = getString(parsedArgs.target);
|
|
68
119
|
const repo = getString(parsedArgs.repo);
|
|
120
|
+
const projectRoot = getString(parsedArgs.project_root);
|
|
121
|
+
const docsDirName = getString(parsedArgs.docs_dir) || "docs";
|
|
69
122
|
const goal = getString(parsedArgs.goal);
|
|
70
123
|
const taskContext = getString(parsedArgs.task_context);
|
|
71
124
|
const direction = normalizeDirection(getString(parsedArgs.direction));
|
|
72
125
|
const maxDepth = Math.max(1, getNumber(parsedArgs.max_depth, 3));
|
|
73
126
|
const includeTests = getBoolean(parsedArgs.include_tests, false);
|
|
74
127
|
const input = getString(parsedArgs.input);
|
|
75
|
-
const finalQuery
|
|
76
|
-
|
|
128
|
+
const { finalQuery, finalTarget } = resolveCodeInsightQuery({
|
|
129
|
+
mode,
|
|
130
|
+
query,
|
|
131
|
+
target,
|
|
132
|
+
input,
|
|
133
|
+
});
|
|
77
134
|
throwIfAborted(context?.signal, "code_insight 已取消");
|
|
78
135
|
const result = await runCodeInsightBridge({
|
|
79
136
|
mode,
|
|
80
137
|
query: finalQuery,
|
|
81
138
|
target: finalTarget,
|
|
82
139
|
repo: repo || undefined,
|
|
140
|
+
projectRoot: projectRoot || undefined,
|
|
83
141
|
goal: goal || undefined,
|
|
84
142
|
taskContext: taskContext || undefined,
|
|
85
143
|
direction,
|
|
@@ -98,6 +156,8 @@ export async function codeInsight(args, context) {
|
|
|
98
156
|
状态: ${result.available ? "可用" : "降级"}
|
|
99
157
|
模式: ${result.modeRequested} -> ${result.modeResolved}
|
|
100
158
|
来源: ${result.provider}
|
|
159
|
+
工作区: ${result.workspaceMode}
|
|
160
|
+
源目录: ${result.sourceRoot}
|
|
101
161
|
|
|
102
162
|
摘要:
|
|
103
163
|
${result.summary}
|
|
@@ -106,7 +166,7 @@ ${result.summary}
|
|
|
106
166
|
${executionSummary}
|
|
107
167
|
|
|
108
168
|
${result.warnings.length > 0 ? `警告: ${result.warnings.join(", ")}` : ""}`.trim();
|
|
109
|
-
|
|
169
|
+
const structured = {
|
|
110
170
|
status: result.available ? "ok" : "degraded",
|
|
111
171
|
provider: result.provider,
|
|
112
172
|
mode: {
|
|
@@ -117,7 +177,100 @@ ${result.warnings.length > 0 ? `警告: ${result.warnings.join(", ")}` : ""}`.tr
|
|
|
117
177
|
warnings: result.warnings,
|
|
118
178
|
executions: result.executions,
|
|
119
179
|
repo: result.repo || null,
|
|
180
|
+
workspaceMode: result.workspaceMode,
|
|
181
|
+
sourceRoot: result.sourceRoot,
|
|
182
|
+
analysisRoot: result.analysisRoot,
|
|
183
|
+
pathMapped: result.pathMapped,
|
|
184
|
+
};
|
|
185
|
+
const docsSnapshot = buildProjectDocsOutputs({
|
|
186
|
+
projectRoot: projectRoot || result.sourceRoot,
|
|
187
|
+
docsDirName,
|
|
188
|
+
mode,
|
|
189
|
+
structured,
|
|
120
190
|
});
|
|
191
|
+
let projectDocs;
|
|
192
|
+
let persistencePlan;
|
|
193
|
+
if (docsSnapshot) {
|
|
194
|
+
const docsDir = path.dirname(docsSnapshot.markdownFilePath);
|
|
195
|
+
projectDocs = {
|
|
196
|
+
docsDir,
|
|
197
|
+
projectContextFilePath: toPosixPath(path.join(path.resolve(projectRoot || result.sourceRoot), docsDirName, "project-context.md")),
|
|
198
|
+
latestMarkdownFilePath: toPosixPath(path.join(docsDir, "latest.md")),
|
|
199
|
+
latestJsonFilePath: toPosixPath(path.join(docsDir, "latest.json")),
|
|
200
|
+
archiveMarkdownFilePath: docsSnapshot.markdownFilePath,
|
|
201
|
+
archiveJsonFilePath: docsSnapshot.jsonFilePath,
|
|
202
|
+
navigationSnippet: `### [代码图谱洞察](./graph-insights/latest.md)
|
|
203
|
+
最近一次 code_insight 分析结果,包含调用链、上下文与影响面摘要
|
|
204
|
+
`,
|
|
205
|
+
devGuideSnippet: `- **代码图谱洞察**: [graph-insights/latest.md](./graph-insights/latest.md) - 需要理解模块依赖、调用链和影响面时优先查看
|
|
206
|
+
`,
|
|
207
|
+
};
|
|
208
|
+
structured.projectDocs = projectDocs;
|
|
209
|
+
structured.nextAction = `请按 delegated plan 落盘图谱文档,并更新 ${projectDocs.projectContextFilePath} 的索引入口`;
|
|
210
|
+
persistencePlan = {
|
|
211
|
+
mode: "delegated",
|
|
212
|
+
steps: [
|
|
213
|
+
{
|
|
214
|
+
id: "ensure-project-context",
|
|
215
|
+
action: `检查 ${projectDocs.projectContextFilePath} 是否存在;若不存在,先调用 init_project_context 生成项目上下文索引`,
|
|
216
|
+
outputs: [projectDocs.projectContextFilePath],
|
|
217
|
+
note: "只有 project-context.md 存在,后续图谱文档入口才可持续复用",
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
id: "save-latest-md",
|
|
221
|
+
action: `将本次 code_insight 的文本分析结果写入 ${projectDocs.latestMarkdownFilePath}`,
|
|
222
|
+
outputs: [projectDocs.latestMarkdownFilePath],
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
id: "save-archive-md",
|
|
226
|
+
action: `将本次 code_insight 的文本分析结果归档到 ${projectDocs.archiveMarkdownFilePath}`,
|
|
227
|
+
outputs: [projectDocs.archiveMarkdownFilePath],
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
id: "save-latest-json",
|
|
231
|
+
action: `将本次 code_insight 的 structuredContent 写入 ${projectDocs.latestJsonFilePath}`,
|
|
232
|
+
outputs: [projectDocs.latestJsonFilePath],
|
|
233
|
+
note: "建议保留完整结构化结果,便于后续 AI 继续读取",
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
id: "save-archive-json",
|
|
237
|
+
action: `将本次 code_insight 的 structuredContent 归档到 ${projectDocs.archiveJsonFilePath}`,
|
|
238
|
+
outputs: [projectDocs.archiveJsonFilePath],
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
id: "update-project-context-index",
|
|
242
|
+
action: `更新 ${projectDocs.projectContextFilePath},在“## 📚 文档导航”加入图谱文档入口,并在“## 💡 开发时查看对应文档”加入代码图谱洞察链接`,
|
|
243
|
+
outputs: [projectDocs.projectContextFilePath],
|
|
244
|
+
note: `建议插入内容:\n${projectDocs.navigationSnippet}\n${projectDocs.devGuideSnippet}`,
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
};
|
|
248
|
+
structured.plan = persistencePlan;
|
|
249
|
+
}
|
|
250
|
+
return okStructured(projectDocs && persistencePlan
|
|
251
|
+
? `${renderOrchestrationHeader({
|
|
252
|
+
tool: "code_insight",
|
|
253
|
+
goal: "完成图谱分析后,将结果按 delegated plan 落盘到 docs/graph-insights",
|
|
254
|
+
tasks: [
|
|
255
|
+
"先消费本次 code_insight 返回的分析结果",
|
|
256
|
+
"严格按 delegated plan 将 Markdown / JSON 保存到指定路径",
|
|
257
|
+
"不要只口头总结而不写文件",
|
|
258
|
+
],
|
|
259
|
+
notes: [
|
|
260
|
+
`工作区模式: ${result.workspaceMode}`,
|
|
261
|
+
`来源目录: ${result.sourceRoot}`,
|
|
262
|
+
],
|
|
263
|
+
})}${message}
|
|
264
|
+
|
|
265
|
+
## delegated plan
|
|
266
|
+
${renderPlanSteps(persistencePlan.steps)}
|
|
267
|
+
|
|
268
|
+
后续操作:
|
|
269
|
+
- 请先确保 ${projectDocs.projectContextFilePath} 可用,并把图谱入口挂到该索引中
|
|
270
|
+
- 请将本次分析保存到 ${projectDocs.latestMarkdownFilePath}
|
|
271
|
+
- 如需归档,请额外保存到 ${projectDocs.archiveMarkdownFilePath}
|
|
272
|
+
- 如需结构化副本,请保存 JSON 到 ${projectDocs.latestJsonFilePath} 或 ${projectDocs.archiveJsonFilePath}`
|
|
273
|
+
: message, structured);
|
|
121
274
|
}
|
|
122
275
|
catch (error) {
|
|
123
276
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
package/build/tools/fix_bug.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* fix_bug 工具
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* 模式:指令生成器模式 -
|
|
4
|
+
* 功能:基于 TBP 8 步法输出真因分析与修复指南
|
|
5
|
+
* 模式:指令生成器模式 - 返回详细的 RCA 与修复指南,由 AI 执行实际操作
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* 流程:定义现象 → 复盘时间线 → 排除错误方向 → 找共同模式 → 定位边界 → 陈述真因 → 闭合证据链 → 设计修复方案
|
|
8
8
|
*/
|
|
9
9
|
/**
|
|
10
10
|
* fix_bug 工具实现
|