ai-dev-harness 0.1.0

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.
@@ -0,0 +1,217 @@
1
+ import { defaultConfigYaml } from "./config.js";
2
+ export const rootTemplates = {
3
+ "harness.yaml": defaultConfigYaml(),
4
+ "AGENTS.md": `# Project Agent Rules
5
+
6
+ - Work inside this Git repository only.
7
+ - Prefer small, reviewable changes.
8
+ - Do not modify generated or dependency directories.
9
+ - Always explain impact, risk, verification, and follow-up knowledge updates.
10
+ - Before changing code, state assumptions, affected areas, and a minimal plan.
11
+ - If required context is missing or confidence is low, stop and report what is missing instead of guessing.
12
+ - Do not perform broad refactors, formatting-only sweeps, dependency upgrades, or unrelated cleanup unless the task explicitly asks for them.
13
+ `,
14
+ "TOOLS.md": `# Tool Rules
15
+
16
+ - Codex and Claude Code are execution agents.
17
+ - CodeGraph MCP provides code impact context.
18
+ - Knowledge Graph MCP provides ADR, module, API, runbook, and decision context.
19
+ - Verification commands are defined in harness.yaml and skill verify.yaml files.
20
+ - Harness run evidence is stored in runs/<run_id>/ and must be treated as the audit record.
21
+ - Knowledge Graph is read-only in v1. Write suggestions to persist_suggestions.md only.
22
+ `,
23
+ "SKILLS.md": `# Skill Index
24
+
25
+ - fix_bug: diagnose and fix a defect with tests when possible.
26
+ - add_feature: implement a scoped feature with verification.
27
+ - write_tests: add or improve tests for existing behavior.
28
+ - update_docs: update project documentation and knowledge suggestions.
29
+
30
+ Every skill follows the same execution contract:
31
+
32
+ 1. Analysis Contract: identify assumptions, affected files/modules, likely risks, and the smallest viable plan.
33
+ 2. Execution Contract: make only task-relevant changes and avoid opportunistic refactors.
34
+ 3. Verification Contract: run or defer verification with explicit reasons.
35
+ 4. Report Contract: explain root cause or intent, changed files, verification, residual risk, and knowledge suggestions.
36
+ `
37
+ };
38
+ const baseContext = `queries:
39
+ - related files
40
+ - symbols
41
+ - call graph
42
+ - dependencies
43
+ - impact analysis
44
+ fallbackInclude:
45
+ - README.md
46
+ - src
47
+ `;
48
+ const defaultVerify = `- name: git status
49
+ command: git status --short
50
+ `;
51
+ export const skillTemplates = {
52
+ fix_bug: {
53
+ "SKILL.md": `# fix_bug
54
+
55
+ ## Goal
56
+ Diagnose and fix a defect with the smallest safe code change.
57
+
58
+ ## Inputs
59
+ - Task text or issue description.
60
+ - CodeGraph impact context.
61
+ - Knowledge Graph related decisions, module notes, APIs, and runbooks.
62
+
63
+ ## Outputs
64
+ - Code change.
65
+ - Test or verification result.
66
+ - Summary with root cause, impact, risk, and follow-up suggestions.
67
+
68
+ ## Analysis Contract
69
+ - Restate the observed defect and expected behavior.
70
+ - Identify the likely root cause and the code path involved.
71
+ - List assumptions and confidence level before editing.
72
+ - Use CodeGraph impact context to avoid missing callers or dependent modules.
73
+ - Use Knowledge Graph decisions and runbooks to avoid violating historical constraints.
74
+
75
+ ## Execution Contract
76
+ - Make the smallest task-relevant change.
77
+ - Prefer adding or updating a regression test when feasible.
78
+ - Do not rewrite unrelated code, rename public APIs, change formatting broadly, or upgrade dependencies.
79
+ - If root cause is unclear after inspection, stop and report missing evidence instead of guessing.
80
+
81
+ ## Verification Contract
82
+ - Run the configured verification commands through Harness.
83
+ - If manual or targeted verification is needed, describe the exact command or scenario.
84
+ - Record any verification that could not be run and why.
85
+
86
+ ## Report Contract
87
+ - Root cause.
88
+ - Files changed.
89
+ - Why the fix is safe.
90
+ - Verification result.
91
+ - Residual risk.
92
+ - Knowledge or runbook update suggestions.
93
+
94
+ ## Acceptance
95
+ - The defect is explained.
96
+ - The change is limited to the affected area.
97
+ - Relevant verification commands pass or failures are recorded.
98
+ `,
99
+ "verify.yaml": defaultVerify,
100
+ "context.yaml": baseContext
101
+ },
102
+ add_feature: {
103
+ "SKILL.md": `# add_feature
104
+
105
+ ## Goal
106
+ Implement a scoped feature using existing project patterns.
107
+
108
+ ## Analysis Contract
109
+ - Restate the feature intent and non-goals.
110
+ - Identify affected modules, public interfaces, data flows, and compatibility risks.
111
+ - Check CodeGraph context for callers, dependencies, and likely integration points.
112
+ - Check Knowledge Graph context for ADRs, API contracts, and previous decisions.
113
+
114
+ ## Execution Contract
115
+ - Implement the smallest coherent slice that satisfies the task.
116
+ - Follow existing patterns before introducing new abstractions.
117
+ - Do not change unrelated behavior or broaden scope.
118
+ - Preserve backward compatibility unless the task explicitly allows breaking changes.
119
+
120
+ ## Verification Contract
121
+ - Add or update tests when behavior changes.
122
+ - Run configured verification commands through Harness.
123
+ - Record unverified scenarios and reasons.
124
+
125
+ ## Report Contract
126
+ - Feature behavior implemented.
127
+ - Files changed.
128
+ - Compatibility notes.
129
+ - Verification result.
130
+ - Residual risk.
131
+ - Documentation or knowledge suggestions.
132
+
133
+ ## Outputs
134
+ - Code change.
135
+ - Tests or verification.
136
+ - Documentation and knowledge suggestions when behavior changes.
137
+ `,
138
+ "verify.yaml": defaultVerify,
139
+ "context.yaml": baseContext
140
+ },
141
+ write_tests: {
142
+ "SKILL.md": `# write_tests
143
+
144
+ ## Goal
145
+ Add or improve tests for existing behavior without changing production behavior unless required.
146
+
147
+ ## Analysis Contract
148
+ - Identify the behavior under test and the risk it protects.
149
+ - Locate existing test patterns, fixtures, and helper APIs.
150
+ - Check CodeGraph context for affected code paths.
151
+ - Avoid inventing behavior not supported by source code or knowledge context.
152
+
153
+ ## Execution Contract
154
+ - Prefer focused tests over broad snapshot or brittle integration tests.
155
+ - Do not change production code unless the existing code is untestable and the change is minimal.
156
+ - Keep fixtures deterministic and local to the test where possible.
157
+
158
+ ## Verification Contract
159
+ - Run the relevant test command or configured Harness verification.
160
+ - If the full suite is too expensive, document the targeted command and why.
161
+
162
+ ## Report Contract
163
+ - Scenarios covered.
164
+ - Files changed.
165
+ - Verification result.
166
+ - Remaining uncovered risks.
167
+
168
+ ## Outputs
169
+ - Test files or fixture updates.
170
+ - Verification result.
171
+ - Summary of covered scenarios.
172
+ `,
173
+ "verify.yaml": defaultVerify,
174
+ "context.yaml": baseContext
175
+ },
176
+ update_docs: {
177
+ "SKILL.md": `# update_docs
178
+
179
+ ## Goal
180
+ Update project documentation and propose knowledge graph additions.
181
+
182
+ ## Analysis Contract
183
+ - Identify the audience and purpose of the documentation update.
184
+ - Cross-check docs against code, CodeGraph context, and Knowledge Graph records.
185
+ - Distinguish confirmed facts from inferred guidance.
186
+
187
+ ## Execution Contract
188
+ - Update only documentation or knowledge suggestion artifacts unless explicitly asked to change code.
189
+ - Keep docs concise, operational, and linked to actual project behavior.
190
+ - Do not write directly to Knowledge Graph in v1.
191
+
192
+ ## Verification Contract
193
+ - Check links, commands, paths, and filenames mentioned in the docs.
194
+ - Run configured verification when docs affect generated artifacts or examples.
195
+
196
+ ## Report Contract
197
+ - Docs changed.
198
+ - Source of truth used.
199
+ - Verification result.
200
+ - Knowledge Graph suggestions.
201
+
202
+ ## Outputs
203
+ - Documentation changes.
204
+ - Persist suggestions for ADR, module notes, runbooks, or decisions.
205
+ `,
206
+ "verify.yaml": defaultVerify,
207
+ "context.yaml": `queries:
208
+ - related docs
209
+ - ADR
210
+ - module notes
211
+ - API docs
212
+ fallbackInclude:
213
+ - README.md
214
+ - docs
215
+ `
216
+ }
217
+ };
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/utils.js ADDED
@@ -0,0 +1,95 @@
1
+ import { spawn } from "node:child_process";
2
+ import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ export async function pathExists(filePath) {
5
+ try {
6
+ await stat(filePath);
7
+ return true;
8
+ }
9
+ catch {
10
+ return false;
11
+ }
12
+ }
13
+ export async function ensureDir(dir) {
14
+ await mkdir(dir, { recursive: true });
15
+ }
16
+ export async function writeFileIfMissing(filePath, content) {
17
+ if (await pathExists(filePath))
18
+ return false;
19
+ await ensureDir(path.dirname(filePath));
20
+ await writeFile(filePath, content, "utf8");
21
+ return true;
22
+ }
23
+ export async function readTextIfExists(filePath) {
24
+ if (!(await pathExists(filePath)))
25
+ return "";
26
+ return readFile(filePath, "utf8");
27
+ }
28
+ export function nowIso() {
29
+ return new Date().toISOString();
30
+ }
31
+ export function slugify(input) {
32
+ return input
33
+ .toLowerCase()
34
+ .replace(/[^a-z0-9\u4e00-\u9fa5]+/g, "-")
35
+ .replace(/^-+|-+$/g, "")
36
+ .slice(0, 48) || "task";
37
+ }
38
+ export function timestampId() {
39
+ return new Date().toISOString().replace(/[:.]/g, "-");
40
+ }
41
+ export function truncate(input, max = 4000) {
42
+ if (input.length <= max)
43
+ return input;
44
+ return `${input.slice(0, max)}\n...[truncated ${input.length - max} chars]`;
45
+ }
46
+ export function runCommand(command, cwd, input, timeoutMs = 10 * 60 * 1000, options = {}) {
47
+ const started = Date.now();
48
+ return new Promise((resolve) => {
49
+ const child = spawn(command, {
50
+ cwd,
51
+ shell: true,
52
+ stdio: ["pipe", "pipe", "pipe"],
53
+ windowsHide: true
54
+ });
55
+ let stdout = "";
56
+ let stderr = "";
57
+ const timer = setTimeout(() => {
58
+ child.kill();
59
+ stderr += `\nCommand timed out after ${timeoutMs}ms.`;
60
+ }, timeoutMs);
61
+ child.stdout.on("data", (chunk) => {
62
+ const text = chunk.toString();
63
+ stdout += text;
64
+ if (options.live)
65
+ process.stdout.write(options.stdoutPrefix ? prefixLines(text, options.stdoutPrefix) : text);
66
+ });
67
+ child.stderr.on("data", (chunk) => {
68
+ const text = chunk.toString();
69
+ stderr += text;
70
+ if (options.live)
71
+ process.stderr.write(options.stderrPrefix ? prefixLines(text, options.stderrPrefix) : text);
72
+ });
73
+ child.on("error", (error) => {
74
+ clearTimeout(timer);
75
+ resolve({ exitCode: null, stdout, stderr: `${stderr}\n${error.message}`, durationMs: Date.now() - started });
76
+ });
77
+ child.on("close", (code) => {
78
+ clearTimeout(timer);
79
+ resolve({ exitCode: code, stdout, stderr, durationMs: Date.now() - started });
80
+ });
81
+ if (input)
82
+ child.stdin.write(input);
83
+ child.stdin.end();
84
+ });
85
+ }
86
+ function prefixLines(text, prefix) {
87
+ return text
88
+ .split(/(\r?\n)/)
89
+ .map((part) => (part === "\n" || part === "\r\n" || part === "" ? part : `${prefix}${part}`))
90
+ .join("");
91
+ }
92
+ export async function git(cwd, args) {
93
+ const result = await runCommand(`git ${args}`, cwd, undefined, 60_000);
94
+ return result.exitCode === 0 ? result.stdout.trim() : "";
95
+ }
package/dist/verify.js ADDED
@@ -0,0 +1,28 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import { runCommand, truncate } from "./utils.js";
3
+ export async function runVerify(cwd, config, skill, verifyPath) {
4
+ const commands = skill.verifyCommands.length ? skill.verifyCommands : config.verify.defaultCommands;
5
+ if (!commands.length) {
6
+ const skipped = { status: "skipped", commands: [] };
7
+ await writeFile(verifyPath, `${JSON.stringify(skipped, null, 2)}\n`, "utf8");
8
+ return skipped;
9
+ }
10
+ const results = [];
11
+ for (const command of commands) {
12
+ const result = await runCommand(command.command, cwd);
13
+ results.push({
14
+ name: command.name,
15
+ command: command.command,
16
+ exitCode: result.exitCode,
17
+ stdout: truncate(result.stdout, 6000),
18
+ stderr: truncate(result.stderr, 6000),
19
+ durationMs: result.durationMs
20
+ });
21
+ }
22
+ const verify = {
23
+ status: results.every((item) => item.exitCode === 0) ? "passed" : "failed",
24
+ commands: results
25
+ };
26
+ await writeFile(verifyPath, `${JSON.stringify(verify, null, 2)}\n`, "utf8");
27
+ return verify;
28
+ }
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "ai-dev-harness",
3
+ "version": "0.1.0",
4
+ "description": "Auditable AI development harness for Codex and Claude Code.",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "ai",
8
+ "agent",
9
+ "codex",
10
+ "claude",
11
+ "harness",
12
+ "mcp",
13
+ "cli"
14
+ ],
15
+ "type": "module",
16
+ "bin": {
17
+ "harness": "dist/cli.js"
18
+ },
19
+ "files": [
20
+ "dist/",
21
+ "!dist/**/*.test.js",
22
+ "!dist/testSupport.js",
23
+ "scripts/",
24
+ "README.md",
25
+ "package.json"
26
+ ],
27
+ "scripts": {
28
+ "build": "tsc -p tsconfig.json",
29
+ "test": "npm run build && node --test dist/**/*.test.js",
30
+ "start": "node dist/cli.js",
31
+ "prepack": "npm run build"
32
+ },
33
+ "engines": {
34
+ "node": ">=20"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^22.10.2",
38
+ "typescript": "^5.7.2"
39
+ }
40
+ }
@@ -0,0 +1,36 @@
1
+ param(
2
+ [Parameter(Mandatory = $true)]
3
+ [string]$Run,
4
+
5
+ [string]$Repo = (Get-Location).Path
6
+ )
7
+
8
+ $ErrorActionPreference = "Stop"
9
+
10
+ $repoPath = Resolve-Path -LiteralPath $Repo
11
+ $runPath = if (Test-Path -LiteralPath $Run) {
12
+ Resolve-Path -LiteralPath $Run
13
+ } else {
14
+ Resolve-Path -LiteralPath (Join-Path $repoPath "runs\$Run")
15
+ }
16
+
17
+ Write-Host "[harness-skill] evidence: $runPath"
18
+
19
+ $summary = Join-Path $runPath "summary.md"
20
+ $verify = Join-Path $runPath "verify.json"
21
+ $audit = Join-Path $runPath "audit.jsonl"
22
+
23
+ if (Test-Path -LiteralPath $summary) {
24
+ Write-Host "`n--- summary.md ---"
25
+ Get-Content -LiteralPath $summary
26
+ }
27
+
28
+ if (Test-Path -LiteralPath $verify) {
29
+ Write-Host "`n--- verify.json ---"
30
+ Get-Content -LiteralPath $verify
31
+ }
32
+
33
+ if (Test-Path -LiteralPath $audit) {
34
+ Write-Host "`n--- audit degraded/errors ---"
35
+ Select-String -LiteralPath $audit -Pattern "degraded|failed|error|run_finished" | ForEach-Object { $_.Line }
36
+ }
@@ -0,0 +1,53 @@
1
+ param(
2
+ [Parameter(Mandatory = $true)]
3
+ [string]$Task,
4
+
5
+ [Parameter(Mandatory = $true)]
6
+ [ValidateSet("fix_bug", "add_feature", "write_tests", "update_docs")]
7
+ [string]$Skill,
8
+
9
+ [Parameter(Mandatory = $true)]
10
+ [ValidateSet("claude", "codex")]
11
+ [string]$Agent,
12
+
13
+ [string]$Repo = (Get-Location).Path,
14
+
15
+ [switch]$Live = $true
16
+ )
17
+
18
+ $ErrorActionPreference = "Stop"
19
+
20
+ $repoPath = Resolve-Path -LiteralPath $Repo
21
+ $harnessYaml = Join-Path $repoPath "harness.yaml"
22
+ $cliPath = Resolve-Path -LiteralPath (Join-Path $PSScriptRoot "..\dist\cli.js")
23
+
24
+ Write-Host "[harness-skill] repo: $repoPath"
25
+ Write-Host "[harness-skill] checking config: $harnessYaml"
26
+
27
+ if (-not (Test-Path -LiteralPath $harnessYaml)) {
28
+ Write-Error "harness.yaml not found in $repoPath. Run: node `"$cliPath`" init"
29
+ }
30
+
31
+ $gitCheck = git -C $repoPath rev-parse --show-toplevel 2>$null
32
+ if ($LASTEXITCODE -ne 0) {
33
+ Write-Error "$repoPath is not a Git repository. AI Dev Harness run requires a business Git repository."
34
+ }
35
+
36
+ $argsList = @(
37
+ $cliPath,
38
+ "run",
39
+ "--task",
40
+ $Task,
41
+ "--skill",
42
+ $Skill,
43
+ "--agent",
44
+ $Agent
45
+ )
46
+
47
+ if ($Live) {
48
+ $argsList += "--live"
49
+ }
50
+
51
+ Write-Host "[harness-skill] starting harness: $Skill via $Agent"
52
+ node @argsList
53
+ exit $LASTEXITCODE