README.md

<p align="center">
  <img src="assets/prompt_runner_sdk.svg" alt="Prompt Runner SDK" width="200" height="200">
</p>

<h1 align="center">Prompt Runner SDK</h1>

<p align="center">
  <strong>Run ordered prompt sequences with streaming output, automatic git commits, and dual LLM support</strong>
</p>

<p align="center">
  <a href="https://hex.pm/packages/prompt_runner_sdk"><img src="https://img.shields.io/hexpm/v/prompt_runner_sdk.svg" alt="Hex.pm"></a>
  <a href="https://hexdocs.pm/prompt_runner_sdk"><img src="https://img.shields.io/badge/docs-hexdocs-blue.svg" alt="Documentation"></a>
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
</p>

---

## What It Does

Prompt Runner SDK executes a sequence of LLM prompts against your codebase, with:

- **Streaming output** - See responses as they're generated
- **Automatic git commits** - Each prompt gets its own commit
- **Dual LLM support** - Claude Agent SDK and Codex SDK through one interface
- **Progress tracking** - Resume interrupted runs with `--continue`

## Two Use Cases

### Use Case 1: Single Repository (Most Common)

You have one codebase. Prompts run against it. Commits go to it.

```elixir
# runner_config.exs
%{
  project_dir: "/path/to/your/project",
  prompts_file: "prompts.txt",
  commit_messages_file: "commit-messages.txt",
  progress_file: ".progress",
  log_dir: "logs",
  model: "sonnet",
  llm: %{sdk: "claude_agent_sdk"}
}
```

```
# prompts.txt
01|1|5|Setup database|001-setup.md
02|1|8|Add API layer|002-api.md
```

```
# commit-messages.txt
=== COMMIT 01 ===
feat: setup database schema

=== COMMIT 02 ===
feat: add API layer
```

Run it:
```bash
mix run run_prompts.exs -c runner_config.exs --run 01
mix run run_prompts.exs -c runner_config.exs --run 02
```

**Example:** `examples/simple/`

### Use Case 2: Multiple Repositories

You have multiple codebases. Each prompt specifies which repos it modifies.

```elixir
# runner_config.exs
%{
  project_dir: "/path/to/workspace",  # LLM working directory
  target_repos: [
    %{name: "frontend", path: "/path/to/frontend", default: true},
    %{name: "backend", path: "/path/to/backend"}
  ],
  prompts_file: "prompts.txt",
  commit_messages_file: "commit-messages.txt",
  progress_file: ".progress",
  log_dir: "logs",
  model: "sonnet",
  llm: %{sdk: "claude_agent_sdk"}
}
```

```
# prompts.txt (add TARGET_REPOS column)
01|1|5|Setup both|001-setup.md|frontend,backend
02|1|8|Frontend only|002-frontend.md|frontend
03|1|8|Backend only|003-backend.md|backend
```

```
# commit-messages.txt (use repo-specific markers)
=== COMMIT 01:frontend ===
feat(frontend): initial setup

=== COMMIT 01:backend ===
feat(backend): initial setup

=== COMMIT 02:frontend ===
feat(frontend): add components

=== COMMIT 03:backend ===
feat(backend): add API routes
```

**Example:** `examples/multi_repo_dummy/`

## Quick Start

```bash
# Clone and setup
git clone https://github.com/nshkrdotcom/prompt_runner_sdk.git
cd prompt_runner_sdk
mix deps.get

# Run the multi-repo example (recommended starting point)
bash examples/multi_repo_dummy/setup.sh
mix run run_prompts.exs -c examples/multi_repo_dummy/runner_config.exs --list
mix run run_prompts.exs -c examples/multi_repo_dummy/runner_config.exs --run 01
```

## Installation

```elixir
def deps do
  [{:prompt_runner_sdk, "~> 0.1.1"}]
end
```

## CLI Commands

```bash
# List prompts and their status
mix run run_prompts.exs -c config.exs --list

# Preview what would run (no execution)
mix run run_prompts.exs -c config.exs --dry-run 01

# Run a single prompt
mix run run_prompts.exs -c config.exs --run 01

# Run all prompts
mix run run_prompts.exs -c config.exs --run --all

# Resume from last completed
mix run run_prompts.exs -c config.exs --run --continue

# Run without committing
mix run run_prompts.exs -c config.exs --run 01 --no-commit
```

## Dual LLM Support

Switch between Claude and Codex per-prompt:

```elixir
%{
  llm: %{
    sdk: "claude_agent_sdk",  # default
    model: "sonnet",
    prompt_overrides: %{
      "03" => %{sdk: "codex_sdk", model: "gpt-5.1-codex"}
    }
  }
}
```

## File Format Reference

### prompts.txt

Format: `NUM|PHASE|SP|NAME|FILE[|TARGET_REPOS]`

| Field | Description |
|-------|-------------|
| NUM | Prompt number (01, 02, ...) |
| PHASE | Phase grouping (1-5) |
| SP | Story points (for tracking) |
| NAME | Display name |
| FILE | Markdown file with prompt content |
| TARGET_REPOS | Optional: comma-separated repo names |

### commit-messages.txt

Single repo: `=== COMMIT NN ===`
Multi repo: `=== COMMIT NN:repo_name ===`

### runner_config.exs

```elixir
%{
  # Required
  project_dir: "/path/to/project",
  prompts_file: "prompts.txt",
  commit_messages_file: "commit-messages.txt",
  progress_file: ".progress",
  log_dir: "logs",
  model: "sonnet",

  # Optional: multi-repo support
  target_repos: [
    %{name: "app", path: "/path/to/app", default: true},
    %{name: "lib", path: "/path/to/lib"}
  ],

  # Optional: LLM configuration
  llm: %{
    sdk: "claude_agent_sdk",
    model: "sonnet",
    permission_mode: :accept_edits,
    allowed_tools: ["Read", "Write", "Bash"],
    prompt_overrides: %{}
  },

  # Optional: display
  log_mode: :compact,
  phase_names: %{1 => "Setup", 2 => "Implementation"}
}
```

## Architecture

<p align="center">
  <img src="assets/architecture.svg" alt="Prompt Runner SDK Architecture" width="700">
</p>

## Examples

| Example | Description |
|---------|-------------|
| `examples/simple/` | Single repo, dual LLM (Claude + Codex) |
| `examples/multi_repo_dummy/` | Multiple repos, per-repo commits |

## Development

```bash
mix test          # Run tests
mix credo --strict # Lint
mix dialyzer       # Type check
mix docs           # Generate docs
```

## License

MIT - see [LICENSE](LICENSE)

---

<p align="center">
  Made with 💚 by <a href="https://github.com/nshkrdotcom">nshkrdotcom</a>
</p>