Bundle hooks, MCP bridge, and skills as a plugin for Claude and Codex - #120
Draft
big-guy wants to merge 4 commits into
Draft
Bundle hooks, MCP bridge, and skills as a plugin for Claude and Codex#120big-guy wants to merge 4 commits into
big-guy wants to merge 4 commits into
Conversation
big-guy
force-pushed
the
hooks-install-rework
branch
from
May 28, 2026 05:55
c97d799 to
5f5bfb5
Compare
big-guy
force-pushed
the
hooks-install-rework
branch
4 times, most recently
from
June 4, 2026 05:07
264649b to
2cb74ef
Compare
…in skills
The DEFAULT_HARNESS_SYSTEM_PROMPT used to enumerate every harness-control
MCP tool and inline its workflow guidance (clickables-first click pattern,
create_shell vs Bash heuristic, how to write a good initialPrompt). That
content is always-on context every Claude turn pays for, even when the
task has nothing to do with browser tabs or shell management.
Move the workflow content into three skills shipped in the bundled
plugin under resources/plugins/harness-status/skills/:
- harness-browser — clickables → click_tab pattern, screenshot as
verification-only, scroll/type workflow
- harness-shell — create_shell vs Bash, list before spawning,
read_shell_output filtering
- harness-worktree — when to spin off a worktree, how to write an
initialPrompt the new session can actually act on
Claude Code auto-discovers each skill via the same --plugin-dir flag
that already loads our hooks and harness-control MCP server, so no
additional wiring is needed. Skills are pulled in on-demand by
description match, so unrelated turns pay zero token cost.
Shrinks DEFAULT_HARNESS_SYSTEM_PROMPT from ~3.8KB to a 418-char
identity line that points at the skills, and trims
DEFAULT_HARNESS_SYSTEM_PROMPT_MAIN to drop the bits that duplicated
harness-shell content. Tool-level descriptions on the MCP side still
cover the "what does this do" surface — the skills layer on the
"when and why."
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move Claude status hooks and the harness-control MCP bridge into a bundled Claude Code plugin at resources/plugins/harness-status/. Every Claude spawn (xterm + json-mode) now passes --plugin-dir, so there's no user-file install to consent to and no merge against user-authored hooks. The plugin tree's .mcp.json + servers/mcp-bridge.js replace the previous per-terminal mcp.json materialization under userData. The plugin tree also doubles as a Codex plugin marketplace (.agents/plugins/marketplace.json wraps the same harness-status dir). Codex 0.133+ reads the .claude-plugin/plugin.json manifest, hooks.json, .mcp.json and skills/ from the same tree without modification. codex-plugin.ts scaffolds the boot-time install path (marketplace add + plugin add) but is not yet wired into the Codex consent flow — that landing is a follow-up. Other changes: - One-shot boot migration strips legacy Harness entries from ~/.claude/settings.json + per-worktree .claude/settings.local.json (gated by config.hooksMigratedToPlugin). - Hooks consent slice is now Codex-only; Claude needs no consent. - New harness-control-env.ts builds the MCP server env (port, token, per-terminal scope) so both spawn paths set it consistently. - harnessPluginMarketplaceRoot() resolves the marketplace root for the forthcoming Codex install path.
…overrides
Codex consumes the same plugin tree as Claude (resources/plugins/
harness-status/), but the integration path is different in three
fundamental ways the implementation had to discover empirically:
1. Codex does NOT interpolate `${VAR}` in .mcp.json — not user env
(${HARNESS_NODE_EXEC}), not its own injected vars (${CLAUDE_PLUGIN_ROOT}).
Templates are passed verbatim to execve, and the plugin's source
.mcp.json (which Claude expands correctly via --plugin-dir) would
fail with "MCP startup failed: No such file or directory" if Codex
tried to spawn it. We neutralize the cached .mcp.json post-install
to `{"mcpServers": {}}` and register harness-control per-Codex-spawn
via `-c mcp_servers.harness-control.command="..."` literal overrides.
2. Codex spawns MCP subprocesses with a stripped env — only
HOME/PATH/PWD/USER/LANG inherit. The HARNESS_* vars set on the
agent PTY don't reach the bridge. The per-spawn `-c` override
bakes literal values directly into the MCP env block.
3. Codex's `~` resolution uses getpwuid_r (system passwd database)
rather than $HOME on macOS, so an isolated $HOME doesn't reach the
Codex binary. We set process.env.CODEX_HOME = $HOME/.codex at boot
when not already set — idempotent for the common case, fixes
isolation for dev sessions, propagates to all child processes.
Install path:
- `hooks:accept` IPC runs `codex plugin marketplace add <bundled-root>`
+ `codex plugin add harness-status@harness`. Both write to
$CODEX_HOME/config.toml; cleanly reversible via the matching remove
commands.
- Boot logic re-runs the install every launch when consent === 'accepted'.
Codex's plugin cache never auto-refreshes, so re-running is how we
propagate new Harness releases to the cache.
- Spawn args: agents/codex.ts buildSpawnArgs appends -c overrides for
command, args, and env when opts.harnessControl is set.
Verification (Settings → Codex → "Harness status plugin"):
- Plugin enabled in Codex (`codex plugin list --marketplace harness`)
- Hooks file present in cache
- Hooks trusted by Codex (key-based presence check —
`[hooks.state."harness-status@harness:hooks/hooks.json:<event>:N:M"]`
with non-empty trusted_hash for every Codex-recognized event from
hooks.json; Notification is filtered out as Claude-only).
Trust:
- We do NOT auto-trust. Codex's trust prompt is TUI-only; there's no
CLI command or flag to grant trust non-interactively. The Settings
card surfaces inline guidance when hooks aren't yet trusted: open a
Codex tab, accept the "Hooks need review / Trust all and continue"
prompt, then Re-check.
Uninstall:
- `codex plugin remove` + `codex plugin marketplace remove`
- Strip [hooks.state] entries from $CODEX_HOME/config.toml by
`harness-status@harness:` key prefix (Codex's `trusted_hash` is over
a normalized form we can't reproduce, so key-based matching is the
reliable signal). A reinstall re-prompts for trust review rather
than silently inheriting prior consent via content-hash match.
UI:
- Install prompt lives as an in-tab overlay in XTerminal.tsx
(`agentKind === 'codex' && hooksConsent === 'pending'`), mirroring
the Claude "Switch to Chat mode" hint. No top-of-app banner —
Claude-only users never see it.
- Settings card shows the actual CLI commands Harness runs, the
verification panel, and a Re-check button.
- Card title: "Harness status plugin" (it's a plugin Harness ships
for Codex to call back into Harness, not a Codex-authored plugin).
Codex agent module (agents/codex.ts):
- Removed installHooks/uninstallHooks/hooksInstalled (~150 lines of
dead code from the pre-plugin ~/.codex/hooks.json install path).
stripHooksFromWorktree reduced to a one-line delegate satisfying
the AgentModule contract; effectively dead post-migration.
- Sessions dir lookup honors CODEX_HOME.
Migration:
- `codexPluginMigrated` flag gates a one-shot strip of legacy
~/.codex/hooks.json + per-worktree .codex/hooks.json Harness entries
on first boot post-upgrade. Inferred consent (positive legacy strip
→ 'accepted') so prior consenters don't get re-prompted.
Onboarding:
- Move the Codex plugin install prompt from a top-level onboarding step
into an inline sub-section under the agent picker, mirroring the
Claude interface (chat/terminal) toggle. Onboarding collapses from 4
steps to 3 so Claude-only users don't see a Codex question they
don't need to answer.
Add SessionStart to the Claude status plugin's hook set, mirroring Codex. A freshly started/resumed session derives to 'waiting', and its payload carries session_id so the generic discovery in tailLog picks up the session ID before the first prompt. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
big-guy
force-pushed
the
hooks-install-rework
branch
from
June 4, 2026 21:49
2cb74ef to
63259e6
Compare
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.
Fixes #44
This is an unblocking refactor to more easily support multiple accounts with Claude.
For Claude, we do not need to modify anything.
For Codex, we still need to modify global files, but we can do this with codex commands vs editing the files manually.
On uninstall, we still edit files manually for codex.
NOTE: Testing this can be annoying because when you run Harness, it deletes things from the global configuration. When you switch back to an older branch, the hooks are not re-installed.
I haven't gone through the onboarding/fresh install step.
Summary
Three commits that move Harness's status-detection infrastructure from per-agent ad-hoc installs (writing into
~/.claude/settings.jsonand~/.codex/hooks.json) into a single bundled plugin tree atresources/plugins/harness-status/consumed by both agents.--plugin-dir <bundled-path>on every spawn (xterm + json-mode). No user-file install, no consent prompt, no merge against user-authored hooks.codex plugin marketplace add+codex plugin addat consent time. Gated by an in-tab install hint that only appears for actual Codex sessions (no top-of-app banner).skills/dir so both agents auto-discover them.What changed for Codex specifically
Migration
Two one-shot strips run on first boot post-upgrade, gated by persisted flags so they don't re-run:
Test plan