Toolcraft Coordinator

The integration layer that connects Fledgling, blq, and jetsam to implement the Patterns for Toolcraft.


Why a coordinator

Three tools, three levels, three roles:

  • Fledgling reads code intelligence (level 0)

  • blq executes and captures builds/tests (level 1-2)

  • jetsam manages workflow state (level 1-2)

The patterns (Quartermaster, Mode Controller, Coach, Write/Execute Separation, Sandbox Specs) compose these tools. But none of the three should implement the composition — that would pull them out of their natural level. The composition is Harness-level logic: specified rules that operate on the outputs of all three.

The Coordinator is that composition layer.

What it does

1. Path protection (Write/Execute Separation)

Block writes to specific paths based on the current mode.

Mechanism: Claude Code PreToolUse hook

{
  "hooks": {
    "PreToolUse": [
      {
        "command": "python3 toolcraft-coordinator/path_guard.py",
        "description": "Enforce path protection per mode"
      }
    ]
  }
}

The hook receives the full tool call on stdin as JSON, including tool_name and tool_input with file paths. For write tools (Edit, Write, Bash), it checks the path against the current mode’s writable set.

This works with Claude Code’s built-in tools directly. No reimplementation. The PreToolUse hook controls Edit, Write, Bash — the same tools the agent already uses. Exit code 0 with permissionDecision: "deny" blocks the call and sends the reason to the agent.

#!/usr/bin/env python3
"""path_guard.py — Block writes to protected paths per mode.

Works with Claude Code's built-in tools (Edit, Write, Bash).
No tool reimplementation needed.

PreToolUse hook protocol:
  Input (stdin): {"tool_name": "Edit", "tool_input": {"file_path": "...", ...}, ...}
  Output (stdout): JSON with permissionDecision: "deny"|"allow"|"ask"
  Exit 0: output parsed. Exit 2: blocked with stderr as reason.
"""

import json
import sys
from pathlib import Path

def get_current_mode():
    state_file = Path(".jetsam/state.json")
    if state_file.exists():
        return json.loads(state_file.read_text()).get("mode", "implement")
    # Also check a simple mode file for systems without jetsam
    mode_file = Path(".kibitzer/mode")
    if mode_file.exists():
        return mode_file.read_text().strip()
    return "implement"

MODE_POLICIES = {
    "debug": {
        "writable": [],
        "message": "Debug mode: read-only. Use `jetsam mode implement` to start editing."
    },
    "implement": {
        "writable": ["src/"],
        "message": "Implementation mode: tests/ is protected. Use `jetsam mode test_dev` to edit tests."
    },
    "test_dev": {
        "writable": ["tests/"],
        "message": "Test development mode: src/ is protected. Use `jetsam mode implement` to edit source."
    },
    "review": {
        "writable": [],
        "message": "Review mode: read-only. Use `jetsam mode implement` to start editing."
    },
}

def extract_path(tool_name, tool_input):
    """Extract the target file path from a tool call."""
    if tool_name == "Edit":
        return tool_input.get("file_path", "")
    elif tool_name == "Write":
        return tool_input.get("file_path", "")
    elif tool_name == "Bash":
        # Can't reliably extract paths from bash commands
        # but we can check for obvious write patterns
        cmd = tool_input.get("command", "")
        # For bash, the sandbox spec (blq) is the right enforcement layer
        # Path guard only handles structured tools
        return ""
    # MCP tools
    return tool_input.get("path", tool_input.get("file_path", ""))

if __name__ == "__main__":
    hook_input = json.loads(sys.stdin.read())
    tool_name = hook_input.get("tool_name", "")
    tool_input = hook_input.get("tool_input", {})

    # Only check write tools
    write_tools = {"Edit", "Write"}
    if tool_name not in write_tools:
        sys.exit(0)  # allow non-write tools

    path = extract_path(tool_name, tool_input)
    if not path:
        sys.exit(0)  # no path to check

    mode = get_current_mode()
    policy = MODE_POLICIES.get(mode, {"writable": ["*"], "message": ""})

    # Check path against writable set
    allowed = False
    for writable_prefix in policy["writable"]:
        if path.startswith(writable_prefix):
            allowed = True
            break

    if not allowed and policy["writable"] != ["*"]:
        # Deny with reason — the agent sees this in its context
        print(json.dumps({
            "hookSpecificOutput": {
                "hookEventName": "PreToolUse",
                "permissionDecision": "deny",
                "permissionDecisionReason": f"{policy['message']} (tried to write: {path})"
            }
        }))
    # else: exit 0 with no output = allow

