uni-harness 0.1.2 → 0.2.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/.claude/hooks/session-start.sh +14 -7
- package/.claude/skills/checkpoint/SKILL.md +14 -7
- package/CLAUDE.md +6 -4
- package/README.md +1 -2
- package/bin/cli.js +69 -10
- package/harness/tests/test_hooks.sh +9 -3
- package/harness/tests/test_installer.sh +25 -0
- package/package.json +1 -1
- package/.claude/agents/TEMPLATE.md +0 -64
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# ════════════════════════════════════════════════════════════════
|
|
3
3
|
# SessionStart context injection (startup|resume|clear|compact)
|
|
4
|
-
# (1) If an in-progress checkpoint (progress.json
|
|
4
|
+
# (1) If an in-progress checkpoint (.harness/state/progress.json,
|
|
5
|
+
# legacy: ./progress.json) exists, surface
|
|
5
6
|
# it so completed steps aren't repeated. Re-injected after
|
|
6
7
|
# compaction too.
|
|
7
8
|
# (2) If tool failures have piled up in the last 7 days of logs,
|
|
@@ -36,15 +37,21 @@ try:
|
|
|
36
37
|
except Exception:
|
|
37
38
|
pass
|
|
38
39
|
|
|
39
|
-
# (1) in-progress checkpoint
|
|
40
|
+
# (1) in-progress checkpoint (.harness/state/; legacy installs kept it at the root)
|
|
40
41
|
try:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
ckpt, where = None, None
|
|
43
|
+
for rel in (".harness/state/progress.json", "progress.json"):
|
|
44
|
+
p = os.path.join(proj, rel)
|
|
45
|
+
if os.path.exists(p):
|
|
46
|
+
with open(p) as f:
|
|
47
|
+
ckpt = json.load(f)
|
|
48
|
+
where = os.path.dirname(rel) or "."
|
|
49
|
+
break
|
|
50
|
+
if ckpt and ckpt.get("status") not in ("completed", "abandoned"):
|
|
44
51
|
parts.append(
|
|
45
52
|
"[harness memory] An in-progress checkpoint exists. Before starting "
|
|
46
|
-
"work, read progress.json / plan.md / decisions.jsonl
|
|
47
|
-
"completed steps, and resume from the next step.\n"
|
|
53
|
+
f"work, read progress.json / plan.md / decisions.jsonl in {where}/, "
|
|
54
|
+
"do not repeat completed steps, and resume from the next step.\n"
|
|
48
55
|
f"Checkpoint summary: {json.dumps(ckpt, ensure_ascii=False)[:1500]}")
|
|
49
56
|
except Exception:
|
|
50
57
|
pass
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: checkpoint
|
|
3
|
-
description: Save current work state to plan.md / decisions.jsonl / progress.json
|
|
3
|
+
description: Save current work state to .harness/state/ checkpoints (plan.md / decisions.jsonl / progress.json). Use after completing a meaningful step, before pausing a long task, or when the user invokes /checkpoint.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Checkpoint — Saving Work State
|
|
7
7
|
|
|
8
|
-
The filesystem is the memory. Maintain these three files
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
The filesystem is the memory. Maintain these three files in
|
|
9
|
+
**`.harness/state/`** (created by the installer; gitignored by default —
|
|
10
|
+
remove the `.gitignore` entry if you want decisions.jsonl in version
|
|
11
|
+
control). The single test: **if this session died right now, could the
|
|
12
|
+
next session pick up where it left off?**
|
|
11
13
|
|
|
12
14
|
## Procedure
|
|
13
15
|
|
|
14
|
-
1.
|
|
16
|
+
1. **`.harness/state/plan.md`** — update if the plan changed. Format:
|
|
15
17
|
```markdown
|
|
16
18
|
# Plan: [task title]
|
|
17
19
|
- [x] completed step
|
|
@@ -19,13 +21,13 @@ pick up where it left off?**
|
|
|
19
21
|
- [ ] later step
|
|
20
22
|
```
|
|
21
23
|
|
|
22
|
-
2.
|
|
24
|
+
2. **`.harness/state/decisions.jsonl`** — append only *settled decisions* from this step,
|
|
23
25
|
one per line (no exploratory reasoning or transient facts):
|
|
24
26
|
```json
|
|
25
27
|
{"ts": "ISO8601", "decision": "what was decided", "why": "one-line rationale", "alternatives_rejected": ["rejected option"]}
|
|
26
28
|
```
|
|
27
29
|
|
|
28
|
-
3.
|
|
30
|
+
3. **`.harness/state/progress.json`** — rewrite in full:
|
|
29
31
|
```json
|
|
30
32
|
{
|
|
31
33
|
"task_id": "task identifier",
|
|
@@ -40,6 +42,11 @@ pick up where it left off?**
|
|
|
40
42
|
|
|
41
43
|
## Rules
|
|
42
44
|
|
|
45
|
+
- Legacy layout: if plan.md / progress.json / decisions.jsonl sit at the
|
|
46
|
+
project root (installs before v0.2), propose moving them into
|
|
47
|
+
`.harness/state/` — ask first, since a root plan.md might be the
|
|
48
|
+
project's own document rather than the harness's.
|
|
49
|
+
|
|
43
50
|
- When the task is fully done, set `status: "completed"`. The SessionStart
|
|
44
51
|
hook does not inject completed checkpoints — forget this closing step and
|
|
45
52
|
the next session will read stale state.
|
package/CLAUDE.md
CHANGED
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
|
|
32
32
|
## Work Loop
|
|
33
33
|
|
|
34
|
-
- For any task with 3+ steps, write the plan to
|
|
34
|
+
- For any task with 3+ steps, write the plan to `.harness/state/plan.md`
|
|
35
|
+
before starting.
|
|
35
36
|
- Retry ceiling: at most 3 fix attempts per goal. Beyond that, stop and
|
|
36
37
|
write an escalation packet.
|
|
37
38
|
- Escalation packet format (report to the user):
|
|
@@ -45,10 +46,11 @@
|
|
|
45
46
|
|
|
46
47
|
## Checkpoints
|
|
47
48
|
|
|
48
|
-
- At session start, if
|
|
49
|
-
and do not repeat completed steps. (The SessionStart hook
|
|
49
|
+
- At session start, if `.harness/state/progress.json` (checkpoint) exists,
|
|
50
|
+
read it first and do not repeat completed steps. (The SessionStart hook
|
|
51
|
+
will remind you.)
|
|
50
52
|
- After completing each meaningful step, run `/checkpoint` to update
|
|
51
|
-
plan.md / decisions.jsonl / progress.json
|
|
53
|
+
plan.md / decisions.jsonl / progress.json in `.harness/state/`.
|
|
52
54
|
- Keep transient facts and exploratory reasoning out of checkpoints. Record
|
|
53
55
|
only settled decisions in decisions.jsonl.
|
|
54
56
|
|
package/README.md
CHANGED
|
@@ -56,7 +56,6 @@ anything you've customized is skipped (listed, with `--force` to override).
|
|
|
56
56
|
| `.claude/hooks/guard-pre-bash.sh` | Blocks destructive commands and verification bypasses (`--no-verify`) before execution |
|
|
57
57
|
| `.claude/hooks/session-start.sh` | Re-injects in-progress checkpoints at session start/resume/compaction; nudges `/ratchet` when failures pile up |
|
|
58
58
|
| `.claude/hooks/observe-log.sh` | Logs every tool call as JSONL + tripwires (same failure 3x, call surge) |
|
|
59
|
-
| `.claude/agents/TEMPLATE.md` | Custom agent template (inactive until you uncomment `name`; example: fresh-context code reviewer) |
|
|
60
59
|
| `harness/harness_report.py` | Health scorecard from the logs |
|
|
61
60
|
| `harness/tests/` | Self-tests for the hooks and the installer |
|
|
62
61
|
| `bin/cli.js` | The installer (init / update / doctor / uninstall) |
|
|
@@ -66,7 +65,7 @@ Skills:
|
|
|
66
65
|
| Skill | Role |
|
|
67
66
|
|---|---|
|
|
68
67
|
| `/harness-init` | Scan the repo → fill PROJECT section & commands.env (once, at install) |
|
|
69
|
-
| `/checkpoint` | Save state to plan.md / decisions.jsonl / progress.json |
|
|
68
|
+
| `/checkpoint` | Save state to `.harness/state/` (plan.md / decisions.jsonl / progress.json) |
|
|
70
69
|
| `/ratchet [mistake]` | Reproduce → classify → propose rule/sensor/permission → verify (no args: diagnose the logs) |
|
|
71
70
|
| `/guide-audit` | Audit CLAUDE.md rules — keep / delete / convert-to-sensor (monthly) |
|
|
72
71
|
|
package/bin/cli.js
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
* npx uni-harness uninstall [dir] --yes remove machinery files
|
|
10
10
|
*
|
|
11
11
|
* File boundary (the core contract of this installer):
|
|
12
|
-
* machinery (managed) — .claude/hooks/, .claude/skills/,
|
|
13
|
-
*
|
|
12
|
+
* machinery (managed) — .claude/hooks/, .claude/skills/, harness/
|
|
13
|
+
* → init installs them, update refreshes
|
|
14
14
|
* them. Hashes are recorded in
|
|
15
15
|
* .harness/kit-manifest.json so files the user has
|
|
16
16
|
* modified are NEVER overwritten (unless --force).
|
|
@@ -26,7 +26,7 @@ const crypto = require('crypto');
|
|
|
26
26
|
const { spawnSync } = require('child_process');
|
|
27
27
|
|
|
28
28
|
const PKG_ROOT = path.resolve(__dirname, '..');
|
|
29
|
-
const MACHINERY_DIRS = ['.claude/hooks', '.claude/skills', '
|
|
29
|
+
const MACHINERY_DIRS = ['.claude/hooks', '.claude/skills', 'harness'];
|
|
30
30
|
// Files that live in these dirs but belong to the kit repository only.
|
|
31
31
|
// test_installer.sh tests bin/cli.js, which is never installed into target
|
|
32
32
|
// projects — shipping it there guarantees a failing test suite.
|
|
@@ -103,6 +103,24 @@ function runHookTests(target) {
|
|
|
103
103
|
|
|
104
104
|
const log = (s) => process.stdout.write(s + '\n');
|
|
105
105
|
|
|
106
|
+
// runtime dirs: logs (observability) and state (checkpoints — plan.md,
|
|
107
|
+
// progress.json, decisions.jsonl). Both gitignored by default; users who
|
|
108
|
+
// want decisions.jsonl in version control can drop the state entry.
|
|
109
|
+
// Called by init AND update so upgrades pick up newly added dirs/entries.
|
|
110
|
+
function ensureRuntimeDirs(target) {
|
|
111
|
+
fs.mkdirSync(path.join(target, '.harness/logs'), { recursive: true });
|
|
112
|
+
fs.mkdirSync(path.join(target, '.harness/state'), { recursive: true });
|
|
113
|
+
const gi = path.join(target, '.gitignore');
|
|
114
|
+
const giBody = fs.existsSync(gi) ? fs.readFileSync(gi, 'utf8') : '';
|
|
115
|
+
const giLines = giBody.split('\n').map((l) => l.trim().replace(/\/$/, ''));
|
|
116
|
+
const giAdd = ['.harness/logs', '.harness/state'].filter((d) => !giLines.includes(d));
|
|
117
|
+
if (giAdd.length) {
|
|
118
|
+
fs.writeFileSync(gi, giBody + (giBody && !giBody.endsWith('\n') ? '\n' : '') +
|
|
119
|
+
giAdd.map((d) => d + '/\n').join(''));
|
|
120
|
+
log(' ~ .gitignore (+' + giAdd.map((d) => d + '/').join(', +') + ')');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
106
124
|
// ── settings.json merge (init only) ──────────────────────────────
|
|
107
125
|
// Hooks: append only the entries whose hook script is not yet registered
|
|
108
126
|
// for that event. Everything the user already has is left untouched.
|
|
@@ -162,6 +180,36 @@ function mergeSettings(target) {
|
|
|
162
180
|
}
|
|
163
181
|
}
|
|
164
182
|
|
|
183
|
+
// pre-v0.2 checkpoints lived at the project root. Migrate them into
|
|
184
|
+
// .harness/state/, but ONLY files whose content is recognizably the
|
|
185
|
+
// harness's own checkpoint format — a root plan.md may well be the
|
|
186
|
+
// project's own document, and those are never touched.
|
|
187
|
+
function migrateLegacyState(target) {
|
|
188
|
+
const isOurs = {
|
|
189
|
+
'progress.json': (s) => {
|
|
190
|
+
try { const j = JSON.parse(s); return !!j && typeof j === 'object' && 'status' in j && ('task_id' in j || 'next_step' in j); }
|
|
191
|
+
catch { return false; }
|
|
192
|
+
},
|
|
193
|
+
'decisions.jsonl': (s) => {
|
|
194
|
+
const lines = s.trim().split('\n');
|
|
195
|
+
return lines.length > 0 && lines.every((l) => {
|
|
196
|
+
try { return 'decision' in JSON.parse(l); } catch { return false; }
|
|
197
|
+
});
|
|
198
|
+
},
|
|
199
|
+
'plan.md': (s) => /^# Plan:/m.test(s),
|
|
200
|
+
};
|
|
201
|
+
for (const [name, test] of Object.entries(isOurs)) {
|
|
202
|
+
const src = path.join(target, name);
|
|
203
|
+
const dst = path.join(target, '.harness/state', name);
|
|
204
|
+
if (!fs.existsSync(src) || fs.existsSync(dst)) continue;
|
|
205
|
+
let body;
|
|
206
|
+
try { body = fs.readFileSync(src, 'utf8'); } catch { continue; }
|
|
207
|
+
if (!test(body)) { log(' ! ' + name + ' left at the root (not in harness checkpoint format)'); continue; }
|
|
208
|
+
fs.renameSync(src, dst);
|
|
209
|
+
log(' > ' + name + ' -> .harness/state/' + name + ' (migrated)');
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
165
213
|
// ── init ─────────────────────────────────────────────────────────
|
|
166
214
|
function init(target, force) {
|
|
167
215
|
const files = machineryFiles();
|
|
@@ -193,13 +241,7 @@ function init(target, force) {
|
|
|
193
241
|
}
|
|
194
242
|
mergeSettings(target);
|
|
195
243
|
|
|
196
|
-
|
|
197
|
-
const gi = path.join(target, '.gitignore');
|
|
198
|
-
const giBody = fs.existsSync(gi) ? fs.readFileSync(gi, 'utf8') : '';
|
|
199
|
-
if (!giBody.split('\n').some((l) => l.trim().replace(/\/$/, '') === '.harness/logs')) {
|
|
200
|
-
fs.writeFileSync(gi, giBody + (giBody && !giBody.endsWith('\n') ? '\n' : '') + '.harness/logs/\n');
|
|
201
|
-
log(' ~ .gitignore (+.harness/logs/)');
|
|
202
|
-
}
|
|
244
|
+
ensureRuntimeDirs(target);
|
|
203
245
|
|
|
204
246
|
writeManifest(target, files);
|
|
205
247
|
|
|
@@ -253,14 +295,31 @@ function update(target, force) {
|
|
|
253
295
|
const dst = path.join(target, rel);
|
|
254
296
|
if (fs.existsSync(dst) && sha256(dst) === manifest.files[rel]) {
|
|
255
297
|
fs.unlinkSync(dst);
|
|
298
|
+
try { fs.rmdirSync(path.dirname(dst)); } catch { /* keep if not empty */ }
|
|
256
299
|
log(' - ' + rel + ' (removed from kit)');
|
|
257
300
|
changed++;
|
|
258
301
|
}
|
|
259
302
|
}
|
|
260
303
|
|
|
261
304
|
writeManifest(target, files);
|
|
305
|
+
ensureRuntimeDirs(target);
|
|
306
|
+
migrateLegacyState(target);
|
|
262
307
|
log(`\n${changed} file(s) updated (v${manifest.kitVersion} -> v${KIT_VERSION}). ` +
|
|
263
308
|
'CLAUDE.md / commands.env / settings.json were not touched.');
|
|
309
|
+
|
|
310
|
+
// pre-v0.2 installs kept checkpoints at the project root and their
|
|
311
|
+
// (owned, never-touched) CLAUDE.md still says so — tell, don't touch
|
|
312
|
+
try {
|
|
313
|
+
const cm = fs.readFileSync(path.join(target, 'CLAUDE.md'), 'utf8');
|
|
314
|
+
if (cm.includes('progress.json') && !cm.includes('.harness/state')) {
|
|
315
|
+
log('\nNote: checkpoint files (plan.md / progress.json / decisions.jsonl)');
|
|
316
|
+
log('now live in .harness/state/ instead of the project root. Recognized');
|
|
317
|
+
log('checkpoint files were migrated automatically (see above). Your');
|
|
318
|
+
log('CLAUDE.md still references the old locations — update its Work Loop');
|
|
319
|
+
log('and Checkpoints sections. Until then the SessionStart hook still');
|
|
320
|
+
log('reads a root progress.json as a fallback.');
|
|
321
|
+
}
|
|
322
|
+
} catch { /* no CLAUDE.md — nothing to migrate */ }
|
|
264
323
|
if (skipped.length) {
|
|
265
324
|
log('Skipped files with local modifications (use --force to overwrite):');
|
|
266
325
|
for (const s of skipped) log(' ! ' + s);
|
|
@@ -102,12 +102,18 @@ echo "session-start.sh"
|
|
|
102
102
|
fresh_project
|
|
103
103
|
OUT=$(bash "$HOOKS/session-start.sh")
|
|
104
104
|
check "silent without checkpoint" "EMPTY" "$OUT"
|
|
105
|
-
|
|
105
|
+
mkdir -p "$TMP/.harness/state"
|
|
106
|
+
echo '{"task_id":"t1","status":"in_progress","next_step":"do X"}' > "$TMP/.harness/state/progress.json"
|
|
106
107
|
OUT=$(bash "$HOOKS/session-start.sh")
|
|
107
|
-
check "injects in-progress checkpoint" "
|
|
108
|
-
echo '{"task_id":"t1","status":"completed"}' > "$TMP/progress.json"
|
|
108
|
+
check "injects in-progress checkpoint" ".harness/state" "$OUT"
|
|
109
|
+
echo '{"task_id":"t1","status":"completed"}' > "$TMP/.harness/state/progress.json"
|
|
109
110
|
OUT=$(bash "$HOOKS/session-start.sh")
|
|
110
111
|
check "skips completed checkpoint" "EMPTY" "$OUT"
|
|
112
|
+
rm "$TMP/.harness/state/progress.json"
|
|
113
|
+
# legacy layout (pre-v0.2): checkpoint at the project root still read
|
|
114
|
+
echo '{"task_id":"t1","status":"in_progress","next_step":"do X"}' > "$TMP/progress.json"
|
|
115
|
+
OUT=$(bash "$HOOKS/session-start.sh")
|
|
116
|
+
check "reads legacy root checkpoint" "progress.json" "$OUT"
|
|
111
117
|
rm "$TMP/progress.json"
|
|
112
118
|
NOW=$(python3 -c 'import datetime; print(datetime.datetime.now().isoformat(timespec="seconds"))')
|
|
113
119
|
for _ in 1 2 3; do
|
|
@@ -38,6 +38,8 @@ check "installs CLAUDE.md" "yes" "$([ -f "$TGT/CLAUDE.md" ] && echo yes)"
|
|
|
38
38
|
check "installs settings.json" "yes" "$([ -f "$TGT/.claude/settings.json" ] && echo yes)"
|
|
39
39
|
check "writes manifest" "yes" "$([ -f "$TGT/.harness/kit-manifest.json" ] && echo yes)"
|
|
40
40
|
check "gitignores logs" ".harness/logs/" "$(cat "$TGT/.gitignore")"
|
|
41
|
+
check "creates state dir" "yes" "$([ -d "$TGT/.harness/state" ] && echo yes)"
|
|
42
|
+
check "gitignores state" ".harness/state/" "$(cat "$TGT/.gitignore")"
|
|
41
43
|
check "init runs hook tests" "Hook tests passed" "$OUT"
|
|
42
44
|
check "prints next step" "/harness-init" "$OUT"
|
|
43
45
|
# regression: test_installer.sh tests bin/cli.js, which only exists in the
|
|
@@ -101,6 +103,29 @@ OUT=$(node "$CLI" update "$TGT" 2>&1)
|
|
|
101
103
|
check "update removes stale kit-only file" "removed from kit" "$OUT"
|
|
102
104
|
check "stale file is gone" "EMPTY" "$([ -f "$TGT/harness/tests/test_installer.sh" ] && echo left)"
|
|
103
105
|
|
|
106
|
+
# update on a pre-v0.2 install must also add the new gitignore entry
|
|
107
|
+
sed -i.bak '/.harness\/state/d' "$TGT/.gitignore" && rm -f "$TGT/.gitignore.bak"
|
|
108
|
+
OUT=$(node "$CLI" update "$TGT" 2>&1)
|
|
109
|
+
check "update re-adds state gitignore entry" ".harness/state/" "$(cat "$TGT/.gitignore")"
|
|
110
|
+
|
|
111
|
+
# pre-v0.2 CLAUDE.md pointing checkpoints at the project root -> migration
|
|
112
|
+
# note (told, never touched); the current template must NOT trigger it
|
|
113
|
+
printf '# old project\n- read progress.json first\n' > "$TGT/CLAUDE.md"
|
|
114
|
+
# legacy root checkpoints: harness-format files are migrated, foreign ones kept
|
|
115
|
+
echo '{"task_id":"t1","status":"in_progress","next_step":"do X"}' > "$TGT/progress.json"
|
|
116
|
+
printf '{"ts":"2026-01-01","decision":"use X","why":"y"}\n' > "$TGT/decisions.jsonl"
|
|
117
|
+
printf '# Roadmap\nnot a harness plan\n' > "$TGT/plan.md"
|
|
118
|
+
OUT=$(node "$CLI" update "$TGT" 2>&1)
|
|
119
|
+
check "prints checkpoint migration note" "now live in .harness/state/" "$OUT"
|
|
120
|
+
check "migrates harness-format progress.json" "yes" "$([ -f "$TGT/.harness/state/progress.json" ] && [ ! -f "$TGT/progress.json" ] && echo yes)"
|
|
121
|
+
check "migrates harness-format decisions.jsonl" "yes" "$([ -f "$TGT/.harness/state/decisions.jsonl" ] && echo yes)"
|
|
122
|
+
check "keeps foreign plan.md at the root" "yes" "$([ -f "$TGT/plan.md" ] && [ ! -f "$TGT/.harness/state/plan.md" ] && echo yes)"
|
|
123
|
+
rm -f "$TGT/plan.md" "$TGT/.harness/state/progress.json" "$TGT/.harness/state/decisions.jsonl"
|
|
124
|
+
check "migration note does not edit CLAUDE.md" "# old project" "$(cat "$TGT/CLAUDE.md")"
|
|
125
|
+
cp "$ROOT/CLAUDE.md" "$TGT/CLAUDE.md" # restore for the doctor section
|
|
126
|
+
OUT=$(node "$CLI" update "$TGT" 2>&1)
|
|
127
|
+
check "no migration note for current template" "EMPTY" "$(printf '%s' "$OUT" | grep -F 'now live in' || true)"
|
|
128
|
+
|
|
104
129
|
# ── doctor ───────────────────────────────────────────────────────
|
|
105
130
|
echo "doctor"
|
|
106
131
|
OUT=$(node "$CLI" doctor "$TGT" 2>&1)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uni-harness",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Agent harness kit for Claude Code — installs verification sensors, destructive-command guards, checkpoint recovery, observability logs, and ratchet skills into your project",
|
|
5
5
|
"bin": {
|
|
6
6
|
"uni-harness": "bin/cli.js"
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
# ── Custom agent template (this file itself is inactive) ────────
|
|
3
|
-
# Claude Code treats a file without a `name` in its frontmatter as
|
|
4
|
-
# documentation and ignores it. To create an agent: copy this file →
|
|
5
|
-
# uncomment and fill the fields below → replace the body with that
|
|
6
|
-
# agent's system prompt.
|
|
7
|
-
#
|
|
8
|
-
# name: lowercase-hyphen identifier (becomes the call name) [required]
|
|
9
|
-
# description: WHEN to delegate to this agent — the main agent
|
|
10
|
-
# reads this sentence to decide auto-delegation [required]
|
|
11
|
-
# tools: allowed tools (omit to inherit all)
|
|
12
|
-
# disallowedTools: explicit denials (take precedence over tools)
|
|
13
|
-
# model: sonnet | opus | haiku (omit to inherit)
|
|
14
|
-
#
|
|
15
|
-
# name: code-reviewer
|
|
16
|
-
# description: Reviews code changes in a fresh context, without seeing the implementation process. Use after finishing a feature or fix, before commit/PR. Judges plan compliance and defects, and reports.
|
|
17
|
-
# tools: Read, Grep, Glob, Bash
|
|
18
|
-
#
|
|
19
|
-
# Note: creating the first active agent in agents/ requires one session
|
|
20
|
-
# restart. The harness hooks and permissions apply to agent tool calls too.
|
|
21
|
-
# ────────────────────────────────────────────────────────────────
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
The body below is the system prompt of the example (fresh-context code
|
|
25
|
-
reviewer). Replace it entirely when creating a new agent.
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
You are a code reviewer. You see only the result, not the implementer's
|
|
30
|
-
reasoning — that is your value. Find what the implementer missed, without
|
|
31
|
-
inheriting their assumptions.
|
|
32
|
-
|
|
33
|
-
## Procedure
|
|
34
|
-
|
|
35
|
-
1. Read the full change via `git diff` (or the scope you were given).
|
|
36
|
-
2. If `plan.md` exists, read it and check the change against the plan and
|
|
37
|
-
requirements.
|
|
38
|
-
3. Read enough surrounding code to judge each change in context.
|
|
39
|
-
4. If `.harness/commands.env` has a TEST_CMD, run it and check the result.
|
|
40
|
-
|
|
41
|
-
## Judging Criteria (in priority order)
|
|
42
|
-
|
|
43
|
-
1. **Compliance**: implemented differently from the plan/requirements, or
|
|
44
|
-
requirements missing
|
|
45
|
-
2. **Defects**: code that produces wrong results or crashes on real input —
|
|
46
|
-
report only what you can pair with a concrete failure scenario
|
|
47
|
-
3. **Verification gaps**: changed behavior that no test covers
|
|
48
|
-
4. **Simplification**: duplication replaceable by existing code (only when
|
|
49
|
-
confident)
|
|
50
|
-
|
|
51
|
-
## Rules
|
|
52
|
-
|
|
53
|
-
- Report only. Never modify code.
|
|
54
|
-
- Anchor every finding to `file:line` with evidence. If it's speculation,
|
|
55
|
-
say so.
|
|
56
|
-
- If there is nothing to report, say so. Never invent findings to fill space.
|
|
57
|
-
- No style preferences — that's the linter's job.
|
|
58
|
-
|
|
59
|
-
## Output Format
|
|
60
|
-
|
|
61
|
-
- **Verdict**: approvable / needs changes (one-line rationale)
|
|
62
|
-
- **Findings**: ordered by severity, each with location, problem, evidence,
|
|
63
|
-
failure scenario
|
|
64
|
-
- **Scope checked**: files read, verification commands run and their results
|