Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions .claude/hooks/rtk-rewrite.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#!/usr/bin/env bash
# rtk-hook-version: 3
# RTK Claude Code hook — rewrites commands to use rtk for token savings.
# Requires: rtk >= 0.23.0, jq
#
# This is a thin delegating hook: all rewrite logic lives in `rtk rewrite`,
# which is the single source of truth (src/discover/registry.rs).
# To add or change rewrite rules, edit the Rust registry — not this file.
#
# Exit code protocol for `rtk rewrite`:
# 0 + stdout Rewrite found, no deny/ask rule matched → auto-allow
# 1 No RTK equivalent → pass through unchanged
# 2 Deny rule matched → pass through (Claude Code native deny handles it)
# 3 + stdout Ask rule matched → rewrite but let Claude Code prompt the user

if ! command -v jq &>/dev/null; then
echo "[rtk] WARNING: jq is not installed. Hook cannot rewrite commands. Install jq: https://jqlang.github.io/jq/download/" >&2
exit 0
fi

if ! command -v rtk &>/dev/null; then
echo "[rtk] WARNING: rtk is not installed or not in PATH. Hook cannot rewrite commands. Install: https://github.com/rtk-ai/rtk#installation" >&2
exit 0
fi

# Version guard: rtk rewrite was added in 0.23.0.
# Older binaries: warn once and exit cleanly (no silent failure).
# Cache the version check to avoid spawning multiple processes on every hook call.
CACHE_DIR=${XDG_CACHE_HOME:-$HOME/.cache}
CACHE_FILE="$CACHE_DIR/rtk-hook-version-ok"
if [ ! -f "$CACHE_FILE" ]; then
RTK_VERSION_RAW=$(rtk --version 2>/dev/null)
RTK_VERSION=${RTK_VERSION_RAW#rtk }
RTK_VERSION=${RTK_VERSION%% *}
if [ -n "$RTK_VERSION" ]; then
IFS=. read -r MAJOR MINOR PATCH <<<"$RTK_VERSION"
# Require >= 0.23.0
if [ "$MAJOR" -eq 0 ] && [ "$MINOR" -lt 23 ]; then
echo "[rtk] WARNING: rtk $RTK_VERSION is too old (need >= 0.23.0). Upgrade: cargo install rtk" >&2
exit 0
fi
fi
mkdir -p "$CACHE_DIR" 2>/dev/null
touch "$CACHE_FILE" 2>/dev/null
fi

INPUT=$(cat)
CMD=$(jq -r '.tool_input.command // empty' <<<"$INPUT")

if [ -z "$CMD" ]; then
exit 0
fi

# Delegate all rewrite + permission logic to the Rust binary.
REWRITTEN=$(rtk rewrite "$CMD" 2>/dev/null)
EXIT_CODE=$?

case $EXIT_CODE in
0)
# Rewrite found, no permission rules matched — safe to auto-allow.
# If the output is identical, the command was already using RTK.
[ "$CMD" = "$REWRITTEN" ] && exit 0
;;
1)
# No RTK equivalent — pass through unchanged.
exit 0
;;
2)
# Deny rule matched — let Claude Code's native deny rule handle it.
exit 0
;;
3)
# Ask rule matched — rewrite the command but do NOT auto-allow so that
# Claude Code prompts the user for confirmation.
;;
*)
exit 0
;;
esac