Key design: the denial reason tells the agent how to switch modes. “Use jetsam mode implement to start editing.” The agent can respond by requesting a mode switch rather than being silently blocked. This is the transparency principle from the Ma series — project constraints into the actor’s scope.

2. Mode transitions (Mode Controller)

Mechanism: Claude Code PostToolUse hook

#!/usr/bin/env python3
"""mode_controller.py — Switch modes on failure patterns."""

import json
import sys
from pathlib import Path

def get_state():
    state_file = Path(".jetsam/state.json")
    if state_file.exists():
        return json.loads(state_file.read_text())
    return {"mode": "implement", "failure_count": 0, "turns_in_mode": 0}

def update_state(state):
    state_file = Path(".jetsam/state.json")
    state_file.parent.mkdir(exist_ok=True)
    state_file.write_text(json.dumps(state, indent=2))

# Read hook input (tool call result)
tool_result = json.loads(sys.stdin.read())
success = tool_result.get("success", True)
tool_name = tool_result.get("tool_name", "")

state = get_state()
state["turns_in_mode"] = state.get("turns_in_mode", 0) + 1

if not success:
    state["failure_count"] = state.get("failure_count", 0) + 1

# Transition rules
mode = state["mode"]
transitions = []

if mode == "implement" and state["failure_count"] > 3:
    transitions.append(("debug", "Too many failures — switching to diagnosis"))

if mode == "debug" and state["turns_in_mode"] > 20:
    transitions.append(("implement", "Extended diagnosis — time to try fixing"))

if mode == "implement" and tool_name == "run_tests" and success:
    transitions.append(("review", "Tests passing — switching to review"))

if transitions:
    new_mode, reason = transitions[0]
    state["mode"] = new_mode
    state["failure_count"] = 0
    state["turns_in_mode"] = 0
    # Log the transition
    print(f"[mode-controller] Switching to {new_mode}: {reason}")

update_state(state)

3. Coach suggestions (The Coach)

Mechanism: Claude Code PostToolUse hook, fires every N calls

#!/usr/bin/env python3
"""coach.py — Suggest more efficient tool usage."""

import json
import subprocess
import sys
from pathlib import Path

# Only fire every N calls
state = get_state()  # reuse jetsam state
call_count = state.get("total_calls", 0) + 1
state["total_calls"] = call_count
update_state(state)

if call_count % 5 != 0:
    sys.exit(0)  # not time yet

# Query Fledgling for tool usage patterns
def query_fledgling(sql):
    result = subprocess.run(
        ["fledgling", "query", sql],
        capture_output=True, text=True, timeout=5
    )
    return result.stdout

# Check for repeated searches
repeated = query_fledgling("""
    SELECT arguments->>'pattern' as pattern, count(*) as times
    FROM ChatToolUsage
    WHERE tool_name = 'file_search'
    AND sequence_number > (SELECT max(sequence_number) - 10 FROM ChatToolUsage)
    GROUP BY pattern
    HAVING count(*) >= 3
""")

if repeated.strip():
    print(f"[coach] You've searched for the same pattern multiple times. "
          f"Try FindDefinitions for AST-aware search across all files.")

# Check for edit-without-test
edits_since_test = query_fledgling("""
    SELECT count(*) as edits
    FROM ChatToolUsage
    WHERE tool_name LIKE '%edit%'
    AND sequence_number > COALESCE(
        (SELECT max(sequence_number) FROM ChatToolUsage WHERE tool_name = 'run_tests'),
        0
    )
""")
# Parse and suggest if needed

4. Quartermaster kit activation (The Quartermaster)

Mechanism: Claude Code session start hook or skill invocation

The Quartermaster runs at session start, queries the model and task, and configures:

  • Which Fledgling kit to activate (tool publication)

  • Which jetsam mode to start in

  • Which blq sandbox spec to use

  • Whether to include the strategy instruction

#!/usr/bin/env bash
# quartermaster.sh — Configure the session based on task and model

MODEL=$(claude --version 2>&1 | grep model | awk '{print $NF}')
TASK_TYPE="${1:-implement}"  # debug, implement, review, etc.

