Repository navigation
Add Claude Code and Codex plugins backed by one hook table - #14
Merged
Merged
Conversation
The fallback action now carries a classified failure (timeout, aborted, HTTP status and error code, or an error name) so local logs distinguish a client timeout from a server error or a recall deadline. Messages, URLs and bodies are never copied.
Claude Code and Codex can now install Crosshead as a plugin from this repository's marketplace (.claude-plugin/marketplace.json and .agents/plugins/marketplace.json). Each plugin contains only its hooks, a setup skill and a POSIX launcher that runs the installed runtime's existing hook entrypoints; it never bundles, downloads or updates the runtime. Without a configured runtime, SessionStart prints one setup line and every hook exits 0. src/runtime/host-hook-spec.ts is the single source for hook events, matchers, status messages and timeouts. The settings.json/hooks.json installer and the generated plugin files (scripts/host-plugins.mjs, checked by npm run plugins:check and a unit test) both render from it. crosshead install claude-code|codex and quickstart take --hook-mode auto|plugin|settings. auto detects an enabled plugin. In plugin mode the installer writes no host hooks and idempotently removes the ones it wrote before. The hook entrypoint skips the duplicate registration, so each event is handled once even before migration. crosshead doctor reports the active mode per host and flags duplicate or missing registrations. quickstart reuses an already running daemon, so setting up a second host no longer fails with "Detached daemon exited before becoming ready". scripts/test-host-plugins.sh runs the real Claude Code and Codex CLIs in a disposable container against local fixtures.
In plugin mode the installer intentionally writes no host hook files, so the adapter automation checks no longer report them missing. They point to the claude_code_hooks/codex_hooks checks, which verify the plugin.
Contributor
Author
Independent review: changes requiredThe structure holds up. The plugins are thin, there is one hook table, the legacy layout is byte-identical, and the release tarball is unchanged. Typecheck, High
Medium
Low
|
… charged The recorded failure code passes the same identifier check as the error name. The fallback warning now follows the failure class: a server deadline that reports nothing charged says so, a 4xx names the rejection, and only timeouts, network failures and unconfirmed server errors keep the may-still-settle caution. A lifecycle test asserts that the fallback action records details.failure.
The daemon's health response now reports its entry script. When quickstart finds a healthy daemon running a different release than the installed one, it restarts it on the installed release through the ownership-checked stop/start path, and reports the mismatch when a safe restart is not possible (legacy ownership, organizational worker). --port applies on start and restart; a current daemon on another port is reported instead of silently kept.
- Dedup checks exactly the settings files the host loads: for Claude Code the user settings and the session's CLAUDE_PROJECT_DIR (plus the git-root local file), with no ancestor walk; for Codex the hooks.json of the CODEX_HOME it ran with. Doctor uses the same lookup. - Legacy hooks are detected by counting Crosshead commands; empty arrays, empty groups and null groups never count and never throw. - Plugin mode, and removal of installer hooks, require the plugin enabled at user scope. Project-only enablement keeps settings mode; Codex project config counts only for trusted projects. Doctor explains both. - Codex keeps its installer hooks until a plugin invocation has been recorded, so untrusted plugin hooks never leave a session uncaptured; doctor points to /hooks trust when none has been seen. - The setup skill is user-invoked only, and SessionStart notices are user-visible systemMessages with no model context. - The launcher only locates the runtime home (empty variables unset, two-homes conflict reported); the runtime resolves --claude-home/--codex-home installs itself, and a capability marker in the hook wrappers identifies runtimes that support plugin dispatch. - Migration never deletes a settings file, keeps unknown Codex hooks.json keys, refuses symlinked files before writing anything, and the TOML reader handles comments and matches only crosshead@crosshead. - SessionStart also matches clear; plugin content is pinned per version.
Contributor
Author
Review findings: statusFixes are in b63a7a8 (quickstart daemon), e007b2a (everything else, with tests and docs) and 632b49a (e2e container uses
Verification
|
…o feat/host-plugins-20261106
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add native Claude Code and Codex plugins with explicit, user-invoked setup and hooks that run the locally installed Crosshead runtime. Plugin hooks perform no automatic downloads. The setup skill requires consent, verifies the installer digest and connects the runtime in plugin mode.
Migration preserves unrelated settings and hooks, refuses symlinked managed files before writing, and removes global registrations only for a user-scope plugin. Duplicate suppression applies to the matching event and source, including fork sessions. Codex migration waits for a receipt from the current plugin version and the actual Codex home, so stale trust evidence cannot remove working hooks. Doctor explains inactive plugins and pending trust. Plugin 1.0.1 content is hash-pinned.
Published as v0.5.0-alpha.9 (source c3648e3). Validation: 470/470 tests; typecheck, boundary, build, generated-file and release smoke checks. Real Claude Code 2.1.319 and Codex 0.167.0 journeys pass in a bounded disposable Docker container and on macOS CI: fresh setup, no-runtime notices, one capture per event with both registrations, migration, uninstall and doctor. Containers and volumes return to their baseline.
Coverage limits: model APIs and installer downloads are local fixtures; setup commands are exercised directly. Interactive consent and /hooks UI, native compaction, hosted identity registration and the production installer are separate acceptance paths. Public installation passed after deployment: GitHub workflow 42337328354 completed fresh installs and alpha.6 upgrades on Linux/macOS, and the public marketplace/installer Docker journey passed all nine checks. Desktop/mobile checks passed for all four setup pages.