Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

git-paw (Parallel AI Worktrees) orchestrates multiple AI coding CLI sessions across git worktrees from a single terminal using tmux.

Working with AI coding assistants like Claude, Codex, or Gemini is powerful — but what if you could run them in parallel across multiple branches at once? That’s exactly what git-paw does.

The Problem

You have a feature branch, a bugfix branch, and a refactoring branch. You want an AI assistant working on each one simultaneously. Normally you’d need to:

  1. Open multiple terminals
  2. Create git worktrees manually
  3. Navigate to each worktree
  4. Launch your AI CLI in each one
  5. Juggle between them

The Solution

With git-paw, you run a single command:

git paw

git-paw will:

  • Detect which AI CLIs you have installed (Claude, Codex, Gemini, Aider, etc.)
  • Prompt you to pick branches and a CLI (or different CLIs per branch)
  • Create git worktrees for each selected branch
  • Launch a tmux session with one pane per branch, each running your chosen AI CLI
  • Persist the session state so you can stop, resume, or recover after crashes

Key Features

  • One command to go from zero to parallel AI sessions
  • Smart start — reattaches to active sessions, recovers crashed ones, or launches fresh
  • Per-branch CLI selection — use Claude on one branch and Gemini on another
  • Session persistence — stop and resume without losing your place
  • Custom CLI support — register any AI CLI binary, not just the built-in ones
  • Presets — save branch + CLI combos in config for one-command launch
  • Non-interactive mode — pass --cli and --branches flags for scripting
  • Dry run — preview what git-paw will do before it does it

How It Works

┌──────────────────────────────────────────────────────┐
│                    tmux session                       │
│  ┌────────────────────┐  ┌────────────────────────┐  │
│  │  feat/auth → claude │  │  feat/api → claude     │  │
│  │                     │  │                        │  │
│  │  (git worktree)     │  │  (git worktree)        │  │
│  │                     │  │                        │  │
│  ├────────────────────┤  ├────────────────────────┤  │
│  │  fix/bug → gemini   │  │  refactor/db → aider   │  │
│  │                     │  │                        │  │
│  │  (git worktree)     │  │  (git worktree)        │  │
│  │                     │  │                        │  │
│  └────────────────────┘  └────────────────────────┘  │
└──────────────────────────────────────────────────────┘

Each pane runs in its own git worktree, so there are no branch conflicts. Your AI assistants work independently and in parallel.

Requirements

  • Git (2.20+ recommended for worktree improvements)
  • tmux (any recent version)
  • At least one AI coding CLI installed (see Supported AI CLIs)
  • macOS or Linux (Windows via WSL only)

Next Steps

Installation

Prerequisites

Before installing git-paw, ensure you have:

  • Git 2.20 or later
  • tmux — any recent version

Installing tmux

macOS:

brew install tmux

Ubuntu / Debian:

sudo apt install tmux

Fedora:

sudo dnf install tmux

Arch Linux:

sudo pacman -S tmux

Install git-paw

cargo install git-paw

Via Homebrew

brew install bearicorn/tap/git-paw

Shell installer

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/bearicorn/git-paw/releases/latest/download/git-paw-installer.sh | sh

From source

git clone https://github.com/bearicorn/git-paw.git
cd git-paw
cargo install --path .

Verify installation

git-paw --version

You should see output like:

git-paw 0.1.0

Since git-paw is named with the git- prefix, git recognizes it as a subcommand. Both of these work:

git-paw --help
git paw --help

Platform Support

PlatformSupport
macOS (ARM / Apple Silicon)Full support
macOS (x86_64 / Intel)Full support
Linux (x86_64)Full support
Linux (ARM64 / aarch64)Full support
WindowsWSL only

Windows (WSL)

git-paw requires tmux, which is not natively available on Windows. Use Windows Subsystem for Linux (WSL):

# Install WSL (PowerShell as admin)
wsl --install

# Then inside WSL:
sudo apt install tmux
cargo install git-paw

All git-paw features work inside WSL. Your AI CLIs must also be installed within the WSL environment.

Install an AI CLI

git-paw needs at least one AI coding CLI installed. See Supported AI CLIs for the full list. Some popular options:

# Claude Code
npm install -g @anthropic-ai/claude-code

# OpenAI Codex
npm install -g @openai/codex

# Aider
pip install aider-chat

Next Steps

Quick Start: Same CLI Mode

This walkthrough shows how to launch git-paw with the same AI CLI on all branches — the most common workflow.

Prerequisites

  • git-paw installed
  • tmux installed
  • At least one AI CLI installed (e.g., claude)
  • A git repository with multiple branches

Step 1: Navigate to your repo

cd ~/projects/my-app

Step 2: Launch git-paw

git paw

Step 3: Select your mode

git-paw presents a mode picker:

? How would you like to assign CLIs to branches?
> Same CLI for all branches
  Different CLI per branch

Select Same CLI for all branches and press Enter.

Step 4: Select branches

A multi-select list of all your branches appears with fuzzy search:

? Select branches (space to toggle, enter to confirm):
  [ ] main
  [x] feat/auth
  [x] feat/api
  [ ] fix/typo
  [x] refactor/db

Use arrow keys to navigate, Space to toggle, and Enter to confirm.

Step 5: Select your CLI

Pick which AI CLI to use on all selected branches:

? Select AI CLI:
> claude
  codex
  gemini

Step 6: git-paw does the rest

git-paw now:

  1. Creates a git worktree for each selected branch
  2. Creates a tmux session named paw-my-app
  3. Opens one pane per branch
  4. Launches your chosen CLI in each pane
  5. Saves the session state for later recovery
Creating worktrees...
  ✓ my-app-feat-auth (feat/auth)
  ✓ my-app-feat-api (feat/api)
  ✓ my-app-refactor-db (refactor/db)

Launching tmux session: paw-my-app
  Pane 1: feat/auth → claude
  Pane 2: feat/api → claude
  Pane 3: refactor/db → claude

Attaching to session...

You’re now inside a tmux session with three panes, each running Claude in its own worktree:

┌─── feat/auth → claude ────────┬─── feat/api → claude ─────────┐
│                                │                                │
│  Claude is ready to help...    │  Claude is ready to help...    │
│                                │                                │
├─── refactor/db → claude ──────┴────────────────────────────────┤
│                                                                 │
│  Claude is ready to help...                                     │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Mouse mode is enabled by default — click a pane to switch to it, or drag borders to resize.

Non-interactive shortcut

Skip all prompts by passing flags:

git paw start --cli claude --branches feat/auth,feat/api,refactor/db

What’s next

  • Switch panes: Click with mouse, or use Ctrl-b then arrow keys
  • Detach: Press Ctrl-b d to detach from tmux (session keeps running)
  • Reattach: Run git paw again — it detects the active session and reattaches
  • Stop: Run git paw stop to kill tmux but keep worktrees
  • Purge: Run git paw purge to remove everything

See the User Guide for the full details.

Quick Start: Per-Branch CLI Mode

This walkthrough shows how to assign different AI CLIs to different branches — useful when you want to compare AI assistants or use specialized tools for specific tasks.

Scenario

You have three branches and want to use:

  • Claude for the auth feature (complex logic)
  • Gemini for the API work (lots of boilerplate)
  • Aider for the database refactor (incremental edits)

Step 1: Launch git-paw

cd ~/projects/my-app
git paw

Step 2: Select per-branch mode

? How would you like to assign CLIs to branches?
  Same CLI for all branches
> Different CLI per branch

Step 3: Select branches

? Select branches (space to toggle, enter to confirm):
  [ ] main
  [x] feat/auth
  [x] feat/api
  [x] refactor/db

Step 4: Assign a CLI to each branch

git-paw prompts you for each branch individually:

? Select CLI for feat/auth:
> claude
  codex
  gemini
  aider
? Select CLI for feat/api:
  claude
  codex
> gemini
  aider
? Select CLI for refactor/db:
  claude
  codex
  gemini
> aider

Step 5: Watch it launch

Creating worktrees...
  ✓ my-app-feat-auth (feat/auth)
  ✓ my-app-feat-api (feat/api)
  ✓ my-app-refactor-db (refactor/db)

Launching tmux session: paw-my-app
  Pane 1: feat/auth → claude
  Pane 2: feat/api → gemini
  Pane 3: refactor/db → aider

Attaching to session...

The tmux session shows each pane with its branch and CLI clearly labeled in the pane border:

┌─── feat/auth → claude ────────┬─── feat/api → gemini ─────────┐
│                                │                                │
│  Claude is ready to help...    │  Gemini is ready...            │
│                                │                                │
├─── refactor/db → aider ───────┴────────────────────────────────┤
│                                                                 │
│  Aider v0.x loaded...                                           │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Comparing approaches

Per-branch mode is great for:

  • A/B testing AI assistants — give the same task to Claude and Gemini, compare results
  • Specialization — use a code-generation-focused tool for boilerplate and a reasoning-focused tool for complex logic
  • Trying new tools — test a new AI CLI on one branch while using your trusted tool on others

Recovery

Session state captures the per-branch CLI assignments. If your terminal closes or tmux crashes:

git paw

git-paw detects the saved session, recreates tmux, and relaunches each branch with its assigned CLI — no re-selection needed.

Next Steps

  • User Guide — session management, presets, dry-run, and more
  • Configuration — save per-branch presets in config

Quick Start: Supervisor Mode

This walkthrough shows how to launch git-paw in supervisor mode — git-paw’s hands-off, spec-driven workflow where a supervisor agent orchestrates several coding agents in parallel, runs tests between merges, and writes a final session report.

What Supervisor Mode Does

Supervisor mode (git paw start --supervisor) launches every pending spec as its own coding agent in a background tmux pane and gives you an interactive supervisor agent in the foreground pane. The supervisor automatically launches all agents per spec, runs the configured test_command between merges, walks branches in topological order based on declared dependencies, writes a .git-paw/session-summary.md when the run is complete, and auto-approves common safe permission prompts (e.g. cargo test, git commit, broker curl calls) so the run can proceed unattended.

Prerequisites

  • The standard git-paw prerequisites (git-paw installed, tmux, an AI CLI on PATH, a git repo).
  • [broker] enabled = true in your config — supervisor mode requires the coordination broker.
  • A [supervisor] section is recommended but optional. If it is missing, git-paw prompts for the supervisor CLI, the test command, and the agent approval level on first run.
  • A specs directory configured via [specs] (default: specs/) containing at least one pending spec.

Smallest Runnable Example

1. Create one OpenSpec-format spec

mkdir -p specs/add-greeting
cat > specs/add-greeting/tasks.md <<'MD'
## Add a greeting helper

- [ ] Add `pub fn greet(name: &str) -> String` to `src/lib.rs`.
- [ ] Add a unit test asserting `greet("paw") == "hello, paw"`.
- [ ] Run `cargo test` and make sure it passes.
MD

2. Configure broker + supervisor

.git-paw/config.toml:

[broker]
enabled = true
port = 9119

[specs]
dir = "specs"
type = "openspec"

[supervisor]
enabled = true
cli = "claude"
test_command = "cargo test"
agent_approval = "auto"

3. Launch

git paw start --supervisor

git-paw creates a worktree per spec, opens a tmux session with the supervisor in pane 0, the dashboard in pane 1, and one coding agent per spec in the remaining panes (pane 2 onwards). The supervisor injects its boot prompt automatically; you only need to chat with it for high-level approvals.

When every agent has reported verified and the supervisor has merged each branch in topological order, git-paw writes .git-paw/session-summary.md and exits.

Skipping Supervisor for One Session

If your project sets [supervisor] enabled = true (the recommended setup for active development), the supervisor runs by default for every git paw start. To skip the supervisor for a single session — e.g. for a quick debug-only run or a one-off branch flip — without editing the config, pass --no-supervisor:

# Project config says [supervisor] enabled = true, but we want a plain session
git paw start --no-supervisor --cli claude --branches feat/quick-fix

--no-supervisor is the highest-precedence step in the supervisor mode resolution chain — it wins over both [supervisor] enabled = true in config and any prompt. It is mutually exclusive with --supervisor; passing both fails at parse time.

The flag only affects the current session. Your config is untouched, so the next git paw start returns to supervisor mode as configured.

Key Config Knobs

The full reference lives in Configuration → Supervisor. The fields you will reach for most often:

FieldWhat it controls
[supervisor].enabledDefault to supervisor mode without passing --supervisor.
[supervisor].cliWhich AI CLI runs as the supervisor (falls back to default_cli).
[supervisor].test_commandCommand run after each agent reports done and again after every merge.
[supervisor].agent_approval"manual", "auto", or "full-auto" — translates into CLI permission flags.
[supervisor.auto_approve].enabledMaster switch for git-paw’s safe-prompt auto-dismisser.
[supervisor.auto_approve].safe_commandsProject-specific command prefixes appended to the built-in safe list.
[supervisor.auto_approve].approval_level"off", "conservative", or "safe" preset for the auto-approve whitelist.
[supervisor.common_dev_allowlist].enabledSeeds Claude’s allowed_bash_prefixes with a curated preset of safe dev-loop commands on supervisor start. Default true.
[supervisor.common_dev_allowlist].extraProject-specific prefix patterns appended to the built-in preset (e.g. ["pnpm test", "deno fmt"]).

See Configuration → Broker for [broker] settings and Configuration → Dashboard if you want the live broker-message panel turned on.

Broker Wire Format

Every coordination message the supervisor and its agents exchange goes through the broker as a JSON envelope of the form {"type": "agent.<variant>", "agent_id": "<slug>", "payload": {...}}. The canonical examples below come from src/broker/messages.rs. agent_id slugs must be lowercase alphanumeric + - / _ (no slashes — feat/auth is the git branch name; feat-auth is the slug form the broker accepts).

agent.status (auto-published by the filesystem watcher whenever an agent’s worktree changes; the manual form below is an escape hatch):

curl -s -X POST "$GIT_PAW_BROKER_URL/publish" \
  -H "Content-Type: application/json" \
  -d '{"type":"agent.status","agent_id":"feat-auth","payload":{"status":"working","modified_files":["src/auth.rs"],"message":"wiring JWT verifier"}}'

agent.artifact (auto-published by the post-commit hook with the committed files; manual form is the escape hatch for code-less tasks):

curl -s -X POST "$GIT_PAW_BROKER_URL/publish" \
  -H "Content-Type: application/json" \
  -d '{"type":"agent.artifact","agent_id":"feat-auth","payload":{"status":"done","exports":["AuthClient"],"modified_files":[]}}'

agent.blocked, agent.intent, agent.question, agent.feedback, and agent.verified round out the seven shipped variants — see the full reference with payload-field semantics in Agent Coordination.

Where to Look When Things Go Wrong

When a supervisor run misbehaves, three places have everything you need:

  1. Dashboard pane (pane 1). With [broker] enabled = true the dashboard runs in pane 1 of the supervisor session and updates every second. Use it to spot which agent is blocked, which is working, and how long it has been since each agent’s last status update. See the Dashboard chapter for status-symbol meanings and controls.
  2. Supervisor pane (pane 0). The supervisor CLI runs here and prints feedback inline as it walks agents through the merge plan. Scroll back with tmux’s copy mode if you need to inspect earlier reasoning.
  3. .git-paw/session-summary.md. Written by the supervisor when the run exits. Contains per-branch test results, merge order, and any agents that were skipped. If the supervisor exited early, this file tells you why.
  4. .git-paw/broker.log. A JSONL audit trail of every message the broker handled — agent.status, agent.artifact, agent.blocked, agent.intent, agent.question, agent.feedback, and agent.verified. Tail it live with tail -f .git-paw/broker.log to watch coordination as it happens. The file is flushed every five seconds; see Agent Coordination for the full schema.

If you need to inspect an individual agent’s pane history, run tmux capture-pane -p -t <session>:<window>.<pane> against the session printed at launch.

Governance

If your project has existing governance docs (ADRs, DoD, security checklist, test strategy, constitution), point the supervisor at them via the [governance] table in .git-paw/config.toml. When set, those paths are injected into the supervisor’s boot prompt and the supervisor consults each doc as a sub-step of its spec-audit step. Findings flow through the existing agent.feedback errors path — no new wire format. See the Governance chapter for the full config, what the supervisor checks per doc, and illustrative starting-point examples for each doc type.

What’s Next

User Guide

This guide covers the full range of git-paw features beyond the quick starts.

Starting Sessions

Interactive start

Running git paw (or git paw start) with no flags launches the interactive flow:

  1. Mode selection — same CLI for all branches, or different CLI per branch
  2. Branch selection — multi-select with fuzzy search
  3. CLI selection — single pick (uniform) or per-branch assignment

Smart start behavior

git paw start inspects the current repo and decides what to do:

StateBehavior
Active tmux session existsReattaches immediately
Saved session, tmux dead (crash/reboot)Auto-recovers: reuses worktrees, recreates tmux, relaunches CLIs
No sessionFull interactive launch

You never need to think about whether to “start” or “resume” — just run git paw.

Non-interactive start

Skip prompts with flags:

# Specify both CLI and branches — no prompts at all
git paw start --cli claude --branches feat/auth,feat/api

# Specify just CLI — still prompted for branches
git paw start --cli claude

# Specify just branches — still prompted for CLI
git paw start --branches feat/auth,feat/api

Using presets

Define named presets in your config (see Configuration):

git paw start --preset backend

This uses the branches and CLI defined in the [presets.backend] section of your config.

CLI Modes

Same CLI for all branches

The default mode. Every branch gets the same AI CLI. Best for:

  • Working on related features with your preferred tool
  • Batch processing branches with a single assistant

Different CLI per branch

Assign a different CLI to each branch. Best for:

  • Comparing AI assistants side by side
  • Using specialized tools for specific tasks
  • Trying a new CLI on one branch while keeping your usual tool on others

Session Management

Checking status

git paw status

Displays the current session state:

Session: paw-my-app
Status:  🟢 active
Created: 2025-01-15T10:30:00Z

Worktrees:
  feat/auth    → claude  (../my-app-feat-auth)
  feat/api     → claude  (../my-app-feat-api)
  refactor/db  → aider   (../my-app-refactor-db)

Status indicators:

  • 🟢 active — tmux session is running
  • 🟡 stopped — session state saved, tmux not running (recoverable)
  • No session — nothing saved for this repo

Stopping a session

git paw stop

This kills the tmux session but preserves:

  • Git worktrees (with any uncommitted work)
  • Session state file (branch/CLI assignments)

Run git paw later to recover the session with the same setup.

Purging a session

git paw purge

The nuclear option. Removes:

  • Tmux session
  • All git worktrees created by git-paw
  • Session state file

Requires confirmation. Use --force to skip:

git paw purge --force

Dry Run

Preview what git-paw will do without executing:

git paw start --dry-run

Or with flags:

git paw start --cli claude --branches feat/auth,feat/api --dry-run

This runs the detection, selection, and planning steps, then prints the session plan and exits without creating worktrees or tmux sessions.

Tmux Navigation

Once inside a git-paw tmux session:

