Files
zeekay 621d446b19 Add a Claude Code plugin: Kai hooks in shadow mode and the kai skill
examples/claude-code holds a Stop hook, a PreToolUse hook and the kai
skill. .claude-plugin/marketplace.json makes this repo the marketplace
"kai" (not "hanzo", a name another Hanzo marketplace already holds), so
/plugin install kai@kai installs it.

Kai does not yet read agent work reliably: its zero-shot answers to
"done", "verified" and "risky" are near chance. So rules in code decide
first, Kai can only tighten (send a turn back, or ask before an action;
never allow), both hooks fail open on any error or timeout, and the
default mode, shadow, logs Kai's answers and changes nothing. The log
holds probabilities and verdicts only, 0600 in a 0700 directory. A state
sent to Kai is redacted and at most 3,000 characters of JSON; only https,
or http on loopback, is called, and no redirect is followed.

Against the first draft: plugin options are read as
CLAUDE_PLUGIN_OPTION_<KEY> uppercased, as Claude Code exports them (the
draft read lowercase names and never saw the plugin's key or mode). The
floor is checked before the read-only shortcut, which let "env rm -rf"
and awk's system() through, and now catches "git checkout -- .",
"rm -Rf" and split rm flags. A background-task notice no longer starts a
new turn. Stop answers with decision "block" as JSON. Only the last
8 MiB of a transcript is read: a 745 MB one made the hook throw.

test/kai.test.mjs runs both hooks offline against a stand-in for Kai on
127.0.0.1, 25 tests; test.sh runs it with the rest. In a live Claude
Code 2.1.296 session against the stand-in, the Stop block came back as
"Stop hook feedback", the PreToolUse ask denied an allowed command
under -p, and shadow showed a notice and let it run.
2026-10-11 10:33:43 -07:00

40 lines
1.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Runs every example and recipe against api.hanzo.ai and prints a count per language.
# An example passes when it answers; a recipe passes when every answer in its
# expect.json is right; the Claude Code plugin's tests run offline. Needs
# HANZO_API_KEY, curl, jq, uv, node, go and cargo.
set -uo pipefail
: "${HANZO_API_KEY:?set HANZO_API_KEY}"
cd "$(dirname "$0")"
npm ci --no-audit --no-fund --loglevel=error || exit 1
py() { uv run -q --no-project --python 3.12 --with-requirements requirements.txt python "$@"; }
pass=0 fail=0 failed=0 summary=
check() { # check <label> <command...>
local out
if out=$("${@:2}" 2>&1); then pass=$((pass + 1)); echo "pass $1"; else fail=$((fail + 1)); echo "FAIL $1"; fi
printf '%s\n' "$out" | sed 's/^/ /'
}
count() { # count <language>
summary+="$1: $pass passed, $fail failed"$'\n'
failed=$((failed + fail)) pass=0 fail=0
}
for f in examples/curl/*.sh; do check "$f" bash "$f"; done
count curl
for f in examples/python/*.py recipes/*/run.py; do check "$f" py "$f"; done
count python
for f in examples/typescript/*.ts recipes/*/run.ts; do check "$f" npx tsx "$f"; done
count typescript
check examples/go go -C examples/go run .
count go
check examples/rust cargo run -q --manifest-path examples/rust/Cargo.toml
count rust
# Offline: the plugin's hooks against a stand-in for Kai on 127.0.0.1, never the key above.
check examples/claude-code node --test examples/claude-code/test/kai.test.mjs
count claude-code
printf '\n%s' "$summary"
[ "$failed" -eq 0 ]