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.
Files changed (52) hide show
  1. package/README.md +15 -4
  2. package/build/lib/__tests__/gitnexus-bridge.unit.test.d.ts +1 -0
  3. package/build/lib/__tests__/gitnexus-bridge.unit.test.js +86 -0
  4. package/build/lib/gitnexus-bridge.d.ts +23 -0
  5. package/build/lib/gitnexus-bridge.js +518 -22
  6. package/build/schemas/code-analysis-tools.d.ts +9 -1
  7. package/build/schemas/code-analysis-tools.js +9 -1
  8. package/build/schemas/git-tools.d.ts +1 -1
  9. package/build/schemas/git-tools.js +1 -1
  10. package/build/schemas/index.d.ts +23 -3
  11. package/build/schemas/orchestration-tools.d.ts +13 -1
  12. package/build/schemas/orchestration-tools.js +13 -1
  13. package/build/schemas/output/core-tools.d.ts +130 -1
  14. package/build/schemas/output/core-tools.js +71 -1
  15. package/build/schemas/output/index.d.ts +2 -2
  16. package/build/schemas/output/index.js +2 -2
  17. package/build/schemas/output/project-tools.d.ts +13 -0
  18. package/build/schemas/output/project-tools.js +9 -0
  19. package/build/schemas/structured-output.d.ts +358 -5
  20. package/build/schemas/structured-output.js +169 -5
  21. package/build/tools/__tests__/code_insight.unit.test.js +81 -1
  22. package/build/tools/__tests__/fix_bug.unit.test.d.ts +1 -0
  23. package/build/tools/__tests__/fix_bug.unit.test.js +31 -0
  24. package/build/tools/__tests__/gencommit.unit.test.d.ts +1 -0
  25. package/build/tools/__tests__/gencommit.unit.test.js +41 -0
  26. package/build/tools/__tests__/init_project_context.unit.test.d.ts +1 -0
  27. package/build/tools/__tests__/init_project_context.unit.test.js +63 -0
  28. package/build/tools/__tests__/start_bugfix.unit.test.js +10 -0
  29. package/build/tools/__tests__/start_feature.unit.test.js +10 -0
  30. package/build/tools/code_insight.d.ts +10 -0
  31. package/build/tools/code_insight.js +156 -3
  32. package/build/tools/fix_bug.d.ts +3 -3
  33. package/build/tools/fix_bug.js +297 -312
  34. package/build/tools/gencommit.js +144 -123
  35. package/build/tools/init_project_context.js +211 -53
  36. package/build/tools/start_bugfix.js +170 -70
  37. package/build/tools/start_feature.js +79 -25
  38. package/docs/data/tools.js +33 -31
  39. package/docs/i18n/all-tools/en.json +9 -9
  40. package/docs/i18n/all-tools/ja.json +9 -9
  41. package/docs/i18n/all-tools/ko.json +9 -9
  42. package/docs/i18n/all-tools/zh-CN.json +9 -9
  43. package/docs/i18n/en.json +535 -518
  44. package/docs/i18n/ja.json +533 -516
  45. package/docs/i18n/ko.json +535 -518
  46. package/docs/i18n/zh-CN.json +535 -518
  47. package/docs/index.html +5 -4
  48. package/docs/pages/all-tools.html +3 -2
  49. package/docs/pages/examples.html +58 -31
  50. package/docs/pages/getting-started.html +3 -3
  51. package/docs/pages/migration.html +19 -10
  52. 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,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 = query || (mode === "query" ? input : "");
76
- const finalTarget = target || ((mode === "context" || mode === "impact") ? input : "");
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
- return okStructured(message, {
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);
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * fix_bug 工具
3
3
  *
4
- * 功能:指导完整的 Bug 修复流程
5
- * 模式:指令生成器模式 - 返回详细的修复指南,由 AI 执行实际操作
4
+ * 功能:基于 TBP 8 步法输出真因分析与修复指南
5
+ * 模式:指令生成器模式 - 返回详细的 RCA 与修复指南,由 AI 执行实际操作
6
6
  *
7
- * 流程:问题定位原因分析修复方案验证测试
7
+ * 流程:定义现象复盘时间线排除错误方向找共同模式 → 定位边界 → 陈述真因 → 闭合证据链 → 设计修复方案
8
8
  */
9
9
  /**
10
10
  * fix_bug 工具实现