ActionKeys
Switch paneClick with mouse, or Ctrl-b + arrow key
Resize paneDrag border with mouse, or Ctrl-b Ctrl-arrow
Detach (keep running)Ctrl-b d
Scroll upCtrl-b [ then arrow keys, q to exit
Zoom pane (fullscreen toggle)Ctrl-b z

Mouse mode is enabled by default, so clicking and dragging just works. You can disable it in your config.

One Session Per Repo

git-paw manages one session per repository. If you run git paw in a repo that already has a session, it reattaches rather than creating a second session.

To work with multiple repos simultaneously, open separate terminals and run git paw in each repo directory.

Session Lifecycle

This chapter covers what git paw start does to your worktrees on each launch (or relaunch), and how to control its behaviour when the defaults don’t fit.

Rebase on start

Every git paw start rebases each existing agent branch onto the repository’s default branch (whatever origin/HEAD tracks — typically main) before opening or reopening that branch’s worktree. This keeps agents starting from current main, which matters when the supervisor (you) advances main while agents are still working on their branches.

Why this is the default:

  • A supervisor session typically lands commits on main while one or more agents are running on feat/* branches. Without rebasing on start, every subsequent agent commit chains from the stale baseline, forcing a per-branch rebase at merge time.
  • The rebase is cheap when there’s nothing to do: git rebase exits zero with no rewrite when the branch is already up to date.
  • The branch ref is updated even in the resume case — git paw start on a session whose worktrees already exist on disk picks up the new main commits transparently.

Rebases that don’t happen:

  • A brand-new branch (one git-paw creates via git worktree add -b during this launch) is not rebased. It’s already at the current default-branch tip by construction.
  • If the repo has no origin/HEAD (a rare brand-new repo with no remote), the rebase step errors out cleanly. Workaround: run with --no-rebase, or push the default branch to origin and set origin/HEAD first.

Opting out: --no-rebase

Pass --no-rebase when you want the pre-v0.6 behaviour — agent branches opened at their current SHA with no rebase attempt:

git paw start --no-rebase
git paw start --no-rebase --cli claude --branches feat/auth,feat/api
git paw start --no-rebase --supervisor   # combines with other flags

Use cases:

  • You are deliberately working against a stale baseline (e.g. an agent is reproducing a bug on a specific historical commit).
  • An external tool has pinned a pre-rebase SHA on the agent branch and rewriting history would break it.
  • You’re scripting against the pre-v0.6 launch contract and don’t want the new default behaviour.

Rebase conflicts

If the rebase hits a conflict — typically because both main and the agent branch modified the same line of the same file — git paw start:

  1. Runs git rebase --abort on the affected branch.
  2. Leaves the branch at its pre-rebase HEAD (no half-rebased state survives).
  3. Exits with an error like Error: rebase onto main failed: <git's stderr listing the conflicting files>.

When you see this error:

  1. cd into the affected agent’s worktree (typically at ../<project>-<branch-slug>/).
  2. Inspect the conflicting files git named in the error.
  3. Either reconcile manually (e.g. git rebase main followed by resolving conflicts and git rebase --continue), or decide the change should land via a merge commit instead.
  4. Once the branch is in a clean state, rerun git paw start (with or without --no-rebase depending on what you decided).

The conflict-abort path is intentional and not configurable — leaving a branch half-rebased poisons every subsequent git operation against it, which is far worse for the agent in that pane than a clean error at launch time.

What rebase does NOT do

  • It does not git fetch or git pull for main. The rebase target is whatever origin/HEAD tracks locally; the user owns the remote-sync decision.
  • It does not rewrite the working tree files inside a surviving worktree. After a rebase-on-resume, the worktree directory’s files may diverge from the branch’s new HEAD until the agent does its own checkout or pull inside the worktree. This is the standard git rebase caveat — git status from inside the pane will surface it.
  • It does not rebase one agent branch onto another agent branch. Only feat/<change> onto the repository’s default branch.
  • It does not loop or retry on failure. One attempt, abort on conflict, surface the error.

Resume vs. fresh launch

The rebase step runs in both flows:

  • Fresh launch: branch exists locally, no worktree yet. Branch is rebased onto main, then the worktree is created at the rebased SHA.
  • Resume (worktree already on disk from a prior session): branch is rebased in the existing worktree, the existence check confirms the worktree is registered for that branch, and git paw start returns success with the worktree’s branch ref now pointing at the rebased HEAD.

If you’re debugging a launch and want to know whether the rebase modified anything, git rev-parse <branch> from the main repo before and after git paw start will show the SHA change (or lack thereof).

Spec-Driven Launch

The --from-all-specs and --specs flags let you define branches, CLI assignments, and prompts in spec files instead of using interactive selection. git-paw reads spec files from a configured directory, creates worktrees for each pending spec, and launches AI CLIs with the spec content injected into each worktree’s AGENTS.md.

Quick Example

# Initialize repo config (creates .git-paw/config.toml)
git paw init

# Add spec files to your specs/ directory, then launch every discovered spec
git paw start --from-all-specs

# Narrow to specific specs without editing config
git paw start --specs add-auth,fix-session

# Open a multi-select picker (requires an interactive terminal)
git paw start --specs

Picking specs at launch time

You have three ways to control which specs are launched:

  • --from-all-specs — launches every discovered spec across the configured backend.
  • --specs NAME[,NAME...] — comma-separated list of spec names. Mirrors the existing --branches feat/a,feat/b syntax. Unknown names exit with the discovered-set listed as candidates so you can correct quickly.
  • --specs (bare, no values) — opens a multi-select picker showing every discovered spec. Each row shows the unit identifier; for Spec Kit features that decompose into multiple worktrees, the row also shows a worktree-count hint (e.g. 003-user-list — 3 worktrees: 2 [P] + 1 phase/).

--from-all-specs and --specs are mutually exclusive — they express opposing intents — and clap rejects any invocation that combines them.

Picker requires a TTY

The bare --specs form requires an interactive terminal. When stdin is not a TTY (CI, scripted invocation, redirected input), git-paw exits with an actionable error pointing at the explicit forms:

error: --specs without values requires an interactive terminal
  Use `--specs NAME[,NAME...]` to narrow explicitly, or
  `--from-all-specs` to launch every discovered spec.

Name resolution rules

--specs NAME matches against the discovered set using these strategies in order:

  1. Exact match on the spec id (case-sensitive). Matches OpenSpec change names, Markdown filename stems, or Spec Kit decomposed entry ids like 003-user-list-T009.
  2. Spec Kit feature-name match — the value matches a Spec Kit feature directory prefix (e.g. 003-user-list). All decomposed entries belonging to that feature are launched together.
  3. Spec Kit numeric prefix — a digits-only value (e.g. 003) matches a unique feature directory whose name starts with 003 followed by a non-digit boundary. Ambiguous prefixes (two features both starting with 003) are rejected with the candidate list.

Unknown names error out before any worktrees are created. The error message includes the unresolved name AND the discovered candidate list. There is no partial start.

Spec Formats

git-paw supports three spec formats: OpenSpec (directory-based), Markdown (file-based), and Spec Kit (.specify/-based, GitHub Spec Kit).

OpenSpec Format (default)

Each pending change lives in its own subdirectory under the specs directory. The subdirectory name becomes the branch identifier.

specs/
  add-auth/
    tasks.md          # Required — main prompt
    specs/
      jwt/spec.md     # Optional — supplementary spec
  fix-pagination/
    tasks.md

Discovery is by archive status, not task completion. --from-all-specs includes every change subdirectory under specs/ except anything under specs/archive/. A change with all tasks marked - [x] in its tasks.md is still picked up — task-completion is a progress tracker, not a discovery filter. After completing and verifying a change, run:

openspec archive <change-name>

before the next git paw start --from-all-specs invocation. openspec archive moves the change directory under specs/archive/<date>-<change>/ and syncs its delta specs into the main specs at specs/<capability>/spec.md. The next session won’t spawn a worktree for it.

tasks.md contains the prompt content sent to the AI CLI. It supports optional YAML frontmatter:

---
paw_cli: claude
---

## Implement JWT Authentication

Add JWT token support to the auth module.

The paw_cli field overrides the CLI for this specific spec. If omitted, the default resolution chain applies (see CLI Resolution below).

Supplementary spec files in specs/<name>/spec.md are appended to the prompt with section headers. File ownership can be declared with “Files owned:” or “Owned files:” followed by a markdown list.

Markdown Format

Flat .md files in the specs directory. Each file uses YAML frontmatter to control status and branch mapping.

specs/
  add-auth.md
  fix-pagination.md
  design-notes.md      # ignored — no paw_status: pending

Example file (specs/add-auth.md):

---
paw_status: pending
paw_branch: add-auth
paw_cli: claude
---

## Implement JWT Authentication

Add JWT token support to the auth module.

Frontmatter Fields

FieldRequiredDescription
paw_statusYesMust be "pending" to be included. Other values ("done", "in-progress") are ignored.
paw_branchNoBranch name suffix. Falls back to filename stem if absent.
paw_cliNoCLI override for this spec.

Only files with paw_status: pending are picked up.

Spec Kit Format

Spec Kit projects place each feature in its own directory under .specify/specs/<feature>/, alongside an optional .specify/memory/constitution.md for project-level rules.

.specify/
  memory/
    constitution.md            # Optional — project rules / governance
  specs/
    001-room-setup/
      spec.md                  # Feature requirements
      plan.md                  # Implementation plan
      tasks.md                 # Required — task list with phases
      checklists/              # Optional — advisory validation criteria
        security.md
    002-poker-voting/
      spec.md
      plan.md
      tasks.md

Auto-detection. When .specify/specs/ exists at the repo root and you have no [specs] section in .git-paw/config.toml, git paw start --from-all-specs defaults to specs.type = "speckit" with specs.dir = ".specify/specs". git paw init also detects .specify/ and writes the matching [specs] section to the generated config so the choice is locked.

You can override with --specs-format:

# Force Spec Kit even when [specs] config says otherwise
git paw start --from-all-specs --specs-format speckit

# Force OpenSpec on a project that has a `.specify/` folder
git paw start --from-all-specs --specs-format openspec

tasks.md decomposition. Spec Kit’s tasks.md files use ## Phase N: <Name> headings and - [ ] T<NNN> task lines. git-paw decomposes the current phase (the first phase with any incomplete task) into one worktree per kind:

  • Each incomplete [P]-marked task → its own worktree on task/<task-id>-<slug> (one agent per task, parallel).
  • All remaining incomplete non-[P] tasks → one consolidated worktree on phase/<feature>-<phase-slug> (one agent works through them sequentially, since the absence of [P] indicates shared files or context).
## Phase 2: Foundational
- [ ] T009 [P] Contract test POST /auth/otp/request   → task/t009-contract-test-...
- [ ] T010 [P] Contract test POST /auth/otp/verify    → task/t010-contract-test-...
- [ ] T011 Setup database schema                       ┐
- [ ] T012 Create auth tables                          ├ phase/<feature>-foundational
- [ ] T013 Seed test data                              ┘

In this example, one feature directory produces three worktrees and three branches. This “one Spec Kit feature → multiple branches” model is intentional — [P] tasks parallelise across worktrees; non-[P] tasks sequence within a single worktree.

Phase advancement. Phases earlier than the current one are assumed complete (all - [x]). Phases later than the current one are deferred — they only produce worktrees on a future scan, after every task in the current phase has been ticked off. Fully completed features (every task in every phase is - [x]) are skipped silently.

Boot prompts. Each Spec Kit worktree’s AGENTS.md contains:

  1. Feature Context — the full spec.md content.
  2. Implementation Plan — the full plan.md content (omitted when absent).
  3. Validation Criteria (advisory) — each checklists/<file>.md content under its own heading. Checklists are advisory in this release; full enforcement is planned for v1.0.0.
  4. Your Task — the single task description (task/... worktrees) or the ordered task list plus sequential-execution + - [x] writeback + agent.artifact (with status: "done") instructions (phase/... worktrees).

Task writeback. Agents working in a phase/... worktree flip - [ ] to - [x] in the worktree’s tasks.md as they complete each task. The writeback may be committed alongside the task’s code change or as a separate commit. Per-line edits across worktrees are merged by git in the normal way; the conflict-detection layer catches the pathological case of two worktrees racing on the same task ID.

Constitution wiring. When .specify/memory/constitution.md exists, git-paw exposes its path via the SpecKit backend’s detect_constitution probe. A future [governance.constitution] config slot (the governance-config change) will consume this path automatically, so projects that already have a Spec Kit constitution get governance configured for free.

Configuration

Configure spec scanning in .git-paw/config.toml (or the global config):

# Default CLI for spec-mode launches (bypasses picker when set).
default_spec_cli = "my-cli"

# Prefix for spec-derived branch names (default: "spec/").
branch_prefix = "spec/"

# Spec scanning configuration.
[specs]
dir = "specs"         # Directory containing spec files (relative to repo root)
type = "openspec"     # "openspec" (default), "markdown", or "speckit"

The interpretation of dir depends on the format:

  • openspec — directory of change subdirectories (<dir>/<change>/tasks.md).
  • markdown — directory of .md files (<dir>/<spec>.md).
  • speckit — directory of feature subdirectories (<dir>/<feature>/tasks.md); typically .specify/specs.

Branch Naming

Branch names depend on the format:

  • OpenSpec: <branch_prefix><id> where ID is the subdirectory name. specs/add-auth/ with prefix spec/ becomes branch spec/add-auth.
  • Markdown: <branch_prefix><id> where ID is paw_branch (if set) or the filename stem. specs/add-auth.md with prefix spec/ becomes branch spec/add-auth.
  • Spec Kit: branch prefix is task/ or phase/ depending on the entry kind; branch_prefix is not applied. Example: .specify/specs/003-user-list/tasks.md with task - [ ] T009 [P] Add login form produces branch task/t009-add-login-form.

CLI Resolution

When a spec-mode launch runs, CLIs are resolved in priority order:

  1. --cli flag (highest) — applies to all specs, no prompt
  2. paw_cli in spec — per-spec override from frontmatter
  3. default_spec_cli in config — fills remaining specs without prompt
  4. default_cli in config — pre-selects in picker for remaining
  5. Interactive picker (lowest) — prompts for any unresolved specs
# Override all specs to use claude
git paw start --from-all-specs --cli claude

# Use per-spec paw_cli and default_spec_cli from config
git paw start --from-all-specs

# Narrow to a subset
git paw start --specs add-auth,fix-session --cli claude

# Preview without executing
git paw start --from-all-specs --dry-run
git paw start --specs add-auth --dry-run

Combining --from-all-specs with supervisor mode

--from-all-specs --supervisor (or --from-all-specs with [supervisor] enabled = true in your config) engages the supervisor flow against the discovered specs. git-paw scans the configured specs directory, creates one worktree per spec, launches the dashboard pane and per-spec agent panes, and starts the supervisor CLI in your foreground terminal — same supervisor architecture as --branches-driven sessions, just with branches discovered from specs.

# Spec-driven session with supervisor watching
git paw start --from-all-specs --supervisor

# Same outcome via [supervisor] enabled = true in .git-paw/config.toml
git paw start --from-all-specs

# Skip supervisor for this session even if enabled in config
git paw start --from-all-specs --no-supervisor

When [broker] enabled = true (the default in supervisor mode), each spec agent pane receives a boot block via tmux send-keys carrying its BRANCH_ID, broker URL, and curl-publish patterns. This applies to both spec-mode-only (--from-all-specs without supervisor) and supervisor mode.

Non-interactive launches

If git paw start is invoked from a non-interactive terminal — CI, scripted invocation, or a harness tool that pipes stdin — git-paw skips the auto-attach step and prints an attach hint instead:

Session 'paw-myproject' started in detached mode.
Attach with:  tmux attach -t paw-myproject

For supervisor mode in a non-interactive context, the foreground supervisor CLI is also skipped (Claude/Codex etc. need a TTY to run interactively). The session, dashboard pane, and agent panes are all created; you can attach later from a real terminal and start the supervisor manually:

tmux attach -t paw-myproject
# in another terminal, in the repo root:
cd /path/to/repo && claude

Session Logging

git-paw can capture the full terminal output of each AI CLI pane to log files. This lets you review what happened in a session after it ends.

Enabling Logging

Add the [logging] section to your .git-paw/config.toml (or global config):

[logging]
enabled = true

When enabled, git-paw uses tmux pipe-pane to capture each pane’s output to a log file when a session starts.

Log Storage

Logs are stored under .git-paw/logs/ organized by session:

.git-paw/logs/
  paw-my-project/
    feat--add-auth.log
    fix--pagination.log

Branch names are sanitized for filenames — / becomes -- (e.g., feat/add-auth becomes feat--add-auth.log).

The git paw init command automatically adds .git-paw/logs/ to .gitignore so logs are never committed.

Replaying Logs

Use git paw replay to view captured logs.

List available sessions and branches

git paw replay --list

Shows all sessions and their logged branches.

Replay a branch

git paw replay feat/add-auth

Branch names are fuzzy-matched — you can use the original branch name, the sanitized filename, or a partial match.

By default, ANSI escape codes are stripped for clean text output.

Replay with colors

git paw replay feat/add-auth --color

Preserves ANSI colors and pipes through less -R for scrollable colored output.

Replay from a specific session

git paw replay feat/add-auth --session paw-my-project

By default, the most recent session is used. Use --session to specify an older one.

Flags

FlagDescription
--listList available log sessions and branches
--colorDisplay with ANSI colors via less -R
--session <name>Session to replay from (defaults to most recent)

AGENTS.md Injection

AGENTS.md is the full source of truth for an agent’s working context. When git-paw launches a session, it generates an AGENTS.md in each worktree containing the project’s root AGENTS.md content plus a managed git-paw section that carries the entire spec body for the agent’s assigned change: proposal, design (when present), task list, and any spec deltas. The agent reads the spec from AGENTS.md plus openspec/changes/<id>/, not from the boot prompt.

Boot-Prompt-Full-Body Model

Earlier versions of git-paw embedded a condensed spec excerpt inside the supervisor-mode boot prompt itself (“Branch + CLI + Spec content + Owned files”). In v0.5.0 the supervisor-mode boot prompt is intentionally short: it points the agent at AGENTS.md and at the change directory openspec/changes/<id>/, and tells the agent to read those files before acting.

The benefits of this split:

  • The spec body lives in a markdown file the agent can re-read at any time, not in volatile boot-prompt context.
  • The boot prompt becomes small enough to remain in the agent’s working context for the entire session, even after long conversations.
  • Manual operators can pre-stage the boot block at the pane input line via paste, the same way the supervisor stages it programmatically.
  • The same generator (src/agents.rs::setup_worktree_agents_md or its v0.5.0 successor) writes the same content in both supervisor mode and broker-only mode, so the agent’s experience does not depend on which mode launched it.

How It Works

  1. Worktrees. When a session starts, each worktree gets its own AGENTS.md placed at the worktree root. The file is overwritten on every git paw start so the spec body never goes stale.
  2. Exclusion. Worktree AGENTS.md files are added to the worktree’s .git/info/exclude so they never appear in git status or get committed alongside the agent’s work.

Markers

git-paw manages its section using HTML comment markers so the rest of the file (a hand-written project AGENTS.md, for example) is preserved across launches:

<!-- git-paw:start — managed by git-paw, do not edit manually -->

(git-paw content here — full spec body)

<!-- git-paw:end -->

Content between these markers is replaced on each launch. Content outside the markers is preserved. If no markers exist, the section is appended to the end of the file.

Worktree AGENTS.md Content

Each worktree’s AGENTS.md includes:

  • The root repo’s AGENTS.md content (if any), preserved verbatim.
  • A git-paw section between the markers carrying:
    • Branch name and assigned CLI for this worktree.
    • Full spec body — proposal, design, tasks, and any spec deltas from openspec/changes/<id>/. The exact set depends on what the change directory contains; missing files (design.md is often optional) are silently skipped.
    • Owned files list (from OpenSpec file-ownership declarations) when declared.
    • Boot block with the broker curl commands the agent uses to self-report status, artifacts, blockers, intents, and questions.

The supervisor-mode boot prompt does not duplicate any of this content — it points at the file. If a change is updated on disk during a session (e.g. the supervisor edits tasks.md to mark a task complete), the worktree’s AGENTS.md is regenerated on the next git paw start invocation rather than live-patched mid-session.

Agent Coordination

When multiple AI agents work in parallel, they benefit from knowing what the others are doing. The coordination broker is a lightweight HTTP server that lets agents share status updates, publish artifacts, and flag blockers – all without touching git.

Enabling the Broker

Add a [broker] section to your .git-paw/config.toml:

[broker]
enabled = true

When you run git paw start, pane 0 becomes a dashboard instead of an agent pane. The dashboard hosts the broker and displays a live status table.

How Agents Discover the Broker

git-paw sets the GIT_PAW_BROKER_URL environment variable in every agent pane. Agents use this URL to send and receive messages. A typical value is http://127.0.0.1:9119.

When skill templates are enabled (the default), each agent’s AGENTS.md also contains curl commands for interacting with the broker, so agents know how to use it without any manual setup.

Boot-Prompt Injection

To ensure reliable agent self-reporting, git-paw automatically injects a standardized boot instruction block into every agent’s initial prompt. This boot block contains pre-expanded curl commands for four essential operations:

1. REGISTER - Immediate Status Publication

Agents automatically publish their working status with a “booting” message as their very first action:

curl -s -X POST http://127.0.0.1:9119/publish \
  -H "Content-Type: application/json" \
  -d '{"type":"agent.status","agent_id":"feat-auth","payload":{"status":"working","message":"booting","modified_files":[]}}'

2. DONE - Task Completion Reporting

The primary completion path is git commit. The git-paw post-commit hook auto-publishes agent.artifact { status: "committed" } with modified_files derived from git diff HEAD~1 --name-only, so agents working on code changes do not publish anything manually — they commit and the hook reports on their behalf.

The boot block retains a manual agent.artifact { status: "done" } curl as a fallback for code-less tasks (docs-only updates handled outside the worktree, planning notes, exploration tasks where the artifact is information reported to the broker). The block warns agents NOT to publish manual done while their worktree has uncommitted changes — they should commit instead.

curl -s -X POST http://127.0.0.1:9119/publish \
  -H "Content-Type: application/json" \
  -d '{"type":"agent.artifact","agent_id":"feat-auth","payload":{"status":"done","exports":[],"modified_files":[]}}'

3. BLOCKED - Dependency Waiting Notification

Agents can properly declare when they’re waiting on dependencies:

curl -s -X POST http://127.0.0.1:9119/publish \
  -H "Content-Type: application/json" \
  -d '{"type":"agent.blocked","agent_id":"feat-api","payload":{"needs":"auth token format","from":"feat-auth"}}'

4. QUESTION - Uncertainty Escalation (Critical)

Agents are instructed to publish questions and wait for answers rather than guessing:

curl -s -X POST http://127.0.0.1:9119/publish \
  -H "Content-Type: application/json" \
  -d '{"type":"agent.question","agent_id":"feat-auth","payload":{"question":"Should the JWT use RS256 or HS256 signing?"}}'

IMPORTANT: The boot block explicitly instructs agents: “DO NOT CONTINUE UNTIL YOU RECEIVE AN ANSWER!”

Boot Block Injection Modes

  • Supervisor Mode: Boot block is prepended to each agent’s task prompt before injection
  • Manual Broker Mode: Boot block is pre-filled into each agent pane’s input line (user pastes task after boot instructions)

Paste Handling

The boot block includes instructions for proper paste handling, particularly the requirement to send an additional Enter key after paste operations to ensure full content processing.

Benefits

  • Reliable Monitoring: Agents self-report immediately on boot
  • Consistent Behavior: All agents follow the same coordination pattern
  • No Permission Prompts: Pre-expanded curl commands avoid shell variable expansion issues
  • Supervisor Visibility: Questions and blockers surface to the dashboard promptly
  • Audit Trail: All boot operations are logged in the broker log

Message Types

Every broker message uses the same JSON envelope:

{
  "type": "agent.<variant>",
  "agent_id": "<slug>",
  "payload": { ... }
}

<variant> is one of seven shipped values; <slug> is the agent’s slugified branch name (lowercase alphanumeric + - / _; slashes from a branch name like feat/auth become hyphens — feat-auth). The seven variants are agent.status, agent.artifact, agent.blocked, agent.intent, agent.question, agent.feedback, and agent.verified; src/broker/messages.rs is the source of truth for the payload schemas.

agent.status and agent.artifact are normally automatic. The filesystem watcher publishes agent.status (with modified_files) whenever a tracked file changes in a worktree, and the post-commit git hook publishes agent.artifact with status: "committed" and the committed file list every time an agent commits. The manual curl examples below are escape hatches for cases where the automatic publishers do not apply (e.g. code-less tasks or heartbeat injection during read-only investigation).

Status

An agent reports what it is currently doing along with any files it has already modified in this work step.

curl -s -X POST "$GIT_PAW_BROKER_URL/publish" \
  -H "Content-Type: application/json" \
  -d '{"type":"agent.status","agent_id":"feat-auth","payload":{"status":"working","modified_files":["src/auth.rs"],"message":"implementing login endpoint"}}'

Artifact

An agent shares the result of a commit (or the analogous output of a code-less task) so peers can see exports and modified files. The modified_files array is what the conflict detector watches for in-flight overlap.

curl -s -X POST "$GIT_PAW_BROKER_URL/publish" \
  -H "Content-Type: application/json" \
  -d '{"type":"agent.artifact","agent_id":"feat-auth","payload":{"status":"committed","exports":["AuthClient"],"modified_files":["src/auth.rs","src/auth/client.rs"]}}'

Blocked

An agent declares that it is waiting on something specific from another agent (or external resource). from names the agent that can unblock it.

curl -s -X POST "$GIT_PAW_BROKER_URL/publish" \
  -H "Content-Type: application/json" \
  -d '{"type":"agent.blocked","agent_id":"feat-api","payload":{"needs":"auth token format","from":"feat-auth"}}'

Intent

An agent declares which files it plans to modify before any edit lands. The broker conflict detector reads agent.intent to flag forward conflicts when two agents target overlapping paths. valid_for_seconds is the TTL after which consumers MAY treat the intent as stale.

curl -s -X POST "$GIT_PAW_BROKER_URL/publish" \
  -H "Content-Type: application/json" \
  -d '{"type":"agent.intent","agent_id":"feat-auth","payload":{"files":["src/auth.rs","src/auth/client.rs"],"summary":"wire AuthClient","valid_for_seconds":900}}'

Question

An agent escalates an uncertainty to the supervisor inbox. The asking agent blocks at its prompt until a typed reply arrives.

curl -s -X POST "$GIT_PAW_BROKER_URL/publish" \
  -H "Content-Type: application/json" \
  -d '{"type":"agent.question","agent_id":"feat-auth","payload":{"question":"Should the JWT use RS256 or HS256 signing?"}}'

Feedback

A supervisor (or the broker’s auto-emitted [conflict-detector] voice) sends a list of error messages to a target agent. The target agent_id field on the envelope is the receiver; from inside the payload is the sender.

curl -s -X POST "$GIT_PAW_BROKER_URL/publish" \
  -H "Content-Type: application/json" \
  -d '{"type":"agent.feedback","agent_id":"feat-auth","payload":{"from":"supervisor","errors":["missing rustdoc on AuthClient::new","test for HS256 path is failing"]}}'

Verified

A supervisor confirms that an agent’s work has passed every verification gate. The agent_id is the agent whose work was verified; verified_by names the verifier.

curl -s -X POST "$GIT_PAW_BROKER_URL/publish" \
  -H "Content-Type: application/json" \
  -d '{"type":"agent.verified","agent_id":"feat-auth","payload":{"verified_by":"supervisor","message":"all five gates pass"}}'

Polling for Messages

Agents poll for messages from other agents using cursor-based pagination. The since parameter is a sequence number – the broker returns only messages with a sequence greater than the given value.

# First poll -- get all messages
curl -s "$GIT_PAW_BROKER_URL/messages/feat-auth?since=0"

The response includes a last_seq field. Pass this value as since on the next poll to get only new messages:

# Subsequent poll -- only new messages since last check
curl -s "$GIT_PAW_BROKER_URL/messages/feat-auth?since=42"

This cursor-based approach is lossless – no messages are missed between polls, regardless of timing.

Checking Overall Status

The /status endpoint returns a summary of all agents and their latest state:

curl -s "$GIT_PAW_BROKER_URL/status"

Multi-Repo Considerations

Each git-paw session runs its own broker. If you have multiple repos running sessions simultaneously, each needs a unique port:

# In repo-a/.git-paw/config.toml
[broker]
enabled = true
port = 9119

# In repo-b/.git-paw/config.toml
[broker]
enabled = true
port = 9120

The default port is 9119. The broker always binds to 127.0.0.1 (localhost only) and should never be exposed to the network.

Automatic Conflict Detection (v0.5.0)

When supervisor mode is active, the broker runs an in-process conflict detector that auto-emits agent.feedback (tagged [conflict-detector] in the errors array) and, for unresolved in-flight conflicts, escalates to the supervisor inbox via agent.question. Three failure shapes are detected:

  • Forward conflicts — two agents publish agent.intent with overlapping files. Both receive agent.feedback listing the other agent and the overlapping paths. Toggle with [supervisor.conflict] warn_on_intent_overlap.
  • In-flight conflicts — an agent publishes agent.status or agent.artifact whose modified_files overlap with another agent’s active intent or recent status. The broker waits [supervisor.conflict] window_seconds (default 120) for one side to retract; if both sides keep modifying, the detector escalates to the supervisor inbox via agent.question.
  • Ownership violations — an agent’s modified_files includes a path the spec marks as owned by another change. The violator receives agent.feedback; if [supervisor.conflict] escalate_on_violation = true the supervisor inbox also receives a follow-up agent.question.

See the Conflict Detection chapter for the full walkthrough — failure shapes, the [conflict-detector] tag, supervisor inbox routing, and the configuration knobs.

Audit Trail

The broker writes all messages to .git-paw/broker.log as JSONL (one JSON object per line). This file is flushed every 5 seconds and provides a complete audit trail of agent communication.

The log file is automatically cleaned up by git paw purge. It is also covered by the .gitignore entry that git paw init creates.

Working Heartbeat

The broker’s filesystem watcher publishes agent.status whenever a file in a worktree changes, which keeps the dashboard’s last_seen timestamp fresh during active editing. The watcher cannot observe read-only tool uses (file reads, greps, searches), permission-prompt waits, or LLM-only deliberation between tool calls — so a long read-heavy investigation looks stuck on the dashboard even though the agent is making progress.

To bridge that gap, the embedded coordination skill instructs agents to publish a lightweight agent.status heartbeat every 5 tool uses while actively working:

curl -s -X POST http://127.0.0.1:9119/publish \
  -H "Content-Type: application/json" \
  -d '{"type":"agent.status","agent_id":"feat-auth","payload":{"status":"working","message":"reviewing auth tests","modified_files":[]}}'

The heartbeat reuses the existing agent.status shape — no new wire format is introduced. The broker merges heartbeats with watcher-driven updates without conflict.

Commit Cadence

The bundled coordination skill teaches a per-group commit cadence. When a change has an OpenSpec-style tasks.md with numbered groups (## 1., ## 2., …), the agent commits after every - [ ] item in a group is - [x] — one group, one commit (by default) — before starting the next group.

The skill bounds uncommitted work to roughly ten files at a time. If a single group exceeds that mid-implementation, the agent splits into multiple commits using a (part N of M) suffix:

feat(coverage): close per-scenario gaps for v0.5.0 (part 1 of 2)
feat(coverage): close per-scenario gaps for v0.5.0 (part 2 of 2)

Each commit uses a conventional-commit prefix (feat(<scope>):, fix(<scope>):, docs(<scope>):, test(<scope>):, chore(<scope>):) — the scope is typically the change name’s key word.

Per-group cadence protects against agent crashes, conflict mediation, and /clear resets losing unbounded work, and it maps cleanly to the post-commit hook’s agent.artifact { status: "committed" } event sequence the supervisor consumes during verification.

Terminal Action — Commit Then Publish, Never Archive

The bundled coordination skill defines the coding agent’s terminal action as:

  1. A commit. The post-commit git hook auto-publishes agent.artifact { status: "committed" } with the committed file list. For code changes this is the canonical “done” signal.
  2. A manual agent.artifact { status: "done" } (rare). Used only for code-less tasks or to announce named exports peers should cherry-pick.

The skill is explicit that the coding agent SHALL NOT invoke /opsx:verify <change-id> or /opsx:archive <change-id>both are off-limits for the coding agent and are the supervisor’s job:

  • Verification runs the supervisor’s five-gate framework (testing → regression → spec audit → doc audit → security audit) against the committed branch. Self-verification by the coding agent bypasses gates and produces a premature agent.verified the supervisor never reviewed.
  • Archiving happens on the release branch during the supervisor’s cherry-pick
    • merge flow, not on the agent’s feature branch. Archiving from a feature branch leaves the change directory deleted on an unmerged branch and produces confused history.

This is a paw-specific rule for the bundled coordination skill. Single-agent workflows that self-verify can override the rule via the standard skill resolution chain (a user override at <config_dir>/git-paw/agent-skills/coordination.md wins over the bundled default).

Identifier Forms — Branch vs agent_id

Two related forms of an agent identifier appear throughout the broker protocol:

  • Branch name — the original git ref (e.g. feat/no-supervisor-flag). Used in git checkout, git worktree, git push, and any other git command.
  • agent_id — the dashed slug form (e.g. feat-no-supervisor-flag). Used in every /publish payload, every /messages/<id> URL, and the target field of agent.feedback and agent.question payloads.

agent_id is the slugified form of the branch name. The conversion (named slugify_branch in the source) lowercases the input, replaces every character outside [a-z0-9_] with -, collapses runs of -, trims leading and trailing -, and falls back to the literal agent if the result is empty.

Match the form to the context: dashed agent_id in any JSON going to or coming from the broker; slashed branch name in any shell command involving git.

Stash Hygiene in Worktrees

When multiple worktrees run side-by-side, every worktree shares the same underlying git stash list. A git stash pop invoked without inspection can pop an entry created by a different worktree, conflict with your in-progress changes, and wipe work. The embedded coordination skill teaches agents three rules:

  1. List before popgit stash list first; inspect every entry’s branch label and timestamp.
  2. Inspect before popgit stash show -p stash@{N} to read the patch contents of the specific entry before popping.
  3. Pop only your own — only pop entries you authored on the current worktree. If authorship is uncertain, leave the stash alone and escalate via agent.question.

Blind git stash pop is a data-loss pattern in a multi-worktree session and is not recommended.

Supervisor Acknowledgement of agent.question

When an agent publishes agent.question, it blocks at its prompt waiting for a typed reply. v0.5.0 agents do not poll their inbox for agent.feedback responses, so a supervisor that only publishes agent.feedback to the broker will see its answer recorded on the dashboard while the asking agent stays blocked indefinitely.

The supervisor skill therefore instructs supervisors (both human and LLM) to both publish agent.feedback and send the answer text to the asking agent’s tmux pane via tmux send-keys. This dual write is transitional; MCP-mediated inbox access in v0.6.0 will let agents consume agent.feedback directly and remove the second step.

Spec Kit Consolidated Worktrees

When git-paw drives a Spec Kit project (.specify/specs/<feature>/), each feature’s current phase decomposes into multiple worktrees:

  • One worktree per [P]-marked task (branch prefix task/). These are parallelisable.
  • One consolidated worktree per non-[P] task group (branch prefix phase/). Non-[P] tasks share files or context, so a single agent works through them sequentially.

The embedded coordination skill picks up on the branch prefix:

  • task/<task-id>-<slug> branches: the agent runs the standard “before/while editing” coordination pattern for a single task.
  • phase/<feature>-<phase-slug> branches: the agent:
    1. Works through the listed tasks in tasks.md order.
    2. Flips - [ ] to - [x] for each completed task in the worktree’s tasks.md. The writeback can be a separate commit or bundled with the task’s code change.
    3. Publishes agent.intent for the union of files across the next 1–2 tasks (with a generous TTL) rather than one publish per task.
    4. Publishes agent.artifact with status: "done" only when every listed task shows - [x] in tasks.md. Partial completion is not “done”.

When tasks.md is the merge-conflict surface between worktrees, git’s line-level merge handles per-task checkbox flips automatically. If two worktrees ever flip the same task ID, conflict detection (via agent.intent overlap) catches it upstream.

Workflow phases

The bundled coordination skill structures an agent’s editing work into two phases that mirror the skill’s “Before you start editing” and “While you’re editing” sections.

Before you start editing

Before touching any file, the agent:

  1. Reads the spec or task description in full to understand the scope.
  2. Publishes agent.intent listing the specific files it plans to modify, a one-line summary, and a TTL in seconds (default 900 = 15 minutes). This advertises ownership to the broker conflict detector so forward conflicts are caught before any edit lands.
  3. Polls its inbox once for warnings or overlapping peer intents — not a busy loop, a single poll.
  4. Decides on overlap: if a peer’s intent already covers the same files, the agent picks among wait (peer’s TTL is short, work is small), split (narrow the file list to avoid overlap, re-publish agent.intent with the reduced scope), or escalate (publish agent.question describing the overlap so the supervisor or human can decide). If no overlap is reported, the agent proceeds to edit immediately — there is no explicit go-ahead to wait for.

While you’re editing

Once editing is underway, the agent keeps the intent honest and asks rather than racing:

  • Re-publish intent on scope growth. If the in-progress work touches files that were not in the original agent.intent, the agent re-publishes agent.intent with the expanded files list before touching the new files. The re-published intent replaces the previous claim for downstream consumers.
  • Question on peer overlap. If a peer’s agent.intent arrives in the inbox naming a file in the same module the agent is editing, the agent sends agent.question describing the overlap and pauses edits on the contested file. Silently racing the peer to a commit is forbidden.

The agent MUST NOT:

  • Perform pairwise check-ins on every change — the broker is not a chat channel and peers are not waiting for status pings.
  • Wait for an explicit go-ahead from peers when no conflict signal exists — silence from the broker means “no overlap detected”, not “permission pending”.
  • Block on broker silence — if agent.intent polling returns no overlap, the agent proceeds.

Supervisor

This chapter mirrors the user-facing prose for the bundled assets/agent-skills/supervisor.md skill — the doctrine the supervisor agent runs against in supervisor mode. Sections here document the same rules the embedded skill teaches, so users reading the mdBook can understand supervisor behaviour without opening the skill file directly.

For the launcher-level “how do I start supervisor mode” walkthrough, see Quick Start: Supervisor Mode. For the broker-side message contract the supervisor exchanges with coding agents, see Agent Coordination.

Resolve Pane to Agent via pane_current_path

Before the supervisor tmux capture-panes or tmux send-keyss a specific agent, it needs the pane index for that agent. The bundled supervisor skill is explicit that pane indices are NOT alphabetical by agent_id, NOT in the CLI-argument order from git paw start --specs A B C, and SHALL NOT be inferred from git paw status output or the dashboard’s row order (both of those are sorted alphabetically by the broker, which has no relationship to the launcher’s internal scan order).

The canonical resolution command queries tmux directly:

tmux display-message -t paw-<project>:0.<pane> -p '#{pane_current_path}'

The output is the pane’s working directory. For coding-agent panes that is the agent’s worktree path, whose basename ends in <project>-feat-<branch> — the authoritative agent_id (with the slash form feat/<branch> for git operations). A pane whose pane_current_path ends in myproj-feat-auth belongs to agent feat-auth.

The supervisor agent builds the {pane_index → agent_id} map once per session and reuses it; re-resolution only happens when the supervisor notices an inconsistency.

The bundled .git-paw/scripts/sweep.sh invokes this command on every sweep iteration. If the helper is missing for any reason, the supervisor falls back to invoking tmux display-message directly — this is the documented escape hatch.

The supervisor MUST NOT use git paw status output (or the dashboard’s row order) as a mapping source — both are sorted alphabetically by the broker and have no relationship to the launcher’s pane assignment. Always resolve via pane_current_path first.

Spec audit governance sub-step

When a project’s .git-paw/config.toml lists governance documents under [supervisor].governance.docs, the supervisor reads each doc as part of the Spec Audit gate and flags drift between the diff and the doc’s checklist. The five canonical doc-checklist examples are:

  • DoD (Definition of Done) — walk each - [ ] item against branch state.
  • ADRs (Architectural Decision Records) — verify new architectural decisions (new deps, new patterns) have a matching ADR.
  • security.md — walk each security checklist item against the diff.
  • test-strategy.md — check that test composition matches the documented strategy.
  • constitution.md — check the diff against documented principles (e.g. “no panics in library code”).

Findings surface as standard agent.feedback errors tagged [doc audit] mixed in with other doc-audit gaps. See Governance for the config schema and how the supervisor reads each doc.

Common dev-command allowlist

A bundled preset whitelists routine dev commands so the supervisor stops escalating every cargo test, cargo build, git commit, git push, mdbook build, or broker curl on 127.0.0.1. The preset is on by default and ships with the launcher.

To opt out for a session, set:

[supervisor.common_dev_allowlist]
enabled = false

To extend the preset with project-specific patterns (e.g. just, nox, a custom test runner), use the extra field:

[supervisor.common_dev_allowlist]
enabled = true
extra = ["just check", "nox -s tests"]

extra patterns are prefix-matched against the captured command line, the same way the built-in patterns are. See Configuration for the full schema.

Repo-configurable gate commands

The supervisor’s five verification gates each invoke a configurable command substituted from [supervisor] keys at session boot. The seven keys are:

  • test_command
  • lint_command
  • build_command
  • fmt_check_command
  • doc_build_command
  • spec_validate_command
  • security_audit_command

When a key is missing or empty, the placeholder renders as (not configured) in the supervisor skill and the supervisor gracefully skips the tooling invocation for that gate — the gate’s manual review still applies. See Configuration for defaults and examples.

Broker-side conflict detector

Starting with v0.5.0 the broker auto-detects three failure shapes between parallel agents and emits agent.feedback (and, where configured, agent.question) on the supervisor’s behalf. All auto-emitted messages begin with the [conflict-detector] token so the supervisor can distinguish detector output from human-typed feedback. The three failure shapes are:

  • Forward conflict — two agents publish overlapping agent.intent declarations.
  • In-flight conflict — two agents’ filesystem-watched modified_files sets overlap on the same file.
  • Ownership violation — an agent’s modified_files include a file inside another agent’s active intent.

See Conflict Detection for the algorithm, configuration, and escalation behaviour.

Learnings aggregator

When [supervisor.learnings] enabled = true, the supervisor session records deterministic friction signals (sandbox warnings, approval patterns, recurring errors) into a markdown file you can review after the run. See Learnings Mode for the file format and how to opt in.

When the user types in your pane

The supervisor pane is interactive — the user can type at any time while the autonomous monitoring loop is running. The supervisor finishes the current step (spec audit, test run), responds, then resumes the loop. User input is a high-priority interrupt, not a replacement for the loop.

Each kind of user input maps to an existing mechanism — the supervisor does not invent new channels:

  1. Status question (“how’s feat-auth going?”, “anything blocked?”) — answered conversationally in the pane using sweep.sh status, sweep.sh inbox, and sweep.sh capture <pane>. Nothing is published to the broker — this is a conversation with the user, not a session-wide event.
  2. Directive (“ask feat-auth to use bcrypt”, “tell feat-api to skip the migration”) — published as agent.feedback to the named agent with the [directive] gate prefix, plus a conversational confirmation to the user.
  3. Judgment-call ask (“should we merge feat-a before feat-b?”) — the supervisor applies its normal escalation rules. If the user has already provided enough information to decide, the supervisor answers in the pane using its reasoning. agent.question only fires when the call is genuinely ambiguous beyond what the user just provided — typically when the user is asking because they don’t know either.

The mechanisms (curl /status, tmux capture-pane, agent.feedback, tmux send-keys, agent.question) are unchanged. The addition is when to use which in response to user input.

Merge orchestration

Once every spec’d agent has published agent.verified (or the user explicitly asks for a merge), the supervisor runs the merge orchestration loop. v0.5.0 removed the Rust auto-merge loop; merging is now the supervisor’s responsibility, performed with the existing shell + curl tools.

Trigger. Either every spec’d agent has published agent.verified, or the user has explicitly requested the merge.

Merge order. The supervisor reads the broker’s message log (/messages/supervisor) and builds a dependency graph from agent.blocked events: each event from agent X with payload.from = Y is an edge “X depends on Y”. The supervisor then topologically sorts the graph: agents with no incoming edges merge first; dependents follow.

Per-branch merge. For each branch in topological order, the supervisor checks out main and runs:

git merge --ff-only feat/<branch>

Never a merge commit — fast-forward only. If --ff-only fails (the branch diverges from main, or there is a conflict), the supervisor SKIPS that branch and publishes agent.feedback to the owning agent asking them to rebase or resolve. On a successful fast-forward, the supervisor runs the configured {{TEST_COMMAND}}; if tests fail, the supervisor reverts the merge with git reset --hard <prev-HEAD> and publishes agent.feedback tagged [regression].

Cycle handling. If the dependency graph has a cycle, the supervisor does NOT merge any branch in the cycle. Instead, it publishes agent.question to the human and waits for guidance before continuing.

Final summary. When the loop completes, the supervisor publishes a final agent.status summarising which branches merged cleanly, which were skipped (and why), and any regressions encountered.

Dashboard

When the broker is enabled, one tmux pane runs the dashboard instead of an agent CLI. In supervisor mode the dashboard lives at pane 1 (pane 0 hosts the supervisor CLI itself); in broker-only mode (broker on, supervisor off) the dashboard lives at pane 0. Either way it renders the same live status table that updates every second, giving you an at-a-glance view of what each agent is doing.

The dashboard is observation-only — the only keystroke it handles is q to quit. Human input (questions, directives, replies to agent.question events) happens in the supervisor pane itself; the dashboard simply renders broker state.

What the Dashboard Pane Shows

The dashboard renders a table with one row per agent. When a supervisor pane is running, its row is pinned to the top with a horizontal-line divider beneath it; coding-agent rows follow in alphabetical order:

┌────────────┬────────┬────────────┬─────────┬──────────────────────────────┐
│ Agent      │ CLI    │ Status     │ Time    │ Summary                      │
├────────────┼────────┼────────────┼─────────┼──────────────────────────────┤
│ supervisor │ claude │ 🔵 watching│ 12s ago │ session online               │
│ ────────── │ ────── │ ────────── │ ─────── │ ──────────────────────────── │
│ feat-api   │ claude │ 🟡 blocked │ 1m 05s  │ waiting for auth token format│
│ feat-auth  │ claude │ 🔵 working │ 3m 22s  │ implementing login endpoint  │
│ fix-typo   │ gemini │ 🟢 done    │ 8m 41s  │ done — all typos fixed       │
└────────────┴────────┴────────────┴─────────┴──────────────────────────────┘

When no supervisor pane is running (e.g. --no-broker is not in play but no --supervisor was passed, or during the boot window before the supervisor has finished self-registering), the divider is not rendered and coding agents fill rows alphabetically from the top.

Status Symbols

SymbolMeaning
🔵Working – agent is actively processing
🟢Done/verified – agent has completed its task
🟣Committed – agent has committed work
🟡Blocked – agent is waiting on something
Idle / unknown phase – agent has not reported a recognised status

The Time column shows elapsed time since the agent’s last status update. The Summary column shows the most recent status or blocked message body.

Supervisor row, cli field, and phase field

The supervisor row’s Status column does not show the wire-message type label that a coding-agent row shows. Instead the supervisor publishes a phase field on its agent.status messages (e.g. baseline, watching, approving, answering, merging, summary), and the dashboard prefers that label when rendering its row. This avoids the misleading status=feedback label the supervisor would otherwise show when it publishes agent.feedback to a coding agent.

The supervisor pane is also not a watch target, so the broker cannot infer its cli from the watch-target map. To populate the CLI column for the supervisor row, the supervisor self-registration agent.status includes a cli field (e.g. "cli":"claude"). The broker upserts that value into its internal CLI map when it receives the message. Coding agents do not need to publish cli — the broker populates their CLI from the watch-target map at startup.

When the supervisor row appears

The supervisor row appears after the supervisor pane’s CLI has booted and published its first self-registration agent.status — typically within 3-5 seconds of git paw start --supervisor returning. There is no phantom supervisor row at launch time; if the supervisor pane fails to start, the row simply never appears. Aborted launches (non-TTY skip, missing CLI on PATH, system-level pane spawn failure) leave the agent table free of a misleading supervisor entry.

Controls

Press q to quit the dashboard. This shuts down the broker and terminates the dashboard process in the dashboard pane. The agent panes continue running – they simply lose the ability to communicate via the broker.

Broker Messages Panel

When enabled, the dashboard shows a broker messages panel at the bottom, displaying recent communication between agents and the broker for at-a-glance observability.

Enabling the Panel

Add this to your .git-paw/config.toml:

[dashboard]
show_message_log = true

Message Types

The panel shows six types of broker messages:

SymbolTypeMeaning
📤StatusAgent status updates
📦ArtifactShared files/artifacts
🚧BlockedAgent blocked requests
VerifiedSupervisor verification
💬FeedbackSupervisor feedback
QuestionAgent questions

Example Layout

┌──────────┬────────┬────────┬─────────┬──────────────────────────────┐
│ Agent    │ CLI    │ Status │ Time    │ Summary                      │
├──────────┼────────┼────────┼─────────┼──────────────────────────────┤
│ feat/auth│ claude │ 🔵     │ 3m 22s  │ implementing login endpoint  │
│ feat/api │ claude │ 🟡     │ 1m 05s  │ waiting for auth token format│
│ fix/typo │ gemini │ 🟢     │ 8m 41s  │ done — all typos fixed       │
└──────────┴────────┴────────┴─────────┴──────────────────────────────┘

[14:30:22] agent-0 📤 working on login endpoint
[14:29:45] agent-1 📦 shared auth_schema.json
[14:28:10] agent-2 🚧 blocked: need API spec format

Each message shows timestamp (HH:MM:SS), agent ID, message type symbol, and content. The panel shows the 20 most recent messages.

Relationship to the Broker

The dashboard and broker run in the same process (git paw __dashboard). The dashboard reads from shared state that the broker’s HTTP handlers (and the watcher, conflict detector, and learnings aggregator subsystems) write to. There is no separate broker process to manage.

Replying to agent questions

Earlier dashboard versions included a “Questions” panel and a “Reply to” input field for human-typed answers to agent.question events. The panel was removed in v0.5.0 because the supervisor pane is the natural input surface — typed questions and replies go through tmux send-keys and the supervisor agent’s own curl machinery, not through the dashboard.

agent.question messages still flow through the broker. The supervisor pane polls the supervisor inbox, reads incoming questions, and replies via tmux send-keys to the asking agent’s pane (and via agent.feedback to the broker for the audit log). See the Supervisor mode chapter and the embedded supervisor.md skill for the reply flow.

See the v0.5.0 changelog for the removal note.

Pause and Resume

git paw pause is the soft-stop verb for short breaks (lunch, a meeting, end-of-day). It freezes the running session without losing agent state, so you can resume mid-conversation an hour later by running git paw start.

The three teardown verbs

git-paw v0.5.0 ships three verbs for taking a session down:

VerbWhat it killsWhat survivesUse when
pauseTmux client attach, broker processTmux session, all CLI panes (in RAM)Short break — you’ll resume in minutes/hours
stopTmux session, every CLI paneWorktrees and branches on diskLonger break — you want RAM back, OK with fresh CLIs later
purgeEverything (tmux, worktrees, state)NothingYou’re done with the project, or you want to start clean

A future git paw hibernate (v1.0.0) will snapshot tmux state and each CLI’s conversation to disk, then kill the processes — combining pause’s state preservation with stop’s RAM release.

What pause does, mechanically

When you run git paw pause:

  1. Every tmux client attached to the session is detached (tmux detach-client -s <session>).
  2. The dashboard pane only is killed (tmux kill-pane -t :0.<idx>), which causes the __dashboard subprocess to exit, which drops the BrokerHandle, which gracefully shuts down the broker and flushes broker.log.
  3. The session state file flips from status: active to status: paused.
  4. Every coding-agent CLI pane keeps running. Their in-memory conversation, their CLI process, and the worktree they’re working in are untouched.

git paw status shows the paused state with a blue indicator and a “run git paw start to resume” hint.

The RAM trade-off

Pause is fast and state-preserving, but the CLI processes stay allocated. A typical Claude Code instance holds ~300 MB, so a 10-pane session is roughly 3–5 GB of RAM sitting idle while paused.

Pick the right verb based on duration:

  • Pause is right for short breaks where instant resume matters more than RAM.
  • Stop is right for long breaks where you’d rather get the RAM back. Resuming via git paw start spawns fresh CLI processes — you lose conversation context but the worktrees and branches carry over.
  • Hibernate (future v1.0.0) will be both: state preserved AND RAM released.

Resuming a paused session

git paw start

When git paw start detects a paused session that’s still alive in tmux, it takes the restart-from-pause path:

  1. Re-creates the dashboard pane at its saved index.
  2. Sends the git paw __dashboard command to spawn the broker subprocess again.
  3. Flips session status back to active.
  4. Re-attaches your tmux client.

Cost: one tmux pane spawn + one broker boot. No worktree creation, no CLI process spawn, no boot-prompt injection. The agent panes are exactly as you left them — open conversations intact, prompt buffer intact.

If the tmux server died while paused (rare — typically only happens on machine reboot or tmux kill-server), git paw status shows the session as stopped instead, and git paw start falls through to the normal cold-recovery path (fresh CLI spawn).

Idempotency

git paw pause is safe to run repeatedly:

  • Pausing an already-paused session prints “Session ‘NAME’ is already paused.” and exits 0 without changing state.
  • Pausing a stopped session prints “Session ‘NAME’ is already stopped; pause has no effect.” and exits 0.
  • Pausing with no session for the current repo prints “No active session for this repo.” and exits 0.

Future: per-CLI cold resume (drift 61)

For CLIs that support --continue / --resume (Claude Code, etc.), v1.0.0 will extend the cold-recovery path so git paw start after a stop can also restore conversation context — by spawning the CLI with the resume flag. That solves the long-break case without holding RAM, complementing pause for the short-break case.

See also

  • git paw stop — the destructive teardown verb with its new confirmation prompt.
  • git paw purge — the full reset (removes worktrees and branches).
  • Dashboard — what the dashboard pane shows while the session is active.

Skill Templates

git-paw uses standardized agent skills following the agentskills.io specification. Skills are directories containing a SKILL.md file with YAML frontmatter and optional resource subdirectories.

Standard Location

Skills are loaded from .agents/skills/ in your project directory. git-paw walks up the directory tree from the current working directory to find this location.

my-project/
└── .agents/
    └── skills/
        ├── coordination/
        │   ├── SKILL.md          # Main skill file
        │   ├── scripts/          # Optional: Executable scripts
        │   ├── references/       # Optional: Documentation
        │   └── assets/           # Optional: Templates/resources
        └── supervisor/
            ├── SKILL.md
            └── scripts/

Skill Format

Each skill must contain a SKILL.md file with YAML frontmatter:

---
name: my-skill
description: A brief description of what this skill does
license: MIT
compatibility: git-paw v0.4.0+
---

## My Skill Instructions

This skill helps agents with {{BRANCH_ID}} workflows...

Required Fields

  • name: Skill name (max 64 chars, lowercase letters/numbers/hyphens only)
  • description: Clear description of the skill’s purpose (max 1024 chars)

Optional Fields

  • license: License information
  • compatibility: Version compatibility
  • metadata: Custom metadata object

Placeholders

Skills support these placeholders that get replaced at runtime:

  • {{BRANCH_ID}} - Slugified branch name (e.g., feat/http-brokerfeat-http-broker)
  • {{PROJECT_NAME}} - Project name for tmux session
  • {{GIT_PAW_BROKER_URL}} - Full broker URL
  • {{SKILL_NAME}} - Name from YAML frontmatter
  • {{SKILL_DESCRIPTION}} - Description from YAML frontmatter

Supervisor gate-command placeholders

The embedded supervisor skill uses seven additional placeholders fed from [supervisor].*_command keys in .git-paw/config.toml. Each gate-command template renders verbatim into the skill prose where the supervisor agent reads it:

  • {{TEST_COMMAND}}[supervisor].test_command — gate 1 (Testing) test runner
  • {{LINT_COMMAND}}[supervisor].lint_command — gate 1 lint sub-step
  • {{BUILD_COMMAND}}[supervisor].build_command — gate 1 build sub-step
  • {{FMT_CHECK_COMMAND}}[supervisor].fmt_check_command — gate 1 formatter check
  • {{DOC_BUILD_COMMAND}}[supervisor].doc_build_command — gate 4 (Doc audit)
  • {{SPEC_VALIDATE_COMMAND}}[supervisor].spec_validate_command — gate 3 (Spec audit)
  • {{SECURITY_AUDIT_COMMAND}}[supervisor].security_audit_command — gate 5 (Security audit)

(not configured) graceful skip. When a key is omitted from [supervisor], the matching placeholder substitutes to the literal string (not configured) in the rendered skill. The supervisor agent treats that sentinel as “skip the tooling invocation for this gate” and continues with the gate’s manual review only (e.g. the OWASP-category diff scan still runs for the security gate, the spec scenario coverage check still runs for the spec gate). Pre-v0.5.x configs that only set test_command continue to render the remaining gates as (not configured) and the supervisor agent runs them as manual-only — no behavior change for those configs.

{{CHANGE_ID}} is per-invocation, not per-render. Spec validators typically take a change name as argument (e.g. openspec validate my-change-id --strict). To support that, the spec_validate_command template MAY embed the literal substring {{CHANGE_ID}}. git-paw does not substitute {{CHANGE_ID}} at session boot — it passes through the render verbatim. The supervisor agent expands it at verification time using the change name it is currently auditing. This matches how {{BRANCH_ID}} behaves for coding agents (per-agent, not per-render).

Resource Subdirectories

Skills can include optional resource subdirectories:

  • scripts/ - Executable scripts referenced by the skill
  • references/ - Detailed documentation and references
  • assets/ - Templates, configuration files, and other resources

Example structure:

.agents/skills/my-skill/
├── SKILL.md              # Main instructions (< 500 lines)
├── scripts/
│   └── setup.sh          # Executable helper script
├── references/
│   └── api-reference.md  # Detailed API documentation
└── assets/
    └── config-template.json

Creating Custom Skills

To add a custom skill:

# Create skill directory structure
mkdir -p .agents/skills/my-skill

# Create SKILL.md with proper frontmatter
cat > .agents/skills/my-skill/SKILL.md << 'EOF'
---
name: my-skill
description: Custom workflow for our team
license: MIT
compatibility: git-paw v0.4.0+
---

## Custom Team Workflow

Follow these steps for {{BRANCH_ID}}:
1. Analyze requirements
2. Implement solution
3. Test thoroughly
4. Document changes
EOF

# Add optional resource directories
mkdir -p .agents/skills/my-skill/scripts
mkdir -p .agents/skills/my-skill/references

Skill Resolution

git-paw searches for skills in this order:

  1. .agents/skills/<name>/SKILL.md (walking up directory tree from current directory)
  2. Embedded defaults (compiled into git-paw binary)

The first match wins. If no skill is found, resolution fails with an error.

Validation

Skills are validated against the agentskills.io specification:

  • Required name and description fields must be present
  • YAML frontmatter must be valid
  • Skill directory must contain SKILL.md file
  • Clear error messages for validation failures

Examples

See the agentskills.io specification for more examples and best practices.

Migration from Older Versions

If you’re upgrading from git-paw v0.2.x or earlier:

  1. Move skills from ~/.config/git-paw/agent-skills/ to .agents/skills/
  2. Convert single .md files to directory structure with SKILL.md
  3. Add required YAML frontmatter to each skill
  4. Organize related resources into subdirectories

The new standardized format improves interoperability and enables skill sharing across different AI systems that support the agentskills.io standard.

When Skills Are Not Injected

Skill templates are only injected when the broker is enabled ([broker] enabled = true). If the broker is disabled, no coordination instructions are added to AGENTS.md.

Boot-Prompt Injection

In addition to skill template injection, git-paw automatically injects a standardized boot instruction block into every agent’s initial prompt. This ensures reliable agent self-reporting even if skill templates are not used or if agents don’t read the AGENTS.md file thoroughly.

The boot-prompt injection includes pre-expanded curl commands for all essential coordination operations (register, done, blocked, question) and is active in both supervisor and manual broker modes. See the Coordination documentation for details.

Governance

git-paw can read your team’s existing governance documents as context for the supervisor agent — ADRs, a test strategy, a security checklist, a Definition of Done, and a project constitution. You point at the docs in .git-paw/config.toml; git-paw does not generate or vendor any of them.

Why governance docs

The supervisor is an LLM. It does the right thing more often when it can see the rules the team has already written down: which architectural decisions are settled, what tests are expected, what security review looks like, what “done” actually means. Without that context the supervisor has to infer from the diff alone, and inference drifts.

git-paw’s stance on governance is deliberately thin:

  • You own the documents. Their structure, format, and rubric are whatever your team already uses (Scrum, XP, OWASP, adr-tools, Spec Kit, hand-rolled). git-paw does not template them.
  • You opt in per doc. Empty [governance] table, all fields None, no behaviour change from v0.4. Add only the docs you have.
  • The supervisor applies judgment. There is no [governance.gates] table and no per-doc enforcement switch. The supervisor reads each configured doc as context during its audit and surfaces relevant findings via agent.feedback.

Pointing at your docs

Add [governance] to .git-paw/config.toml:

[governance]
adr = "docs/adr"
test_strategy = "docs/test-strategy.md"
security = "docs/security-checklist.md"
dod = "docs/definition-of-done.md"
constitution = ".specify/memory/constitution.md"

All five fields are optional. List only the docs you have. Paths are resolved relative to the repository root; absolute paths are accepted as-is. A missing file does not break config-load — the runtime flags it if the supervisor tries to read it.

For Spec Kit projects, governance.constitution auto-wires to .specify/memory/constitution.md when [specs] type = "speckit". You only need to set constitution explicitly if you keep it somewhere else, or want to disable the auto-wiring (set it to "").

See Configuration → Governance for the full field reference and merging rules.

Illustrative examples

The shapes below are examples, not templates. git-paw never reads structure — only the content of the file you point at. Use whatever format your team already uses.

ADR-0001 — Adopt PostgreSQL for the primary store

# ADR-0001: Adopt PostgreSQL for the primary store

Status: Accepted
Date: 2026-02-14
Deciders: backend team

## Context

We need a primary datastore for user, session, and billing tables.
Options considered: PostgreSQL, MySQL, CockroachDB, DynamoDB.

## Decision

We will use PostgreSQL 16 hosted on our existing managed service.

## Consequences

- All new schema lives in PostgreSQL; we will not introduce a second
  relational store without a follow-up ADR.
- Migrations go through `sqlx-migrate`; ad-hoc DDL in code review is
  rejected by default.
- Read replicas are an operational concern, not an application one —
  applications connect through the connection pool, not directly to
  replicas.

A typical ADR directory has one file per decision (0001-postgres.md, 0002-event-bus.md, …). Whether you use Nygard, MADR, or your own headings does not matter — git-paw passes the directory pointer to the supervisor, which reads the files it finds.

Definition of Done

# Definition of Done

A change is "done" when every box below is checkable:

- [ ] All new behaviour has at least one test covering the happy path
      and one covering the failure mode.
- [ ] `just check` passes locally and in CI.
- [ ] Public functions have rustdoc comments.
- [ ] If a config field was added, the configuration docs section
      that owns it is updated.
- [ ] If a CLI flag was added, `--help` text and `cli-reference.md`
      are updated.
- [ ] If user-visible behaviour changed, the changelog has an entry.
- [ ] The PR description names the spec/issue it implements and
      lists any deviations.

Anything left unchecked goes in the PR description as a known
follow-up with an owner.

Security checklist

# Security Checklist

For every change touching authentication, authorisation, network I/O,
or persisted data:

- [ ] User input is validated at the boundary (HTTP handler or CLI
      arg parser), not in the business logic.
- [ ] Secrets never appear in logs, error messages, telemetry, or
      stack traces. The redaction helpers in `crate::log::redact`
      are applied at the producer side.
- [ ] No new dependency on `unsafe` crates without a written
      justification in the PR description.
- [ ] If the change introduces a new external request, the URL is
      built with a typed builder, never with `format!` over user
      input.
- [ ] Authorization checks happen before side effects — never after.

Test strategy

# Test Strategy

## Pyramid

We invest most heavily in fast, deterministic tests:

- **Unit tests** (`#[cfg(test)] mod tests {}`) — pure logic, no I/O.
  Every public function in `src/` has at least one.
- **Integration tests** (`tests/`) — exercise CLI boundaries with
  `assert_cmd`. Use real filesystems via `tempfile`, never mocks.
- **End-to-end smoke tests** (`tests/e2e/`) — kicked off in CI for
  the happy path of each subcommand. Slow and few; tagged with
  `#[ignore]` so contributors can opt in locally.

## What we do not test

- Generated output of `--help` strings (already covered by clap).
- Rendering loops in the TUI — covered manually via the smoke list
  in `docs/src/user-guide/dashboard.md`.

## Test data

Always synthesise — never copy production data, even anonymised, into
fixtures. Fixtures live under `tests/fixtures/` and are kept small
enough to read at a glance.

Constitution (Spec Kit-style)

# Project Constitution

## Spec-driven

No behaviour ships without a spec. The spec is the contract; the
implementation matches the spec; the tests assert the spec. If the
implementation drifts from the spec, the spec is updated first and
re-reviewed.

## Behavioural tests, not implementation tests

Tests assert observable inputs and outputs. Tests do not assert
internal struct field values, internal function calls, or mock
interactions.

## One commit per logical change

Every commit builds, passes `just check`, and is independently
revertable. PRs may contain multiple commits, but no commit may leave
the tree broken.

Spec Kit users typically keep this file at .specify/memory/constitution.md; git-paw auto-detects it when [specs] type = "speckit".

What the supervisor does

When the supervisor verifies an agent’s change, it reads the configured governance documents alongside the diff. Findings flow through the existing agent.feedback channel — the supervisor does not crash or block on governance, it surfaces what it sees and lets the agent respond.

The runtime side of this — boot-prompt injection of the governance doc paths and the supervisor’s audit handling — lives in the parallel governance-context capability. This chapter and the [governance] config table are the path-pointer slot only.

Rollout suggestion

Adopt incrementally — there is no requirement to fill in all five paths at once:

  1. Start with the doc you already have. Point governance.dod at your existing DoD, or governance.constitution at your principles doc.
  2. Run a supervised session. Note which findings feel useful and which feel noisy.
  3. Add the next doc. Each pointer narrows the gap between what the supervisor knows and what the team already agreed on.
  4. If the team does not have one of these docs at all, write the smallest version you can live with rather than copying a template. The supervisor reads what you wrote — not a generic checklist.

You do not need a full governance framework to benefit from this. A two-paragraph DoD pointed at by governance.dod is more valuable than a perfect-but-empty [governance] table.

Learnings Mode

Learnings mode records deterministic friction signals from a supervisor session into a markdown file you can review after the run. It is an opt-in v0.5.0 feature: it requires supervisor mode to be active and the [supervisor] learnings = true flag to be set explicitly. The output is file-only in v0.5.0; a programmatic agent.learning broker variant is deferred to v0.6.0.

Contents

Why

Supervisor runs absorb a lot of recurring friction silently: sandbox warnings the agent retries past, approvals you reflexively click, brief stuck states, conflicts caught and resolved before they hit your eyes. Each event is too small to interrupt for, but the pattern is the most useful signal git-paw can surface for tool, prompt, or process improvement.

Learnings mode aggregates those events into five deterministic categories and writes them to a markdown file you can read between sessions — turning silent friction into something you can act on.

Enabling Learnings Mode

Set the master switch on the [supervisor] table (default false):

[supervisor]
enabled = true
learnings = true

The subsystem only activates when supervisor mode itself is active. With [supervisor] enabled = false (or no [supervisor] section), the learnings = true value is parsed and ignored — no aggregation runs and no file is written.

Output File

When active, the learnings subsystem writes to:

.git-paw/session-learnings.md

The file lives at the repository root (next to .git-paw/broker.log and the session state). It is:

  • Append-only across sessions. Subsequent supervisor runs add new entries below previous ones; nothing is overwritten or pruned.
  • Human-readable markdown. No JSON or binary; you can tail, grep, or open it in any editor between sessions.
  • Not committed by default. git paw init adds it to the project’s .gitignore alongside the other .git-paw/ runtime files.

The Five Categories

v0.5.0 tracks five deterministic categories. Each entry includes a timestamp, the agent involved (when applicable), and a one-line summary.

1. Stuck duration

An agent’s last_seen exceeds the configured stall threshold without producing a new agent.status, agent.artifact, or agent.intent message. Records how long the agent was stuck and what the supervisor or auto-approver did to recover it (sweep, pane capture, no-op).

Trigger condition: (now - agent.last_seen) > stall_threshold AND the agent’s most recent status is non-terminal (done, verified, blocked, and committed are excluded; those are intentional resting states).

2. Recovery-cycle count

How many auto-approve sweeps (or supervisor-driven tmux send-keys recovery actions) were needed before the agent published a fresh status message. A high count for a single agent across a run usually means the auto-approve allowlist is missing a prefix that agent’s CLI keeps tripping on.

Trigger condition: incremented once per sweep dispatch against a pane; flushed when the agent finally publishes a non-stale message.

3. Forward conflicts

Two agents declared agent.intent payloads with overlapping files before either committed. Records the agent pair and the overlapping paths. Use this category to spot specs that were decomposed too coarsely (two parallel agents both expected to own the same file).

Trigger condition: [supervisor.conflict] warn_on_intent_overlap = true AND the broker conflict detector emitted [conflict-detector]-tagged agent.feedback for the overlap.

4. In-flight conflicts

Two agents have overlapping modified_files in active agent.status or agent.artifact payloads — the second agent committed (or is about to) while the first still considers the path active. Records the agent pair, the overlapping paths, and whether the conflict resolved within [supervisor.conflict] window_seconds or escalated to the supervisor inbox via agent.question.

Trigger condition: any in-flight overlap (forward intent already missing or expired). Escalation is recorded separately when window_seconds elapses without resolution.

5. Ownership violations

An agent’s modified_files includes a path the spec marks as owned by another change. Records the violating agent, the touched path, and the owning change ID. Use this category to spot agents that drifted out of their declared scope — usually a sign the spec body in AGENTS.md did not make the ownership boundary obvious enough.

Trigger condition: ownership match against the OpenSpec / Markdown / Spec Kit ownership declaration parsed at session start.

Sample Output

A short illustrative excerpt (timestamps abbreviated for readability):

# Session learnings — paw-myproject

## 2026-05-13 14:30 — supervisor run start

### Stuck duration

- 14:32:18 — feat-auth — stuck for 42s after permission prompt;
  recovered by auto-approve sweep (`cargo test` allowlist hit).
- 14:38:51 — feat-api — stuck for 118s on rebase conflict prompt;
  recovered manually after supervisor `tmux send-keys`.

### Recovery-cycle count

- feat-auth — 3 sweeps before fresh status.
- feat-api — 7 sweeps; investigate auto-approve prefixes.

### Forward conflicts

- feat-auth ↔ feat-api — overlap on `src/auth/middleware.rs`
  (warned via agent.feedback; both retracted before commit).

### In-flight conflicts

- feat-api ↔ feat-billing — overlap on `src/router.rs`;
  resolved by feat-billing pause within window_seconds.

### Ownership violations

- feat-api modified `src/auth/jwt.rs` (owned by add-auth);
  blocked at agent.feedback; escalated to supervisor inbox.

## 2026-05-13 16:05 — supervisor run start
...

The exact section ordering and bullet shape may evolve across patch releases; the category set and the underlying triggers above are stable for the v0.5.0 release.

Flush Cadence

The aggregator buffers entries in memory and flushes to disk on an interval (default 60s):

[supervisor.learnings_config]
flush_interval_seconds = 60

A shorter interval makes the file fresher (useful when you want to tail -f the file in another terminal); a longer interval batches more entries per write. The file is also flushed at supervisor shutdown so nothing is lost between sessions even if the interval has not elapsed.

See Configuration → Learnings mode tuning for the field reference.

Roadmap: agent.learning (v0.6.0)

v0.5.0 ships learnings as file output only. The agent.learning broker variant — a wire-format message agents and tools can publish and consume via the broker — is intentionally deferred to v0.6.0 alongside MCP-mediated inbox access. Until then, downstream consumers should parse the markdown file rather than poll the broker.

Conflict Detection

When supervisor mode is active, the broker runs an in-process conflict detector that watches for three failure shapes across parallel agents. Detected conflicts surface as [conflict-detector]-tagged agent.feedback delivered to the involved agents, and — for unresolved in-flight shapes — as agent.question escalations to the supervisor inbox. The detector is automatic; you only opt out by setting [supervisor] enabled = false (or tuning the knobs in [supervisor.conflict]).

Contents

The Three Failure Shapes

Forward conflict

Two agents publish agent.intent payloads whose files arrays overlap. The conflict is forward because it surfaces before either agent commits — intent is the early-warning channel.

Trigger. Any non-empty intersection between two active intents (an intent stays active until its valid_for_seconds TTL expires, the agent publishes a fresh intent, or the agent commits).

Action. Each agent receives an agent.feedback with the [conflict-detector] tag in its errors[0], naming the peer and listing the overlapping paths. Neither side blocks; the receiving agents decide how to retract or reshape their plans.

Toggle. [supervisor.conflict] warn_on_intent_overlap (default true). When false, the detector still records intents (so in-flight detection keeps working) but no agent.feedback fires for forward shape.

In-flight conflict

One agent publishes agent.status or agent.artifact whose modified_files overlap with another agent’s active intent — or another agent’s recent status / artifact. The conflict is in-flight because at least one side is already writing.

Trigger. Any non-empty intersection between an agent.status / agent.artifact modified_files array and another agent’s active intent or status (whichever is freshest).

Action. Both agents receive [conflict-detector]-tagged agent.feedback. The detector starts a window_seconds timer; if no side retracts before it elapses, the detector escalates to the supervisor inbox via agent.question (see Supervisor Inbox Routing).

Window. [supervisor.conflict] window_seconds (default 120).

Ownership violation

An agent’s modified_files (in agent.status or agent.artifact) touches a path that the spec marks as owned by a different change. The ownership map is built once at session start from the change directories’ Files owned: / Owned files: declarations.

Trigger. Any path in modified_files matches an ownership entry that points to a change other than the sending agent’s.

Action. The violator receives agent.feedback describing the touched path and the owning change. When [supervisor.conflict] escalate_on_violation = true (default), the supervisor inbox also receives a follow-up agent.question so a human can decide whether to override the boundary or block the work.

The [conflict-detector] Tag

Every auto-emitted agent.feedback from the detector starts its errors array with a fixed tag:

{
  "type": "agent.feedback",
  "agent_id": "feat-auth",
  "payload": {
    "from": "supervisor",
    "errors": [
      "[conflict-detector] forward conflict: feat-api also declares intent over src/auth/middleware.rs",
      "..."
    ]
  }
}

The [conflict-detector] prefix distinguishes detector output from human-typed supervisor feedback. Agents (and dashboards) that filter or classify feedback can match on the tag without parsing payload semantics.

The detector publishes from the "supervisor" agent ID (the from field in the payload) — same source as human-authored supervisor feedback — because routing and display logic already specialise on the supervisor identity. The tag, not the source, is the discriminator.

Supervisor Inbox Routing

When an in-flight conflict has not resolved within window_seconds, the detector escalates to the supervisor by publishing an agent.question addressed to the supervisor (not to either of the conflicting agents). The supervisor pane sees the question in its broker inbox and can:

  1. Type a reply, which the supervisor skill forwards to both involved agents via tmux send-keys (the same dual-write pattern documented in Agent Coordination § Supervisor Acknowledgement).
  2. Resolve the conflict directly by editing one agent’s intent or pausing the offending agent until the other side commits.

Ownership-violation escalations follow the same routing. Forward conflicts do not escalate by default — they are advisory.

Interaction with the Filesystem Watcher

The broker’s filesystem watcher publishes agent.status (with a fresh modified_files array) whenever a tracked file changes in a worktree. The conflict detector consumes these auto-published status messages, so in-flight conflicts surface as soon as edits land on disk — no manual agent.status curl required from the agent.

The watcher is read-only with respect to git (it watches the working tree, not the index), so the detector sees overlaps the instant a file is modified, even before git add or git commit. This is the mechanism that makes “in-flight” meaningfully earlier than the post-commit hook’s agent.artifact { status: "committed" }.

Configuration Knobs

All knobs live under [supervisor.conflict] in .git-paw/config.toml:

[supervisor.conflict]
window_seconds = 120
warn_on_intent_overlap = true
escalate_on_violation = true
FieldDefaultDescription
window_seconds120Seconds to wait before escalating an unresolved in-flight conflict to the supervisor inbox via agent.question.
warn_on_intent_overlaptrueForward-conflict feedback toggle. When false, intents are still tracked but no agent.feedback is emitted on intent overlap.
escalate_on_violationtrueOwnership-violation escalation toggle. When false, the violator still receives agent.feedback, but no follow-up agent.question lands in the supervisor inbox.

The [supervisor.conflict] table is fully optional. Setting [supervisor] enabled = false (or omitting the section) disables the detector subsystem entirely — no auto-emitted feedback fires regardless of the values above. See Configuration → Conflict detector tuning for the canonical field reference.

Configuration

git-paw uses TOML configuration files at two levels, with repo-level settings overriding global ones.

Config File Locations

LevelPathPurpose
Global~/.config/git-paw/config.tomlDefault CLI, custom CLIs, global presets
Per-repo.git-paw/config.toml (in repo root)Repo-specific overrides

Both files are optional. git-paw works with sensible defaults when no config exists.

Full Config Example

# Default CLI used when --cli flag is not provided
default_cli = "my-cli"

# Default CLI for spec-mode launches (--from-all-specs, --specs); bypasses picker when set
# default_spec_cli = "my-cli"

# Prefix for spec-derived branch names (default: "spec/")
# branch_prefix = "spec/"

# Enable mouse mode in tmux sessions (default: true)
mouse = true

# Custom CLI definitions
[clis.my-agent]
command = "/usr/local/bin/my-agent"
display_name = "My Agent"

[clis.local-llm]
command = "ollama-code"
display_name = "Local LLM"

# Named presets for quick launch
[presets.backend]
branches = ["feature/api", "fix/db"]
cli = "claude"

[presets.frontend]
branches = ["feature/ui", "feature/styles"]
cli = "codex"

# Spec scanning configuration
# [specs]
# dir = "specs"
# type = "openspec"    # "openspec", "markdown", or "speckit"

# Session logging
# [logging]
# enabled = false

# Agent coordination broker
# [broker]
# enabled = false
# port = 9119
# bind = "127.0.0.1"

# Pointers to user-maintained governance docs (all optional)
# [governance]
# adr = "docs/adr"
# test_strategy = "docs/test-strategy.md"
# security = "docs/security-checklist.md"
# dod = "docs/definition-of-done.md"
# constitution = ".specify/memory/constitution.md"

Settings Reference

default_cli

The AI CLI to use when --cli is not passed and you want to skip the CLI picker.

default_cli = "my-cli"

default_spec_cli

The AI CLI to use by default when launching with --from-all-specs or --specs. When set, skips the CLI picker for any specs that don’t have a paw_cli override.

default_spec_cli = "my-cli"

See Spec-Driven Launch for the full CLI resolution chain.

branch_prefix

Prefix prepended to spec-derived branch names. Defaults to "spec/".

branch_prefix = "spec/"

For example, a spec with ID add-auth produces branch spec/add-auth.

mouse

Enable or disable tmux mouse mode for git-paw sessions. When enabled, you can click panes to switch, drag borders to resize, and scroll with the mouse wheel. This is set per-session and does not affect your other tmux sessions.

mouse = true  # default

Custom CLIs

Register custom AI CLIs that aren’t in git-paw’s built-in detection list.

Via config file

[clis.my-agent]
command = "/usr/local/bin/my-agent"   # absolute path
display_name = "My Agent"              # optional, shown in prompts

[clis.local-llm]
command = "ollama-code"               # binary name (resolved via PATH)
display_name = "Local LLM"

Via command line

# Add with absolute path
git paw add-cli my-agent /usr/local/bin/my-agent

# Add with binary name on PATH
git paw add-cli my-agent my-agent

# Add with display name
git paw add-cli my-agent my-agent --display-name "My Agent"

# Remove
git paw remove-cli my-agent

The add-cli and remove-cli commands modify the global config at ~/.config/git-paw/config.toml.

Listing CLIs

git paw list-clis

Shows both auto-detected and custom CLIs with their source:

Name       Path                         Source
claude     /usr/local/bin/claude        detected
codex      /usr/local/bin/codex         detected
my-agent   /usr/local/bin/my-agent      custom

Presets

Presets save branch + CLI combinations for one-command launch.

Defining presets

[presets.backend]
branches = ["feature/api", "fix/db-migration"]
cli = "claude"

[presets.full-stack]
branches = ["feature/api", "feature/ui", "feature/styles"]
cli = "gemini"

Using presets

git paw start --preset backend

This skips all interactive prompts and launches with the preset’s branches and CLI.

Specs

Configure spec file scanning for --from-all-specs and --specs mode.

[specs]
dir = "specs"         # Directory containing spec files (relative to repo root)
type = "openspec"     # "openspec" (OpenSpec changes), "markdown" (flat .md files), or "speckit" (GitHub Spec Kit)
FieldDefaultDescription
dir"specs"Directory to scan for spec files
type"openspec"Spec backend: "openspec" (directory-based OpenSpec changes), "markdown" (flat .md files with YAML frontmatter), or "speckit" (GitHub Spec Kit .specify/specs/<feature>/)

When [specs] is omitted and .specify/specs/ exists at the repo root, the spec backend auto-detects to type = "speckit" with dir = ".specify/specs". Use the --specs-format CLI flag to override both the config value and the auto-detection for a single launch.

See Spec-Driven Launch for format details.

Logging

Configure session output logging.

[logging]
enabled = true
FieldDefaultDescription
enabledfalseWhether to capture pane output to log files

When enabled, logs are written to .git-paw/logs/<session>/ using tmux pipe-pane. See Session Logging for details.

Broker

Configure the HTTP broker for agent coordination. When enabled, git-paw starts a lightweight HTTP server that lets agents share status updates, artifacts, and blocked requests.

[broker]
enabled = true
port = 9119
bind = "127.0.0.1"
FieldDefaultDescription
enabledfalseWhether to start the coordination broker
port9119HTTP port for the broker server
bind"127.0.0.1"Bind address – never bind to 0.0.0.0 on shared machines

When the broker is enabled, git-paw injects the GIT_PAW_BROKER_URL environment variable into each agent pane, pointing to http://<bind>:<port>. Agents use this URL to communicate with the broker.

Multi-repo port assignment

If you run git-paw sessions for multiple repositories at the same time, each session needs a different port. Set a unique port in each repo’s .git-paw/config.toml:

# Repo A
[broker]
enabled = true
port = 9119

# Repo B (in its own .git-paw/config.toml)
[broker]
enabled = true
port = 9120

See Agent Coordination for usage details.

Supervisor

Configure the supervisor agent for orchestrating parallel coding sessions. When enabled, the supervisor monitors agents, runs tests, verifies work, and coordinates merges.

[supervisor]
enabled = true
cli = "claude"
test_command = "just check"
lint_command = "cargo clippy -- -D warnings"
build_command = "cargo build"
fmt_check_command = "cargo fmt --check"
doc_build_command = "mdbook build docs/"
spec_validate_command = "openspec validate {{CHANGE_ID}} --strict"
security_audit_command = "cargo audit"
agent_approval = "auto"
FieldDefaultDescription
enabledfalseWhether to use supervisor mode by default (can also use --supervisor flag, or override with --no-supervisor for a single session)
cli(uses default_cli)CLI binary for the supervisor agent
test_command(none)Test runner — gate 1 (e.g. "just check", "cargo test", "npm test", "pytest")
lint_command(none)Lint check — gate 1 (e.g. "cargo clippy -- -D warnings", "npm run lint", "ruff check .", "golangci-lint run")
build_command(none)Compile step — gate 1 when build is distinct from test (e.g. "cargo build", "npm run build", "mvn package", "go build ./...")
fmt_check_command(none)Formatter check — gate 1 (e.g. "cargo fmt --check", "prettier --check .", "gofmt -l .", "black --check .")
doc_build_command(none)Documentation build — gate 4 (e.g. "mdbook build docs/", "sphinx-build", "mkdocs build")
spec_validate_command(none)Spec validator — gate 3 (e.g. "openspec validate {{CHANGE_ID}} --strict" for OpenSpec). {{CHANGE_ID}} is substituted by the supervisor agent at verification time with the change name being audited; it is not expanded at config load
security_audit_command(none)Security audit tooling — gate 5 (e.g. "cargo audit", "npm audit", "bandit -r .", "gosec ./...")
agent_approval"auto"Permission level for coding agents: "manual", "auto", or "full-auto"

Gate-command templating. The seven *_command keys feed the supervisor skill’s five verification gates (testing, regression analysis, spec audit, doc audit, security audit). For each key set on this section, the supervisor skill substitutes the matching {{...}} placeholder at session boot and the supervisor agent runs the literal command during that gate. For each key omitted, the placeholder renders as (not configured) and the supervisor agent skips that tooling step — the gate’s manual review still applies (e.g. the OWASP-category diff scan for the security gate, the spec scenario coverage check for the spec gate). Pre-v0.5.x configs that did not name any of the six new keys continue to work; they just run a less-rigorous verification cycle until the keys are filled in. A user wanting to explicitly opt out of a single gate’s tooling can set the field to "(not configured)" verbatim — the supervisor agent recognises that as the same skip token.

Resolution chain — git-paw picks supervisor mode using the first matching rule:

  1. --no-supervisor → off (highest precedence; overrides everything below).
  2. --supervisor → on.
  3. [supervisor] enabled = true → on.
  4. [supervisor] enabled = false → off.
  5. No [supervisor] section + --dry-run → off.
  6. No [supervisor] section + interactive TTY → prompts you.
  7. No [supervisor] section + non-TTY → off.

--supervisor and --no-supervisor are mutually exclusive — passing both produces a parse error.

Approval levels:

LevelBehavior
manualAgents prompt for every action (safest, slowest)
autoCLI default behavior — some prompts, some auto-approved
full-autoSkip all permission prompts (fastest, agents run unattended)

The supervisor translates the approval level into CLI-specific flags at launch (e.g. --dangerously-skip-permissions for Claude in full-auto mode).

Auto-approve safe permission prompts

When supervisor mode is enabled, git-paw can automatically approve common, known-safe permission prompts (cargo test, git commit, broker curl calls, etc.) in stalled agent panes so the supervisor does not have to dismiss every prompt by hand.

[supervisor.auto_approve]
enabled = true
safe_commands = ["just lint", "just test"]
stall_threshold_seconds = 30
approval_level = "safe"
FieldDefaultDescription
enabledtrueMaster switch for auto-approval. Set to false to disable.
safe_commands[]Project-specific command prefixes appended to the built-in defaults.
stall_threshold_seconds30Seconds an agent’s last_seen must lag before its pane is polled (minimum 5).
approval_level"safe"Coarse preset: "off", "conservative", or "safe".

Built-in safe commands: cargo fmt, cargo clippy, cargo test, cargo build, git commit, git push, curl http://127.0.0.1:.

Approval-level presets:

PresetBehavior
offForces enabled = false. No detection or approval runs.
conservativeDrops git push and curl from the effective whitelist.
safe (default)Approve every entry in the built-in whitelist plus configured extras.

How it works: when an agent’s status is non-terminal (done, verified, blocked, committed are skipped) and its last_seen exceeds the threshold, git-paw runs tmux capture-pane, classifies the pending command, and either dispatches BTab Down Enter (if safe) or publishes an agent.question to the supervisor inbox (if not).

git-paw also seeds .claude/settings.json::allowed_bash_prefixes with the broker endpoints (/publish, /status, /poll, /feedback) so the first broker call never hits a permission prompt. Existing entries in that file are preserved.

Common dev-command allowlist

On every supervisor session start, git-paw seeds a curated preset of dev-loop prefix patterns into .claude/settings.json::allowed_bash_prefixes so agents do not hit a permission prompt for each variant of cargo build, git commit, just check, mdbook build, etc. The mechanism is the same one Claude uses for its “Yes, don’t ask again” flow — but seeded up-front rather than approved one-by-one.

[supervisor.common_dev_allowlist]
enabled = true
extra = ["pnpm test", "deno fmt"]
FieldDefaultDescription
enabledtrueMaster switch for the seeder. Set to false to skip seeding entirely.
extra[]Additional project-specific prefix patterns appended to the built-in preset.

Built-in preset (all v0.5.0 entries):

  • Cargo: cargo build, cargo test, cargo clippy, cargo fmt, cargo check, cargo tree, cargo deny, cargo update
  • Git (read): git status, git log, git diff, git show, git fetch
  • Git (write, non-destructive): git commit, git push, git pull, git merge, git stash, git add, git restore, git rm
  • Just: just (any recipe)
  • mdBook: mdbook build
  • OpenSpec: openspec validate, openspec new, openspec archive, openspec list, openspec status, openspec instructions
  • Search (read-only): find, grep, sed -n

Intentional exclusions: cargo install, cargo run, cargo bench, git rebase, git reset, git checkout, git push --force, sed without -n, and non-cargo package managers (npm, pnpm, yarn, deno, bun, uv, pip, pipx, gem). Add them via extra if you accept the wider surface for your project.

Behaviour:

  • Independent of broker status — non-broker supervisor sessions still benefit.
  • Idempotent: re-seeding on session re-attach never duplicates entries.
  • Non-fatal: write failures log a warning to stderr and session start continues.
  • Targets <repo>/.claude/settings.json always; also writes ~/.claude-oss/settings.json when that directory pre-exists (the alt-config dogfood pattern) but never creates the directory.
  • Entries persist after git paw stop — prune .claude/settings.json manually if you want a clean slate.

Conflict detector tuning

When supervisor mode is enabled, the broker runs an in-process conflict detector that auto-emits agent.feedback (and optionally agent.question) on forward, in-flight, and ownership conflicts. See Agent Coordination § Automatic Conflict Detection for the runtime semantics; the table below documents the configuration surface.

[supervisor.conflict]
window_seconds = 120
warn_on_intent_overlap = true
escalate_on_violation = true
FieldDefaultDescription
window_seconds120Seconds the detector waits before escalating an unresolved in-flight conflict to the supervisor inbox via agent.question.
warn_on_intent_overlaptrueMaster switch for forward-conflict feedback. When false, two agents declaring overlapping agent.intent files no longer trigger agent.feedback, but the intent tracker still records them (so in-flight and ownership detection continue to work).
escalate_on_violationtrueWhether ownership violations escalate to the supervisor inbox. When false, the violator still receives agent.feedback, but no follow-up agent.question lands in the supervisor inbox.

The [supervisor.conflict] table is fully optional. A v0.4 config with [supervisor] and no [supervisor.conflict] loads cleanly with every field at the defaults above. Setting [supervisor] enabled = false (or omitting the section) disables the detector subsystem entirely — no auto-emitted warnings fire regardless of the values here.

Learnings mode tuning

When supervisor mode is active, the parent [supervisor] learnings = true flag (default false) activates the learnings subsystem. Entries are appended to .git-paw/session-learnings.md covering the five deterministic categories tracked in v0.5.0 (stuck duration, recovery-cycle count, forward conflicts, in-flight conflicts, ownership violations). The [supervisor.learnings_config] sub-table tunes the flush cadence; the master switch lives on the parent table.

[supervisor]
learnings = true

[supervisor.learnings_config]
flush_interval_seconds = 60
FieldDefaultDescription
flush_interval_seconds60How often the learnings aggregator flushes accumulated entries from memory to .git-paw/session-learnings.md. The file is append-only across sessions; a longer interval batches more entries per write.

See the Learnings Mode chapter for the category-by-category walkthrough, the output-file format, and the v0.6.0 roadmap for programmatic access via the agent.learning broker variant.

Governance

Point git-paw at your project’s existing governance documents so the supervisor can read them as context. All fields are optional — list only the docs you have.

[governance]
adr = "docs/adr"                          # directory of ADR files
test_strategy = "docs/test-strategy.md"   # single Markdown file
security = "docs/security-checklist.md"   # single Markdown file
dod = "docs/definition-of-done.md"        # single Markdown file
constitution = ".specify/memory/constitution.md"  # single Markdown file
FieldKindDescription
adrdirectoryArchitecture Decision Records. git-paw does not care which convention (Nygard, MADR, adr-tools) — point at the folder where they live.
test_strategyfileThe team’s test-strategy document.
securityfileSecurity checklist (OWASP-style, project-specific, whatever the team uses).
dodfileDefinition of Done for completed work.
constitutionfileProject constitution. Spec Kit users normally let this auto-wire (see below).

git-paw does not dictate the structure, format, or rubric of any of these documents. The supervisor LLM reads them as context and applies judgment during its existing audit flow. There is no [governance.gates] table and no per-doc enforcement switch — gating-per-doc would require git-paw to define “failure” for each doc type, and that is a process choice your team owns.

Paths are stored verbatim and resolved against the repository root at use time. Relative paths point at files inside the repo; absolute paths are accepted as-is. A path that does not exist still loads cleanly — git-paw does not stat the filesystem at config-load. If you point at a missing file, the runtime consumer flags it.

Spec Kit constitution auto-wiring

When governance.constitution is unset AND [specs] type = "speckit", git-paw probes for <specs_dir>/../memory/constitution.md and, if present, populates governance.constitution automatically. This means a typical Spec Kit project (with .specify/specs/ and .specify/memory/constitution.md) gets the constitution wired up without any [governance] entry.

Explicit values always win. If governance.constitution is set to anything — including a path that does not exist or an empty string — auto-wiring is skipped:

[governance]
constitution = ""   # disables auto-wiring without deleting the slot

Auto-wiring only runs for the Spec Kit backend. With [specs] type = "openspec", type = "markdown", or no [specs] section, governance.constitution stays whatever the TOML says (defaulting to None).

What the supervisor does with these paths

This [governance] table is the storage slot. The runtime consumer — boot-prompt injection so the supervisor can read each doc and apply it to its audit — lives in the parallel governance-context capability. See the Governance chapter of the user guide for what that looks like end-to-end.

Dashboard

Configure the dashboard TUI rendered in pane 0 when the broker is enabled.

[dashboard]
show_message_log = true
FieldDefaultDescription
show_message_logfalseWhen true, the dashboard renders a scrolling broker-message panel above the prompts section. Useful for watching live agent traffic; leave false for a more compact layout.

See Dashboard for details.

Multi-repo configuration

Each repository can have its own dashboard settings in .git-paw/config.toml. The repo-level config overrides the global config.

Merging Rules

When both global and repo configs exist, they merge with these rules:

FieldMerge behavior
default_cliRepo wins
default_spec_cliRepo wins
branch_prefixRepo wins
mouseRepo wins
clisMaps merge (repo overrides per-key)
presetsMaps merge (repo overrides per-key)
specsRepo wins
loggingRepo wins
brokerRepo wins
supervisorRepo wins
dashboardRepo wins
governancePer-field merge (repo wins on each set field, unset fields fall back to global)

Example: If global config defines [clis.my-agent] and repo config defines [clis.my-agent] with a different command, the repo version wins. But a [clis.other-tool] in global config still appears — maps are merged, not replaced.

Graceful Absence

If no config files exist, git-paw uses defaults:

  • No default CLI (prompts for selection)
  • Mouse mode enabled
  • No custom CLIs
  • No presets

Supported AI CLIs

git-paw auto-detects these AI coding CLIs on your PATH:

CLIBinaryDescriptionInstall
Claude CodeclaudeAnthropic’s AI coding assistantnpm i -g @anthropic-ai/claude-code
CodexcodexOpenAI’s coding agentnpm i -g @openai/codex
Gemini CLIgeminiGoogle’s Gemini in the terminalnpm i -g @anthropic-ai/gemini-cli
AideraiderAI pair programming in the terminalpip install aider-chat
VibevibeMistral AI’s coding CLISee project docs
QwenqwenAlibaba’s Qwen coding CLISee project docs
AmpampSourcegraph’s AI coding agentSee project docs

How Detection Works

git-paw scans your PATH for each known binary name. If found, it records the full path and makes the CLI available for selection.

Detection runs every time you start a session, so newly installed CLIs are picked up automatically.

Adding Custom CLIs

Any AI CLI not in the list above can be registered as a custom CLI:

# Register by path
git paw add-cli my-agent /usr/local/bin/my-agent

# Register by binary name (resolved via PATH)
git paw add-cli my-agent my-agent --display-name "My Agent"

Custom CLIs appear alongside detected ones in the selection prompt. See Configuration for more details.

Deduplication

If a custom CLI has the same binary name as a detected one, the custom definition takes precedence. This lets you override the path or display name of a detected CLI.

Missing CLIs

If a custom CLI’s command cannot be found (the binary doesn’t exist at the specified path and isn’t on PATH), it is excluded from the selection list with a warning. This prevents launching sessions that would immediately fail.

CLI Reference

git-paw is invoked as git paw (or git-paw). Below is the reference for all subcommands and flags.

git paw

Running with no subcommand is equivalent to git paw start.

Parallel AI Worktrees — orchestrate multiple AI coding CLI sessions across git worktrees

Usage: git-paw [COMMAND]

Commands:
  start       Launch a new session or reattach to an existing one
  pause       Soft-stop: detach client, stop broker, keep CLIs running
  stop        Stop the session (kills tmux, keeps worktrees and state)
  purge       Remove everything (tmux session, worktrees, and state)
  status      Show session state for the current repo
  list-clis   List detected and custom AI CLIs
  add-cli     Register a custom AI CLI
  remove-cli  Unregister a custom AI CLI
  init        Initialize the repository for git-paw (creates .git-paw/)
  replay      Replay a captured pane log (requires session logging)
  help        Print this message or the help of the given subcommand(s)

Options:
  -h, --help     Print help
  -V, --version  Print version

git paw init

Initializes a repository for git-paw. Creates the .git-paw/ directory, a default config.toml, the logs directory, and sets up .gitignore.

Usage: git-paw init

Options:
  -h, --help  Print help

Running init is idempotent — it’s safe to run multiple times.

What it creates:

  • .git-paw/config.toml — default configuration
  • .git-paw/logs/ — log directory (added to .gitignore)

Example:

git paw init

git paw start

Smart start: reattaches if a session is active, recovers if stopped/crashed, or launches a new interactive session.

Usage: git-paw start [OPTIONS]

Options:
      --cli <CLI>              AI CLI to use (skips CLI picker)
      --branches <BRANCHES>    Comma-separated branches (skips branch picker)
      --from-all-specs         Launch from every discovered spec across all configured formats
      --specs [<NAMES>...]     Comma-separated spec names; bare flag opens picker (TTY required)
      --specs-format <FORMAT>  Override spec backend: openspec, markdown, speckit
      --dry-run                Preview the session plan without executing
      --preset <PRESET>        Use a named preset from config
      --supervisor             Run the session in supervisor mode (auto-start agents,
                                run test_command between merges, write session summary)
      --no-supervisor          Disable supervisor for this session, overriding any
                                `[supervisor] enabled = true` in config
      --force                  With `--from-all-specs`/`--specs`, bypass the uncommitted-spec warning
      --no-rebase              Skip rebasing existing agent branches onto the default branch
  -h, --help                   Print help
FlagAccepted valuesPurpose
--cliname of a detected or custom CLISkip the interactive CLI picker; assign this CLI to every agent that doesn’t otherwise pin one.
--branchescomma-separated branchesSkip the interactive branch picker; launch one worktree per branch.
--from-all-specs(flag)Launch every discovered spec across the configured backend. Mutually exclusive with --specs.
--specscomma-separated spec names; bare flag opens a multi-select picker (TTY required)Narrow the session to named specs or open the picker. Mutually exclusive with --from-all-specs.
--specs-formatopenspec, markdown, speckitOverride [specs] type in config and the .specify/ auto-detection for this launch.
--dry-run(flag)Print the session plan; create no worktrees and run no tmux commands.
--presetpreset name from configUse a named [presets.<name>] entry.
--supervisor(flag)Force supervisor mode on. Mutually exclusive with --no-supervisor.
--no-supervisor(flag)Force supervisor mode off (highest precedence in the resolution chain). Mutually exclusive with --supervisor.
--force(flag)Bypass the uncommitted-spec validation warning when launching from specs.
--no-rebase(flag)Skip the default-on rebase of existing agent branches onto the repository’s default branch.

--from-all-specs and --specs are mutually exclusive — one launches every discovered spec, the other narrows to a subset or opens the picker.

Examples:

git paw start
git paw start --cli claude
git paw start --cli claude --branches feat/auth,feat/api
git paw start --dry-run
git paw start --preset backend

# Launch every discovered spec
git paw start --from-all-specs
git paw start --from-all-specs --cli claude
git paw start --from-all-specs --dry-run

# Narrow to specific specs or open the multi-select picker
git paw start --specs add-auth,fix-session
git paw start --specs   # interactive picker (requires a TTY)

# Skip supervisor for this session even when `[supervisor] enabled = true` is set
git paw start --no-supervisor
git paw start --from-all-specs --no-supervisor

Supervisor mode resolution chain

git-paw decides whether to enter supervisor mode using this order (first match wins):

  1. --no-supervisor flag present → supervisor disabled (no prompt, regardless of config).
  2. --supervisor flag present → supervisor enabled (no prompt).
  3. [supervisor] enabled = true in config → supervisor enabled (no prompt).
  4. [supervisor] enabled = false in config → supervisor disabled (no prompt).
  5. No [supervisor] section + --dry-run → supervisor disabled (skip prompt).
  6. No [supervisor] section + interactive TTY → prompt “Start in supervisor mode?”.
  7. No [supervisor] section + non-TTY → supervisor disabled (fallback).

--supervisor and --no-supervisor are mutually exclusive at parse time; passing both is rejected by clap before any command runs.

See Spec-Driven Launch for details on spec formats and configuration.

git paw pause

Soft-stops the session: detaches the tmux client, stops the broker, and leaves every CLI pane running in the background. Preserves agent conversation state for instant resume via git paw start. RAM stays allocated (~300 MB per Claude pane).

Use pause for short breaks (lunch, meetings, end-of-day). For longer breaks, use git paw stop to kill the CLIs and release RAM. See Pause and Resume for the full trade-off discussion.

Usage: git-paw pause

Options:
  -h, --help  Print help

Example:

git paw pause

Idempotent: pausing an already-paused or already-stopped session is a friendly no-op.

git paw stop

Kills the tmux session and every CLI pane process, but preserves worktrees and session state on disk. CLI conversation context is lost. Run git paw start later to recover the session with fresh CLI processes.

v0.5.0 change: stop now prompts for confirmation when stdin is a TTY. Pass --force to skip the prompt (scripts); non-TTY contexts (CI, pipes) bypass the prompt automatically for v0.4 back-compat.

Usage: git-paw stop [OPTIONS]

Options:
      --force  Skip confirmation prompt
  -h, --help   Print help

Examples:

git paw stop          # prompts in TTY, bypasses in non-TTY
git paw stop --force  # always bypasses the prompt

When the session is currently paused, the confirmation prompt additionally warns that continuing will kill the still-running CLIs.

git paw purge

Nuclear option: kills the tmux session, removes all worktrees, and deletes session state. When the broker was enabled, also removes broker.log. Requires confirmation unless --force is used.

Usage: git-paw purge [OPTIONS]

Options:
      --force  Skip confirmation prompt
  -h, --help   Print help

Examples:

git paw purge
git paw purge --force

git paw status

Displays the current session status, branches, CLIs, and worktree paths for the repository in the current directory. When the broker is enabled, also shows the broker URL and connected agent count.

Usage: git-paw status

Options:
  -h, --help  Print help

Example:

git paw status

git paw list-clis

Shows all AI CLIs found on PATH (auto-detected) and any custom CLIs registered in your config.

Usage: git-paw list-clis

Options:
  -h, --help  Print help

Example:

git paw list-clis

git paw add-cli

Adds a custom CLI to your global config (~/.config/git-paw/config.toml). The command can be an absolute path or a binary name on PATH.

Usage: git-paw add-cli [OPTIONS] <NAME> <COMMAND>

Arguments:
  <NAME>     Name to register the CLI as
  <COMMAND>  Command or path to the CLI binary

Options:
      --display-name <DISPLAY_NAME>  Display name shown in prompts
  -h, --help                         Print help

Examples:

git paw add-cli my-agent /usr/local/bin/my-agent
git paw add-cli my-agent my-agent --display-name "My Agent"

git paw remove-cli

Removes a custom CLI from your global config. Only custom CLIs can be removed — auto-detected CLIs cannot.

Usage: git-paw remove-cli <NAME>

Arguments:
  <NAME>  Name of the custom CLI to remove

Options:
  -h, --help  Print help

Example:

git paw remove-cli my-agent

git paw replay

Replay captured session logs. Requires logging to be enabled.

Usage: git-paw replay [OPTIONS] [BRANCH]

Arguments:
  [BRANCH]  Branch to replay (fuzzy-matched against log filenames)

Options:
      --list              List available log sessions and branches
      --color             Display with colors via less -R
      --session <SESSION> Session to replay from (defaults to most recent)
  -h, --help              Print help

Examples:

# List all logged sessions and branches
git paw replay --list

# Replay a branch (stripped of ANSI codes)
git paw replay feat/add-auth

# Replay with colors
git paw replay feat/add-auth --color

# Replay from a specific session
git paw replay feat/add-auth --session paw-my-project

Exit Codes

CodeMeaning
0Success
1Error (git, tmux, config, or other failure)
2User cancelled (Ctrl+C or empty selection)

Architecture

This chapter covers git-paw’s internal architecture: module structure, data flow, and key design decisions.

Module Diagram

┌─────────────────────────────────────────────────────────────────┐
│                              main.rs                             │
│                       (entry point, dispatch)                    │
├──────────┬──────────────┬──────────────┬─────────────┬───────────┤
│  cli.rs  │ interactive  │   config.rs  │  error.rs   │  dirs.rs  │
│  (clap)  │ (dialoguer)  │    (TOML)    │ (PawError)  │  (XDG)    │
├──────────┴──────────────┴──────────────┴─────────────┴───────────┤
│                                                                   │
│  detect.rs    git.rs      tmux.rs    session.rs    logging.rs    │
│  (PATH scan)  (worktrees) (builder)  (JSON state)  (pane logs)   │
│                                                                   │
│  agents.rs    skills.rs   init.rs    replay.rs                   │
│  (AGENTS.md)  (skill      (project    (log                       │
│               templates)  bootstrap)  playback)                  │
├───────────────────────────────────────────────────────────────────┤
│  broker/                  supervisor/             specs/         │
│  ├── mod.rs               ├── mod.rs              ├── mod.rs     │
│  ├── server.rs            ├── approve.rs          ├── openspec.rs│
│  ├── messages.rs          ├── auto_approve.rs     ├── markdown.rs│
│  ├── delivery.rs          ├── curl_allowlist.rs   ├── speckit.rs │
│  ├── conflict.rs          ├── dev_allowlist.rs    └── resolve.rs │
│  ├── learnings.rs         ├── layout.rs                          │
│  ├── watcher.rs           ├── permission_prompt.rs               │
│  └── publish.rs           ├── poll.rs                            │
│                           └── stall.rs                           │
└───────────────────────────────────────────────────────────────────┘

Module Responsibilities

ModuleFilePurpose
CLIsrc/cli.rsArgument parsing with clap v4 derive macros. Defines all subcommands, flags (--from-all-specs, --specs, --specs-format, --supervisor, --no-supervisor, --force, …), and help text.
Detectionsrc/detect.rsScans PATH for known AI CLI binaries (KNOWN_CLIS). Resolves custom CLIs from config. Merges and deduplicates.
Gitsrc/git.rsValidates git repos, lists branches (local + remote, deduplicated), creates/removes worktrees, derives safe directory names.
Tmuxsrc/tmux.rsBuilder pattern for tmux operations. Creates sessions, splits panes, sends commands, applies the supervisor-as-pane layout, sets pane titles.
Sessionsrc/session.rsPersists session state to JSON files under ~/.local/share/git-paw/sessions/. Atomic writes, crash recovery.
Configsrc/config.rsParses TOML from global (~/.config/git-paw/config.toml) and per-repo (.git-paw/config.toml). Merges with repo-wins semantics.
Interactivesrc/interactive.rsTerminal prompts via dialoguer. Mode picker, branch multi-select, CLI picker. Skips prompts when flags are provided.
Errorsrc/error.rsPawError enum with thiserror. Actionable error messages and distinct exit codes.
Dirssrc/dirs.rsIn-tree platform XDG path helper. Replaces the upstream dirs crate (removed in v0.5.0 for license reasons); see AGENTS.md § Dependencies.
Agentssrc/agents.rsGenerates worktree AGENTS.md files; manages the <!-- git-paw:start … end --> marker region; supports the boot-prompt-full-body model.
Skillssrc/skills.rsLoads standardized agent skills from .agents/skills/ following the agentskills.io specification. Injects coordination + supervisor instructions into worktree AGENTS.md.
Initsrc/init.rsgit paw init bootstrap. Creates .git-paw/, default config, logs directory, gitignore entries. Auto-detects .specify/ for Spec Kit.
Replaysrc/replay.rsgit paw replay. Reads pane logs from .git-paw/logs/ and either strips ANSI or pipes through less -R.
Loggingsrc/logging.rsPer-pane log capture via tmux pipe-pane. Files at .git-paw/logs/<session>/<branch>.log.
Brokersrc/broker/HTTP coordination server (axum) with watcher + conflict detector + learnings subsystems. Detail below.
Supervisorsrc/supervisor/Supervisor-mode subsystems (auto-approve, dev allowlist, stall sweeps, permission prompts, pane layout). Detail below.
Specssrc/specs/Spec scanning. Three backends (openspec, markdown, speckit); resolve.rs is the dispatch entry point.

src/broker/ modules

FilePurpose
src/broker/mod.rsPublic surface (start/stop entry points, shared state types).
src/broker/server.rsaxum HTTP server: /publish, /messages/:agent_id, /status.
src/broker/messages.rsBrokerMessage enum + payload types + slug validation. Source of truth for the wire format used in user-facing examples.
src/broker/publish.rsValidation + sequence assignment for incoming /publish calls.
src/broker/delivery.rsRouting layer: which inboxes a message lands in (broadcast, supervisor inbox, targeted delivery).
src/broker/watcher.rsFilesystem watcher that auto-publishes agent.status (with modified_files) whenever a tracked file changes in a worktree.
src/broker/conflict.rsForward / in-flight / ownership conflict detection. Auto-emits [conflict-detector]-tagged agent.feedback and escalates via agent.question.
src/broker/learnings.rsOpt-in learnings subsystem. Aggregates the five deterministic categories and flushes to .git-paw/session-learnings.md.

src/dashboard.rs (top-level, not inside src/broker/) renders the dashboard pane — the live status table and the optional message-log panel — by reading the shared broker state. The dashboard pane sits at pane index 1 in supervisor mode (see the layout diagram below) and at pane 0 in non-supervisor broker mode.

src/supervisor/ modules

FilePurpose
src/supervisor/mod.rsSupervisor boot — composes the subsystems below and drives the supervisor pane.
src/supervisor/approve.rsGeneric approval/feedback decision plumbing shared by the auto-approver.
src/supervisor/auto_approve.rsSafe-command auto-approver against stalled panes (approval_level, safe_commands, sweeps).
src/supervisor/curl_allowlist.rsSeeds the broker curl endpoints into .claude/settings.json::allowed_bash_prefixes so the first broker call never hits a permission prompt.
src/supervisor/dev_allowlist.rsSeeds the curated [supervisor.common_dev_allowlist] preset (cargo / git / just / mdBook / OpenSpec) into .claude/settings.json.
src/supervisor/layout.rsSupervisor-as-pane tmux layout: pane 0 supervisor, pane 1 dashboard, agent panes 2 onwards in the bottom-row grid (row-height proportions documented below).
src/supervisor/permission_prompt.rsPane classification for permission-prompt detection (tmux capture-pane parsing).
src/supervisor/poll.rsStalled-pane polling loop driving the auto-approver.
src/supervisor/stall.rsStall heuristics (last-seen window, approval-level filter).

src/specs/ modules

FilePurpose
src/specs/mod.rsPublic surface for the spec subsystem.
src/specs/resolve.rsDispatch entry point. Picks the backend from [specs] type, the --specs-format CLI override, or .specify/ auto-detection.
src/specs/openspec.rsOpenSpec backend: scans <dir>/<change>/tasks.md directories, skips <dir>/archive/.
src/specs/markdown.rsMarkdown backend: scans flat .md files with YAML frontmatter; only paw_status: pending is picked up.
src/specs/speckit.rsSpec Kit backend: scans .specify/specs/<feature>/, decomposes the current phase into [P]-task worktrees plus one consolidated phase/… worktree; probes <dir>/../memory/constitution.md for the governance auto-wire.

Start Flow

The start command is the primary flow. Here’s what happens step by step:

git paw start
     │
     ▼
┌─ Check for existing session ──────────────────────┐
│                                                     │
│  Session active + tmux alive?  ──yes──► Reattach   │
│         │ no                                        │
│  Session saved + tmux dead?   ──yes──► Recover     │
│         │ no                                        │
│  No session                   ──────► Fresh start  │
└─────────────────────────────────────────────────────┘
     │
     ▼ (fresh start)
┌─ Validate git repo ─────────────────────────────────┐
│  git.validate_repo() → repo root path               │
└──────────────────────────────────────────────────────┘
     │
     ▼
┌─ Load config ────────────────────────────────────────┐
│  config.load_config() → merged PawConfig             │
└──────────────────────────────────────────────────────┘
     │
     ▼
┌─ Detect CLIs ────────────────────────────────────────┐
│  detect.detect_clis() → Vec<CliInfo>                 │
│  (auto-detected + custom, deduplicated)              │
└──────────────────────────────────────────────────────┘
     │
     ▼
┌─ Interactive selection ──────────────────────────────┐
│  interactive.run_selection()                          │
│  → Vec<(branch, cli)> mappings                       │
│  (skipped if --cli + --branches provided)            │
└──────────────────────────────────────────────────────┘
     │
     ▼
┌─ Create worktrees ───────────────────────────────────┐
│  git.create_worktree() for each branch               │
│  → ../project-branch-name/ directories               │
└──────────────────────────────────────────────────────┘
     │
     ▼
┌─ Build tmux session ────────────────────────────────┐
│  TmuxSessionBuilder                                  │
│    .session_name("paw-project")                      │
│    .pane(branch, worktree, cli) × N                  │
│    .mouse(true)                                      │
│    .build() → TmuxSession with command sequence      │
└──────────────────────────────────────────────────────┘
     │
     ▼
┌─ Mode? ─────────────────────────────────────────────┐
│  supervisor   → Pane 0 = supervisor CLI              │
│                 Pane 1 = `git paw __dashboard`       │
│                 Pane 2..N = per-spec agent CLIs      │
│  broker-only  → Pane 0 = `git paw __dashboard`       │
│                 Pane 1..N = per-branch agent CLIs    │
│  no broker    → Pane 0..N = per-branch agent CLIs    │
│                                                       │
│  In every broker mode the dashboard pane:            │
│   ├─ Starts axum HTTP server on configured port      │
│   ├─ Injects GIT_PAW_BROKER_URL into all agent panes │
│   └─ Renders the ratatui status table                │
└──────────────────────────────────────────────────────┘
     │
     ▼
┌─ Save session state ────────────────────────────────┐
│  session.save_session() → atomic JSON write          │
└──────────────────────────────────────────────────────┘
     │
     ▼
┌─ Attach ─────────────────────────────────────────────┐
│  tmux.attach() → user enters tmux session            │
└──────────────────────────────────────────────────────┘

Broker Architecture

When [broker] enabled = true, the dashboard pane runs git paw __dashboard. This single process hosts both the HTTP broker and the dashboard TUI. The dashboard pane sits at pane 1 in supervisor mode and at pane 0 in non-supervisor broker mode.

Dashboard pane process (git paw __dashboard):
├── tokio runtime (background threads)
│   ├── axum HTTP server on localhost:9119
│   │   ├── POST /publish
│   │   ├── GET /messages/:agent_id?since=N
│   │   └── GET /status
│   ├── Filesystem watcher (src/broker/watcher.rs)
│   │   └── Auto-publishes agent.status on file changes
│   ├── Conflict detector (src/broker/conflict.rs)
│   │   └── Forward / in-flight / ownership shapes
│   └── Learnings aggregator (src/broker/learnings.rs)
│       └── Opt-in; flushes to .git-paw/session-learnings.md
├── Flush thread (std::thread, 5s interval)
│   └── Appends to broker.log
└── Main thread
    └── ratatui dashboard (1s tick)

Broker state

The broker state is held in Arc<Mutex<...>> by src/broker/mod.rs and shared between the axum server handlers, the watcher, the conflict detector, the learnings aggregator, and the ratatui dashboard render loop. The server writes incoming messages (validated and sequenced by src/broker/publish.rs, routed by src/broker/delivery.rs); the dashboard reads the latest snapshot each tick.

The flush thread periodically serializes the message log to .git-paw/broker.log as a JSONL audit trail. This runs on a plain std::thread to avoid contention with the tokio runtime.

Environment injection

When the broker is enabled, git-paw sets GIT_PAW_BROKER_URL=http://127.0.0.1:<port> in the tmux environment for the session. Each agent pane inherits this variable and can use it to communicate with the broker.

Supervisor Mode Layout

When --supervisor is active (or [supervisor] enabled = true), the tmux session is laid out as a 50/50 top row plus a row-major agent grid below. This is the canonical v0.5.0 supervisor-as-pane layout established by the supervisor-as-pane archive.

┌──────────────────────────┬──────────────────────────┐
│  pane 0: supervisor      │  pane 1: dashboard       │
├──┬──┬──┬──┬──┬───────────┴──────────────────────────┤
│ 2│ 3│ 4│ 5│ 6│  agent grid (row 1)                  │
├──┴──┴──┴──┴──┤                                      │
│ 7│..│..│..│ N│  agent grid (row 2..M)               │
└──┴──┴──┴──┴──┴──────────────────────────────────────┘

Pane 0 always hosts the supervisor CLI; pane 1 always hosts the dashboard. Pane indices 2 onwards host one CLI per agent. The supervisor reads agent state via the broker and the dashboard; the dashboard reads the same broker state for its status table.

Row-height proportions

The top row is fixed at 50% of the supervisor pane width and the agent rows share the remaining vertical space. Row-height proportions for the agent grid depend on how many bottom rows the layout produces:

Agent rowsBottom-row heights
160% (top row 40%)
240% / 30% / 30% (top + 2 bottom rows)
328% / 24% / 24% / 24%
428% / 18% / 18% / 18% / 18%
528% / 14.4% / 14.4% / 14.4% / 14.4% / 14.4%

The agent-grid columns within each row are split evenly via tmux’s tiled layout. src/supervisor/layout.rs is the source of truth.

Non-Supervisor Layout

When supervisor mode is OFF and the broker is on, the dashboard occupies pane 0 and the agent CLIs occupy panes 1 onwards in a single row-major grid (no top row):

┌───────────────────────────────────────────────────────┐
│  pane 0: dashboard                                    │
├──┬──┬──┬──┬──┬────────────────────────────────────────┤
│ 1│ 2│ 3│ 4│ 5│  agent grid (row 1)                   │
├──┴──┴──┴──┴──┤                                       │
│ 6│..│..│..│ N│  agent grid (row 2..M)                │
└──┴──┴──┴──┴──┴───────────────────────────────────────┘

When the broker is disabled too, every pane (0..N) is an agent CLI and there is no dashboard pane.

Worktree Lifecycle

Git worktrees are the foundation of git-paw’s parallel workflow.

Creation

For a project named my-app and branch feature/auth-flow:

my-app/                         ← main repo (current directory)
my-app-feature-auth-flow/       ← worktree (created by git-paw)
my-app-feat-api/                ← worktree (created by git-paw)

Worktrees are created as siblings of the main repo directory. The naming convention is <project>-<sanitized-branch> where slashes become hyphens.

Lifecycle states

create_worktree()          stop              start (recover)
     │                      │                     │
     ▼                      ▼                     ▼
  [exists on disk]  →  [still on disk]  →  [reused as-is]
                                                  │
                                            purge │
                                                  ▼
                                          [removed from disk]

Key points:

  • Stop preserves worktrees — uncommitted work survives
  • Recover reuses existing worktrees — no data loss
  • Purge removes worktrees — git worktree remove followed by prune

Session State

Session state is persisted as JSON under ~/.local/share/git-paw/sessions/:

{
  "session_name": "paw-my-app",
  "repo_path": "/Users/you/projects/my-app",
  "project_name": "my-app",
  "created_at": "2025-01-15T10:30:00Z",
  "status": "active",
  "broker_port": 9119,
  "broker_enabled": true,
  "worktrees": [
    {
      "branch": "feat/auth",
      "worktree_path": "/Users/you/projects/my-app-feat-auth",
      "cli": "claude"
    },
    {
      "branch": "feat/api",
      "worktree_path": "/Users/you/projects/my-app-feat-api",
      "cli": "gemini"
    }
  ]
}

The broker_port and broker_enabled fields are present when the broker is configured. They allow git paw status to display broker information and git paw purge to clean up broker.log.

Atomic writes

Session state is written atomically: write to a temporary file, then rename. This prevents corruption if the process is killed mid-write.

Effective status

The on-disk status may not reflect reality (e.g., tmux was killed externally). git-paw checks the actual tmux state:

File statustmux alive?Effective status
activeYesActive (reattach)
activeNoStopped (recover)
stoppedN/AStopped (recover)
No fileN/ANo session

Tmux Builder Pattern

The tmux module uses a builder pattern that accumulates operations as data structures rather than immediately executing shell commands. This enables:

  • Testability — generate commands without executing them
  • Dry run — print the plan without side effects
  • Atomicity — validate the full plan before running anything
#![allow(unused)]
fn main() {
TmuxSessionBuilder::new()
    .session_name("paw-my-app")
    .pane(PaneSpec { branch, worktree_path, cli_command })
    .pane(PaneSpec { ... })
    .mouse(true)
    .build()
    // → TmuxSession { name, commands: Vec<TmuxCommand> }
}

The built TmuxSession can be inspected, printed (dry run), or executed.

Error Strategy

All errors flow through PawError (defined with thiserror). Each variant carries an actionable message telling the user what went wrong and how to fix it. No panics in non-test code — all Result propagation.

Exit codes:

  • 0 — success
  • 1 — operational error
  • 2 — user cancelled

Specifications

git-paw uses OpenSpec for formal, testable specifications. Each capability has a dedicated spec file using RFC 2119 keywords (SHALL, MUST, SHOULD) and GIVEN/WHEN/THEN scenarios.

Specification Index

CapabilityDescription
CLI ParsingCommand-line argument parsing and subcommands
CLI DetectionAuto-detect AI CLIs on PATH, load custom CLIs
Git OperationsValidate repos, list branches, manage worktrees
Tmux OrchestrationCreate sessions, manage panes, apply layout
Session StatePersist and recover session state
ConfigurationParse and merge TOML config files
Interactive SelectionUser prompts for mode, branch, and CLI selection
Error HandlingUnified error types with exit codes

CLI Parsing

Purpose

Define the command-line interface for git-paw using clap v4. Declares all subcommands (start, stop, purge, status, list-clis, add-cli, remove-cli), their flags, and argument validation. When no subcommand is given, defaults to start.

Requirements

Requirement: Default to start when no subcommand is given

The system SHALL treat no arguments as equivalent to start with no flags.

The system SHALL also accept a hidden __dashboard subcommand that does not appear in --help output. This subcommand is used internally by pane 0 to run the broker and dashboard.

Scenario: No arguments yields None command

  • GIVEN no arguments are passed
  • WHEN the CLI is parsed
  • THEN command SHALL be None (handled as Start in main)

Scenario: __dashboard subcommand parses

  • GIVEN __dashboard is passed
  • WHEN the CLI is parsed
  • THEN the command SHALL be Command::Dashboard

Scenario: __dashboard does not appear in help

  • GIVEN --help is passed
  • WHEN the help text is rendered
  • THEN the output SHALL NOT contain __dashboard

Requirement: Start subcommand with optional flags

The start subcommand SHALL be extended to accept a --supervisor flag (boolean, defaults to false). The flag MAY be combined with any other start flags.

When --supervisor is passed, the parsed StartArgs struct SHALL have supervisor: bool set to true.

The start subcommand SHALL also accept a --no-rebase flag (boolean, defaults to false). When --no-rebase is passed, the parsed StartArgs struct SHALL have no_rebase: bool set to true. The dispatch SHALL invoke create_worktree with rebase_onto_main = !args.no_rebase for every worktree creation in the launch. When --no-rebase is omitted (i.e. no_rebase == false), agent branches SHALL be rebased onto the repository’s default branch before their worktrees are opened. When --no-rebase is present, agent branches SHALL NOT be rebased, matching the post-worktree-resume-fix v0.5.0 behaviour.

The --no-rebase flag MAY be combined with any other start flags including --supervisor, --from-specs, --cli, and --branches.

Scenario: Start with –supervisor flag

  • GIVEN start --supervisor
  • WHEN the CLI is parsed
  • THEN supervisor SHALL be true

Scenario: Start with –supervisor combined with other flags

  • GIVEN start --supervisor --cli claude --branches feat/a,feat/b
  • WHEN the CLI is parsed
  • THEN supervisor SHALL be true
  • AND cli SHALL be Some("claude")
  • AND branches SHALL be ["feat/a", "feat/b"]

Scenario: Start without –supervisor defaults to false

  • GIVEN start --cli claude
  • WHEN the CLI is parsed
  • THEN supervisor SHALL be false

Scenario: Start with –no-rebase flag

  • GIVEN start --no-rebase
  • WHEN the CLI is parsed
  • THEN no_rebase SHALL be true

Scenario: Start without –no-rebase defaults to false

  • GIVEN start --cli claude
  • WHEN the CLI is parsed
  • THEN no_rebase SHALL be false

Scenario: Start with –no-rebase combined with other flags

  • GIVEN start --no-rebase --supervisor --from-specs
  • WHEN the CLI is parsed
  • THEN no_rebase SHALL be true
  • AND supervisor SHALL be true
  • AND from_specs SHALL be true

Scenario: –no-rebase propagates to create_worktree as rebase_onto_main = false

  • GIVEN start --branches feat/a --no-rebase is invoked
  • WHEN the dispatch reaches the worktree-creation loop
  • THEN create_worktree(repo_root, "feat/a", rebase_onto_main) SHALL be called with rebase_onto_main = false

Scenario: Default start propagates rebase_onto_main = true

  • GIVEN start --branches feat/a is invoked without --no-rebase
  • WHEN the dispatch reaches the worktree-creation loop
  • THEN create_worktree(repo_root, "feat/a", rebase_onto_main) SHALL be called with rebase_onto_main = true

Requirement: Stop subcommand

The stop subcommand SHALL accept an optional --force flag (boolean, defaults to false). When --force is omitted AND stdin is a TTY, cmd_stop SHALL render an interactive confirmation prompt describing the destructive nature of stop and pointing at git paw pause (soft alternative) and git paw purge (full reset). When --force is set OR stdin is not a TTY, the prompt SHALL be skipped and the stop SHALL proceed immediately.

The long_about help text for stop SHALL name all three teardown verbs (pause, stop, purge) with a one-line summary of each, so users can choose the right verb at --help time.

Scenario: Stop parses without flags

  • GIVEN stop is passed
  • WHEN the CLI is parsed
  • THEN the command SHALL be Command::Stop { force: false }

Scenario: Stop parses with –force

  • GIVEN stop --force is passed
  • WHEN the CLI is parsed
  • THEN the command SHALL be Command::Stop { force: true }

Scenario: Stop help names all three teardown verbs

  • WHEN git paw stop --help is run
  • THEN the output SHALL mention pause as the soft alternative
  • AND the output SHALL mention purge as the full reset
  • AND the output SHALL describe what stop itself does (kills CLI processes, preserves worktrees)

Scenario: Stop with –force from a TTY skips the prompt

  • GIVEN an active session and --force is passed
  • WHEN git paw stop --force is run with stdin attached to a TTY
  • THEN no interactive prompt SHALL be rendered
  • AND the session SHALL be killed immediately

Requirement: Purge subcommand with optional –force flag

The purge subcommand SHALL accept an optional --force flag (defaults to false).

Scenario: Purge without –force

  • GIVEN purge is passed without flags
  • WHEN the CLI is parsed
  • THEN force SHALL be false

Test: cli::tests::purge_without_force

Scenario: Purge with –force

  • GIVEN purge --force is passed
  • WHEN the CLI is parsed
  • THEN force SHALL be true

Test: cli::tests::purge_with_force

Requirement: Status subcommand

The status subcommand SHALL parse with no additional arguments.

Scenario: Status parses

  • GIVEN status is passed
  • WHEN the CLI is parsed
  • THEN the command SHALL be Command::Status

Test: cli::tests::status_parses

Requirement: List-CLIs subcommand

The list-clis subcommand SHALL parse with no additional arguments.

Scenario: List-CLIs parses

  • GIVEN list-clis is passed
  • WHEN the CLI is parsed
  • THEN the command SHALL be Command::ListClis

Test: cli::tests::list_clis_parses

Requirement: Add-CLI subcommand with required and optional arguments

The add-cli subcommand SHALL require name and command positional arguments and accept an optional --display-name flag.

Scenario: Add-CLI with required arguments only

  • GIVEN add-cli my-agent /usr/local/bin/my-agent
  • WHEN the CLI is parsed
  • THEN name SHALL be "my-agent", command SHALL be the path, and display_name SHALL be None

Test: cli::tests::add_cli_with_required_args

Scenario: Add-CLI with –display-name

  • GIVEN add-cli my-agent my-agent --display-name "My Agent"
  • WHEN the CLI is parsed
  • THEN display_name SHALL be Some("My Agent")

Test: cli::tests::add_cli_with_display_name

Scenario: Add-CLI missing required arguments is rejected

  • GIVEN add-cli with no positional arguments
  • WHEN the CLI is parsed
  • THEN parsing SHALL fail

Test: cli::tests::add_cli_missing_required_args_is_rejected

Requirement: Remove-CLI subcommand with required argument

The remove-cli subcommand SHALL require a name positional argument.

Scenario: Remove-CLI parses

  • GIVEN remove-cli my-agent
  • WHEN the CLI is parsed
  • THEN name SHALL be "my-agent"

Test: cli::tests::remove_cli_parses

Requirement: Standard flags –version and –help

The CLI SHALL accept --version and --help flags.

Scenario: –version flag is accepted

  • GIVEN --version is passed
  • WHEN the CLI is parsed
  • THEN clap SHALL emit a DisplayVersion response

Test: cli::tests::version_flag_is_accepted

Scenario: –help flag is accepted

  • GIVEN --help is passed
  • WHEN the CLI is parsed
  • THEN clap SHALL emit a DisplayHelp response

Test: cli::tests::help_flag_is_accepted

Requirement: Unknown subcommands are rejected

The CLI SHALL reject unrecognized subcommands with a parse error.

Scenario: Unknown subcommand fails

  • GIVEN an unrecognized subcommand is passed
  • WHEN the CLI is parsed
  • THEN parsing SHALL fail

Test: cli::tests::unknown_subcommand_is_rejected

Requirement: Help output contains all subcommands and quick start

The start --help output SHALL list the --supervisor flag with a description.

Scenario: Start help shows –supervisor flag

  • GIVEN start --help is passed
  • WHEN the binary runs
  • THEN stdout SHALL contain --supervisor

Requirement: Version output includes binary name

The --version output SHALL include the binary name.

Scenario: Version output

  • GIVEN --version is passed
  • WHEN the binary runs
  • THEN stdout SHALL contain “git-paw”

Test: cli_tests::version_output

Requirement: No arguments behaves like start

When no subcommand is provided, the binary SHALL behave identically to start.

Scenario: No args produces same error as start

  • GIVEN the binary is run with no arguments outside a git repo
  • WHEN both git-paw and git-paw start are run
  • THEN they SHALL produce identical stderr output

Test: cli_tests::no_args_behaves_like_start

Requirement: Subcommands run without error when applicable

Subcommands that don’t require a session SHALL succeed in a valid git repo.

Scenario: Stop runs without error

  • GIVEN the binary is run in a git repo
  • WHEN stop is passed
  • THEN it SHALL succeed

Test: cli_tests::stop_runs_without_error

Scenario: Status runs without error

  • GIVEN the binary is run in a git repo
  • WHEN status is passed
  • THEN it SHALL succeed

Test: cli_tests::status_runs_without_error

Scenario: List-CLIs runs without error

  • GIVEN the binary is run in a git repo
  • WHEN list-clis is passed
  • THEN it SHALL succeed

Test: cli_tests::list_clis_runs_without_error

Requirement: Binary rejects missing required arguments

Subcommands with required arguments SHALL fail when they are missing.

Scenario: Add-CLI requires arguments

  • GIVEN add-cli is passed with no arguments
  • WHEN the binary runs
  • THEN it SHALL fail with stderr mentioning “required”

Test: cli_tests::add_cli_requires_arguments

Scenario: Remove-CLI requires argument

  • GIVEN remove-cli is passed with no arguments
  • WHEN the binary runs
  • THEN it SHALL fail with stderr mentioning “required”

Test: cli_tests::remove_cli_requires_argument

Requirement: Not-a-repo error from binary

Commands requiring a git repo SHALL fail with an actionable error when run outside one.

Scenario: Start from non-git directory

  • GIVEN the binary is run outside a git repository
  • WHEN start is passed
  • THEN it SHALL fail with stderr containing “Not a git repository”

Test: cli_tests::start_from_non_git_dir

Scenario: Unknown subcommand from binary

  • GIVEN the binary is passed an unrecognized subcommand
  • WHEN it runs
  • THEN it SHALL fail with stderr containing “error”

Test: cli_tests::unknown_subcommand_fails

Requirement: Replay subcommand

The replay subcommand SHALL accept an optional <branch> positional argument, a --list flag, a --color flag, and an optional --session flag.

Scenario: Replay with branch

  • WHEN replay feat/add-auth is passed
  • THEN branch SHALL be Some("feat/add-auth"), list SHALL be false, color SHALL be false

Scenario: Replay with –list

  • WHEN replay --list is passed
  • THEN list SHALL be true and branch SHALL be None

Scenario: Replay with –color

  • WHEN replay feat/add-auth --color is passed
  • THEN color SHALL be true

Scenario: Replay with –session

  • WHEN replay feat/add-auth --session paw-myproject is passed
  • THEN session SHALL be Some("paw-myproject")

Scenario: Replay with no arguments and no –list

  • WHEN replay is passed with no arguments and no --list
  • THEN parsing SHALL fail with an error indicating either a branch or --list is required

Scenario: Replay help text

  • WHEN replay --help is passed
  • THEN stdout SHALL contain descriptions of --list, --color, and --session flags with examples

Requirement: Init subcommand

The init subcommand SHALL parse with no required arguments.

Scenario: Init parses

  • WHEN init is passed
  • THEN the command SHALL be Command::Init

Scenario: Init help text

  • WHEN init --help is passed
  • THEN stdout SHALL contain a description of project initialization and examples

Requirement: Spec mode + supervisor mode dispatch

The start subcommand dispatch SHALL evaluate the supervisor-mode resolution chain (per supervisor-cli) BEFORE branching on --from-specs. Specifically:

  1. Resolve supervisor-mode-enabled state from the --supervisor flag, --no-supervisor flag, [supervisor] config, and the prompt fallback.
  2. If supervisor mode is enabled, route to cmd_supervisor. When --from-specs is also set, pass branches_flag = None so cmd_supervisor’s existing scan_specs(...) fallback runs to determine branches from configured specs.
  3. Otherwise, if --from-specs is set, route to cmd_start_from_specs.
  4. Otherwise, route to bare cmd_start.

This ordering ensures --from-specs --supervisor (or --from-specs with [supervisor] enabled = true in config) actually engages supervisor mode end-to-end, rather than silently degrading to spec-mode-without-supervisor.

--from-specs combined with --branches continues to follow v0.4’s existing behaviour (the spec-mode flow ignores explicit branches when from-specs is set); this change does not introduce a new mutual-exclusion error for that combination.

Scenario: –from-specs –supervisor engages supervisor mode

  • GIVEN git paw start --from-specs --supervisor is invoked
  • WHEN the dispatch resolves
  • THEN the supervisor-mode resolution chain SHALL evaluate supervisor = true
  • AND the dispatch SHALL route to cmd_supervisor
  • AND cmd_supervisor SHALL receive branches_flag = None, triggering its scan_specs(...) fallback

Scenario: –from-specs without supervisor uses spec mode

  • GIVEN git paw start --from-specs is invoked, no --supervisor flag, and [supervisor] config indicates supervisor mode is not enabled (either explicitly false or absent + non-interactive)
  • WHEN the dispatch resolves
  • THEN the dispatch SHALL route to cmd_start_from_specs

Scenario: –from-specs with [supervisor] enabled = true config engages supervisor mode

  • GIVEN git paw start --from-specs is invoked with no --supervisor flag
  • AND .git-paw/config.toml contains [supervisor] enabled = true
  • WHEN the dispatch resolves
  • THEN supervisor mode SHALL be active per the resolution chain
  • AND the dispatch SHALL route to cmd_supervisor (not cmd_start_from_specs)

Scenario: –no-supervisor –from-specs uses spec mode

  • GIVEN git paw start --from-specs --no-supervisor is invoked
  • AND [supervisor] enabled = true is set in config
  • WHEN the dispatch resolves
  • THEN supervisor mode SHALL be disabled per the resolution chain
  • AND the dispatch SHALL route to cmd_start_from_specs

Scenario: Bare start (no –from-specs, no supervisor) uses cmd_start

  • GIVEN git paw start is invoked with no --from-specs, no --supervisor, and supervisor mode is not enabled in config
  • WHEN the dispatch resolves
  • THEN the dispatch SHALL route to cmd_start

Requirement: Non-TTY launch handling

When a git paw start invocation reaches its session-launch step (after worktrees are created, panes added, and tmux_session.execute() succeeds), the system SHALL detect whether stdin is connected to a terminal via std::io::IsTerminal::is_terminal(&std::io::stdin()).

When stdin is not a terminal:

  • The system SHALL skip the tmux::attach(...) call.
  • The system SHALL print an informational message to stdout naming the launched session and the manual-attach command (tmux attach -t <session>).
  • The system SHALL exit with status 0.
  • For supervisor mode specifically, the system SHALL also skip the foreground supervisor-CLI launch (Command::new(supervisor_cli).status()) with an additional hint that supervisor mode requires an interactive terminal.

When stdin is a terminal, the launch flow proceeds as before (call tmux::attach, run the supervisor CLI in foreground for supervisor mode).

This SHALL apply to all three start paths: cmd_start, cmd_start_from_specs, and cmd_supervisor.

Scenario: Non-TTY bare start exits cleanly with attach hint

  • GIVEN git paw start --branches feat/x,feat/y is invoked with stdin redirected from /dev/null (or otherwise non-TTY)
  • WHEN the launch flow completes its session-build steps
  • THEN the command SHALL exit with status 0
  • AND stdout SHALL contain “Session ‘’ started in detached mode.”
  • AND stdout SHALL contain “Attach with: tmux attach -t
  • AND the tmux session SHALL exist and be alive after exit

Scenario: Non-TTY –from-specs exits cleanly

  • GIVEN git paw start --from-specs is invoked from a non-TTY context
  • WHEN the launch flow completes
  • THEN the command SHALL exit with status 0
  • AND the attach-hint message SHALL be printed
  • AND the tmux session SHALL exist and be alive

Scenario: Non-TTY –supervisor skips supervisor CLI launch

  • GIVEN git paw start --supervisor --from-specs is invoked from a non-TTY context
  • WHEN the launch flow completes
  • THEN the command SHALL exit with status 0
  • AND the foreground supervisor-CLI launch SHALL be skipped
  • AND stdout SHALL contain a hint indicating supervisor mode requires an interactive terminal
  • AND stdout SHALL contain the manual-attach command for the launched session

Scenario: TTY launch attaches as before

  • GIVEN git paw start --branches feat/x,feat/y is invoked from a real TTY
  • WHEN the launch flow completes its session-build steps
  • THEN the system SHALL call tmux::attach(...) for the launched session
  • AND SHALL NOT print the “started in detached mode” hint

Requirement: –from-all-specs flag

The start subcommand SHALL accept a --from-all-specs flag (boolean, default false). When passed, the resulting StartArgs SHALL indicate the “launch every discovered spec” mode — the v0.4 behaviour previously gated by --from-specs.

The flag SHALL appear in git paw start --help output with a description naming it as the canonical name for this behaviour.

Scenario: –from-all-specs sets the launch-all mode

  • GIVEN the user invokes git paw start --from-all-specs
  • WHEN the CLI is parsed
  • THEN the parsed StartArgs SHALL indicate the launch-all-discovered-specs mode

Scenario: –from-all-specs combined with –supervisor

  • GIVEN start --from-all-specs --supervisor
  • WHEN the CLI is parsed
  • THEN both from_all_specs and supervisor SHALL be true

Scenario: –from-all-specs appears in help output

  • WHEN git paw start --help is run
  • THEN the output contains --from-all-specs
  • AND the output describes the flag as launching every discovered spec

Requirement: –from-specs is a hidden alias of –from-all-specs

The start subcommand SHALL accept --from-specs as a hidden alias of --from-all-specs. When the user passes --from-specs, the parsed StartArgs SHALL be byte-for-byte identical to the parse result for --from-all-specs. No stderr warning SHALL be emitted at runtime; the alias is silent.

The alias SHALL NOT appear in git paw start --help output. The alias SHALL be removed in v1.0.0; v0.5.0 keeps it for backward compatibility with v0.4 scripts.

Scenario: –from-specs parses identically to –from-all-specs

  • GIVEN two CLI invocations: start --from-specs and start --from-all-specs
  • WHEN both are parsed
  • THEN the resulting StartArgs values SHALL be equal

Scenario: –from-specs does not appear in help

  • WHEN git paw start --help is run
  • THEN the output SHALL NOT contain the substring --from-specs

Scenario: –from-specs emits no stderr warning

  • GIVEN the user runs a command containing --from-specs
  • WHEN the CLI parses
  • THEN no stderr warning SHALL be emitted regarding the flag’s deprecation
  • AND the command proceeds exactly as if --from-all-specs had been passed

Requirement: –specs flag with comma-separated values

The start subcommand SHALL accept a --specs flag whose value is a comma-separated list of spec names (mirroring the existing --branches feat/a,feat/b syntax). The flag SHALL accept zero or more values:

  • --specs (no values) — indicates the picker mode.
  • --specs NAME — narrows to a single named spec.
  • --specs NAME1,NAME2,NAME3 — narrows to the listed specs.
  • --specs NAME1,NAME2 --specs NAME3 — equivalent to --specs NAME1,NAME2,NAME3 if clap’s value-accumulation across repetitions is enabled (implementation choice; tests assert behaviour for the comma-separated form).

The parsed value distinguishes three states:

  • Flag absent → no spec mode requested.
  • Flag present with zero values → picker mode.
  • Flag present with one or more values → narrow mode with the listed names.

The flag SHALL appear in git paw start --help output.

Scenario: –specs with single value parses as narrow

  • GIVEN start --specs add-auth
  • WHEN the CLI is parsed
  • THEN StartArgs SHALL indicate narrow mode with ["add-auth"]

Scenario: –specs with comma-separated values parses as narrow with multiple names

  • GIVEN start --specs add-auth,fix-session,add-logging
  • WHEN the CLI is parsed
  • THEN StartArgs SHALL indicate narrow mode with ["add-auth", "fix-session", "add-logging"]

Scenario: –specs with no values parses as picker

  • GIVEN start --specs
  • WHEN the CLI is parsed
  • THEN StartArgs SHALL indicate picker mode

Scenario: –specs absent leaves spec mode unset

  • GIVEN start --supervisor (no --specs, no --from-all-specs)
  • WHEN the CLI is parsed
  • THEN StartArgs SHALL indicate no spec mode (falls through to standard branch selection)

Requirement: –from-all-specs and –specs are mutually exclusive

The system SHALL reject any invocation that combines --from-all-specs (or its alias --from-specs) with --specs. clap’s parse step SHALL produce an error before the command runs. The error message SHALL clearly state that the two flags express opposing intents and SHALL list both flags.

Scenario: –from-all-specs and –specs together are rejected

  • GIVEN start --from-all-specs --specs add-auth
  • WHEN the CLI is parsed
  • THEN parsing SHALL fail with an error mentioning both --from-all-specs and --specs

Scenario: –from-specs alias and –specs together are also rejected

  • GIVEN start --from-specs --specs add-auth
  • WHEN the CLI is parsed
  • THEN parsing SHALL fail with an error mentioning both flags
  • AND the alias SHALL enforce the same mutual-exclusion rule as the canonical name

Requirement: –no-supervisor flag

The start subcommand SHALL accept a --no-supervisor flag (boolean, default false). When passed, the parsed StartArgs SHALL have no_supervisor: bool set to true. The flag SHALL appear in git paw start --help output with a description that names the use case (overriding [supervisor] enabled = true for a single session).

Scenario: –no-supervisor sets the flag

  • GIVEN the user invokes git paw start --no-supervisor
  • WHEN the CLI is parsed
  • THEN the parsed StartArgs.no_supervisor SHALL be true
  • AND StartArgs.supervisor SHALL be false

Scenario: –no-supervisor absent leaves flag false

  • GIVEN the user invokes git paw start with neither --supervisor nor --no-supervisor
  • WHEN the CLI is parsed
  • THEN StartArgs.no_supervisor SHALL be false
  • AND StartArgs.supervisor SHALL be false

Scenario: –no-supervisor appears in help output

  • WHEN git paw start --help is run
  • THEN the output contains --no-supervisor
  • AND the output describes the flag as disabling supervisor for the session and overriding any [supervisor] enabled = true config setting

Requirement: –supervisor and –no-supervisor are mutually exclusive

The system SHALL reject any invocation that combines --supervisor and --no-supervisor on the same start command. clap’s parse step SHALL produce an error before the command runs. The error message SHALL clearly state that the two flags express opposing intents and SHALL list both.

Scenario: Both flags together are rejected

  • GIVEN start --supervisor --no-supervisor
  • WHEN the CLI is parsed
  • THEN parsing SHALL fail with an error mentioning both --supervisor and --no-supervisor

Scenario: –no-supervisor combines with other flags

  • GIVEN start --no-supervisor --cli claude --branches feat/a,feat/b
  • WHEN the CLI is parsed
  • THEN no_supervisor SHALL be true
  • AND cli SHALL be Some("claude")
  • AND branches SHALL contain feat/a and feat/b
  • AND parsing SHALL succeed

Requirement: Pause subcommand

The pause subcommand SHALL parse with no additional arguments and SHALL be visible in git paw --help output. The subcommand SHALL include an about string (“Pause the session (detaches client, stops broker, leaves CLIs running)”) and a long_about string that names the RAM trade-off and points the reader at stop and (forthcoming v1.0.0) hibernate for the destructive and RAM-free alternatives respectively.

The pause subcommand SHALL appear in the root after_help quick-start guide alongside start, stop, and purge.

Scenario: Pause parses

  • GIVEN pause is passed
  • WHEN the CLI is parsed
  • THEN the command SHALL be Command::Pause

Scenario: Pause accepts no flags

  • GIVEN pause --anything is passed (any flag)
  • WHEN the CLI is parsed
  • THEN parsing SHALL fail with an unknown-argument error

Scenario: Pause appears in help

  • WHEN git paw --help is run
  • THEN the output SHALL list a pause subcommand
  • AND the output SHALL include the pause line in the quick-start after_help block

Scenario: Pause help text names the RAM trade-off

  • WHEN git paw pause --help is run
  • THEN the output SHALL mention that CLI processes remain running
  • AND the output SHALL mention the RAM-allocation trade-off (or words conveying “RAM stays held”)
  • AND the output SHALL suggest git paw stop for the RAM-releasing alternative

Requirement: git paw purge interactive confirmation SHALL honour y+Enter under all conditions

The cmd_purge interactive confirmation prompt SHALL be reliably readable by the dialoguer Confirm widget regardless of preceding stderr output. When the unmerged-commits warning has been written to stderr immediately before the prompt, the warning writer SHALL flush stderr before the prompt’s interact() call begins, so the user’s y+Enter input is not racing the warning’s buffered bytes.

Scenario: Purge with unmerged commits and y+Enter proceeds

  • GIVEN a session with at least one branch carrying commits not in main
  • AND git paw purge is invoked from a TTY
  • WHEN the prompt “Purge is irreversible. Continue?” appears and the user types y followed by Enter
  • THEN the purge SHALL proceed (kill tmux session + remove worktrees + delete session JSON)
  • AND the exit code SHALL be 0
  • AND stdout SHALL contain Purged session 'paw-...'

Scenario: Purge with unmerged commits and n+Enter cancels

  • GIVEN same setup as above
  • WHEN the user types n followed by Enter
  • THEN the purge SHALL NOT proceed
  • AND the exit code SHALL be 0
  • AND stdout SHALL contain Purge cancelled.
  • AND the session worktrees SHALL still be on disk

Scenario: Purge with bare Enter (no y/n) defaults to no

  • GIVEN same setup
  • WHEN the user types Enter without first typing y or n
  • THEN the prompt SHALL default to false (No)
  • AND the purge SHALL NOT proceed
  • AND stdout SHALL contain Purge cancelled.

Requirement: git paw purge --force SHALL propagate --force to git worktree remove and emit per-worktree progress

When git paw purge is invoked with --force, the underlying git worktree remove invocations SHALL pass --force so the removal succeeds on worktrees with uncommitted changes. The command SHALL also emit per-worktree progress messages to stderr (e.g. Removing worktree <path>... before each removal and done (<elapsed>s) after) so the user can distinguish a slow-but-progressing removal from an actual hang.

Scenario: purge --force removes dirty worktrees

  • GIVEN a session with one worktree containing uncommitted edits
  • WHEN git paw purge --force is invoked
  • THEN the dirty worktree SHALL be removed successfully
  • AND the exit code SHALL be 0
  • AND the underlying git worktree remove invocation SHALL include the --force flag

Scenario: purge --force emits per-worktree progress to stderr

  • GIVEN a session with two or more worktrees
  • WHEN git paw purge --force is invoked
  • THEN stderr SHALL contain a Removing worktree <path>... line for each worktree being removed
  • AND stderr SHALL contain a done or completion marker after each removal
  • AND the order SHALL match the worktree iteration order

Scenario: purge without --force does NOT pass --force to git worktree remove

  • GIVEN a session with one worktree containing uncommitted edits
  • WHEN git paw purge (no --force) is invoked and the user confirms with y
  • THEN the underlying git worktree remove SHALL NOT include the --force flag
  • AND if git worktree remove fails because of the dirty state, the failure SHALL be reported to stderr as warning: failed to remove worktree '<path>': <git error> per the existing error-handling path
  • AND purge SHALL continue with the remaining worktrees

Requirement: git paw stop and git paw purge SHALL strip the supervisor boot-block injection from AGENTS.md

cmd_stop and cmd_purge (src/main.rs) SHALL invoke a helper that removes the supervisor-pane boot-block injection block from <repo>/AGENTS.md. The block is bounded by HTML comment markers <!-- git-paw:start --><!-- git-paw:end --> (or similar — the actual marker strings are owned by the injection code path and SHALL match exactly). The helper SHALL be idempotent and SHALL preserve all surrounding content byte-for-byte.

Scenario: Stop strips the boot-block injection

  • GIVEN a session in which cmd_supervisor or cmd_start injected a <!-- git-paw:start --><!-- git-paw:end --> block into AGENTS.md
  • WHEN git paw stop (with or without --force) is invoked
  • AND the teardown completes successfully
  • THEN the resulting AGENTS.md SHALL contain no <!-- git-paw:start --> marker
  • AND no <!-- git-paw:end --> marker

Scenario: Purge strips the boot-block injection

  • GIVEN the same setup
  • WHEN git paw purge (with or without --force) is invoked
  • THEN AGENTS.md SHALL contain neither marker after the purge completes

Scenario: Stop/purge on AGENTS.md without markers is a no-op

  • GIVEN an AGENTS.md with no <!-- git-paw:start --> marker
  • WHEN git paw stop or git paw purge runs the cleanup helper
  • THEN AGENTS.md SHALL be byte-identical to its pre-cleanup state
  • AND the helper SHALL return success

Requirement: git paw init SHALL be idempotent and additive on existing config files

src/init.rs::run_init SHALL parse the existing .git-paw/config.toml (if any) and compare its top-level keys/tables against the bundled-default schema. The init flow SHALL append commented stanzas ONLY for keys/tables missing from the user’s config. It SHALL NEVER:

  1. Modify the value of an existing key.
  2. Add a second occurrence of any top-level table (e.g. a second [supervisor]) when the user already has that section commented OR uncommented.
  3. Re-order or reformat existing keys/sections.
  4. Strip existing user comments or blank lines.

When every bundled-default key is already present in the user’s config, init SHALL print config.toml already has all default keys; no changes and return Ok without writing.

Init invocations SHALL be idempotent: running git paw init a second time on a config that the first run produced SHALL leave the file byte-identical.

Scenario: First init writes a complete commented default config

  • GIVEN a fresh repo with no .git-paw/config.toml
  • WHEN git paw init is invoked
  • THEN the file SHALL be created
  • AND SHALL parse as valid TOML
  • AND SHALL contain commented stanzas for every bundled-default top-level key/section

Scenario: Second init on the just-written file is a no-op

  • GIVEN the same repo after the first init
  • WHEN git paw init is invoked again
  • THEN the file SHALL be byte-identical to the first-run output
  • AND the exit SHALL be 0

Scenario: Init preserves a user-authored [supervisor] block

  • GIVEN a .git-paw/config.toml containing only:
    [supervisor]
    enabled = true
    cli = "claude-oss"
    
  • WHEN git paw init is invoked
  • THEN the resulting file SHALL contain enabled = true and cli = "claude-oss" byte-identical to the input
  • AND SHALL NOT contain a second [supervisor] section header (commented or uncommented)
  • AND SHALL parse as valid TOML (no duplicate key errors)

Scenario: Init appends missing top-level sections

  • GIVEN a .git-paw/config.toml containing only branch_prefix = "feat/"
  • WHEN git paw init is invoked
  • THEN the resulting file SHALL preserve branch_prefix = "feat/" byte-identical
  • AND SHALL gain commented stanzas for every bundled-default section the user is missing ([broker], [dashboard], [supervisor], etc.)
  • AND SHALL parse as valid TOML

Scenario: Init never modifies existing user values

  • GIVEN a .git-paw/config.toml with [broker] port = 9200 (non-default port)
  • WHEN git paw init is invoked
  • THEN the resulting file SHALL still have port = 9200
  • AND SHALL NOT introduce a second port key or a commented # port = 9119 stanza inside [broker]

CLI Detection

Purpose

Detect available AI coding CLI binaries by scanning PATH for known names and merging with user-defined custom CLIs from configuration. Provides a unified, deduplicated, sorted list for interactive selection or direct use.

Requirements

Requirement: Auto-detect known AI CLIs on PATH

The system SHALL scan PATH for the known CLI binaries: claude, codex, gemini, aider, vibe, qwen, and amp.

Scenario: All known CLIs are present on PATH

  • GIVEN all 8 known CLI binaries exist on PATH
  • WHEN detect_known_clis() is called
  • THEN it SHALL return a CliInfo for each binary with source = Detected, a non-empty display_name, and a valid path

Test: detect::tests::all_known_clis_detected_when_present

Scenario: No known CLIs are present on PATH

  • GIVEN PATH contains no known CLI binaries
  • WHEN detect_known_clis() is called
  • THEN it SHALL return an empty list

Test: detect::tests::returns_empty_when_no_known_clis_on_path

Scenario: Partial set of CLIs on PATH

  • GIVEN only a subset of known CLIs exist on PATH
  • WHEN detect_known_clis() is called
  • THEN it SHALL return only the CLIs that are found

Test: detect::tests::detects_subset_of_known_clis

Requirement: Resolve and merge custom CLIs from configuration

The system SHALL resolve custom CLI definitions by looking up commands as absolute paths or via PATH, and merge them with auto-detected CLIs.

Scenario: Custom CLIs merged with detected CLIs

  • GIVEN auto-detected CLIs exist and custom CLI definitions are provided
  • WHEN detect_clis() is called
  • THEN the result SHALL contain both detected and custom CLIs

Test: detect::tests::custom_clis_merged_with_detected

Scenario: Custom CLI binary not found

  • GIVEN a custom CLI definition references a non-existent binary
  • WHEN detect_clis() is called
  • THEN the missing CLI SHALL be excluded and a warning printed to stderr

Test: detect::tests::custom_cli_excluded_when_binary_missing

Scenario: Custom CLI resolved by absolute path

  • GIVEN a custom CLI definition uses an absolute path to an existing binary
  • WHEN resolve_custom_clis() is called
  • THEN the resolved path SHALL match the absolute path provided

Test: detect::tests::custom_cli_resolved_by_absolute_path

Requirement: Custom CLIs override detected CLIs with the same name

When a custom CLI has the same binary_name as a detected CLI, the custom definition SHALL take precedence.

Scenario: Custom CLI overrides auto-detected CLI

  • GIVEN a custom CLI shares a binary_name with an auto-detected CLI
  • WHEN detect_clis() is called
  • THEN the result SHALL contain only the custom version with source = Custom

Test: detect::tests::custom_cli_overrides_detected_with_same_binary_name

Requirement: Each CLI result includes all required fields

Every CliInfo SHALL have a non-empty display_name, binary_name, a valid path, and a source indicator.

Scenario: Detected CLI has all fields populated

  • GIVEN a known CLI binary exists on PATH
  • WHEN it is detected
  • THEN all fields (display_name, binary_name, path, source) SHALL be populated

Test: detect::tests::detected_cli_has_all_fields

Scenario: Custom CLI has all fields populated

  • GIVEN a custom CLI definition is resolved
  • WHEN it is included in results
  • THEN all fields SHALL be populated

Test: detect::tests::custom_cli_has_all_fields

Requirement: Display name derivation

When no explicit display name is provided, the system SHALL derive one by capitalizing the first letter of the binary name.

Scenario: Custom CLI defaults to capitalized name

  • GIVEN a custom CLI definition has no display_name
  • WHEN it is resolved
  • THEN the display_name SHALL be the binary name with the first letter capitalized

Test: detect::tests::custom_cli_display_name_defaults_to_capitalised_name

Requirement: Results sorted by display name

The combined CLI list SHALL be sorted alphabetically by display_name (case-insensitive).

Scenario: Results are sorted

  • GIVEN multiple CLIs are detected and/or custom
  • WHEN detect_clis() is called
  • THEN the results SHALL be sorted by display name

Test: detect::tests::results_sorted_by_display_name

Requirement: CliSource display format

The CliSource enum SHALL display as "detected" or "custom".

Scenario: CliSource display strings

  • GIVEN CliSource::Detected and CliSource::Custom
  • WHEN formatted with Display
  • THEN they SHALL render as "detected" and "custom" respectively

Test: detect::tests::cli_source_display_format


Git Operations

Purpose

Validate git repositories, list branches, create and remove worktrees, and derive worktree directory names. Provides the git plumbing that underpins parallel branch sessions.

Requirements

Requirement: Validate that a path is inside a git repository

The system SHALL confirm a path is inside a git repository and return the repository root.

Scenario: Path is inside a git repository

  • GIVEN a path inside an initialized git repository
  • WHEN validate_repo() is called
  • THEN it SHALL return Ok with the absolute path to the repository root

Test: git::tests::validate_repo_returns_root_inside_repo

Scenario: Path is not inside a git repository

  • GIVEN a path that is not inside any git repository
  • WHEN validate_repo() is called
  • THEN it SHALL return Err(PawError::NotAGitRepo)

Test: git::tests::validate_repo_returns_not_a_git_repo_outside

Requirement: List branches sorted and deduplicated

The system SHALL list all local and remote branches, deduplicated, sorted, with remote prefixes stripped and HEAD pointers excluded.

Scenario: Branches are returned sorted

  • GIVEN a repository with multiple branches
  • WHEN list_branches() is called
  • THEN it SHALL return branches sorted alphabetically

Test: git::tests::list_branches_returns_sorted_branches

Scenario: Local and remote branches are deduplicated with prefix stripping

  • GIVEN a repository cloned from a remote, with branches existing both locally and as remote-tracking refs
  • WHEN list_branches() is called
  • THEN each branch SHALL appear exactly once, with origin/ prefixes stripped

Test: git_integration::list_branches_strips_remote_prefix_and_deduplicates

Requirement: Derive project name from repository path

The system SHALL extract the project name from the final component of the repository root path, falling back to "project" for root paths.

Scenario: Normal repository path

  • GIVEN a repository at /Users/jie/code/git-paw
  • WHEN project_name() is called
  • THEN it SHALL return "git-paw"

Test: git::tests::project_name_from_path

Scenario: Root path fallback

  • GIVEN a repository at /
  • WHEN project_name() is called
  • THEN it SHALL return "project"

Test: git::tests::project_name_fallback_for_root

Requirement: Build worktree directory names

The system SHALL generate worktree directory names as <project>-<sanitized-branch>, replacing / with - and stripping unsafe characters.

Scenario: Branch with single slash

  • GIVEN project "git-paw" and branch "feature/auth-flow"
  • WHEN worktree_dir_name() is called
  • THEN it SHALL return "git-paw-feature-auth-flow"

Test: git::tests::worktree_dir_name_replaces_slash_with_dash

Scenario: Branch with multiple slashes

  • GIVEN project "git-paw" and branch "feat/auth/v2"
  • WHEN worktree_dir_name() is called
  • THEN it SHALL return "git-paw-feat-auth-v2"

Test: git::tests::worktree_dir_name_handles_multiple_slashes

Scenario: Branch with special characters

  • GIVEN project "my-proj" and branch "fix/issue#42"
  • WHEN worktree_dir_name() is called
  • THEN unsafe characters SHALL be stripped, returning "my-proj-fix-issue42"

Test: git::tests::worktree_dir_name_strips_special_chars

Scenario: Simple branch name

  • GIVEN project "git-paw" and branch "main"
  • WHEN worktree_dir_name() is called
  • THEN it SHALL return "git-paw-main"

Test: git::tests::worktree_dir_name_simple_branch

Requirement: Create worktrees as siblings of the repository

The system SHALL create git worktrees in the parent directory of the repository root using the derived directory name convention.

The create_worktree function SHALL accept a rebase_onto_main: bool parameter. When rebase_onto_main is true AND the target branch already exists in the local repository, the function SHALL rebase the target branch onto the repository’s default branch (as returned by default_branch()) BEFORE performing the existence check for the worktree directory. The rebase SHALL be performed by invoking git rebase <default-branch> from the repository root. When the branch is already at or ahead of the default branch, git rebase exits zero with no rewrite; the function SHALL treat that as success.

If git rebase exits non-zero (rebase conflict or any other failure), the function SHALL invoke git rebase --abort in the repository root and return Err(PawError::WorktreeError("rebase onto main failed: <stderr>")). The branch SHALL be left at its pre-rebase HEAD after the abort; the function SHALL NOT proceed to the existence check or git worktree add when the rebase failed.

If rebase_onto_main is false, the function SHALL skip the rebase block entirely and behave identically to the post-worktree-resume-fix v0.5.0 contract (idempotent existence check followed by git worktree add).

If the target branch does NOT exist in the local repository at the time create_worktree is invoked, the function SHALL skip the rebase block regardless of the rebase_onto_main value and proceed to the existing git worktree add -b <branch> fallback, which creates the branch from current HEAD (already at the default branch tip by construction).

The system SHALL be idempotent in the resume case: when create_worktree() is invoked for a branch whose worktree already exists at the expected path AND is registered with git for that branch, the function SHALL return Ok(WorktreeCreation { path, branch_created: false }) without re-running git worktree add. Idempotency is verified by parsing git worktree list --porcelain output and matching both the worktree path and the refs/heads/<branch> line. When rebase_onto_main is true, the rebase block runs BEFORE this idempotency check, so a surviving worktree’s branch ref SHALL be updated to the rebased SHA before the function returns.

If the expected path exists on disk but is NOT a git worktree registered for the specified branch (e.g. an unrelated directory, or a worktree for a different branch), the function SHALL fall through to the existing git worktree add call so the user sees the actionable fatal: '<path>' already exists error from git directly.

Scenario: Worktree created at correct path

  • GIVEN a repository with a branch feature/test
  • WHEN create_worktree() is called with rebase_onto_main = false
  • THEN a worktree SHALL be created at ../<project>-feature-test containing the repository files

Scenario: Creating worktree for currently checked-out branch fails

  • GIVEN the current branch is checked out in the main repo
  • WHEN create_worktree() is called for that branch
  • THEN it SHALL return Err(PawError::WorktreeError)

Scenario: Resume of an existing worktree returns success without re-running git worktree add

  • GIVEN a worktree already exists at ../<project>-feature-test for branch feature/test from a prior session
  • AND rebase_onto_main = false is passed
  • WHEN create_worktree() is called for feature/test
  • THEN the function SHALL return Ok(WorktreeCreation { path: <expected>, branch_created: false })
  • AND the existing worktree SHALL remain unchanged (HEAD SHA, working tree files, and uncommitted changes preserved)
  • AND no second git worktree add SHALL be executed

Scenario: Path exists but is not a git worktree

  • GIVEN the expected worktree path ../<project>-feature-test exists as a regular directory (not registered with git)
  • WHEN create_worktree() is called for branch feature/test
  • THEN the function SHALL return Err(PawError::WorktreeError) whose message contains the substring already exists

Scenario: Path exists as a worktree but for a different branch

  • GIVEN a worktree already exists at ../<project>-feature-test but registered for branch feature/other
  • WHEN create_worktree() is called for branch feature/test
  • THEN the function SHALL fall through to git worktree add and return Err(PawError::WorktreeError) (preserving the v0.4 contract for unrelated path collisions)

Scenario: Rebase-on-resume happy path advances branch onto current main

  • GIVEN the default branch main has advanced by N commits since branch feat/example was created
  • AND feat/example exists locally and is behind main by exactly N commits with no diverging commits of its own
  • WHEN create_worktree() is called for feat/example with rebase_onto_main = true
  • THEN the function SHALL invoke git rebase <main> against feat/example in the repository root
  • AND the rebase SHALL succeed
  • AND feat/example’s HEAD SHA after the call SHALL be reachable from main (i.e. include the N new commits)
  • AND the function SHALL return Ok(WorktreeCreation { path: <expected>, branch_created: false })

Scenario: Rebase skipped when branch is already up-to-date

  • GIVEN branch feat/example exists locally and is at the same SHA as main (no divergence)
  • WHEN create_worktree() is called for feat/example with rebase_onto_main = true
  • THEN git rebase <main> SHALL be invoked and SHALL exit zero with no rewrite
  • AND feat/example’s HEAD SHA SHALL be unchanged
  • AND the function SHALL return Ok(WorktreeCreation { path: <expected>, branch_created: false })
  • AND no error SHALL be returned

Scenario: Rebase conflict aborts cleanly and surfaces error

  • GIVEN branch feat/example and main both modify the same line of the same file with different content
  • WHEN create_worktree() is called for feat/example with rebase_onto_main = true
  • THEN git rebase <main> SHALL be invoked and SHALL exit non-zero with conflict markers
  • AND the function SHALL invoke git rebase --abort
  • AND feat/example’s HEAD SHA after the call SHALL equal its pre-call HEAD SHA
  • AND no .git/rebase-merge or .git/rebase-apply directory SHALL remain in the repository
  • AND the function SHALL return Err(PawError::WorktreeError(msg)) where msg contains the substring rebase onto main failed
  • AND the worktree directory at ../<project>-feat-example SHALL NOT have been created (or, if it existed from a prior session, SHALL be unchanged)

Scenario: rebase_onto_main = false preserves v0.5 no-rebase behaviour

  • GIVEN branch feat/example exists locally and is behind main by 3 commits
  • WHEN create_worktree() is called for feat/example with rebase_onto_main = false
  • THEN no git rebase invocation SHALL occur
  • AND feat/example’s HEAD SHA after the call SHALL equal its pre-call HEAD SHA
  • AND the function SHALL proceed to the existence check and (if applicable) git worktree add, matching the post-worktree-resume-fix v0.5.0 contract exactly

Scenario: New branch creation skips rebase regardless of flag

  • GIVEN branch feat/new does NOT exist in the local repository
  • AND rebase_onto_main = true is passed
  • WHEN create_worktree() is called for feat/new
  • THEN no git rebase invocation SHALL occur (there is nothing to rebase)
  • AND the function SHALL invoke git worktree add -b feat/new <path> to create the branch from current HEAD
  • AND the function SHALL return Ok(WorktreeCreation { path, branch_created: true })

Requirement: Remove worktrees and prune stale entries

The system SHALL force-remove a worktree and prune stale git worktree metadata. remove_worktree SHALL pass --force to git worktree remove so a worktree containing uncommitted modifications, untracked files, or both is still deleted; the function is only called from the destructive purge path, where leaving worktree directories on disk after the user already opted into a destructive operation is the wrong behaviour.

Scenario: Worktree fully cleaned up after removal

  • GIVEN an existing worktree
  • WHEN remove_worktree() is called
  • THEN the directory SHALL be deleted and git SHALL no longer track it

Test: git::tests::remove_worktree_cleans_up_fully

Scenario: Dirty worktree is force-removed

  • GIVEN an existing worktree containing both a modified tracked file and an untracked file
  • WHEN remove_worktree() is called
  • THEN the call SHALL succeed
  • AND the worktree directory SHALL be deleted from disk
  • AND git SHALL no longer track the worktree

Test: git_integration::remove_worktree_force_removes_dirty_worktree

Requirement: Repository validation SHALL work against real git repos

Scenario: Succeeds inside a real git repo

  • GIVEN a temporary git repository with an initial commit
  • WHEN validate_repo() is called
  • THEN it SHALL return the canonicalized repo root

Test: git_integration::validate_repo_succeeds_inside_git_repo

Scenario: Fails outside a git repo

  • GIVEN a temporary directory that is not a git repo
  • WHEN validate_repo() is called
  • THEN it SHALL return an error

Test: git_integration::validate_repo_fails_outside_git_repo

Requirement: Branch listing SHALL work against real git repos

Scenario: Lists created branches

  • GIVEN a repo with branches feature/auth and fix/db
  • WHEN list_branches() is called
  • THEN both branches SHALL appear in the result

Test: git_integration::list_branches_includes_created_branches

Scenario: Branches are sorted

  • GIVEN branches created in non-alphabetical order
  • WHEN list_branches() is called
  • THEN results SHALL be alphabetically sorted

Test: git_integration::list_branches_returns_sorted

Scenario: Deduplicates local and remote

  • GIVEN a repository with a default branch
  • WHEN list_branches() is called
  • THEN each branch SHALL appear exactly once

Test: git_integration::list_branches_deduplicates_local_and_remote

Requirement: Worktree lifecycle SHALL work against real git repos

Scenario: Create and remove worktree

  • GIVEN a branch in a temporary repo
  • WHEN create_worktree() then remove_worktree() are called
  • THEN the worktree SHALL exist after creation and be gone after removal

Test: git_integration::create_and_remove_worktree

Scenario: Worktree placed as sibling of repo

  • GIVEN a repo at <sandbox>/test-repo/
  • WHEN create_worktree() is called
  • THEN the worktree SHALL be in the same parent directory

Test: git_integration::worktree_placed_as_sibling_of_repo

Scenario: Fails for checked-out branch

  • GIVEN the currently checked-out branch
  • WHEN create_worktree() is called for it
  • THEN it SHALL fail

Test: git_integration::create_worktree_fails_for_checked_out_branch

Requirement: Directory naming SHALL be correct in integration tests

Scenario: Project name from real repo path

  • GIVEN a repo at .../test-repo/
  • WHEN project_name() is called
  • THEN it SHALL return "test-repo"

Test: git_integration::project_name_from_repo_path

Scenario: Worktree dir name replaces slashes

  • WHEN worktree_dir_name("my-project", "feature/auth-flow") is called
  • THEN it SHALL return "my-project-feature-auth-flow"

Test: git_integration::worktree_dir_name_replaces_slashes

Scenario: Worktree dir name strips unsafe chars

  • WHEN worktree_dir_name("proj", "feat/special@chars!") is called
  • THEN @ and ! SHALL be stripped

Test: git_integration::worktree_dir_name_strips_unsafe_chars

Scenario: Worktree dir name handles nested slashes

  • WHEN worktree_dir_name("proj", "feature/deep/nested/branch") is called
  • THEN it SHALL return "proj-feature-deep-nested-branch"

Test: git_integration::worktree_dir_name_handles_nested_slashes

Requirement: Worktree creation produces a usable worktree path

The create_worktree function SHALL create a git worktree for the given branch and return its path. Callers MAY perform post-creation setup (such as AGENTS.md generation) using the returned path.

Scenario: Worktree created at correct path

  • GIVEN a git repo and a branch name
  • WHEN create_worktree() is called
  • THEN it SHALL return the path to the new worktree as a sibling of the repo directory

Test: git::tests::create_worktree_at_correct_path

Scenario: Worktree creation fails for checked-out branch

  • GIVEN a branch that is currently checked out
  • WHEN create_worktree() is called
  • THEN it SHALL return a PawError::WorktreeError

Test: git::tests::create_worktree_errors_on_checked_out_branch


Tmux Orchestration

Purpose

Orchestrate tmux sessions with multiple panes, each running an AI CLI in a git worktree. Uses a builder pattern for testability and dry-run support, with configurable mouse mode and automatic tiled layout.

Requirements

Requirement: Check tmux availability with actionable error

The system SHALL verify tmux is installed on PATH and provide install instructions if missing.

Scenario: tmux is present on PATH

  • GIVEN tmux is installed
  • WHEN ensure_tmux_installed() is called
  • THEN it SHALL return Ok(())

Test: tmux::tests::ensure_tmux_installed_succeeds_when_present

Requirement: Create named sessions derived from project name

The system SHALL name tmux sessions as paw-<project_name>.

Scenario: Session named after project

  • GIVEN project name "my-project"
  • WHEN a session is built
  • THEN the session name SHALL be "paw-my-project"

Test: tmux::tests::session_is_named_after_project

Scenario: Session creation command uses correct name

  • GIVEN project name "app"
  • WHEN a session is built
  • THEN the commands SHALL include new-session with paw-app

Test: tmux::tests::session_creation_command_uses_session_name

Requirement: Session creation passes explicit dimensions for headless environments

Both session builders (the basic TmuxSessionBuilder and build_supervisor_session) SHALL emit tmux new-session with -x 200 -y 50 so the session has explicit window dimensions when created without an attached client. The user’s real terminal resizes the session on tmux attach.

Without explicit dimensions, tmux on Linux (apt-shipped tmux 3.4+) errors with size missing on subsequent split-window operations because the layout engine can’t resolve percentages without a known window size.

Additionally, immediately after the new-session command, both builders SHALL emit tmux set-option -g default-size 200x50. This pins the global default-size so subsequent split-window / resize-pane operations have a fallback size context even when no client is attached. macOS tmux honours per-session -x/-y for splits; Linux tmux 3.4+ requires the server-level fallback.

Scenario: Basic session passes -x/-y to new-session

  • GIVEN any TmuxSessionBuilder with one or more panes
  • WHEN command_strings() is invoked
  • THEN the first command in the output SHALL be a new-session containing the substrings -x 200 and -y 50

Scenario: Basic session sets global default-size after new-session

  • GIVEN any TmuxSessionBuilder with one or more panes
  • WHEN command_strings() is invoked
  • THEN the second command in the output SHALL be set-option -g default-size 200x50

Scenario: Supervisor session passes -x/-y to new-session

  • GIVEN a build_supervisor_session invocation with supervisor + dashboard + N agent panes
  • WHEN the command list is built
  • THEN the first emitted command SHALL be a new-session containing -x 200 and -y 50

Scenario: Supervisor session sets global default-size after new-session

  • GIVEN a build_supervisor_session invocation
  • WHEN the command list is built
  • THEN the second emitted command SHALL be set-option -g default-size 200x50

Scenario: Headless supervisor launch succeeds under socket isolation

  • GIVEN a cold tmux server (no pre-existing client, isolated socket via TMUX_TMPDIR)
  • WHEN git paw start --supervisor --branches a,b is invoked
  • THEN the supervisor session SHALL launch successfully
  • AND stderr SHALL NOT contain Tmux error: size missing
  • AND the exit code SHALL be 0

Test: tmux::tests::built_session_can_be_executed_and_killed, tmux::tests::supervisor_top_row_split_50_50, and the cli_supervisor_no_config::supervisor_without_section_uses_default_when_default_cli_present integration test.

Scenario: Supervisor splits use -l <N>% syntax (tmux 3.1+) not deprecated -p <N>

  • GIVEN a build_supervisor_session invocation with any agent count
  • WHEN the command list is built
  • THEN every emitted split-window command SHALL use the -l <N>% length flag, NOT the deprecated -p <N> percentage flag

Rationale: Linux apt-tmux 3.4 (Ubuntu 24.04) emits cmd-split-window.c: "size missing" when -p cannot resolve the percentage against the parent pane’s laid-out size — on a detached server with no attached client the pane geometry is unresolved. -l <N>% resolves against the window’s -y dimension (set by new-session -x 200 -y 50) instead, which is well-defined in headless mode. macOS tmux 3.6a tolerates either form.

Test: tmux::tests::supervisor_splits_use_l_percent_not_p.

Requirement: Session name override via builder

The builder SHALL support overriding the default paw-<project> session name with a custom name.

Scenario: Override replaces default name

  • GIVEN session_name("custom-session-name") is set on the builder
  • WHEN the session is built
  • THEN the session name SHALL be "custom-session-name" and commands SHALL target it

Test: tmux::tests::session_name_override_replaces_default

Requirement: Dynamic pane count matches input

The number of panes in the session SHALL match the number of PaneSpec entries added via the builder. When broker is enabled, the builder SHALL receive an additional PaneSpec for the dashboard in pane 0, increasing the total pane count by one.

Scenario: Two agent panes plus dashboard created

  • GIVEN broker is enabled and 2 agent pane specs are added
  • WHEN the session is built
  • THEN exactly 3 panes SHALL exist: pane 0 (dashboard) + panes 1-2 (agents)

Scenario: Two panes without broker

  • GIVEN broker is disabled and 2 pane specs are added
  • WHEN the session is built
  • THEN exactly 2 panes SHALL exist (same as v0.2.0)

Requirement: Correct commands sent to each pane

Each pane SHALL receive a cd <worktree> && <cli_command> command targeting the correct pane index.

Scenario: Each pane receives cd and CLI command

  • GIVEN two panes with different worktrees and CLIs
  • WHEN the session is built
  • THEN each send-keys command SHALL contain cd <worktree> && <cli>

Test: tmux::tests::each_pane_receives_cd_and_cli_command

Scenario: Commands are submitted with Enter

  • GIVEN a pane spec
  • WHEN the session is built
  • THEN the send-keys command SHALL include Enter

Test: tmux::tests::pane_commands_are_submitted_with_enter

Scenario: Each pane targets a distinct index

  • GIVEN 3 panes
  • WHEN the session is built
  • THEN send-keys SHALL target :0.0, :0.1, and :0.2 respectively

Test: tmux::tests::each_pane_targets_a_distinct_pane_index

Requirement: Pane titles show branch and CLI

Each pane SHALL be titled with <branch> → <cli_command> and border status configured.

Scenario: Pane titles contain branch and CLI

  • GIVEN panes with branches and CLIs
  • WHEN the session is built
  • THEN select-pane -T commands SHALL set titles like "feat/auth → claude"

Test: tmux::tests::each_pane_is_titled_with_branch_and_cli

Scenario: Pane border status configured

  • GIVEN any session
  • WHEN the session is built
  • THEN pane-border-status SHALL be set to top and pane-border-format SHALL use #{pane_title}

Test: tmux::tests::pane_border_status_is_configured

Requirement: Configurable mouse mode per session

Mouse mode SHALL be enabled by default and be disableable via the builder.

Scenario: Mouse mode enabled by default

  • GIVEN no explicit mouse mode setting
  • WHEN the session is built
  • THEN a mouse on command SHALL be emitted

Test: tmux::tests::mouse_mode_enabled_by_default

Scenario: Mouse mode can be disabled

  • GIVEN mouse_mode(false) is set on the builder
  • WHEN the session is built
  • THEN no mouse on command SHALL be emitted

Test: tmux::tests::mouse_mode_can_be_disabled

Requirement: Attach to a tmux session

The system SHALL attach the current terminal to a named tmux session, returning an error if the session does not exist.

Scenario: Attaching to a nonexistent session fails

  • GIVEN no tmux session with the given name exists
  • WHEN attach() is called
  • THEN it SHALL return an error

Test: e2e_tests::attach_fails_for_nonexistent_session

Requirement: Session liveness check

The system SHALL check whether a tmux session is alive by name.

Scenario: Nonexistent session reports not alive

  • GIVEN no tmux session with the queried name exists
  • WHEN is_session_alive() is called
  • THEN it SHALL return false

Test: tmux::tests::is_session_alive_returns_false_for_nonexistent

Requirement: Session lifecycle management

The system SHALL support creating, checking, and killing tmux sessions.

Scenario: Full create-check-kill lifecycle

  • GIVEN a tmux session is created
  • WHEN is_session_alive() is called, then kill_session(), then is_session_alive() again
  • THEN it SHALL be alive after creation and not alive after killing

Test: tmux::tests::session_lifecycle_create_check_kill

Scenario: Built session can be executed and killed

  • GIVEN a session built via TmuxSessionBuilder
  • WHEN execute() is called
  • THEN the tmux session SHALL be alive, and after kill_session() it SHALL be gone

Test: tmux::tests::built_session_can_be_executed_and_killed

Requirement: Session name collision resolution

The system SHALL resolve name collisions by appending -2, -3, etc. to the base session name.

Scenario: No collision returns base name

  • GIVEN no existing session with the base name
  • WHEN resolve_session_name() is called
  • THEN it SHALL return paw-<project_name>

Test: tmux::tests::resolve_session_name_returns_base_when_no_collision

Scenario: Collision appends numeric suffix

  • GIVEN a session with the base name already exists
  • WHEN resolve_session_name() is called
  • THEN it SHALL return paw-<project_name>-2

Test: tmux::tests::resolve_session_name_appends_suffix_on_collision

Requirement: Tmux session lifecycle SHALL work against a real tmux server

Scenario: Create and kill session lifecycle

  • GIVEN a tmux session is created via the builder
  • WHEN execute(), is_session_alive(), and kill_session() are called
  • THEN the session SHALL be alive after creation and gone after killing

Test: e2e_tests::tmux_session_create_and_kill_lifecycle

Scenario: Five panes with different CLIs

  • GIVEN 5 pane specs with different branch/CLI pairs
  • WHEN the session is executed
  • THEN tmux SHALL have 5 panes with correct titles

Test: e2e_tests::tmux_session_with_five_panes_and_different_clis

Scenario: Mouse mode enabled by default against live tmux

  • GIVEN a session built with default settings
  • WHEN tmux show-option is queried
  • THEN mouse SHALL be “on”

Test: e2e_tests::tmux_mouse_mode_enabled_by_default

Scenario: is_session_alive returns false for nonexistent (e2e)

  • GIVEN no session with the queried name
  • WHEN is_session_alive() is called
  • THEN it SHALL return false

Test: e2e_tests::tmux_is_session_alive_returns_false_for_nonexistent

Scenario: Attach succeeds for live session

  • GIVEN a live tmux session
  • WHEN attach() is called and the client is detached programmatically
  • THEN the function SHALL execute without panic

Test: e2e_tests::attach_succeeds_for_live_session

Requirement: E2E commands SHALL behave correctly against real repos

Scenario: Dry run shows session plan

  • GIVEN a git repo with branches and --dry-run --cli echo --branches feat/a,feat/b
  • WHEN the binary runs
  • THEN stdout SHALL contain “Dry run”, branch names, and the CLI name

Test: e2e_tests::dry_run_with_flags_shows_plan

Scenario: Preset not found returns error

  • GIVEN a git repo with no presets configured
  • WHEN start --preset nonexistent is run
  • THEN it SHALL fail with stderr mentioning “not found”

Test: e2e_tests::preset_not_found_returns_error

Scenario: Stop with no session

  • GIVEN a git repo with no active session
  • WHEN stop is run
  • THEN it SHALL succeed with stdout mentioning “No active session”

Test: e2e_tests::stop_with_no_session

Scenario: Purge with no session

  • GIVEN a git repo with no active session
  • WHEN purge --force is run
  • THEN it SHALL succeed with stdout mentioning “No session to purge”

Test: e2e_tests::purge_with_no_session

Scenario: Status with no session

  • GIVEN a git repo with no active session
  • WHEN status is run
  • THEN it SHALL succeed with stdout mentioning “No session”

Test: e2e_tests::status_with_no_session

Scenario: Stop from non-git directory fails

  • GIVEN a directory that is not a git repository
  • WHEN stop is run
  • THEN it SHALL fail with “Not a git repository”

Test: e2e_tests::stop_from_non_git_dir_fails

Scenario: Status from non-git directory fails

  • GIVEN a directory that is not a git repository
  • WHEN status is run
  • THEN it SHALL fail with “Not a git repository”

Test: e2e_tests::status_from_non_git_dir_fails

Requirement: TmuxSession supports pipe-pane command

The TmuxSession builder SHALL support queuing a pipe-pane command to attach logging to a specific pane.

Scenario: pipe-pane queued in builder

  • WHEN pipe_pane() is called on a TmuxSession with a pane target and log path
  • THEN the command queue SHALL contain a pipe-pane -o -t <pane> "cat >> <path>" entry

Scenario: pipe-pane in dry-run output

  • WHEN a session with pipe_pane() is rendered as dry-run
  • THEN the output SHALL include the tmux pipe-pane command string

Scenario: pipe-pane executed after pane creation

  • WHEN the session commands are executed
  • THEN the pipe-pane command SHALL execute after the corresponding split-window and send-keys commands for that pane

Requirement: TmuxSession supports session-level environment variables

The TmuxSessionBuilder SHALL support setting session-level environment variables via a set_environment(key, value) method. The resulting set-environment -t <session> <key> <value> command SHALL be emitted before any send-keys commands to ensure all panes inherit the variable.

Scenario: set_environment emits correct tmux command

  • GIVEN set_environment("GIT_PAW_BROKER_URL", "http://127.0.0.1:9119") is called on the builder
  • WHEN the session is built
  • THEN the command queue SHALL contain set-environment -t <session> GIT_PAW_BROKER_URL http://127.0.0.1:9119

Scenario: set_environment appears before send-keys

  • GIVEN a builder with environment variables and pane specs
  • WHEN the session is built
  • THEN all set-environment commands SHALL appear before any send-keys commands in the command queue

Scenario: set_environment in dry-run output

  • GIVEN a builder with set_environment called
  • WHEN the session is rendered as dry-run
  • THEN the output SHALL include the tmux set-environment command string

Scenario: Multiple environment variables

  • GIVEN set_environment("A", "1") and set_environment("B", "2") are both called
  • WHEN the session is built
  • THEN both set-environment commands SHALL appear in the command queue

Requirement: Supervisor-mode pane layout

When the tmux session is built for supervisor mode (per the supervisor-launch capability), the system SHALL produce a layout with these structural properties:

  • Top row: split horizontally 50/50 between pane 0 (supervisor agent) and pane 1 (dashboard).

  • Agent grid below: dynamically sized by agent count, with up to 5 columns per row in v0.5.0. The agent grid is a sequence of horizontal rows; each row holds up to 5 agent panes side-by-side.

  • Pane indices: pane 0 = supervisor; pane 1 = dashboard; panes 2..N+1 = coding agents in row-major order (left-to-right, top-to-bottom).

  • Vertical proportions by total-row count (top row + agent rows):

    Total rowsTop row heightEach agent row height
    2 (1-5 agents)60%40%
    3 (6-10 agents)40%30% each
    4 (11-15 agents)28%24% each
    5 (16-20 agents)28%18% each
    6 (21-25 agents)28%14.4% each
  • Hard cap: 25 agents per session. Above 25, the system SHALL reject the launch with a clear “split into multiple sessions” error before any tmux command runs.

The layout SHALL be built using tmux split-window -h and -v with explicit percentages, then enforced via tmux resize-pane -y <pct> for the height proportions. select-layout tiled (or other auto-layouts) SHALL NOT be used for the supervisor-mode layout because they don’t preserve the predictable pane-index ordering this layout relies on.

Scenario: 5-agent supervisor layout has 1 agent row

  • GIVEN a supervisor session with 5 agent branches
  • WHEN the tmux layout is built
  • THEN pane 0 SHALL be the supervisor at 50% of the top row’s width
  • AND pane 1 SHALL be the dashboard at 50% of the top row’s width
  • AND panes 2-6 SHALL be agents arranged in a single row below the top row
  • AND the top row’s height SHALL be 60% and the agent row’s height SHALL be 40%

Scenario: 10-agent supervisor layout has 2 agent rows

  • GIVEN a supervisor session with 10 agent branches
  • WHEN the tmux layout is built
  • THEN total row count SHALL be 3 (1 top + 2 agent rows)
  • AND the top row’s height SHALL be 40%
  • AND each agent row’s height SHALL be 30%
  • AND the first agent row SHALL contain panes 2-6, the second agent row SHALL contain panes 7-11

Scenario: 20-agent supervisor layout has 4 agent rows

  • GIVEN a supervisor session with 20 agent branches
  • WHEN the tmux layout is built
  • THEN total row count SHALL be 5 (1 top + 4 agent rows)
  • AND the top row’s height SHALL be 28%
  • AND each of the 4 agent rows’ height SHALL be 18%

Scenario: 26-agent supervisor session is rejected

  • GIVEN 26 agent branches resolved (via specs, –branches, or a combination)
  • WHEN the supervisor launch flow runs
  • THEN the launch SHALL be rejected with a PawError
  • AND the error message SHALL state the requested count (26), the maximum (25), and a hint suggesting --branches <subset> for splitting into multiple sessions
  • AND no tmux session SHALL be created

Scenario: Pane indices match row-major order

  • GIVEN a supervisor session with 7 agents
  • WHEN the tmux layout is built
  • THEN pane 2 SHALL be the first agent (top-left of the agent grid)
  • AND pane 6 SHALL be the fifth agent (top-right of the first agent row, since agents_per_row = 5)
  • AND pane 7 SHALL be the sixth agent (start of the second agent row)

Session State

Purpose

Persist session state to disk for recovery after crashes, reboots, or manual stops. Stores one JSON file per session under the XDG data directory, with atomic writes and tmux liveness checks.

Requirements

Requirement: Save session state atomically

The system SHALL serialize session data to JSON and write it atomically using a temp file and rename to prevent corruption.

The session data SHALL include optional broker fields: broker_port (Option<u16>), broker_bind (Option<String>), and broker_log_path (Option<PathBuf>). These fields SHALL be omitted from the JSON when None and SHALL default to None when absent during deserialization.

Scenario: Saved session round-trips with all fields intact

  • GIVEN an active session with 3 worktrees
  • WHEN save_session() is called and the session is loaded back
  • THEN all fields (session_name, repo_path, project_name, created_at, status, worktrees) SHALL match the original

Scenario: Saved session with broker fields round-trips

  • GIVEN an active session with broker_port = Some(9119), broker_bind = Some("127.0.0.1"), broker_log_path = Some("/path/to/broker.log")
  • WHEN save_session() is called and the session is loaded back
  • THEN all broker fields SHALL match the original

Scenario: Session without broker fields loads successfully

  • GIVEN a session JSON file saved by v0.2.0 (no broker fields)
  • WHEN the session is loaded
  • THEN broker_port, broker_bind, and broker_log_path SHALL all be None
  • AND all existing fields SHALL load correctly

Scenario: Saving again replaces previous state

  • GIVEN a previously saved session
  • WHEN save_session() is called with updated fields
  • THEN the new state SHALL overwrite the old state

Requirement: Load session by name

The system SHALL load a session from disk by name, returning None if the file does not exist.

Scenario: Loading a nonexistent session returns None

  • GIVEN no session file exists with the given name
  • WHEN load_session() is called
  • THEN it SHALL return Ok(None)

Test: session::tests::loading_nonexistent_session_returns_none

Requirement: Find session by repository path

The system SHALL scan all session files and return the session matching a given repository path.

Scenario: Finds correct session among multiple

  • GIVEN two sessions for different repositories
  • WHEN find_session_for_repo() is called with one repo path
  • THEN it SHALL return the matching session

Test: session::tests::finds_correct_session_among_multiple_by_repo_path

Scenario: No matching session

  • GIVEN saved sessions for other repositories
  • WHEN find_session_for_repo() is called with a different path
  • THEN it SHALL return None

Test: session::tests::find_returns_none_when_no_repo_matches

Scenario: No sessions directory

  • GIVEN no sessions directory exists
  • WHEN find_session_for_repo() is called
  • THEN it SHALL return None

Test: session::tests::find_returns_none_when_no_sessions_exist

Requirement: Delete session by name

The system SHALL delete a session file, succeeding even if the file does not exist (idempotent).

Scenario: Deleted session is no longer loadable

  • GIVEN a saved session
  • WHEN delete_session() is called
  • THEN load_session() SHALL return None

Test: session::tests::deleted_session_is_no_longer_loadable

Scenario: Deleting nonexistent session succeeds

  • GIVEN no session file with the given name
  • WHEN delete_session() is called
  • THEN it SHALL return Ok(())

Test: session::tests::deleting_nonexistent_session_succeeds

Requirement: Effective status combines file state with tmux liveness

Session::effective_status(is_tmux_alive) SHALL combine the persisted status field with the result of is_tmux_alive to produce the runtime-effective status:

Recorded statustmux alive?Effective status
ActiveyesActive
ActivenoStopped
PausedyesPaused
PausednoStopped
StoppedanyStopped

The rule for Paused: tmux must still be alive for the Paused state to be valid — pause’s whole purpose is to keep tmux + CLI panes running while the client is detached. If tmux died despite a recorded Paused state (e.g. tmux server crash), effective_status SHALL downgrade to Stopped, and cmd_start SHALL run the cold-recovery path (fresh CLI spawn) rather than the restart-from-pause path.

Scenario: Active + alive remains Active

  • GIVEN a session with status = Active
  • WHEN effective_status(|_| true) is called
  • THEN it SHALL return Active

Scenario: Active + dead downgrades to Stopped

  • GIVEN a session with status = Active
  • WHEN effective_status(|_| false) is called
  • THEN it SHALL return Stopped

Scenario: Paused + alive remains Paused

  • GIVEN a session with status = Paused
  • WHEN effective_status(|_| true) is called
  • THEN it SHALL return Paused

Scenario: Paused + dead downgrades to Stopped

  • GIVEN a session with status = Paused
  • WHEN effective_status(|_| false) is called
  • THEN it SHALL return Stopped

Scenario: Stopped remains Stopped regardless of tmux liveness

  • GIVEN a session with status = Stopped
  • WHEN effective_status is called with either liveness result
  • THEN it SHALL return Stopped

Requirement: SessionStatus display format

The SessionStatus enum SHALL display as lowercase strings.

Scenario: SessionStatus display strings

  • GIVEN SessionStatus::Active and SessionStatus::Stopped
  • WHEN formatted with Display
  • THEN they SHALL render as "active" and "stopped"

Test: session::tests::session_status_displays_as_lowercase_string

Requirement: Recovery data survives tmux crashes

After a tmux crash, the persisted session SHALL contain all data needed to reconstruct the session.

Scenario: Crashed session has all recovery data including broker fields

  • GIVEN a saved session with worktrees and broker enabled
  • WHEN tmux crashes and the session is loaded from disk
  • THEN it SHALL have the session name, repo path, all worktree details, AND broker_port, broker_bind, broker_log_path

Scenario: Session recovery recreates dashboard pane when broker was enabled

  • GIVEN a saved session with broker_port = Some(9119) and broker_bind = Some("127.0.0.1")
  • WHEN recover_session() is called
  • THEN the rebuilt tmux session SHALL have:
    • Dashboard pane in pane 0 running git-paw __dashboard
    • GIT_PAW_BROKER_URL environment variable set to http://127.0.0.1:9119
    • All original worktree panes in subsequent indices

Scenario: Session recovery uses original broker config, not current config

  • GIVEN a saved session with broker_port = Some(9119)
  • AND current repo config has broker.enabled = false
  • WHEN recover_session() is called
  • THEN the dashboard pane SHALL still be created with the original broker URL

Scenario: Session recovery without original broker creates no dashboard

  • GIVEN a saved session with broker_port = None
  • WHEN recover_session() is called
  • THEN no dashboard pane SHALL be created

Requirement: Session persistence SHALL work through the public API

Scenario: Save and load round-trip

  • GIVEN a session with 2 worktrees
  • WHEN save_session_in() and load_session_from() are called
  • THEN all fields SHALL match

Test: session_integration::save_and_load_round_trip

Scenario: Find session by repo path

  • GIVEN a saved session
  • WHEN find_session_for_repo_in() is called with the matching repo path
  • THEN the correct session SHALL be returned

Test: session_integration::find_session_by_repo_path

Scenario: Find returns None for unknown repo

  • GIVEN no matching session
  • WHEN find_session_for_repo_in() is called
  • THEN it SHALL return None

Test: session_integration::find_session_returns_none_for_unknown_repo

Scenario: Find correct session among multiple

  • GIVEN two sessions for different repos
  • WHEN find_session_for_repo_in() is called for one
  • THEN the correct session SHALL be returned

Test: session_integration::find_correct_session_among_multiple

Scenario: Delete removes session

  • GIVEN a saved session
  • WHEN delete_session_in() is called
  • THEN load_session_from() SHALL return None

Test: session_integration::delete_removes_session

Scenario: Delete nonexistent is idempotent

  • GIVEN no session file
  • WHEN delete_session_in() is called
  • THEN it SHALL succeed

Test: session_integration::delete_nonexistent_is_idempotent

Scenario: Load nonexistent returns None

  • GIVEN no session file
  • WHEN load_session_from() is called
  • THEN it SHALL return None

Test: session_integration::load_nonexistent_returns_none

Scenario: Saving again replaces previous state

  • GIVEN a saved session
  • WHEN the status is changed and saved again
  • THEN the loaded session SHALL have the new status

Test: session_integration::saving_again_replaces_previous_state

Scenario: Effective status active when tmux alive

  • GIVEN a session with Active status and tmux alive
  • WHEN effective_status() is called
  • THEN it SHALL return Active

Test: session_integration::effective_status_active_when_tmux_alive

Scenario: Effective status stopped when tmux dead

  • GIVEN a session with Active status and tmux dead
  • WHEN effective_status() is called
  • THEN it SHALL return Stopped

Test: session_integration::effective_status_stopped_when_tmux_dead

Scenario: Effective status stopped stays stopped

  • GIVEN a session with Stopped status
  • WHEN effective_status() is called
  • THEN it SHALL return Stopped regardless of tmux

Test: session_integration::effective_status_stopped_stays_stopped

Scenario: Saved session has all recovery fields

  • GIVEN a saved and reloaded session
  • WHEN recovery fields are checked
  • THEN session_name, repo_path, project_name, and all worktree entries SHALL be non-empty

Test: session_integration::saved_session_has_all_recovery_fields

Requirement: Paused session status variant

The SessionStatus enum SHALL include a third variant Paused (alongside Active and Stopped). The serde representation SHALL serialize as the lowercase string "paused" and SHALL deserialize from the same string. The Display implementation SHALL render Paused as "paused".

The Paused state means: the tmux session is intended to remain alive, all coding-agent CLI panes are intended to remain running, the user’s tmux client is detached, and the broker is stopped. Session state files saved by v0.4.0 binaries (which only know Active and Stopped) SHALL continue to load successfully under v0.5+ binaries — the new variant only appears in files saved by v0.5+.

Scenario: Paused status serializes lowercase

  • GIVEN a Session with status = SessionStatus::Paused
  • WHEN save_session() is called and the JSON file is inspected
  • THEN the "status" field SHALL be "paused"

Scenario: Paused status round-trips

  • GIVEN a Session with status = SessionStatus::Paused saved to disk
  • WHEN the session is loaded back via load_session()
  • THEN status SHALL be SessionStatus::Paused

Scenario: v0.4-saved sessions load under v0.5

  • GIVEN a session JSON file saved by v0.4.0 (only "active" or "stopped" in the status field)
  • WHEN the file is loaded by a v0.5+ binary
  • THEN the load SHALL succeed
  • AND the status field SHALL match the original (Active or Stopped)

Scenario: Paused Display renders lowercase

  • WHEN format!("{}", SessionStatus::Paused) is evaluated
  • THEN the result SHALL be "paused"

Requirement: Dashboard pane index persisted in session state

The Session struct SHALL include an optional field dashboard_pane: Option<u32> that records the pane index of the dashboard pane within the tmux session. The field SHALL use #[serde(default, skip_serializing_if = "Option::is_none")] so v0.4-saved sessions load with None. The field SHALL be populated by the start flow when broker is enabled (typically 0 for bare-start mode and 1 for supervisor mode).

The restart-from-pause flow (specced in the broker-lifecycle delta) SHALL read this field to determine where to re-spawn the dashboard pane. When the field is None (v0.4-saved session), the restart flow SHALL default to 0.

Scenario: Dashboard pane index round-trips

  • GIVEN a Session with dashboard_pane = Some(1) saved to disk
  • WHEN the session is loaded back
  • THEN dashboard_pane SHALL be Some(1)

Scenario: Session without dashboard_pane defaults to None on load

  • GIVEN a session JSON file with no dashboard_pane field
  • WHEN the session is loaded
  • THEN dashboard_pane SHALL be None

Scenario: Dashboard pane field is omitted when None

  • GIVEN a Session with dashboard_pane = None
  • WHEN save_session() is called and the JSON file is inspected
  • THEN the JSON SHALL NOT contain a dashboard_pane field

Configuration

Purpose

Parse TOML configuration from global (~/.config/git-paw/config.toml) and per-repo (.git-paw/config.toml) files. Supports custom CLI definitions, presets, and programmatic add/remove of custom CLIs with repo config overriding global config.

Requirements

Requirement: Parse TOML config with all fields

The system SHALL parse a TOML configuration file containing default_cli, mouse, clis, presets, and optional sections [specs], [logging], [broker], and [supervisor].

Scenario: Config with all fields populated

  • GIVEN a TOML file with default_cli, mouse, custom CLIs, presets, [broker], and [supervisor] sections
  • WHEN the file is loaded
  • THEN all fields SHALL be correctly parsed including supervisor fields

Scenario: All fields are optional

  • GIVEN a TOML file with only default_cli
  • WHEN the file is loaded
  • THEN missing fields SHALL default to None or empty collections
  • AND supervisor SHALL be None

Requirement: Merge repo config over global config

The system SHALL merge per-repo configuration on top of global configuration, with repo values taking precedence for scalar fields and map entries.

Scenario: Repo overrides global scalar fields

  • GIVEN global config has default_cli = "claude" and mouse = true, and repo has default_cli = "gemini"
  • WHEN configs are merged
  • THEN default_cli SHALL be "gemini" and mouse SHALL be true (preserved from global)

Test: config::tests::repo_config_overrides_global_scalars

Scenario: CLI maps are merged

  • GIVEN global config has CLI agent-a and repo config has CLI agent-b
  • WHEN configs are merged
  • THEN both CLIs SHALL be present

Test: config::tests::repo_config_merges_cli_maps

Scenario: Repo CLI overrides global CLI with same name

  • GIVEN both global and repo define a CLI named my-agent
  • WHEN configs are merged
  • THEN the repo definition SHALL win

Test: config::tests::repo_cli_overrides_global_cli_with_same_name

Scenario: Only global config exists

  • GIVEN a global config file but no repo config
  • WHEN load_config() is called
  • THEN global values SHALL be used

Test: config::tests::load_config_from_reads_global_file_when_no_repo

Scenario: Only repo config exists

  • GIVEN a repo config file but no global config
  • WHEN load_config() is called
  • THEN repo values SHALL be used

Test: config::tests::load_config_from_reads_repo_file_when_no_global

Requirement: Preset lookup by name

The system SHALL provide access to named presets that define branches and a CLI.

Scenario: Preset accessible by name

  • GIVEN a config with a preset named "backend"
  • WHEN get_preset("backend") is called
  • THEN it SHALL return the preset with its branches and CLI

Test: config::tests::preset_accessible_by_name

Scenario: Missing preset returns None

  • GIVEN a config without the requested preset
  • WHEN get_preset("nonexistent") is called
  • THEN it SHALL return None

Test: config::tests::preset_returns_none_when_not_in_config

Requirement: Add custom CLIs to global config

The system SHALL add custom CLI definitions to the global config, resolving non-absolute commands via PATH.

Scenario: Add CLI with absolute path

  • GIVEN an absolute path to a CLI binary
  • WHEN add_custom_cli() is called
  • THEN the CLI SHALL be written to the config file

Test: config::tests::add_cli_writes_to_config_file

Scenario: Adding preserves existing entries

  • GIVEN an existing CLI in the config
  • WHEN a second CLI is added
  • THEN both CLIs SHALL be present

Test: config::tests::add_cli_preserves_existing_entries

Scenario: Adding CLI with missing command fails

  • GIVEN a command that does not exist on PATH
  • WHEN add_custom_cli() is called
  • THEN it SHALL return an error mentioning “not found on PATH”

Test: config::tests::add_cli_errors_when_command_not_on_path

Requirement: Remove custom CLIs from global config

The system SHALL remove a custom CLI by name, returning an error if the CLI is not found.

Scenario: Remove existing CLI

  • GIVEN a config with CLIs keep-me and remove-me
  • WHEN remove_custom_cli("remove-me") is called
  • THEN only keep-me SHALL remain

Test: config::tests::remove_cli_deletes_entry_from_config_file

Scenario: Remove nonexistent CLI returns error

  • GIVEN a config without the named CLI
  • WHEN remove_custom_cli() is called
  • THEN it SHALL return PawError::CliNotFound

Test: config::tests::remove_nonexistent_cli_returns_cli_not_found_error

Scenario: Remove CLI from empty/missing config returns error

  • GIVEN no config file exists
  • WHEN remove_custom_cli() is called
  • THEN it SHALL return PawError::CliNotFound

Test: config::tests::remove_cli_from_empty_config_returns_error

Requirement: Config survives round-trip serialization

A PawConfig SHALL be identical after save and reload.

Scenario: Config round-trip

  • GIVEN a fully populated config
  • WHEN saved and loaded back
  • THEN it SHALL be equal to the original

Test: config::tests::config_survives_save_and_load

Requirement: Config loading SHALL work with real files

The system SHALL provide a load_config(repo_root, user_config_path) function that loads the merged PawConfig from the per-repo .git-paw/config.toml and a user-level (global) config.toml. The second parameter user_config_path: Option<&Path> SHALL control which file is read as the user-level config:

  • When user_config_path is None, the loader SHALL resolve the user-level config path via the platform-default helper (global_config_path()crate::dirs::config_dir().join("git-paw/config.toml")), preserving the v0.4 production behaviour.
  • When user_config_path is Some(p), the loader SHALL read p as the user-level config and SHALL NOT consult the platform-default helper. If p does not exist on disk, the user-level side of the merge SHALL be the default PawConfig, exactly as if no file existed at the platform-default path.

The merge semantics on top of the user-level config (per-repo config overrides user-level for scalar fields and map entries) are unchanged from prior requirements in this capability.

Scenario: Defaults when no files exist

  • GIVEN a temp directory with no config files
  • AND load_config is called with user_config_path = None
  • WHEN load_config() is called
  • THEN all fields SHALL be None/empty

Test: config_integration::load_config_returns_defaults_when_no_files_exist

Scenario: Reads repo .git-paw/config.toml

  • GIVEN a .git-paw/config.toml with default_cli and mouse
  • AND load_config is called with user_config_path = Some(&unused_temp_path)
  • WHEN load_config() is called
  • THEN the values SHALL be read correctly

Test: config_integration::load_config_reads_repo_config

Scenario: Repo config with custom CLIs

  • GIVEN a .git-paw/config.toml with two custom CLIs
  • AND load_config is called with user_config_path = Some(&unused_temp_path)
  • WHEN load_config() is called
  • THEN both CLIs SHALL be parsed with correct fields

Test: config_integration::repo_config_with_custom_clis

Scenario: Repo config with presets

  • GIVEN a .git-paw/config.toml with two presets
  • AND load_config is called with user_config_path = Some(&unused_temp_path)
  • WHEN load_config() is called
  • THEN presets SHALL be accessible with correct branches and CLI

Test: config_integration::repo_config_with_presets

Scenario: Default PawConfig has no presets

  • GIVEN a default PawConfig
  • WHEN get_preset("nonexistent") is called
  • THEN it SHALL return None

Test: config_integration::get_preset_returns_none_for_unknown

Scenario: Repo config overrides default fields

  • GIVEN a .git-paw/config.toml with specific values
  • AND load_config is called with user_config_path = Some(&unused_temp_path)
  • WHEN load_config() is called
  • THEN the repo values SHALL take precedence

Test: config_integration::repo_config_overrides_default_fields

Scenario: Repo config path is correct

  • GIVEN a temp directory
  • WHEN repo_config_path() is called
  • THEN it SHALL return <dir>/.git-paw/config.toml

Test: config_integration::repo_config_path_is_in_repo_root

Scenario: Malformed TOML returns error

  • GIVEN a .git-paw/config.toml with invalid TOML
  • AND load_config is called with user_config_path = Some(&unused_temp_path)
  • WHEN load_config() is called
  • THEN it SHALL return an error

Test: config_integration::malformed_toml_returns_error

Scenario: Empty config file is valid

  • GIVEN an empty .git-paw/config.toml
  • AND load_config is called with user_config_path = Some(&unused_temp_path)
  • WHEN load_config() is called
  • THEN it SHALL return a default config

Test: config_integration::empty_config_file_is_valid

Scenario: None preserves platform-default user-config resolution

  • GIVEN a repo TempDir with no .git-paw/config.toml
  • AND the platform-default user config path (crate::dirs::config_dir().join("git-paw/config.toml")) is a readable file containing a custom CLI named globally-registered
  • WHEN load_config(&repo, None) is called
  • THEN the returned PawConfig.clis SHALL contain globally-registered
  • AND the loader SHALL have resolved the user-level path via global_config_path(), exactly matching v0.4 behaviour

Test: config::tests::load_config_with_none_reads_platform_default_global

Scenario: Some(path) pins the user-level read to that path

  • GIVEN a TempDir containing two distinct files:
    • tmp/global-A.toml defining custom CLI cli-A
    • tmp/global-B.toml defining custom CLI cli-B
  • AND an unrelated CLI cli-C is registered at the platform-default user-config path
  • WHEN load_config(&repo, Some(&tmp.join("global-A.toml"))) is called
  • THEN the returned PawConfig.clis SHALL contain cli-A
  • AND it SHALL NOT contain cli-B or cli-C

Test: config::tests::load_config_with_some_pins_global_to_override_path

Scenario: Some(nonexistent path) returns defaults for the user-level side

  • GIVEN a TempDir and a path tmp/does-not-exist.toml that has never been written
  • AND an unrelated CLI cli-leak is registered at the platform-default user-config path
  • WHEN load_config(&repo, Some(&tmp.join("does-not-exist.toml"))) is called
  • THEN the user-level side of the merge SHALL be the default PawConfig
  • AND the returned PawConfig.clis SHALL NOT contain cli-leak
  • AND no error SHALL be returned (a missing user-config file is not an error)

Test: config::tests::load_config_with_some_nonexistent_returns_defaults

Scenario: Override path does not affect repo-config resolution

  • GIVEN a TempDir with .git-paw/config.toml defining default_cli = "claude"
  • AND a separate path tmp/global.toml defining default_cli = "gemini"
  • WHEN load_config(&tmp, Some(&tmp.join("global.toml"))) is called
  • THEN the repo-level default_cli = "claude" SHALL override the user-level default_cli = "gemini" per the existing repo-overrides-user merge semantics
  • AND the override parameter SHALL only control which user-level file is read, never the repo-level resolution

Test: config::tests::load_config_override_does_not_affect_repo_resolution

Requirement: Custom CLI management SHALL persist through file I/O

Scenario: Add CLI with absolute path

  • GIVEN no config file
  • WHEN add_custom_cli_to() is called with an absolute path
  • THEN the CLI SHALL be persisted and reloadable

Test: config_integration::add_custom_cli_with_absolute_path

Scenario: Add CLI with display name

  • GIVEN no config file
  • WHEN add_custom_cli_to() is called with a display name
  • THEN the display name SHALL be persisted

Test: config_integration::add_custom_cli_with_display_name

Scenario: Multiple CLIs preserved across adds

  • GIVEN 4 CLIs added sequentially
  • WHEN the config is loaded
  • THEN all 4 SHALL be present with correct fields

Test: config_integration::add_multiple_custom_clis_preserves_all

Scenario: Adding overwrites existing entry

  • GIVEN a CLI with name my-agent already exists
  • WHEN add_custom_cli_to() is called with the same name but different values
  • THEN the new values SHALL replace the old

Test: config_integration::add_cli_overwrites_existing_entry

Scenario: Add CLI with nonexistent command fails

  • GIVEN a non-absolute command that is not on PATH
  • WHEN add_custom_cli_to() is called
  • THEN it SHALL return an error

Test: config_integration::add_cli_with_nonexistent_path_command_fails

Scenario: Remove custom CLI

  • GIVEN two CLIs in the config
  • WHEN one is removed
  • THEN only the other SHALL remain

Test: config_integration::remove_custom_cli

Scenario: Remove nonexistent CLI returns error

  • GIVEN no CLIs in the config
  • WHEN remove_custom_cli_from() is called
  • THEN it SHALL return an error

Test: config_integration::remove_nonexistent_cli_returns_error

Scenario: Remove all CLIs leaves empty config

  • GIVEN one CLI in the config
  • WHEN it is removed
  • THEN the CLI map SHALL be empty

Test: config_integration::remove_all_custom_clis_leaves_empty_config

Requirement: Global and repo config SHALL merge custom CLIs correctly

Scenario: Repo custom CLIs merge with global

  • GIVEN global config with 2 CLIs and repo config with 2 CLIs (one overlapping)
  • WHEN load_config_from() is called
  • THEN the result SHALL have 3 CLIs, with repo winning on collision

Test: config_integration::repo_custom_clis_merge_with_global_custom_clis

Requirement: Config SHALL handle many custom CLIs

Scenario: Config with 10 custom CLIs

  • GIVEN a config file with 10 custom CLI definitions
  • WHEN load_config() is called
  • THEN all 10 SHALL be parsed correctly

Test: config_integration::config_with_many_custom_clis

Requirement: The system SHALL support a default_spec_cli config field

The system SHALL support a default_spec_cli field in PawConfig that specifies the CLI to use for --from-specs branches that don’t have a paw_cli override, bypassing the interactive picker.

Scenario: default_spec_cli set

  • WHEN a config has default_spec_cli = "claude"
  • THEN PawConfig.default_spec_cli SHALL be Some("claude")

Scenario: default_spec_cli absent

  • WHEN a config has no default_spec_cli field
  • THEN PawConfig.default_spec_cli SHALL be None

Scenario: Merge preserves repo override

  • WHEN global config has default_spec_cli = "claude" and repo config has default_spec_cli = "gemini"
  • THEN the merged config SHALL have default_spec_cli = Some("gemini")

Requirement: Repo SHALL override new v0.2.0 scalar fields

Scenario: Repo overrides new v0.2.0 scalar fields

  • GIVEN global config has default_spec_cli = "claude" and repo has default_spec_cli = "gemini"
  • WHEN configs are merged
  • THEN default_spec_cli SHALL be "gemini"

Requirement: Specs configuration section

The system SHALL support an optional [specs] section with a dir field and a type field. Field names SHALL match the spec-scanning capability and the implementation in src/config.rs::SpecsConfig.

  • dir: String — path (relative to the repo root) to the directory containing spec files
  • type: String — backend identifier (e.g. "openspec", "markdown"); the field is exposed as spec_type in Rust to avoid clashing with the type keyword and is serialised as type in TOML/JSON via #[serde(rename = "type")]

When the [specs] section is absent, the optional specs field on PawConfig SHALL be None.

Scenario: Specs section with all fields

  • GIVEN a TOML file with [specs] containing dir = "openspec/specs" and type = "openspec"
  • WHEN the file is loaded
  • THEN specs.dir SHALL be "openspec/specs"
  • AND specs.spec_type SHALL be "openspec"

Scenario: Specs section defaults

  • GIVEN a TOML file without a [specs] section
  • WHEN the file is loaded
  • THEN specs SHALL be None

Scenario: Round-trip preserves rename

  • GIVEN a SpecsConfig { dir: "openspec/specs".into(), spec_type: "openspec".into() }
  • WHEN the value is serialised to TOML and parsed back
  • THEN the resulting TOML SHALL contain type = "openspec" (not spec_type)
  • AND parsing SHALL succeed and reproduce the original struct

Requirement: Logging configuration section

The system SHALL support an optional [logging] section with enabled and log_dir fields.

Scenario: Logging section with all fields

  • GIVEN a TOML file with [logging] containing enabled = true and log_dir = ".git-paw/logs"
  • WHEN the file is loaded
  • THEN logging.enabled SHALL be true and logging.log_dir SHALL be ".git-paw/logs"

Scenario: Logging section defaults

  • GIVEN a TOML file without a [logging] section
  • WHEN the file is loaded
  • THEN logging SHALL be None

Requirement: Default config generation

The system SHALL provide a function to generate a default config.toml string with active defaults and commented-out fields including the [supervisor] section.

Scenario: Generated config contains commented supervisor examples

  • WHEN the default config string is generated
  • THEN it SHALL contain commented-out examples for [supervisor] with enabled, cli, test_command, and agent_approval fields

Scenario: Generated config contains commented examples

  • WHEN the default config string is generated
  • THEN it SHALL contain commented-out examples for default_spec_cli, branch_prefix, [specs], [logging], [broker], and [supervisor]

Requirement: Config round-trip with new fields

A PawConfig with v0.2.0 fields populated SHALL be identical after save and reload.

Scenario: Config with specs and logging round-trips

  • GIVEN a config with default_spec_cli, branch_prefix, specs, and logging populated
  • WHEN saved and loaded back
  • THEN it SHALL be equal to the original

Requirement: Broker configuration section

The system SHALL support an optional [broker] section with the following fields:

  • enabled: bool — defaults to false when the field or section is absent
  • port: u16 — defaults to 9119 when absent
  • bind: String — defaults to "127.0.0.1" when absent

The BrokerConfig struct SHALL provide a url(&self) -> String method returning http://<bind>:<port>.

Scenario: Broker section with all fields

  • GIVEN a TOML file with [broker] containing enabled = true, port = 9200, bind = "127.0.0.1"
  • WHEN the file is loaded
  • THEN broker.enabled SHALL be true, broker.port SHALL be 9200, broker.bind SHALL be "127.0.0.1"

Scenario: Broker section defaults

  • GIVEN a TOML file without a [broker] section
  • WHEN the file is loaded
  • THEN broker SHALL have enabled = false, port = 9119, bind = "127.0.0.1"

Scenario: Partial broker section

  • GIVEN a TOML file with [broker] containing only enabled = true
  • WHEN the file is loaded
  • THEN broker.enabled SHALL be true, broker.port SHALL be 9119, broker.bind SHALL be "127.0.0.1"

Scenario: BrokerConfig url method

  • GIVEN BrokerConfig { enabled: true, port: 9200, bind: "127.0.0.1" }
  • WHEN url() is called
  • THEN the result SHALL be "http://127.0.0.1:9200"

Scenario: Broker config round-trips through save and load

  • GIVEN a config with [broker] fully populated
  • WHEN saved and loaded back
  • THEN all broker fields SHALL match the original

Requirement: Internal callers SHALL preserve v0.4 behaviour by passing None

All production call sites of load_config inside the git-paw binary SHALL pass None as the user_config_path argument, so production behaviour is byte-identical to the v0.4 single-argument load_config(repo_root) API.

The Option<&Path> argument SHALL exist only to give test code a discoverable way to isolate the user-level config read from whatever exists at the dev machine’s platform-default path. No production code path SHALL pass Some(_).

Scenario: All production call sites pass None

  • GIVEN the v0.5.0 source tree
  • WHEN every call site of config::load_config inside src/ is inspected
  • THEN every call SHALL be of the form config::load_config(&repo_root, None) (modulo whitespace and the exact name of the repo_root binding)
  • AND no production call site SHALL pass Some(_)

Test: covered by cargo build (compile-time) plus a focused grep-style assertion in src/main.rs::tests or equivalent — see tasks.md task 2.

Scenario: Production behaviour is byte-identical to v0.4

  • GIVEN a v0.5.0 binary built from this change
  • AND the same .git-paw/config.toml and platform-default user config that a v0.4 binary would read
  • WHEN any production command that calls load_config runs (e.g. git paw start, git paw add-cli, git paw dashboard)
  • THEN the merged PawConfig the command operates on SHALL be equal to the merged PawConfig v0.4 would have produced

Test: behaviour preserved by construction (every production call passes None); verified by the v0.4 test suite continuing to pass unchanged plus the new load_config_with_none_reads_platform_default_global unit test.


Interactive Selection

Purpose

Interactive selection prompts for choosing branches and AI CLIs. Supports uniform (same CLI for all branches) and per-branch assignment modes, with CLI flags that skip prompts. Logic is separated from UI via the Prompter trait for testability.

Requirements

Requirement: CLI flags skip all prompts when both provided

When both --cli and --branches flags are provided, the system SHALL skip all interactive prompts and map the CLI to all specified branches.

Scenario: Both flags skip all prompts

  • GIVEN --cli alpha and --branches feature/auth,fix/api flags
  • WHEN run_selection() is called
  • THEN it SHALL return mappings without invoking any prompts

Test: interactive::tests::both_flags_skips_all_prompts_and_maps_cli_to_all_branches

Requirement: CLI flag skips CLI prompt but prompts for branches

When only --cli is provided, the system SHALL prompt for branch selection but skip CLI selection.

Scenario: CLI flag provided, branches prompted

  • GIVEN --cli alpha flag and no branches flag
  • WHEN run_selection() is called
  • THEN branch selection SHALL be prompted and the flag CLI SHALL be used

Test: interactive::tests::cli_flag_skips_cli_prompt_but_prompts_for_branches

Requirement: Branches flag skips branch prompt but prompts for CLI

When only --branches is provided, the system SHALL skip branch selection but prompt for CLI assignment.

Scenario: Branches flag provided, CLI prompted in uniform mode

  • GIVEN --branches flag and no CLI flag
  • WHEN user selects uniform mode
  • THEN the selected CLI SHALL be mapped to all flagged branches

Test: interactive::tests::branches_flag_skips_branch_prompt_but_prompts_for_cli_uniform

Requirement: Uniform mode maps same CLI to all branches

In uniform mode, the system SHALL assign the selected CLI to every selected branch.

Scenario: Uniform mode selection

  • GIVEN user selects uniform mode, picks 2 branches and 1 CLI
  • WHEN run_selection() completes
  • THEN both branches SHALL be mapped to the same CLI

Test: interactive::tests::uniform_mode_maps_same_cli_to_all_selected_branches

Requirement: Per-branch mode maps different CLIs to each branch

In per-branch mode, the system SHALL prompt for a CLI for each selected branch individually.

Scenario: Per-branch mode selection

  • GIVEN user selects per-branch mode with 2 branches
  • WHEN different CLIs are chosen for each branch
  • THEN each branch SHALL be mapped to its respective CLI

Test: interactive::tests::per_branch_mode_maps_different_cli_to_each_branch

Scenario: Per-branch mode with branches flag

  • GIVEN branches provided via flag and per-branch mode selected
  • WHEN different CLIs are chosen
  • THEN each flagged branch SHALL be mapped to its selected CLI

Test: interactive::tests::per_branch_mode_with_branches_flag

Requirement: Error when no CLIs available

The system SHALL return PawError::NoCLIsFound when the CLI list is empty.

Scenario: Empty CLI list

  • GIVEN no CLIs available
  • WHEN run_selection() is called
  • THEN it SHALL return Err(PawError::NoCLIsFound)

Test: interactive::tests::no_clis_available_returns_error

Requirement: Error when no branches available

The system SHALL return PawError::BranchError when the branch list is empty.

Scenario: Empty branch list

  • GIVEN no branches available
  • WHEN run_selection() is called
  • THEN it SHALL return Err(PawError::BranchError)

Test: interactive::tests::no_branches_available_returns_error

Requirement: User cancellation propagates as PawError::UserCancelled

The system SHALL propagate cancellation (Ctrl+C or empty selection) as PawError::UserCancelled.

Scenario: User cancels branch selection

  • GIVEN user presses Ctrl+C during branch selection
  • WHEN run_selection() is called
  • THEN it SHALL return Err(PawError::UserCancelled)

Test: interactive::tests::user_cancels_branch_selection_returns_cancelled

Scenario: User selects no branches

  • GIVEN user confirms with zero branches selected
  • WHEN run_selection() is called
  • THEN it SHALL return Err(PawError::UserCancelled)

Test: interactive::tests::user_selects_no_branches_returns_cancelled

Scenario: User cancels CLI selection

  • GIVEN user presses Ctrl+C during CLI selection
  • WHEN run_selection() is called
  • THEN it SHALL return Err(PawError::UserCancelled)

Test: interactive::tests::user_cancels_cli_selection_returns_cancelled

Requirement: Subset branch selection

The system SHALL support selecting a subset of available branches.

Scenario: Selecting one of two branches

  • GIVEN 2 available branches
  • WHEN user selects only the second
  • THEN only that branch SHALL appear in the result

Test: interactive::tests::selecting_subset_of_branches_works

Requirement: CliMode display format

The CliMode enum SHALL display as human-readable descriptions.

Scenario: CliMode display strings

  • GIVEN CliMode::Uniform and CliMode::PerBranch
  • WHEN formatted with Display
  • THEN they SHALL render as "Same CLI for all branches" and "Different CLI per branch"

Test: interactive::tests::cli_mode_display

Requirement: CliInfo display format

CliInfo SHALL display as the binary name when it matches the display name, or as "DisplayName (binary)" when they differ.

Scenario: Same display and binary name

  • GIVEN a CliInfo where display_name equals binary_name
  • WHEN formatted with Display
  • THEN it SHALL render as just the binary name

Test: interactive::tests::cli_info_display_same_names

Scenario: Different display and binary name

  • GIVEN a CliInfo where display_name differs from binary_name
  • WHEN formatted with Display
  • THEN it SHALL render as "DisplayName (binary_name)"

Test: interactive::tests::cli_info_display_different_names

Requirement: CLI picker with optional pre-selection

The select_cli method on the Prompter trait SHALL accept an optional default CLI name for pre-selection in the interactive picker.

Scenario: Picker with default pre-selected

  • WHEN select_cli() is called with default = Some("claude") and "claude" is in the CLI list
  • THEN the picker SHALL display with "claude" highlighted as the default selection

Scenario: Picker without default

  • WHEN select_cli() is called with default = None
  • THEN the picker SHALL display with the first item selected (no pre-selection)

Scenario: Default CLI not in available list

  • WHEN select_cli() is called with default = Some("nonexistent") and that CLI is not available
  • THEN the picker SHALL display with no pre-selection (graceful fallback)

Requirement: Spec multi-select picker

The Prompter trait SHALL include a select_specs(&self, specs: &[SpecEntry]) -> Result<Vec<SpecEntry>, PawError> method that presents a multi-select picker for spec entries and returns the user’s chosen subset.

The default TerminalPrompter implementation SHALL display one row per logical spec unit (a feature in Spec Kit terms; a change in OpenSpec; a file in plain Markdown), grouping multiple SpecEntry values that decompose from the same Spec Kit feature into a single row. Selecting a row SHALL cause every SpecEntry belonging to that row’s logical unit to be returned.

Each row’s display label SHALL include the unit identifier and, for Spec Kit features that decompose into multiple worktrees, a worktree-count hint summarising the breakdown (e.g. "003-user-list — 3 worktrees: 2 [P] + 1 phase/"). For OpenSpec changes and Markdown specs, the label SHALL be the unit identifier alone (one entry → one worktree, no hint needed).

The picker SHALL behave the same way as select_branches for cancellation:

  • User pressing Ctrl+C → PawError::UserCancelled.
  • User confirming with zero rows selected → PawError::UserCancelled.

Scenario: select_specs returns the chosen subset

  • GIVEN 3 OpenSpec entries add-auth, fix-session, add-logging
  • WHEN the user toggles add-auth and add-logging and presses enter
  • THEN select_specs returns a Vec containing those two entries

Scenario: select_specs groups Spec Kit entries by feature

  • GIVEN 4 SpecEntry values from a Spec Kit project: two [P] entries (003-user-list-T009, 003-user-list-T010), one consolidated entry (003-user-list-phase-2), and one entry from a different feature (004-error-handling-phase-1)
  • WHEN the picker renders
  • THEN it displays exactly 2 rows — one per logical feature
  • AND the row for feature 003-user-list shows a worktree-count hint summarising the 3 underlying entries (2 [P] + 1 phase/)

Scenario: Selecting a Spec Kit feature row pulls in all its entries

  • GIVEN a picker rendering one row for feature 003-user-list (3 underlying SpecEntry values)
  • WHEN the user selects only that row and confirms
  • THEN select_specs returns all 3 underlying SpecEntry values

Scenario: User cancels spec picker via Ctrl+C

  • GIVEN the spec picker is open
  • WHEN the user presses Ctrl+C
  • THEN select_specs returns Err(PawError::UserCancelled)

Scenario: User confirms with zero rows selected

  • GIVEN the spec picker is open with N rows displayed
  • WHEN the user confirms without toggling any row
  • THEN select_specs returns Err(PawError::UserCancelled)

Requirement: Spec picker requires an interactive terminal

When the start command would invoke select_specs (i.e. the user passed --specs with no values), the system SHALL detect whether stdin is connected to a terminal before invoking the picker. If stdin is NOT a terminal (CI, scripted invocation, redirected input), the system SHALL exit with an actionable error pointing at the explicit forms (--specs NAME[,NAME...] to narrow, --from-all-specs to launch every discovered spec).

The system SHALL NOT block waiting for picker input on a non-interactive stdin.

Scenario: Bare –specs in non-TTY environment exits with guidance

  • GIVEN the user runs git paw start --specs with stdin redirected (or no controlling terminal)
  • WHEN the start command attempts to open the picker
  • THEN the command SHALL exit with a non-zero status before any picker UI is drawn
  • AND the error message SHALL point the user at --specs NAME[,NAME...] and --from-all-specs

Scenario: Bare –specs on TTY proceeds to picker

  • GIVEN the user runs git paw start --specs from an interactive terminal
  • WHEN the start command runs
  • THEN the picker SHALL open
  • AND no TTY-required error SHALL be emitted

Requirement: Spec name resolution for narrow mode

When --specs is passed with one or more values (narrow mode), the system SHALL resolve each value against the discovered SpecEntry set returned by scan_specs(). Resolution SHALL apply the following matching strategies in order, taking the first that succeeds:

  1. Exact match on SpecEntry.id (case-sensitive). For Spec Kit, this matches a specific decomposed entry like 003-user-list-T009. For OpenSpec / Markdown, it matches the change name or filename stem.
  2. Spec Kit feature match on the feature directory prefix of the SpecEntry.id (e.g. 003-user-list matches all entries belonging to that feature). When the value matches a Spec Kit feature unambiguously, ALL entries belonging to that feature SHALL be selected.
  3. Spec Kit numeric prefix match (e.g. 003) matching a Spec Kit feature directory name’s leading numeric portion. The match SHALL succeed only when exactly one feature directory begins with the given prefix followed by a non-digit boundary; ambiguous prefixes SHALL be rejected (see below).

Resolution SHALL fail (and the start command SHALL exit before any worktrees are created) when:

  • A value matches no SpecEntry and no feature.
  • A Spec Kit numeric prefix matches more than one feature directory (ambiguous).

The resulting error SHALL list the unresolved or ambiguous names AND the discovered candidate names so the user can correct quickly.

Scenario: Exact match resolves to a single SpecEntry

  • GIVEN a discovered set including OpenSpec change add-auth
  • WHEN the user passes --specs add-auth
  • THEN the resolved set SHALL contain exactly that one SpecEntry

Scenario: Spec Kit feature name resolves to all decomposed entries

  • GIVEN a Spec Kit feature 003-user-list decomposing into 3 SpecEntry values (2 [P] + 1 consolidated)
  • WHEN the user passes --specs 003-user-list
  • THEN the resolved set SHALL contain all 3 entries belonging to that feature

Scenario: Spec Kit numeric prefix resolves unambiguously

  • GIVEN a Spec Kit project with a single feature directory beginning with 003- (e.g. 003-user-list)
  • WHEN the user passes --specs 003
  • THEN the resolved set SHALL contain all entries belonging to that feature

Scenario: Ambiguous numeric prefix is rejected

  • GIVEN a Spec Kit project containing both 003-user-list and 003a-experiment
  • WHEN the user passes --specs 003
  • THEN the start command SHALL exit with an error
  • AND the error message SHALL list both candidate feature names

Scenario: Unknown spec name is rejected with candidate list

  • GIVEN a discovered set containing add-auth, fix-session
  • WHEN the user passes --specs no-such-spec
  • THEN the start command SHALL exit with an error
  • AND the error message SHALL include no-such-spec
  • AND the error message SHALL list add-auth and fix-session as candidates

Scenario: Multiple values are resolved independently

  • GIVEN a discovered set including add-auth, fix-session, add-logging
  • WHEN the user passes --specs add-auth,add-logging
  • THEN the resolved set SHALL contain entries for add-auth and add-logging
  • AND the resolved set SHALL NOT contain the entry for fix-session

Scenario: Partial-failure batches do not partially start

  • GIVEN a user passes --specs add-auth,no-such-spec
  • WHEN resolution runs
  • THEN the start command SHALL exit with the unknown-name error
  • AND no worktrees SHALL be created
  • AND the error message SHALL include no-such-spec (the unresolved name)

Error Handling

Purpose

Define the central error type PawError used across all git-paw modules. Every variant carries an actionable, user-facing message and maps to a process exit code.

Requirements

Requirement: Actionable error messages for each variant

Each PawError variant SHALL produce a user-facing message that explains the problem and suggests a remedy where appropriate.

Scenario: NotAGitRepo is actionable

  • GIVEN PawError::NotAGitRepo
  • WHEN formatted with Display
  • THEN the message SHALL mention “git repository” and name the tool

Test: error::tests::test_not_a_git_repo_is_actionable

Scenario: TmuxNotInstalled includes install instructions

  • GIVEN PawError::TmuxNotInstalled
  • WHEN formatted with Display
  • THEN the message SHALL include both brew install and apt install hints

Test: error::tests::test_tmux_not_installed_includes_install_instructions

Scenario: NoCLIsFound suggests add-cli

  • GIVEN PawError::NoCLIsFound
  • WHEN formatted with Display
  • THEN the message SHALL suggest the add-cli command

Test: error::tests::test_no_clis_found_suggests_add_cli

Scenario: WorktreeError includes detail

  • GIVEN PawError::WorktreeError("failed to create")
  • WHEN formatted with Display
  • THEN the message SHALL include the inner detail string

Test: error::tests::test_worktree_error_includes_detail

Scenario: SessionError includes detail

  • GIVEN PawError::SessionError("file corrupt")
  • WHEN formatted with Display
  • THEN the message SHALL include the inner detail string

Test: error::tests::test_session_error_includes_detail

Scenario: ConfigError includes detail

  • GIVEN PawError::ConfigError("invalid toml")
  • WHEN formatted with Display
  • THEN the message SHALL include the inner detail string

Test: error::tests::test_config_error_includes_detail

Scenario: BranchError includes detail

  • GIVEN PawError::BranchError("not found")
  • WHEN formatted with Display
  • THEN the message SHALL include the inner detail string

Test: error::tests::test_branch_error_includes_detail

Scenario: UserCancelled has a message

  • GIVEN PawError::UserCancelled
  • WHEN formatted with Display
  • THEN the message SHALL not be empty

Test: error::tests::test_user_cancelled_is_not_empty

Scenario: TmuxError includes detail

  • GIVEN PawError::TmuxError("session failed")
  • WHEN formatted with Display
  • THEN the message SHALL include the inner detail string

Test: error::tests::test_tmux_error_includes_detail

Scenario: CliNotFound includes CLI name

  • GIVEN PawError::CliNotFound("my-agent")
  • WHEN formatted with Display
  • THEN the message SHALL include the missing CLI name

Test: error::tests::test_cli_not_found_includes_cli_name

Requirement: Exit codes distinguish cancellation from errors

UserCancelled SHALL exit with code 2; all other errors SHALL exit with code 1.

Scenario: UserCancelled exit code

  • GIVEN PawError::UserCancelled
  • WHEN exit_code() is called
  • THEN it SHALL return 2

Test: error::tests::test_user_cancelled_exit_code

Scenario: General errors exit code

  • GIVEN any non-cancellation error variant
  • WHEN exit_code() is called
  • THEN it SHALL return 1

Test: error::tests::test_general_errors_exit_code

Requirement: Exit method prints to stderr and exits with correct code

PawError::exit() SHALL print the error message to stderr and terminate with the appropriate exit code.

Scenario: NotAGitRepo exits with code 1

  • GIVEN the binary is run outside a git repository
  • WHEN the error propagates to exit()
  • THEN the process SHALL exit with code 1 and stderr SHALL contain the error message

Test: e2e_tests::error_exit_code_is_1_for_not_a_git_repo

Scenario: ConfigError exits with code 1

  • GIVEN a nonexistent preset is requested
  • WHEN the error propagates to exit()
  • THEN the process SHALL exit with code 1 and stderr SHALL mention “not found”

Test: e2e_tests::error_exit_code_is_1_for_preset_not_found

Requirement: Debug representation is derivable

All PawError variants SHALL support Debug formatting.

Scenario: Debug format includes variant name

  • GIVEN PawError::NotAGitRepo
  • WHEN formatted with Debug
  • THEN the output SHALL contain "NotAGitRepo"

Test: error::tests::test_debug_derived

Requirement: SkillError variants with actionable messages

The system SHALL define a SkillError type with variants for skill loading failures. Each variant SHALL produce a user-facing message that explains the problem and suggests a remedy. SkillError SHALL be wrappable inside PawError as a variant.

The following variants SHALL exist:

  • UnknownSkill { name: String } — no embedded or user override found for the requested skill name
  • UserOverrideRead { path: PathBuf, source: std::io::Error } — a user override file exists but cannot be read

Scenario: UnknownSkill is actionable

  • GIVEN SkillError::UnknownSkill { name: "nonexistent" }
  • WHEN formatted with Display
  • THEN the message SHALL mention the skill name "nonexistent" and indicate no embedded default exists

Scenario: UserOverrideRead is actionable

  • GIVEN SkillError::UserOverrideRead { path: "/home/user/.config/git-paw/agent-skills/coordination.md", .. }
  • WHEN formatted with Display
  • THEN the message SHALL include the file path and suggest checking permissions

Scenario: SkillError exit code

  • GIVEN any SkillError variant wrapped in PawError
  • WHEN exit_code() is called
  • THEN it SHALL return 1

Requirement: BrokerError variants with actionable messages

The system SHALL define a BrokerError type with variants for broker-specific failures. Each variant SHALL produce a user-facing message that explains the problem and suggests a remedy. BrokerError SHALL be wrappable inside PawError as a variant.

The following variants SHALL exist:

  • PortInUse { port: u16, source: std::io::Error } — the configured port is already occupied; source carries the underlying bind/probe io::Error so callers can chain or log the original cause
  • ProbeTimeout { port: u16 } — the stale-broker probe timed out
  • BindFailed(std::io::Error) — socket bind failed for a reason other than port-in-use
  • RuntimeFailed(std::io::Error) — tokio runtime construction failed

PortInUse.source SHALL be marked #[source] (or equivalent thiserror attribute) so it participates in std::error::Error::source() chains. The Display output SHALL NOT include the source by default — it is reserved for explicit chaining via {:?} or programmatic .source() access — to avoid duplicated diagnostics in user-facing CLI output.

Scenario: PortInUse is actionable

  • GIVEN BrokerError::PortInUse { port: 9119, source: io::Error::from(io::ErrorKind::AddrInUse) }
  • WHEN formatted with Display
  • THEN the message SHALL mention port 9119 and suggest changing [broker] port in config
  • AND the message SHALL NOT contain the underlying io::Error Display text

Scenario: PortInUse exposes underlying cause

  • GIVEN a PortInUse value with an AddrInUse source
  • WHEN std::error::Error::source() is called on it
  • THEN the result SHALL be Some(&dyn Error) referencing the wrapped io::Error

Scenario: ProbeTimeout is actionable

  • GIVEN BrokerError::ProbeTimeout { port: 9119 }
  • WHEN formatted with Display
  • THEN the message SHALL mention the port and suggest checking for stuck processes

Scenario: BrokerError exit code

  • GIVEN any BrokerError variant wrapped in PawError
  • WHEN exit_code() is called
  • THEN it SHALL return 1

Contributing

Contributions to git-paw are welcome! This guide covers the development workflow.

Prerequisites

  • Rust (see rust-toolchain.toml for the exact version)
  • tmux
  • just (task runner)

Getting Started

git clone https://github.com/bearicorn/git-paw.git
cd git-paw
just check

Development Commands

git-paw uses just as a task runner. Key recipes:

CommandDescription
just checkRun fmt + clippy + tests
just testRun all tests
just test-allRun all tests including tmux-dependent ones
just lintRun cargo fmt --check and cargo clippy
just coverageGenerate HTML coverage report
just docsBuild and open mdBook documentation
just api-docsBuild and open Rustdoc API docs
just buildBuild release binary
just installInstall from local source
just cleanClean build artifacts

Building

# Debug build
cargo build

# Release build
cargo build --release

# Install locally
cargo install --path .

Testing

# Unit tests
cargo test

# Include tmux-dependent tests (requires tmux installed)
cargo test -- --include-ignored

# Coverage report
cargo llvm-cov --html
# Open: target/llvm-cov/html/index.html

Tests are organized as:

  • Unit tests#[cfg(test)] modules within each source file
  • Integration teststests/ directory (CLI binary tests, worktree lifecycle, session round-trips)
  • Tmux-dependent tests — run normally (tmux is a hard dependency)

Code Style

  • Formatting: cargo fmt (config in rustfmt.toml)
  • Linting: cargo clippy -- -D warnings with pedantic lints enabled
  • No panics: No unwrap() or expect() in non-test src/ code
  • Documentation: //! module-level doc comments, /// on all public items

Commit Format

This project uses Conventional Commits:

<type>(<scope>): <description>

[optional body]

[optional footer(s)]

Types: feat, fix, docs, style, refactor, test, ci, chore

Scopes: cli, detect, git, tmux, session, config, interactive, error

Examples:

feat(tmux): add mouse mode support
fix(session): handle missing state file gracefully
docs: update installation instructions
test(git): add worktree creation edge cases

Branch Naming

feat/<description>     # New features
fix/<description>      # Bug fixes
docs/<description>     # Documentation
test/<description>     # Test additions
ci/<description>       # CI/CD changes

Pull Request Process

  1. Fork the repository
  2. Create a feature branch from main
  3. Make your changes
  4. Ensure just check passes (fmt, clippy, tests)
  5. Write or update tests as needed
  6. Open a PR against main

PRs should:

  • Have a clear title and description
  • Pass all CI checks
  • Include tests for new functionality
  • Follow the commit format above

Architecture

See the Architecture chapter for an overview of the module structure and design decisions.

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[0.5.0] - 2026-05-25

Features

  • (cli,main,interactive) SpecMode dispatcher, pause subcommand, –no-supervisor, –specs picker, –from-specs –supervisor routing
  • (init,skills) Bundle sweep.sh helper installed by git paw init, idempotent merge against existing configs
  • (supervisor) Supervisor-as-pane, dev-allowlist seeding, default-config fallback, auto-approve, stall detection, layout helper
  • (tmux,git,agents,session,dirs) Pause primitives, idempotent worktrees, AGENTS.md boot-block lifecycle, worktree base rebase
  • (dashboard) Supervisor-as-pane row, prompt-inbox removal, phase-aware status, layout collapse
  • (config) [governance], [common_dev_allowlist], supervisor gate-command keys, user_config_path override
  • (skills) Supervisor + coordination skill v0.5.0 doctrine
  • (specs) Spec Kit backend, backend-tagged SpecEntry, per-backend boot-prompt dispatch
  • (broker) Agent.intent, learnings aggregator, conflict detector, status payload metadata, agent_id validation

Bug Fixes

  • (docs) List all crates in third-party licenses page

Documentation

  • Align README, mdBook, AGENTS.md, and user-guide with v0.5.0 surface
  • (specs) V0.5.0 OpenSpec changes, archive plan, and main-spec alignment

Testing

  • Behavioral coverage for v0.5.0 surfaces + tmux/config-integration isolation harness

[0.4.0] - 2026-04-17

Features

  • (supervisor) Auto-approve patterns
  • (supervisor) Mode with merge loop, session summary, recovery, question forwarding
  • (cli,config,init,git) Supervisor + force flags, supervisor config schema, branch handling
  • (dashboard) Committed counter, prompt-inbox interactivity, message log panel, layout
  • (broker) Hook injection, watcher, sticky terminal status, real uptime, verified/feedback messages
  • (skills) Standardize agent-skill resolution to agentskills.io layout
  • (detect) Expand auto-detection to cover 10 additional AI CLI tools

Documentation

  • Align README, mdBook, and AGENTS.md with v0.4.0 surface
  • (specs) V0.4.0 OpenSpec changes, archive plan, and main-spec alignment

Testing

  • Behavioral integration and unit tests for v0.4.0

[0.3.0] - 2026-04-10

Features

  • (broker) Wire broker into session lifecycle and update docs (#43)
  • Add dashboard, skills, and agent coordination
  • (broker) Add HTTP broker with message types, delivery, and config

CI/CD

  • (deps) Switch dependabot to monthly and ignore cargo-dist actions
  • (deps) Bump actions/deploy-pages from 4 to 5 (#40) (#40)

Build

  • (deps) Bump toml from 0.9.12+spec-1.1.0 to 1.1.2+spec-1.1.0 (#41) (#41)

[0.2.0] - 2026-04-08

Features

  • Add v0.2.0 spec-driven launch, init, logging, replay, and AGENTS.md integration (#42)

[0.1.0] - 2026-03-25

Features

  • Add CLI tool for parallel AI coding sessions across git worktrees [0.5.0]: https://github.com/bearicorn/git-paw/compare/v0.4.0…v0.5.0 [0.4.0]: https://github.com/bearicorn/git-paw/compare/v0.3.0…v0.4.0 [0.3.0]: https://github.com/bearicorn/git-paw/compare/v0.2.0…v0.3.0 [0.2.0]: https://github.com/bearicorn/git-paw/compare/v0.1.0…v0.2.0

FAQ

General

What does “paw” stand for?

Parallel AI Worktrees.

Does git-paw work on Windows?

Only through WSL (Windows Subsystem for Linux). git-paw requires tmux, which is not natively available on Windows. See the Installation chapter for WSL setup instructions.

Do I need tmux experience to use git-paw?

No. git-paw creates and manages tmux sessions for you. Mouse mode is enabled by default, so you can click to switch panes and drag to resize. The only tmux shortcut you might need is Ctrl-b d to detach.

Can I use git-paw with AI CLIs not in the supported list?

Yes! Use git paw add-cli to register any CLI binary. See Custom CLIs.

Sessions

What happens if I close my terminal?

The tmux session keeps running in the background. Run git paw again to reattach.

What happens if tmux crashes or my machine reboots?

git-paw saves session state to disk. The next time you run git paw, it detects the saved state and automatically recovers: reuses existing worktrees, recreates the tmux session, and relaunches your AI CLIs.

Can I run multiple git-paw sessions?

One session per repository. To work with multiple repos, open separate terminals and run git paw in each repo directory.

How do I switch between branches in a session?

Click the pane you want (mouse mode is on by default), or use Ctrl-b followed by arrow keys to navigate between panes. Each pane is labeled with its branch and CLI in the border title.

Worktrees

What are git worktrees?

Git worktrees let you check out multiple branches simultaneously in separate directories. Each worktree is a fully functional working copy of the repository sharing the same .git data. Changes in one worktree don’t affect others.

Where does git-paw create worktrees?

As siblings of your main repo directory. For a project at ~/projects/my-app with branch feat/auth:

~/projects/my-app/              ← your repo
~/projects/my-app-feat-auth/    ← worktree created by git-paw

Does stopping a session delete my worktrees?

No. git paw stop kills the tmux session but keeps worktrees and any uncommitted work intact. Only git paw purge removes worktrees.

Can I manually work in a git-paw worktree?

Yes. Worktrees are regular git working directories. You can cd into them, edit files, commit, push — anything you’d do in a normal repo. When you restart the session, git-paw reuses the existing worktrees.

Configuration

Where are config files stored?

LevelPath
Global~/.config/git-paw/config.toml
Per-repo.git-paw/config.toml (in repo root)

Both are optional. See Configuration.

How do I set a default CLI?

Add to your global or repo config:

default_cli = "my-cli"

How do I disable mouse mode?

mouse = false

This only affects git-paw’s tmux sessions, not your other tmux usage.

Troubleshooting

“Not a git repository”

Run git-paw from inside a git repository. It needs to be anywhere within a repo’s working tree.

“tmux is required but not installed”

Install tmux:

  • macOS: brew install tmux
  • Ubuntu/Debian: sudo apt install tmux
  • Fedora: sudo dnf install tmux

“No AI CLIs found on PATH”

Install at least one AI coding CLI (see Supported AI CLIs), or register a custom one:

git paw add-cli my-tool /path/to/my-tool

“no space for new pane” in tmux

This can happen with many branches on a small terminal. Make your terminal window larger before launching, or select fewer branches. git-paw applies tiled layout progressively to minimize this issue.

Session state seems stale

git-paw checks tmux liveness to determine effective status. If something seems off, try:

git paw purge --force
git paw start

Third-Party Licenses

git-paw includes the following third-party software.

Summary

  • 190 crates under MIT License (MIT)
  • 5 crates under Apache License 2.0 (Apache-2.0)
  • 1 crates under BSD 3-Clause “New” or “Revised” License (BSD-3-Clause)
  • 1 crates under Unicode License v3 (Unicode-3.0)
  • 1 crates under zlib License (Zlib)

License notices

Each section below covers crates that share an identical license-text variant. License-IDs with multiple copyright holders therefore appear more than once, with the relevant crates and copyright notice grouped together.

Apache License 2.0 (Apache-2.0)

Used by:

  • sdd 3.0.10
License text
                                 Apache License
                           Version 2.0, April 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   Copyright 2024-present Changgyoo Park

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

Apache License 2.0 (Apache-2.0)

Used by:

  • normalize-line-endings 0.3.0
License text
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "{}"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright {yyyy} {name of copyright owner}

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

Apache License 2.0 (Apache-2.0)

Used by:

  • scc 2.4.0
License text
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   Copyright 2020-2024 Changgyoo Park

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

Apache License 2.0 (Apache-2.0)

Used by:

  • ryu 1.0.23
  • sync_wrapper 1.0.2
License text
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.

"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.

"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.

"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.

"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.

"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.

"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).

"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.

"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."

"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.

3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:

     (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and

     (b) You must cause any modified files to carry prominent notices stating that You changed the files; and

     (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and

     (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.

     You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!)  The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.

Copyright [yyyy] [name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

BSD 3-Clause “New” or “Revised” License (BSD-3-Clause)

Used by:

  • matchit 0.8.4
License text
BSD 3-Clause License

Copyright (c) 2013, Julien Schmidt
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
   contributors may be used to endorse or promote products derived from
   this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

MIT License (MIT)

Used by:

  • instability 0.3.12
License text
# MIT License

Copyright (c) 2020 Stephen M. Coakley
Copyright (c) The Ratatui Developers

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • core-foundation-sys 0.8.7
License text
Copyright (c) 2012-2013 Mozilla Foundation

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • form_urlencoded 1.2.2
License text
Copyright (c) 2013-2016 The rust-url developers

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • percent-encoding 2.3.2
License text
Copyright (c) 2013-2025 The rust-url developers

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • cc 1.2.61
  • cfg-if 1.0.4
  • find-msvc-tools 0.1.9
  • js-sys 0.3.97
  • socket2 0.6.3
  • wait-timeout 0.2.1
  • wasm-bindgen-macro-support 0.2.120
  • wasm-bindgen-macro 0.2.120
  • wasm-bindgen-shared 0.2.120
  • wasm-bindgen 0.2.120
License text
Copyright (c) 2014 Alex Crichton

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • mio 1.2.0
License text
Copyright (c) 2014 Carl Lerche and other MIO contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

MIT License (MIT)

Used by:

  • errno 0.3.14
License text
Copyright (c) 2014 Chris Wong

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • mime 0.3.17
License text
Copyright (c) 2014 Sean McArthur

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


MIT License (MIT)

Used by:

  • bitflags 2.11.0
  • log 0.4.29
  • num-traits 0.2.19
  • regex-automata 0.4.14
  • regex-syntax 0.8.10
  • regex 1.12.3
License text
Copyright (c) 2014 The Rust Project Developers

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • float-cmp 0.10.0
License text
Copyright (c) 2014-2020 Optimal Computing (NZ) Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • hyper 1.9.0
License text
Copyright (c) 2014-2026 Sean McArthur

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

MIT License (MIT)

Used by:

  • either 1.15.0
  • itertools 0.13.0
License text
Copyright (c) 2015

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • tempfile 3.27.0
License text
Copyright (c) 2015 Steven Allen

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • heck 0.5.0
  • unicode-segmentation 1.13.2
  • unicode-width 0.1.14
  • unicode-width 0.2.0
License text
Copyright (c) 2015 The Rust Project Developers

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • which 8.0.2
License text
Copyright (c) 2015 fangyuanziti

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

MIT License (MIT)

Used by:

  • winapi 0.3.9
License text
Copyright (c) 2015-2018 The winapi-rs Developers

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • httparse 1.10.1
License text
Copyright (c) 2015-2025 Sean McArthur

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


MIT License (MIT)

Used by:

  • futures-channel 0.3.32
  • futures-core 0.3.32
  • futures-executor 0.3.32
  • futures-task 0.3.32
  • futures-util 0.3.32
License text
Copyright (c) 2016 Alex Crichton
Copyright (c) 2017 The Tokio Authors

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • hashbrown 0.15.5
  • hashbrown 0.17.0
License text
Copyright (c) 2016 Amanieu d'Antras

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • serde_urlencoded 0.7.1
License text
Copyright (c) 2016 Anthony Ramine

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • utf8parse 0.2.2
License text
Copyright (c) 2016 Joe Wilm

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • httpdate 1.0.3
License text
Copyright (c) 2016 Pyfisch

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

MIT License (MIT)

Used by:

  • lock_api 0.4.14
  • parking_lot 0.12.5
  • parking_lot_core 0.9.12
License text
Copyright (c) 2016 The Rust Project Developers

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • shell-words 1.1.1
License text
Copyright (c) 2016 Tomasz Miąsko

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • indexmap 2.14.0
License text
Copyright (c) 2016--2017

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • equivalent 1.0.2
License text
Copyright (c) 2016--2023

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • scopeguard 1.2.0
License text
Copyright (c) 2016-2019 Ulrik Sverdrup "bluss" and scopeguard developers

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • redox_syscall 0.5.18
License text
Copyright (c) 2017 Redox OS Developers

MIT License

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • http 1.4.0
License text
Copyright (c) 2017 http-rs authors

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • signal-hook-mio 0.2.5
  • signal-hook-registry 1.4.8
  • signal-hook 0.3.18
License text
Copyright (c) 2017 tokio-jsonrpc developers

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • bytes 1.11.1
License text
Copyright (c) 2018 Carl Lerche

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • autocfg 1.5.0
License text
Copyright (c) 2018 Josh Stone

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • smallvec 1.15.1
License text
Copyright (c) 2018 The Servo Project Developers

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • serial_test 3.4.0
  • serial_test_derive 3.4.0
License text
Copyright (c) 2018 Tom Parker-Shemilt

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • want 0.3.1
License text
Copyright (c) 2018-2019 Sean McArthur

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


MIT License (MIT)

Used by:

  • try-lock 0.2.5
License text
Copyright (c) 2018-2023 Sean McArthur
Copyright (c) 2016 Alex Crichton

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


MIT License (MIT)

Used by:

  • getrandom 0.4.2
License text
Copyright (c) 2018-2026 The rust-random Project Developers
Copyright (c) 2014 The Rust Project Developers

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • unicode-truncate 1.1.0
License text
Copyright (c) 2019 Aetf <aetf at unlimitedcodeworks dot xyz>

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • slab 0.4.12
License text
Copyright (c) 2019 Carl Lerche

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • bumpalo 3.20.2
License text
Copyright (c) 2019 Nick Fitzgerald

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • tracing-core 0.1.36
  • tracing 0.1.44
License text
Copyright (c) 2019 Tokio Contributors

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • tower-layer 0.3.3
  • tower-service 0.3.3
  • tower 0.5.3
License text
Copyright (c) 2019 Tower Contributors

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • axum 0.8.8
License text
Copyright (c) 2019 axum Contributors

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • http-body 1.0.1
License text
Copyright (c) 2019-2024 Sean McArthur & Hyper Contributors

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • http-body-util 0.1.3
License text
Copyright (c) 2019-2025 Sean McArthur & Hyper Contributors

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • iana-time-zone-haiku 0.1.2
  • iana-time-zone 0.1.65
License text
Copyright (c) 2020 Andrew D. Straw

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • hyper-util 0.1.20
License text
Copyright (c) 2023-2025 Sean McArthur

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

MIT License (MIT)

Used by:

  • anstream 1.0.0
  • anstyle-parse 1.0.0
  • anstyle-query 1.1.5
  • anstyle-wincon 3.0.11
  • anstyle 1.0.14
  • assert_cmd 2.2.0
  • clap 4.6.0
  • clap_builder 4.6.0
  • clap_derive 4.6.0
  • clap_lex 1.1.0
  • colorchoice 1.0.5
  • is_terminal_polyfill 1.70.2
  • once_cell_polyfill 1.70.2
  • predicates-core 1.0.10
  • predicates-tree 1.0.13
  • predicates 3.1.4
  • serde_spanned 1.1.1
  • termtree 0.5.1
  • toml 1.1.2+spec-1.1.0
  • toml_datetime 1.1.1+spec-1.1.0
  • toml_parser 1.1.2+spec-1.1.0
  • toml_writer 1.1.1+spec-1.1.0
License text
Copyright (c) Individual contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • libc 0.2.184
License text
Copyright (c) The Rust Project Developers

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • lru 0.12.5
License text
MIT License

Copyright (c) 2016 Jerome Froelich

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • static_assertions 1.1.0
License text
MIT License

Copyright (c) 2017 Nikolai Vazquez

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • darling 0.23.0
  • darling_core 0.23.0
  • darling_macro 0.23.0
License text
MIT License

Copyright (c) 2017 Ted Driggs

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • zeroize 1.8.2
License text
MIT License

Copyright (c) 2018-2021 The RustCrypto Project Developers

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • schemars 0.8.22
  • schemars_derive 0.8.22
License text
MIT License

Copyright (c) 2019 Graham Esau

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • strum 0.26.3
  • strum_macros 0.26.4
License text
MIT License

Copyright (c) 2019 Peter Glotfelty

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • tokio-macros 2.7.0
License text
MIT License

Copyright (c) 2019 Yoshua Wuyts
Copyright (c) Tokio Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • axum-core 0.5.6
License text
MIT License

Copyright (c) 2019–2025 axum Contributors

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • compact_str 0.8.1
License text
MIT License

Copyright (c) 2021 Parker Timmerman

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • castaway 0.2.4
License text
MIT License

Copyright (c) 2021 Stephen M. Coakley

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • matchit 0.8.4
License text
MIT License

Copyright (c) 2022 Ibraheem Ahmed

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • git-paw 0.5.0
License text
MIT License

Copyright (c) 2026 bearicorn

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • chrono 0.4.44
  • difflib 0.4.0
  • r-efi 6.0.0
  • wasip2 1.0.2+wasi-0.2.9
  • wasip3 0.4.0+wasi-0.3.0-rc-2026-01-06
  • winapi-i686-pc-windows-gnu 0.4.0
  • winapi-x86_64-pc-windows-gnu 0.4.0
  • windows-core 0.62.2
  • windows-implement 0.60.2
  • windows-interface 0.59.3
  • windows-link 0.2.1
  • windows-result 0.4.1
  • windows-strings 0.5.1
  • windows-sys 0.59.0
  • windows-sys 0.61.2
  • windows-targets 0.52.6
  • windows_aarch64_gnullvm 0.52.6
  • windows_aarch64_msvc 0.52.6
  • windows_i686_gnu 0.52.6
  • windows_i686_gnullvm 0.52.6
  • windows_i686_msvc 0.52.6
  • windows_x86_64_gnu 0.52.6
  • windows_x86_64_gnullvm 0.52.6
  • windows_x86_64_msvc 0.52.6
License text
MIT License

Copyright (c) <year> <copyright holders>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • tokio 1.51.1
License text
MIT License

Copyright (c) Tokio Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • ident_case 1.0.1
License text
MIT License

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • crossterm 0.28.1
  • crossterm_winapi 0.9.1
License text
MIT License

Copyright (c) 2019 Timon

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • anyhow 1.0.102
  • atomic-waker 1.1.2
  • dyn-clone 1.0.20
  • fastrand 2.4.1
  • indoc 2.0.7
  • itoa 1.0.18
  • linux-raw-sys 0.12.1
  • linux-raw-sys 0.4.15
  • once_cell 1.21.4
  • paste 1.0.15
  • pin-project-lite 0.2.17
  • proc-macro2 1.0.106
  • quote 1.0.45
  • rustix 0.38.44
  • rustix 1.1.4
  • rustversion 1.0.22
  • serde 1.0.228
  • serde_core 1.0.228
  • serde_derive 1.0.228
  • serde_derive_internals 0.29.1
  • serde_json 1.0.149
  • serde_path_to_error 0.1.20
  • serde_yaml 0.9.34+deprecated
  • syn 2.0.117
  • thiserror-impl 2.0.18
  • thiserror 2.0.18
  • unicode-ident 1.0.24
  • unsafe-libyaml 0.2.11
  • wasi 0.11.1+wasi-snapshot-preview1
  • wit-bindgen 0.51.0
  • zmij 1.0.21
License text
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • allocator-api2 0.2.21
License text
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • winnow 1.0.1
License text
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • encode_unicode 1.0.0
License text
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE

MIT License (MIT)

Used by:

  • android_system_properties 0.1.5
License text
The MIT License (MIT)

Copyright (c) 2013 Nicolas Silva

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

MIT License (MIT)

Used by:

  • aho-corasick 1.1.4
  • memchr 2.8.0
License text
The MIT License (MIT)

Copyright (c) 2015 Andrew Gallant

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

MIT License (MIT)

Used by:

  • strsim 0.11.1
License text
The MIT License (MIT)

Copyright (c) 2015 Danny Guo
Copyright (c) 2016 Titus Wormer <tituswormer@gmail.com>
Copyright (c) 2018 Akash Kurdekar

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • shlex 1.3.0
License text
The MIT License (MIT)

Copyright (c) 2015 Nicholas Allegra (comex).

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

MIT License (MIT)

Used by:

  • ratatui 0.29.0
License text
The MIT License (MIT)

Copyright (c) 2016-2022 Florian Dehau
Copyright (c) 2023-2024 The Ratatui Developers

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

MIT License (MIT)

Used by:

  • console 0.16.3
  • dialoguer 0.12.0
License text
The MIT License (MIT)

Copyright (c) 2017 Armin Ronacher <armin.ronacher@active-4.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


MIT License (MIT)

Used by:

  • bstr 1.12.1
License text
The MIT License (MIT)

Copyright (c) 2018-2019 Andrew Gallant

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

MIT License (MIT)

Used by:

  • cassowary 0.3.0
License text
The MIT License (MIT)

Copyright (c) 2016 Dylan Ede

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Unicode License v3 (Unicode-3.0)

Used by:

  • unicode-ident 1.0.24
License text
UNICODE LICENSE V3

COPYRIGHT AND PERMISSION NOTICE

Copyright © 1991-2023 Unicode, Inc.

NOTICE TO USER: Carefully read the following legal agreement. BY
DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR
SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT
DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.

Permission is hereby granted, free of charge, to any person obtaining a
copy of data files and any associated documentation (the "Data Files") or
software and any associated documentation (the "Software") to deal in the
Data Files or Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, and/or sell
copies of the Data Files or Software, and to permit persons to whom the
Data Files or Software are furnished to do so, provided that either (a)
this copyright and permission notice appear with all copies of the Data
Files or Software, or (b) this copyright and permission notice appear in
associated Documentation.

THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
THIRD PARTY RIGHTS.

IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA
FILES OR SOFTWARE.

Except as contained in this notice, the name of a copyright holder shall
not be used in advertising or otherwise to promote the sale, use or other
dealings in these Data Files or Software without prior written
authorization of the copyright holder.

zlib License (Zlib)

Used by:

  • foldhash 0.1.5
License text
Copyright (c) 2024 Orson Peters

This software is provided 'as-is', without any express or implied warranty. In
no event will the authors be held liable for any damages arising from the use of
this software.

Permission is granted to anyone to use this software for any purpose, including
commercial applications, and to alter it and redistribute it freely, subject to
the following restrictions:

1. The origin of this software must not be misrepresented; you must not claim
    that you wrote the original software. If you use this software in a product,
    an acknowledgment in the product documentation would be appreciated but is
    not required.

2. Altered source versions must be plainly marked as such, and must not be
    misrepresented as being the original software.

3. This notice may not be removed or altered from any source distribution.