# Select kit based on model + task
case "$MODEL" in
    *haiku*)
        FLEDGLING_KIT="navigate"
        STRATEGY="Do not start editing until you understand the full picture."
        TOOL_RESTRICTION="simple"
        ;;
    *sonnet*)
        FLEDGLING_KIT="diagnose"
        STRATEGY=""  # Sonnet doesn't need it
        TOOL_RESTRICTION="full"
        ;;
    *opus*)
        FLEDGLING_KIT="diagnose"
        STRATEGY=""  # Harmful for Opus
        TOOL_RESTRICTION="full"
        ;;
esac

# Configure Fledgling
fledgling profile "$FLEDGLING_KIT"

# Configure jetsam mode
jetsam mode "$TASK_TYPE"

# Inject strategy if needed
if [ -n "$STRATEGY" ]; then
    echo "$STRATEGY" >> .claude/CLAUDE.md
fi

echo "[quartermaster] Configured: model=$MODEL kit=$FLEDGLING_KIT mode=$TASK_TYPE"

The extension: kibitzer

All four hooks package into a single extension:

kibitzer/
├── path_guard.py          # PreToolUse: path protection per mode
├── mode_controller.py     # PostToolUse: failure-driven transitions
├── coach.py               # PostToolUse (every N): efficiency suggestions
├── quartermaster.sh       # Session start: kit/mode/strategy selection
├── config.toml            # Mode policies, thresholds, coach frequency
└── install.sh             # Adds hooks to settings.json

Configuration

# kibitzer/config.toml

[modes.debug]
writable = []
strategy = "Identify all failures before proposing fixes."

[modes.implement]
writable = ["src/"]
strategy = ""

[modes.test_dev]
writable = ["tests/"]
strategy = "Write tests for the expected behavior, not the current behavior."

[modes.review]
writable = []
strategy = "Read everything before forming an opinion."

[controller]
max_failures_before_debug = 3
max_turns_in_debug = 20
auto_review_on_tests_passing = true

[coach]
frequency = 5           # suggest every N tool calls
max_suggestion_length = 2  # lines
model_config.haiku = "aggressive"    # every 3 calls
model_config.sonnet = "standard"     # every 5 calls
model_config.opus = "minimal"        # every 10, action-oriented only

[quartermaster]
default_mode = "implement"
haiku_kit = "navigate"
sonnet_kit = "diagnose"
opus_kit = "diagnose"

Installation

# Install the extension
kibitzer install

# This adds to .claude/settings.json:
# {
#   "hooks": {
#     "PreToolUse": ["python3 kibitzer/path_guard.py"],
#     "PostToolUse": [
#       "python3 kibitzer/mode_controller.py",
#       "python3 kibitzer/coach.py"
#     ],
#     "SessionStart": ["bash kibitzer/quartermaster.sh"]
#   }
# }

Plugin architecture: tool-aware interception

The coordinator isn’t just hooks — it’s a plugin system where each tool (blq, jetsam, Fledgling) registers what bash patterns it can handle and what it offers instead.

How plugins work

Each plugin declares:

  • Triggers: bash command patterns it recognizes

  • Intercept: what it suggests or does instead

  • Mode: observe (log only), suggest (inform agent), redirect (deny bash, offer alternative)

from dataclasses import dataclass, field
from typing import Optional
from enum import Enum

class InterceptMode(Enum):
    OBSERVE = "observe"    # allow bash, log the suggestion (ratchet data)
    SUGGEST = "suggest"    # allow bash, inject suggestion into context
    REDIRECT = "redirect"  # deny bash, agent must use the alternative

@dataclass
class Suggestion:
    tool: str                          # the structured alternative
    reason: str                        # why it's better
    grade_before: Optional[str] = None # e.g. "level 4"
    grade_after: Optional[str] = None  # e.g. "level 1"

@dataclass
class Plugin:
    name: str
    triggers: list[str]                # bash patterns to match
    mode: InterceptMode = InterceptMode.SUGGEST

    def intercept(self, tool_name: str, tool_input: dict) -> Optional[Suggestion]:
        """Check if this plugin can handle the tool call. Return suggestion or None."""
        raise NotImplementedError

The plugins

class BlqPlugin(Plugin):
    """Intercepts build/test commands, offers blq structured capture."""

    name = "blq"
    triggers = ["pytest", "python -m pytest", "make", "npm test",
                "cargo test", "go test", "gradle test"]

    def intercept(self, tool_name, tool_input):
        if tool_name != "Bash":
            return None
        cmd = tool_input.get("command", "")

        for trigger in self.triggers:
            if trigger in cmd:
                return Suggestion(
                    tool=f"blq run test",
                    reason="Captures structured output — errors queryable via blq errors, "
                           "results persisted across sessions, diffable over time",
                    grade_before="level 4 (arbitrary bash)",
                    grade_after="level 1 (structured query over captured output)",
                )
        return None


