---
slug: "pi-committer"
source_type: "readme"
source_url: "https://cdn.jsdelivr.net/gh/tmonk/pi-committer@main/README.md"
repo: "https://github.com/tmonk/pi-committer"
source_file: "README.md"
branch: "main"
---
# pi-committer

Conventional commit automation for the [pi coding agent](https://pi.ai).

Creates commits as you work. The extension is opt-in — enable it per-project with a config file.

## Features

- Commits automatically when a [pi-goal](https://pi.dev/packages/@capyup/pi-goal) completes (`on_goal` trigger, opt-in). Other modes available.
- Splits diffs into separate commits via a subagent (e.g. feature and tests in one commit, docs in another).
- Commit messages generated by a subagent using your model.
- Pick the model with `/commit-model` or the `subagent_model` config key.
- Detects and commits in any git repositories the agent touched.
- `commit_changes` tool that agents can call to checkpoint work.
- Exclusion patterns for files matching glob patterns (`*.log`, `build/`, etc.).
- Slash commands: `/commit`, `/commit-config`, `/commit-model`.

## Quick start

### Install from npm (recommended)

```bash
pi install npm:pi-committer
```

This installs the package globally. Pi auto-loads it on startup.

### Install from source

```bash
git clone https://github.com/tmonk/pi-committer.git
cd pi-committer && npm install

# Load it in a session
pi -e ./index.ts
```

### Global auto-discovery (source install)

```bash
ln -s ~/projects/pi-committer ~/.pi/agent/extensions/pi-committer
```

## Configuration

Create `.pi-committer.toml` (or `.pi-committer.json`) in your project root. The extension walks up directories to find it.

```toml
[committer]
enabled           = false             # off by default; set to true to enable
trigger_mode      = "on_goal"       # on_goal | agent_sensible | after_tool | manual
detailed_body     = true
min_changes       = 1
staged_commits    = true
exclude_patterns  = ["*.log", "node_modules/"]

# When true and trigger_mode is "on_goal", the commit_changes tool will skip
# when pi-goal has an active goal and defer to the automatic commit-after-audit
# flow. Defaults to false — commit_changes always proceeds immediately unless
# you explicitly opt in to deferral.
# defer_to_goal_audit = false

# Minimum number of changed files before the subagent is called for grouping.
# Small change sets below this threshold use a single commit with a
# subagent-generated message (unless also below subagent_message_min_files).
# Default: 15 (determined empirically — see benchmark results).
# subagent_grouping_min_files = 15

# Minimum number of changed files before the subagent is called for the
# commit message. Below this threshold, the deterministic fallback is used
# directly (no LLM call at all). Default: 3 — 1-2 file changes are simple
# enough for deterministic.
# subagent_message_min_files = 3

# Optional: override the model used by the commit-message subagent
# subagent_model = "openai/gpt-4o-mini"

# Threshold for async (background subprocess) commits. When the number of changed
# files >= this value, /commit and commit_changes fork the commit pipeline into a
# detached subprocess so the conversation continues immediately. The widget shows
# progress and Esc cancels. Default: 10. Set to 0 to disable async (always sync).
# async_threshold = 10

# Optional: extend conventional commit types / restrict scopes
# custom_types    = ["api", "wip"]
# allowed_scopes  = ["api", "cli", "core"]
```

### Trigger modes

| Mode | Behaviour |
|---|---|
| `on_goal` | Commits when a goal transitions to `complete` |
| `agent_sensible` | Commits after every agent turn |
| `after_tool` | Commits after each tool call |
| `manual` (default) | Never auto-commits; use `/commit` or `commit_changes` only |

## Usage

First, enable auto-commit in your project config:

```toml
# .pi-committer.toml
[committer]
enabled = true
```

### Auto-commit on goal completion

With `enabled = true` and `trigger_mode = "on_goal"` (default), the extension hooks into [pi-goal](https://pi.dev/packages/@capyup/pi-goal)'s lifecycle. When a goal managed by pi-goal transitions to **complete** status, pi-committer automatically stages and commits the changes:

```
/goals "Add user authentication"   ← start a goal via /goals
> agent implements auth logic       ← work happens
> pi-goal marks the goal complete   ← pi-committer auto-commits
```

The commit is triggered by the goal state transition — no manual `/commit` needed.

### Manual commit

```
/commit
```

Or ask the agent to "save my progress" and it will call the `commit_changes` tool.

### Choose the commit-message model

```
/commit-model
```

Opens an interactive selector. Defaults to your current agent model.

### Reload config

```
/commit-config
```

## Staged commits

When `staged_commits = true` (default), the subagent receives the diff and organizes changes into commit groups. Editing a source file, adding tests, and updating docs might produce:

```
feat(api): add user authentication endpoint
test(api): add authentication tests
docs: update API documentation
```

The subagent decides the grouping from the diff content, not from file extensions.

## Multi-repo

If the agent edits files in multiple git repositories during a session, `commit_changes` finds and commits in all of them. Detection works via session tool-call history — repos where the agent created or modified files using `write` or `edit` tools are detected and added on top of the primary working directory.

## Deterministic commit message fallback

When the subagent is unavailable (e.g., SDK load failure) or returns no result, pi-committer generates a deterministic commit message using string analysis — no LLM calls needed:

- **Smart scope**: Uses the longest common ancestor directory across all changed files. If files span unrelated directories, scope is omitted entirely.
- **Specific description**: Extracts meaningful keywords from file names (strips extensions, skips boilerplate like `__init__` and `conftest`, converts `snake_case` to readable words). Never says "update N modules".
- **Structured body**: Includes the description summary line followed by a file list with change stats.

Every subagent fallback decision point logs a `DIAG:` diagnostic message to help identify root causes.

## Architecture

```
Extension events (turn_end, tool_result, goal_event)
        │
        ▼
  commitAllRepos(dir, ctx)
        │
        ├─ findDirtyRepos(ctx)     discover all dirty repos
        │
        └─ tryCommit(repo, ...)    for each dirty repo
               │
               ├─ stageAll / unstageExcluded
               │
               ├─ generateStagedCommitGroups   subagent decides grouping
               │      └─ createAgentSession    no tools, diff inline
               │             └─ prompt: "Organize changes into logical commits"
               │
               ├─ generateCommitMessageViaSubagent   single-commit fallback
               │
               └─ git commit for each group
```

The subagent uses `createAgentSession` from `@earendil-works/pi-coding-agent`, the same pattern as pi-goal's `runGoalCompletionAuditor`. No `pi-subagents` dependency required.

## Requirements

- [pi coding agent](https://pi.ai) 0.71+
- Node.js 18+
- Git
- `smol-toml` (installed via `npm install`)

## Development

```bash
git clone <this-repo>
cd pi-committer
npm install

npm test            # unit tests
npm run test:e2e    # end-to-end tests
```
## License

MIT
