ai-dev-harness 0.1.0 → 0.1.2

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 (45) hide show
  1. package/COLLEAGUE_TRIAL_GUIDE.md +175 -0
  2. package/ONLINE_INSTALL_GUIDE.md +458 -0
  3. package/README.md +253 -54
  4. package/dist/agent.js +74 -49
  5. package/dist/approval.js +64 -0
  6. package/dist/audit.js +1 -0
  7. package/dist/checkpoint.js +83 -0
  8. package/dist/cli.js +326 -99
  9. package/dist/config.js +118 -9
  10. package/dist/context.js +62 -21
  11. package/dist/contextScore.js +44 -0
  12. package/dist/diagnostics.js +58 -0
  13. package/dist/doctor.js +100 -0
  14. package/dist/feedback.js +5 -0
  15. package/dist/init.js +8 -20
  16. package/dist/integration.js +84 -93
  17. package/dist/loop.js +116 -0
  18. package/dist/mcp.js +29 -112
  19. package/dist/metrics.js +91 -0
  20. package/dist/replay.js +27 -0
  21. package/dist/runPaths.js +31 -0
  22. package/dist/summarize.js +170 -39
  23. package/dist/templates.js +39 -215
  24. package/package.json +5 -2
  25. package/scripts/read-evidence.ps1 +45 -3
  26. package/scripts/run-harness.ps1 +9 -6
  27. package/templates/default-project/AGENTS.md +9 -0
  28. package/templates/default-project/SKILLS.md +13 -0
  29. package/templates/default-project/TOOLS.md +11 -0
  30. package/templates/default-project/skills/add_feature/SKILL.md +34 -0
  31. package/templates/default-project/skills/add_feature/context.yaml +33 -0
  32. package/templates/default-project/skills/add_feature/verify.yaml +2 -0
  33. package/templates/default-project/skills/fix_bug/SKILL.md +45 -0
  34. package/templates/default-project/skills/fix_bug/context.yaml +33 -0
  35. package/templates/default-project/skills/fix_bug/verify.yaml +2 -0
  36. package/templates/default-project/skills/update_docs/SKILL.md +28 -0
  37. package/templates/default-project/skills/update_docs/context.yaml +30 -0
  38. package/templates/default-project/skills/update_docs/verify.yaml +2 -0
  39. package/templates/default-project/skills/write_tests/SKILL.md +30 -0
  40. package/templates/default-project/skills/write_tests/context.yaml +32 -0
  41. package/templates/default-project/skills/write_tests/verify.yaml +2 -0
  42. package/templates/integrations/claude/SKILL.md +115 -0
  43. package/templates/integrations/codex/AGENTS_BLOCK.md +82 -0
  44. package/templates/integrations/cursor/ai-dev-harness.mdc +55 -0
  45. package/templates/integrations/gemini/GEMINI_BLOCK.md +46 -0
package/README.md CHANGED
@@ -1,82 +1,143 @@
1
1
  # AI Dev Harness
2
2
 
3
- Auditable TypeScript CLI harness that wraps Codex CLI or Claude Code, retrieves context from CodeGraph and Knowledge Graph MCP services, verifies changes, and writes local run evidence.
3
+ AI Dev Harness 是一个可分发的 TypeScript CLI,用来把 CodexClaude Code、Cursor Agent、Gemini CLI 的研发任务变成可审计的执行闭环。
4
4
 
5
- This repository is a distributable Harness package:
5
+ 它的核心定位不是替代 Agent,也不是实现 MCP Client,而是:
6
6
 
7
- - CLI: `dist/cli.js`
8
- - Claude/Codex conversation integration installer
9
- - Claude Skill package installed into `.claude/skills/ai-dev-harness/`
10
- - Agent helper scripts installed into `.harness/ai-dev-harness/scripts/`
11
- - Git-native project skills installed into `skills/`
7
+ > 让大模型知道如何组装上下文、制定计划、执行任务、验证结果,并把整个生命周期记录下来。
12
8
 
