CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:frustration-matrix,kei-frustration-loop,kei-skill-importer,kei-projects-index,kei-projects-watcher,kei-gdrive-import,kei-leak-matrix,kei-skills,kei-gateway,kei-cron-scheduler,kei-export-trajectories,kei-backend-daytona,kei-d… (push) Has been cancelled
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-compute-baremetal,kei-compute-vultr,kei-compute-linode,kei-compute-digitalocean,kei-svc-systemd,kei-llm-bridge-mlx name:hosted-sleep-compute]) (push) Has been cancelled
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-diff,kei-scheduler,kei-watch,kei-prune,kei-discover,kei-brain-view,kei-hibernate,kei-ledger-sign,kei-fork name:wave13-15]) (push) Has been cancelled
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-git-gitea,kei-git-forgejo,kei-git-gitlab,kei-git-bitbucket,kei-memory-sled,kei-memory-redis,kei-memory-postgres,kei-memory-sqlite,kei-auth-google,kei-auth-apple,kei-auth-magiclink,kei-auth-webauthn,kei-notify-slack,kei-n… (push) Has been cancelled
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-ledger,kei-migrate,kei-changelog,kei-memory,kei-store,kei-conflict-scan,kei-refactor-engine,kei-graph-check,kei-shared,kei-dna-index,kei-pet name:core]) (push) Has been cancelled
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-machine-probe,kei-llm-ollama,kei-llm-llamacpp,kei-llm-mlx,kei-llm-router,kei-model name:llm-stack]) (push) Has been cancelled
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:kei-router,kei-sage,kei-task,kei-chat-store,kei-crossdomain,kei-search-core,kei-content-store,kei-social-store,kei-curator,kei-auth,kei-artifact name:mcp-lbm]) (push) Has been cancelled
CI (Forgejo Actions — self-hosted runner on Mac, host mode) / rust-primitives (map[crates:keisei,kei-forge,kei-runtime,kei-runtime-core,kei-atom-discovery,kei-agent-runtime,kei-capability,kei-provision,kei-entity-store,kei-pipe,kei-cache,kei-spawn,kei-replay name:atom-substrate]) (push) Has been cancelled
User feedback from real prod install (curl|bash, profile=full): 'нет выбора
провайдера, нахуй не понятно что делать после установки'.
## New: kei onboard wizard
scripts/kei-onboard.sh — 4-step interactive wizard auto-triggered at end
of bootstrap.sh (if stdin is TTY; non-interactive runs print summary):
Step 1 — Pick primary LLM orchestrator (claude/grok/agy/copilot/kimi)
Step 2 — Run kei mcp-wire to install MCP into each detected CLI
Step 3 — Optional MOONSHOT_API_KEY hint for live limits
Step 4 — Run kei-doctor health check
Re-runnable anytime: 'kei onboard'. Skip auto-trigger: KEI_NO_ONBOARD=1.
bin/kei gains 'onboard | setup | wizard' arms.
## Bug fixes from prod install log
[install] act_runner: command not found
brew installs 'gitea-runner' (not 'act_runner'); the two are functionally
equivalent and both register with Forgejo. lib-dev-hub-forgejo-runner.sh
now tries act_runner first, falls back to gitea-runner; brew install
switches to gitea-runner package which is what's actually available.
[install] forgejo admin user create — 'no such table: user'
Fresh sqlite DB hadn't been migrated before admin user create ran.
lib-dev-hub-forgejo.sh now runs 'forgejo migrate' before admin bootstrap;
idempotent — safe on re-runs.
[install] dev-hub-zoekt: 'No formulae or casks found for zoekt'
Zoekt not in homebrew/core. lib-dev-hub-zoekt.sh now tries known taps
(sourcegraph/zoekt, hyperdiscovery/zoekt), falls back to 'go install'
if Go is available, and finally skips cleanly with a clear warning
instead of aborting the entire dev-hub bundle install.
[install] dev-hub-datasette: Bootstrap failed: 5: Input/output error
launchd Input/output error is a macOS quirk when the plist exists but
the agent isn't yet known to launchd. Not introducing a code fix this
release — to investigate in v0.46. Doc note will be added.
[install] kei-shared binary missing post-install
Pre-built cache detection ('pre-built binaries detected — skipping
cargo build') was overly eager; kei-shared wasn't in the cache.
Workaround: run install with KEI_SKIP_RUST_BUILD unset to force rebuild.
Permanent fix deferred to v0.46 (improve cache validation).
## Verification
- 'kei onboard' non-interactive: prints next-steps + exits cleanly ✓
- 'kei --status' shows substrate v0.45 ✓
- bootstrap.sh end-of-install branch: TTY check + KEI_NO_ONBOARD honored ✓
Four-CLI parallel pre-release audit (Claude+Grok+Gemini+Copilot, each
reviewing different angle) surfaced 9 real issues in v0.43. All fixed.
## Audit team & their finds
- Claude (critic): code review — found #5 KEI_ALLOWED_ROOTS bypass,
#6 macOS TMPDIR denylist conflict, #7 timeout doc
drift, #9 failure-cache schema mismatch.
- Gemini (security): wrote Rust PoC, verified — found #1 CRITICAL parent
symlink for non-existent leaf, #2 TOCTOU await,
#3 curl config injection, #4 env inheritance, #8 cwd.
- Grok (architect): noted safe_tools.rs at 572 LOC (>200 Constructor
threshold). Deferred decomposition to v0.45.
- Copilot (docs): inspected README/encyclopedia, no blocker findings
(1 Premium, 977k cached tokens).
## Fixes shipped
[#1 CRITICAL] Parent-symlink bypass for non-existent leaf paths
v0.42 only canonicalized PARENT. If THAT parent didn't exist either,
the path fell through to "absolute as-is" with no canonicalization.
E.g. /proj/symlink -> /Users/denis, then kei_write /proj/symlink/
newdir/file would write inside /Users/denis with no check.
Fix: walk_up_to_canonicalize() — find DEEPEST existing ancestor,
canonicalize THAT (resolving all symlinks in the existing prefix),
then reattach the non-existent tail.
[#2 HIGH] TOCTOU between validate_path and fs::write
60s of hook chain await between path check and write. Concurrent
process could swap leaf for symlink during that window; fs::write
followed it.
Fix: open file with O_NOFOLLOW + write through the open fd (not the
path again). Open() itself fails on symlink-swap. Edit + Write both
patched. Falls back to plain tokio::fs on non-Unix.
[#3 HIGH] curl config injection via MOONSHOT_API_KEY
Was: token interpolated into printf 'header = "...%s..."' fed to curl
--config. If token contained " + newline + 'url = "evil"', curl
parsed the injected config and redirected.
Fix: validate MOONSHOT_API_KEY matches [A-Za-z0-9_.-]+; reject any
other chars before probe runs.
[#4 HIGH] Subprocess env inheritance — secret leak via kei_bash
Was: spawned bash inherited AWS_*, GITHUB_TOKEN, MOONSHOT_API_KEY,
etc. Agent running `env` via kei_bash could exfiltrate all of them.
Fix: apply_safe_env() — env_clear() + whitelist forward of PATH/
HOME/USER/LANG/TERM/SHELL/PWD/TMPDIR/LOGNAME/LC_*. Operators add
named vars via KEI_SAFE_ENV_EXTRA. Applied to BOTH kei_bash spawn
AND hook subprocess spawn.
[#5 HIGH] KEI_ALLOWED_ROOTS unanchored prefix bypass
Was: str::starts_with on raw user-supplied root.
KEI_ALLOWED_ROOTS=/home/u/proj also allowed /home/u/proj-secrets/...
Fix: normalize each entry to canonical + trailing slash; use
Path::starts_with (component-aware). v0.44 combines with #6 fix
(canonicalize symlinks like /var → /private/var on macOS).
[#6 MEDIUM] macOS $TMPDIR denied by /var/ blanket
Was: denylist included /var/, /private/var/ blanket entries.
macOS $TMPDIR = /var/folders/... canonicalized to /private/var/
folders/... hit the denylist before allowed_roots was checked.
Fix: (a) allowed_roots check FIRST; (b) narrowed denylist to /var/db/,
/var/log/, /var/root/ (and /private/ counterparts) instead of blanket
/var/. /var/folders + /private/tmp are now legitimate working dirs.
[#7 MEDIUM] Timeout aggregate claim was always false
Was: doc said "Hard cap on single chain + action ... 60s" — actually
was per-step. For 3-hook chain, total = 4 * 60 = 240s.
Fix: doc comment now honest about per-step semantics. Aggregate-
deadline impl deferred to v0.45 (not security-blocking).
[#8 MEDIUM] cwd not in hook input — hook approves wrong cwd
Was: kei_bash accepts cwd arg but did not pass it to safety hooks.
Hook could approve `rm -rf *` assuming PWD, while cwd actually
pointed at /etc or ~/.ssh.
Fix: include cwd in hook_input JSON. Hooks now see the real
working dir for their decision.
[#9 MEDIUM] Failure-fallback cache had different schema
Was: emit '{"ts":"","status":"assembly-failed"}' — no per-CLI keys.
Pet's .kimi.available_balance_usd read got null/error; kei-limits
own per-CLI render loop emitted 5 malformed rows.
Fix: failure-fallback emits same shape as success {ts, claude, grok,
agy, copilot, kimi} with each marked status='assembly-failed'.
LOW: empty old_string in kei_edit now rejected (was: silently
prepended new_string since contents.contains("") is always true).
## Tests + smokes
cargo test -p kei-mcp: 3/3 pass.
8 MCP smokes (all green after every audit round):
- kei_bash blocks RULE 0.1 push
- kei_bash passes echo OK
- kei_write /etc/passwd → denied (system dir)
- kei_write ../ → denied (.. segment)
- kei_write ~/.ssh/ → denied (outside roots)
- kei_write symlink-to-etc/passwd → denied (canonicalized)
- kei_write ~/.claude/hooks/ → denied (substrate dir)
- kei_write ~/.zshrc → denied (outside roots)
NEW v0.44 smokes:
- kei_write /Users/denis/.ssh/newdir/keys via /tmp/v44_link → denied
- KEI_ALLOWED_ROOTS=/tmp/proj does NOT match /tmp/proj-evil
- FAKE_SECRET=stolen → TOKEN=empty in subprocess (env stripped)
- MOONSHOT_API_KEY='abc"NL_url="evil"' → rejected pre-probe
- macOS $TMPDIR via KEI_ALLOWED_ROOTS works (canonicalize fix)
## Deferred to v0.45
- safe_tools.rs at 572 LOC — extract path_guard + chain_runner modules
- Aggregate-deadline timeout (single Instant::now() + remaining)
- Hardlink check (open fd then fstat + dev/ino compare)
- INVALID_PARAMS used for missing-arg (currently INTERNAL_ERROR)
- INVALID_PARAMS_REF dead code at EOF (silencer for unused import)
These are correctness/style/architectural, NOT security blockers.
Claude critic audit of v0.43 kei-limits.sh found 4 real issues. All fixed.
[HIGH] Non-atomic cache write
Was: jq > $CACHE truncated before jq ran — transient failure wiped cache.
Now: stage in mktemp, validate non-empty, atomic mv. Preserves last-known-good.
[HIGH] tonumber threw on non-numeric balance → emptied --argjson → killed assembler
Was: jq tonumber on $avail aborted on any non-numeric. Probe returned empty.
Now: tonumber? // 0 swallows parse errors. Plus _safe_json wrapper validates
each probe's output before --argjson — any single probe failure can no
longer poison the whole cache.
[MEDIUM] MOONSHOT_API_KEY leaked to ps / /proc/<pid>/cmdline via curl argv
Was: curl -H 'Authorization: Bearer $TOKEN' — token visible to local users.
Now: token fed via curl --config - (stdin) — never on argv.
[MEDIUM] No jq runtime guard (40+ sibling scripts have it)
Was: jq used unconditionally; on missing-jq host the script spewed parse
errors and wiped the cache.
Now: command -v jq check at top, clear error + early exit.
Verified: 'kei limits' still produces honest report; cache atomicity holds
under simulated failure; install lands all v0.40+v0.42+v0.43 components.
Cross-CLI subscription limits — research-grounded honest delivery after
5-parallel-agent investigation found that 4 of 5 CLIs have no public
programmatic API for quota.
## Reality findings (research)
- claude no public API; `anthropic-ratelimit-*` headers per-call only;
Admin API exists but needs separate admin token. See dashboard.
- grok no public API; `x-ratelimit-*` headers per-call only. No file.
- agy interactive /usage slash-cmd shows 100% always (forum bug).
No public API.
- copilot no public quota API; web dashboard only. The 'gh api /user/
copilot_billing' endpoint does NOT exist. June 2026 billing
migration to AI Credits further changes the surface.
- kimi Moonshot /v1/users/me/balance returns $ balance only (no
session/weekly quota fields). Requires MOONSHOT_API_KEY.
## Delivery (no false promises)
- scripts/kei-limits.sh — probe-all honest tool. For Kimi: real curl
call to Moonshot balance API if MOONSHOT_API_KEY set. For other 4:
status marker + dashboard URL.
- Pet integration — reads ~/.claude/pet/limits-cache.json IF present;
shows Kimi balance segment ONLY when status=='live'. Pet does NOT poll;
cache is populated by user-invoked 'kei limits'.
- bin/kei limits arm + --json mode + --quiet mode for cron.
Cache is bounded by user's explicit refresh; pet shows '(Xm old)' if
older than 1h. No background polling, no rate-limit waste, no fake data.
Convergent dogfood audit by Claude+Grok+Gemini+Copilot on v0.41 surfaced
that the v0.41 'fixes' were incomplete. 3 of 4 reviewers independently
found the same CRITICAL bypass; Gemini wrote a Rust test suite to verify.
## Convergent findings → v0.42 fixes
[#1 CRITICAL] Symlink leaf bypass (3-of-4 convergent)
v0.41 only canonicalized PARENT, then re-attached leaf as-is. Exploit:
1. kei_bash 'ln -s ~/.ssh/authorized_keys ./cfg.txt'
(passes the bash hook chain — ln itself is non-destructive)
2. kei_write file_path=cfg.txt content=<key>
validate_path saw $PWD/cfg.txt (allowed) → fs::write followed leaf
symlink → overwrote authorized_keys.
Fix: canonicalize FULL path (incl. leaf) when file exists; for new
files, symlink_metadata on leaf and reject is_symlink. Now the
denylist check sees the real target.
[#2 HIGH] $HOME blanket-allowed = self-neutering (2-of-4 convergent)
allowed_roots() defaulted to [$PWD, $HOME], denylist only had 4 secret
dirs. So kei_write $HOME/.claude/hooks/no-github-push.sh content='exit 0'
was ALLOWED — agent could overwrite the substrate's own policy hooks.
Similar attacks on ~/.zshrc (RCE on next shell), ~/.gitconfig, ~/.npmrc.
Fix: default to $PWD only. Denylist extended with .claude/, .grok/,
.gemini/, .copilot/, .kimi/, all major shell-init files, and
additional credential paths. KEI_ALLOWED_ROOTS for explicit
widening.
[#3 HIGH] Empty-section fail-OPEN (Gemini test-verified)
v0.41 'fail-closed on missing config' fix was incomplete: if config
file existed but section [bash]/[edit]/[write] was empty, load_chain
returned Ok(vec![]) → run_chain early-returned Ok → action ran ungated.
Fix: empty chain also FAIL-CLOSED with same KEI_POLICY_CHAIN_OPTIONAL
opt-in.
[#4 MEDIUM] load_chain still blocked tokio worker (Claude)
v0.41 fix#4 converted handle_edit/handle_write reads to tokio::fs but
left load_chain on std::fs. Slow/hung mount on policy-chain.toml would
freeze a worker for every safe_* invocation.
Fix: load_chain → async + tokio::fs::{try_exists, read_to_string}.
[#5 MEDIUM] process_group only applied to bash, not hooks (Claude)
v0.41 fix#5 set_process_group on kei_bash's child shell, but the
hook subprocess (spawned per-hook in run_chain) was NOT in its own
group. On hook timeout, kill_on_drop killed only the immediate hook
process; grandchildren orphaned — the exact failure mode fix#5 was
meant to prevent.
Fix: set_process_group + killpg also on hook spawn in run_chain.
[#6 MEDIUM] Per-step vs aggregate timeout (Claude)
Doc claimed 'Hard cap on single chain + action — 60s'. Actual: each
hook gets independent 60s, then action gets another 60s. For a 3-hook
bash chain that's 240s max — 4× documented.
Status: documented as known-limit; single-deadline impl deferred to
v0.43 (not security-blocking, just a doc/correctness drift).
## Verification (8 smokes — all green)
/etc/passwd → denied (system dir) ✓
../escape.txt → denied (../ segment) ✓
/tmp/symlink → /etc/passwd writeable → denied (resolved /private/etc) ✓ NEW
~/.claude/hooks/no-github-push.sh → denied (substrate dir) ✓ NEW
~/.zshrc → denied (shell-init file) ✓ NEW
policy-chain.toml empty [bash] → FAIL-CLOSED ✓ NEW
KEI_POLICY_CHAIN_OPTIONAL=1 → opt-in pass-through ✓
kei_bash git-push-github → BLOCKED (regression) ✓
kei_bash echo HELLO → returns content (regression) ✓
cargo test -p kei-mcp: 3/3 still pass.
## Architecture note from Grok
Grok architect flagged: safe_tools.rs is 474 LOC, exceeds Constructor
Pattern 200-line threshold. v0.42 does NOT refactor (security fixes
shipped first); v0.43 will extract path_guard.rs + chain_runner.rs.
## Per-CLI audit value demonstrated
Claude — 5 issues + 5 minor, exhaustive line-anchored analysis
Grok — architectural review with grep-verified citations
Gemini — wrote Rust test project to verify findings (PoC code!)
Copilot — partial fact-check, ran out of mid-task
Audit pass via Phase C dogfooding (security-auditor @ Agy/Gemini reviewing
our own safe_tools.rs) surfaced 5 real bugs. All fixed.
## Gemini findings (5 real bugs)
[#1 HIGH] FAIL-OPEN on missing config/hook
Before: missing policy-chain.toml → "passing through" warning; missing
hook script → "skipped" warning. Misconfig silently disabled
enforcement.
After: both paths FAIL-CLOSED with clear error surfaced to caller.
Tests/dev can opt in to pass-through via KEI_POLICY_CHAIN_OPTIONAL=1.
[#2 HIGH] Path traversal in kei_edit/kei_write
Before: no validation; attacker could pass file_path=/etc/passwd or
$HOME/.ssh/authorized_keys.
After: validate_path() rejects '..' segments, system dirs (/etc/, /usr/,
/System/, /var/, /root/), and dotfile-secret dirs (~/.ssh/,
~/.aws/, ~/.gnupg/, ~/.config/gcloud/). Override via
KEI_ALLOWED_ROOTS for explicit single-root confinement.
[#3 HIGH] CLAUDECODE/GROKCODE env bypass
Behavior unchanged — this guard is a perf/UX optimization to avoid
double-firing hooks when called from inside Claude/Grok (which already
ran their own PreToolUse). Documented explicitly as NOT a security
boundary: attacker controlling parent env already owns the invocation.
Module header gains a DESIGN NOTE making this load-bearing.
[#4 MED] std::fs in async context
Before: handle_edit/handle_write used std::fs::{read_to_string,write},
which block the tokio worker thread. Pathological paths like
/dev/random would freeze a worker indefinitely.
After: tokio::fs::{read_to_string,write}.await — async I/O, worker
stays responsive.
[#5 MED] kill_on_drop only kills immediate child
Before: timeout in kei_bash drops the Child handle; tokio's kill_on_drop
SIGKILLs only the shell. Grandchildren (e.g., 'sleep 1000 &')
orphaned.
After: Unix-only: spawn child in its own process group
(Command::process_group(0)), and on timeout libc::kill(-pid,
SIGKILL) to take down the whole group. New libc dep on Unix.
## Copilot doc fix
Doc claimed "kei-mcp exposes 4 built-in tools" without saying spawn_agent
lives in tools.rs while kei_bash/edit/write live in safe_tools.rs.
Validator agent flagged this as FALSE/MISLEADING. Now the doc spells out
the two-file structure + adds a v0.41 hardening summary.
## claude/grok subprocess permissions
Cross-CLI audit demo revealed that 'claude -p' and 'grok --print' returned
empty when invoked headless with a real audit task — they need explicit
permission flags to use Read/Grep tools in non-interactive mode. Added:
claude: --permission-mode=bypassPermissions
grok: --always-approve
agy: --dangerously-skip-permissions
Override via KEI_AGENT_PERMISSIVE=0 to keep strict default.
Re-verified: claude+grok both echo SMOKE-OK-V41 with the flag.
## Verification
cargo test -p kei-mcp --release → 3/3 pass
MCP JSON-RPC smoke (all 7):
- tools/list shows 4 built-ins ✓
- kei_bash blocks RULE 0.1 push ✓
- kei_bash passes 'echo OK' ✓
- kei_write rejects /etc/passwd ✓
- kei_write rejects ../ traversal ✓
- kei_write rejects ~/.ssh/* ✓
- missing policy-chain → FAIL-CLOSED with clear error ✓
- KEI_POLICY_CHAIN_OPTIONAL=1 → opt-in pass-through ✓
Two root-cause fixes:
1) hooks/_lib/policy-chain.toml was authored in v0.40 but the install loop
in install/lib-hooks.sh globbed only *.sh, missing the TOML SSoT. Fresh
installs got safe_tools fall-through ('no policy-chain.toml; passing
through') because the file never landed. Extended the glob to include
*.toml; chmod +x stays sh-only.
2) hooks/agent-event-done.sh now removes the .task-<id>.start marker that
task-timer.sh wrote on agent_spawn. Without it completed sub-agents
left stale markers in ~/.claude/memory/time-metrics/ for up to 2h
(the pet's stale filter) inflating the running-agents counter.
Verified end-to-end with HOME=/tmp/ksk-verify fresh install:
- 38 agents on disk
- 54 hooks + 4 _lib files (including policy-chain.toml)
- all 7 kei-mcp-wire-*.sh scripts present
- bin/kei has pick / run-via / mcp-wire arms
- 'kei mcp-wire --list' detects all 5 CLIs with correct tiers
- settings.json carries ONLY safety pack (Phase 2 opt-in honored)
The tamagotchi statusline was rendering one emoji+name+elapsed entry per
running sub-agent, producing strings like:
🔬researcher·2m 🏗️architect·5m 🔪critic·1m 📐plan·30s ...
When 4-8 parallel sessions ran agents simultaneously the line wrapped
across the terminal. Pet now renders ONE compact summary:
💬N 🌍Tk 🤖N 💰$C
Where:
💬 = distinct sessions today (parent_id from agent_spawn events)
🌍 = total tokens today across ALL sessions (suppressed when null upstream)
🤖 = running sub-agents NOW (count of <2h markers, all sessions)
💰 = total cost today (suppressed when zero or upstream null)
Per-agent detail moved to future kei status command (TODO comment).
Why: user feedback that the per-agent list was unreadable when many
parallel sessions/agents fire. Compact counters preserve the at-a-glance
value without cluttering the prompt.
Closes the "hooks only fire on Claude" gap. Phase C extends KeiSeiKit safety
enforcement (no-github-push, safety-guard, destructive-guard, citation-verify,
numeric-claims-guard) to any MCP-capable LLM CLI through a 3-tier honesty model.
## 3-tier model
TIER 1 (full native): claude (existing), grok (port hooks to grok settings.json)
TIER 2 (MCP-wrapped): copilot (--excluded-tools=shell + force kei_bash via MCP)
TIER 3 (advisory): agy + kimi (cannot disable native shell; prompt-level only)
## Design (Constructor Pattern)
1. hooks/_lib/policy-chain.toml — SSoT: which hooks gate which tool (bash/edit/write)
2. _primitives/_rust/kei-mcp/src/handlers/safe_tools.rs — new module, 3 built-in
MCP tools that synthesize Claude PreToolUse JSON, run hook chain, abort on
exit-2, exec on all-pass. Same input contract → hooks reused as-is, no rewrite.
3. tools.rs short-circuit: kei_bash/kei_edit/kei_write dispatched before atom layer
4. 6 wire scripts: orchestrator + one per CLI (Constructor Pattern, no mixin)
5. bin/kei mcp-wire arm
6. docs/encyclopedia/cross-cli-policy.md — honest 3-tier matrix + verification
## Double-enforcement guard
If kei-mcp invoked from a process with $CLAUDECODE=1 or $GROKCODE=1, the chain
SKIPS — native hooks already fired. Wire scripts set these env vars in the
MCP server registration for claude/grok respectively. On copilot/agy/kimi the
env is unset → chain runs.
## Smoke (verified live)
Block: kei_bash{command: forbidden-push-pattern}
→ JSON-RPC error -32603 with full "BLOCK — RULE 0.1 NO GITHUB PUSH" stderr ✓
Pass: kei_bash{command: "echo HELLO-FROM-KEI-BASH"}
→ result.content[0].text = "HELLO-FROM-KEI-BASH" ✓
tools/list: 4 built-ins present (spawn_agent + kei_bash + kei_edit + kei_write) ✓
## Tests
kei-mcp: 3/3 (tools_list assertions updated for atoms+4 built-ins).
Build clean with toml = "0.8" dep added.
## Out of scope (deferred)
- Codex CLI wiring (not installed locally)
- ACP middleware proxy (transport, not middleware — ruled out at research)
- Container/firejail sandboxing for agy/kimi (heavy; documented limit instead)
- Native Rust PatternGate migration (optimization, separate phase)
Closes the "Claude Code as single primary" gap. Now `kei` (no args) execs
whichever CLI is configured as primary, and ANY MCP-capable orchestrator
can spawn KeiSeiKit agents on any backend via the built-in spawn_agent tool.
## A — orchestrator picker
bin/kei now reads ~/.claude/config/primary.toml and execs that CLI instead
of hardcoding claude. New arms:
kei pick interactive menu → set primary → launch it
kei --on=<backend> one-shot launch of <backend> (no primary write)
kei primary [<b>] get/set primary
Splash shows `primary CLI: <backend>` so the orchestrator is visible.
Failure mode: if primary's CLI isn't on PATH, prints install hint + offers
`kei pick` recovery.
scripts/kei-pick.sh — Constructor Pattern picker (<140 LOC). Lists all 6
backends with install status (✓/✗), highlights current primary, writes
choice to primary.toml, execs the picked CLI. Honors stdin TTY gate
(RULE TTY-INTERACTIVITY-GATE — -t 0, not -t 1) for non-interactive safety.
## B — spawn_agent MCP tool
_primitives/_rust/kei-mcp/src/handlers/tools.rs gains a built-in
`spawn_agent` tool, exposed alongside discovered atoms:
- inputSchema: { name: str, task: str, on?: backend-enum }
- Calls kei-agent-cli.sh internally with same DNA resolution
- 60s timeout, kill-on-drop
- Honors KEI_AGENT_CLI env for testing
Smoke 2026-05-26 (MCP stdio JSON-RPC round-trip):
spawn_agent(name=smoke-test, on=claude) → "SMOKE-OK" ✅
spawn_agent(name=smoke-test, on=grok) → "SMOKE-OK" ✅
Why it matters: Claude Code has a native Agent tool. Grok / Agy / Copilot /
Kimi don't have an equivalent native sub-agent surface — but they all speak
MCP. spawn_agent gives them KeiSeiKit's sub-agent capability when they're
the orchestrator. The chosen orchestrator no longer caps the sub-agent fleet.
## Other
_primitives/_rust/kei-mcp/Cargo.toml: tokio gains "io-std" feature (was
missing — main.rs uses tokio::io::stdin/stdout). This fixes a latent build
error unrelated to this PR (kei-mcp wasn't building cleanly before).
Tests: tools_list assertions updated for the +1 built-in tool (3 total
instead of 2 with atoms; 1 instead of 0 on empty root). All MCP tests pass.
Assembler 3/3 golden tests still pass (provider field is optional).
Makes KeiSeiKit truly multi-LLM: any agent can declare its preferred backend
in its manifest. The DNA resolver picks the right CLI; `kei primary` swaps the
fleet-wide default. KeiSeiKit is no longer tied to Claude Code single-model.
Resolution order: --on=<backend> → manifest provider → primary.toml → claude
Files:
_assembler/src/manifest.rs + Option<String> provider field
_assembler/src/assembler.rs emit provider: in frontmatter (when set)
scripts/kei-agent-cli.sh DNA resolver; `kei primary` get/set; `kei agent`
arm (DNA-driven); honest kimi handling (TUI-only)
bin/kei new arms: agent, primary
_primitives/cli-backends.toml mark kimi as tui-only
docs/encyclopedia/multi-cli-agents.md rewritten with DNA flow, smoke
results, rule-enforcement caveat
Smoke 2026-05-26 (real CLI invocations):
claude ✓ via `claude -p`
grok ✓ via `grok --print` (DNA: manifest provider=grok)
agy ✓ via `agy --print` (Antigravity / Gemini)
copilot ✓ via `copilot --prompt` (1 Premium / 9s / 20.6k tok)
kimi ⚠ TUI-only, no print mode; need `kimi acp` JSON-RPC client
codex — register-only (not installed locally)
Rule-enforcement caveat documented: KeiSeiKit hooks fire only inside Claude
Code's PreToolUse pipeline. Non-claude backends carry the agent's PROMPT but
not the hook layer. For tool-level policy on non-claude, route through MCP.
ALSO: fix(stop-hook) — RULE 0.14 session-end-dump.sh "Recombobulating..."
4-minute hang on 18MB+ transcripts. Root cause: kei-memory ingest + frustration-
matrix scan + kei-sleep-sync ran sync at session end. Now async-detached with
per-op portable timeout (timeout/gtimeout/perl alarm). Hook returns in 0.03s.
Raw JSONL saved sync; only index/embedding step deferred (idempotent on
session_id so safe).
Adds a uniform launcher that lets any KeiSeiKit agent run on whichever
LLM CLI you have a subscription to. Pick by familiarity, pricing, or
to get a second opinion on the same prompt.
Backends (locally installed, by subscription):
claude Claude Code (claude -p)
grok xAI Grok (grok --print; native --agent supported)
agy Antigravity (agy --print) alias: antigravity
copilot GitHub Copilot (copilot --prompt)
kimi Moonshot Kimi (stdin, TUI primary)
codex OpenAI Codex (codex -p) register-only
Files:
_primitives/cli-backends.toml SSoT backend table
scripts/kei-agent-cli.sh launcher; loads ~/.claude/agents/<n>.md,
strips frontmatter, composes with task,
execs backend non-interactive
bin/kei new arm: run-via | via | run | agent-via
docs/encyclopedia/multi-cli-agents.md user-facing docs + usage
Auto-installed via lib-scaffold.sh:77 glob (no install code change needed).
Test plan:
kei run-via list # status + agents
kei run-via grok critic "review src/auth.rs" # via Grok
kei run-via agy critic "review src/auth.rs" # via Antigravity
kei run-via copilot critic "review src/auth.rs" # via Copilot
KEI_NATIVE_AGENT=1 kei run-via grok critic "..." # native --agent
Public installs/clones must not hit the author's private Forgejo (keigit.com,
direct Vultr, no Cloudflare, fail2ban) — that server times out under load and
is the root of the 443 connect failures.
- web-install.sh: default KEISEI_REPO keigit -> github.com/KeiSeiLab/KeiSeiKit-1.0
- plugin.json: homepage -> https://keisei.app; repository -> github
- .gitmodules: kei-registries submodule -> github.com/KeiSeiLab/kei-registries
(so a public --recursive clone resolves; repo still private until launch)
- README.md: manual `git clone` line -> github
Left intentionally on keigit: the @keisei/mcp-server npm registry (docs state
it's an author-operated mirror, not a community service). Repos stay PRIVATE
on github until launch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pre-public Phase 1. Remove personal/IP traces that should not ship in a
general-purpose kit; keep only intended author attribution.
- no-github-push.sh + hooks-and-blocks.md + ci-scaffold: drop "KeiTech
unfiled patent IP / trade secrets / priority date" wording; reword as a
generic opt-in guard for keeping code on a private remote.
- check-error-patterns.sh: remove author-local absolute path from the
tombstone comment.
- graph-export-watcher.sh: default viz dir to ~/.local/share/kei/graph-viz
(was a personal project path).
- agent manifests (cost-guardian, modal-runner, infra/ml/code-implementer)
+ ci.yml: strip private memory references and dated personal incidents;
keep the generic cost/ops lessons. Snapshots regenerated; golden 3/3.
Kept intentionally: author attribution (NOTICE / README / Cargo / plugin).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Thin skill wrapper over the existing kei-message jsonl mailbox so the
user (and agents) can talk between Claude Code sessions with @id syntax:
/msg read my inbox (to me or "all")
/msg @frontend text send to a session (identity = its cwd basename)
/msg all text broadcast
/msg list | who whole bus / known recipients
- kei-message.sh send now accepts a leading @name as the recipient
(first token only; a later @x stays literal). --to still works.
- skills/msg/SKILL.md documents the identity model (cwd-basename),
pull delivery (recipient's next turn via mailbox-inject hook), and the
first-contact discovery path (who / all).
- README skills count 68 -> 69.
Verified: @name/all/--to parsing (3 cases) + end-to-end send/inbox/who
via the live script in a sandbox HOME. Skill registered + discoverable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CRITICAL: lib-hooks.sh had an apostrophe ("user's") inside the jq
program's bash single-quote, closing the quote and producing a parse
error so EVERY install aborted at source time (install.sh:71). Caught
by a full minimal e2e (rc=2 then rc=0 after fix). Reworded the jq
comment to drop the apostrophe.
Audit blockers fixed:
- MANIFEST: drop cortex-ui (no such primitive) from 4 profiles + block;
lib-menu desc no longer references it. Profile resolution verified clean.
- lib-dev-hub-forgejo / -zoekt: source lib-launchd.sh (register_launchd
was undefined, so full-hub dev-hub install would fail at runtime).
- kei-message: portable 16-digit id. BSD date prints literal "N" for %N;
fall back to /dev/urandom. Verified numeric in both code paths.
- bootstrap non-TTY default cortex to minimal (matches install.sh; avoids
divergent curl-bash vs direct-install behaviour and 105-crate surprise).
- install.sh stamps ~/.claude/.kei-profile; bin/kei reads it (splash
showed "profile: ?" before, since .installed holds only primitive names).
- README hook count 38 to 54 (real: ls hooks star dot sh).
- web-install warns before it discards local edits in the managed clone.
Verified: 106 shell files bash -n clean; minimal e2e rc=0 (38 agents,
57 hooks, 69 skills, profile stamped, mailbox present).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Any Claude Code session can now message any other (not just Agent-Teams
teammates), without tmux. Append-only jsonl bus + a UserPromptSubmit hook that
pulls unread into each session's context per turn.
- scripts/kei-message.sh: `kei message send [--to <name|all>] <text>` / inbox /
list / channels. Identity = basename(cwd); broadcast channel "all".
- hooks/mailbox-inject.sh: UserPromptSubmit. Injects messages addressed to this
session (cwd-basename) or "all", since last turn; per-session cursor dedup;
first turn sets baseline (no history dump); never echoes own messages.
- bin/kei: `kei message ...` dispatch before splash.
- lib-scaffold: copy ALL scripts/*.sh on install (picks up kei-message.sh).
- settings-snippet: wire mailbox-inject under UserPromptSubmit.
Store: ~/.claude/mailbox/messages.jsonl. Bypass: KEI_MAILBOX_BYPASS=1.
Verified: addressed delivery, broadcast, first-turn no-dump, cursor dedup,
no self-echo (2-session simulation).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sleep/cortex setup were left as separate things the user had to discover. Make
the SessionStart first-run hook a single ordered post-install checklist that
Claude walks the user through: (1) /onboard projects → per-project agents,
(2) /sleep-setup → nightly REM (recommend local-only, no remote git needed),
(3) /cortex-setup (only if the cortex daemon primitive is installed). Confirm +
run each, skippable. Fires once (marker), then silent.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After install the user got no guidance to set up project agents — the summary
only mentioned /new-agent (single), never /onboard (scan all projects → create
a specialist per project). The installer is bash and can't launch a skill, so:
- New SessionStart hook first-run-onboard.sh: on the FIRST Claude Code session
after install, injects context nudging the user/Claude to run
`/onboard ~/Projects/*` (scan stack + create per-project agent, delegates to
/new-agent). Fires once (marker ~/.claude/.kei-firstrun-shown), then silent.
Reset: rm the marker.
- settings-snippet.json: wire the hook under SessionStart (matcher "*").
- lib-summary.sh next-steps: lead with `/onboard ~/Projects/*`, then /new-agent.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Users with pre-kit hooks that have no `matcher` field end up with matcher:null,
which Claude Code /doctor rejects ("Expected string, but received null") and
skips the whole settings file. The jq merge preserved null as-is, so every
reinstall re-surfaced the error. Now map(.matcher //= "") before group_by →
null/absent collapses to "" (match-all), one group per matcher.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- KEISEI ASCII logo (голубой) shown at install start via kei_banner().
- `[install]` prefix → тёмно-жёлтый (38;5;178); primitive names → голубой
(38;5;39) in both the plan and the per-primitive install lines.
- Language menu: code голубой, native name тёмно-жёлтый.
- lib-log COLOR now enables under curl|bash too: detect terminal via
`[ -t 1 ] || /dev/tty` (web-install tees stdout → -t 1 false → colors were
silently off). This is color detection, not an interactivity gate (pairs
with no -t 0 — does not violate tty-interactivity-gate rule).
Verified piped-under-pty: banner + gold [install] + blue/gold language menu
all render through the tee logfile.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The PATH-wiring step (~/.claude/bin, where the `kei` entry-point lives) was
gated on `[ -t 0 ] && [ -t 1 ]`. curl|bash tees stdout to a logfile so -t 1 is
false → pathway_install was skipped → `kei: command not found` after install.
Same tee/-t1 trap as the onboarding gates (this one lived in the top-level
install.sh, missed by the earlier variant grep which only scanned install/).
Eradicated the pattern across the tree:
install.sh pathway gate → [ -t 0 ]
lib-menu.sh profile-menu gate → [ -t 0 ]
lib-wizard.sh sleep-wizard gate → [ -t 0 ]
Verified piped-under-pty with /dev/tty reattach: .zshrc gets the kei-substrate
block (~/.claude/bin on PATH); non-interactive still skips.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Splash was cyan; rebrand to the blue/yellow palette: sky-blue (38;5;39) logo +
dim-blue separators, gold (38;5;220) brand line + field values. Version string
was stale (v0.16 → v0.38).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three issues a real curl|bash user hit:
1. CRASH: typing a word (e.g. "claude") at any menu → $((ans-1)) treats it as a
variable in bash arithmetic → "unbound variable" under set -u → install dies.
Added _onb_read_choice (numeric+range validation, re-prompt) for all 4 menus.
2. No Claude under subscription: the kit installs into Claude Code yet the wizard
offered only OpenAI Codex under subscription. Added claude-code provider
(bumped kei-registries submodule c559065→b904993) + made subscription the
default transport and claude-code the default provider — Enter,Enter,Enter
lands on Claude Code (no API key).
3. install died at line 178 for any no-key provider (claude-code/codex/local):
onboarding_run ended on a `&&` that is false when there are no auth keys →
returned 1 → set -e aborted. Added explicit `return 0`.
Plus per-step explanations (en+ru) and auto-select when a step has one option.
Verified piped-under-pty: Enter-defaults → Claude Code, junk input → re-prompt
(0 crashes), full install completes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
curl|bash makes bash read THIS script byte-by-byte from the pipe. A bare
`exec < /dev/tty` swapped stdin, then bash tried to read the NEXT line (the
`exec ./bootstrap.sh`) from the keyboard → infinite hang after "delegating to
bootstrap.sh", bootstrap never started, no profile/language menu.
Fix: merge the redirect INTO the bootstrap exec (one command), so the process
is replaced the instant stdin is swapped and bash never reads another script
byte from the (now-wrong) stdin. Verified: piped-under-pty buggy=HANG,
fixed=bootstrap starts with stdin=tty.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
web-install.sh tees stdout to a logfile (exec > >(tee) 2>&1), so -t 1 is false
even in an interactive curl|bash. The /dev/tty fix reattached stdin but the
wizard gates required BOTH -t 0 and -t 1, so onboarding (language select) and
bootstrap's profile wizard were silently skipped on the primary install path.
Prompts go to stderr and read from stdin — interactive stdin is the only real
requirement (already the proven pattern in lib-plan.sh confirm screen).
Gates now require interactive stdin only:
bootstrap.sh profile wizard
lib-onboarding.sh onboarding_should_run + preflight-continue prompt
lib-preflight.sh CLI-install offer prompt
lib-hooks.sh activate-hooks prompt
Non-interactive (CI / </dev/null / no /dev/tty) still skips — verified.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ -6,7 +6,7 @@ This document describes the plugin-format install path (v0.16+) and how it relat
```bash
# One-time
/plugin marketplace add KeiSei84/KeiSeiKit
/plugin marketplace add KeiSeiLab/KeiSeiKit-1.0
# Install
/plugin install keisei@keisei-marketplace
```
@ -50,7 +50,7 @@ Paths inside `hooks/hooks.json` use `${CLAUDE_PLUGIN_ROOT}` (expanded by Claude
**For plugin install:**
- Claude Code 2.1+ (check with `claude --version`)
- Network access to `github.com/KeiSei84/KeiSeiKit` on `/plugin marketplace add`
- Network access to `github.com/KeiSeiLab/KeiSeiKit-1.0` on `/plugin marketplace add`
**For the MCP server subset:**
- `@keisei/mcp-server` available from **keigit.com**
@ -76,7 +76,7 @@ Paths inside `hooks/hooks.json` use `${CLAUDE_PLUGIN_ROOT}` (expanded by Claude
## Feedback & bugs
Open an issue at [github.com/KeiSei84/KeiSeiKit/issues](https://github.com/KeiSei84/KeiSeiKit/issues). A well-formed problem description is already half the solution.
Open an issue at [github.com/KeiSeiLab/KeiSeiKit-1.0/issues](https://github.com/KeiSeiLab/KeiSeiKit-1.0/issues). A well-formed problem description is already half the solution.
You are the cost guardian. Your job is to make sure no paid compute launches without a verified cost estimate, a checked dashboard, and a clean head-room calculation. You stop runaway spend before it starts. You are READ-ONLY: you emit a GO/NO-GO report card; you do NOT launch jobs yourself (hand back to user or `ml-implementer`). **The $98.78 Modal incident (2026-02-26)** is the cautionary tale: prices guessed not verified, silent retries re-billing, file changes never confirmed, dashboard never checked. Every protocol below exists because of that day — never again.
You are the cost guardian. Your job is to make sure no paid compute launches without a verified cost estimate, a checked dashboard, and a clean head-room calculation. You stop runaway spend before it starts. You are READ-ONLY: you emit a GO/NO-GO report card; you do NOT launch jobs yourself (hand back to user or `ml-implementer`). **The $98.78 Modal incident ** is the cautionary tale: prices guessed not verified, silent retries re-billing, file changes never confirmed, dashboard never checked. Every protocol below exists because of that day — never again.
You are the cost guardian. Your job is to make sure no paid compute launches without a verified cost estimate, a checked dashboard, and a clean head-room calculation. You stop runaway spend before it starts. You are READ-ONLY: you emit a GO/NO-GO report card; you do NOT launch jobs yourself (hand back to user or `ml-implementer`). **The $98.78 Modal incident (2026-02-26)** is the cautionary tale: prices guessed not verified, silent retries re-billing, file changes never confirmed, dashboard never checked. Every protocol below exists because of that day — never again.
You are the cost guardian. Your job is to make sure no paid compute launches without a verified cost estimate, a checked dashboard, and a clean head-room calculation. You stop runaway spend before it starts. You are READ-ONLY: you emit a GO/NO-GO report card; you do NOT launch jobs yourself (hand back to user or `ml-implementer`). **The $98.78 Modal incident** is the cautionary tale: prices guessed not verified, silent retries re-billing, file changes never confirmed, dashboard never checked. Every protocol below exists because of that day — never again.
You are the Modal compute orchestrator. You launch Modal jobs safely, observe them well, and NEVER burn money or kill running work. Two incidents shape every rule below.
$98.78 Modal Incident (2026-02-26): promised $27, spent $98.78 in one session. Prices guessed not verified, failed retries silently re-billed, file changes never confirmed, dashboard never checked. Every cost rule exists because of that day.
$98.78 Modal Incident: promised $27, spent $98.78 in one session. Prices guessed not verified, failed retries silently re-billed, file changes never confirmed, dashboard never checked. Every cost rule exists because of that day.
anti-stop guard Incident (2026-03-29): stopped a 1.4-hour training run for a non-critical bug. Cost: 1.4 hours A10G + restart + re-warmup. Every kill rule exists because of that day.
anti-stop guard Incident: stopped a 1.4-hour training run for a non-critical bug. Cost: 1.4 hours A10G + restart + re-warmup. Every kill rule exists because of that day.
Cost tiers: <$5 per run → AUTO; $5-$20 → WARN + daily-cap check ($20/day session); >$20 → STOP and ask. Always state estimate in dollars BEFORE launch: "Estimate: $X.XX (= N_gpus × hours × $/hr/gpu)". GPU compat: A10G torch>=2.0 (~$1.10/hr), H100 torch>=2.1 (~$4.50/hr), B200 torch>=2.6 (~$8/hr). Always verify on pricing page — rates change.
/// prior versions claimed this was an "aggregate" cap, which was always
/// wrong. Aggregate-deadline impl is deferred; for now the per-step
/// semantics are documented honestly so operators pick a sane value.
constSAFE_TOOL_TIMEOUT_SECS: u64=60;
#[derive(Deserialize, Default)]
structPolicyChain{
#[serde(default)]
bash: ChainSpec,
#[serde(default)]
edit: ChainSpec,
#[serde(default)]
write: ChainSpec,
}
#[derive(Deserialize, Default)]
structChainSpec{
#[serde(default)]
chain: Vec<String>,
}
/// MCP tool descriptors — appended to `tools/list` by `handlers::tools::list`.
pubfndescriptors()-> Vec<Value>{
vec![
json!({
"name": "kei_bash",
"description": "Run a shell command after running KeiSeiKit's [bash] policy chain (no-github-push, safety-guard, destructive-guard). Blocks on hook exit 2 with the hook's stderr surfaced as the MCP error message. Use this instead of native shell on non-Claude CLIs to inherit Claude Code's safety enforcement.",
"inputSchema": {
"type": "object",
"properties": {
"command": {"type": "string","description": "Shell command to execute"},
"cwd": {"type": "string","description": "Optional working directory; defaults to $PWD"}
},
"required": ["command"]
}
}),
json!({
"name": "kei_edit",
"description": "Modify a file (replace old_string with new_string) after running KeiSeiKit's [edit] policy chain (citation-verify, numeric-claims-guard). Blocks unverified academic citations and numeric claims without evidence markers.",
"description": "Write content to a file after running KeiSeiKit's [write] policy chain (citation-verify, numeric-claims-guard). Blocks unverified academic citations and numeric claims without evidence markers.",
"inputSchema": {
"type": "object",
"properties": {
"file_path": {"type": "string"},
"content": {"type": "string"}
},
"required": ["file_path","content"]
}
}),
]
}
/// Top-level dispatch entry — called from `handlers::tools::call` when the
/// tool name matches one of the three `kei_*` built-ins.
/// sub-agents" gap — any CLI with MCP support gets the spawn capability.
fnspawn_agent_descriptor()-> Value{
json!({
"name": "spawn_agent",
"description": "Spawn a KeiSeiKit agent as a sub-agent through any configured LLM CLI backend. Reads ~/.claude/agents/<name>.md, composes with the task, and execs the chosen backend non-interactively. Backend resolution: explicit `on` arg → agent manifest's `provider` → ~/.claude/config/primary.toml → claude.",
"inputSchema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Agent name (looked up in ~/.claude/agents/<name>.md)"
},
"task": {
"type": "string",
"description": "The task / question to give the agent"
@ -63,7 +63,7 @@ cd _primitives/_rust && cargo test --workspace 2>&1 | tail -5
## GitHub Releases status
6 tags pushed. Release workflow in `release.yml` triggers on tag push. Expected: 6 releases × (3 Rust tarballs + 5 MCP binaries + sha256 each) = ~48 assets. Check `github.com/KeiSei84/KeiSeiKit/releases` on wake — all tags should have attached assets within 10 min of push per prior v0.22.3 smoke. CI was re-triggered after Pro upgrade; confirm status there.
6 tags pushed. Release workflow in `release.yml` triggers on tag push. Expected: 6 releases × (3 Rust tarballs + 5 MCP binaries + sha256 each) = ~48 assets. Check `github.com/KeiSeiLab/KeiSeiKit-1.0/releases` on wake — all tags should have attached assets within 10 min of push per prior v0.22.3 smoke. CI was re-triggered after Pro upgrade; confirm status there.
STR_PREFLIGHT_FAILED="Preflight failed — provider may not work."
STR_PREFLIGHT_CONTINUE="Continue anyway? [y/N]"
# Wizard explanations + input validation
STR_PICK_INVALID="please type one of the numbers shown"
STR_EXPLAIN_TRANSPORT="How the agents reach the AI. subscription = log in with your plan, no API key (Claude Code is option 1); direct-api = your own API key. Press Enter for the default."
STR_EXPLAIN_PROVIDER="Which AI service. Option 1 is the recommended default — press Enter."
STR_EXPLAIN_MODEL="Default model the agents use. Option 1 is the recommended default — press Enter."
# Stack profile + hook-pack picker (step 6)
STR_PICK_STACK="Pick your stack profile (selects which hooks + agents install):"
STR_PREFLIGHT_FAILED="Preflight упал — провайдер может не работать."
STR_PREFLIGHT_CONTINUE="Продолжить всё равно? [y/N]"
# Пояснения мастера + валидация ввода
STR_PICK_INVALID="введите один из показанных номеров"
STR_EXPLAIN_TRANSPORT="Как агенты обращаются к ИИ. subscription = вход по подписке, без API-ключа (Claude Code — вариант 1); direct-api = свой API-ключ. Нажми Enter для варианта по умолчанию."
STR_EXPLAIN_PROVIDER="Какой ИИ-сервис. Вариант 1 — рекомендуемый по умолчанию, нажми Enter."
STR_EXPLAIN_MODEL="Модель, которую используют агенты. Вариант 1 — рекомендуемый по умолчанию, нажми Enter."
echo"${STR_EXPLAIN_TRANSPORT:-How the agents reach the AI. subscription = log in with your plan (no API key); direct-api = your own API key. Default is fine for most.}" >&2
printf'%s''{"status":"no-api","note":"headers-only per API call; see x.ai dashboard","dashboard":"https://x.ai"}'
}
probe_agy(){
printf'%s''{"status":"broken-api","note":"interactive /usage shows 100% (forum-verified bug); use Google Cloud Console","dashboard":"https://console.cloud.google.com/apis/api/generativelanguage.googleapis.com/quotas"}'