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.
Files changed (32) hide show
  1. package/COLLEAGUE_TRIAL_GUIDE.md +147 -0
  2. package/README.md +116 -52
  3. package/dist/agent.js +44 -31
  4. package/dist/cli.js +65 -42
  5. package/dist/config.js +79 -8
  6. package/dist/context.js +53 -21
  7. package/dist/init.js +8 -20
  8. package/dist/integration.js +22 -91
  9. package/dist/mcp.js +29 -112
  10. package/dist/runPaths.js +1 -0
  11. package/dist/summarize.js +42 -39
  12. package/dist/templates.js +39 -215
  13. package/package.json +3 -1
  14. package/scripts/read-evidence.ps1 +9 -3
  15. package/scripts/run-harness.ps1 +8 -5
  16. package/templates/default-project/AGENTS.md +9 -0
  17. package/templates/default-project/SKILLS.md +13 -0
  18. package/templates/default-project/TOOLS.md +8 -0
  19. package/templates/default-project/skills/add_feature/SKILL.md +34 -0
  20. package/templates/default-project/skills/add_feature/context.yaml +30 -0
  21. package/templates/default-project/skills/add_feature/verify.yaml +2 -0
  22. package/templates/default-project/skills/fix_bug/SKILL.md +45 -0
  23. package/templates/default-project/skills/fix_bug/context.yaml +30 -0
  24. package/templates/default-project/skills/fix_bug/verify.yaml +2 -0
  25. package/templates/default-project/skills/update_docs/SKILL.md +28 -0
  26. package/templates/default-project/skills/update_docs/context.yaml +26 -0
  27. package/templates/default-project/skills/update_docs/verify.yaml +2 -0
  28. package/templates/default-project/skills/write_tests/SKILL.md +30 -0
  29. package/templates/default-project/skills/write_tests/context.yaml +29 -0
  30. package/templates/default-project/skills/write_tests/verify.yaml +2 -0
  31. package/templates/integrations/claude/SKILL.md +79 -0
  32. package/templates/integrations/codex/AGENTS_BLOCK.md +51 -0
@@ -1,16 +1,17 @@
1
1
  import path from "node:path";