13
- ## Quick Start
9
+ ## 核心边界
10
+
11
+ - Codex / Claude / Cursor / Gemini 负责:理解任务、调用 MCP、调用工具、使用 Skill、修改代码。
12
+ - Harness 负责:执行闭环、过程日志、验证命令、审计证据、总结、沉淀建议。
13
+ - CodeGraph / Knowledge Graph 是 Agent 侧 MCP 工具源,不是 Harness 内部服务依赖。
14
+ - Harness 会记录 MCP source metadata,但不会记录 bearer token 原文。
15
+
16
+ ## 包内容
17
+
18
+ - CLI:`dist/cli.js`
19
+ - Claude/Codex/Cursor/Gemini 对话集成安装器
20
+ - Claude Skill:`.claude/skills/ai-dev-harness/`
21
+ - Agent 辅助脚本:`.harness/ai-dev-harness/scripts/`
22
+ - Git-native 项目技能:`skills/`
23
+ - 可编辑模板:`templates/`
24
+
25
+ 线上安装使用手册见 `ONLINE_INSTALL_GUIDE.md`。
26
+ 同事试用手册见 `COLLEAGUE_TRIAL_GUIDE.md`。
27
+
28
+ ## 快速开始
14
29
 
15
30
  ```bash
16
31
  npm install
17
32
  npm run build
18
33
  node dist/cli.js init
19
- node dist/cli.js run --task "fix a small bug" --skill fix_bug --agent codex
34
+ node dist/cli.js doctor
35
+ node dist/cli.js smoke --agent codex --live
20
36
  ```
21
37
 
22
- The CLI is intended to be installed inside a business Git repository. `init` creates `harness.yaml`, `skills/`, `AGENTS.md`, `TOOLS.md`, `SKILLS.md`, and `runs/`.
23
- `harness run` must execute inside a Git repository; if it is launched elsewhere, the harness writes intake/audit evidence and exits nonzero.
38
+ CLI 应在业务 Git 仓库内初始化。`init` 会创建 `harness.yaml`、`skills/`、`AGENTS.md`、`TOOLS.md`、`SKILLS.md` `runs/`。
24
39
 
25
- ## Commands
40
+ ## 命令
26
41
 
27
42
  ```bash
28
43
  node dist/cli.js init
44
+ node dist/cli.js init --no-integration
45
+ node dist/cli.js doctor
46
+ node dist/cli.js smoke --agent claude --live
29
47
  node dist/cli.js run --task "add refund flow tests" --skill write_tests --agent claude
30
48
  node dist/cli.js run --task "analyze download startup flow. Do not modify repository files." --skill update_docs --agent claude --live
49
+ node dist/cli.js run --task "fix a small bug" --skill fix_bug --agent cursor --live
50
+ node dist/cli.js run --task "fix a small bug" --skill fix_bug --agent gemini --live
31
51
  node dist/cli.js verify --run <run_id_or_path>
32
52
  node dist/cli.js summarize --run <run_id_or_path>
33
- node dist/cli.js install-integration --agent both --target <repo>
53
+ node dist/cli.js restore --run <run_id_or_path> --iteration 1
54
+ node dist/cli.js runs metrics --json
55
+ node dist/cli.js replay --run <run_id_or_path> --dry
56
+ node dist/cli.js replay --run <run_id_or_path> --agent codex
57
+ node dist/cli.js install-integration --agent all --target <repo>
34
58
  ```
35
59
 
36
- ## Claude/Codex Conversation Trigger
60
+ ## Claude/Codex/Cursor/Gemini 对话触发
37
61
 
38
- Install conversation integration into a business repository:
62
+ `harness init` 默认安装对话集成。也可以手动刷新:
39
63
 
40
64
  ```bash
41
- node "E:\ai native\dist\cli.js" install-integration --agent both --target "C:\path\to\repo"
65
+ node "E:\ai native\dist\cli.js" install-integration --agent all --target "C:\path\to\repo"
42
66
  ```
43
67
 
44
- This creates:
68
+ 它会创建:
45
69
 
46
- - `.claude/skills/ai-dev-harness/SKILL.md` for Claude Code.
47
- - An `AGENTS.md` Harness section for Codex.
70
+ - `.claude/skills/ai-dev-harness/SKILL.md`
71
+ - `AGENTS.md` 中的 Harness 规则块
72
+ - `.cursor/rules/ai-dev-harness.mdc`
73
+ - `GEMINI.md` 中的 Harness 规则块
48
74
  - `.harness/ai-dev-harness/scripts/run-harness.ps1`
49
75
  - `.harness/ai-dev-harness/scripts/read-evidence.ps1`
50
76
 
