# pi-config

> Customize and extend your AI development assistant with custom agents, rules, and memory

---

Source: quickstart.md

# Installation & Quickstart

Get pi-config installed, configure your project, start the local daemons, and run your first agent workflow so you can automate repository tasks in under a minute.

## Prerequisites

- Node.js (>= 22)
- Git installed and configured
- `pi` (install with `npm install -g @earendil-works/pi-coding-agent`)
- `uv` (Python package manager)

## Quick Example

```bash
# Install everything non-interactively
uv run scripts/install.py --all

# Start a session, then in the chat:
# /pidash start
# /pidiff start
# /scout-and-plan Review the authentication module and propose a migration plan to JWT.
```

## Step-by-Step Guide

### 1. Install pi-config and tooling

Run the interactive installer from a pi-config checkout:

```bash
uv run scripts/install.py
```

Follow the prompts to select packages (orchestrator, CLI tools, browser automation, gitignore entries, and more).

To skip prompts and install everything available:

```bash
uv run scripts/install.py --all
```

> **Note:** The installer exits if `pi` is missing. Install `@earendil-works/pi-coding-agent` globally first.

When it finishes, start a session:

```bash
pi
```

### 2. Add project settings

Create `.pi/pi-config-settings.json` in your repository root:

```json
{
  "commit_trailer": "Assisted-by",
  "dco": true,
  "dream_interval_hours": 3,
  "pidash_enable": true,
  "pidiff_enable": true,
  "cli_agents": ["claude", "cursor"]
}
```

> **Note:** Project settings override global defaults for the current repository. See [Configuration & Settings](configuration.html) for the full option list.

### 3. Start the background daemons

Inside an active `pi` TUI session:

```text
/pidash start
/pidiff start
```

- `/pidash start` launches the web dashboard (default `http://localhost:19190`).
- `/pidiff start` launches the per-project diff viewer on a free local port.

Check daemon state anytime:

```text
/pidash status
/pidiff status
```

> **Tip:** Open the dashboard URL from the status output to monitor sessions and background work. See [Using the Web Dashboard](using-the-web-dashboard.html).

### 4. Run your first workflow

```text
/scout-and-plan Review the authentication module and propose a migration plan to JWT.
```

This chains a scout pass (find relevant code) into a planner pass (implementation plan) without writing changes yet. For creating and routing specialists, see [Managing Custom Agents](managing-custom-agents.html).

## Advanced Usage

### Install only what you need

| Mode | Command | Behavior |
|------|---------|----------|
| Interactive | `uv run scripts/install.py` | Step through packages and confirm |
| Non-interactive | `uv run scripts/install.py --all` | Auto-select every available tool |

The installer can also add `.pi/` and `.worktrees/` to your global git excludes file so local agent data is not committed.

### Configure via environment variables

Skip the settings file when you prefer env vars. Resolution order:

1. `.pi/pi-config-settings.json` (project)
2. `~/.pi/pi-config-settings.json` (global)
3. Environment variables (for example `PI_DREAM_INTERVAL_HOURS=3`, `CLI_AGENTS=claude,cursor`)
4. Built-in defaults

See [Configuration & Settings](configuration.html) for keys and env var names. For CLI utilities used by review and memory workflows, see [myk_pi_tools CLI Reference](cli-reference.html).

### Keep project data out of git

| Method | Command |
|--------|---------|
| Preferred | `git config --global core.excludesfile ~/.config/git/ignore && echo ".pi/" >> ~/.config/git/ignore` |
| Manual | Append `.pi/` (and `.worktrees/` if you use worktrees) to your global excludes file |

> **Tip:** The installer Environment Setup step can configure these entries for you.

## Troubleshooting

- **"Cannot continue without pi":** Install the coding agent globally (`npm install -g @earendil-works/pi-coding-agent`), confirm `pi` is on your `PATH`, then re-run the installer.
- **Daemon fails to start:** Confirm `pidash_enable` / `pidiff_enable` are not set to `false`. Run `/pidash status` or `/pidiff status`. For pidash failures, check `~/.pi/pidash-server.log`.
- **pidash says TUI-only:** Start daemons from an interactive `pi` session, not a headless/CLI-only mode.
- **Pre-commit / formatting failures:** Run `prek run --all-files` to apply fixes, then retry the commit.

## Related Pages

- [Configuration & Settings](configuration.html)
- [Using the Web Dashboard](using-the-web-dashboard.html)
- [Built-in Workflow Commands](built-in-workflows.html)
- [Creating Slash Commands](custom-slash-commands.html)
- [myk_pi_tools CLI Reference](cli-reference.html)

---

Source: automating-code-reviews.md

# Automating Code Reviews

Use the review loop when you want Pi to fetch PR feedback, apply fixes, reply to review comments, and keep re-checking until your AI reviewers approve. This is the fastest way to turn Qodo and CodeRabbit feedback into working code without hand-copying comments between GitHub and your editor.

## Prerequisites

- A GitHub pull request already opened for your current branch
- Qodo and/or CodeRabbit enabled on that repository
- A Pi session running in the repo
- `uv`, `gh`, and `myk-pi-tools` available in your environment

## Quick Example

```bash
/review-handler --autorabbit --autoqodo
```

Run this inside Pi to start the automatic loop for the current PR. It watches both AI review sources, fixes actionable findings, posts replies, pushes follow-up commits, and keeps polling until approval or until you stop it.

## Step-by-Step

1. **Choose the review mode**

   Use the command that matches how much automation you want:

   | Goal | Command |
   |---|---|
   | Auto-fix CodeRabbit comments | `/review-handler --autorabbit` |
   | Auto-fix Qodo comments | `/review-handler --autoqodo` |
   | Auto-fix both AI reviewers | `/review-handler --autorabbit --autoqodo` |
   | Review all sources manually | `/review-handler` |

2. **Start with the simplest working flow**

   If you want a hands-off loop, start with both AI reviewers enabled:

   ```bash
   /review-handler --autorabbit --autoqodo
   ```

   In auto mode, Pi skips the manual approval table and goes straight into fetch → fix → test → commit → push → reply → poll.

3. **Let Pi process the current round of comments**

   In auto mode, the handler works from the current PR and processes:
   - CodeRabbit comments
   - Qodo findings
   - follow-up reviewer pushback on earlier fixes

   The loop keeps running until one of these happens:
   - the reviewer approves the PR
   - you explicitly stop the loop

   > **Tip:** The loop can run for a long time while waiting for new bot comments. See [Running Background Agents and Scheduled Tasks](async-agents-and-cron.html) for details on monitoring long-running background work.

4. **Use manual mode when humans are involved**

   If you run:

   ```bash
   /review-handler
   ```

   Pi fetches human, Qodo, and CodeRabbit items and presents them for review. This is the better choice when you want to approve, skip, or explain items one by one before Pi makes changes.

5. **Handle one reviewer at a time when needed**

   If one bot is noisy or blocked, run only the other source first:

   ```bash
   /review-handler --autorabbit
   ```

   ```bash
   /review-handler --autoqodo
   ```

   This is useful when you want a smaller fix cycle before bringing both reviewers back in.

6. **Let the loop re-check after each push**

   After Pi fixes comments and pushes a follow-up commit, it polls for new reviewer output again. For Qodo, that includes follow-up responses on sticky findings; for CodeRabbit, it includes re-triggered review cycles after cooldowns or pauses.

## Advanced Usage

### Review with the CLI instead of the slash command

Use the CLI flow when you want to script review automation outside the interactive handler:

```bash
myk-pi-tools reviews fetch --output-dir .pi/tmp/
```

```bash
myk-pi-tools reviews poll --source qodo --output-dir .pi/tmp/
```

```bash
myk-pi-tools reviews post .pi/tmp/pr-42-reviews.json
```

```bash
myk-pi-tools reviews store .pi/tmp/pr-42-reviews.json
```

Use this flow when you need custom wrappers, CI experiments, or one-off tooling. See [myk_pi_tools CLI Reference](cli-reference.html) for details.

> **Warning:** `--autorabbit` and `--autoqodo` are slash-command flags for `/review-handler`. They are not CLI flags for `myk-pi-tools reviews ...`.

### Ask Qodo follow-up questions

If Qodo keeps objecting and you need a more specific answer, ask it directly from the current PR context:

```bash
myk-pi-tools reviews ask-qodo "What edge cases are missing?"
```

This is especially useful when a finding is still actionable but the fix direction is unclear.

### Generate a review status report

To inspect the current PR’s accumulated review history and produce an HTML report:

```bash
myk-pi-tools reviews status --output-dir .pi/reports/
```

Use this when you want a durable summary across multiple review cycles instead of only the latest comment thread state.

### Run multiple PR review loops safely

If you need to automate reviews for more than one PR at the same time, use separate worktrees instead of switching branches in place:

```bash
git worktree add .worktrees/pr-42 origin/fix/issue-42
git worktree add .worktrees/pr-43 origin/feat/issue-43
```

Then run `/review-handler` from each worktree independently. This avoids cross-contaminating parallel review sessions.

### Understand cycle limits in auto mode

Automated review flows use a shared review-cycle budget controlled by `review_loop_max_cycles`. Auto mode also runs in two stages: spec first, then code quality, with both stages drawing from the same cycle cap.

If the cycle cap is reached before approval, Pi stops re-dispatching reviewers and reports the remaining state instead of pretending the PR is clean. See [Configuration & Settings](configuration.html) for details.

## Troubleshooting

- **CodeRabbit is rate-limited:** run:
  ```bash
  /coderabbit-rate-limit
  ```
  This waits out the cooldown and re-triggers the review on the current PR.

- **CodeRabbit pauses after too many reviewed commits:** add this to `.coderabbit.yaml`:
  ```yaml
  reviews:
    auto_review:
      auto_pause_after_reviewed_commits: 0
  ```

- **Qodo keeps resurfacing the same finding:** ask a follow-up question with `myk-pi-tools reviews ask-qodo "..."`, then either change the code or clarify the requirement before rerunning the loop.

- **Commits are blocked even after fixes:** your review loop may still be failing tests or waiting on reviewer approval. See [Configuration & Settings](configuration.html) for the cycle cap settings, and see [Implementing Command Guards](safety-enforcements.html) for commit enforcement details.

- **You want a simpler first pass:** start with `/review-handler --autorabbit` or `/review-handler --autoqodo`, then enable both once the PR is stable.

## Related Pages

- [myk_pi_tools CLI Reference](cli-reference.html)
- [Built-in Workflow Commands](built-in-workflows.html)
- [Running Background Agents and Scheduled Tasks](async-agents-and-cron.html)
- [Using the Web Dashboard](using-the-web-dashboard.html)
- [Curating Project Memory](curating-project-memory.html)

---

Source: managing-custom-agents.md

# .pi/agents/security-auditor.md
---
name: security-auditor
description: Audits external repositories for security risks before adoption — checks for malicious code, data exfiltration, supply chain risks, and trust signals.
tools: read, bash
---

# Security Auditor

You are a security auditor specializing in evaluating external repositories for safe adoption.
Your job is to determine: **Is this repo safe for us to use?**
```

Then start a new Pi session and ask:

```text
Audit this GitHub repository before we adopt it.
```

Because the agent keeps the same `name: security-auditor`, your project version takes precedence for this repo.

## Step-by-Step

### 1. Choose the right scope

Use the smallest scope that matches how broadly you want the agent to apply.

| Scope | Put the file here | Best for |
|---|---|---|
| Project-only | `.pi/agents/` | One repository with project-specific behavior |
| Global | `~/.pi/agent/agents/` | The same specialist across all repositories |

> **Tip:** Project-local agents win over global agents when both use the same `name:`.

### 2. Create the agent file

Start with a `.md` file that has YAML frontmatter at the top, then write the instructions below it.

```markdown
---
name: security-auditor
description: Audits external repositories for security risks before adoption — checks for malicious code, data exfiltration, supply chain risks, and trust signals.
tools: read, bash
---

# Security Auditor

You are a security auditor specializing in evaluating external repositories for safe adoption.
```

Use these fields:

- `name` — the agent ID Pi uses when selecting or dispatching the agent
- `description` — a short summary of what the agent is for
- `tools` — a comma-separated allowlist such as `read, bash` or `read, write, edit, bash`

You can also add optional `provider` and `model` frontmatter if you want that agent to prefer a specific backend.

> **Warning:** Give agents the fewest tools they need. If an agent can work with `read` alone, do not add write-capable tools. For shell-heavy agents, see [Implementing Command Guards](safety-enforcements.html) for details.

### 3. Keep the instructions narrow

The bundled specialists are very specific: one agent reviews, another runs tests, another audits third-party repos. Follow the same pattern for your own agents.

Good custom-agent prompts usually include:

- what the agent should do
- what it must not do
- the format you want back
- any repo-specific rules that matter every time

A strong prompt is usually better than a long prompt. Short, opinionated instructions are easier for Pi to follow reliably.

### 4. Route the agent from normal chat

If you want Pi to pick your specialist from everyday requests, add a project rule that tells the orchestrator when to use it.

```markdown
# .pi/rules/custom-agents.md

- When the task is to audit a third-party repository before adoption, delegate to `security-auditor`.
- When the task is to run tests and explain failures without fixing code, delegate to `test-runner`.
```

Write the rule in plain language. Be specific about both the trigger and the agent name.

> **Note:** Project rules are loaded automatically for that repository. Start a new session after adding or changing them.

### 5. Test the routing

Start a fresh session, then try a request that should clearly match your rule.

Examples that align with bundled specialists already in the project:

```text
Audit this dependency repo before we vendor it.
```

```text
Run the tests and summarize the failures without fixing anything.
```

A good test prompt is narrow enough that only one specialist should make sense. If Pi chooses the wrong one, tighten the wording in your rule instead of making the agent prompt broader.

### 6. Use workflow-only agents when chat routing is the wrong fit

Some specialists should only run inside a specific flow rather than being selected from general chat. In that case, keep them out of your chat-routing rules and dispatch them from a prompt template or slash command instead.

Built-in workflows already do this for review specialists such as `/issue-review`, `/pr-review`, and `/review-local`. See [Creating Slash Commands](custom-slash-commands.html) for details.

## Advanced Usage

### Override order

When two agents use the same `name:`, Pi resolves them in this order:

| Highest precedence | Use case |
|---|---|
| `.pi/agents/` | Override behavior for one repository |
| `~/.pi/agent/agents/` | Reuse a specialist everywhere |
| Bundled defaults | Fallback when you have not overridden anything |

This is the easiest way to customize a built-in agent for one repo without affecting your other work.

### Reuse a built-in pattern instead of starting from scratch

If your new agent is “like the existing one, but stricter,” copy the shape of a bundled specialist and change only what matters:

- `security-auditor` for third-party repo checks
- `test-runner` for test execution and failure summaries
- `reviewer` for read-only analysis
- `worker` for broad, write-capable tasks

This gets you productive faster than inventing a brand-new prompt structure.

### Pin a provider or model for one agent

If one specialist works better on a specific backend, set defaults in project settings:

```json
{
  "agent_provider": "cli-cursor",
  "agent_model": "cursor:cursor-grok-4.5-high-fast",
  "agent_overrides": {
    "test-runner": {
      "provider": "cli-claude",
      "model": "claude-3-5-sonnet"
    }
  }
}
```

Use this when one agent needs a different model from the rest of your session. See [Configuration & Settings](configuration.html) for details.

### Force an agent to inherit the parent session model

You can explicitly clear an override with `null`:

```json
{
  "agent_overrides": {
    "reviewer": {
      "provider": null,
      "model": null
    }
  }
}
```

This is useful when you want most agents pinned, but one specialist should always follow the current session.

### Prefer workflow agents for long or structured jobs

If the agent needs multiple phases, parallel reviewers, scheduled runs, or background execution, put it behind a workflow instead of normal chat routing. See [Creating Slash Commands](custom-slash-commands.html) and [Running Background Agents and Scheduled Tasks](async-agents-and-cron.html) for details.

## Troubleshooting

- **Pi does not pick the new agent:** Start a new session, confirm the file is under `.pi/agents/` or `~/.pi/agent/agents/`, and make sure the frontmatter includes both `name:` and `description:`.
- **Pi picks the wrong specialist:** Make your routing rule more explicit. If the task is really workflow-only, dispatch it from a slash command instead of relying on everyday chat.
- **The agent has too much authority:** Reduce the `tools:` list first. Many specialists only need `read` and `bash`.
- **The agent uses the wrong model:** Check `provider:` / `model:` frontmatter and your `agent_provider`, `agent_model`, and `agent_overrides` settings. See [Configuration & Settings](configuration.html) for details.
- **A project-level agent is not found from a subdirectory:** Put it under the repository’s `.pi/agents/` folder, then start a new session from anywhere inside that repo.

## Related Pages

- [Creating Slash Commands](custom-slash-commands.html)
- [Built-in Workflow Commands](built-in-workflows.html)
- [Running Background Agents and Scheduled Tasks](async-agents-and-cron.html)
- [Inter-Agent Communication Network](inter-agent-communication.html)
- [Implementing Command Guards](safety-enforcements.html)

---

Source: using-the-web-dashboard.md

# Using the Web Dashboard

Monitor multiple active terminal sessions, interact with background tasks, and perform visual code reviews without leaving your workflow. Using the local React UI allows you to manage long-running agents and inspect git diffs across multiple projects from a single browser window.

- **Prerequisites:**
  - An active Terminal UI (TUI) session (the web features cannot be launched from CLI-only modes).
  - Port `19190` available on your machine (the default port for the global dashboard).

## Quick Example

Start the global dashboard directly from your active TUI session:

```text
/pidash start
```

Once the background server launches, the TUI status line will display a clickable `pi-dash` label and web link (e.g., `🌐 http://localhost:19190`). Click the label or open this URL in your web browser to manage your workspace.

## Step-by-Step Guide

1. **Launch the Dashboard:** Run the `/pidash start` command inside your active session. The background server will initialize and establish a WebSocket connection with your terminal.
2. **Navigate Sessions:** Open the dashboard URL in your browser. The interface displays a list of all active sessions across your system. Clicking a session name will automatically switch your terminal's context to that project directory.
3. **Adjust Agent Settings:** Use the dropdowns in the web UI to change the active LLM provider or adjust the thinking level dynamically. Setting changes are immediately reflected in your terminal.
4. **Monitor Background Tasks:** View the status of asynchronous agents and scheduled cron jobs. If a background task is stuck, you can terminate it directly from the dashboard using the kill controls.
5. **Send Prompts:** You can type prompts directly into the web UI. The dashboard forwards these commands to your terminal session as follow-up instructions for the agent.
6. **Stop the Dashboard:** When you are finished, shut down the web server by running `/pidash stop` in your terminal.

## Advanced Usage

### Dashboard vs. Diff Viewer

The project uses two distinct web interfaces depending on what you need to accomplish:

| Feature | `/pidash` (Web Dashboard) | `/pidiff` (Diff Viewer) |
|---------|---------------------------|-------------------------|
| **Scope** | Global (sees all active sessions) | Local (tied to the current project) |
| **Port Mapping** | Fixed (`19190` by default) | Dynamic (allocates a random free port) |
| **Primary Use** | Session switching, agent config, background task monitoring | Visualizing git diffs, publishing inline code review comments |
| **Commands** | `/pidash start\|stop\|restart\|status` | `/pidiff start\|stop\|restart\|status` |

### Using the Project Diff Viewer

While the main dashboard monitors your global state, the `pidiff` extension provides a dedicated interface for reviewing code changes within a specific repository. It runs a separate server to isolate project context.

Launch the diff viewer from your project session:

```text
/pidiff start
```

