Summary
Currently all 6 Claude Code hooks and 2 MCP servers are Python scripts that get deployed to .claude/hooks/ and .claude/mcp/ during crosslink init. This works, but introduces a hard Python dependency with significant complexity. This issue proposes migrating them to crosslink hook <name> and crosslink mcp <name> subcommands in the main Rust binary.
Current pain points
- ~200 lines of
detect_python_prefix heuristics handling uv, poetry, .venv, pipenv, system python — fragile across environments
- Silent failures: the hook shell wrappers (
if [ -f "$HOOK" ]; then ... else exit 0; fi) swallow errors when Python isn't found or configured correctly
- File drift: deployed
.py files can diverge from embedded versions, requiring workflow diff machinery and crosslink init --force to update after upgrades
- Startup latency: Python interpreter startup (~30-50ms) × 6 hooks adds up on every prompt/edit cycle
- Zero test coverage on hook logic — separate Python test harness would be needed vs. integrating with existing
cargo test
Proposed approach
Hooks → crosslink hook <name>
Replace settings.json entries like:
"command": "HOOK=\"$(git rev-parse --show-toplevel)/.claude/hooks/post-edit-check.py\"; if [ -f \"$HOOK\" ]; then __PYTHON_PREFIX__ \"$HOOK\"; else exit 0; fi"
with:
"command": "crosslink hook post-edit"
Each hook reads JSON from stdin and writes JSON to stdout — same interface, just handled by the Rust binary.
MCP servers → crosslink mcp <name>
Same idea: crosslink mcp safe-fetch and crosslink mcp knowledge as stdio JSON-RPC servers. Needs a Rust MCP implementation (or thin protocol layer).
Trade-offs
|
Python (status quo) |
Rust subcommands |
| Python dependency |
Required |
Eliminated |
| Startup latency |
~30-50ms/hook |
~1ms/hook |
| Deployment |
Copy files + template substitution |
Just write settings.json |
| Drift |
Possible, needs workflow diff |
Impossible — always matches binary |
| User customization |
Edit .py directly |
Via config/rules (already exists) |
| Testing |
Separate harness needed |
Integrated cargo test |
| Init complexity |
Python prefix detection, file copy |
Minimal |
| Iteration speed |
Edit .py, no rebuild |
Requires cargo build |
Migration path
Could be incremental — migrate one hook at a time, starting with the simplest (e.g., heartbeat), while keeping Python fallback for the others. The crosslink init command already knows about all hooks, so it can generate the right settings.json entries based on what's been migrated.
Open questions
- Is the iteration speed trade-off acceptable? (hooks change less frequently now that they're stable)
- For MCP servers: use an existing Rust MCP crate, or roll a minimal stdio JSON-RPC layer?
- Should we keep a "user override" mechanism (e.g., if
.claude/hooks/post-edit-check.py exists, prefer it over the built-in)?
Summary
Currently all 6 Claude Code hooks and 2 MCP servers are Python scripts that get deployed to
.claude/hooks/and.claude/mcp/duringcrosslink init. This works, but introduces a hard Python dependency with significant complexity. This issue proposes migrating them tocrosslink hook <name>andcrosslink mcp <name>subcommands in the main Rust binary.Current pain points
detect_python_prefixheuristics handling uv, poetry, .venv, pipenv, system python — fragile across environmentsif [ -f "$HOOK" ]; then ... else exit 0; fi) swallow errors when Python isn't found or configured correctly.pyfiles can diverge from embedded versions, requiringworkflow diffmachinery andcrosslink init --forceto update after upgradescargo testProposed approach
Hooks →
crosslink hook <name>Replace
settings.jsonentries like:with:
Each hook reads JSON from stdin and writes JSON to stdout — same interface, just handled by the Rust binary.
MCP servers →
crosslink mcp <name>Same idea:
crosslink mcp safe-fetchandcrosslink mcp knowledgeas stdio JSON-RPC servers. Needs a Rust MCP implementation (or thin protocol layer).Trade-offs
workflow diffcargo testcargo buildMigration path
Could be incremental — migrate one hook at a time, starting with the simplest (e.g.,
heartbeat), while keeping Python fallback for the others. Thecrosslink initcommand already knows about all hooks, so it can generate the rightsettings.jsonentries based on what's been migrated.Open questions
.claude/hooks/post-edit-check.pyexists, prefer it over the built-in)?