51
- Then, inside Claude Code or Codex, ask naturally:
77
+ 然后在 Claude Code、Codex、Cursor Gemini 中自然提问:
52
78
 
53
79
  ```text
54
80
  Use AI Dev Harness to run update_docs: analyze the download startup flow. Do not modify repository files.
55
81
  ```
56
82
 
57
- or:
83
+ 或:
58
84
 
59
85
  ```text
60
86
  Use Harness to run fix_bug: fix the download resume progress issue.
61
87
  ```
62
88
 
63
- The generated integration uses `--live`, so the current conversation terminal can see Harness stages and agent output as the run progresses.
89
+ 集成脚本默认使用 `--live`,所以当前对话终端能看到 Harness 阶段日志和 Agent 输出。
64
90
 
65
- The installed Skill prefers the helper script:
91
+ ## MCP Preflight
66
92
 
67
- ```bash
68
- powershell -ExecutionPolicy Bypass -File ".harness/ai-dev-harness/scripts/run-harness.ps1" -Task "<task>" -Skill fix_bug -Agent claude -Live
69
- ```
93
+ Harness 不直接调用 MCP。Agent 应在触发 Harness 前使用自己已配置的 MCP 工具,并按以下格式汇报:
70
94
 
71
- The direct CLI command remains available as a fallback:
95
+ 如果 CodeGraph 使用的是 `colbymchenry/codegraph`,默认优先调用 `codegraph_explore`。用任务里的业务流程、文件路径、函数/类/符号名或错误现象作为 query,并重点读取它返回的 grouped source、line-numbered source、call paths、dynamic dispatch hops 和 blast-radius summary。
72
96
 
73
- ```bash
74
- node "E:\ai native\dist\cli.js" run --task "<task>" --skill fix_bug --agent claude --live
97
+ 如果 Knowledge Graph 使用的是 `browser_knowledge_service`,推荐读取顺序:
98
+
99
+ 1. 用 `list_path`、`search_docs`、`search_sections`、`search_chunks` 缩小范围。
100
+ 2. RAG 场景优先用 `answer_context`;需要自控排序时用 `hybrid_search`。
101
+ 3. 定位 concept_id 后先用 `read_doc_outline` 看结构。
102
+ 4. 再用 `read_section`、`read_doc_range`、`read_chunk`、`get_related_chunks` 精读证据。
103
+ 5. 需要关系时用 `get_backlinks`,需要近期变更记录时用 `get_audit_log`。
104
+ 6. v1 不直接调用 `propose_doc` 写入知识库;只生成 `persist_suggestions.md`。
105
+
106
+ ```text
107
+ ## MCP Preflight Findings
108
+ - CodeGraph tools used:
109
+ - CodeGraph primary query:
110
+ - Source grouped by file:
111
+ - Current line-numbered source:
112
+ - Call paths:
113
+ - Dynamic dispatch hops:
114
+ - Blast-radius summary:
115
+ - Knowledge Graph tools used:
116
+ - Candidate concept_ids:
117
+ - Read tools used:
118
+ - Evidence sections/chunks:
119
+ - ADRs / decisions:
120
+ - Module notes:
121
+ - API notes / runbooks:
122
+ - Audit/backlink findings:
123
+ - Missing context:
75
124
  ```
76
125
 
77
- ## Configuration
126
+ 关键 MCP 发现应被带入 Harness 的任务文本,进入审计证据。
127
+
128
+ ## OKF 知识沉淀
129
+
130
+ `persist_suggestions.md` 按 PC 浏览器团队知识库规范生成 OKF 候选内容,不自动写入 Knowledge Graph。
131
+
132
+ - 候选文档路径应落到 `/<project>/overview/`、`/<project>/features/`、`/<project>/metrics/`、`/<project>/api/`、`/<project>/playbooks/` 或 `/<project>/references/`。
133
+ - 每个候选 concept 应包含 `type`、`title`、`description`、`tags`、`timestamp`;API/数据类建议补充 `resource`。
134
+ - `## 相关` 必须使用有效 Markdown 链接,推荐 bundle 根相对路径。
135
+ - `type: API Reference` 需要包含 `## 关键实现锚点` 和 `### Agent 使用建议`。
136
+ - 合入知识库前应同步更新对应目录的 `index.md` 和 `log.md`,并人工检查敏感信息。
78
137
 
