Skip to content

Commit 2d325a7

Browse files
authored
Merge pull request #9 from rand/feat/go-hooks
feat: Go hook binaries, event coordination, and rlm-core default enablement
2 parents ad7e2c7 + 75fd7fb commit 2d325a7

43 files changed

Lines changed: 2061 additions & 91 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude-plugin/marketplace.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "rlm-claude-code",
3-
"description": "Marketplace for RLM-Claude-Code: Recursive Language Model with intelligent multi-provider routing",
3+
"description": "Marketplace for RLM-Claude-Code: Recursive Language Model with Go hook binaries, cross-plugin event system, and intelligent multi-provider routing",
44
"owner": {
55
"name": "rand",
66
"url": "https://github.com/rand"
@@ -9,8 +9,8 @@
99
{
1010
"name": "rlm-claude-code",
1111
"source": "./",
12-
"description": "Recursive Language Model integration for Claude Code - intelligent multi-provider routing and unbounded context handling",
13-
"version": "0.5.0"
12+
"description": "Recursive Language Model integration for Claude Code - Go binary hooks (~5ms startup), cross-plugin event coordination (DP/RLM), version-aware config migration, and intelligent multi-provider routing",
13+
"version": "0.6.0"
1414
}
1515
]
1616
}

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "rlm-claude-code",
33
"description": "Recursive Language Model integration for Claude Code - intelligent multi-provider routing and unbounded context handling",
4-
"version": "0.5.0",
4+
"version": "0.6.0",
55
"author": {
66
"name": "Rand",
77
"url": "https://github.com/rand"

.github/workflows/build-hooks.yml

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
name: Build Hook Binaries
2+
3+
on:
4+
push:
5+
branches: [main]
6+
tags: ['v*']
7+
pull_request:
8+
branches: [main]
9+
10+
permissions:
11+
contents: write
12+
13+
jobs:
14+
build:
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
21+
- name: Set up Go
22+
uses: actions/setup-go@v5
23+
with:
24+
go-version: '1.23'
25+
cache: true
26+
27+
- name: Install dependencies
28+
run: go mod download
29+
30+
- name: Run tests
31+
run: go test -v -race ./...
32+
33+
- name: Build all platforms
34+
run: |
35+
mkdir -p bin
36+
37+
CMDS="session-init complexity-check trajectory-save"
38+
39+
for cmd in $CMDS; do
40+
[ -d "cmd/$cmd" ] || continue
41+
42+
echo "Building $cmd..."
43+
44+
GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 go build -ldflags="-s -w" -trimpath \
45+
-o "bin/${cmd}-darwin-arm64" "./cmd/$cmd"
46+
GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -trimpath \
47+
-o "bin/${cmd}-darwin-amd64" "./cmd/$cmd"
48+
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -trimpath \
49+
-o "bin/${cmd}-linux-amd64" "./cmd/$cmd"
50+
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -ldflags="-s -w" -trimpath \
51+
-o "bin/${cmd}-linux-arm64" "./cmd/$cmd"
52+
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -trimpath \
53+
-o "bin/${cmd}-windows-amd64.exe" "./cmd/$cmd"
54+
done
55+
56+
ls -la bin/
57+
58+
- name: Upload artifacts
59+
uses: actions/upload-artifact@v4
60+
with:
61+
name: hook-binaries
62+
path: bin/
63+
retention-days: 7
64+
65+
release:
66+
needs: build
67+
if: startsWith(github.ref, 'refs/tags/')
68+
runs-on: ubuntu-latest
69+
70+
steps:
71+
- name: Checkout
72+
uses: actions/checkout@v4
73+
74+
- name: Download artifacts
75+
uses: actions/download-artifact@v4
76+
with:
77+
name: hook-binaries
78+
path: bin/
79+
80+
- name: Create archives
81+
run: |
82+
VERSION=${GITHUB_REF#refs/tags/}
83+
mkdir -p dist
84+
85+
cd bin
86+
87+
tar -czvf "../dist/hooks-${VERSION}-darwin-arm64.tar.gz" *-darwin-arm64 2>/dev/null || true
88+
tar -czvf "../dist/hooks-${VERSION}-darwin-amd64.tar.gz" *-darwin-amd64 2>/dev/null || true
89+
tar -czvf "../dist/hooks-${VERSION}-linux-amd64.tar.gz" *-linux-amd64 2>/dev/null || true
90+
tar -czvf "../dist/hooks-${VERSION}-linux-arm64.tar.gz" *-linux-arm64 2>/dev/null || true
91+
zip "../dist/hooks-${VERSION}-windows-amd64.zip" *-windows-amd64.exe 2>/dev/null || true
92+
93+
cd ..
94+
ls -la dist/
95+
96+
- name: Create Release
97+
uses: softprops/action-gh-release@v2
98+
with:
99+
files: dist/*
100+
generate_release_notes: true
101+
draft: false
102+
prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') || contains(github.ref, 'rc') }}
103+
env:
104+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,6 @@ htmlcov/
5656
.DS_Store
5757
Thumbs.db
5858
.hypothesis/
59+
60+
# Go build output
61+
bin/

CHANGELOG.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Changelog
2+
3+
## [0.6.0] - 2026-02-02
4+
5+
### Added
6+
- Go hook binaries replacing Python scripts (~5ms vs ~500ms startup)
7+
- Cross-plugin event system (`~/.claude/events/`) for DP↔RLM coordination
8+
- JSON Schema definitions for all event types
9+
- Python event emission/consumption helpers (`src/events/`)
10+
- Version-aware config migration (V1→V2) preserving user customizations
11+
- Platform-aware hook dispatcher with fallback chain
12+
- GitHub Actions CI for cross-compilation (5 platforms)
13+
- Unit tests for hookio, events, and config packages
14+
15+
### Changed
16+
- **rlm-core enabled by default** (`USE_RLM_CORE=true`) — set `RLM_USE_CORE=false` to disable
17+
- rlm-core PyO3 bindings now support metadata, provenance, and embedding parameters
18+
- rlm-core uses separate `-core.db` file to avoid schema conflicts with Python SQLite
19+
- hooks.json now uses Go binaries + prompt-based hooks
20+
- Complexity check responds to all DP phases (not just spec/review)
21+
- RLM orchestrator agent informed about parallel tool call behavior
22+
- session-init verifies rlm_core is importable on startup
23+
24+
### Fixed
25+
- Hyperedge queries used `row["type"]` instead of `row["edge_type"]` matching SQLite schema
26+
27+
### Deprecated
28+
- Python hook scripts moved to `scripts/legacy/`
29+
- Set `RLM_USE_LEGACY_HOOKS=1` to use legacy Python hooks

Makefile

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Makefile for Claude Code plugin hooks
2+
# Usage:
3+
# make dev - Build for current platform (fast)
4+
# make all - Build for all platforms
5+
# make test - Run tests
6+
# make clean - Remove built binaries
7+
8+
GO := go
9+
GOFLAGS := -ldflags="-s -w" -trimpath
10+
11+
# Commands to build (add more as you create them)
12+
CMDS := session-init complexity-check trajectory-save
13+
14+
# Platforms to build for
15+
PLATFORMS := darwin/arm64 darwin/amd64 linux/amd64 linux/arm64 windows/amd64
16+
17+
.PHONY: all dev clean test lint help
18+
19+
# Default: build for current platform
20+
dev:
21+
@mkdir -p bin
22+
@for cmd in $(CMDS); do \
23+
if [ -d "cmd/$$cmd" ]; then \
24+
echo "Building $$cmd..."; \
25+
$(GO) build $(GOFLAGS) -o bin/$$cmd ./cmd/$$cmd; \
26+
fi; \
27+
done
28+
@echo "Built binaries in bin/"
29+
@ls -la bin/
30+
31+
# Build for all platforms
32+
all:
33+
@mkdir -p bin
34+
@for cmd in $(CMDS); do \
35+
if [ -d "cmd/$$cmd" ]; then \
36+
echo "Building $$cmd for all platforms..."; \
37+
GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 $(GO) build $(GOFLAGS) -o bin/$$cmd-darwin-arm64 ./cmd/$$cmd; \
38+
GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 $(GO) build $(GOFLAGS) -o bin/$$cmd-darwin-amd64 ./cmd/$$cmd; \
39+
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 $(GO) build $(GOFLAGS) -o bin/$$cmd-linux-amd64 ./cmd/$$cmd; \
40+
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 $(GO) build $(GOFLAGS) -o bin/$$cmd-linux-arm64 ./cmd/$$cmd; \
41+
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 $(GO) build $(GOFLAGS) -o bin/$$cmd-windows-amd64.exe ./cmd/$$cmd; \
42+
fi; \
43+
done
44+
@echo "Built binaries:"
45+
@ls -la bin/
46+
47+
# Run tests
48+
test:
49+
$(GO) test -v -race ./...
50+
51+
# Run linter (requires golangci-lint)
52+
lint:
53+
@command -v golangci-lint >/dev/null 2>&1 || { echo "Install: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest"; exit 1; }
54+
golangci-lint run ./...
55+
56+
# Clean build artifacts
57+
clean:
58+
rm -rf bin/
59+
60+
# Install development tools
61+
install-tools:
62+
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
63+
64+
# Show help
65+
help:
66+
@echo "Available targets:"
67+
@echo " dev - Build for current platform (fast)"
68+
@echo " all - Build for all platforms"
69+
@echo " test - Run tests"
70+
@echo " lint - Run linter"
71+
@echo " clean - Remove built binaries"
72+
@echo " install-tools - Install development tools"
73+
@echo ""
74+
@echo "Commands being built: $(CMDS)"
75+
@echo ""
76+
@echo "To add a new command, create cmd/<name>/main.go and add to CMDS"
77+
78+
# Quick test of hooks
79+
test-hooks: dev
80+
@echo "Testing session-init..."
81+
@echo '{"session_id":"test","source":"startup"}' | HOOK_DEBUG=1 ./bin/session-init || true

README.md

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Transform Claude Code into a Recursive Language Model (RLM) agent with intelligent orchestration, unbounded context handling, persistent memory, and REPL-based decomposition.
44

5-
**rlm-core integration**: This project optionally uses [rlm-core](https://github.com/rand/loop) as the unified RLM orchestration library, providing shared implementations with [recurse](https://github.com/rand/recurse). When rlm-core is installed and `RLM_USE_CORE=true`, Rust-based pattern classification offers 10-50x faster performance. Falls back to Python when unavailable.
5+
**rlm-core integration**: This project uses [rlm-core](https://github.com/rand/loop) by default as the unified RLM orchestration library, providing shared implementations with [recurse](https://github.com/rand/recurse). rlm-core provides Rust-based pattern classification (10-50x faster) via PyO3 bindings. Falls back to Python automatically when rlm-core is not installed.
66

77
## What is RLM?
88

@@ -40,9 +40,9 @@ uv sync --all-extras
4040
uv run pytest tests/ -v
4141
```
4242

43-
### Optional: Building rlm-core for Performance
43+
### Building rlm-core (Enabled by Default)
4444

45-
For 10-50x faster pattern classification, build the [rlm-core](https://github.com/rand/loop) Rust library with Python bindings:
45+
rlm-core is enabled by default for 10-50x faster pattern classification. Build the [rlm-core](https://github.com/rand/loop) Rust library with Python bindings:
4646

4747
```bash
4848
# Clone rlm-core (if not already present)
@@ -103,9 +103,9 @@ cd ~/.claude/plugins/cache/rlm-claude-code/rlm-claude-code/$(ls ~/.claude/plugin
103103
uv venv && uv sync
104104
```
105105

106-
#### Step 3 (Optional): Enable rlm-core for Performance
106+
#### Step 3: Install rlm-core for Performance (Enabled by Default)
107107

108-
For 10-50x faster pattern classification:
108+
rlm-core is enabled by default. Install for 10-50x faster pattern classification:
109109

110110
```bash
111111
# First, build rlm-core if you haven't already (see "Building rlm-core" section above)
@@ -266,8 +266,8 @@ Add `"use_rlm_core": true` to `~/.claude/rlm-config.json`:
266266

267267
| Setting | Behavior |
268268
|---------|----------|
269-
| `use_rlm_core: true` | Use rlm-core Rust bindings (requires installation) |
270-
| `use_rlm_core: false` or unset | Use Python fallback (default, always works) |
269+
| `use_rlm_core: true` or unset | Use rlm-core Rust bindings (default, requires installation) |
270+
| `use_rlm_core: false` | Use Python fallback (always works) |
271271

272272
When rlm-core is enabled but not installed, a warning is logged and Python fallback is used automatically.
273273

@@ -277,9 +277,7 @@ When rlm-core is enabled but not installed, a warning is logged and Python fallb
277277
|-----------|-----------------|---------------|
278278
| Pattern Classifier | Python regex | Rust via PyO3 (10-50x faster) |
279279
| Trajectory Events | Python classes | Rust types via PyO3 |
280-
| Memory Store | Python + SQLite | Python + SQLite (rlm-core memory disabled*) |
281-
282-
*rlm-core memory store integration is disabled pending feature parity (metadata, FTS5 search).
280+
| Memory Store | Python + SQLite | Python + SQLite primary, rlm-core secondary (`-core.db`) |
283281

284282
### Benefits of rlm-core
285283

@@ -506,7 +504,7 @@ RLM stores configuration at `~/.claude/rlm-config.json`:
506504

507505
| Key | Type | Description |
508506
|-----|------|-------------|
509-
| `use_rlm_core` | bool | Enable rlm-core Rust bindings (default: `false`) |
507+
| `use_rlm_core` | bool | Enable rlm-core Rust bindings (default: `true`) |
510508
| `activation.mode` | string | `"micro"`, `"complexity"`, `"always"`, `"manual"` |
511509
| `depth.default` | int | Default recursion depth (1-3) |
512510
| `trajectory.verbosity` | string | `"minimal"`, `"normal"`, `"verbose"`, `"debug"` |
@@ -525,7 +523,39 @@ RLM stores configuration at `~/.claude/rlm-config.json`:
525523

526524
## Hooks
527525

528-
RLM integrates with Claude Code via hooks:
526+
### Go Binary Hooks (v0.6.0+)
527+
528+
As of v0.6.0, RLM uses compiled Go binaries for hook execution, reducing startup latency from ~500ms (Python) to ~5ms. The three primary hooks are:
529+
530+
| Hook | Binary | Purpose |
531+
|------|--------|---------|
532+
| `SessionStart` | `session-init` | Initialize RLM environment, emit `session.started` event |
533+
| `UserPromptSubmit` | `complexity-check` | Decide if RLM should activate, responds to DP phases |
534+
| `Stop` | `trajectory-save` | Save trajectory and emit `session.ended` event |
535+
536+
Prompt-based hooks (no binary needed) handle context sync, output capture, and pre-compaction.
537+
538+
#### Cross-Plugin Event System
539+
540+
Hooks emit and consume events via `~/.claude/events/`, enabling coordination between plugins (e.g., DP and RLM). All event types have JSON Schema definitions. Python helpers in `src/events/` provide emit/consume APIs.
541+
542+
#### Config Migration
543+
544+
v0.6.0 introduces a V2 config format. Existing V1 configs are automatically migrated on first run, preserving user customizations.
545+
546+
#### Legacy Fallback
547+
548+
To use the original Python hook scripts:
549+
550+
```bash
551+
export RLM_USE_LEGACY_HOOKS=1
552+
```
553+
554+
Legacy scripts are located in `scripts/legacy/`.
555+
556+
### Legacy Hook Reference
557+
558+
The original Python hooks (now in `scripts/legacy/`):
529559

530560
| Hook | Script | Purpose |
531561
|------|--------|---------|

agents/rlm-orchestrator.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,19 @@ model = cp.Model([x > 5])
4545
model.solve()
4646
```
4747

48+
## Platform: Parallel Tool Execution
49+
50+
When you issue multiple tool calls in a single message, they run in parallel. If **any one call fails** (non-zero exit code), all sibling calls are cancelled with "Sibling tool call errored" — even if those siblings would have succeeded.
51+
52+
This matters for exploratory Bash calls: commands like `ls`, `grep`, and `find` return **exit code 1** when they find no matches, which is semantically "no results" but triggers sibling cancellation.
53+
54+
When choosing between parallel and sequential tool calls:
55+
- **Parallel**: when you're confident all calls will succeed (e.g., reading known files, running tested commands)
56+
- **Sequential**: when calls are exploratory and may legitimately return no results
57+
- **Guard with `|| true`**: when you want to parallelize uncertain commands without risking cascade failure
58+
59+
Prefer Glob and Grep tools over Bash `find`/`grep` for file discovery — they handle no-results gracefully without non-zero exit codes.
60+
4861
## Rules
4962

5063
1. **Don't request full context dumps** — Use programmatic access

0 commit comments

Comments
 (0)