class JetsamPlugin(Plugin):
    """Intercepts git commands, offers jetsam workflow operations."""

    name = "jetsam"
    triggers = ["git add", "git commit", "git push", "git stash",
                "git checkout", "git branch", "git diff", "git log"]

    def intercept(self, tool_name, tool_input):
        if tool_name != "Bash":
            return None
        cmd = tool_input.get("command", "")

        if "git add" in cmd and "git commit" in cmd:
            return Suggestion(
                tool="jetsam save 'description'",
                reason="Atomic save with plan tracking, confirmation step, "
                       "and automatic branch management",
                grade_before="level 4",
                grade_after="level 2 (structured workflow operation)",
            )
        if "git push" in cmd:
            return Suggestion(
                tool="jetsam sync",
                reason="Syncs with remote, handles rebasing, "
                       "requires confirmation before pushing",
            )
        if "git diff" in cmd:
            return Suggestion(
                tool="jetsam diff",
                reason="Structured diff with plan context",
            )
        if "git log" in cmd:
            return Suggestion(
                tool="jetsam log",
                reason="Commit history with plan annotations",
            )
        return None


class FledglingPlugin(Plugin):
    """Intercepts code search/navigation, offers semantic alternatives."""

    name = "fledgling"
    triggers = ["grep -r", "grep -rn", "find . -name", "find . -type f"]

    def intercept(self, tool_name, tool_input):
        if tool_name != "Bash":
            return None
        cmd = tool_input.get("command", "")

        # Detect searching for definitions
        if ("grep" in cmd and
            any(kw in cmd for kw in ["def ", "class ", "function ", "fn "])):
            return Suggestion(
                tool="FindDefinitions(name_pattern='...')",
                reason="AST-aware search — understands scope, type, and nesting. "
                       "Finds the definition, not just text matches",
                grade_before="level 4 (bash grep)",
                grade_after="level 0 (structured query over parsed AST)",
            )

        # Detect searching for file structure
        if "find" in cmd and ("-name" in cmd or "-type" in cmd):
            pattern = ""  # extract from cmd if possible
            return Suggestion(
                tool=f"CodeStructure(file_pattern='**/*.py')",
                reason="Shows classes, functions, and nesting — "
                       "not just file names but code organization",
                grade_before="level 4",
                grade_after="level 0",
            )

        # Detect reading files for understanding
        if "cat" in cmd and cmd.count("cat") >= 2:
            return Suggestion(
                tool="ReadLines with multiple files",
                reason="Structured multi-file read with line numbers "
                       "and context windowing",
            )

        return None

The interception hook

The PreToolUse hook runs all plugins against every Bash call:

#!/usr/bin/env python3
"""plugin_interceptor.py — Route bash commands to structured alternatives."""

import json
import sys
from pathlib import Path

# Load plugins (in practice, imported from the extension package)
plugins = [BlqPlugin(), JetsamPlugin(), FledglingPlugin()]

# Load interception mode from config
config_file = Path(".kibitzer/config.toml")
default_mode = InterceptMode.SUGGEST  # start with suggest, graduate to redirect