79
- Edit `harness.yaml` in the business repository:
138
+ ## 配置
139
+
140
+ 编辑业务仓库里的 `harness.yaml`:
80
141
 
81
142
  ```yaml
82
143
  agents:
@@ -84,11 +145,20 @@ agents:
84
145
  command: codex exec --full-auto -
85
146
  claude:
86
147
  command: claude -p -
148
+ cursor:
149
+ command: cursor-agent
150
+ gemini:
151
+ command: gemini
87
152
  mcp:
88
153
  codegraph:
154
+ type: sse
89
155
  url: http://localhost:7331/mcp
156
+ headers: {}
90
157
  knowledgeGraph:
158
+ type: http
91
159
  url: http://localhost:7332/mcp
160
+ headers:
161
+ Authorization: Bearer ${OKF_TOKEN}
92
162
  timeoutMs: 15000
93
163
  verify:
94
164
  defaultCommands:
@@ -96,36 +166,165 @@ verify:
96
166
  command: git status --short
97
167
  audit:
98
168
  path: runs
169
+ loop:
170
+ maxIterations: 1
171
+ stopOn:
172
+ - same_failure_repeated
173
+ - max_iterations_reached
174
+ policy:
175
+ protectedPaths:
176
+ - .github/
177
+ - scripts/release/
178
+ - security/
179
+ requireApproval:
180
+ - protected_path_change
181
+ - dependency_change
182
+ - delete_files
183
+ - public_api_change_declared
184
+ - security_change_declared
185
+ approvalMode: file
186
+ ```
187
+
188
+ MCP header 支持环境变量,例如 `${OKF_TOKEN}`。不要把真实 token 提交到 Git。
189
+
190
+ `agents.*.command` 都可以按团队机器上的实际 CLI 改写。Harness 统一通过 stdin 把最终 prompt 传给执行器,不绑定 Codex、Claude、Cursor 或 Gemini 的内部能力。
191
+
192
+ ## 自纠偏循环
193
+
194
+ Harness 会在每轮执行后进入 `Evaluate` 阶段,判断是否完成目标、Agent 契约是否满足、验证是否通过。
195
+
196
+ 默认 `loop.maxIterations: 1`,行为等同单轮执行。需要开启低风险自动重做时,在业务仓库 `harness.yaml` 中调大:
197
+
198
+ ```yaml
199
+ loop:
200
+ maxIterations: 2
201
+ stopOn:
202
+ - same_failure_repeated
203
+ - max_iterations_reached
204
+ ```
205
+
206
+ 会自动进入下一轮的典型情况:
207
+
208
+ - Agent 命令失败。
209
+ - Agent 输出缺少 `Impact Analysis / Plan / Execution Notes / Verification Notes / Final Report`。
210
+ - 验证命令失败。
211
+
212
+ 会停止并要求人工评审的典型情况:
213
+
214
+ - 连续两轮出现同一种失败。
215
+ - 达到最大循环轮次。
216
+ - Agent 在报告里说明目标不明确、需要扩大范围、要改公共接口或安全策略。
217
+
218
+ ## 运行证据
219
+
220
+ 每次 `run` 会创建 `runs/<run_id>/`:
221
+
222
+ - `intake.json`:任务、skill、agent、Git 状态。
223
+ - `mcp_queries.jsonl`:Agent-driven MCP source metadata,不包含 token 原文。
224
+ - `context_pack.md`:上下文组装协议、MCP source 记录、本地兜底上下文。
225
+ - `agent_prompt.md`:发送给当前执行器的最终提示词。
226
+ - `plan.md`:Harness 执行阶段协议。
227
+ - `execution.log`:Agent stdout/stderr。
228
+ - `agent_contract.json`:Agent 必需章节检查结果。
229
+ - `git_diff.patch`:代码变更。
230
+ - `verify.json`:验证结果。
231
+ - `verify_diagnostics.json`:从验证 stdout/stderr 提取的通用失败诊断。
232
+ - `context_score.json`:Agent 是否按协议提供上下文证据的结构化评分。
233
+ - `approval_required.json`:机械审批门禁结果。
234
+ - `feedback.jsonl`:Harness 可见机器反馈事件,不记录 Agent 内部工具调用。
235
+ - `checkpoint.json`:本轮 Git checkpoint 和 restore 支持状态。
236
+ - `pre_iteration_diff.patch` / `post_iteration_diff.patch`:本轮前后 diff。
237
+ - `loop_review.json`:每轮评估、失败归因和下一步动作。
238
+ - `loop_report.md`:自纠偏循环的人类可读报告。
239
+ - `summary.md`:评审/PR 总结。
240
+ - `persist_suggestions.md`:知识沉淀建议。
241
+ - `audit.jsonl`:完整审计事件流。
242
+
243
+ Harness 会检查 Agent 输出是否包含以下章节:
244
+
245
+ - `Impact Analysis`
246
+ - `Plan`
247
+ - `Execution Notes`
248
+ - `Verification Notes`
249
+ - `Final Report`
250
+
251
+ 检查结果写入 `agent_contract.json`,并记录到 `audit.jsonl`。
252
+
253
+ 每轮循环会额外生成 `iteration-<n>/` 子目录。根目录同名文件是最后一轮快照,方便旧脚本继续读取。
254
+
255
+ ## Checkpoint / Restore
256
+
257
+ Harness 每轮开始记录 checkpoint。只有运行开始前工作区是 clean,才允许自动 restore:
258
+
259
+ ```bash
260
+ node dist/cli.js restore --run <run_id_or_path> --iteration 1
261
+ ```
262
+
263
+ 如果运行前已有未提交改动,Harness 会拒绝自动恢复,避免覆盖用户手工改动。
264
+
265
+ ## Approval Gate
266
+
267
+ Harness 只做机械门禁,不判断业务方案。默认会在以下情况写入 `approval_required.json` 并停止:
268
+
269
+ - 命中 `policy.protectedPaths`。
270
+ - 修改依赖文件,例如 `package.json`、lockfile。
271
+ - 删除文件。
272
+ - Agent 最终报告声明 `public_api_change_declared` 或 `security_change_declared`。
273
+
274
+ ## Metrics / Replay
275
+
276
+ 读取本地 `runs/` 证据生成运行指标:
277
+
278
+ ```bash
279
+ node dist/cli.js runs metrics
280
+ node dist/cli.js runs metrics --json
99
281
  ```
