@profoundry-us/highball 0.3.0 → 0.3.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.
- package/ONBOARDING.md +48 -10
- package/README.md +20 -13
- package/bin/highball.js +0 -0
- package/lib/init.js +5 -2
- package/lib/run.js +16 -2
- package/package.json +1 -1
package/ONBOARDING.md
CHANGED
|
@@ -14,7 +14,7 @@ yourself.
|
|
|
14
14
|
|
|
15
15
|
## 0. Preconditions
|
|
16
16
|
|
|
17
|
-
`npx highball --help` must work (Node >= 18, package installed as a dev
|
|
17
|
+
`npx @profoundry-us/highball --help` must work (Node >= 18, package installed as a dev
|
|
18
18
|
dependency). If it doesn't, ask your human whether to install from the npm
|
|
19
19
|
registry (`npm install --save-dev @profoundry-us/highball`) or from a
|
|
20
20
|
local tarball path they provide. In a repo with no `package.json`, create
|
|
@@ -36,19 +36,57 @@ Answer these by reading, not assuming:
|
|
|
36
36
|
repo *already trust* — look at `package.json` scripts, a `justfile` or
|
|
37
37
|
`Makefile`, CI workflows, README instructions. Wire what exists; invent
|
|
38
38
|
no new tooling in the first pass.
|
|
39
|
-
- **Host or container?**
|
|
40
|
-
|
|
41
|
-
and
|
|
42
|
-
|
|
43
|
-
know what paths they see.
|
|
39
|
+
- **Host or container?** Settle this before writing a single rule — see the
|
|
40
|
+
callout immediately below. Getting it wrong makes every rule fail for the
|
|
41
|
+
same uninteresting reason, and it is the most common way this setup
|
|
42
|
+
stalls.
|
|
44
43
|
- **What's fast?** Time candidate commands. Only sub-~2s commands belong
|
|
45
44
|
on the per-edit path; test suites belong at turn end; anything needing a
|
|
46
45
|
live server or long setup should not gate turns at all (leave it to the
|
|
47
46
|
repo's existing workflow, or declare it `todo`).
|
|
48
47
|
|
|
48
|
+
### If the repo's toolchain lives in Docker
|
|
49
|
+
|
|
50
|
+
Plenty of repos run *everything* through containers — the host may have no
|
|
51
|
+
Ruby, no Python, no database at all. Highball handles this, but only if you
|
|
52
|
+
declare it. **The runner itself always stays on the host** (that's where the
|
|
53
|
+
hooks fire and where the journal lives); only the rule commands move.
|
|
54
|
+
|
|
55
|
+
Find the dev service and confirm what it actually sees, rather than assuming
|
|
56
|
+
the layout:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
docker compose ps --services
|
|
60
|
+
docker compose exec -T <service> sh -c 'pwd && ls'
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Then declare the wrapper once, and every rule runs through it:
|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
exec:
|
|
67
|
+
via: docker compose exec -T --workdir /app app
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Four traps, each of which has bitten a real onboarding:
|
|
71
|
+
|
|
72
|
+
1. **`-T` is mandatory.** Hook shells have no TTY; without it commands hang
|
|
73
|
+
or die with "the input device is not a TTY".
|
|
74
|
+
2. **Set `--workdir`** to wherever the repo is mounted (verify with `pwd`
|
|
75
|
+
above). Containers frequently start somewhere other than the mount root.
|
|
76
|
+
3. **Self-orchestrating commands must opt out with `exec: host`.** A
|
|
77
|
+
`just test` / `make test` target that runs its *own* `docker compose
|
|
78
|
+
exec` would otherwise be double-wrapped into nonsense.
|
|
79
|
+
4. **Host-only tools opt out too.** If a linter or parser exists on the host
|
|
80
|
+
but not in the image (`node --check` against a JS bundle, say), mark that
|
|
81
|
+
rule `exec: host`.
|
|
82
|
+
|
|
83
|
+
A stopped container makes every wrapped rule fail. That's correct behavior —
|
|
84
|
+
unverifiable is not passing — but say so plainly to your human rather than
|
|
85
|
+
quietly dropping the rules.
|
|
86
|
+
|
|
49
87
|
## 2. Scaffold
|
|
50
88
|
|
|
51
|
-
Run `npx highball init`. It never overwrites: an existing
|
|
89
|
+
Run `npx @profoundry-us/highball init`. It never overwrites: an existing
|
|
52
90
|
`.highball/checks.yml` is kept, and if `.claude/settings.json` already
|
|
53
91
|
exists it prints the hook snippet for you to merge by hand — merge it
|
|
54
92
|
without disturbing existing hooks. Otherwise it creates both files.
|
|
@@ -110,7 +148,7 @@ Decision rules:
|
|
|
110
148
|
You must never see, type, or store a token value. Ask your human to:
|
|
111
149
|
|
|
112
150
|
1. Create this project (and a token for it) in their Highball app.
|
|
113
|
-
2. Run `npx highball login` themselves — interactively, or piping the
|
|
151
|
+
2. Run `npx @profoundry-us/highball login` themselves — interactively, or piping the
|
|
114
152
|
token via `--token-stdin` to keep it out of shell history.
|
|
115
153
|
|
|
116
154
|
This stores the token in `~/.highball/credentials.json` (machine-local,
|
|
@@ -119,8 +157,8 @@ vars instead. The repo tree never contains a secret.
|
|
|
119
157
|
|
|
120
158
|
## 5. Verify — all four proofs, not just the happy path
|
|
121
159
|
|
|
122
|
-
1. **Fast path:** `npx highball run --fast` exits 0, every rule passed.
|
|
123
|
-
2. **Full path:** `npx highball run` exits 0 (or fails honestly on real
|
|
160
|
+
1. **Fast path:** `npx @profoundry-us/highball run --fast` exits 0, every rule passed.
|
|
161
|
+
2. **Full path:** `npx @profoundry-us/highball run` exits 0 (or fails honestly on real
|
|
124
162
|
pre-existing issues — surface those to your human rather than papering
|
|
125
163
|
over them).
|
|
126
164
|
3. **The guardrail:** prove exit 2 works. Create an obviously-temporary
|
package/README.md
CHANGED
|
@@ -13,11 +13,10 @@ block, they just aren't recorded.
|
|
|
13
13
|
|
|
14
14
|
Published releases: `npm install --save-dev @profoundry-us/highball`.
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
(`npx @profoundry-us/highball <command>`).
|
|
16
|
+
**Always use the scoped name.** The unscoped npm name `highball` belongs to
|
|
17
|
+
an unrelated package, so a bare `npx highball` — in a committed hook, a
|
|
18
|
+
README, or a one-off — is a single uninstalled checkout away from fetching
|
|
19
|
+
a stranger's code and running it.
|
|
21
20
|
|
|
22
21
|
From a local tarball (pre-release):
|
|
23
22
|
|
|
@@ -31,7 +30,7 @@ npm install --save-dev ../highball-runner/profoundry-us-highball-<v>.tgz
|
|
|
31
30
|
Highball is installed *by the AI agent that will be checked by it*. After
|
|
32
31
|
installing the package, tell the repo's Claude Code agent:
|
|
33
32
|
|
|
34
|
-
> Run `npx highball onboard` and follow the instructions.
|
|
33
|
+
> Run `npx @profoundry-us/highball onboard` and follow the instructions.
|
|
35
34
|
|
|
36
35
|
[ONBOARDING.md](ONBOARDING.md) (which that command prints) walks the agent
|
|
37
36
|
through surveying the repo's real toolchain, scaffolding, writing rules that
|
|
@@ -41,8 +40,8 @@ human, and verifying all four proofs — including that exit 2 actually blocks.
|
|
|
41
40
|
The pieces, for reference or manual setup:
|
|
42
41
|
|
|
43
42
|
```bash
|
|
44
|
-
npx highball init # scaffolds
|
|
45
|
-
npx highball login # stores
|
|
43
|
+
npx @profoundry-us/highball init # scaffolds checks.yml + Claude Code hooks
|
|
44
|
+
npx @profoundry-us/highball login # stores a project token (once per machine)
|
|
46
45
|
```
|
|
47
46
|
|
|
48
47
|
`init` never overwrites an existing `checks.yml` and never edits an existing
|
|
@@ -95,9 +94,17 @@ in hosts that render Apps (Claude Desktop and friends), asking about your
|
|
|
95
94
|
checks produces an interactive inline dashboard — click a run for per-rule
|
|
96
95
|
detail with expandable command output, re-run fast or full checks from a
|
|
97
96
|
button. In hosts without Apps support the same tools answer in plain text,
|
|
98
|
-
per the extension's graceful-degradation rule. Register it
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
per the extension's graceful-degradation rule. Register it with the scoped
|
|
98
|
+
name — hosts spawn the server from an arbitrary directory, so it resolves
|
|
99
|
+
from the registry rather than a local install:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
"highball": { "command": "npx", "args": ["-y", "@profoundry-us/highball", "mcp"] }
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The journal it reads is machine-global (`~/.highball/runs/`), so one
|
|
106
|
+
registration covers every repo on that machine — there is no per-repo MCP
|
|
107
|
+
setup.
|
|
101
108
|
|
|
102
109
|
The split is capability-driven, not guesswork: the server reads the
|
|
103
110
|
client's initialize capabilities (`io.modelcontextprotocol/ui`) — hosts
|
|
@@ -112,8 +119,8 @@ Desktop restart.
|
|
|
112
119
|
|
|
113
120
|
Every run also appends to a local journal (`~/.highball/runs/<project>.jsonl`,
|
|
114
121
|
pruned to the last 200) — unconditionally, whether or not reporting is
|
|
115
|
-
configured. `npx highball runs` lists recent runs;
|
|
116
|
-
shows one run's detail with failure output, and `--logs` prints every
|
|
122
|
+
configured. `npx @profoundry-us/highball runs` lists recent runs; adding a
|
|
123
|
+
number shows one run's detail with failure output, and `--logs` prints every
|
|
117
124
|
rule's captured output, GitHub-Actions-style — the journal keeps the last
|
|
118
125
|
10KB per rule, pass or fail, while the dashboard receives failure tails
|
|
119
126
|
only. So the runner is self-sufficient out of the box: the hosted
|
package/bin/highball.js
CHANGED
|
File without changes
|
package/lib/init.js
CHANGED
|
@@ -36,17 +36,20 @@ checks:
|
|
|
36
36
|
# fast: true
|
|
37
37
|
`;
|
|
38
38
|
|
|
39
|
+
// Always the SCOPED command. The unscoped npm name belongs to an unrelated
|
|
40
|
+
// package, so a bare `npx highball` in a committed hook is one uninstalled
|
|
41
|
+
// checkout away from fetching a stranger's code and running it on every edit.
|
|
39
42
|
const HOOKS_JSON = {
|
|
40
43
|
hooks: {
|
|
41
44
|
PostToolUse: [
|
|
42
45
|
{
|
|
43
46
|
matcher: "Write|Edit",
|
|
44
|
-
hooks: [{ type: "command", command: "npx highball run --fast" }]
|
|
47
|
+
hooks: [{ type: "command", command: "npx @profoundry-us/highball run --fast" }]
|
|
45
48
|
}
|
|
46
49
|
],
|
|
47
50
|
Stop: [
|
|
48
51
|
{
|
|
49
|
-
hooks: [{ type: "command", command: "npx highball run", timeout: 900 }]
|
|
52
|
+
hooks: [{ type: "command", command: "npx @profoundry-us/highball run", timeout: 900 }]
|
|
50
53
|
}
|
|
51
54
|
]
|
|
52
55
|
}
|
package/lib/run.js
CHANGED
|
@@ -137,11 +137,25 @@ export async function run(args) {
|
|
|
137
137
|
// friends); that id groups this run with the rest of the agent's session
|
|
138
138
|
// on the dashboard. A TTY means a human at a terminal — don't block on
|
|
139
139
|
// read.
|
|
140
|
+
//
|
|
141
|
+
// The deadline matters: a non-TTY stdin that nobody writes to and nobody
|
|
142
|
+
// closes (a pipeline, a task runner, a CI step) would otherwise hang the
|
|
143
|
+
// runner forever waiting for EOF. Hooks write their payload immediately,
|
|
144
|
+
// so a short wait costs nothing and turns an indefinite hang into a run
|
|
145
|
+
// with no session context.
|
|
146
|
+
const HOOK_STDIN_DEADLINE_MS = 400;
|
|
147
|
+
|
|
140
148
|
async function readHookPayload() {
|
|
141
149
|
if (process.stdin.isTTY) return {};
|
|
142
150
|
try {
|
|
143
|
-
|
|
144
|
-
|
|
151
|
+
const text = await Promise.race([
|
|
152
|
+
(async () => {
|
|
153
|
+
let buffered = "";
|
|
154
|
+
for await (const chunk of process.stdin) buffered += chunk;
|
|
155
|
+
return buffered;
|
|
156
|
+
})(),
|
|
157
|
+
new Promise((resolve) => setTimeout(() => resolve(""), HOOK_STDIN_DEADLINE_MS))
|
|
158
|
+
]);
|
|
145
159
|
return JSON.parse(text);
|
|
146
160
|
} catch {
|
|
147
161
|
return {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@profoundry-us/highball",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Highball runner — local CI for AI coding agents: runs a repo's .highball/checks.yml rules, blocks the agent on failure, and reports runs to a Highball dashboard.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|