def run():
    hook_input = json.loads(sys.stdin.read())
    tool_name = hook_input.get("tool_name", "")
    tool_input = hook_input.get("tool_input", {})

    # Only intercept Bash calls
    if tool_name != "Bash":
        return

    # Check each plugin
    suggestions = []
    for plugin in plugins:
        suggestion = plugin.intercept(tool_name, tool_input)
        if suggestion:
            suggestions.append((plugin, suggestion))

    if not suggestions:
        return  # no plugin matches, allow the bash call

    # Use the first matching suggestion (could rank by grade_reduction)
    plugin, suggestion = suggestions[0]

    mode = plugin.mode  # or override from config

    if mode == InterceptMode.OBSERVE:
        # Log the suggestion but allow bash
        log_entry = {
            "bash_command": tool_input.get("command", "")[:200],
            "suggested_tool": suggestion.tool,
            "reason": suggestion.reason,
            "plugin": plugin.name,
        }
        log_file = Path(".kibitzer/intercept.log")
        log_file.parent.mkdir(exist_ok=True)
        with open(log_file, "a") as f:
            f.write(json.dumps(log_entry) + "\n")
        return  # allow

    elif mode == InterceptMode.SUGGEST:
        # Allow bash but inject the suggestion into context
        print(json.dumps({
            "hookSpecificOutput": {
                "hookEventName": "PreToolUse",
                "additionalContext": (
                    f"[toolcraft] {plugin.name} suggests: {suggestion.tool}\n"
                    f"Reason: {suggestion.reason}"
                    + (f"\nGrade: {suggestion.grade_before} → {suggestion.grade_after}"
                       if suggestion.grade_before else "")
                ),
            }
        }))
        return  # allow, but with suggestion in context

    elif mode == InterceptMode.REDIRECT:
        # Deny bash, provide the alternative
        print(json.dumps({
            "hookSpecificOutput": {
                "hookEventName": "PreToolUse",
                "permissionDecision": "deny",
                "permissionDecisionReason": (
                    f"A structured alternative is available: {suggestion.tool}\n"
                    f"{suggestion.reason}"
                    + (f"\nThis reduces the grade from {suggestion.grade_before} to {suggestion.grade_after}."
                       if suggestion.grade_before else "")
                ),
            }
        }))
        return

if __name__ == "__main__":
    run()

The three modes: the ratchet applied to interception

OBSERVE → SUGGEST → REDIRECT

Start in observe: log every bash command that has a structured alternative. This builds the ratchet data — which commands are candidates for promotion.

After reviewing the logs and confirming the alternatives work, graduate to suggest: the agent sees “jetsam save is available” in its context alongside the bash call. It can choose either.

After confirming the agent follows suggestions reliably, graduate to redirect: bash calls with structured alternatives are denied. The agent must use the structured tool.

Each graduation is a ratchet turn. The mode can be set per-plugin:

[plugins.blq]
mode = "redirect"          # tests must go through blq (mature)

[plugins.jetsam]
mode = "suggest"           # git workflow suggestions (learning)

[plugins.fledgling]
mode = "observe"           # code navigation alternatives (new, gathering data)

PostToolUse validation plugins

The same plugin architecture works for PostToolUse — validating results:

class BlqPostPlugin(Plugin):
    """After blq run test, check for new failures."""

    def validate(self, tool_name, tool_input, tool_result):
        if tool_name == "blq_run" or "pytest" in str(tool_input):
            # Parse test results, update failure counter
            failures = parse_test_failures(tool_result)
            if failures > previous_failures:
                return PostAction(
                    type="warn",
                    message=f"New failures introduced: {failures - previous_failures}. "
                            f"Consider reverting the last edit.",
                )
            if failures == 0 and previous_failures > 0:
                return PostAction(
                    type="celebrate",
                    message=f"All tests passing! {previous_failures} failures fixed.",
                )

class FledglingPostPlugin(Plugin):
    """After file_edit, check if the agent should read related files."""

    def validate(self, tool_name, tool_input, tool_result):
        if tool_name in ("Edit", "file_edit"):
            edited_file = tool_input.get("file_path", "")
            # Query Fledgling for files that import from the edited file
            related = query_fledgling(
                f"SELECT file FROM imports WHERE imported_module LIKE '%{edited_file}%'"
            )
            if related:
                return PostAction(
                    type="suggest",
                    message=f"You edited {edited_file}. These files import from it: "
                            f"{', '.join(related)}. Consider checking they still work.",
                )

Grade of the coordinator

The coordinator is level 1 — specified mutations over structured data:

  • Path guard: reads mode state (JSON), checks path against a list, allows or blocks

  • Mode controller: reads counters, compares to thresholds, updates mode state

  • Coach: queries Fledgling (level 0), formats a suggestion string

  • Quartermaster: pattern-matches on model name and task type, sets configuration

No computation channels. No trained judgment. Every decision is traceable to a specified rule in config.toml. The coordinator stays in the specified band.

Dependency architecture

toolcraft (coordinator, level 1)
├── reads from: Fledgling (level 0, via query)
├── reads from: blq (level 1, via events/status)
├── reads from: jetsam (level 1, via state)
├── writes to: jetsam state (mode transitions)
├── writes to: Claude Code context (coach suggestions)
├── writes to: MCP tool config (kit activation)
└── enforces: path protection (PreToolUse hook)

Each dependency is specified and auditable. The coordinator never interprets executable specification. It reads structured data and applies rules.