100
282
 
101
- The agent command receives the generated prompt on stdin. CodeGraph and Knowledge Graph MCP are queried first; if either service is unavailable, the run is marked degraded and the harness falls back to local Git/file context.
102
- If the agent command or verification fails, the run still writes all evidence files and then exits nonzero so CI can treat the run as failed.
283
+ Replay 复用历史 run task skill,创建新的 run,不覆盖旧证据:
103
284
 
104
- ## Run Evidence
285
+ ```bash
286
+ node dist/cli.js replay --run <run_id_or_path> --dry
287
+ node dist/cli.js replay --run <run_id_or_path> --agent claude
288
+ ```
105
289
 
106
- Each `run` creates `runs/<run_id>/` with:
290
+ ## Skill 规范
107
291
 
108
- - `intake.json`
109
- - `mcp_queries.jsonl`
110
- - `context_pack.md`
111
- - `agent_prompt.md`
112
- - `plan.md`
113
- - `execution.log`
114
- - `git_diff.patch`
115
- - `verify.json`
116
- - `summary.md`
117
- - `persist_suggestions.md`
118
- - `audit.jsonl`
292
+ 内置 skill 会通过 `context.yaml` 告诉 Agent:
119
293
 
120
- Knowledge Graph updates are not applied automatically in v1. Review `persist_suggestions.md` before writing them back.
294
+ - 应该从 CodeGraph MCP 查询什么。
295
+ - 应该从 Knowledge Graph MCP 查询什么。
296
+ - 什么情况下必须停止,不要猜。
297
+ - 本地兜底上下文应包含哪些路径。
121
298
 
122
- ## Engineering Prompt Contracts
299
+ 示例:
123
300
 
124
- Generated skills and agent prompts use a company-grade execution contract:
301
+ ```yaml
302
+ mcp_preflight:
303
+ codegraph:
304
+ required: true
305
+ primary_tool: codegraph_explore
306
+ ask_for:
307
+ - relevant_source_grouped_by_file
308
+ - current_line_numbered_source_for_known_file_or_symbol
309
+ - call_paths_between_related_symbols
310
+ - dynamic_dispatch_hops
311
+ - blast_radius_summary
312
+ knowledgeGraph:
313
+ required: true
314
+ ask_for:
315
+ - search_docs_or_search_chunks_for_defect_terms
316
+ - answer_context_for_direct_rag_context
317
+ - read_doc_outline_for_relevant_concepts
318
+ - read_section_or_read_doc_range_for_evidence
319
+ - get_backlinks_for_related_decisions
320
+ stop_conditions:
321
+ - root cause is unclear
322
+ - impact scope cannot be determined
323
+ ```
125
324
 