if [ "$EXIT_CODE" -eq 3 ]; then
# Ask: rewrite the command, omit permissionDecision so Claude Code prompts.
jq -c --arg cmd "$REWRITTEN" \
'.tool_input.command = $cmd | {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"updatedInput": .tool_input
}
}' <<<"$INPUT"
else
# Allow: rewrite the command and auto-allow.
jq -c --arg cmd "$REWRITTEN" \
'.tool_input.command = $cmd | {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "RTK auto-rewrite",
"updatedInput": .tool_input
}
}' <<<"$INPUT"
fi
4 changes: 4 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
"type": "command",
"command": "sh -c 'exec node \"${CLAUDE_PROJECT_DIR:-.}/.claude/helpers/hook-handler.cjs\" pre-bash'",
"timeout": 5000
},
{
"type": "command",
"command": ".claude/hooks/rtk-rewrite.sh"
}
]
},
Expand Down
36 changes: 36 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,39 @@ Engineering rules live in `.claude/rules/agent-rules.md` (auto-loaded by Claude

- Documentation: https://github.com/ruvnet/claude-flow
- Issues: https://github.com/DarkCodePE/quipu/issues

## RTK Token Reduction (External CLI Dependency)

> Optional dependency. **Install with one of**: `brew install rtk` (recommended), `cargo install --git https://github.com/rtk-ai/rtk`, or `curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/refs/heads/master/install.sh | sh`. Provides 60-90% token savings on Bash tool outputs. Hook degrades silently if missing.
>
> ⚠️ **DO NOT run `cargo install rtk`** — name collision: a different Lua toolkit owns the `rtk` crate name on crates.io and `cargo install rtk` will pull the wrong package (v0.1.0 of an unrelated mlua wrapper). Always install from the upstream repo or via brew.

# RTK - Rust Token Killer

**Usage**: Token-optimized CLI proxy (60-90% savings on dev operations)

## Meta Commands (always use rtk directly)

```bash
rtk gain # Show token savings analytics
rtk gain --history # Show command usage history with savings
rtk discover # Analyze Claude Code history for missed opportunities
rtk proxy <cmd> # Execute raw command without filtering (for debugging)
```

## Installation Verification

```bash
rtk --version # Should show: rtk X.Y.Z
rtk gain # Should work (not "command not found")
which rtk # Verify correct binary
```

⚠️ **Name collision**: If `rtk gain` fails, you may have reachingforthejack/rtk (Rust Type Kit) installed instead.

## Hook-Based Usage

All other commands are automatically rewritten by the Claude Code hook.
Example: `git status` → `rtk git status` (transparent, 0 tokens overhead)

Refer to CLAUDE.md for full command reference.
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Reference: `docs/diagrams/workflow-overview.drawio`
| **Workflow kernel** | brainstorming → plans → subagent-driven dev → TDD → verification gate |
| **30 canonical agents** | 7 buckets: core, review, arch, security, ops, coord, meta |
| **65 skills** | 20 universal kernel + 45 opt-in (enable per project) |
| **24 hooks pipeline** | 12 default ON + 6 opt-in + 6 security/memory, budget ≤2s hot-path |
| **25 hooks pipeline** | 13 default ON + 6 opt-in + 6 security/memory, budget ≤2s hot-path |
| **3-layer memory** | L1 session · L2 sql.js local · L3 RuVector PostgreSQL with GNN (opt-in) |
| **5 security layers** | AIMDS prompts · sandbox tools · PII/secrets · CVE/SBOM · truth-score outputs |
| **OWASP LLM 10/10** | Full coverage of LLM Top 10 threats |
Expand All @@ -109,6 +109,7 @@ Reference: `docs/diagrams/workflow-overview.drawio`
| **Learning path** | 10 lessons (14h total) + 30-question self-assessment quiz |
| **Pocock alignment cycle** | Mandatory grilling + PRD before any production code (Enhancement/Fix paths) |
| **Scope Guardian Phase 2** | scope-enforce blocks file mutations outside scope; conscious-override via env var |
| **RTK token reduction** | PreToolUse hook delegates Bash output to upstream `rtk` binary (60-90% fewer tokens, opt-in via install) |

## Scope Guardian (Phase 2)

Expand All @@ -119,6 +120,22 @@ Real enforcement on `Edit`, `Write`, and `MultiEdit` via `PreToolUse` hook.
- Override via `RUFLO_SCOPE_OVERRIDE=1` env var (action is logged)
- Runs in `mode=warn` during the 7-day calibration window; flip to `mode=block` after

## RTK Token Reduction (optional)

A PreToolUse hook delegates eligible `Bash` commands to the upstream [`rtk`](https://github.com/rtk-ai/rtk) binary (Rust Token Killer), reducing token consumption on CLI outputs by 60-90% (upstream benchmark). RTK is an **optional external dependency** — install it once and any Claude Code session in a Quipu project automatically benefits.

Install with **one of**:

```bash
brew install rtk # macOS / Linuxbrew (recommended)
cargo install --git https://github.com/rtk-ai/rtk # from source
curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/refs/heads/master/install.sh | sh
```

> ⚠️ Do NOT run `cargo install rtk` — name collision: a different Lua toolkit owns the `rtk` crate name on crates.io and `cargo install rtk` will pull the wrong package (v0.1.0 of an unrelated mlua wrapper). The hook's upstream version guard catches this and emits a warning, but you save time by installing correctly the first time.

The hook degrades silently when rtk is not installed (`exit 0`, no transcript noise). After a Claude Code session, run `rtk gain` to see actual tokens saved per command class. Architectural rationale and alternatives considered: see [`docs/adr/ADR-032`](docs/adr/ADR-032-rtk-external-cli-dependency.md).

## Requirements

- [Claude Code](https://claude.com/code) ≥ 2.0.65
Expand Down
77 changes: 77 additions & 0 deletions docs/adr/ADR-032-rtk-external-cli-dependency.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# ADR-032: RTK as External CLI Dependency (Install, Not Absorb)

**U-ID:** `260510-rtkext`

## Status

Proposed (2026-05-10) - Author: Orlando Kuan

## Context

RuFlo tools produce verbose CLI output (git, grep, ls, ripgrep, cargo test). That verbosity consumes tokens on every PreToolUse/PostToolUse cycle. RTK (Rust Token Killer, `github.com/rtk-ai/rtk`, v0.34.3) is a standalone binary authored by Patrick Szymkowiak that rewrites CLI output before it reaches the model, reducing token consumption 60-90% on measured benchmarks. RTK ships a native Claude Code hook at `rtk/hooks/claude/rtk-rewrite.sh` (PreToolUse, 60+ upstream tests, CI green, distributed via brew and cargo).

A separate project, 9Router (`9router/open-sse/rtk/`), absorbed RTK by rewriting it in JavaScript inside a MITM proxy. The result is roughly 1000 LOC owned by 9Router, no upstream test suite, no published benchmarks, and claimed gains degraded to "20-40%" without reproducible measurement. That absorption traded maintenance burden for marginal capability.

RuFlo can get the same compression by invoking the upstream binary directly from a PreToolUse hook, the same integration point RTK was designed for.

## Decision

Adopt the upstream `rtk` binary as an optional external dependency, invoked from a PreToolUse hook. RuFlo does not absorb, copy, or maintain any RTK source code. The hook exits 0 silently when `rtk` is not installed, so the integration degrades gracefully for developers who have not installed the binary. Installation remains the developer's responsibility via `brew install rtk`, `cargo install --git https://github.com/rtk-ai/rtk`, or the upstream `install.sh` script. The plain `cargo install rtk` form is **not valid**: a different Lua toolkit owns the `rtk` name on crates.io, so installing by crate name pulls the wrong package. Setup instructions must call this out explicitly.

## Consequences

### Positive

- Zero RuFlo-owned code for token compression. The binary is a leaf dependency, same category as `git`, `jq`, and `ripgrep`.
- 60-90% token reduction on CLI output (upstream benchmark), available immediately on install without any RuFlo code change.
- PreToolUse placement rewrites the command output before the model reads it, which is cleaner than PostToolUse compression of stdout after the fact.
- RTK's active release cadence means improvements flow in without RuFlo work.

### Negative

- External binary introduces an implicit dependency. Developers without `rtk` installed get no compression and no error, only silent degradation. This must be documented in setup instructions.
- Binary availability varies by platform. Windows support depends on RTK upstream; RuFlo cannot fix it.
- **Crate-name collision on crates.io**: the `rtk` name is taken by an unrelated Lua binding, so `cargo install rtk` installs the wrong package (v0.1.0, mlua wrapper). The upstream version guard catches this at hook startup (`rtk X.Y.Z is too old (need >= 0.23.0)`), but onboarding docs must steer developers to `brew`, `install.sh`, or `cargo install --git` to avoid the trap.

### Neutral

- The hook file lives in RuFlo's `.claude/hooks/` directory. Its content is thin: a conditional exec to the upstream binary. It is not RTK source code.

## Alternatives Considered

### Option A: Absorb token-filter logic as TypeScript inside RuFlo

- **Pros**: no external dependency, runs on any Node environment.
- **Cons**: RuFlo owns the maintenance burden; manual filters would produce far less compression than RTK's full AST-aware rewrite; no test suite to inherit.

### Option B: Copy 9Router's JavaScript port

- **Pros**: already written, no Rust toolchain required.
- **Cons**: ~1000 LOC with no upstream tests, benchmarks inflated ("20-40%" unverified), and frozen at a point-in-time snapshot of RTK v0.x behavior. Every future RTK optimization requires a manual port. This is the absorption failure mode ADR-027 warns against.

### Option C (chosen): Invoke upstream `rtk` binary via PreToolUse hook

- **Pros**: 60-90% measured compression, zero owned code, active upstream, graceful degradation.
- **Cons**: optional dependency that must be documented; platform coverage depends on RTK CI; crate-name collision on crates.io requires explicit install guidance.

## Relation to ADR-027

ADR-027 governs Claude Code plugins and establishes an "absorb-not-install" strategy for that category. That rule applies specifically to CE's agent plugin, where installing the plugin would surrender architectural control over agent routing, memory namespaces, and claims auth.

RTK is not a Claude Code plugin. It is a standalone CLI binary with no RuFlo lifecycle hooks beyond the single PreToolUse invocation. The correct model here is "install-not-absorb": treat it the same way RuFlo treats `git`, `jq`, and `ripgrep` - declare it as a tool the developer is expected to have, invoke it directly, and depend on upstream maintenance. ADR-027 and ADR-032 are complementary, not contradictory. ADR-027 says do not install CE plugins; ADR-032 says do install CLI tools rather than rewriting them.

## Verification

After a Claude Code session with `rtk` installed, run `rtk gain` to confirm the compression ratio reported matches the 60-90% range for the session's CLI output. If `rtk` is not installed, the PreToolUse hook exits 0 and the session proceeds normally without error. Both paths should be validated during onboarding.

## Related Decisions

- ADR-027: CE plugin adoption strategy - defines "absorb-not-install" for Claude Code plugins; ADR-032 defines "install-not-absorb" for standalone CLI tools. Different categories, compatible rules.
- ADR-004: MCP transport optimization - related interest in reducing round-trip overhead; RTK attacks a different surface (token volume, not transport latency).

## References

- RTK README: `/home/orlando/Desktop/framework/rtk/README.md`
- RTK Claude hook: `/home/orlando/Desktop/framework/rtk/hooks/claude/rtk-rewrite.sh`
- 9Router RTK port (negative example): `/home/orlando/Desktop/framework/9router/open-sse/rtk/`
- RTK upstream: `https://github.com/rtk-ai/rtk`
Loading
Loading