uni-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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: harness-init
|
|
3
|
-
description: Scan the repository
|
|
3
|
+
description: Scan the repository to fill in the PROJECT section of CLAUDE.md and .harness/commands.env, and propose initial RULES/ANTI-PATTERNS. Run once right after installing the harness, or whenever CLAUDE.md is still in placeholder state. Verifies detected commands by actually running them before applying. If the repo has no project code yet (greenfield), offers to scaffold a minimal project so the sensors start active from the first line of code.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Harness Init — Initialize the Harness by Scanning the Repo
|
|
@@ -11,6 +11,16 @@ running them.
|
|
|
11
11
|
|
|
12
12
|
## Step 1: Scan
|
|
13
13
|
|
|
14
|
+
- **Exclude the harness's own files first.** Everything the installer put
|
|
15
|
+
here — `.claude/`, `.harness/`, `harness/` — is the kit's machinery, not
|
|
16
|
+
the project. Never let it drive language detection, and never propose the
|
|
17
|
+
kit's self-test (`harness/tests/test_hooks.sh`) as the project's TEST_CMD:
|
|
18
|
+
it verifies the harness, not the user's code, and pointing the stop gate
|
|
19
|
+
at it would "pass" every turn regardless of what broke.
|
|
20
|
+
- The scan decides which mode you are in: project code exists → continue
|
|
21
|
+
with Step 2 (and the brownfield section if the project predates the
|
|
22
|
+
harness). Nothing but harness files → switch to the **New Projects
|
|
23
|
+
(greenfield)** section below. Do not stop at "nothing to configure".
|
|
14
24
|
- Language/version: from manifests (package.json, pyproject.toml, go.mod,
|
|
15
25
|
Cargo.toml, *.csproj, ...). Prefer versions pinned in lockfiles/config.
|
|
16
26
|
- BUILD / TEST / LINT candidates: collect from manifest scripts, Makefile,
|
|
@@ -38,6 +48,32 @@ running them.
|
|
|
38
48
|
truisms ("write good code") are forbidden — only things observable in
|
|
39
49
|
this repository.
|
|
40
50
|
|
|
51
|
+
## New Projects (greenfield — the harness was installed before any code)
|
|
52
|
+
|
|
53
|
+
If the scan finds no project code at all (no manifest, no sources, no CI),
|
|
54
|
+
the user is starting a project *with* the harness — the whole point is
|
|
55
|
+
that sensors guard the very first code written. Ending with "come back
|
|
56
|
+
later" leaves them off during the riskiest phase. Instead, offer to
|
|
57
|
+
bootstrap:
|
|
58
|
+
|
|
59
|
+
1. **Ask, don't guess:** what is being built, which language/runtime,
|
|
60
|
+
which framework and test runner (offer the ecosystem defaults, e.g.
|
|
61
|
+
pytest / vitest / go test).
|
|
62
|
+
2. **Propose a minimal scaffold** and apply it only after approval: the
|
|
63
|
+
manifest (package.json / pyproject.toml / go.mod ...), a source entry
|
|
64
|
+
point, a test setup with **one real passing test**, and a
|
|
65
|
+
linter/formatter config if the ecosystem has a standard one. Announce
|
|
66
|
+
any dependency installs with the reason before running them.
|
|
67
|
+
3. If it is not a git repository, offer `git init` (the guard hooks and
|
|
68
|
+
future ANTI-PATTERNS mining assume git history).
|
|
69
|
+
4. Then continue with Step 2 exactly as usual: run the scaffold's
|
|
70
|
+
BUILD/TEST/LINT commands, confirm they pass, and fill CLAUDE.md and
|
|
71
|
+
commands.env with the **verified** commands.
|
|
72
|
+
|
|
73
|
+
The exit criterion for greenfield: sensors ACTIVE against a real (if tiny)
|
|
74
|
+
test suite. If the user declines the scaffold, leave everything blank as
|
|
75
|
+
before and tell them to re-run /harness-init once code exists.
|
|
76
|
+
|
|
41
77
|
## Existing Projects (brownfield)
|
|
42
78
|
|
|
43
79
|
- If CLAUDE.md existed before the harness was installed, never replace it.
|
package/bin/cli.js
CHANGED
|
@@ -27,6 +27,10 @@ const { spawnSync } = require('child_process');
|
|
|
27
27
|
|
|
28
28
|
const PKG_ROOT = path.resolve(__dirname, '..');
|
|
29
29
|
const MACHINERY_DIRS = ['.claude/hooks', '.claude/skills', '.claude/agents', 'harness'];
|
|
30
|
+
// Files that live in these dirs but belong to the kit repository only.
|
|
31
|
+
// test_installer.sh tests bin/cli.js, which is never installed into target
|
|
32
|
+
// projects — shipping it there guarantees a failing test suite.
|
|
33
|
+
const KIT_ONLY_FILES = new Set(['harness/tests/test_installer.sh']);
|
|
30
34
|
const MANIFEST = '.harness/kit-manifest.json';
|
|
31
35
|
const KIT_VERSION = require(path.join(PKG_ROOT, 'package.json')).version;
|
|
32
36
|
|
|
@@ -53,7 +57,9 @@ function machineryFiles() {
|
|
|
53
57
|
const files = [];
|
|
54
58
|
for (const d of MACHINERY_DIRS) {
|
|
55
59
|
for (const f of walk(path.join(PKG_ROOT, d))) {
|
|
56
|
-
|
|
60
|
+
const rel = path.relative(PKG_ROOT, f).split(path.sep).join('/');
|
|
61
|
+
if (KIT_ONLY_FILES.has(rel)) continue;
|
|
62
|
+
files.push(rel);
|
|
57
63
|
}
|
|
58
64
|
}
|
|
59
65
|
return files.sort();
|
|
@@ -40,6 +40,9 @@ check "writes manifest" "yes" "$([ -f "$TGT/.harness/kit-manifest.json" ] && ech
|
|
|
40
40
|
check "gitignores logs" ".harness/logs/" "$(cat "$TGT/.gitignore")"
|
|
41
41
|
check "init runs hook tests" "Hook tests passed" "$OUT"
|
|
42
42
|
check "prints next step" "/harness-init" "$OUT"
|
|
43
|
+
# regression: test_installer.sh tests bin/cli.js, which only exists in the
|
|
44
|
+
# npm package — shipping it to targets guarantees a failing suite there
|
|
45
|
+
check "does not ship test_installer.sh" "EMPTY" "$([ -f "$TGT/harness/tests/test_installer.sh" ] && echo left)"
|
|
43
46
|
|
|
44
47
|
# ── brownfield init: existing project files are preserved ────────
|
|
45
48
|
echo "init (brownfield)"
|
|
@@ -82,6 +85,22 @@ check "states owned files untouched" "were not touched" "$OUT"
|
|
|
82
85
|
OUT=$(node "$CLI" update "$TGT" --force 2>&1)
|
|
83
86
|
check "--force overwrites modifications" "EMPTY" "$(grep -F 'local customization' "$TGT/.claude/hooks/guard-pre-bash.sh" || true)"
|
|
84
87
|
|
|
88
|
+
# simulate a v0.1.0 install that shipped test_installer.sh: an unmodified
|
|
89
|
+
# manifest-tracked copy must be cleaned up by update
|
|
90
|
+
cp "$ROOT/harness/tests/test_installer.sh" "$TGT/harness/tests/test_installer.sh"
|
|
91
|
+
python3 - "$TGT" <<'EOF'
|
|
92
|
+
import json, sys, hashlib, os
|
|
93
|
+
t = sys.argv[1]
|
|
94
|
+
mp = os.path.join(t, '.harness/kit-manifest.json')
|
|
95
|
+
m = json.load(open(mp))
|
|
96
|
+
p = os.path.join(t, 'harness/tests/test_installer.sh')
|
|
97
|
+
m['files']['harness/tests/test_installer.sh'] = hashlib.sha256(open(p, 'rb').read()).hexdigest()
|
|
98
|
+
json.dump(m, open(mp, 'w'), indent=2)
|
|
99
|
+
EOF
|
|
100
|
+
OUT=$(node "$CLI" update "$TGT" 2>&1)
|
|
101
|
+
check "update removes stale kit-only file" "removed from kit" "$OUT"
|
|
102
|
+
check "stale file is gone" "EMPTY" "$([ -f "$TGT/harness/tests/test_installer.sh" ] && echo left)"
|
|
103
|
+
|
|
85
104
|
# ── doctor ───────────────────────────────────────────────────────
|
|
86
105
|
echo "doctor"
|
|
87
106
|
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.1.2",
|
|
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"
|