126
- - Analysis Contract: restate the task, list assumptions, affected areas, risks, and missing context.
127
- - Execution Contract: make only task-relevant minimal changes; no opportunistic refactors or broad cleanup.
128
- - Verification Contract: run configured verification or record why checks could not run.
129
- - Report Contract: summarize intent or root cause, changed files, verification, residual risk, and knowledge suggestions.
325
+ ## 验证
130
326
 
131
- The test suite verifies that initialized skills and run evidence include these contracts.
327
+ ```bash
328
+ npm test
329
+ npm pack --dry-run
330
+ ```
package/dist/agent.js CHANGED
@@ -1,54 +1,90 @@
1
1
  import { appendFile, readFile, writeFile } from "node:fs/promises";
2
2
  import { git, runCommand, truncate } from "./utils.js";
3
3
  function renderPrompt(input, contextPack) {
4
- return `You are running inside an auditable AI development harness. Treat this as a company engineering task, not a casual chat.
4
+ return `你正在一个可审计的 AI 研发 Harness 中执行任务。请把它当作公司级研发任务,而不是随意聊天。
5
5
 
6
- Task:
6
+ 当前循环轮次:
7
+ ${input.iteration ?? 1}
8
+
9
+ 任务:
7
10
  ${input.task}
8
11
 
9
12
  Skill:
10
13
  ${input.skill.name}
11
14
 
12
- Execution Contract:
13
- 1. Analysis first.
14
- - Restate the task in one or two sentences.
15
- - Identify assumptions, affected files/modules, relevant CodeGraph context, relevant Knowledge Graph context, and risks.
16
- - If required context is missing or confidence is low, stop and report what is missing instead of guessing.
17
- 2. Plan before editing.
18
- - Provide a minimal step-by-step plan.
19
- - Keep the plan limited to the requested task.
20
- 3. Safe execution.
21
- - Make the smallest task-relevant change.
22
- - Do not perform opportunistic refactors, formatting-only sweeps, dependency upgrades, broad renames, or unrelated cleanup.
23
- - Preserve public interfaces and backward compatibility unless explicitly instructed otherwise.
24
- - Do not update Knowledge Graph directly.
25
- 4. Verification discipline.
26
- - Harness will run configured verification after you finish.
27
- - Only run extra commands when needed to understand or validate the task.
28
- - Report any verification you could not run and why.
29
- 5. Required final report.
30
- - Root cause or implementation intent.
31
- - Files changed.
32
- - Verification performed or deferred.
33
- - Residual risk.
34
- - Knowledge persistence suggestions.
15
+ 执行契约:
16
+ 1. 先分析。
17
+ - 用一两句话复述任务。
18
+ - 识别假设、受影响文件/模块、CodeGraph 上下文、Knowledge Graph 上下文和风险。
19
+ - 如果关键上下文缺失或信心不足,停止并说明缺什么,不要猜。
20
+ 2. 先计划再修改。
21
+ - 给出最小步骤计划。
22
+ - 计划必须限定在当前任务范围内。
23
+ 3. 安全执行。
24
+ - 只做和任务相关的最小变更。
25
+ - 不做机会主义重构、纯格式化清扫、依赖升级、大范围重命名或无关清理。
26
+ - 除非任务明确要求,否则保持公共接口和向后兼容。
27
+ - 不直接写入 Knowledge Graph
28
+ 4. 验证纪律。
29
+ - Harness 会在你完成后运行配置的验证命令。
30
+ - 只有在理解或验证任务确实需要时才额外运行命令。
31
+ - 说明无法执行的验证及原因。
32
+ 5. 必须输出最终报告。
33
+ - 根因或实现意图。
34
+ - 修改文件。
35
+ - 已执行或延后的验证。
36
+ - 残余风险。
37
+ - 知识沉淀建议。建议必须遵循 OKF v0.1 扩展:说明建议写入的项目目录、子域、type、title、description、相关 concept 链接、index.md/log.md 更新项;API 文档还要说明关键实现锚点和 Agent 使用建议。
38
+
39
+ 自纠偏规则:
40
+ - 如果这是第 2 轮或更高轮次,先阅读上一轮失败诊断,只围绕失败原因做最小 rework。
41
+ - 不要因为验证失败就绕过验证或删除验证。
42
+ - 如果需要扩大范围、改公共接口、修改安全策略、或目标本身不明确,请停止并在 Final Report 中说明需要人工确认。
35
43
 
36
- Output Format:
44
+ ${input.previousFeedback ? `上一轮反馈:\n${input.previousFeedback}\n` : ""}
45
+
46
+ 必须使用以下输出格式,标题不要改名:
37
47
  ## Impact Analysis
38
48
  ## Plan
39
49
  ## Execution Notes
40
50
  ## Verification Notes
41
51
  ## Final Report
42
52
 
43
- Project Rules:
44
- - Respect AGENTS.md, TOOLS.md, and SKILLS.md when present.
45
- - Work inside this repository only.
46
- - Leave an auditable, reviewable result.
53
+ 项目规则:
54
+ - 遵守 AGENTS.mdTOOLS.md SKILLS.md
55
+ - 只在当前仓库内工作。
56
+ - 留下可审计、可复查的结果。
47
57
 
48
- Context pack:
58
+ Context Pack:
49
59
  ${contextPack}
50
60
  `;
51
61
  }
62
+ export function checkAgentContract(output) {
63
+ const required = ["Impact Analysis", "Plan", "Execution Notes", "Verification Notes", "Final Report"];
64
+ const requiredSections = required.map((name) => ({
65
+ name,
66
+ present: new RegExp(`^##\\s+${name}\\s*$`, "im").test(output)
67
+ }));
68
+ const missingSections = requiredSections.filter((section) => !section.present).map((section) => section.name);
69
+ return {
70
+ status: missingSections.length ? "failed" : "passed",
71
+ requiredSections,
72
+ missingSections
73
+ };
74
+ }
75
+ function parseChangedFiles(status) {
76
+ return status
77
+ .split(/\r?\n/)
78
+ .map((line) => line.trim())
79
+ .filter(Boolean)
80
+ .map((line) => {
81
+ const rename = line.match(/^R\s+(.+)\s+->\s+(.+)$/);
82
+ if (rename)
83
+ return rename[2];
84
+ return line.slice(3).trim();
85
+ })
86
+ .filter(Boolean);
87
+ }
52
88
  async function executeAgent(input) {
53
89
  const contextPack = await readFile(input.contextPackPath, "utf8");
54
90
  const prompt = renderPrompt(input, contextPack);
@@ -60,9 +96,9 @@ async function executeAgent(input) {
60
96
  stderrPrefix: `[${input.agent}:stderr] `
61
97
  });
