ai-dev-harness 0.1.0 → 0.1.1
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/COLLEAGUE_TRIAL_GUIDE.md +147 -0
- package/README.md +116 -52
- package/dist/agent.js +44 -31
- package/dist/cli.js +65 -42
- package/dist/config.js +79 -8
- package/dist/context.js +53 -21
- package/dist/init.js +8 -20
- package/dist/integration.js +22 -91
- package/dist/mcp.js +29 -112
- package/dist/runPaths.js +1 -0
- package/dist/summarize.js +42 -39
- package/dist/templates.js +39 -215
- package/package.json +3 -1
- package/scripts/read-evidence.ps1 +9 -3
- package/scripts/run-harness.ps1 +8 -5
- package/templates/default-project/AGENTS.md +9 -0
- package/templates/default-project/SKILLS.md +13 -0
- package/templates/default-project/TOOLS.md +8 -0
- package/templates/default-project/skills/add_feature/SKILL.md +34 -0
- package/templates/default-project/skills/add_feature/context.yaml +30 -0
- package/templates/default-project/skills/add_feature/verify.yaml +2 -0
- package/templates/default-project/skills/fix_bug/SKILL.md +45 -0
- package/templates/default-project/skills/fix_bug/context.yaml +30 -0
- package/templates/default-project/skills/fix_bug/verify.yaml +2 -0
- package/templates/default-project/skills/update_docs/SKILL.md +28 -0
- package/templates/default-project/skills/update_docs/context.yaml +26 -0
- package/templates/default-project/skills/update_docs/verify.yaml +2 -0
- package/templates/default-project/skills/write_tests/SKILL.md +30 -0
- package/templates/default-project/skills/write_tests/context.yaml +29 -0
- package/templates/default-project/skills/write_tests/verify.yaml +2 -0
- package/templates/integrations/claude/SKILL.md +79 -0
- package/templates/integrations/codex/AGENTS_BLOCK.md +51 -0
package/dist/integration.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { appendFile,
|
|
2
|
+
import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { readTemplateFile, renderTemplate } from "./templates.js";
|
|
3
4
|
import { pathExists } from "./utils.js";
|
|
4
5
|
export async function installIntegration(options) {
|
|
5
6
|
const created = [];
|
|
6
7
|
const updated = [];
|
|
7
8
|
const skipped = [];
|
|
8
|
-
const scriptResult = await installScriptAssets(options.targetDir, options.packageRoot ?? process.cwd());
|
|
9
|
+
const scriptResult = await installScriptAssets(options.targetDir, options.packageRoot ?? process.cwd(), options.harnessCliPath);
|
|
9
10
|
created.push(...scriptResult.created);
|
|
10
11
|
updated.push(...scriptResult.updated);
|
|
11
12
|
skipped.push(...scriptResult.skipped);
|
|
12
13
|
if (options.agent === "claude" || options.agent === "both") {
|
|
13
|
-
const result = await installClaudeSkill(options.targetDir, options.harnessCommand);
|
|
14
|
+
const result = await installClaudeSkill(options.targetDir, options.harnessCommand, options.packageRoot);
|
|
14
15
|
if (result === "created")
|
|
15
16
|
created.push(".claude/skills/ai-dev-harness/SKILL.md");
|
|
16
17
|
else if (result === "updated")
|
|
@@ -19,7 +20,7 @@ export async function installIntegration(options) {
|
|
|
19
20
|
skipped.push(".claude/skills/ai-dev-harness/SKILL.md");
|
|
20
21
|
}
|
|
21
22
|
if (options.agent === "codex" || options.agent === "both") {
|
|
22
|
-
const result = await installCodexRules(options.targetDir, options.harnessCommand);
|
|
23
|
+
const result = await installCodexRules(options.targetDir, options.harnessCommand, options.packageRoot);
|
|
23
24
|
if (result === "created")
|
|
24
25
|
created.push("AGENTS.md");
|
|
25
26
|
else if (result === "updated")
|
|
@@ -29,7 +30,7 @@ export async function installIntegration(options) {
|
|
|
29
30
|
}
|
|
30
31
|
return { created, updated, skipped };
|
|
31
32
|
}
|
|
32
|
-
async function installScriptAssets(targetDir, packageRoot) {
|
|
33
|
+
async function installScriptAssets(targetDir, packageRoot, harnessCliPath) {
|
|
33
34
|
const created = [];
|
|
34
35
|
const updated = [];
|
|
35
36
|
const skipped = [];
|
|
@@ -42,7 +43,8 @@ async function installScriptAssets(targetDir, packageRoot) {
|
|
|
42
43
|
const rel = path.join(".harness", "ai-dev-harness", "scripts", asset);
|
|
43
44
|
if (!(await pathExists(source)))
|
|
44
45
|
continue;
|
|
45
|
-
const
|
|
46
|
+
const raw = await readFile(source, "utf8");
|
|
47
|
+
const next = renderTemplate(raw, { HARNESS_CLI_PATH: harnessCliPath ?? "{{HARNESS_CLI_PATH}}" });
|
|
46
48
|
if (await pathExists(target)) {
|
|
47
49
|
const current = await readFile(target, "utf8");
|
|
48
50
|
if (current === next) {
|
|
@@ -53,28 +55,28 @@ async function installScriptAssets(targetDir, packageRoot) {
|
|
|
53
55
|
updated.push(rel);
|
|
54
56
|
continue;
|
|
55
57
|
}
|
|
56
|
-
await
|
|
58
|
+
await writeFile(target, next, "utf8");
|
|
57
59
|
created.push(rel);
|
|
58
60
|
}
|
|
59
61
|
return { created, updated, skipped };
|
|
60
62
|
}
|
|
61
|
-
async function installClaudeSkill(targetDir, harnessCommand) {
|
|
63
|
+
async function installClaudeSkill(targetDir, harnessCommand, packageRoot) {
|
|
62
64
|
const skillPath = path.join(targetDir, ".claude", "skills", "ai-dev-harness", "SKILL.md");
|
|
63
65
|
await mkdir(path.dirname(skillPath), { recursive: true });
|
|
64
66
|
if (await pathExists(skillPath)) {
|
|
65
67
|
const current = await readFile(skillPath, "utf8");
|
|
66
|
-
const next = claudeSkill(harnessCommand);
|
|
68
|
+
const next = await claudeSkill(harnessCommand, packageRoot);
|
|
67
69
|
if (current === next)
|
|
68
70
|
return "skipped";
|
|
69
71
|
await writeFile(skillPath, next, "utf8");
|
|
70
72
|
return "updated";
|
|
71
73
|
}
|
|
72
|
-
await writeFile(skillPath, claudeSkill(harnessCommand), "utf8");
|
|
74
|
+
await writeFile(skillPath, await claudeSkill(harnessCommand, packageRoot), "utf8");
|
|
73
75
|
return "created";
|
|
74
76
|
}
|
|
75
|
-
async function installCodexRules(targetDir, harnessCommand) {
|
|
77
|
+
async function installCodexRules(targetDir, harnessCommand, packageRoot) {
|
|
76
78
|
const agentsPath = path.join(targetDir, "AGENTS.md");
|
|
77
|
-
const block = codexBlock(harnessCommand);
|
|
79
|
+
const block = await codexBlock(harnessCommand, packageRoot);
|
|
78
80
|
if (!(await pathExists(agentsPath))) {
|
|
79
81
|
await writeFile(agentsPath, `# Project Agent Rules\n\n${block}`, "utf8");
|
|
80
82
|
return "created";
|
|
@@ -90,84 +92,13 @@ async function installCodexRules(targetDir, harnessCommand) {
|
|
|
90
92
|
await appendFile(agentsPath, `\n\n${block}`, "utf8");
|
|
91
93
|
return "updated";
|
|
92
94
|
}
|
|
93
|
-
function claudeSkill(harnessCommand) {
|
|
94
|
-
return
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
---
|
|
98
|
-
|
|
99
|
-
# AI Dev Harness
|
|
100
|
-
|
|
101
|
-
Use this skill when the user asks to use Harness, AI Dev Harness, an audited run, CodeGraph/Knowledge Graph context, or one of these skills: \`fix_bug\`, \`add_feature\`, \`write_tests\`, \`update_docs\`.
|
|
102
|
-
|
|
103
|
-
## Command
|
|
104
|
-
|
|
105
|
-
Run from the repository root:
|
|
106
|
-
|
|
107
|
-
\`\`\`bash
|
|
108
|
-
powershell -ExecutionPolicy Bypass -File ".harness/ai-dev-harness/scripts/run-harness.ps1" -Task "<task>" -Skill <skill> -Agent claude -Live
|
|
109
|
-
\`\`\`
|
|
110
|
-
|
|
111
|
-
Fallback direct command:
|
|
112
|
-
|
|
113
|
-
\`\`\`bash
|
|
114
|
-
${harnessCommand} run --task "<task>" --skill <skill> --agent claude --live
|
|
115
|
-
\`\`\`
|
|
116
|
-
|
|
117
|
-
## Skill Mapping
|
|
118
|
-
|
|
119
|
-
- Defect or regression: \`fix_bug\`
|
|
120
|
-
- Scoped feature: \`add_feature\`
|
|
121
|
-
- Add or improve tests: \`write_tests\`
|
|
122
|
-
- Read-only analysis or docs: \`update_docs\`
|
|
123
|
-
|
|
124
|
-
For read-only analysis, include "Do not modify repository files" in the task.
|
|
125
|
-
|
|
126
|
-
## Reporting
|
|
127
|
-
|
|
128
|
-
After the command finishes, inspect and report:
|
|
129
|
-
|
|
130
|
-
- Evidence directory printed by Harness
|
|
131
|
-
- \`summary.md\`
|
|
132
|
-
- \`verify.json\`
|
|
133
|
-
- \`audit.jsonl\`
|
|
134
|
-
- MCP degradation errors, if present
|
|
135
|
-
|
|
136
|
-
You may use:
|
|
137
|
-
|
|
138
|
-
\`\`\`bash
|
|
139
|
-
powershell -ExecutionPolicy Bypass -File ".harness/ai-dev-harness/scripts/read-evidence.ps1" -Run <run_id_or_path>
|
|
140
|
-
\`\`\`
|
|
141
|
-
|
|
142
|
-
Do not bypass Harness for implementation tasks unless the user explicitly asks for direct manual work.
|
|
143
|
-
`;
|
|
95
|
+
async function claudeSkill(harnessCommand, packageRoot) {
|
|
96
|
+
return renderTemplate(await readTemplateFile(path.join("integrations", "claude", "SKILL.md"), packageRoot), {
|
|
97
|
+
HARNESS_COMMAND: harnessCommand
|
|
98
|
+
});
|
|
144
99
|
}
|
|
145
|
-
function codexBlock(harnessCommand) {
|
|
146
|
-
return
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
- \`fix_bug\`
|
|
151
|
-
- \`add_feature\`
|
|
152
|
-
- \`write_tests\`
|
|
153
|
-
- \`update_docs\`
|
|
154
|
-
|
|
155
|
-
Run from the repository root:
|
|
156
|
-
|
|
157
|
-
\`\`\`bash
|
|
158
|
-
powershell -ExecutionPolicy Bypass -File ".harness/ai-dev-harness/scripts/run-harness.ps1" -Task "<task>" -Skill <skill> -Agent codex -Live
|
|
159
|
-
\`\`\`
|
|
160
|
-
|
|
161
|
-
Fallback direct command:
|
|
162
|
-
|
|
163
|
-
\`\`\`bash
|
|
164
|
-
${harnessCommand} run --task "<task>" --skill <skill> --agent codex --live
|
|
165
|
-
\`\`\`
|
|
166
|
-
|
|
167
|
-
For read-only analysis, include "Do not modify repository files" in the task and usually use \`update_docs\`.
|
|
168
|
-
|
|
169
|
-
After every Harness run, report the evidence directory, inspect \`summary.md\`, \`verify.json\`, and \`audit.jsonl\`, and report MCP degradation errors if present.
|
|
170
|
-
Use \`.harness/ai-dev-harness/scripts/read-evidence.ps1\` to print the evidence summary when useful.
|
|
171
|
-
Do not write directly to Knowledge Graph in v1; use \`persist_suggestions.md\`.
|
|
172
|
-
`;
|
|
100
|
+
async function codexBlock(harnessCommand, packageRoot) {
|
|
101
|
+
return renderTemplate(await readTemplateFile(path.join("integrations", "codex", "AGENTS_BLOCK.md"), packageRoot), {
|
|
102
|
+
HARNESS_COMMAND: harnessCommand
|
|
103
|
+
});
|
|
173
104
|
}
|
package/dist/mcp.js
CHANGED
|
@@ -1,49 +1,25 @@
|
|
|
1
1
|
import { appendFile } from "node:fs/promises";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
method: "POST",
|
|
9
|
-
headers: { "content-type": "application/json", accept: "application/json, text/event-stream" },
|
|
10
|
-
body: JSON.stringify(body),
|
|
11
|
-
signal: controller.signal
|
|
12
|
-
});
|
|
13
|
-
const text = await response.text();
|
|
14
|
-
if (!response.ok)
|
|
15
|
-
throw new Error(`HTTP ${response.status}: ${truncate(text, 1000)}`);
|
|
16
|
-
return parseMcpResponse(text, response.headers.get("content-type") ?? "");
|
|
17
|
-
}
|
|
18
|
-
finally {
|
|
19
|
-
clearTimeout(timer);
|
|
20
|
-
}
|
|
2
|
+
function endpointSummary(endpoint) {
|
|
3
|
+
return {
|
|
4
|
+
type: endpoint.type ?? "http",
|
|
5
|
+
url: endpoint.url,
|
|
6
|
+
headerNames: Object.keys(endpoint.headers ?? {})
|
|
7
|
+
};
|
|
21
8
|
}
|
|
22
|
-
function
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
try {
|
|
39
|
-
return JSON.parse(text);
|
|
40
|
-
}
|
|
41
|
-
catch {
|
|
42
|
-
return text;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
function summarize(data) {
|
|
46
|
-
return truncate(typeof data === "string" ? data : JSON.stringify(data, null, 2), 6000);
|
|
9
|
+
function agentDrivenResult(source, endpoint, skill) {
|
|
10
|
+
const request = {
|
|
11
|
+
mode: "agent-driven",
|
|
12
|
+
instruction: "Claude/Codex should call this MCP server with its own configured MCP client before invoking Harness. Harness records the configured source but does not implement MCP transport.",
|
|
13
|
+
endpoint: endpointSummary(endpoint),
|
|
14
|
+
suggestedQueries: skill.contextQueries
|
|
15
|
+
};
|
|
16
|
+
return {
|
|
17
|
+
source,
|
|
18
|
+
ok: true,
|
|
19
|
+
request,
|
|
20
|
+
responseSummary: `${source} is configured as an agent-driven MCP context source.`,
|
|
21
|
+
data: request
|
|
22
|
+
};
|
|
47
23
|
}
|
|
48
24
|
export class CodeGraphSource {
|
|
49
25
|
config;
|
|
@@ -52,41 +28,10 @@ export class CodeGraphSource {
|
|
|
52
28
|
this.config = config;
|
|
53
29
|
this.queryLogPath = queryLogPath;
|
|
54
30
|
}
|
|
55
|
-
async getImpactContext(
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
skill: skill.name,
|
|
60
|
-
queries: skill.contextQueries,
|
|
61
|
-
changedFiles
|
|
62
|
-
};
|
|
63
|
-
return this.query("codegraph", this.config.mcp.codegraph.url, request);
|
|
64
|
-
}
|
|
65
|
-
async query(source, url, request) {
|
|
66
|
-
try {
|
|
67
|
-
const data = await postJson(url, request, this.config.mcp.timeoutMs);
|
|
68
|
-
const result = {
|
|
69
|
-
source,
|
|
70
|
-
ok: true,
|
|
71
|
-
degraded: false,
|
|
72
|
-
request,
|
|
73
|
-
responseSummary: summarize(data),
|
|
74
|
-
data
|
|
75
|
-
};
|
|
76
|
-
await appendFile(this.queryLogPath, `${JSON.stringify(result)}\n`, "utf8");
|
|
77
|
-
return result;
|
|
78
|
-
}
|
|
79
|
-
catch (error) {
|
|
80
|
-
const result = {
|
|
81
|
-
source,
|
|
82
|
-
ok: false,
|
|
83
|
-
degraded: true,
|
|
84
|
-
request,
|
|
85
|
-
error: error instanceof Error ? error.message : String(error)
|
|
86
|
-
};
|
|
87
|
-
await appendFile(this.queryLogPath, `${JSON.stringify(result)}\n`, "utf8");
|
|
88
|
-
return result;
|
|
89
|
-
}
|
|
31
|
+
async getImpactContext(_task, skill) {
|
|
32
|
+
const result = agentDrivenResult("codegraph", this.config.mcp.codegraph, skill);
|
|
33
|
+
await appendFile(this.queryLogPath, `${JSON.stringify(result)}\n`, "utf8");
|
|
34
|
+
return result;
|
|
90
35
|
}
|
|
91
36
|
}
|
|
92
37
|
export class KnowledgeGraphSource {
|
|
@@ -96,38 +41,10 @@ export class KnowledgeGraphSource {
|
|
|
96
41
|
this.config = config;
|
|
97
42
|
this.queryLogPath = queryLogPath;
|
|
98
43
|
}
|
|
99
|
-
async searchKnowledge(
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
skill: skill.name,
|
|
104
|
-
queries: skill.contextQueries,
|
|
105
|
-
codeContext
|
|
106
|
-
};
|
|
107
|
-
try {
|
|
108
|
-
const data = await postJson(this.config.mcp.knowledgeGraph.url, request, this.config.mcp.timeoutMs);
|
|
109
|
-
const result = {
|
|
110
|
-
source: "knowledgeGraph",
|
|
111
|
-
ok: true,
|
|
112
|
-
degraded: false,
|
|
113
|
-
request,
|
|
114
|
-
responseSummary: summarize(data),
|
|
115
|
-
data
|
|
116
|
-
};
|
|
117
|
-
await appendFile(this.queryLogPath, `${JSON.stringify(result)}\n`, "utf8");
|
|
118
|
-
return result;
|
|
119
|
-
}
|
|
120
|
-
catch (error) {
|
|
121
|
-
const result = {
|
|
122
|
-
source: "knowledgeGraph",
|
|
123
|
-
ok: false,
|
|
124
|
-
degraded: true,
|
|
125
|
-
request,
|
|
126
|
-
error: error instanceof Error ? error.message : String(error)
|
|
127
|
-
};
|
|
128
|
-
await appendFile(this.queryLogPath, `${JSON.stringify(result)}\n`, "utf8");
|
|
129
|
-
return result;
|
|
130
|
-
}
|
|
44
|
+
async searchKnowledge(_task, skill) {
|
|
45
|
+
const result = agentDrivenResult("knowledgeGraph", this.config.mcp.knowledgeGraph, skill);
|
|
46
|
+
await appendFile(this.queryLogPath, `${JSON.stringify(result)}\n`, "utf8");
|
|
47
|
+
return result;
|
|
131
48
|
}
|
|
132
49
|
}
|
|
133
50
|
export class ContextSource {
|
|
@@ -139,7 +56,7 @@ export class ContextSource {
|
|
|
139
56
|
}
|
|
140
57
|
async searchTaskContext(task, skill) {
|
|
141
58
|
const codeContext = await this.codeGraph.getImpactContext(task, skill);
|
|
142
|
-
const knowledgeContext = await this.knowledgeGraph.searchKnowledge(task, skill
|
|
59
|
+
const knowledgeContext = await this.knowledgeGraph.searchKnowledge(task, skill);
|
|
143
60
|
return { codeContext, knowledgeContext };
|
|
144
61
|
}
|
|
145
62
|
}
|
package/dist/runPaths.js
CHANGED
|
@@ -12,6 +12,7 @@ export function makeRunPaths(cwd, config, task) {
|
|
|
12
12
|
agentPrompt: path.join(runDir, "agent_prompt.md"),
|
|
13
13
|
plan: path.join(runDir, "plan.md"),
|
|
14
14
|
executionLog: path.join(runDir, "execution.log"),
|
|
15
|
+
agentContract: path.join(runDir, "agent_contract.json"),
|
|
15
16
|
gitDiff: path.join(runDir, "git_diff.patch"),
|
|
16
17
|
verify: path.join(runDir, "verify.json"),
|
|
17
18
|
summary: path.join(runDir, "summary.md"),
|
package/dist/summarize.js
CHANGED
|
@@ -2,14 +2,14 @@ import { readFile, writeFile } from "node:fs/promises";
|
|
|
2
2
|
import { truncate } from "./utils.js";
|
|
3
3
|
export async function writeSummary(options) {
|
|
4
4
|
const diff = await readFile(options.gitDiffPath, "utf8").catch(() => "");
|
|
5
|
-
const summary = `#
|
|
5
|
+
const summary = `# 运行总结
|
|
6
6
|
|
|
7
|
-
##
|
|
8
|
-
-
|
|
9
|
-
- Agent
|
|
10
|
-
-
|
|
7
|
+
## 执行状态
|
|
8
|
+
- 任务状态: ${options.agentResult?.status === "success" && options.verifyResult?.status === "passed" ? "可进入评审" : "需要关注"}
|
|
9
|
+
- Agent 状态: ${options.agentResult?.status ?? "未运行"}
|
|
10
|
+
- 验证状态: ${options.verifyResult?.status ?? "未运行"}
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## 任务
|
|
13
13
|
${options.task}
|
|
14
14
|
|
|
15
15
|
## Skill
|
|
@@ -18,54 +18,57 @@ ${options.skillName}
|
|
|
18
18
|
## Agent
|
|
19
19
|
${options.agentName}
|
|
20
20
|
|
|
21
|
-
##
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
21
|
+
## 执行结果
|
|
22
|
+
- 状态: ${options.agentResult?.status ?? "未运行"}
|
|
23
|
+
- 退出码: ${options.agentResult?.exitCode ?? "n/a"}
|
|
24
|
+
- 修改文件: ${options.agentResult?.changedFiles.join(", ") || "无"}
|
|
25
25
|
|
|
26
|
-
##
|
|
27
|
-
-
|
|
26
|
+
## 验证结果
|
|
27
|
+
- 状态: ${options.verifyResult?.status ?? "未运行"}
|
|
28
28
|
|
|
29
|
-
##
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
29
|
+
## 评审检查清单
|
|
30
|
+
- 确认变更限定在任务范围内。
|
|
31
|
+
- 确认公共接口和向后兼容性没有被意外破坏。
|
|
32
|
+
- 确认失败或跳过的验证项已被理解。
|
|
33
|
+
- 确认知识沉淀建议经过人工评审后再写入 Knowledge Graph。
|
|
34
34
|
|
|
35
|
-
## PR
|
|
36
|
-
###
|
|
37
|
-
${options.agentResult?.changedFiles.length ?
|
|
35
|
+
## PR 描述草稿
|
|
36
|
+
### 变更内容
|
|
37
|
+
${options.agentResult?.changedFiles.length ? `更新 ${options.agentResult.changedFiles.length} 个文件。` : "未检测到文件变更。"}
|
|
38
38
|
|
|
39
|
-
###
|
|
40
|
-
-
|
|
39
|
+
### 变更原因
|
|
40
|
+
- 见 execution.log 中的 Agent 影响分析、计划和最终报告。
|
|
41
41
|
|
|
42
|
-
###
|
|
43
|
-
${options.verifyResult?.commands.map((cmd) => `- ${cmd.name}: ${cmd.exitCode === 0 ? "
|
|
42
|
+
### 验证
|
|
43
|
+
${options.verifyResult?.commands.map((cmd) => `- ${cmd.name}: ${cmd.exitCode === 0 ? "通过" : "失败"} (${cmd.command})`).join("\n") || "- 未运行"}
|
|
44
44
|
|
|
45
|
-
###
|
|
46
|
-
-
|
|
45
|
+
### 风险
|
|
46
|
+
- 合并前需要评审正确性、安全性、兼容性和知识沉淀建议。
|
|
47
47
|
|
|
48
|
-
###
|
|
49
|
-
-
|
|
48
|
+
### 知识沉淀跟进
|
|
49
|
+
- 写入 Knowledge Graph 前先评审 persist_suggestions.md。
|
|
50
50
|
|
|
51
|
-
## Diff
|
|
51
|
+
## Diff 快照
|
|
52
52
|
\`\`\`diff
|
|
53
53
|
${truncate(diff, 12000)}
|
|
54
54
|
\`\`\`
|
|
55
55
|
`;
|
|
56
|
-
const persist = `#
|
|
56
|
+
const persist = `# 知识沉淀建议
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
写入 Knowledge Graph 前,请先人工评审这些建议。
|
|
59
59
|
|
|
60
|
-
-
|
|
60
|
+
- 任务: ${options.task}
|
|
61
61
|
- Skill: ${options.skillName}
|
|
62
|
-
-
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
62
|
+
- 修改文件: ${options.agentResult?.changedFiles.join(", ") || "无"}
|
|
63
|
+
- 沉淀策略: 写入 Knowledge Graph 前必须人工评审。
|
|
64
|
+
- 建议沉淀类型:
|
|
65
|
+
- ADR Candidate: 如果实现涉及有意义的技术取舍,补充决策记录。
|
|
66
|
+
- Module Note Candidate: 如果变更澄清了模块职责或行为,补充模块说明。
|
|
67
|
+
- API Note Candidate: 如果接口语义、入参、返回值或兼容性有变化,补充接口说明。
|
|
68
|
+
- Runbook Candidate: 如果验证或排障暴露了可复用步骤,补充运行手册。
|
|
69
|
+
- Test Knowledge Candidate: 如果新增了典型测试场景,补充测试知识。
|
|
70
|
+
- Skill Improvement Candidate: 如果本次任务暴露 Skill 缺口,补充 Skill 改进建议。
|
|
71
|
+
- Missing Context Candidate: 如果缺少 Agent 侧 MCP 上下文影响了信心,记录缺失项。
|
|
69
72
|
`;
|
|
70
73
|
await writeFile(options.summaryPath, summary, "utf8");
|
|
71
74
|
await writeFile(options.persistPath, persist, "utf8");
|