Docs · IBM Bob

Run Cleave in Bob

Cleave's planner is a custom mode in IBM Bob. Bob groups and orders the hunks Cleave gives it, and it has no tool that can write code. These are the files the mode ships with, exactly as cleave init installs them.

01

Install the engine and the mode

The engine is a Python package with a cleave command. cleave init adds the mode, the MCP server entry, the hooks and the mode rules to your repository's .bob/ folder. Modes, MCP servers and hooks you already have stay as they are, and running it again changes nothing.

From the Cleave repository
uv tool install ./packages/engine
In the repository you want to split
cleave init

02

The ✂ Cleave mode

The mode's tool groups are the guarantee. It can read, call MCP tools, spawn read-only explore subagents and keep a todo list. It has no edit and no execute group, so it cannot change a file or run a command, however it is prompted.

.bob/custom_modes.yaml
customModes:
  - slug: cleave
    name: ✂ Cleave
    description: Split a large diff into a proven stack. Never writes code.
    roleDefinition: >-
      You split an existing diff into an ordered stack of layers. You only group
      and order the atoms Cleave gives you. You never write or edit code.
    whenToUse: A branch or PR is too large to review as one change.
    customInstructions: Follow .bob/rules-cleave/. Change the plan only with cleave_* tools.
    groups: [read, mcp, subagent, todo]
    allowedSubagents: [explore]              # read-only subagents
  • read
  • mcp
  • subagent (explore)
  • todo
  • edit
  • execute

The mode follows a fixed procedure, shipped as its rules:

.bob/rules-cleave/01-procedure.md
# 01 — Procedure

You split an existing change into a stack of layers. You never write or edit code, never
run commands, and never switch modes. Every change to the plan goes through `cleave_*` tools.

1. **Status.** Call `cleave_status`. If a run is already active, continue it.
2. **Start.** Otherwise read the request, `Cleave <head> onto <base>`, and call
   `cleave_start(base, head)` with those branch names. The result lists the slices.
3. **Explore.** Start one read-only `explore` subagent per slice file. Each reads its slice
   and returns, for every atom id in it, a concern label (≤ 60 characters) and an intent
   (≤ 200 characters). If subagents aren't available, read the slices yourself.
4. **Propose.** Call `cleave_propose_plan` with ordered layers and the labels
   (`02-plan-schema.md`): foundations first (models, schemas, configuration), then logic,
   then interfaces; tests in the same layer as the code they test; each layer under the
   repository's line limit where the dependencies allow. Fix any returned violation with
   another proposal or `cleave_move_atoms` before verifying.
5. **Verify.** Call `cleave_verify`. If it returns `pending`, call
   `cleave_verify_status(round)` until the status is `pass` or `fail`.
6. **Repair.** For each failing layer follow `03-repair.md`: `cleave_read_log`, then
   `cleave_move_atoms`, then verify again. At most 3 repair rounds.
7. **Describe.** Call `cleave_describe_layer(n, title, body)` for every layer: a pull
   request title and a short body saying what the layer adds and why it stands alone.
8. **Finish.** Call `cleave_finish`, then end the task with a summary: the layers, the five
   checks, and the report path it returned.

03

Cleave tools: Bob's only write surface

Bob changes the plan only through the Cleave MCP server. None of its eleven tools can write source code; the ones that write touch the plan, .cleave/ or cleave/* branches built from existing hunks.

.bob/mcp.json
{
  "mcpServers": {
    "cleave": {
      "command": "cleave",
      "args": ["mcp"],
      "alwaysAllow": [
        "cleave_start",
        "cleave_status",
        "cleave_atoms",
        "cleave_graph",
        "cleave_propose_plan",
        "cleave_move_atoms",
        "cleave_verify",
        "cleave_verify_status",
        "cleave_read_log",
        "cleave_describe_layer",
        "cleave_finish"
      ]
    }
  }
}

In your repository, cleave init writes the full path of cleave and mcp --repo <repository> into this entry, because Bob may start MCP servers outside the repository and without your shell's PATH.

ToolWrites
cleave_start(base, head)

Atomize, build the graph, write slices and open a run

.cleave/ only
cleave_status()

Atom count, current plan version, last verify result

—
cleave_atoms(slice?)

Atoms with file, line counts and symbols

—
cleave_graph(atom_id?)

Dependency edges and forced groups

—
cleave_propose_plan(layers)

Check coverage and order, save a plan version, return violations

Plan only
cleave_move_atoms(ids, to_layer, reason)

New plan version plus violations

Plan only
cleave_verify()

Build every prefix and run the check command in parallel

cleave/* branches
cleave_verify_status(round)

Poll a long verification round

—
cleave_read_log(layer, round)

Excerpt of a failing layer's log

—
cleave_describe_layer(n, title, body)

Store the pull request text for a layer

Text only
cleave_finish()

Write the report and close the run

.cleave/ only

04

Hooks: a second lock and an audit trail

A PreToolUse hook guards every tool call while a run is active, and a PostToolUse hook appends each call to the run's event log. That log is where “0 lines written” on every proof page comes from. Hooks are shell commands, so they cost no Bobcoins.

.bob/settings.json
{
  "hooks": {
    "PreToolUse": [
      { "hooks": [{ "type": "command", "command": "python3 .bob/hooks/guard.py", "timeout": 10 }] }
    ],
    "PostToolUse": [
      { "hooks": [{ "type": "command", "command": "python3 .bob/hooks/audit.py", "timeout": 10 }] }
    ]
  }
}
  • Read tools are allowed.
  • Read-only explore subagents are allowed.
  • Todo updates, questions to you and finishing the task are allowed.
  • Calls to the cleave MCP server are allowed.
  • Everything else is blocked with exit code 2 while a run is active: file writes, commands, mode switches and other MCP servers.

05

Run a split in Bob IDE

Switch Bob to ✂ Cleave and name the branch to split. Bob reads the diff in parallel slices with explore subagents, proposes layers, verifies them, and moves atoms when a layer fails. After three rounds it merges the two layers and says so instead of writing glue code.

Message to Bob
Cleave feat/loyalty-and-seat-upgrades onto main

When the run finishes, send it to the web app to get the stack pages and a shareable proof.

Terminal
cleave push --title "<pull request title>" --pr <number> --head-branch <branch> --base-branch main

06

Headless runs with bob run

The same mode runs without the IDE through Bob Shell. The runner uses this command, and the cost cap comes from the repository's Bobcoin setting.

Terminal
bob run --mode cleave --format stream-json --max-cost 3 "Cleave feat/loyalty-and-seat-upgrades onto main"

To start splits from the web app instead, keep a runner going on your machine. It asks for runs you queue on New split, runs each one with the command above, streams its events to the run's page and sends the finished bundle, exactly like cleave push.

Terminal
export CLEAVE_URL=<your deployment> CLEAVE_TOKEN=clv_…
cleave runner

See what a finished run looks like.

Bob configuration references: custom modes, MCP, lifecycle hooks and non-interactive runs in the IBM Bob documentation.