uni-harness 0.2.0 → 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/bin/cli.js +35 -4
- package/harness/tests/test_installer.sh +8 -0
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -180,6 +180,36 @@ function mergeSettings(target) {
|
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
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
|
+
|
|
183
213
|
// ── init ─────────────────────────────────────────────────────────
|
|
184
214
|
function init(target, force) {
|
|
185
215
|
const files = machineryFiles();
|
|
@@ -273,6 +303,7 @@ function update(target, force) {
|
|
|
273
303
|
|
|
274
304
|
writeManifest(target, files);
|
|
275
305
|
ensureRuntimeDirs(target);
|
|
306
|
+
migrateLegacyState(target);
|
|
276
307
|
log(`\n${changed} file(s) updated (v${manifest.kitVersion} -> v${KIT_VERSION}). ` +
|
|
277
308
|
'CLAUDE.md / commands.env / settings.json were not touched.');
|
|
278
309
|
|
|
@@ -282,11 +313,11 @@ function update(target, force) {
|
|
|
282
313
|
const cm = fs.readFileSync(path.join(target, 'CLAUDE.md'), 'utf8');
|
|
283
314
|
if (cm.includes('progress.json') && !cm.includes('.harness/state')) {
|
|
284
315
|
log('\nNote: checkpoint files (plan.md / progress.json / decisions.jsonl)');
|
|
285
|
-
log('now live in .harness/state/ instead of the project root.
|
|
316
|
+
log('now live in .harness/state/ instead of the project root. Recognized');
|
|
317
|
+
log('checkpoint files were migrated automatically (see above). Your');
|
|
286
318
|
log('CLAUDE.md still references the old locations — update its Work Loop');
|
|
287
|
-
log('and Checkpoints sections
|
|
288
|
-
log('
|
|
289
|
-
log('a root progress.json as a fallback.');
|
|
319
|
+
log('and Checkpoints sections. Until then the SessionStart hook still');
|
|
320
|
+
log('reads a root progress.json as a fallback.');
|
|
290
321
|
}
|
|
291
322
|
} catch { /* no CLAUDE.md — nothing to migrate */ }
|
|
292
323
|
if (skipped.length) {
|
|
@@ -111,8 +111,16 @@ check "update re-adds state gitignore entry" ".harness/state/" "$(cat "$TGT/.git
|
|
|
111
111
|
# pre-v0.2 CLAUDE.md pointing checkpoints at the project root -> migration
|
|
112
112
|
# note (told, never touched); the current template must NOT trigger it
|
|
113
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"
|
|
114
118
|
OUT=$(node "$CLI" update "$TGT" 2>&1)
|
|
115
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"
|
|
116
124
|
check "migration note does not edit CLAUDE.md" "# old project" "$(cat "$TGT/CLAUDE.md")"
|
|
117
125
|
cp "$ROOT/CLAUDE.md" "$TGT/CLAUDE.md" # restore for the doctor section
|
|
118
126
|
OUT=$(node "$CLI" update "$TGT" 2>&1)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uni-harness",
|
|
3
|
-
"version": "0.2.
|
|
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"
|