2
- import { appendFile, copyFile, mkdir, readFile, writeFile } from "node:fs/promises";
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 next = await readFile(source, "utf8");
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 copyFile(source, target);
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
- name: ai-dev-harness
96
- description: Trigger the auditable AI Dev Harness CLI from Claude Code conversations and stream the Harness process in the current chat.
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 `## AI Dev Harness
147
-
148
- Use the AI Dev Harness CLI when the user asks for Harness, an audited run, CodeGraph/Knowledge Graph assisted work, or one of these skills:
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
- import { truncate } from "./utils.js";
3
- async function postJson(url, body, timeoutMs) {
4
- const controller = new AbortController();
5
- const timer = setTimeout(() => controller.abort(), timeoutMs);
6
- try {
7
- const response = await fetch(url, {
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 parseMcpResponse(text, contentType) {
23
- if (contentType.includes("text/event-stream")) {
24
- const dataLines = text
25
- .split(/\r?\n/)
26
- .filter((line) => line.startsWith("data:"))
27
- .map((line) => line.slice(5).trim())
28
- .filter((line) => line && line !== "[DONE]");
29
- return dataLines.map((line) => {
30
- try {
31
- return JSON.parse(line);
32
- }
33
- catch {
34
- return line;
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(task, skill, changedFiles) {
56
- const request = {
57
- type: "codegraph.impact",
58
- task,
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(task, skill, codeContext) {
100
- const request = {
101
- type: "knowledge.search",
102
- task,
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, codeContext.data ?? codeContext.error);
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 = `# Run Summary
5
+ const summary = `# 运行总结
6
6
 
7
- ## Executive Status
8
- - Task status: ${options.agentResult?.status === "success" && options.verifyResult?.status === "passed" ? "ready for review" : "requires attention"}
9
- - Agent status: ${options.agentResult?.status ?? "not-run"}
10
- - Verification status: ${options.verifyResult?.status ?? "not-run"}
7
+ ## 执行状态
8
+ - 任务状态: ${options.agentResult?.status === "success" && options.verifyResult?.status === "passed" ? "可进入评审" : "需要关注"}
9
+ - Agent 状态: ${options.agentResult?.status ?? "未运行"}
10
+ - 验证状态: ${options.verifyResult?.status ?? "未运行"}
11
11
 
12
- ## Task
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
- ## Execution
22
- - Status: ${options.agentResult?.status ?? "not-run"}
23
- - Exit code: ${options.agentResult?.exitCode ?? "n/a"}
24
- - Changed files: ${options.agentResult?.changedFiles.join(", ") || "(none)"}
21
+ ## 执行结果
22
+ - 状态: ${options.agentResult?.status ?? "未运行"}
23
+ - 退出码: ${options.agentResult?.exitCode ?? "n/a"}
24
+ - 修改文件: ${options.agentResult?.changedFiles.join(", ") || ""}
25
25
 
26
- ## Verification
27
- - Status: ${options.verifyResult?.status ?? "not-run"}
26
+ ## 验证结果
27
+ - 状态: ${options.verifyResult?.status ?? "未运行"}
28
28
 
29
- ## Review Checklist
30
- - Confirm the change is limited to the requested task.
31
- - Confirm public interfaces and backward compatibility were preserved.
32
- - Confirm verification failures or skipped checks are understood.
33
- - Confirm Knowledge Graph suggestions are reviewed before persistence.
29
+ ## 评审检查清单
30
+ - 确认变更限定在任务范围内。
31
+ - 确认公共接口和向后兼容性没有被意外破坏。
32
+ - 确认失败或跳过的验证项已被理解。
33
+ - 确认知识沉淀建议经过人工评审后再写入 Knowledge Graph
34
34
 
35
- ## PR Description Draft
36
- ### What changed
37
- ${options.agentResult?.changedFiles.length ? `Updated ${options.agentResult.changedFiles.length} file(s).` : "No file changes detected."}
35
+ ## PR 描述草稿
36
+ ### 变更内容
37
+ ${options.agentResult?.changedFiles.length ? `更新 ${options.agentResult.changedFiles.length} 个文件。` : "未检测到文件变更。"}
38
38
 
39
- ### Why
40
- - See execution.log for the agent's impact analysis, plan, and final report.
39
+ ### 变更原因
40
+ - execution.log 中的 Agent 影响分析、计划和最终报告。
41
41
 
42
- ### Verification
43
- ${options.verifyResult?.commands.map((cmd) => `- ${cmd.name}: ${cmd.exitCode === 0 ? "passed" : "failed"} (${cmd.command})`).join("\n") || "- Not run"}
42
+ ### 验证
43
+ ${options.verifyResult?.commands.map((cmd) => `- ${cmd.name}: ${cmd.exitCode === 0 ? "通过" : "失败"} (${cmd.command})`).join("\n") || "- 未运行"}
44
44
 
45
- ### Risk
46
- - Review required for correctness, safety, compatibility, and knowledge persistence before merging.
45
+ ### 风险
46
+ - 合并前需要评审正确性、安全性、兼容性和知识沉淀建议。
47
47
 
48
- ### Knowledge Follow-up
49
- - Review persist_suggestions.md before updating Knowledge Graph.
48
+ ### 知识沉淀跟进
49
+ - 写入 Knowledge Graph 前先评审 persist_suggestions.md。
50
50
 
51
- ## Diff Snapshot
51
+ ## Diff 快照
52
52
  \`\`\`diff
53
53
  ${truncate(diff, 12000)}
54
54
  \`\`\`
55
55
  `;
56
- const persist = `# Knowledge Persist Suggestions
56
+ const persist = `# 知识沉淀建议
57
57
 
58
- Review these suggestions before updating the Knowledge Graph.
58
+ 写入 Knowledge Graph 前,请先人工评审这些建议。
59
59
 
60
- - Task: ${options.task}
60
+ - 任务: ${options.task}
61
61
  - Skill: ${options.skillName}
62
- - Changed files: ${options.agentResult?.changedFiles.join(", ") || "(none)"}
63
- - Persistence policy: human review required before writing to Knowledge Graph.
64
- - Suggested knowledge updates:
65
- - Add or update module notes if the change clarifies ownership or behavior.
66
- - Add ADR/decision note if the implementation chose between meaningful alternatives.
67
- - Add runbook/testing note if verification uncovered a recurring operational step.
68
- - Record known MCP degradation or missing context if it affected confidence.
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");