agentic scripts / humans

how compaction-driven orchestration works

← back

the problem

Without a map, an AI agent typically needs 10-20+ tool calls to understand a codebase — globbing for files, reading each one in full, grepping for relationships. Each call is an LLM round-trip that costs tokens and time.

the solution: compaction

Compaction is a pre-processing step that strips source files down to their structural skeleton: function signatures, imports, exports, component props, and type definitions. No implementation details. The agent greps this single file instead of reading source files one by one.

vanilla agent with compaction
tool calls to understand codebase 10-20+ 2-4
tokens on source code high (full files) low (signatures only)
LLM round-trips many few
risk of wrong path high low

The tradeoff is a one-time compaction cost (~5-10s to run the script). It pays for itself on the first task and is skipped entirely on subsequent ones.

why this works: out-of-context processing

JS scripts run outside the LLM context window. When an agent reads a 500-line file to find three function signatures, all 500 lines count as input tokens. These tools do the filtering in Node.js (zero tokens) and hand the agent only the structural output.

A typical 200-file project might be ~50k tokens of raw source. After compaction, the same project is ~3-5k tokens of signatures. This means fewer tool calls, less context pollution, and faster convergence on the right file to edit.

tools

compaction.js

Walks your project tree and strips every JS/TS/Python file down to its skeleton: imports, exports, function signatures, component props, hook usage, class declarations, and type definitions. Outputs a single compacted_*.md file. Supports React-specific patterns (memo, forwardRef, useState, useEffect with dependency arrays, custom hooks).

dep-graph.js

Builds a full dependency graph from import/require statements. For each file it records what it imports (forward edges), what imports it (reverse edges / imported-by), and which external packages it uses. Runs cycle detection via DFS. Agents grep imported-by to check blast radius before modifying a file.

symbols.js

Extracts every top-level symbol (components, functions, hooks, classes, types, constants) with its file path, line number, and export type (default, named, or internal). Lets agents answer "where is X defined?" with a single grep instead of globbing the entire codebase.

All three scripts are zero-dependency (Node.js built-ins only), run in ~5-10 seconds, and produce markdown files that persist across agent sessions.

claude code hooks

Claude Code supports hooks — shell scripts that run automatically at specific points in the agent lifecycle. This project uses four hooks to make orchestration fully automatic, with no manual prompting required.

classify.sh · UserPromptSubmit

Fires on every prompt. Reads the user's message, matches signal words against a classification table (feature, bugfix, refactor, etc.), and injects the full orchestration protocol plus the matched workflow into the agent's context. Also auto-detects the project's technology (React, .NET, or general) for correct workflow routing. Extracts prompt keywords — content words from the user's message with stop words and classification signal words stripped — and writes them to .prompt_keywords for use by the grep quality gate. If the project has no .orchestration/ directory, it silently does nothing.

maintain.sh · SessionStart

Runs once when a session starts. Compares the local orchestration.md against the CDN version and updates if stale. Fetches the tool script manifest and re-downloads any scripts whose sha256 hash has changed. Cleans up old compaction/dep-graph/symbol artifacts and removes any whose embedded git-sha no longer matches HEAD.

guard-explore.sh · PreToolUse

Enforces the compaction-first gate with grep quality validation. Blocks the agent from reading source files, globbing src/, or spawning Explore agents until it has grepped the compacted output with meaningful search terms. Rejects trivial patterns (single characters, .*, empty strings) and tracks every grep pattern in .grep_patterns. Source access unlocks only when the agent has used 2+ distinct non-trivial patterns, or 1 pattern that matches a prompt keyword extracted by classify.sh. When blocked, the deny message shows which patterns have been tried and what's still needed.

rehydrate.sh · utility

Re-injects the orchestration protocol after context compaction (when the LLM's context window is compressed). Not registered as a hook event (Claude Code doesn't have a PostCompact event), but kept as a utility script that can be called manually or by future hook events.

grep quality gate

The guard doesn't just check whether the agent grepped compaction — it checks what it searched for. Without this, an agent could grep for a nonsense string, get zero results, and proceed to read every source file directly, defeating the purpose of compaction.

When the agent greps compaction, the guard records the pattern and checks two unlock criteria:

criterion what it means
2+ distinct patterns agent is genuinely exploring the compacted output
1 pattern matches a prompt keyword agent is searching for something task-relevant

Prompt keywords are extracted by classify.sh on every prompt: the user's message is lowercased, stop words (the, a, for, etc.) and classification signal words (fix, add, build, etc.) are stripped, and the remaining 3+ character words are saved. For example, "fix the login bug in TokenRefreshInterceptor" produces keywords login and tokenrefreshinterceptor. If the agent greps compaction for "login", the gate unlocks immediately because it matches a prompt keyword. All markers are cleared on each new prompt, so the gate resets per-task.

the hook installer

The hook installer is a self-executing prompt — you pipe it into Claude Code and it installs everything automatically:

It writes all four hook scripts to ~/.claude/hooks/, registers them in ~/.claude/settings.json with the correct absolute paths, matchers, and timeouts, then verifies each hook exits cleanly and the settings file is valid JSON. The hooks are installed globally, so they activate for every project. Projects without an .orchestration/ directory are silently skipped.