62
98
  await appendFile(input.executionLogPath, `## STDOUT\n${result.stdout}\n\n## STDERR\n${result.stderr}\n`, "utf8");
63
- const changedFilesRaw = await git(input.cwd, "diff --name-only");
99
+ const changedFilesRaw = await git(input.cwd, "status --porcelain");
64
100
  const diff = await git(input.cwd, "diff --no-ext-diff");
65
- const changedFiles = changedFilesRaw ? changedFilesRaw.split(/\r?\n/).filter(Boolean) : [];
101
+ const changedFiles = changedFilesRaw ? parseChangedFiles(changedFilesRaw) : [];
66
102
  return {
67
103
  status: result.exitCode === 0 ? "success" : "failed",
68
104
  exitCode: result.exitCode,
@@ -73,20 +109,11 @@ async function executeAgent(input) {
73
109
  error: result.exitCode === 0 ? undefined : `Agent command failed with exit code ${result.exitCode}`
74
110
  };
75
111
  }
76
- export class CodexAdapter {
77
- command;
78
- name = "codex";
79
- constructor(command) {
80
- this.command = command;
81
- }
82
- run(input) {
83
- return executeAgent({ ...input, agent: this.name, command: this.command });
84
- }
85
- }
86
- export class ClaudeAdapter {
112
+ export class CliAgentAdapter {
113
+ name;
87
114
  command;
88
- name = "claude";
89
- constructor(command) {
115
+ constructor(name, command) {
116
+ this.name = name;
90
117
  this.command = command;
91
118
  }
92
119
  run(input) {
@@ -94,7 +121,5 @@ export class ClaudeAdapter {
94
121
  }
95
122
  }
96
123
  export function createAgentAdapter(agent, config) {
97
- if (agent === "codex")
98
- return new CodexAdapter(config.agents.codex.command);
99
- return new ClaudeAdapter(config.agents.claude.command);
124
+ return new CliAgentAdapter(agent, config.agents[agent].command);
100
125
  }
@@ -0,0 +1,64 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { git } from "./utils.js";
4
+ function normalizeRel(file) {
5
+ return file.replace(/\\/g, "/").replace(/^\.\//, "");
6
+ }
7
+ function matchesProtected(file, protectedPath) {
8
+ const normalized = normalizeRel(file);
9
+ const protectedNormalized = normalizeRel(protectedPath).replace(/\/?$/, "/");
10
+ return normalized === protectedNormalized.slice(0, -1) || normalized.startsWith(protectedNormalized);
11
+ }
12
+ function dependencyFile(file) {
13
+ const base = path.basename(file).toLowerCase();
14
+ return ["package.json", "package-lock.json", "yarn.lock", "pnpm-lock.yaml", "requirements.txt", "poetry.lock", "go.mod", "go.sum", "cargo.toml", "cargo.lock"].includes(base);
15
+ }
16
+ function declaredRisk(output, marker) {
17
+ return new RegExp(marker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i").test(output);
18
+ }
19
+ function deletedFilesFromNameStatus(nameStatus) {
20
+ return nameStatus
21
+ .split(/\r?\n/)
22
+ .filter((line) => line.startsWith("D\t"))
23
+ .map((line) => normalizeRel(line.slice(2)));
24
+ }
25
+ export async function evaluateApprovalGate(options) {
26
+ const changedFiles = options.agentResult.changedFiles.map(normalizeRel);
27
+ const unstagedNameStatus = await git(options.cwd, "diff --name-status");
28
+ const stagedNameStatus = await git(options.cwd, "diff --cached --name-status");
29
+ const deleted = Array.from(new Set([...deletedFilesFromNameStatus(unstagedNameStatus), ...deletedFilesFromNameStatus(stagedNameStatus)]));
30
+ const reasons = [];
31
+ const required = new Set(options.config.policy.requireApproval);
32
+ if (required.has("protected_path_change")) {
33
+ for (const file of changedFiles) {
34
+ const matched = options.config.policy.protectedPaths.find((protectedPath) => matchesProtected(file, protectedPath));
35
+ if (matched)
36
+ reasons.push({ type: "protected_path_change", detail: `${file} matches ${matched}` });
37
+ }
38
+ }
39
+ if (required.has("dependency_change")) {
40
+ for (const file of changedFiles.filter(dependencyFile))
41
+ reasons.push({ type: "dependency_change", detail: file });
42
+ }
43
+ if (required.has("delete_files")) {
44
+ for (const file of deleted)
45
+ reasons.push({ type: "delete_files", detail: file });
46
+ }
47
+ if (required.has("public_api_change_declared") && declaredRisk(options.agentOutput, "public_api_change_declared")) {
48
+ reasons.push({ type: "public_api_change_declared", detail: "Agent declared public API change." });
49
+ }
50
+ if (required.has("security_change_declared") && declaredRisk(options.agentOutput, "security_change_declared")) {
51
+ reasons.push({ type: "security_change_declared", detail: "Agent declared security change." });
52
+ }
53
+ const approval = {
54
+ required: reasons.length > 0,
55
+ reasons,
56
+ changedFiles,
57
+ mode: "file",
58
+ message: reasons.length
59
+ ? "Harness 已停止:本次变更触发审批门禁,请人工评审 approval_required.json。"
60
+ : "未触发审批门禁。"
61
+ };
62
+ await writeFile(options.approvalPath, `${JSON.stringify(approval, null, 2)}\n`, "utf8");
63
+ return approval;
64
+ }
package/dist/audit.js CHANGED
@@ -17,5 +17,6 @@ export async function prepareRunFiles(paths) {
17
17
  await ensureDir(paths.runDir);
18
18
  await writeJson(paths.intake, {});
19
19
  await writeFile(paths.mcpQueries, "", "utf8");
20
+ await writeFile(paths.feedback, "", "utf8");
20
21
  await writeFile(paths.audit, "", "utf8");
21
22
  }