1. The TUI status line will display a clickable `pi-diff` label and dynamically allocated port for this specific project's diff viewer.
2. Click the `pi-diff` label or open the URL to inspect local git diffs side-by-side and annotate specific lines of code.
3. Once you publish review comments from the web UI, they are automatically injected into your TUI session as formatted instructions. The active agent will read the comments and begin resolving them.

To check the health and port of your active diff server at any time, run:

```text
/pidiff status
```

> **Tip:** If you need to forcefully reset the project diff viewer, run `/pidiff restart`. This kills the local server and allocates a new random port.

### Running in Containers

The web dashboard automatically detects if your session is running inside a Docker or Podman container. Ensure you expose the necessary ports when launching your container so your host machine's browser can connect:
- Map port `19190` for the global dashboard.
- If using the diff viewer, map a specific port range and ensure your environment is configured to allow dynamic port allocation.

> **Note:** To disable the web dashboard extensions entirely in headless environments or CI pipelines, set `PI_PIDASH_ENABLE=false` and `PI_PIDIFF_ENABLE=false` in your environment variables (or set `pidash_enable: false` and `pidiff_enable: false` in `pi-config-settings.json`).

## Troubleshooting

- **Dashboard fails to start:** Check if another application is using port `19190`. You can change the port by setting the `PI_PIDASH_PORT` environment variable (or `pidash_port` in `pi-config-settings.json`) before starting your session.
- **Session not showing in UI:** Ensure your terminal is running in TUI mode. The web interface relies on the TUI's event hooks to synchronize state.
- **Cannot connect to diff viewer:** The `pidiff` server uses local lockfiles in your project's `.pi/tmp/` directory to track active ports. If the server becomes unresponsive or port conflicts occur, run `/pidiff stop` to clear the lockfiles before starting it again.

See [Installation & Quickstart](quickstart.html) to learn more about basic chat commands, or read [Managing Custom Agents](managing-custom-agents.html) to understand how background async tasks operate.

## Related Pages

- [Daemon & Websocket Networking](daemon-and-websockets.html)
- [Installation & Quickstart](quickstart.html)
- [Running Background Agents and Scheduled Tasks](async-agents-and-cron.html)
- [Discord Bot Notifications](discord-bot.html)
- [Automating Code Reviews](automating-code-reviews.html)

---

Source: curating-project-memory.md

# Project preference
myk-pi-tools memory add -c preference -s "Always use uv run" --pinned

# Architectural decision
myk-pi-tools memory add -c decision -s "Use Redis for shared caching"

# Repeated pitfall
myk-pi-tools memory add -c mistake -s "Buildah chown -R skips the target dir on this OS"
```

Use short, specific summaries. The CLI accepts these categories:

| Category | Use it for |
|---|---|
| `preference` | How you want work done |
| `decision` | A project choice that should stay consistent |
| `mistake` | A failure mode worth avoiding |
| `pattern` | A recurring convention or approach |
| `lesson` | A correction or practical takeaway |
| `done` | A completed milestone worth remembering |

> **Tip:** Keep each memory to one line. Short, concrete entries are easier for Pi to reuse well.

### 3. Pin rules that should not fade

```bash
myk-pi-tools memory add -c preference -s "Always use uv run" --pinned
```

Use `--pinned` for rules you want to keep stable over time. Pinned entries are protected during background cleanup and consolidation.

A good rule of thumb:

| If the memory is... | Save it as... |
|---|---|
| A long-term project rule | `--pinned` |
| A normal lesson from recent work | regular memory |
| A one-off observation | regular memory, only if it will matter again |

### 4. Save memories directly from chat when that is faster

```text
/remember Always use uv run for Python commands in this repo
```

Use `/remember` when you are already in a Pi session and want to save something immediately without leaving chat. It stores the result as a pinned project memory.

### 5. Audit memory quality and promotion status

```bash
myk-pi-tools memory status
```

This gives you a quick health check of the memory system, including:

- topic-backed memory currently being injected as context
- code-enforced entries
- open promotion candidates

Use this when memory feels noisy, when a rule should probably become a hard guard, or when you want to confirm the project is learning from repeated corrections.

## Advanced Usage

### Let Pi capture preferences automatically

You do not need to save every preference manually. During normal conversation, Pi automatically looks for explicit signals such as:

- “I prefer…”
- “always use…”
- “never use…”

When those statements are clear enough, Pi records them as project preferences and reinforces them when they show up again later.

> **Note:** Automatic capture works best when you phrase the rule as a short, direct instruction instead of a long explanation.

### Inspect where memory is stored

```bash
myk-pi-tools memory path
```

Use this when you want to inspect the memory directory itself, back it up, or compare projects. This is especially useful if you work across multiple repositories and want to confirm you are editing the right project memory.

### Consolidate memory in the background

```text
/dream
/dream-auto on
```

Use `/dream` for a one-time background consolidation pass. Use `/dream-auto on` to let Pi keep consolidating memories automatically over time.

Background consolidation helps by:

- extracting durable lessons from past sessions
- deduplicating similar entries
- reorganizing topic files
- preserving pinned entries

If you want to tune the schedule, set `dream_interval_hours` in your project settings. See [Background Memory Consolidation (Dreaming)](background-dreaming.html) and [Configuration & Settings](configuration.html) for details.

### Migrate older memory data into topic files

```bash
myk-pi-tools memory migrate
```

If your project still has older memory data from a previous format, run this once to move it into the current topic-based layout. This is mainly useful when upgrading an existing project rather than starting fresh.

### Learn from review feedback automatically

When you skip a code review finding for a reason that applies more broadly, Pi can reuse that decision in future reviews so the same kind of complaint does not keep coming back.

Use this together with your PR workflow, then review the results in later cycles. See [Automating Code Reviews](automating-code-reviews.html) for details.

### Know when to use memory versus hard guards

Use memory for guidance such as preferences, patterns, and decisions. Use a command guard when a rule must be enforced mechanically, not just remembered.

Examples:

| Need | Better fit |
|---|---|
| “Prefer this style in this repo” | Project memory |
| “Never allow this dangerous command” | Command guard |
| “Remember this design choice for future work” | Project memory |
| “Block this action unless another step happened first” | Command guard |

See [Implementing Command Guards](safety-enforcements.html) for details.

## Troubleshooting

- **A saved rule does not seem to affect later sessions:** Run `myk-pi-tools memory show` and confirm the entry is present. If it is critical, save it again as a pinned memory with a shorter, clearer summary.
- **Automatic background consolidation does not run:** If you are using ACPX-backed async work, make sure `async_llm_provider` and `async_llm_model` are configured. See [Configuration & Settings](configuration.html) for details.
- **Memory feels too noisy or repetitive:** Check `myk-pi-tools memory status`, keep entries one-line and specific, then run `/dream` to consolidate the store.
- **You need deeper scoring and lifecycle details:** See [Memory Architecture](memory-architecture.html) for details.

## Related Pages

- [Memory Architecture](memory-architecture.html)
- [Background Memory Consolidation (Dreaming)](background-dreaming.html)
- [Implementing Command Guards](safety-enforcements.html)
- [Configuration & Settings](configuration.html)
- [Built-in Workflow Commands](built-in-workflows.html)

---

Source: btw-command.md

# Asking Side Questions with /btw

Use `/btw` when you want a fast answer based on the current conversation without cluttering the main chat history. It is useful for small clarifications, naming questions, or quick follow-ups while you stay focused on the current task.

## Prerequisites

- An active Pi session with the terminal UI
- A currently selected model
- Enough conversation context for the model to answer from the existing session

## Quick Example

```text
/btw What helper name did we agree on for the auth token parser?
```

This opens a temporary answer view instead of adding a new turn to the main conversation.

## Step-by-step

### 1. Ask the side question

```text
/btw <question>
```

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `<question>` | string | (required) | The short question to answer from the current conversation context. |

```text
/btw What was the final scope of the refactor?
```

**Effect:** Starts a temporary side-question flow. If the question is empty, Pi shows `Usage: /btw <question>`.

### 2. Wait for the temporary answer

While the answer is being generated, Pi shows a loading box with the active model id:

```text
Thinking (<model-id>)...
```

| Item | Effect |
| :--- | :--- |
| Loading view | Shows the request is in progress |
| Abort action | Cancels the side question |
| Empty answer | Shows `No answer received` |

```text
/btw Summarize the decision we made about caching in one sentence.
```

### 3. Read the answer in the overlay

The result appears in a scrollable overlay with the original question and the generated answer.

| Key | Action |
| :--- | :--- |
| `Esc` | Close the overlay |
| `Space` | Close the overlay |
| `q` | Close the overlay |
| `Ctrl+C` | Close the overlay |
| `↑` or `k` | Scroll up |
| `↓` or `j` | Scroll down |
| `PgUp` | Scroll up faster |
| `PgDn` | Scroll down faster |

```text
/btw Which part of the plan is still unresolved?
```

**Effect:** Displays the answer in a temporary view and returns you to the same session after dismissal.

### 4. Continue your main task

`/btw` does not append the side exchange to the main conversation branch.

| Behavior | Normal turn | `/btw` |
| :--- | :--- | :--- |
| Uses current conversation context | Yes | Yes |
| Uses tool access | Yes | No |
| Writes a new turn into the main history | Yes | No |
| Shows answer in a temporary overlay | No | Yes |

```text
/btw Remind me which file we said owns the retry logic.
```

> **Note:** `/btw` answers only from the current conversation plus loaded project context such as context file names and available skill names.

## Advanced Usage

### Best use cases

| Use case | Good fit for `/btw` |
| :--- | :--- |
| Clarify a recent decision | Yes |
| Ask for a one-line recap | Yes |
| Check naming or terminology from the current session | Yes |
| Ask for file inspection or command output | No |
| Make code changes | No |

```text
/btw Give me the shortest version of the migration plan.
```

> **Tip:** Use `/btw` for quick memory refresh. Use a normal prompt when you want the answer preserved in the main transcript or you need tools.

### Context available to the answer

`/btw` can use:

- The current conversation branch
- Loaded project context file names
- Available skill names

`/btw` does not use:

- File reads during the side question
- Shell commands during the side question
- Workspace edits during the side question

```text
/btw Based on this session only, what are the two biggest risks left?
```

### Using and extending

| Goal | What to do |
| :--- | :--- |
| Use `/btw` | Run `/btw <question>` in any active session |
| Create similar commands | Add your own slash commands with custom behavior |

For creating related commands, see [Creating Slash Commands](custom-slash-commands.html) for details.

## Troubleshooting

| Problem | What it means | What to do |
| :--- | :--- | :--- |
| `Usage: /btw <question>` | No question was provided | Run `/btw` with text after it |
| `Cancelled` | The request was aborted | Run the command again |
| `No answer received` | The model returned no usable text | Retry with a clearer question |
| The answer is too vague | The current session does not contain enough context | Ask a more specific question or continue with a normal prompt |

> **Warning:** `/btw` has no tool access, so it cannot verify files, run commands, or inspect new code while answering.

## Related Pages

- [Creating Slash Commands](custom-slash-commands.html)
- [Built-in Workflow Commands](built-in-workflows.html)
- [Installation & Quickstart](quickstart.html)
- [Managing Custom Agents](managing-custom-agents.html)

---

Source: custom-slash-commands.md

# Creating Slash Commands

`pi-config` already points Pi at `./prompts` and `./extensions` in `package.json`, so those are the supported places to add new slash commands.

## Add a prompt-only command
Use this when you want a reusable slash command without writing any TypeScript.

```markdown
<!-- prompts/security-audit.md -->
---
description: "Scan the current changes for security problems"
---

Review the current git diff for security issues.

Focus on:
- hardcoded secrets
- shell injection
- auth and permission regressions
- unsafe file or network access

Return the findings as a short bullet list with severity labels.
```

Dropping this file into `prompts/` creates `/security-audit`. The filename becomes the command name, and the frontmatter `description` is what users see when browsing commands. Use this pattern for repeatable prompts that only need text instructions.

- Keep filenames kebab-case so the slash command is predictable.
- Use prompt templates when the command does not need UI hooks, events, or custom runtime logic.

## Pass arguments into a prompt command
Use this when the command should accept a target like a file path, branch name, or symbol.

````markdown
<!-- prompts/explain-file.md -->
---
description: "Explain a file or symbol from the current project"
argument-hint: "<path-or-symbol>"
---

## Raw Arguments

```text
$ARGUMENTS
```

Explain the target above in 5-8 bullets.

Include:
- what it is
- where it fits in the project
- risky edges or gotchas
- the next file to read
````

`$ARGUMENTS` is replaced with the raw text after the slash command, and `argument-hint` gives the user an inline usage hint. This is the fastest way to turn an ad hoc prompt into a reusable command without adding extension code.

> **Tip:** Keep the prompt robust when `$ARGUMENTS` is empty, either by asking a follow-up question or by describing the expected input format.

## Register a TypeScript command
Use this when the command needs session context, UI notifications, or custom runtime behavior.

```typescript
// extensions/orchestrator/hello.ts
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";

export function registerHello(pi: ExtensionAPI): void {
  pi.registerCommand("hello", {
    description: "Show a quick greeting for the current workspace",
    handler: async (args, ctx) => {
      const name = args?.trim() || "Developer";

      if (!ctx.hasUI) return;

      ctx.ui.notify(`Hello, ${name}! Workspace: ${ctx.cwd}`, "info");
    },
  });
}

// extensions/orchestrator/index.ts
import { registerHello } from "./hello.js";

// inside the default export:
registerHello(pi);
```

This follows the same `registerCommand()` pattern used throughout `extensions/orchestrator/`. The handler receives raw arguments plus a live command context, including `ctx.cwd`, `ctx.hasUI`, and `ctx.ui.notify()`. Use this when the command needs to inspect session state or talk to the UI directly.

- Put orchestrator commands under `extensions/orchestrator/` so they can be wired into `extensions/orchestrator/index.ts`.
- For larger delegated workflows, see [Managing Custom Agents](managing-custom-agents.html) for details.

## Add static argument completions to an extension command
Use this when an extension command has a small fixed set of subcommands or modes.

```typescript
// extensions/orchestrator/deploy-check.ts
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";

export function registerDeployCheck(pi: ExtensionAPI): void {
  pi.registerCommand("deploy-check", {
    description: "Run a quick pre-deploy checklist",
    getArgumentCompletions: (prefix: string) => {
      const items = [
        { value: "staging", label: "staging", description: "Check staging settings" },
        { value: "prod", label: "prod", description: "Check production settings" },
      ];
      return items.filter((item) =>
        item.value.startsWith(prefix.toLowerCase()),
      );
    },
    handler: async (args, ctx) => {
      const target = (args?.trim() || "staging").toLowerCase();

      if (!ctx.hasUI) return;

      ctx.ui.notify(`Running deploy checks for ${target}...`, "info");
    },
  });
}

// extensions/orchestrator/index.ts
import { registerDeployCheck } from "./deploy-check.js";

// inside the default export:
registerDeployCheck(pi);
```

This matches the built-in pattern used by commands like `/pidash` and `/pidiff`. `getArgumentCompletions()` is the shortest path when your choices are known up front and do not require fetching repository data.

- Prefer this over editing the shared autocomplete layer when the command is a normal extension command.
- For daemon-backed or long-running handlers, see [Daemon & Websocket Networking](daemon-and-websockets.html) for details.

## Add issue-number autocomplete to a prompt command
Use this when a prompt template should tab-complete dynamic values like open issues, PRs, branches, or models.

```text
# prompts/pick-issue.md
---
description: "Summarize an open issue by number"
argument-hint: "[issue number]"
---

## Raw Arguments

```text
$ARGUMENTS
```

Summarize issue #$ARGUMENTS in 5 bullets:
- problem
- current scope
- missing acceptance criteria
- likely owner
- first implementation step

# extensions/orchestrator/extended-autocomplete.ts

// 1) Add a completion entry inside the `completions` record
"pick-issue": (prefix: string) => {
  void ctx.fetchOpenIssues(ctx.lastCwd);
  return ctx.issueCache.data
    ? filter(ctx.issueCache.data, prefix.replace(/^#/, ""))
    : null;
},

// 2) Add the command name to `promptTemplateCommands`
const promptTemplateCommands = new Set([
  "external-ai",
  "pr-review",
  "issue-review",
  "coderabbit-rate-limit",
  "review-local",
  "release",
  "review-handler",
  "cron",
  "create-skill",
  "create-coms-feature-manager",
  "pick-issue",
]);
```

Prompt templates are registered by Pi itself, so they do not go through the `registerCommand()` wrapper used for extension commands. That is why prompt-template autocomplete needs both pieces: a `completions` entry and inclusion in `promptTemplateCommands`.

> **Note:** `extensions/orchestrator/extended-autocomplete.ts` already includes cached fetchers such as `ctx.fetchOpenIssues(ctx.lastCwd)`, so follow that pattern instead of building a second autocomplete system.

- For PR numbers, mirror the existing `/pr-review` pattern.
- For branch names, mirror the existing `/review-local` pattern.
- For model/provider completions, see [External AI Agents & CLI](external-ai-agents.html) for details.

## Related Pages

- [Built-in Workflow Commands](built-in-workflows.html)
- [Managing Custom Agents](managing-custom-agents.html)
- [Asking Side Questions with /btw](btw-command.html)
- [Implementing Command Guards](safety-enforcements.html)
- [Installation & Quickstart](quickstart.html)

---

Source: safety-enforcements.md

# Implementing Command Guards

Use these recipes to create code-enforced memories with `memory_add(...)`. For how memories are stored and managed over time, see [Curating Project Memory](curating-project-memory.html). For automated review-loop commit blocking, see [Automating Code Reviews](automating-code-reviews.html).

> **Note:** These recipes document project-specific guards you add yourself. For the full settings reference, see [Configuration & Settings](configuration.html).

## Block a destructive cleanup command

Use this when you want Pi to reject a specific bash command before it runs.

```text
memory_add(
  text="Never prune Docker images or containers from this repo",
  category="lesson",
  trigger="bash_contains docker system prune",
  action="block"
)
```

This creates a hard block on any `bash` tool call containing `docker system prune`. Use `bash_contains` when the command is easy to match with a plain substring and you do not need regex flexibility.

- Good fits: `terraform destroy`, `kubectl delete namespace`, `aws s3 rm --recursive`
- Keep one rule per dangerous command so the reason stays obvious

## Block force-pushes with a regex

Use this when a command has multiple spellings and you need one rule to catch all of them.

```text
memory_add(
  text="Never force-push from this repo",
  category="lesson",
  trigger="bash_regex git\\s+push\\s+--force(?:-with-lease)?\\b",
  action="block"
)
```

This blocks both `git push --force` and `git push --force-with-lease`. Use `bash_regex` when a simple substring is too broad or would miss important variants.

> **Tip:** Keep regexes short and targeted. This matcher is for command guards, not full shell parsing.

## Warn when editing secret files

Use this when a file should stay editable, but every edit deserves an extra reminder.

```text
memory_add(
  text="Check for secrets before saving .env files",
  category="pattern",
  trigger="file_modified *.env",
  action="warn"
)
```

This appends a warning when Pi uses `write` or `edit` on a matching file path. Use `warn` for sensitive files that sometimes need changes but should never be edited casually.

- `file_modified *.py` matches by extension
- `file_modified Dockerfile` matches by path substring

## Require approval before merging a PR

Use this when a command is allowed only after another tool has run in the same turn.

```text
memory_add(
  text="Always get explicit approval before merging a pull request",
  category="pattern",
  verifier="tool_called ask_user before gh pr merge"
)
```

This adds a semantic verifier instead of a trigger/action pair. If Pi runs `gh pr merge` without calling `ask_user` first, the turn is flagged as a violation.

> **Note:** This checks ordering inside a single turn: `ask_user` must happen before the merge command.

## Make the repo read-only for direct edits

Use this when you want Pi to inspect and plan, but never modify files directly.

```text
memory_add(
  text="Do not write files directly in this repo",
  category="preference",
  trigger="tool_name write",
  action="block"
)

memory_add(
  text="Do not edit files directly in this repo",
  category="preference",
  trigger="tool_name edit",
  action="block"
)
```

These rules block tool calls by exact tool name rather than by shell text. Use `tool_name` when the risky behavior is the tool itself, not a particular bash command.

- Common targets: `write`, `edit`, or other project-specific tools
- For agent-level workflow controls, see [Managing Custom Agents](managing-custom-agents.html)

## Run one safe follow-up command after a file edit

Use this when you want Pi to perform a tightly controlled check right after a successful change.

```text
# In your shell before starting pi
export PI_ENFORCEMENT_ALLOWED_COMMANDS="git diff -- Dockerfile"

# In pi chat
memory_add(
  text="Show the Dockerfile diff after every edit",
  category="pattern",
  trigger="file_modified Dockerfile",
  action="run_after git diff -- Dockerfile"
)
```

This runs `git diff -- Dockerfile` immediately after a matching `write` or `edit` succeeds, and only because the command is allowlisted exactly. Use this for short, deterministic checks that you want attached to a specific kind of edit.

> **Warning:** Allowlist entries must match exactly, character for character.

- To allow more than one follow-up command, use a colon-separated list:
  `export PI_ENFORCEMENT_ALLOWED_COMMANDS="git diff -- Dockerfile:git status --short"`
- Keep follow-up commands fast so they do not slow down normal edits

## Related Pages

- [Curating Project Memory](curating-project-memory.html)
- [Memory Architecture](memory-architecture.html)
- [Configuration & Settings](configuration.html)
- [Automating Code Reviews](automating-code-reviews.html)
- [Managing Custom Agents](managing-custom-agents.html)

---

Source: cli-reference.md

# Default grouping (by source)
myk-pi-tools db stats

# Group by reviewer and print JSON
myk-pi-tools db stats --by-reviewer --json
```

**Return value / effect**

- Prints a table or JSON array to stdout.
- Exits with an error if both `--by-source` and `--by-reviewer` are passed.

### `db patterns`

Finds recurring dismissed comment patterns grouped by file path and body similarity.

| Option | Type | Default | Description |
|---|---|---|---|
| `--min` | Integer | `2` | Minimum occurrence count required for a pattern to be reported. |
| `--json` | Flag | `False` | Output JSON instead of a formatted table. |
| `--db-path` | String | auto-detected | Path to the SQLite database file. |

```bash
# Show recurring dismissed patterns with at least 3 occurrences
myk-pi-tools db patterns --min 3
```

**Return value / effect**

- Prints rows with `path`, `occurrences`, `reason`, and `body_sample`.
- Uses dismissed comments (`not_addressed` / `skipped`) as the source set.

### `db dismissed`

Returns stored dismissed comments for a repository.

| Option | Type | Default | Description |
|---|---|---|---|
| `--owner` | String | `(required)` | Repository owner or organization. |
| `--repo` | String | `(required)` | Repository name. |
| `--json` | Flag | `False` | Output JSON instead of a formatted table. |
| `--db-path` | String | auto-detected | Path to the SQLite database file. |

```bash
myk-pi-tools db dismissed --owner myk-org --repo pi-config --json
```

**Return value / effect**

- Prints dismissed review records for the specified repository.
- Returned rows include `path`, `line`, `body`, `status`, `reply`, `skip_reason`, `author`, `type`, and `comment_id`.
- Includes `not_addressed` and `skipped` comments, plus supported addressed body-comment/Qodo sticky types used for auto-skip logic.

### `db query`

Runs a read-only SQL query against the reviews database.

| Parameter / Option | Type | Default | Description |
|---|---|---|---|
| `sql` | String | `(required)` | SQL statement to execute. |
| `--json` | Flag | `False` | Output JSON instead of a formatted table. |
| `--db-path` | String | auto-detected | Path to the SQLite database file. |

> **Warning:** Only `SELECT` and `WITH` (CTE) queries are accepted. Multiple statements and write-oriented keywords are rejected.

```bash
# Count comments by status
myk-pi-tools db query "SELECT status, COUNT(*) AS cnt FROM comments GROUP BY status"

# JSON output
myk-pi-tools db query "SELECT path, line, status FROM comments LIMIT 5" --json
```

**Return value / effect**

- Prints query results as a table or JSON array.
- Returns an empty result set if the database is missing.
- Exits with an error for disallowed SQL.

### `db find-similar`

Reads a candidate comment from stdin and finds a previously dismissed comment with the same path and similar body text.

| Option | Type | Default | Description |
|---|---|---|---|
| `--owner` | String | `(required)` | Repository owner or organization. |
| `--repo` | String | `(required)` | Repository name. |
| `--threshold` | Float | `0.6` | Minimum similarity score from `0.0` to `1.0`. |
| `--json` | Flag | `False` | Output JSON instead of human-readable text. |
| `--db-path` | String | auto-detected | Path to the SQLite database file. |

```bash
echo '{"path":"src/main.py","body":"Add error handling for edge cases"}' | \
  myk-pi-tools db find-similar --owner myk-org --repo pi-config --json
```

**Return value / effect**

- Reads JSON from stdin with required keys `path` and `body`.
- Prints the best match or `null` / “No similar comment found”.
- Uses exact path match plus Jaccard word-overlap similarity.

## Review Handling (`myk-pi-tools reviews`)

### `reviews fetch`

Fetches review threads for the current PR and writes a normalized review JSON file.

| Parameter / Option | Type | Default | Description |
|---|---|---|---|
| `review_url` | String | `""` | Optional PR review URL or discussion URL for context. |
| `--include-resolved` | Flag | `False` | Include resolved threads in the output JSON. |
| `--user` | String | `None` | Filter threads by author username. |
| `--output-dir` | String | `(required)` | Directory for the output JSON file. |

```bash
myk-pi-tools reviews fetch --output-dir .pi/tmp/
```

**Return value / effect**

- Writes `<output-dir>/pr-<number>-reviews.json`.
- Prints the full normalized JSON payload to stdout.
- Output JSON contains `metadata`, `human`, `qodo`, and `coderabbit` arrays.

### `reviews poll`

Polls for new review activity until actionable feedback or approval is detected.

| Parameter / Option | Type | Default | Description |
|---|---|---|---|
| `review_url` | String | `""` | Optional PR review URL or discussion URL for context. |
| `--source` | String | `coderabbit` | Reviewer source to poll: `coderabbit` or `qodo`. |
| `--output-dir` | String | `(required)` | Directory for the review JSON file. |

```bash
# Poll CodeRabbit
myk-pi-tools reviews poll --output-dir .pi/tmp/

# Poll Qodo
myk-pi-tools reviews poll --source qodo --output-dir .pi/tmp/
```

**Return value / effect**

- Loops until one of these conditions is met:
  - approval is detected, or
  - actionable comments are available.
- Updates `<output-dir>/pr-<number>-reviews.json`.
- Prints a JSON object to stdout with review data and an `approved` flag when it returns.
- For `coderabbit`, handles rate-limit and paused-review recovery internally.
- For `qodo`, retries stuck reviews and may request sticky-comment re-evaluation before returning.

### `reviews post`

Posts replies and resolves review threads from a processed reviews JSON file.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `json_path` | String | `(required)` | Path to the processed review JSON file. |

```bash
myk-pi-tools reviews post .pi/tmp/pr-42-reviews.json
```

**Return value / effect**

- Reads the JSON produced by `reviews fetch`.
- Posts replies for processed entries and resolves eligible threads.
- Updates the JSON file with posting and resolution timestamps.
- Exits with an error if required replies are empty or too vague.

> **Note:** Qodo sticky findings are code-enforced to require status `addressed`. Non-`addressed` sticky entries are rejected.

### `reviews pending-fetch`

Fetches the authenticated user’s pending PR review and writes a pending-review JSON file.

| Parameter / Option | Type | Default | Description |
|---|---|---|---|
| `pr_url` | String | `(required)` | GitHub PR URL. |
| `--output-dir` | String | `(required)` | Directory for the output JSON file. |

```bash
myk-pi-tools reviews pending-fetch "https://github.com/owner/repo/pull/123" --output-dir .pi/tmp/
```

**Return value / effect**

- Writes `<output-dir>/pr-<owner>-<repo>-<number>-pending-review.json`.
- Prints the saved file path to stdout.
- Output JSON contains `metadata`, `comments`, and `diff`.

### `reviews pending-update`

Updates accepted pending-review comment bodies, and optionally submits the review.

| Parameter / Option | Type | Default | Description |
|---|---|---|---|
| `json_path` | String | `(required)` | Path to the pending-review JSON file. |
| `--submit` | Flag | `False` | Submit the review after updating comments. Submission only occurs if the JSON metadata also includes a valid `submit_action`. |

```bash
myk-pi-tools reviews pending-update .pi/tmp/pr-owner-repo-123-pending-review.json --submit
```

**Return value / effect**

- Updates comments whose status is `accepted` and that include `refined_body`.
- Backfills missing `node_id` values from the GitHub API before applying updates.
- If both `--submit` and JSON metadata `submit_action` are present, submits the review with `COMMENT`, `APPROVE`, or `REQUEST_CHANGES`.
- Exits with an error if required `node_id` values cannot be resolved.

### `reviews status`

Shows stored review status for a PR and generates an HTML report.

| Option | Type | Default | Description |
|---|---|---|---|
| `--pr` | Integer | auto-detect | PR number. If omitted, the command tries to detect the current branch’s PR. |
| `--output-dir` | String | `(required)` | Directory for the generated HTML report. |

```bash
myk-pi-tools reviews status --output-dir .pi/reports/

myk-pi-tools reviews status --pr 42 --output-dir .pi/reports/
```

**Return value / effect**

- Writes `<output-dir>/review-status-<pr>.html`.
- Prints a terminal table and the HTML report location.
- If no PR can be auto-detected and `--pr` is omitted, lists PRs present in the local reviews database instead of generating a report.

> **Tip:** Use this command against stored review data after `reviews store`. See [Automating Code Reviews](automating-code-reviews.html) for review-loop usage.

### `reviews ask-qodo`

Posts a `/qodo` comment on a PR and waits for a matching reply.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `args` | String | `(required)` | Question text, or `--pr owner/repo <pr_number> <question>` to target a specific PR. |

```bash
# Auto-detect the current PR
myk-pi-tools reviews ask-qodo "What edge cases are missing?"

# Target a specific PR
myk-pi-tools reviews ask-qodo --pr myk-org/pi-config 42 "What edge cases are missing?"
```

**Return value / effect**

- Prints Qodo’s reply body to stdout.
- Auto-detects the current PR if `--pr` is not provided.
- Exits with code `1` if the question is empty, the post fails, or no matching reply arrives before timeout.

### `reviews store`

Stores a completed review JSON payload in the local SQLite database.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `json_path` | String | `(required)` | Path to the completed review JSON file. |

```bash
myk-pi-tools reviews store .pi/tmp/pr-42-reviews.json
```

**Return value / effect**

- Stores review metadata and comment rows in `<project-root>/.pi/data/reviews.db`.
- Anchors the stored review to the current commit SHA.
- Deletes the source JSON file after successful storage.

## Memory Management (`myk-pi-tools memory`)

### Global Option (`memory` group)

This option applies to every `memory` subcommand.

| Option | Type | Default | Description |
|---|---|---|---|
| `--file-path` | String | auto-detected | Path to the memory topics directory. If omitted, uses `<git-root>/.pi/memory/topics/`. |

```bash
myk-pi-tools memory --file-path /tmp/topics show
```

**Return value / effect**

- Overrides the default per-repository topics directory for the current invocation.

### `memory add`

Adds a memory entry to a topic file.

| Option | Type | Default | Description |
|---|---|---|---|
| `--category`, `-c` | String | `(required)` | Memory category: `lesson`, `decision`, `mistake`, `pattern`, `done`, or `preference`. |
| `--summary`, `-s` | String | `(required)` | One-line memory text. |
| `--pinned` | Flag | `False` | Store the entry as pinned. |

```bash
# Learned memory
myk-pi-tools memory add -c lesson -s "Cache mounts need uid"

# Pinned memory
myk-pi-tools memory add -c preference -s "Always use uv run" --pinned
```

**Return value / effect**

- Appends a markdown entry to the category’s topic file.
- Category-to-file mapping:
  - `preference` → `preferences.md`
  - `lesson` → `lessons.md`
  - `pattern` → `patterns.md`
  - `decision` → `decisions.md`
  - `done` → `completions.md`
  - `mistake` → `mistakes.md`
- Pinned entries are written with `*(pinned)*`.

### `memory show`

Prints all topic files as merged markdown.

| Option | Type | Default | Description |
|---|---|---|---|
| `--file-path` | String | auto-detected | Global `memory` option. |

```bash
myk-pi-tools memory show
```

**Return value / effect**

- Prints merged topic-file contents to stdout.
- Reads all `*.md` files from the topics directory in filename order.

### `memory migrate`

Migrates legacy SQLite memory data into topic files.

| Option | Type | Default | Description |
|---|---|---|---|
| `--file-path` | String | auto-detected | Global `memory` option. |

```bash
myk-pi-tools memory migrate
```

**Return value / effect**

- Reads `memories.db` from the parent memory directory.
- Writes migrated entries as learned topic-file entries.
- Deletes legacy files after migration: `memories.db`, `dreams.md`, and `dreams.lock`.
- Prints a migration summary to stderr.

### `memory forget`

Removes a matching memory entry.

| Option | Type | Default | Description |
|---|---|---|---|
| `--category`, `-c` | String | `(required)` | Memory category. |
| `--summary`, `-s` | String | `(required)` | Exact entry text to remove. |
| `--file-path` | String | auto-detected | Global `memory` option. |

```bash
myk-pi-tools memory forget -c mistake -s "Used sleep for polling"
```

**Return value / effect**

- Removes the matching learned or pinned line from the category topic file.
- Removes the matching entry hash from `memory-scores.json` if present.
- Prints either `Forgotten: ...` or `Not found: ...`.

### `memory path`

Prints the active memory topics directory.

| Option | Type | Default | Description |
|---|---|---|---|
| `--file-path` | String | auto-detected | Global `memory` option. |

```bash
myk-pi-tools memory path
```

**Return value / effect**

- Prints the absolute path to the active topics directory.

### `memory status`

Prints the memory enforcement-honesty inventory.

| Option | Type | Default | Description |
|---|---|---|---|
| `--file-path` | String | auto-detected | Global `memory` option. |

```bash
myk-pi-tools memory status
```

**Return value / effect**

- Prints:
  - active topics path,
  - injected topic-entry count,
  - code-tier enforced-entry count,
  - proposed promotion-candidate count.
- Lists code-tier entries when present.
- Reads `memory-scores.json` and `promotions.md` from the parent memory directory.

> **Tip:** See [Implementing Command Guards](safety-enforcements.html) for enforcement behavior and [Memory Architecture](memory-architecture.html) for storage and scoring details.

## Related Pages

- [Built-in Workflow Commands](built-in-workflows.html)
- [Automating Code Reviews](automating-code-reviews.html)
- [Curating Project Memory](curating-project-memory.html)
- [Configuration & Settings](configuration.html)

---

Source: configuration.md

# Configuration & Settings

## Settings Files

### Project settings file

| Parameter | Type | Default | Description | Effect |
|---|---|---|---|---|
| Path | string | none | `.pi/pi-config-settings.json` in the repository root. | Highest-precedence settings source for the current project. |

```json
{
  "pidash_enable": true,
  "pidash_port": 19190,
  "cli_agents": ["claude", "cursor"]
}
```

### Global settings file

| Parameter | Type | Default | Description | Effect |
|---|---|---|---|---|
| Path | string | none | `~/.pi/pi-config-settings.json` in the current user's home directory. | Fallback settings source for all projects when a key is not set in the project file. |

```json
{
  "dream_interval_hours": 6,
  "review_loop_enforcement": true
}
```

### Resolution order

| Parameter | Type | Default | Description | Effect |
|---|---|---|---|---|
| Resolution order | ordered list | built-in default last | Settings resolve in this order: project file → global file → environment variable → default. | Determines which value is used when the same key appears in multiple places. |

```text
project file -> global file -> environment variable -> default
```

## Git & Workflow Keys

### `commit_trailer`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `commit_trailer` | string or `false` | `false` | `PI_COMMIT_TRAILER` | Commit trailer name, or a comma-separated list of trailer names. | Injects a trailer into `git commit` commands when a string value is set. |

> **Note:** A comma-separated string such as `"Assisted-by, Co-authored-by"` is treated as a selectable list of trailer names.

```json
{
  "commit_trailer": "Assisted-by"
}
```

```bash
export PI_COMMIT_TRAILER="Assisted-by"
```

### `allow_push_to_protected_branches`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `allow_push_to_protected_branches` | boolean | `false` | `PI_ALLOW_PUSH_TO_PROTECTED_BRANCHES` | Allows commits and pushes to protected branches. | Disables the protected-branch block in git enforcement. |

```json
{
  "allow_push_to_protected_branches": true
}
```

```bash
export PI_ALLOW_PUSH_TO_PROTECTED_BRANCHES=true
```

### `use_worktrees`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `use_worktrees` | boolean | `false` | `PI_USE_WORKTREES` | Forces worktree-only branch workflows. | Blocks branch-changing `git checkout` and `git switch` commands in the main worktree. |

```json
{
  "use_worktrees": true
}
```

```bash
export PI_USE_WORKTREES=true
```

### `dco`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `dco` | boolean | `false` | `PI_DCO` | Enables Developer Certificate of Origin signing. | Adds `--signoff` to `git commit` when the flag is not already present. |

```json
{
  "dco": true
}
```

```bash
export PI_DCO=true
```

### `orchestrator_edit_write_block`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `orchestrator_edit_write_block` | boolean | `false` | none | Blocks the top-level orchestrator from calling `edit` and `write` directly. | File changes must be delegated through the `subagent` tool. Subagent child processes are not blocked by this key. |

```json
{
  "orchestrator_edit_write_block": true
}
```

See [Managing Custom Agents](managing-custom-agents.html) for details.

## Review & PR Keys

### `comment_signature`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `comment_signature` | boolean | `false` | none | Enables an AI signature on PR comments posted through the review CLI helpers. | Sets the `PI_COMMENT_SIGNATURE` process variable on session start for review tooling to read. |

```json
{
  "comment_signature": true
}
```

See [Automating Code Reviews](automating-code-reviews.html) for details.

### `review_loop_enforcement`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `review_loop_enforcement` | boolean | `false` | `PI_REVIEW_LOOP_ENFORCEMENT` | Enables review-loop enforcement. | Blocks `git commit` until the review state is clean and tests have passed. Also enables the review status UI slot. |

```json
{
  "review_loop_enforcement": true
}
```

```bash
export PI_REVIEW_LOOP_ENFORCEMENT=true
```

See [Automating Code Reviews](automating-code-reviews.html) for details.

### `review_loop_max_cycles`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `review_loop_max_cycles` | integer `1`-`10` | `3` | `PI_REVIEW_LOOP_MAX_CYCLES` | Maximum number of review-loop cycles when review-loop enforcement is enabled. | Caps reviewer re-dispatch count. Invalid values fall through to the next resolution layer or the default. |

> **Warning:** Accepted values are integers `1` through `10`, or digit strings `"1"` through `"10"`. Values such as `0`, `11`, `"01"`, `"10.0"`, `"1e1"`, and `"inf"` are ignored.

```json
{
  "review_loop_max_cycles": 5
}
```

```bash
export PI_REVIEW_LOOP_MAX_CYCLES=7
```

See [Automating Code Reviews](automating-code-reviews.html) for details.

## Provider Registration & Model Routing

### `cli_agents`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `cli_agents` | string or string[] | `[]` | `CLI_AGENTS` | List of CLI-backed agent names. | Registers `cli-<agent>` providers in the unified provider extension. |

| Supported built-in value | Registered provider ID |
|---|---|
| `claude` | `cli-claude` |
| `gemini` | `cli-gemini` |
| `cursor` | `cli-cursor` |

> **Note:** Values are lowercased, deduplicated, and invalid names are filtered out.


> **Tip:** Set an explicit empty array (`[]`) in the project file to override inherited global or environment values.

```json
{
  "cli_agents": ["claude", "cursor"]
}
```

```bash
export CLI_AGENTS="Cursor,Gemini"
```

See [External AI Agents & CLI](external-ai-agents.html) for details.

### `acpx_agents`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `acpx_agents` | string or string[] | `[]` | `ACPX_AGENTS` | List of ACPX agent names to register. | Registers `acpx-<agent>` providers in the unified provider extension. |

> **Note:** Values are lowercased, deduplicated, and invalid names are filtered out.

```json
{
  "acpx_agents": ["cursor"]
}
```

```bash
export ACPX_AGENTS="cursor,claude"
```

See [ACPX Provider Integration](acpx-provider.html) for details.

### `agent_provider`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `agent_provider` | string | `""` | none | Default provider for subagents. | Used when a per-agent override and agent frontmatter do not provide a provider. |

```json
{
  "agent_provider": "cli-cursor"
}
```

### `agent_model`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `agent_model` | string | `""` | none | Default model ID for subagents. | Used when a per-agent override and agent frontmatter do not provide a model. |

```json
{
  "agent_model": "cursor:cursor-grok-4.5-high-fast"
}
```

### `agent_overrides`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `agent_overrides` | object | `{}` | none | Per-agent provider/model overrides. | Highest-precedence settings layer for subagent model routing. |
| `provider` | string or `null` | inherited | none | Provider override for a named agent. | `null` means inherit the parent provider directly. |
| `model` | string or `null` | inherited | none | Model override for a named agent. | `null` means inherit the parent model directly. |

| Resolution priority | Source |
|---|---|
| 1 | `agent_overrides[name]` |
| 2 | Agent frontmatter (`provider`, `model`) |
| 3 | `agent_provider` / `agent_model` |
| 4 | Parent session provider/model |

```json
{
  "agent_provider": "cli-cursor",
  "agent_model": "cursor:cursor-grok-4.5-high-fast",
  "agent_overrides": {
    "debugger": {
      "provider": null,
      "model": null
    },
    "reviewer": {
      "provider": "cli-claude",
      "model": "claude-sonnet"
    }
  }
}
```

See [Managing Custom Agents](managing-custom-agents.html) for details.

## Dashboard & UI Keys

### `pidash_enable`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `pidash_enable` | boolean | `true` | `PI_PIDASH_ENABLE` | Enables the global dashboard extension. | Controls `/pidash` availability and daemon connection behavior. |

> **Note:** Environment values `false`, `0`, `no`, and `off` disable pidash.

```json
{
  "pidash_enable": false
}
```

```bash
export PI_PIDASH_ENABLE=false
```

### `pidiff_enable`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `pidiff_enable` | boolean | `true` | `PI_PIDIFF_ENABLE` | Enables the diff viewer extension. | Controls `/pidiff` availability and per-project diff server startup. |

> **Note:** Environment values `false`, `0`, `no`, and `off` disable pidiff.

```json
{
  "pidiff_enable": false
}
```

```bash
export PI_PIDIFF_ENABLE=off
```

### `pidash_port`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `pidash_port` | integer | `19190` | `PI_PIDASH_PORT` | HTTP and WebSocket port for pidash. | Sets the port used when spawning or reconnecting to the pidash daemon. |

> **Warning:** Only integer values in the range `1`-`65535` are accepted. Invalid values fall back to the next resolution layer or the default.

```json
{
  "pidash_port": 19191
}
```

```bash
export PI_PIDASH_PORT=19191
```

See [Using the Web Dashboard](using-the-web-dashboard.html) for details.

### `image_model`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `image_model` | string | `""` | `PI_IMAGE_MODEL` | Gemini image generation model name. | Enables the `generate_image` tool to call the Gemini API. |

```json
{
  "image_model": "gemini-3-pro-image"
}
```

```bash
export PI_IMAGE_MODEL=gemini-3-pro-image
```

See [Image Generation](image-generation.html) for details.

## Background & Async Keys

### `dream_interval_hours`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `dream_interval_hours` | number | `3` | `PI_DREAM_INTERVAL_HOURS` | Interval between automatic dream passes. | Controls how frequently background memory consolidation is scheduled. |

```json
{
  "dream_interval_hours": 6
}
```

```bash
export PI_DREAM_INTERVAL_HOURS=6
```

See [Background Memory Consolidation (Dreaming)](background-dreaming.html) for details.

### `async_llm_provider`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `async_llm_provider` | string | `""` | `PI_ASYNC_LLM_PROVIDER` | Provider to use for must-async LLM work when a sidecar provider/model pair is required. | Combined with `async_llm_model` to define the sidecar provider. |

```json
{
  "async_llm_provider": "openai"
}
```

```bash
export PI_ASYNC_LLM_PROVIDER=openai
```

### `async_llm_model`

| Parameter | Type | Default | Environment variable | Description | Effect |
|---|---|---|---|---|---|
| `async_llm_model` | string | `""` | `PI_ASYNC_LLM_MODEL` | Model ID paired with `async_llm_provider` for must-async LLM work. | Combined with `async_llm_provider` to define the sidecar model. |

```json
{
  "async_llm_model": "gpt-5.4"
}
```

```bash
export PI_ASYNC_LLM_MODEL=gpt-5.4
```

> **Warning:** Both `async_llm_provider` and `async_llm_model` must be set together. If either is missing, must-async work is skipped.


> **Warning:** `async_llm_provider` cannot be an `acpx-*` provider ID.

See [Running Background Agents and Scheduled Tasks](async-agents-and-cron.html) for details.

## Environment-only Process Flag

### `PI_SUBAGENT_CHILD`

| Parameter | Type | Default | Description | Effect |
|---|---|---|---|---|
| `PI_SUBAGENT_CHILD` | string | unset | Process flag set to `"1"` for subagent child processes. | Disables parent-session-only startup behavior such as pidash/pidiff registration and settings cache reset hooks in child processes. |

```bash
PI_SUBAGENT_CHILD=1
```

See [Running Background Agents and Scheduled Tasks](async-agents-and-cron.html) for details.

## Related Pages

- [Installation & Quickstart](quickstart.html)
- [Daemon & Websocket Networking](daemon-and-websockets.html)
- [Curating Project Memory](curating-project-memory.html)
- [Running Background Agents and Scheduled Tasks](async-agents-and-cron.html)
- [ACPX Provider Integration](acpx-provider.html)

---

Source: built-in-workflows.md

# Built-in Workflow Commands

Built-in workflow commands cover implementation, review, release, memory, and glossary tasks. Text after the command name is passed to the command as its raw arguments.

> **Note:** Commands that invoke `myk-pi-tools` require `uv` and `myk-pi-tools` on PATH. See [myk_pi_tools CLI Reference](cli-reference.html) for the underlying CLI commands.

## Implementation Commands

### `/implement`

Runs a three-stage workflow: scout the codebase, build an implementation plan, then apply the code changes.

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `<task>` | string | (required) | Task description to explore, plan, and implement. |

**Return value / effect:** Produces a scoped code exploration summary, an implementation plan, and the resulting code changes.

```text
/implement Add rate limiting to the login endpoint
```

### `/implement-and-review`

Implements a task, runs parallel review passes, then performs a follow-up fix pass.

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `<task>` | string | (required) | Task description for implementation and review. |

| Review pass | Purpose |
| :--- | :--- |
| Quality | Code quality review |
| Guidelines | Project guideline adherence |
| Security | Bug and security review |
| Docs | Documentation review |
| Spec | Specification compliance review |

**Return value / effect:** Applies the requested changes, gathers review feedback from parallel review passes, then fixes the reported issues.

```text
/implement-and-review Refactor the settings loader to support env overrides
```

> **Tip:** For planning without code changes, see [Installation & Quickstart](quickstart.html) for `/scout-and-plan`.

## Review Commands

### `/issue-review`

Reviews a GitHub issue for spec quality, feasibility, and scope, then updates the issue body after user approval.

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `[issue number or URL]` | string | auto-detect | Issue number, full GitHub issue URL, or empty to auto-detect. |

| Auto-detect source | Order | Description |
| :--- | :--- | :--- |
| Branch name | 1 | Extracts issue numbers from branch names such as `feat/issue-42-...` or `fix/issue-42-...` |
| Assigned issues | 2 | Uses `gh issue list --assignee @me --state open --limit 1` |
| User input | 3 | Prompts the user if nothing is detected |

**Prerequisites:** GitHub CLI (`gh`) authenticated and repository access available.

**Return value / effect:** Creates a multi-step review plan, gathers issue context, reviews the issue from multiple angles, and edits the issue body with the approved fixes.

```text
/issue-review
/issue-review 42
/issue-review https://github.com/owner/repo/issues/42
```

### `/pr-review`

Reviews a GitHub pull request, presents findings, posts selected inline comments, and stores the posted comments.

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `[PR number or URL]` | string | current branch PR | Pull request number, full GitHub PR URL, or empty to auto-detect from the current branch. |

**Prerequisites:** `uv`, `myk-pi-tools`, and `gh`.

**Return value / effect:** Clones the PR, runs review passes for quality, guidelines, security, docs, and spec, then posts selected findings and stores them for later review-cycle tracking.

```text
/pr-review
/pr-review 123
/pr-review https://github.com/owner/repo/pull/123
```

### `/review-local`

Reviews local changes without posting to GitHub.

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `[base branch]` | string | staged + unstaged changes | Base branch to diff against. If omitted, reviews local staged and unstaged changes. |

**Return value / effect:** Builds a diff, runs the same five review passes used by `/pr-review`, merges findings, and presents them in the session.

```text
/review-local
/review-local main
/review-local feature/other-branch
```

### `/refine-review`

Fetches the user’s pending GitHub review comments, refines them, and optionally submits the review.

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `<PR URL>` | string | (required) | Full GitHub pull request URL containing the pending review. |
| `[instructions]` | string | empty | Optional refinement guidance included in the command arguments. |

**Prerequisites:** `uv`, `myk-pi-tools`, and an existing pending review on the target PR.

**Return value / effect:** Fetches pending comments, generates refined versions, lets the user accept or edit them, updates the review JSON, and optionally submits the review.

```text
/refine-review https://github.com/owner/repo/pull/123
```

See [Automating Code Reviews](automating-code-reviews.html) for automated PR review workflows.

## Release Command

### `/release`

Creates a GitHub release from commit history and optionally updates version files before creating the release.

| Parameter / Option | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `[version]` | string | auto-detect bump | Explicit version to release. If omitted, the workflow derives the release level from commit history. |
| `--dry-run` | flag | off | Previews the release workflow without creating the release. |
| `--prerelease` | flag | off | Marks the release as a prerelease. |
| `--draft` | flag | off | Creates the release as a draft. |
| `--target <branch>` | string | current or auto-detected target | Target branch to validate and release from. |
| `--tag-match <pattern>` | string | none | Tag glob used during release info lookup. |

**Prerequisites:** `myk-pi-tools`, a clean working tree, the correct target branch, and a synced remote.

**Return value / effect:** Validates release state, inspects version files, builds a PR-grouped changelog, optionally bumps versions, and creates the GitHub release.

```text
/release
/release 1.17.1
/release --dry-run
/release --prerelease
/release --draft
```

## Memory And Skill Commands

### `/remember`

Stores a pinned project memory.

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `<what to remember>` | string | (required) | Memory content to store. |

| Category | Meaning |
| :--- | :--- |
| `lesson` | Something learned |
| `decision` | Design or architecture choice |
| `mistake` | Something to avoid repeating |
| `pattern` | Recurring convention or approach |
| `done` | Completed task or milestone |
| `preference` | User preference |

**Return value / effect:** Chooses a category, runs the memory add workflow with `--pinned`, and confirms the saved memory.

```text
/remember Prefer uv over pip for all Python installs in this repo
```

See [Curating Project Memory](curating-project-memory.html) for memory storage and organization.

### `/create-skill`

Creates a reusable skill from the current conversation.

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `[name]` | string | prompt if empty | Skill name. Must be lowercase, hyphenated, start with a letter, and be at most 64 characters. |
| Scope | choice | ask user | Where to create the skill: **Global** or **Project**. |

| Scope | Location | Effect |
| :--- | :--- | :--- |
| Global | `~/.agents/skills/<name>/SKILL.md` | Skill is available across projects |
| Project | `.pi/skills/<name>/SKILL.md` | Skill is available only in the current project |

**Return value / effect:** Extracts the completed workflow from the current conversation and writes a `SKILL.md` file with frontmatter and step-by-step instructions.

```text
/create-skill debug-container-build
/create-skill
```

See [Managing Custom Agents](managing-custom-agents.html) for related customization patterns.

## Glossary Command

### `/domain-model`

Scans code identifiers and drafts or updates a root `CONTEXT.md` glossary.

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `[focus area]` | string | full codebase | Optional focus area to narrow the glossary scan. |

**Return value / effect:** Identifies project-specific terms from code, compares them with any existing `CONTEXT.md`, and presents a draft glossary for approval or editing.

```text
/domain-model
/domain-model providers and sessions
```

## Review Database Command

### `/query-db`

Runs review analytics queries through `myk-pi-tools db`.

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `<query>` | string | (required for useful output) | Database subcommand and flags after `db`. |

**Prerequisites:** `uv` and `myk-pi-tools`.

| Example query | Effect |
| :--- | :--- |
| `stats --by-source` | Shows stats grouped by review source |
| `stats --by-reviewer` | Shows stats grouped by reviewer author |
| `patterns --min 2` | Shows recurring dismissed patterns |
| `dismissed --owner X --repo Y` | Lists dismissed comments for a repository |
| `query "SELECT * FROM comments WHERE status='skipped' LIMIT 10"` | Runs a custom SELECT query |

**Return value / effect:** Executes the requested review database query and prints the results using the underlying CLI behavior.

```text
/query-db stats --by-source
/query-db patterns --min 2
/query-db dismissed --owner myk-org --repo pi-config
/query-db query "SELECT * FROM comments WHERE status='skipped' LIMIT 10"
```

## Shared Command Behavior

### Argument Handling

| Behavior | Effect |
| :--- | :--- |
| Text after command name | Passed to the command as raw arguments |
| Empty required argument | Command aborts or prompts, depending on the workflow |
| Interactive choice needed | Command asks the user through the Pi UI |

### Side Effects

| Command area | Possible side effect |
| :--- | :--- |
| Implementation | Code changes |
| Issue and PR review | GitHub issue or PR updates |
| Release | Version bumps, changelog generation, GitHub release creation |
| Memory and skill | Memory entries or `SKILL.md` files |
| Glossary | `CONTEXT.md` creation or update |

> **Warning:** Commands that mutate GitHub state, create releases, or write project files may prompt for confirmation before final actions.

See [Creating Slash Commands](custom-slash-commands.html) for custom prompt commands and [myk_pi_tools CLI Reference](cli-reference.html) for the underlying CLI details.

## Related Pages

- [Creating Slash Commands](custom-slash-commands.html)
- [myk_pi_tools CLI Reference](cli-reference.html)
- [Automating Code Reviews](automating-code-reviews.html)
- [Managing Custom Agents](managing-custom-agents.html)
- [Curating Project Memory](curating-project-memory.html)

---

Source: memory-architecture.md

# Memory Architecture

Pi’s memory architecture is the layer that turns one-off conversations into durable project knowledge. It matters because it decides what the agent remembers, what it forgets, what gets surfaced automatically in future turns, and when repeated lessons become stronger guardrails instead of loose suggestions.

The practical result is simple: you spend less time repeating project conventions, and the agent gets better at bringing the right context back at the right moment.

## The Big Picture

The memory system is local, file-backed, and layered. Topic files are the source of truth; everything else is derived from or built around them.

| Layer | What it stores | Where it lives | Why it matters |
|---|---|---|---|
| Topic files | Human-readable memory entries by category | `.pi/memory/topics/*.md` | This is the canonical memory content. |
| Score index | Stability score, evidence count, lifecycle, enforcement metadata | `.pi/memory/memory-scores.json` | Decides which memories stay active and which fade out. |
| Embedding store | Local vectors for semantic matching | `.pi/memory/embeddings.json` | Lets Pi retrieve related memories even when the wording changes. |
| Situation report | Token-budgeted summary for prompt injection | Built at runtime | Determines what memory the agent actually sees during a turn. |
| Promotion queue | Candidates for skills, enforcement, or project rules | `.pi/memory/promotions.md` | Captures repeated patterns that may deserve stronger structure. |
| Provenance sidecar | Source-session metadata waiting to be merged | `.pi/memory/provenance-pending.json` | Preserves where a memory came from without letting background jobs edit the score file directly. |

### End-to-end flow

1. A memory enters the system from a direct tool call, the Python CLI, or background consolidation.
2. The entry is written into a category topic file such as `lessons.md` or `preferences.md`.
3. The scoring engine rebuilds `memory-scores.json`, recalculates stability, and assigns a lifecycle state.
4. The embedding layer lazily creates or refreshes local vectors for semantic search.
5. On `before_agent_start`, Pi builds a situation report, optionally adds contextually relevant memories and past-session matches, then appends that material to the tail of the system prompt.
6. As evidence accumulates, the promotion system proposes stronger structures such as enforcement metadata or reusable skills.

> **Note:** The architecture is intentionally split between human-editable topic files and machine-managed indexes. That keeps the memory easy to inspect while still supporting scoring, retrieval, and promotion.

## Key Concepts

### Topic files are the source of truth

Pi stores memory as Markdown topic files under `.pi/memory/topics/`, not in a database-first format.

| Category | Topic file |
|---|---|
| `preference` | `preferences.md` |
| `lesson` | `lessons.md` |
| `pattern` | `patterns.md` |
| `decision` | `decisions.md` |
| `done` | `completions.md` |
| `mistake` | `mistakes.md` |

Each line is a single memory entry. Entries can carry markers such as `*(pinned)*` or `*(enforced)*`.

That design has two user-visible benefits:

- You can inspect project memory with normal file tools.
- Background maintenance can reorganize memories without inventing a separate opaque store.

### Scoring is what makes memory fade or stick

The scoring engine calculates stability with this formula:

`cue_weight × exp(-Δt / half_life) × ln(1 + evidence_count)`

It combines three forces:

- **Cue weight:** how strongly the memory was learned.
- **Recency decay:** older memories weaken if they are never reinforced.
- **Evidence count:** repeated use makes a memory more stable.

#### Cue weights

| Cue type | Weight | Typical meaning |
|---|---:|---|
| `explicit` | 1.0 | The user stated it directly. |
| `structural` | 0.9 | It was inferred from durable structure. |
| `behavioral` | 0.7 | It came from repeated workflow behavior. |
| `recurrence` | 0.6 | It was seen enough times to matter. |

#### Half-lives by category

| Category | Half-life |
|---|---|
| Preferences | 90 days |
| Lessons | 60 days |
| Patterns | 30 days |
| Decisions | 30 days |
| Completions | 14 days |
| Mistakes | 14 days |

Pi then assigns each scored entry to a lifecycle state:

- `active`: high-value memory that should influence current behavior
- `provisional`: still relevant, but below the top tier
- `candidate`: weak memory that may soon be dropped
- `dropped`: no longer injected

The system also applies caps so one category cannot crowd out everything else. Current budgets allow more room for preferences and lessons than for decisions or completions, with an overall cap of 40 active entries plus a smaller overflow pool for provisional ones.

> **Tip:** Pinned memories bypass normal decay, and enforced memories are kept active so their guardrails stay intact.

### Topic organization is separate from scoring

Topic files are organized for readability, while `memory-scores.json` is organized for ranking and lifecycle management. That separation lets Pi:

- reorder and trim injected memory without rewriting your topic structure every turn
- archive cold topic files when their newest entries have aged past roughly two half-lives
- keep pinned topics around even when everything else would have gone cold

User-visible effect: your memory files stay understandable, while the agent still gets a compact, prioritized view.

### Embeddings are local and file-backed

Semantic retrieval is handled by `memory-embeddings.ts`. It uses the local `Xenova/bge-small-en-v1.5` model through `@huggingface/transformers`, produces 384-dimensional vectors, and stores them in `.pi/memory/embeddings.json`.

Important details:

- The model is loaded lazily on first real use.
- Embeddings are cached per process for speed.
- The on-disk store is updated with atomic write-then-rename behavior.
- If the model cannot load, memory features degrade gracefully instead of failing the turn.

This layer is used in two places:

- semantic memory search
- automatic “contextually relevant memories” injection before a turn starts

That means Pi can still find a useful lesson even when your current prompt does not repeat the exact wording of the original memory.

### The situation report is the runtime view

The situation report is the text that Pi actually injects into the system prompt. It is built from scored topic entries, not from raw conversation history.

By default, it targets a 1700-token budget and builds sections such as:

- Pinned
- Active Preferences
- Active Lessons
- Vetoes & Mistakes
- Patterns
- Recent Decisions
- Recent Completions

It also shows a usage header and warns when memory is above 80% of its budget.

User-visible effect:

- high-priority memories reliably survive into the prompt
- lower-priority material is truncated instead of overflowing context
- the agent gets a stable, predictable memory block rather than a noisy dump

### Query classes bias what gets injected

Before a turn starts, Pi classifies the prompt into one of four query classes:

| Query class | What it boosts |
|---|---|
| `pr_review` | mistakes, patterns, lessons |
| `git_release` | lessons, decisions, preferences |
| `debug` | mistakes, lessons |
| `general` | no special boost |

That bias changes section ordering, section budget, and how many semantic matches Pi prefers to retrieve.

This is why the same project memory can feel different depending on what you are doing:

- a debugging request brings forward mistakes and lessons
- a release task gives more weight to decisions and conventions
- a review workflow favors repeated review-related guidance

### Promotion turns repeated memories into stronger structure

When a memory accumulates enough evidence, Pi can promote it.

Current promotion destinations are:

| Destination | What happens |
|---|---|
| `enforcement` | Safe, high-confidence rules can be applied automatically. |
| `skill` | Multi-step patterns can be proposed as reusable skills. |
| `project_rule` | Project-wide conventions are queued as proposals only. |
| `discard` | Low-value or superseded items can be marked for removal. |

The queue is stored in `.pi/memory/promotions.md` with statuses of `proposed`, `applied`, or `rejected`.

A few important boundaries keep this safe:

- enforcement auto-application is limited to high-confidence cases
- project rules are never auto-written into `rules/` or `.pi/rules/`
- promotion state is visible in a plain Markdown file instead of being hidden in a binary store

> **Warning:** Enforced memories are text-hash keyed. If background maintenance rewrites the text of an enforced entry, the binding would break, so the system explicitly protects those entries.

### Provenance is merged through a sidecar

Background consolidation can attach metadata such as the source session or what a memory informs. Instead of editing the score file directly, it writes a sidecar file at `.pi/memory/provenance-pending.json`. The orchestrator then merges that metadata back into `memory-scores.json` on completion.

This keeps the scoring index authoritative while still preserving traceability.

User-visible effect: a memory can later explain where it came from without forcing every background worker to edit the score file itself.

### Legacy memory migration still exists

The Python memory store includes a one-time migration path from the older SQLite-based memory store.

The CLI command:

```bash
myk-pi-tools memory migrate
```

moves entries from legacy `memories.db` into topic files under `.pi/memory/topics/`, then cleans up older memory-side files such as `dreams.md` and `dreams.lock`.

That matters if you are carrying an older repo or restoring archived state: the modern architecture is topic-file-first, but the project still provides a supported bridge forward.

## How It Affects the User

- **The agent remembers durable project context without re-reading everything.** The situation report surfaces the highest-value memories automatically.
- **Project conventions get stronger over time.** Repeated lessons accumulate evidence and can eventually become enforcement metadata or promotion candidates.
- **Semantic recall works even when wording changes.** Local embeddings let Pi match meaning, not just exact phrases.
- **Memory stays inspectable.** Topic files, score indexes, embedding caches, and promotion queues all live on disk in predictable locations.
- **Old state is still recoverable.** If your project used the older memory database, the Python CLI can migrate it into the current topic-file model.
- **Background consolidation stays safe.** Dreaming, provenance merges, and promotion passes operate through sidecars and queues instead of rewriting everything in place.

## Related Pages

- See [Curating Project Memory](curating-project-memory.html) for the hands-on workflow for adding, inspecting, and pruning memories.
- See [Background Memory Consolidation (Dreaming)](background-dreaming.html) for the background process that extracts, reorganizes, and promotes memories over time.
- See [Implementing Command Guards](safety-enforcements.html) for the enforcement side of memories that graduate into hard rules.
- See [Configuration & Settings](configuration.html) for the knobs that affect memory timing and runtime behavior.
- See [myk_pi_tools CLI Reference](cli-reference.html) for the Python commands that manage topic files and perform legacy migration.
- See [Automating Code Reviews](automating-code-reviews.html) for review-specific workflows that interact with memory, but belong to the review system rather than the core memory model.

## Related Pages

- [Curating Project Memory](curating-project-memory.html)
- [Background Memory Consolidation (Dreaming)](background-dreaming.html)
- [Implementing Command Guards](safety-enforcements.html)
- [Configuration & Settings](configuration.html)

---

Source: daemon-and-websockets.md

# Daemon & Websocket Networking

Background daemons and WebSockets keep your terminal session, browser dashboard, and long-running agents in sync without blocking your chat. Understanding this networking model helps when ports collide, a dashboard looks empty after a refresh, or an async agent keeps running after a crash.

## The Big Picture

Two complementary servers handle real-time UI and IPC. They share the same WebSocket patterns but differ in scope and how they pick ports.

| Component | Scope | Port | What users see |
|-----------|-------|------|----------------|
| **pidash** | Shared across all `pi` sessions on the machine | Fixed (default `19190`, overridable) | Global web dashboard — sessions, prompts, async status |
| **pidiff** | One instance per project working directory | Free port chosen at start | Project diff viewer with review comments |

| Layer | Role | Typical state |
|-------|------|----------------|
| **Interactive `pi` client** | Starts/stops daemons, forwards session events over WebSocket | In-memory per terminal |
| **Daemon HTTP + WebSocket server** | Aggregates clients, serves UI, health checks | Background Node process |
| **Browser UI** | Subscribes to the daemon for live updates | React app talking to `/ws/browser` |

**Typical connect flow:**

1. You start `pi` (or run `/pidash start` / `/pidiff start`).
2. The extension checks whether a healthy daemon already answers `/api/health`.
3. If not, it spawns the server script and waits until health checks succeed.
4. The terminal connects on `/ws/pi` and begins forwarding session events.
5. The browser connects on `/ws/browser` and receives the same live stream (plus buffered catch-up when needed).

> **Tip:** Check daemon health with `/pidash status` or `/pidiff status`. See [Using the Web Dashboard](using-the-web-dashboard.html) for day-to-day UI usage.

## Key Concepts

### Shared dashboard vs per-project diff server

**pidash** listens on a configured port (`pidash_port`, default `19190`). Every project session can attach to the same daemon, so one browser window can switch across terminals. Logs for spawn failures live under `~/.pi/pidash-server.log`.

**pidiff** binds a free local port for the current project and records that port (and PID when known) under the project’s `.pi/tmp/` directory as `pidiff.port` / `pidiff.pid`. That avoids collisions when several projects run diffs at once.

> **Note:** Toggle either server with `pidash_enable` / `pidiff_enable` (or their env vars). See [Configuration & Settings](configuration.html).

### Health checks, heartbeats, and reconnect

Clients probe `http://127.0.0.1:<port>/api/health` before trusting a daemon. WebSocket connections use ping/pong heartbeats; a missed pong forces reconnect. A periodic reconnect poller also re-attaches sessions that started before the daemon was ready.

While disconnected, the pidash client buffers recent events and replays them when the socket comes back — so a browser refresh usually catches up instead of starting blank.

### Async agent isolation

Long-running specialist work can spawn a detached child `pi` process with `PI_SUBAGENT_CHILD=1`. That flag tells extensions to skip UI mounts and other parent-only behavior so background work does not steal focus from your editor or chat.

Each job keeps status, prompts, and output under a unique directory in the project’s `.pi/tmp/` tree (named from the agent and a unique suffix). Results surface back to the parent session when the job finishes. For spawning, monitoring, and killing these jobs, see [Running Background Agents and Scheduled Tasks](async-agents-and-cron.html).

### Event bridge to the browser

Token streams and tool activity originate inside the `pi` session. Extensions forward those events over the daemon WebSocket so the React UI can update live. The daemon also exposes session lists and other small HTTP APIs for the UI — the hot path for streaming remains WebSockets, not REST polling.

## How it Affects the User

- **Refresh without losing context:** Closing the laptop or reloading the dashboard usually reconnects to the same pidash daemon and replays buffered activity.
- **Multiple terminals, one dashboard:** Several `pi` sessions can share pidash; the UI lists them so you can jump between projects.
- **Project-local diffs:** Opening pidiff in repo A does not collide with repo B, because each project owns its lockfiles and port under `.pi/tmp/`.
- **Files appear under `.pi/tmp/`:** Expect job folders, result JSON, debug logs, and pidiff lockfiles. They are project-scoped working state, not source code — keep `.pi/` out of git (the installer can configure this; see [Installation & Quickstart](quickstart.html)).
- **Graceful degradation:** If a daemon cannot start (port busy, slow first compile, firewall), the terminal session keeps working; real-time dashboard features simply stay disconnected until `/pidash start` or `/pidiff start` succeeds.

> **Warning:** pidash requires TUI mode. Headless or non-UI invocations will not keep a dashboard connection.

## Related Pages

- [Using the Web Dashboard](using-the-web-dashboard.html) — Start, stop, and use pidash / pidiff day to day.
- [Configuration & Settings](configuration.html) — `pidash_port`, enable flags, and related env vars.
- [Running Background Agents and Scheduled Tasks](async-agents-and-cron.html) — Spawn, monitor, and kill async agents that use this IPC path.
- [Installation & Quickstart](quickstart.html) — First-time install and daemon startup.

## Related Pages

- [Using the Web Dashboard](using-the-web-dashboard.html)
- [Running Background Agents and Scheduled Tasks](async-agents-and-cron.html)
- [Installation & Quickstart](quickstart.html)
- [Discord Bot Notifications](discord-bot.html)
- [Inter-Agent Communication Network](inter-agent-communication.html)

---

Source: neovim-integration.md

# Neovim Integration

Use Pi inside Neovim when you want to turn changed files into a navigable quickfix list and stay in your editor while reviewing work. This is fastest when you are iterating on a branch and want to jump file-to-file without leaving the terminal buffer.

## Prerequisites

- Neovim is installed and running.
- The `nvim` CLI is available on your `PATH`.
- Pi is started from a Neovim terminal buffer such as `:terminal`.
- Your repository has `git` available, with `origin/main` or `origin/master` fetched if you want branch-vs-base comparisons.

## Quick Example

Start Pi inside Neovim, then send changed files to quickfix:

```bash
:term pi
```

```text
/nvim-changed-files
```

Neovim opens the quickfix window and fills it with changed files so you can move through them with normal quickfix commands.

## Step-by-Step

1. Open a terminal inside Neovim.

```vim
:terminal
```

You can also use a split if you prefer to keep code visible while Pi runs:

```vim
:vsplit term://bash
```

2. Start Pi from that terminal.

```bash
pi
```

Pi only exposes the Neovim quickfix command when it is launched from a Neovim terminal session.

3. Populate quickfix with the files you need to review.

```text
/nvim-changed-files
```

Pi collects changed files from the current repository and sends them to Neovim's quickfix list.

4. Navigate the results in Neovim.

```vim
:copen
:cnext
:cprev
```

Each quickfix item points to a changed file and includes its git status such as `modified`, `added`, `deleted`, or `renamed`.

5. Re-run the command after new edits or commits.

```text
/nvim-changed-files
```

This refreshes quickfix with the latest set of changed files for the current branch or working tree.

## Advanced Usage

### What the Command Compares

`/nvim-changed-files` behaves differently depending on your current branch:

| Current branch | What Pi includes |
|---|---|
| `main` or `master` | Changes in your current working tree compared to `HEAD` |
| Any other branch | Branch changes compared to `origin/main` if it exists, otherwise `origin/master`, plus current uncommitted changes |

> **Tip:** On feature branches, this makes quickfix useful for both local edits and the branch-level diff you are preparing for review.

### Run Remote Lua in Your Current Neovim Session

Because Pi inherits the `$NVIM` socket when started from a Neovim terminal, you can trigger editor actions from commands, scripts, or prompts.

```bash
nvim --server "$NVIM" --remote-expr 'luaeval("vim.notify(\"Task complete from Pi!\")")'
```

A simple use case is sending yourself a notification after a long-running command or test finishes.

### Use Neovim for Review, Browser UI for Diff Publishing

If you want keyboard-first navigation, Neovim quickfix is the fastest path. If you want a browser diff viewer with inline comment publishing, see [Using the Web Dashboard](using-the-web-dashboard.html) for details.

## Troubleshooting

- **`/nvim-changed-files` does not appear:** Start Pi from inside Neovim, not from tmux or a separate terminal window.
- **Quickfix does not open or update:** Make sure the `nvim` CLI can talk to the current editor session through `$NVIM`.
- **No files are listed:** Check that your repository actually has changed files. On feature branches, also make sure `origin/main` or `origin/master` is available locally.
- **Remote Lua command fails:** Verify that `$NVIM` is set in the shell where you run the command.
- **You are using a background subagent:** Neovim integration is only available from your main interactive session, not background child sessions.

See [Installation & Quickstart](quickstart.html) for basic Pi setup.

## Related Pages

- [Installation & Quickstart](quickstart.html)
- [Built-in Workflow Commands](built-in-workflows.html)
- [Creating Slash Commands](custom-slash-commands.html)
- [Using the Web Dashboard](using-the-web-dashboard.html)

---

Source: discord-bot.md

# ~/.pi/discord.env
DISCORD_BOT_TOKEN=MTE...your.token.here...
DISCORD_ALLOWED_USERS=123456789012345678
```

Start the dashboard daemon from Pi:

```text
/pidash start
```

Then, in Discord, run `/sessions`, pick a session, and continue the conversation in a DM with the bot.

## Step-by-Step

### 1. Create the Discord credentials file

```env
# ~/.pi/discord.env
DISCORD_BOT_TOKEN=MTE...your.token.here...
DISCORD_ALLOWED_USERS=123456789012345678
```

`DISCORD_BOT_TOKEN` enables the bot. `DISCORD_ALLOWED_USERS` is optional, but recommended, and accepts a comma-separated list of Discord user IDs.

### 2. Create and configure the Discord bot

Set up your bot in the Discord Developer Portal, then:

- copy the bot token
- enable **Message Content Intent**
- invite the bot to your server with the `application.commands` scope if you want slash commands there

If you only want DM-based control, the bot can still work without using server chat for prompts.

### 3. Start or restart the pidash daemon

```text
/pidash start
```

If pidash is already running, restart it so it reloads the Discord credentials:

```text
/pidash restart
```

The Discord integration is attached to the pidash daemon, so the bot logs in when pidash starts.

> **Note:** If you are already using the web dashboard, you do not need a separate Discord-specific service. The same pidash daemon handles both.

### 4. Choose a session from Discord

Use these slash commands in a Discord server where the bot is present:

| Command | What it does |
|---|---|
| `/sessions` | Lists active Pi sessions and shows buttons to connect or disconnect |
| `/status` | Shows the currently watched session, model, branch, and current state |
| `/stop` | Sends an interrupt to the watched session |

A typical flow is:

1. Run `/sessions`
2. Click the session you want to watch
3. Open a DM with the bot
4. Send prompts there

### 5. Send prompts from Discord DMs

Once you are watching a session, send a DM to the bot just like you would type into Pi.

- Plain text messages are forwarded as prompts
- If the agent asks a question, reply in the same DM
- `/stop` also works as a DM message

Use this split to stay oriented:

| Use Discord server slash commands for | Use Discord DMs for |
|---|---|
| picking a session | sending prompts |
| checking status | replying to agent questions |
| sending stop signals | uploading files or images |

### 6. Send attachments when needed

You can DM attachments to the bot along with your prompt.

Supported behavior:

- text-like files under 100KB are inlined into the prompt
- images are forwarded for the agent to inspect
- larger or binary files are only mentioned, not embedded

This is useful for quick log review, screenshots, small configs, or error snippets.

> **Tip:** If you want the agent to focus on an uploaded file, include a short instruction in the same DM, such as “Review this log and summarize the failure.”

## Advanced Usage

### Authorize multiple people

```env
# ~/.pi/discord.env
DISCORD_ALLOWED_USERS=111111111111111111,222222222222222222,333333333333333333
```

Use this when a small team needs access to the same running sessions.

> **Warning:** If `DISCORD_ALLOWED_USERS` is omitted, the bot accepts DMs from any user who can reach it. Set this variable unless you intentionally want open access.

### Understand how “watching” works

The bot only forwards prompts to the session you are currently watching. If you switch sessions in Discord, new prompts go to the newly selected one.

This makes it easy to keep one DM thread per active task without guessing where your next message will land.

### Use the bot with agent questions

If the running agent triggers an interactive choice, the bot forwards that prompt into your DM and waits for your reply. You can respond with either:

- a number, when options are listed
- free text, when the prompt expects a typed answer

### Change the pidash port if needed

If port `19190` is already taken, configure pidash with either:

- `PI_PIDASH_PORT`
- `pidash_port` in project settings

Then start pidash again. See [Using the Web Dashboard](using-the-web-dashboard.html) and [Configuration & Settings](configuration.html) for details.

## Troubleshooting

- **Bot does not come online:** Make sure `DISCORD_BOT_TOKEN` is present in `~/.pi/discord.env`, then run `/pidash restart`.
- **Slash commands do not appear:** Confirm the bot was invited with the `application.commands` scope, then restart pidash so commands are registered again.
- **Your DM gets “Not watching any session”:** Run `/sessions` first and click a session button before sending prompts.
- **Another user cannot control the bot:** Add their Discord user ID to `DISCORD_ALLOWED_USERS`, comma-separated, then restart pidash.
- **Uploaded file does not seem to reach the agent:** Keep text files under 100KB for inline forwarding. Very large or binary files are only referenced, not embedded.

See [Using the Web Dashboard](using-the-web-dashboard.html) for details on starting and managing pidash. See [Running Background Agents and Scheduled Tasks](async-agents-and-cron.html) for details on the kinds of long-running work you can monitor from Discord.

## Related Pages

- [Using the Web Dashboard](using-the-web-dashboard.html)
- [Daemon & Websocket Networking](daemon-and-websockets.html)
- [Running Background Agents and Scheduled Tasks](async-agents-and-cron.html)
- [Inter-Agent Communication Network](inter-agent-communication.html)

---

Source: image-generation.md

# Image Generation

Set up Gemini once, then ask Pi to create images directly in chat so you can produce mockups, concept art, and visual assets without leaving your development session. You should be able to go from API key to saved image in a minute.

## Prerequisites

- A Gemini API key in `GEMINI_API_KEY` or `GOOGLE_API_KEY`
- An image-capable Gemini model set with `PI_IMAGE_MODEL` or in `.pi/pi-config-settings.json`
- An active Pi session

## Quick Example

```bash
export GEMINI_API_KEY="your-api-key"
export PI_IMAGE_MODEL="gemini-3-pro-image"
```

```text
Generate a pixel art image of a cat hacking on a mechanical keyboard.
```

Pi replies with the saved file path under your project's `.pi/tmp/` directory. In container-based sessions, Pi also includes a clickable `http://localhost:<port>/<filename>` preview URL.

## Step-by-step

1. **Set the model**

   For the current shell:

   ```bash
   export PI_IMAGE_MODEL="gemini-3-pro-image"
   ```

   Or save it in your project settings:

   ```json
   {
     "image_model": "gemini-3-pro-image"
   }
   ```

   Save that JSON in `.pi/pi-config-settings.json`.

2. **Set the API key**

   ```bash
   export GEMINI_API_KEY="your-api-key"
   ```

   If you already use `GOOGLE_API_KEY`, that works too.

3. **Ask for the image in plain language**

   ```text
   Generate an image of a coffee cup spilling over on a futuristic desk.
   ```

   You do not need a special slash command. A normal request is enough.

4. **Open the result**

   Pi returns one or more saved file paths such as:

   ```text
   /your-project/.pi/tmp/pi-image-123456-abcd12.png
   ```

   If your session is running in a container, Pi also prints a localhost preview link for the same file.

> **Tip:** Keep the model in `.pi/pi-config-settings.json` if you want the same default across sessions, and keep the API key in your shell environment.

## Advanced Usage

### Use structured prompt fields

When you want tighter control over composition, ask with named fields:

```text
Generate an image. Subject: A coffee cup. Action: spilling over. Scene: A busy futuristic desk. Composition: close-up. Lighting: neon glow. Style: photorealistic. Text: "ERROR 404". Aspect ratio: 16:9.
```

These fields are supported:

- `Subject`
- `Action`
- `Scene`
- `Composition`
- `Lighting`
- `Style`
- `Text`
- `Aspect ratio`

Use this format when a short natural-language prompt is not specific enough.

### Supported aspect ratios

Use one of these exact values when you want a specific canvas shape:

| Value | Best for |
|---|---|
| `1:1` | Square avatars, icons, thumbnails |
| `3:4` | Portrait images |
| `4:3` | Standard landscape images |
| `9:16` | Mobile and story-style images |
| `16:9` | Widescreen banners and mockups |

If you omit `Aspect ratio`, Pi sends the request without one and lets the model use its default output shape.

### Know what file types to expect

Pi saves whatever image format Gemini returns. Current output formats include:

- `.png`
- `.jpg`
- `.gif`
- `.webp`

If a request returns multiple images, Pi lists every saved path in the response.

### Work smoothly in containers

In Docker or Podman-style sessions, Pi automatically serves generated images over HTTP so you can open them outside the container. You do not need to start a separate preview command.

See [Configuration & Settings](configuration.html) for the full settings reference and [Installation & Quickstart](quickstart.html) for general setup.

## Troubleshooting

- **"Model not configured"**
  Set `PI_IMAGE_MODEL` or add `"image_model": "gemini-3-pro-image"` to `.pi/pi-config-settings.json`, then restart Pi.

- **"No API key found"**
  Export `GEMINI_API_KEY` or `GOOGLE_API_KEY` before starting Pi.

- **"Image generation blocked by safety filter"**
  Rephrase the prompt to remove unsafe or explicit content.

- **"No image data returned from Gemini"**
  Retry with a simpler prompt or switch to a different image-capable Gemini model.

> **Warning:** Pi reads the API key from the current session environment, so export it before launching Pi.

## Related Pages

- [Configuration & Settings](configuration.html)
- [Installation & Quickstart](quickstart.html)
- [External AI Agents & CLI](external-ai-agents.html)
- [Google Vertex Claude Provider](vertex-claude-provider.html)

---

Source: vertex-claude-provider.md

# Google Vertex Claude Provider

Run Claude through your Google Cloud project so usage stays on Vertex AI billing and IAM, without Anthropic API keys.

## Prerequisites

- `pi` installed and working
- Google Cloud SDK (`gcloud`) with a project that can call Claude on Vertex AI
- Application Default Credentials (interactive login or a service account key)

## Quick Example

```bash
gcloud auth application-default login
export GOOGLE_CLOUD_PROJECT=your-project-id

pi install git:github.com/myk-org/pi-vertex-claude

pi --provider google-vertex-claude --model claude-sonnet-4-6
```

## Step-by-step

### 1. Install the provider

Interactive installer (choose **pi-vertex-claude** under Pi Packages):

```bash
uv run scripts/install.py
```

Or install / update directly:

```bash
pi install git:github.com/myk-org/pi-vertex-claude
pi update git:github.com/myk-org/pi-vertex-claude
```

npm package name: `@myk-org/pi-vertex-claude`.

### 2. Authenticate

```bash
gcloud auth application-default login
```

Credentials are read from `GOOGLE_APPLICATION_CREDENTIALS` when set; otherwise from the default ADC file under your home directory.

> **Tip:** In CI, set `GOOGLE_APPLICATION_CREDENTIALS` to a service account JSON key instead of using interactive login.

### 3. Set the GCP project

One of these must be set (priority order):

| Variable | Role |
| :--- | :--- |
| `GOOGLE_CLOUD_PROJECT` | Preferred project id |
| `GCLOUD_PROJECT` | Common `gcloud` project id |
| `ANTHROPIC_VERTEX_PROJECT_ID` | Compatible alternate |

```bash
export GOOGLE_CLOUD_PROJECT=your-project-id
```

### 4. (Optional) Set the region

```bash
export GOOGLE_CLOUD_LOCATION=us-east5
```

| Variable | Default | Effect |
| :--- | :--- | :--- |
| `GOOGLE_CLOUD_LOCATION` | `us-east5` | Vertex region |
| `CLOUD_ML_REGION` | — | Used only if `GOOGLE_CLOUD_LOCATION` is unset |

### 5. Start Pi

```bash
pi --provider google-vertex-claude --model claude-sonnet-4-6
```

> **Note:** The provider appears only when a project id **and** ADC credentials exist before `pi` starts. Export them in the same shell (or your profile), then launch Pi.

### 6. Choose a model

| Model id | Context | Max output | Reasoning |
| :--- | ---: | ---: | :---: |
| `claude-opus-4-6` | 200K | 128K | yes |
| `claude-sonnet-4-6` | 200K | 64K | yes |
| `claude-opus-4-5@20251101` | 200K | 32K | yes |
| `claude-opus-4-1@20250805` | 200K | 32K | yes |
| `claude-opus-4@20250514` | 200K | 32K | yes |
| `claude-sonnet-4-5@20250929` | 200K | 64K | yes |
| `claude-sonnet-4@20250514` | 200K | 64K | yes |
| `claude-3-7-sonnet@20250219` | 200K | 64K | yes |
| `claude-haiku-4-5@20251001` | 200K | 64K | yes |
| `claude-3-5-sonnet-v2@20241022` | 200K | 8K | no |
| `claude-3-5-haiku@20241022` | 200K | 8K | no |

All of these accept text and image input.

For async agents, dreaming, and other settings that take provider/model pairs, use `google-vertex-claude` with one of the ids above. See [Configuration & Settings](configuration.html) and [Running Background Agents and Scheduled Tasks](async-agents-and-cron.html).

## Advanced Usage

### 1M context variants

Enable extra catalog entries for Opus 4.6 and Sonnet 4.6:

```bash
export VERTEX_CLAUDE_1M=true
pi --provider google-vertex-claude --model claude-opus-4-6-1m
```

| Model id | Context | Max output |
| :--- | ---: | ---: |
| `claude-opus-4-6-1m` | 1M | 128K |
| `claude-sonnet-4-6-1m` | 1M | 64K |

> **Warning:** Only the string `true` enables these models. Other values leave the `-1m` ids unregistered.

### Shell helper

**bash / zsh:**

```bash
piv() {
  GOOGLE_CLOUD_PROJECT=your-project-id \
  GOOGLE_CLOUD_LOCATION=us-east5 \
  pi --provider google-vertex-claude --model claude-sonnet-4-6 "$@"
}
```

**fish:**

```fish
function piv
  set -x GOOGLE_CLOUD_PROJECT your-project-id
  set -x GOOGLE_CLOUD_LOCATION us-east5
  pi --provider google-vertex-claude --model claude-opus-4-5@20251101 $argv
end
```

### Environment reference

| Variable | Type | Default | Required | Effect |
| :--- | :--- | :--- | :---: | :--- |
| `GOOGLE_CLOUD_PROJECT` | string | — | one of three* | GCP project id (highest priority) |
| `GCLOUD_PROJECT` | string | — | one of three* | GCP project id |
| `ANTHROPIC_VERTEX_PROJECT_ID` | string | — | one of three* | GCP project id |
| `GOOGLE_CLOUD_LOCATION` | string | `us-east5` | no | Vertex region |
| `CLOUD_ML_REGION` | string | — | no | Region fallback |
| `GOOGLE_APPLICATION_CREDENTIALS` | path | ADC default file | no | Service account key path |
| `VERTEX_CLAUDE_1M` | `"true"` \| other | unset | no | When `"true"`, registers `-1m` models |

\*Set exactly one project id variable for the provider to load and stream.

### Using vs extending

| Goal | What you do |
| :--- | :--- |
| **Use** | Install the package, set project + ADC, run `pi --provider google-vertex-claude --model …` |
| **Extend** | Publish another pi package that registers its own provider id with a custom stream handler; this package registers `google-vertex-claude` only when project id and ADC are available at load time |

Lifecycle when you start `pi`:

1. Extension loads.
2. Missing project id or ADC → provider is not registered (models stay hidden).
3. Both present → `google-vertex-claude` registers with the current model catalog (including `-1m` if enabled).
4. Each turn re-checks project, region, and ADC, then streams from Vertex.

For ACPX/CLI backends (separate from Vertex), see [ACPX Provider Integration](acpx-provider.html) and [External AI Agents & CLI](external-ai-agents.html).

## Troubleshooting

| Problem | Fix |
| :--- | :--- |
| Provider missing from `/model` | Export a project id, ensure ADC exists, restart `pi`. |
| `Vertex AI requires a project ID` | Set `GOOGLE_CLOUD_PROJECT`, `GCLOUD_PROJECT`, or `ANTHROPIC_VERTEX_PROJECT_ID`. |
| `Vertex AI requires Application Default Credentials` | Run `gcloud auth application-default login`, or set `GOOGLE_APPLICATION_CREDENTIALS`. |
| `-1m` models not listed | Export `VERTEX_CLAUDE_1M=true` before starting `pi`. |
| Region or quota errors | Set `GOOGLE_CLOUD_LOCATION` to a region where Claude on Vertex is enabled. |

> **Tip:** Env changes apply only after you start a new `pi` process so the provider can re-register.

## Related Pages

- [External AI Agents & CLI](external-ai-agents.html)
- [Pi Sidecar HTTP API](pi-sidecar.html)
- [Configuration & Settings](configuration.html)
- [Installation & Quickstart](quickstart.html)
- [ACPX Provider Integration](acpx-provider.html)

---

Source: pi-sidecar.md

# or: pip install pi-sidecar-client
```

CLIs after install: `pi-sidecar` (server) and `pi-sidecar-start` (dev start script).

### 2. Start the sidecar

**Production-style (default port 9100):**

```bash
npx pi-sidecar
# or: node dist/server.js
```

**Local dev helper (default port 9201):**

```bash
npx pi-sidecar-start
# foreground: npx pi-sidecar-start --foreground
# stop:       npx pi-sidecar-start --stop
```

| Variable | Default (`pi-sidecar`) | Default (`pi-sidecar-start`) | Effect |
| :--- | :--- | :--- | :--- |
| `SIDECAR_PORT` | `9100` | `9201` | Listen port |
| `SIDECAR_HOST` | `127.0.0.1` | `127.0.0.1` | Bind address (`DEV_MODE=true` → `0.0.0.0` if host unset) |
| `SIDECAR_URL` | — | — | Client base URL (Python default `http://127.0.0.1:9100`) |
| `PI_SIDECAR_LOG_LEVEL` | `info` | `info` | Server / client log level |

> **Tip:** Point the client at the port you actually started. Dev script uses **9201**; the Node server and Python client default to **9100**.

```bash
export SIDECAR_URL=http://127.0.0.1:9201   # when using pi-sidecar-start
```

### 3. Wait until healthy

```bash
curl -s http://127.0.0.1:9100/health
```

| `status` | Meaning |
| :--- | :--- |
| `ok` | Ready (model discovery finished) |
| `starting` | Discovery still running (HTTP 503) |
| `degraded` | Discovery failed; sessions may still work for already-known models |

```python
from pi_sidecar_client import check_sidecar_available

ok, msg = await check_sidecar_available()
print(ok, msg)
```

### 4. Discover models

```python
from pi_sidecar_client import list_models

all_models = await list_models()
gemini = await list_models(provider="gemini")
```

Friendly Python provider aliases map to sidecar ids:

| Client alias | Sidecar provider |
| :--- | :--- |
| `gemini` | `google` |
| `cursor` | `acpx-cursor` (model prefixed with `cursor:` if needed) |
| `claude` | `google-vertex-claude` |

Refresh after changing agents or auth:

```bash
curl -X POST http://127.0.0.1:9100/models/refresh
```

Diagnose a single provider:

```python
from pi_sidecar_client import get_sidecar_client

client = get_sidecar_client()
status = await client.get_model_provider_status("google")
print(status["registered"], status["modelCount"])
```

> **Note:** On non-loopback binds, auth fields on provider status are redacted. Prefer `127.0.0.1` for local diagnostics. For Vertex auth setup, see [Google Vertex Claude Provider](vertex-claude-provider.html).

### 5. Run a session

**Single-shot (auto cleanup):** use `call_ai_once` as in the Quick Example.

**Multi-turn:**

```python
from pi_sidecar_client import call_ai, get_sidecar_client

result = await call_ai(
    "I'm building a REST API in Python. What framework should I use?",
    ai_provider="gemini",
    ai_model="gemini-2.5-flash",
    system_prompt="You are a senior Python developer. Be concise.",
)
session_id = result.session_id

result = await call_ai(
    "Show me a minimal example with that framework.",
    ai_provider="gemini",
    ai_model="gemini-2.5-flash",
    session_id=session_id,
)

await get_sidecar_client().delete_session(session_id)
```

Session lifecycle:

1. Create session (`POST /sessions`) with provider, model, system prompt, cwd.
2. Prompt (`POST /sessions/:id/prompt`) — one in-flight prompt per session.
3. Optional abort (`POST /sessions/:id/abort`).
4. Delete (`DELETE /sessions/:id`) when finished.

Default built-in tools when you omit `tools`: `read`, `grep`, `find`, `ls`, `bash`.

`cwd` loads project resources from `{cwd}/.pi/` and `AGENTS.md`. Optional `agent_dir` points at a global agent directory (e.g. `~/.pi/agent`); it is rejected on non-loopback binds unless `DEV_MODE=true` (then ignored).

## Advanced Usage

### HTTP API surface

| Method | Path | Effect |
| :--- | :--- | :--- |
| `GET` | `/health` | Readiness (`ok` / `starting` / `degraded`) |
| `GET` | `/models` | List discovered models |
| `POST` | `/models/refresh` | Re-run discovery |
| `GET` | `/models/:provider/status` | Registration, model count, auth (redacted off-loopback) |
| `POST` | `/sessions` | Create session → `{ session_id }` |
| `POST` | `/sessions/:id/prompt` | Send `{ "message": "..." }` |
| `POST` | `/sessions/:id/abort` | Cancel in-flight prompt |
| `DELETE` | `/sessions/:id` | Destroy session |

Body size limit: 1 MiB. Concurrent prompts on the same session return conflict (busy).

### Custom HTTP-backed tools

Pass `custom_tools` when creating a session. Entries with an `http` block are executed as outbound HTTP; `{param}` placeholders interpolate from tool args.

```python
result = await call_ai_once(
    "Look up user 42",
    ai_provider="gemini",
    ai_model="gemini-2.5-flash",
    custom_tools=[{
        "name": "fetch_data",
        "description": "Fetch user data by ID",
        "parameters": {
            "type": "object",
            "properties": {"userId": {"type": "string"}},
        },
        "http": {
            "method": "GET",
            "url": "https://api.example.com/users/{userId}",
        },
    }],
)
```

| `http` field | Type | Default | Effect |
| :--- | :--- | :--- | :--- |
| `method` | `GET` \| `POST` \| `PUT` \| `DELETE` \| `PATCH` | (required) | HTTP method |
| `url` | string | (required) | URL template with `{params}` |
| `headers` | object | — | Header templates |
| `query_params` / `queryParams` | object | — | Query templates |
| `body_template` / `bodyTemplate` | object \| string | — | Body template |
| `timeout_ms` / `timeoutMs` | number | `30000` | Request timeout |

Restrict the built-in tool allowlist with `tools=["read", "grep"]` (or `[]` for none).

### ACPX and CLI model sources

```bash
export ACPX_AGENTS=cursor
export CLI_AGENTS=claude,gemini
npx pi-sidecar
```

Then use providers such as `acpx-cursor` / `cli-claude` (or Python aliases `cursor` / mapped names). Refresh with `POST /models/refresh` after changing env. See [Configuration & Settings](configuration.html) for project-level agent lists used by interactive Pi.

### Watchdog (companion process)

```bash
export SIDECAR_WATCHDOG_URL=http://127.0.0.1:8000/health
npx pi-sidecar
```

When set, the sidecar polls that URL and shuts itself down after consecutive failures (default: 60s grace, 30s interval, 10s timeout, 6 failures).

### Docker / process coupling

Start the sidecar, wait for `/health`, then start your app and trap cleanup — see the package `entrypoint.example.sh` pattern (`SIDECAR_PORT` default `9100`).

### Using vs extending

| Goal | What you do |
| :--- | :--- |
| **Use** | Run `pi-sidecar`, call `call_ai_once` / `SidecarClient`, pass `custom_tools` / `tools` |
| **Extend** | Call `startSidecar({ port, host, watchdogUrl, watchdogOptions })` from Node, or override extension paths with `SIDECAR_ACPX_EXTENSION_PATH`, `SIDECAR_CLI_PROVIDER_EXTENSION_PATH`, `SIDECAR_PROVIDER_EXTENSION_PATH`, `SIDECAR_VERTEX_EXTENSION_PATH`, `SIDECAR_SUBAGENT_EXTENSION_PATH` |

Startup lifecycle:

1. Assert Pi SDK version ≥ 0.81.1.
2. Listen on host/port.
3. Optionally start watchdog.
4. Async model discovery — `/health` is `starting` until ready.
5. Serve session/prompt routes; idle sessions cleaned after 1 hour.

### Parallel calls and usage hooks

```python
from pi_sidecar_client import run_parallel_with_limit, set_usage_recorder

set_usage_recorder(my_recorder)  # sync or async; kwargs: request_id, result, call_type, ...
results = await run_parallel_with_limit(tasks, max_concurrency=5)
```

`ai_call_timeout` on `call_ai` / `call_ai_once` is in **minutes** (converted to HTTP timeout seconds).

## Troubleshooting

| Problem | Fix |
| :--- | :--- |
| Client connection errors | Align `SIDECAR_URL` with the server port (`9100` vs `9201`). |
| `/health` stuck on `starting` / 503 | Wait for discovery; check logs (`PI_SIDECAR_LOG_LEVEL=debug`). |
| Empty model list | Auth providers; set `ACPX_AGENTS` / `CLI_AGENTS`; `POST /models/refresh`; check `GET /models/:provider/status`. |
| Session create rejects model | Use an id from `GET /models`; interactive OAuth-only providers are blocked in this headless service. |
| `409` / session busy | Wait for the current prompt or `POST .../abort`. |
| `agent_dir` rejected | Bind on loopback, or use `DEV_MODE=true` (value discarded). |
| Pi version error at startup | Upgrade `@earendil-works/pi-coding-agent` to ≥ 0.81.1. |

> **Warning:** Default bind is loopback-only. Binding to `0.0.0.0` exposes an unauthenticated AI API on the network — use only behind a trusted boundary.

## Related Pages

- [Google Vertex Claude Provider](vertex-claude-provider.html)
- [ACPX Provider Integration](acpx-provider.html)
- [External AI Agents & CLI](external-ai-agents.html)
- [Configuration & Settings](configuration.html)
- [Daemon & Websocket Networking](daemon-and-websockets.html)

---

Source: external-ai-agents.md

# External AI Agents & CLI

Run prompts through Cursor, Claude, or Gemini from your terminal or Pi chat when you want that provider’s models, tools, and session behavior without leaving your workflow.

## Prerequisites

- `myk-pi-tools` installed and on your `PATH` (for example `uv tool install myk-pi-tools`)
- Provider CLIs installed and authenticated:
  - **cursor** → `agent` binary
  - **claude** → `claude` binary
  - **gemini** → `gemini` binary

## Quick Example

```bash
# List models for Claude
myk-pi-tools ai-cli models claude

# Run a one-shot prompt
myk-pi-tools ai-cli run "Summarize the changes in src/main.rs" --provider claude --model claude-sonnet-4-6
```

Inside a Pi session:

```text
/external-ai cursor explain the authentication flow
```

## Step-by-Step Guide

### 1. Pick a provider and model

Supported providers: `cursor`, `claude`, `gemini`.

| Provider | Default model (when `--model` is omitted) |
|----------|-------------------------------------------|
| `cursor` | `composer-2-fast` |
| `claude` | `claude-sonnet-4-6` |
| `gemini` | `gemini-2.5-flash` |

```bash
myk-pi-tools ai-cli models cursor
```

```text
/external-ai cursor --model gpt-5.4-high review the latest PR
```

### 2. Run a read-only prompt

By default, `/external-ai` and `ai-cli run` are read-only — the external agent should not modify files.

```text
/external-ai gemini explain this function
```

```bash
myk-pi-tools ai-cli run "List security concerns in the auth package" --provider cursor
```

### 3. Allow writes with `--fix`

```text
/external-ai claude --fix rewrite the error handling in database.ts
```

> **Note:** In a dirty git worktree, Pi asks whether to create a checkpoint commit (`chore: checkpoint before ai-cli changes`) or continue anyway before applying changes.


> **Warning:** `--fix` works with a single provider only. It cannot be combined with `--peer` or a comma-separated provider list.

### 4. Continue a session with `--resume`

```text
/external-ai cursor --resume add tests for the edge cases too
```

```bash
myk-pi-tools ai-cli run "Continue from the last review" --provider cursor --resume
```

| Mode | Session behavior |
|------|------------------|
| Default | Fresh session each call |
| `--resume` | Continue the most recent session |
| `--session-id <id>` | Resume a specific session (CLI only; mutually exclusive with `--resume`) |

## Advanced Usage

### Peer review loops

Use `--peer` for an AI-to-AI review loop. Pi orchestrates rounds: the peer reports findings, Pi applies agreed fixes or returns technical counter-arguments, then the peer re-reviews until peers report no remaining issues.

```text
/external-ai cursor --model gpt-5.4-xhigh --peer review this pull request
```

Multiple peers (comma-separated) review in parallel; later rounds share group context:

```text
/external-ai cursor,claude --peer review the architecture design
```

> **Warning:** Do not combine `--peer` with `--fix` or `--resume`. Peer mode manages sessions automatically via `--session-id` after the first round.

### Persist defaults

Save last-used agents or peer sets to `.pi/external-ai-config.json`:

```bash
myk-pi-tools ai-cli save-config --agents "cursor --model gpt-5.4-high"
myk-pi-tools ai-cli save-config --peers "cursor,claude"
```

Then omit the provider:

```text
/external-ai write a unit test for this script
/external-ai --peer review this
```

### Extra CLI flags

Pass through flags the underlying binary understands:

```bash
myk-pi-tools ai-cli run "Review auth" --provider cursor --cli-flags=--trust
```

### Other providers

For agents outside `cursor` / `claude` / `gemini` (for example Codex or Copilot), use ACPX instead of `/external-ai`. See [ACPX Provider Integration](acpx-provider.html).

For registering `cli-*` providers inside Pi sessions, see [Configuration & Settings](configuration.html). To customize slash-command behavior, see [Creating Slash Commands](custom-slash-commands.html).

## Troubleshooting

- **Permission / unexpected file changes:** You ran without `--fix`, or the agent ignored read-only instructions. Retry with `--fix` after a clean or checkpointed tree.
- **Unknown provider:** Use only `cursor`, `claude`, or `gemini` with `/external-ai`. Other agents need [ACPX Provider Integration](acpx-provider.html).
- **`agent` / `claude` / `gemini` not found:** Install and authenticate that provider’s CLI separately, then confirm it is on your `PATH`.
- **Long-running or “stuck” agents:** Multi-step tool use can take several minutes. Do not cancel early — execution commands intentionally run without strict timeouts.
- **`--resume` and `--peer` together:** Not allowed; drop one of the flags.

## Related Pages

- [ACPX Provider Integration](acpx-provider.html)
- [Google Vertex Claude Provider](vertex-claude-provider.html)
- [Pi Sidecar HTTP API](pi-sidecar.html)
- [Configuration & Settings](configuration.html)
- [Built-in Workflow Commands](built-in-workflows.html)

---

Source: async-agents-and-cron.md

# Running Background Agents and Scheduled Tasks

Run long tasks without blocking your main session, and schedule recurring work so Pi can keep checking, reviewing, or cleaning up in the background. This is useful when you want to keep coding while another agent works, or when you want a workflow to run on a timer.

## Prerequisites

- A running Pi session in your project repository
- A TUI session if you want to use the fullscreen status overlays
- `git` available if your background task depends on repository state
- If you use ACPX-backed models for detached work, `async_llm_provider` and `async_llm_model` may be required. See [Configuration & Settings](configuration.html) for details.

## Quick Example

Start a background agent with a plain-English request:

> Run the `security-auditor` on the `src/` directory in the background. Let me know when it's done.

Schedule a recurring task with `/cron`:

```bash
/cron Run the test-automator every 30 minutes
```

Use these when you want non-blocking work right away: one-off jobs through natural language, recurring jobs through `/cron`.

## Step-by-Step

1. Start a background job.

Ask Pi to run a specialist in the background:

> Run the `security-auditor` on the `src/` directory in the background. Let me know when it's done.

Pi can keep your main session free while the background job runs and report back when it finishes.

2. Monitor running async work.

Open the async overlay:

```bash
/async-status
```

This shows queued and running jobs, elapsed time, and live output. Press `Enter` to inspect a job, or press `x` to kill the selected job.

3. Stop async work when needed.

Open the interactive kill picker:

```bash
/async-kill
```

Or cancel everything at once:

```bash
/async-kill all
```

> **Tip:** If you only need to stop one job, the interactive picker is safer because it shows the exact running entries before you kill them.

4. Create a recurring schedule.

Use `/cron` with natural language:

```bash
/cron Every day at 9:00 AM, run the git-expert to generate a daily summary.
```

Pi turns that into a recurring task for the current session.

5. Review or remove scheduled tasks.

List tasks in the current session:

```bash
/cron list
```

List tasks across active Pi sessions:

```bash
/cron list-all
```

Remove a task by ID:

```bash
/cron remove 1
```

In the cron overlays, you can also press `x` to remove the selected task.

## Advanced Usage

### Pick the Right Monitoring Command

| Goal | Command | What you get |
|---|---|---|
| Watch async jobs | `/async-status` | Fullscreen list, live output, kill with `x` |
| Kill async jobs | `/async-kill` | Interactive kill picker |
| See local schedules | `/cron list` | Fullscreen list of this session’s recurring tasks |
| See all session schedules | `/cron list-all` | Cross-session view of active cron files |
| Remove a known cron | `/cron remove <id>` | Direct removal by task ID |

### Keep Background Context Between Runs

If you want a background agent to remember previous work, ask Pi to persist that session:

> Run the code reviewer in the background and persist its session so it remembers previous feedback.

This is most useful for iterative review loops and repeated follow-up work. See [Automating Code Reviews](automating-code-reviews.html) for a full review workflow.

### Use Fire-and-Forget for Maintenance Jobs

For maintenance tasks where you do not want a follow-up result injected back into chat, ask for fire-and-forget behavior:

> Run a background memory cleanup task as fire-and-forget.

This is a good fit for housekeeping work where a completion notification is enough. See [Background Memory Consolidation (Dreaming)](background-dreaming.html) for a concrete example.

### Understand Cron Lifetime

Cron tasks belong to the current Pi process. They survive session refreshes inside that process, but they do not keep running after a full Pi exit.

> **Note:** If you need to confirm what is still scheduled, run `/cron list` or `/cron list-all` after reconnecting instead of assuming an older schedule is still active.

### ACPX Compatibility

Detached LLM work is more restricted when your parent session is using an `acpx-*` provider. In that case, some async work may be skipped or require a separate async LLM provider/model to be configured first.

See [ACPX Provider Integration](acpx-provider.html) and [Configuration & Settings](configuration.html) for the exact setup.

## Troubleshooting

- **Async job does not start:** If Pi complains about a missing `taskId`, the workflow is expecting the background work to be linked to a tracked task or explicitly marked as independent.
- **Job starts but immediately skips:** If your session is using an ACPX-backed provider, configure `async_llm_provider` and `async_llm_model` before retrying.
- **You want live output but nothing appears:** Use `/async-status` from a TUI session. The fullscreen overlay is the supported live-view interface.
- **A cron task seems stuck or outdated:** Run `/cron list` or `/cron list-all`, then remove the old task and create a fresh one.
- **You want browser-based monitoring instead of terminal overlays:** See [Using the Web Dashboard](using-the-web-dashboard.html) for details.

## Related Pages

- [Daemon & Websocket Networking](daemon-and-websockets.html)
- [Using the Web Dashboard](using-the-web-dashboard.html)
- [Automating Code Reviews](automating-code-reviews.html)
- [Managing Custom Agents](managing-custom-agents.html)
- [Configuration & Settings](configuration.html)

---

Source: inter-agent-communication.md

# Inter-Agent Communication Network

`pi-config` includes an on-demand inter-agent messaging system so multiple Pi sessions can discover each other, exchange prompts, and hand work back and forth without leaving the terminal. You should care because this is what makes peer review loops, planner/worker splits, and background helper sessions feel like one coordinated workspace instead of a pile of disconnected terminals.

## The Big Picture

Two communication modes exist in the codebase:

| System | How it is activated | Transport | Tool names | Best fit |
| :--- | :--- | :--- | :--- | :--- |
| `coms` | `/coms start` | Direct peer-to-peer | `coms_list`, `coms_send`, `coms_get` | Simple local session-to-session messaging |
| `coms-net` | `/coms-net start` or `/coms-net connect` | Local HTTP + SSE hub | `coms_net_list`, `coms_net_send`, `coms_net_get` | More durable multi-session coordination |

This page focuses on `coms-net`, the networked mode built around a local hub server.

### Main Components

| Component | What it does | User-visible effect |
| :--- | :--- | :--- |
| `/coms-net` wrapper | Starts, connects to, disconnects from, or stops the hub | You can bring the network up only when you need it |
| Hub server | Tracks peers, queues messages, and pushes updates over SSE | Replies arrive automatically while you keep working |
| Client in each Pi session | Registers identity, sends heartbeats, and receives inbound work | Each session can appear as a named peer |
| Pool widget | Renders connected peers, status, queue depth, and context usage | You can see who is online and busy at a glance |
| Messaging tools | `coms_net_list`, `coms_net_send`, `coms_net_get` | Agents can discover peers, delegate work, and inspect message state |

### Message Flow

1. A session activates `coms-net` and registers with the hub.
2. The hub assigns that session a project-scoped identity and opens an SSE stream.
3. Another session calls `coms_net_send` with a peer name and prompt.
4. The hub either delivers the message immediately or queues it if the target is busy.
5. The receiving session gets the prompt as a follow-up turn in Pi.
6. The receiver answers normally in chat.
7. The extension captures that final assistant reply and posts it back to the hub.
8. The original sender receives the result as a follow-up message automatically.

> **Tip:** In normal use, you send with `coms_net_send`, then end the turn. You do not need a polling loop unless you explicitly want a non-blocking status check with `coms_net_get`.

## Key Concepts

### Project-Scoped Peer Discovery

`coms-net` isolates peers by project namespace. If you do not pass `--project`, the wrapper derives one from the current working directory, so sessions in different repos do not accidentally see each other.

That is why `/coms-net` only shows peers from the same project group by default. The effect for users is simple: start multiple Pi sessions in one repo, and they naturally discover one another.

### Named Identities

Each session registers with a peer identity: name, purpose, model, color, cwd, and status. The wrapper uses `--cname` for peer naming so it does not conflict with Pi's own `--name` behavior.

This is what makes peer lists readable instead of showing only opaque session IDs. A user can target `planner`, `worker`, or another named peer directly rather than guessing which terminal to message.

### Heartbeats and Presence

Every connected session sends heartbeat updates on a fixed interval. Those updates include context-window usage, queue depth, and optional task summary data.

For users, that turns into a live presence model:
- `online` means the peer is healthy and actively reporting
- `stale` means heartbeats stopped recently
- `offline` means the peer was removed from the active registry

The pool widget and peer list output reflect those states, so you can avoid delegating work to a dead or overloaded session.

### Automatic Reply Capture

Inbound messages are delivered into Pi as follow-up turns. The important rule is that the receiver should answer normally in chat, because the extension captures that reply automatically and returns it to the sender.

> **Warning:** Do not use `coms_net_send` to reply to an inbound `coms-net` message. That starts a brand-new outbound conversation and can create a ping-pong loop.

This behavior matters because it keeps the experience conversational. To the user, cross-session messaging feels like asking a peer for help and getting a normal reply back in the same session.

### Structured Task Delegation

`coms_net_send` supports a `tasks` array alongside the prompt. Those task objects are shown to the receiving peer as structured work items instead of a loose prose blob.

This makes delegation clearer and easier to track when the receiving session uses the task system. For users, it means you can send a prompt plus a checklist, not just a paragraph.

### Hidden vs Discoverable Peers

Peers launched with `--explicit` stay off the default discovery list. They still exist, but they are meant to be contacted intentionally rather than advertised broadly.

This is useful when a helper session should not clutter the shared pool. Users see a cleaner peer list by default, while advanced workflows can still reveal explicit peers when needed.

### Local Server Discovery and Authentication

The hub writes project-local connection details under `~/.pi/coms-net/projects/<project>/`. That includes `server.json`, and for local loopback setups, a `server.secret.json` bearer token file with restricted permissions.

The user-visible effect is convenience with reasonable safety:
- local sessions can auto-discover the hub
- the wrapper can reconnect after reloads
- tokens are kept out of normal command output

> **Note:** By default, the hub is a local service. The wrapper starts it on loopback unless you intentionally change the bind settings.

### Queueing and Hop Limits

The hub enforces queue depth and hop limits. Queueing prevents dropped messages when a peer is busy, while hop limits stop agents from forwarding work forever.

For users, that shows up as more predictable behavior:
- busy peers do not lose messages
- runaway agent-to-agent loops stop instead of spiraling forever
- queue depth is visible in pool output and peer listings

## How It Affects the User

The internals mostly stay out of your way, but they explain several behaviors you will notice in daily use.

| What you see | What is happening underneath |
| :--- | :--- |
| `/coms-net start` brings peers online | The wrapper starts or reuses the local hub, then registers the current session |
| A live peer pool appears in the TUI | The client receives SSE updates and re-renders the widget from hub snapshots |
| A reply shows up later without polling | The receiver's normal assistant reply was captured and pushed back over the hub |
| A peer shows `stale` instead of disappearing immediately | Heartbeats stopped, but the server has not yet fully expired that peer |
| A delegated task arrives with structure | The sender included `tasks`, and the receiver rendered them as assigned work |
| Reconnecting after reload feels automatic | The wrapper persists activation state and re-registers on reload |

A few practical implications are worth remembering:

- `coms-net` is best when you want named peer sessions that stay coordinated over time.
- It is especially useful when one session should keep coding while another handles planning, review, or background work.
- If you only need a lightweight direct link, the older `coms` mode is still available alongside `coms-net`.

## Related Pages

- See [Managing Custom Agents](managing-custom-agents.html) for details.
- See [Running Background Agents and Scheduled Tasks](async-agents-and-cron.html) for details.
- See [Daemon & Websocket Networking](daemon-and-websockets.html) for details.
- See [Creating Slash Commands](custom-slash-commands.html) for details.

## Related Pages

- [Managing Custom Agents](managing-custom-agents.html)
- [Running Background Agents and Scheduled Tasks](async-agents-and-cron.html)
- [Daemon & Websocket Networking](daemon-and-websockets.html)
- [Using the Web Dashboard](using-the-web-dashboard.html)
- [Built-in Workflow Commands](built-in-workflows.html)

---

Source: background-dreaming.md

# Background Memory Consolidation (Dreaming)

Dreaming is Pi’s background memory-maintenance system. It periodically reads recent session history, extracts durable project knowledge, and refreshes the files that power long-term memory, skills, and enforcement metadata.

You should care because it changes what the agent remembers without requiring constant manual cleanup. Over time, recurring decisions, conventions, and corrections become easier for Pi to reuse, while stale or low-value memory is less likely to keep polluting future sessions.

## The Big Picture

### What Runs

| Component | What it does | User-visible effect |
|---|---|---|
| Dream worker | Runs a background `worker` agent that reads recent session transcripts and updates memory topic files. | New lessons, preferences, decisions, and completions appear without manual entry. |
| Rebuild pass | Re-scores and re-organizes memory topic files after dreaming and on a periodic timer. | Memory stays sorted, deduplicated, and ready for prompt injection. |
| Provenance merge | Merges pending provenance metadata into scored memory records. | Stored memories can keep a link back to their source session. |
| Promotion pass | Evaluates mature memories for enforcement metadata or queueing into the promotion file. | Repeated patterns can turn into stronger guardrails or skill candidates. |

### Trigger Points

| Trigger | Source | Default behavior |
|---|---|---|
| Automatic timer | Dreaming extension | Runs periodically while auto-dreaming is enabled. |
| Manual run | `/dream` | Starts a background pass immediately. |
| Session quit | Session shutdown hook | May queue one final pass when the session exits normally. |
| Rebuild timer | Rebuild worker | Re-scores topic files every 30 minutes. |

### End-to-End Flow

1. Pi decides whether a dream pass can run in the current session.
2. A background `worker` agent is launched with a task focused on memory maintenance.
3. The worker reads existing topic files under `.pi/memory/topics/`.
4. The worker scans recent session transcripts and extracts durable knowledge.
5. The worker may update topic files, create project skill files, and write promotion/provenance sidecars.
6. When the background job completes, Pi runs follow-up maintenance:
   - `rebuildAndOrganize(...)`
   - `mergeProvenancePending(...)`
   - `runPromotionPass(...)`

> **Note:** Dreaming runs only in the top-level orchestrator session, not inside spawned subagents.

## Key Concepts

### Topic Files

Dreaming treats the topic files in `.pi/memory/topics/` as the source of truth for project memory.

Current categories map to these files:

| Category | Topic file |
|---|---|
| `preference` | `preferences.md` |
| `lesson` | `lessons.md` |
| `pattern` | `patterns.md` |
| `decision` | `decisions.md` |
| `done` | `completions.md` |
| `mistake` | `mistakes.md` |

Each entry is a single markdown bullet such as:

```md
- [lesson] Use uv run for Python commands
- [preference] Keep commit messages short *(pinned)*
```

User-visible effect:

- Future sessions can reuse these entries through the memory system.
- Topic files stay readable and editable by humans.
- Pinned entries survive normal cleanup.

### Quality Gate

Dreaming does not blindly ingest every chat. The worker is instructed to prefer sessions with actual decisions, corrections, or completed work, and to skip trivial material.

Examples of high-signal material:

- A user correcting how commands should be run
- A repeated workflow that keeps showing up across sessions
- A completed feature or merged task worth remembering

Examples of low-signal material:

- Greetings
- Short one-off questions
- Sessions with little substantive work

User-visible effect:

- Memory quality improves more slowly, but with less noise.
- Pi is less likely to “remember” throwaway chatter as a project rule.

### Watermark Tracking

Dreaming uses a watermark file at `.pi/memory/.dream-watermark` to avoid reprocessing the same recent session history over and over.

It also limits how many historical sessions are processed in one pass.

User-visible effect:

- Automatic runs stay incremental instead of rescanning everything.
- Dreaming is less likely to monopolize background capacity after a long gap.

### Rebuild and Re-Scoring

After dreaming writes topic content, Pi runs `rebuildAndOrganize(...)` to rescore entries from the current topic files.

This keeps the scored memory state aligned with the human-readable files.

User-visible effect:

- New entries start influencing future prompts more quickly.
- Duplicate or weak entries are less likely to accumulate indefinitely.
- Topic ordering can shift as some memories become “hotter” than others.

### Provenance Sidecar

Dreaming can write pending provenance data to `.pi/memory/provenance-pending.json`.

That sidecar can include fields such as:

- `sourceSession`
- `derivedFrom`
- `informs`

After the dream job finishes, Pi merges that sidecar into scored memory records.

User-visible effect:

- Some memories can retain a trace back to where they came from.
- Provenance helps later tooling explain why a memory exists.

### Promotion Queue

Dreaming and related memory logic can append candidates to `.pi/memory/promotions.md`.

Promotion destinations currently include:

| Destination | Meaning |
|---|---|
| `memory` | Keep as regular memory |
| `skill` | Candidate for a project skill |
| `enforcement` | Candidate for code-enforced behavior |
| `project_rule` | Proposed rule only |
| `discard` | Not worth keeping |

Important promotion thresholds in the current code:

| Promotion type | Evidence threshold |
|---|---|
| Enforcement | `3` |
| Skill | `3` |
| Project rule | `5` |

User-visible effect:

- Repeated, strong patterns can graduate into stricter behavior.
- Not everything is auto-applied: project rules remain proposals until a human handles them.

> **Warning:** `project_rule` candidates are not auto-written into rule files. They stay proposed in the promotion queue.

### Skill Auto-Creation

The dream worker may create project-level skill files under `.pi/skills/<name>/SKILL.md` when it sees a recurring multi-step workflow.

This is separate from normal topic memory. It is meant for procedures, not just facts.

User-visible effect:

- Repeated workflows can become reusable agent skills.
- Future sessions may benefit from a structured checklist instead of a loose memory entry.

### Pinned and Enforced Entries

Dreaming is instructed to preserve entries marked with `*(pinned)*` and not rewrite entries marked `*(enforced)*`.

Why that matters to users:

- Pinned entries represent intentionally protected memory.
- Enforced entries are tied to enforcement metadata and should remain stable.

User-visible effect:

- Explicitly protected memory is safer from cleanup.
- Code-enforced guardrails are less likely to break during reorganization.

## How It Affects the User

### What You Will Notice

| Behavior | What it means |
|---|---|
| A moon icon appears in the status bar | A dream pass is currently running. |
| Pi starts following a repeated convention without being told again | Dreaming likely turned a repeated correction into stored memory. |
| New topic entries show up under `.pi/memory/topics/` | The worker extracted durable knowledge from recent sessions. |
| Promotion candidates appear in `.pi/memory/promotions.md` | A memory has crossed a threshold for stronger handling. |
| A project skill appears under `.pi/skills/` | Dreaming recognized a repeatable multi-step workflow. |

### Controls You Can Use

| Control | Effect |
|---|---|
| `/dream` | Starts a manual background pass. |
| `/dream-auto on` | Enables periodic dreaming for the current project. |
| `/dream-auto off` | Disables the automatic timer for the current project. |
| `dream_interval_hours` | Adjusts the automatic interval in project or global settings. |
| `PI_DREAM_INTERVAL_HOURS` | Environment-variable override for the interval. |

### When Dreaming Skips Work

Dreaming depends on an async-capable LLM path. In ACPX-based sessions, that usually means configuring both:

- `async_llm_provider`
- `async_llm_model`

If that path is unavailable, Pi skips the run and surfaces a warning in the UI.

User-visible effect:

- Manual `/dream` may appear to do nothing except show a warning.
- Auto-dreaming may remain enabled, but individual runs can be skipped until async LLM settings are valid.

> **Tip:** See [Configuration & Settings](configuration.html) for the exact settings used to control the dream interval and async LLM fallback.

## Related Pages

- See [Curating Project Memory](curating-project-memory.html) for details on manual memory entry and inspection.
- See [Memory Architecture](memory-architecture.html) for how scores, topic hotness, and prompt injection work.
- See [Running Background Agents and Scheduled Tasks](async-agents-and-cron.html) for the background execution model used by dreaming.
- See [Implementing Command Guards](safety-enforcements.html) for how promoted memory can affect enforcement behavior.
- See [Configuration & Settings](configuration.html) for the settings that control dream frequency and async LLM routing.

## Related Pages

- [Curating Project Memory](curating-project-memory.html)
- [Memory Architecture](memory-architecture.html)
- [Configuration & Settings](configuration.html)
- [Implementing Command Guards](safety-enforcements.html)
- [Running Background Agents and Scheduled Tasks](async-agents-and-cron.html)

---

Source: acpx-provider.md

# ACPX Provider Integration

> **Note:** `extensions/acpx-provider/` is a backward-compatible shim for consumers such as pi-sidecar. It exports `discoverAcpxModels`, `mapAcpxDiscoveredModels`, `modelIdToDisplayName`, and a no-op extension entry. Provider registration and streaming run through `extensions/providers/` (`AcpxDriver` + `ProviderDriverRegistry`). See [Configuration & Settings](configuration.html) for settings file locations and [External AI Agents & CLI](external-ai-agents.html) for CLI usage of external agents.

## `acpx_agents`

Agents registered as ACPX providers at session start.

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `acpx_agents` | `string[]` \| `string` | `[]` | Agent identifiers to create via `AcpxDriver`. Accepts a JSON array or a comma-separated string. Names are trimmed, lowercased, and filtered to `/^[a-z0-9_-]+$/`. |

**Resolution order:** project settings → global settings → `ACPX_AGENTS` env → `[]`.

**Driver mapping (`ACPX_AGENT_TO_DRIVER`):** `cursor` → `acpx`. Any other listed name falls back to driver kind `acpx` with `config.agent` set to that name.

```json
{
  "acpx_agents": ["cursor"]
}
```

```bash
export ACPX_AGENTS=cursor
```

**Effect:** For each agent, `extensions/providers/` creates a `ProviderInstance`, registers provider id `acpx-<agent>`, and exposes models under that provider.

> **Note:** During extension startup, each agent races `registry.createInstance` against `DISCOVERY_TIMEOUT_MS` (`30000`). Timed-out creates are torn down if they finish later.

## `loadAcpxRuntime()`

Resolves the `acpx/runtime` module. Memoized across calls; failed loads clear the cache.

**Module:** `extensions/acpx-provider/load-runtime.ts`

| Return field | Type | Description |
| :--- | :--- | :--- |
| `createAcpRuntime` | `Function` | Builds an ACP runtime for a cwd / session store / agent registry. |
| `createFileSessionStore` | `Function` | Filesystem session store factory. |
| `createAgentRegistry` | `Function` | ACPX agent registry factory. |

**Resolution order:**
1. Global npm root (`npm root -g`) package `acpx` → `dist/runtime.js`
2. Candidate roots: `/usr/local/lib/node_modules`, `~/.npm-global/lib/node_modules`
3. Package import `acpx/runtime`

```typescript
import { loadAcpxRuntime } from "./load-runtime.js";

const { createAcpRuntime, createFileSessionStore, createAgentRegistry } =
  await loadAcpxRuntime();
```

**Throws:** `Error` if runtime cannot be resolved (`npm install -g acpx` or package dependency `acpx` required).

## `discoverAcpxModels(agent, cwd?)`

Async discovery of available model ids for one ACPX agent. Creates a temporary oneshot session, reads `getStatus().models.availableModelIds`, closes the session, and deletes the temp state dir.

**Module:** `extensions/acpx-provider/index.ts`

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `agent` | `string` | (required) | Agent id. Must match `/^[a-z0-9_-]+$/i`. |
| `cwd` | `string` | `process.cwd()` | Working directory for the discovery session. |

**Returns:** `Promise<Array<{ id: string; name: string; provider: string }>>`

| Field | Description |
| :--- | :--- |
| `id` | `<agent>:<modelId>` |
| `name` | Display name from `modelIdToDisplayName(modelId)` plus ` (<agent>)` |
| `provider` | `acpx-<agent>` |

On discovery failure, returns `[]` (does not throw). Invalid `agent` throws `Error`.

```typescript
import { discoverAcpxModels } from "pi-orchestrator-config/extensions/acpx-provider";

const models = await discoverAcpxModels("cursor", "/path/to/project");
// [{ id: "cursor:gpt-5.4[...]", name: "Gpt 5.4 (cursor)", provider: "acpx-cursor" }]
```

> **Note:** Discovery uses `permissionMode: "deny-all"` and state under `~/.acpx/discover-<pid>-<uid>/`.

## `mapAcpxDiscoveredModels(agent, modelIds)`

Maps raw model id strings to pi-ai runtime `Model` objects for provider registration.

**Module:** `extensions/acpx-provider/runtime-models.ts`

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `agent` | `string` | (required) | Agent id used in model/provider ids. |
| `modelIds` | `readonly string[]` | (required) | Raw ids from ACPX status / snapshot. |

**Returns:** `Model[]` with `api: "acpx"` and `provider: "acpx-<agent>"`.

| Condition | Effect |
| :--- | :--- |
| `modelIds.length === 0` | Single fallback model `{ id: "<agent>:default", name: "<agent> (default)" }` |
| otherwise | One model per id: `{ id: "<agent>:<m>", name: "<Display> (<agent>)" }` |

```typescript
import { mapAcpxDiscoveredModels } from "pi-orchestrator-config/extensions/acpx-provider";

const models = mapAcpxDiscoveredModels("cursor", ["gpt-5.4[context=272k]"]);
// id: "cursor:gpt-5.4[context=272k]", name: "Gpt 5.4 (cursor)", provider: "acpx-cursor"
```

## `modelIdToDisplayName(modelId)`

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `modelId` | `string` | (required) | Raw ACPX model id. |

**Returns:** `string` — bracket suffix stripped, hyphens → spaces, title-cased.

```typescript
import { modelIdToDisplayName } from "pi-orchestrator-config/extensions/acpx-provider";

modelIdToDisplayName("gpt-5.4[context=272k]"); // "Gpt 5.4"
```

## `buildAmbientLoginAuth(opts)`

Builds `/login` api-key auth that stores a configured marker when `isConfigured()` is true.

**Module:** `extensions/shared/create-runtime-provider.ts`
**Wired by:** `registerAcpxAgent()` in `extensions/providers/index.ts`

| Option | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `displayName` | `string` | (required) | Label in the `/login` prompt (`ACPX <agent>`). |
| `isConfigured` | `() => boolean \| Promise<boolean>` | (required) | Gate for login/resolve/check. For ACPX: `acpxInstances.has(agent)`. |
| `sourceLabel` | `string` | (required) | Auth source string (e.g. `"cursor acpx runtime"`). |

**Returns:** `ProviderAuth["apiKey"]` for `createRuntimeProvider()` / `createProvider()`.

```typescript
import { buildAmbientLoginAuth } from "../shared/create-runtime-provider.js";

// Wired in extensions/providers/index.ts during registerAcpxAgent()
const auth = buildAmbientLoginAuth({
  displayName: "ACPX cursor",
  isConfigured: () => acpxInstances.has("cursor"),
  sourceLabel: "cursor acpx runtime",
});
```

## `AcpxDriver`

Built-in `ProviderDriver` (`driverKind: "acpx"`) in `extensions/providers/acpx-driver.ts`. Listed in `BUILT_IN_DRIVERS` and created through `ProviderDriverRegistry` in `extensions/providers/index.ts`.

### Config (`AcpxConfig`)

| Field | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `agent` | `string` | `"cursor"` | ACPX agent name passed to `ensureSession`. |
| `enabled` | `boolean` | `true` | Instance enabled flag. |

### `AcpxDriver.probe(config)`

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `config` | `AcpxConfig` | (required) | Passed by the registry; availability checks `loadAcpxRuntime()` only. |

**Returns:** `{ available: true }` if `loadAcpxRuntime()` resolves; otherwise `{ available: false, reason: string }`.

### `AcpxDriver.create(input)`

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `input.instanceId` | `string` | (required) | Registry instance id (`acpx-<agent>`). |
| `input.config` | `AcpxConfig` | (required) | Agent + enabled. |
| `input.cwd` | `string` | (required) | Project cwd; hashed to 12-char `cwdSlug` for state/session keys. |
| `input.displayName` | `string` | `` `ACPX ${agent}` `` | UI display name. |
| `input.enabled` | `boolean` | from input | Instance enabled flag. |

**Returns:** `ProviderInstance` with managed model snapshot, adapter, and `dispose` → `adapter.stopAll()`.

**Create side effects:**
- Runtime state dir: `~/.acpx/pi-<cwdSlug>/`
- `permissionMode: "approve-all"`
- Initial persistent session key: `pi-<agent>-default`
- Snapshot refresh uses a separate oneshot discovery helper

```typescript
const instance = await registry.createInstance(
  "acpx-cursor",
  { driver: "acpx", config: { agent: "cursor" }, enabled: true },
  projectCwd,
);
```

### Adapter: `startSession(opts)`

| Option | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `model` | `string` | `"default"` | ACPX model id (suffix of pi model id after `:`). |
| `systemPrompt` | `string` | from `buildExternalSystemPrompt` | Prompt applied when the handle is first created for that model. |
| `cwd` | `string` | (required by type) | Project cwd (adapter already bound at create). |

**Returns:** `{ sessionId, model }` where `sessionId` is `pi-<agent>[-<sanitizedModel>]-<cwdSlug>`.

### Adapter: `sendTurn(handle, prompt, opts?)`

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `handle` | `SessionHandle` | (required) | From `startSession`. |
| `prompt` | `string` | (required) | Latest user message text only. |
| `opts.signal` | `AbortSignal` | — | Aborts the turn. |
| `opts.onEvent` | `(event) => void` | — | Receives `{ kind: "text_delta" \| "thinking_delta", text }`. |

**Returns:** `TurnResult` — `{ text, thinking?, stopReason?, usage? }`.

**Stream mapping:** ACPX `text_delta` with `stream === "thought"` → `thinking_delta`; other `text_delta` → `text_delta`.

```typescript
const turn = runtime.startTurn({
  handle: acpxHandle,
  text: prompt,
  mode: "prompt",
  requestId: `pi-${Date.now()}-${randomSuffix}`,
  signal: abortController.signal,
});

for await (const event of turn.events) {
  if (event.type === "text_delta" && event.text) {
    // event.stream === "thought" → thinking_delta; else → text_delta
  }
}
```

> **Warning:** Only the latest user message is sent each turn. Full historical message arrays are not forwarded to the ACPX runtime.

### Adapter: `stopSession` / `stopAll`

| Method | Effect |
| :--- | :--- |
| `stopSession(handle)` | `runtime.close` for that model handle; clears prompt/usage tracking for the key. |
| `stopAll()` | Closes all handles; clears handle maps. Called from instance `dispose`. |

## Stream bridge (`StreamAssembler`)

`extensions/providers/index.ts` wraps `adapter.sendTurn` and feeds driver events into `StreamAssembler` (`extensions/shared/stream-builder.ts`) for native pi assistant streams.

| Step | Action |
| :--- | :--- |
| 1 | Resolve instance from `acpxInstances` |
| 2 | Parse model id: `agent:modelId` → driver model suffix |
| 3 | `adapter.startSession({ model, systemPrompt, cwd })` |
| 4 | `extractLatestUserMessage(context)` → prompt |
| 5 | `adapter.sendTurn(..., { onEvent: assembler.handleEvent })` |
| 6 | `assembler.finalize({ finalText, finalThinking, stopReason, usage })` |

## `runtime.ensureSession(options)`

ACPX runtime API used by discovery and the driver adapter.

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `sessionKey` | `string` | (required) | Stable key (`discover-...` or `pi-<agent>...-<cwdSlug>`). |
| `agent` | `string` | (required) | Target agent id. |
| `mode` | `string` | (required) | `"oneshot"` for throwaway discovery; `"persistent"` for stream sessions. |
| `cwd` | `string` | (required) | Session working directory. |
| `sessionOptions` | `object` | omitted if empty | Optional `{ model?, systemPrompt? }`. |

```typescript
const handle = await runtime.ensureSession({
  sessionKey: "pi-cursor-gpt-4-a1b2c3d4e5f6",
  agent: "cursor",
  mode: "persistent",
  cwd: "/home/user/project",
  sessionOptions: {
    model: "gpt-4",
    systemPrompt: "You are being used as a backend LLM...",
  },
});
```

## Session shutdown

| Hook | Effect |
| :--- | :--- |
| `session_shutdown` | Stops CLI session reaper; `registry.teardownAll()` (each ACPX instance `dispose` → `adapter.stopAll()`); clears `cliInstances` and `acpxInstances`. |

```typescript
pi.on("session_shutdown", async () => {
  stopCliSessionReaper();
  await registry.teardownAll();
  cliInstances.clear();
  acpxInstances.clear();
});
```

## Related Pages

- [External AI Agents & CLI](external-ai-agents.html)
- [Configuration & Settings](configuration.html)
- [Pi Sidecar HTTP API](pi-sidecar.html)
- [Google Vertex Claude Provider](vertex-claude-provider.html)
- [Running Background Agents and Scheduled Tasks](async-agents-and-cron.html)

---
