Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions cmd/chief/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type TUIOptions struct {
Merge bool
Force bool
NoRetry bool
Agent string // --agent claude|codex
Agent string // --agent claude|codex|opencode|cursor
AgentPath string // --agent-path
}

Expand Down Expand Up @@ -134,7 +134,7 @@ func parseAgentFlags(args []string, startIdx int) (agentName, agentPath string,
i++
agentName = args[i]
} else {
fmt.Fprintf(os.Stderr, "Error: --agent requires a value (claude, codex, or opencode)\n")
fmt.Fprintf(os.Stderr, "Error: --agent requires a value (claude, codex, opencode, or cursor)\n")
os.Exit(1)
}
case strings.HasPrefix(arg, "--agent="):
Expand Down Expand Up @@ -530,7 +530,7 @@ Commands:
help Show this help message

Global Options:
--agent <provider> Agent CLI to use: claude (default), codex, or opencode
--agent <provider> Agent CLI to use: claude (default), codex, opencode, or cursor
--agent-path <path> Custom path to agent CLI binary
--max-iterations N, -n N Set maximum iterations (default: dynamic)
--no-retry Disable auto-retry on agent crashes
Expand All @@ -557,6 +557,7 @@ Examples:
Launch auth PRD with 5 max iterations
chief --verbose Launch with raw agent output visible
chief --agent codex Use Codex CLI instead of Claude
chief --agent cursor Use Cursor CLI as agent
chief new Create PRD in .chief/prds/main/
chief new auth Create PRD in .chief/prds/auth/
chief new auth "JWT authentication for REST API"
Expand Down
1 change: 1 addition & 0 deletions docs/.vitepress/theme/components/AgentSupport.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const agents = [
{ name: 'Claude Code', description: 'By Anthropic' },
{ name: 'Codex CLI', description: 'By OpenAI' },
{ name: 'OpenCode', description: 'Open source' },
{ name: 'Cursor CLI', description: 'By Cursor' },
]
</script>

Expand Down
2 changes: 1 addition & 1 deletion docs/concepts/prd-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Each PRD lives in its own subdirectory inside `.chief/prds/`:
- **`prd.md`** — Written by you. Provides context, background, and guidance.
- **`prd.json`** — The source of truth. Chief reads, updates, and drives execution from this file.
- **`progress.md`** — Written by the agent. Tracks what was done, what changed, and what was learned.
- **`claude.log`** (or `codex.log` / `opencode.log`) — Written by Chief. Raw output from the agent for debugging.
- **`claude.log`** (or `codex.log` / `opencode.log` / `cursor.log`) — Written by Chief. Raw output from the agent for debugging.

## prd.md — The Human-Readable File

Expand Down
9 changes: 9 additions & 0 deletions docs/guide/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ To use [OpenCode CLI](https://opencode.ai) as an alternative:
2. Ensure `opencode` is on your PATH, or set `agent.cliPath` in `.chief/config.yaml` (see [Configuration](/reference/configuration#agent)).
3. Run Chief with `chief --agent opencode` or set `CHIEF_AGENT=opencode`, or set `agent.provider: opencode` in `.chief/config.yaml`.

### Option D: Cursor CLI

To use [Cursor CLI](https://cursor.com/docs/cli/overview) as the agent:

1. Install Cursor CLI per the [official docs](https://cursor.com/docs/cli/overview)
2. Ensure `agent` is on your PATH, or set `agent.cliPath` in `.chief/config.yaml`.
3. Run `agent login` for authentication.
4. Run Chief with `chief --agent cursor` or set `CHIEF_AGENT=cursor`, or set `agent.provider: cursor` in `.chief/config.yaml`.

### Optional: GitHub CLI (`gh`)

If you want Chief to automatically create pull requests when a PRD completes, install the [GitHub CLI](https://cli.github.com/):
Expand Down
2 changes: 2 additions & 0 deletions docs/guide/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ Before you begin, make sure you have:
- [Claude Code](https://github.com/anthropics/claude-code) (default)
- [Codex CLI](https://developers.openai.com/codex/cli/reference)
- [OpenCode CLI](https://opencode.ai/docs/)
- [Cursor CLI](https://cursor.com/docs/cli/overview)
- A project you want to work on (or create a new one)

::: tip Verify your agent CLI is working
Run the version command for your agent to confirm it's installed:
- `claude --version` (Claude Code)
- `codex --version` (Codex)
- `opencode --version` (OpenCode)
- `agent --version` (Cursor CLI)
:::

## Step 1: Install Chief
Expand Down
17 changes: 13 additions & 4 deletions docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Chief stores project-level settings in `.chief/config.yaml`. This file is create

```yaml
agent:
provider: claude # or "codex" or "opencode"
provider: claude # or "codex", "opencode", or "cursor"
cliPath: "" # optional path to CLI binary
worktree:
setup: "npm install"
Expand All @@ -27,7 +27,7 @@ onComplete:

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `agent.provider` | string | `"claude"` | Agent CLI to use: `claude`, `codex`, or `opencode` |
| `agent.provider` | string | `"claude"` | Agent CLI to use: `claude`, `codex`, `opencode`, or `cursor` |
| `agent.cliPath` | string | `""` | Optional path to the agent binary (e.g. `/usr/local/bin/opencode`). If empty, Chief uses the provider name from PATH. |
| `worktree.setup` | string | `""` | Shell command to run in new worktrees (e.g., `npm install`, `go mod download`) |
| `onComplete.push` | bool | `false` | Automatically push the branch to remote when a PRD completes |
Expand Down Expand Up @@ -88,7 +88,7 @@ These settings are saved to `.chief/config.yaml` and can be changed at any time

| Flag | Description | Default |
|------|-------------|---------|
| `--agent <provider>` | Agent CLI to use: `claude`, `codex`, or `opencode` | From config / env / `claude` |
| `--agent <provider>` | Agent CLI to use: `claude`, `codex`, `opencode`, or `cursor` | From config / env / `claude` |
| `--agent-path <path>` | Custom path to the agent CLI binary | From config / env |
| `--max-iterations <n>`, `-n` | Loop iteration limit | Dynamic |
| `--no-retry` | Disable auto-retry on agent crashes | `false` |
Expand All @@ -102,7 +102,7 @@ When `--max-iterations` is not specified, Chief calculates a dynamic limit based

## Agent

Chief can use **Claude Code** (default), **Codex CLI**, or **OpenCode CLI** as the agent. Choose via:
Chief can use **Claude Code** (default), **Codex CLI**, **OpenCode CLI**, or **Cursor CLI** as the agent. Choose via:

- **Config:** `agent.provider: opencode` and optionally `agent.cliPath: /path/to/opencode` in `.chief/config.yaml`
- **Environment:** `CHIEF_AGENT=opencode`, `CHIEF_AGENT_PATH=/path/to/opencode`
Expand All @@ -122,6 +122,15 @@ claude config set model claude-3-opus-20240229

See [Claude Code documentation](https://github.com/anthropics/claude-code) for details.

When using Cursor CLI:

```bash
# Authentication (or set CURSOR_API_KEY for headless)
agent login
```

Chief runs Cursor in headless mode with `--trust` and `--force` so it can modify files without prompts. See [Cursor CLI documentation](https://cursor.com/docs/cli/overview) for details.

## Permission Handling

Some agents (like Claude Code) ask for permission before executing bash commands, writing files, and making network requests. Chief automatically configures the agent for autonomous operation by disabling these prompts.
Expand Down
17 changes: 12 additions & 5 deletions docs/troubleshooting/common-issues.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ Error: OpenCode CLI not found in PATH. Install it or set agent.cliPath in .chief
cliPath: /usr/local/bin/opencode
```
Verify with `opencode --version` (or your `cliPath`).
- **Cursor:** Install [Cursor CLI](https://cursor.com/docs/cli/overview) (`curl https://cursor.com/install -fsS | bash`) and ensure `agent` is in PATH, or set the path in config:
```yaml
agent:
provider: cursor
cliPath: /path/to/agent
```
Run `agent login`. Verify with `agent --version` (or your `cliPath`).

## Permission Denied

Expand All @@ -63,9 +70,9 @@ Chief automatically configures the agent for autonomous operation by disabling p

**Solution:**

1. Check the agent log for errors (the log file matches your agent: `claude.log`, `codex.log`, or `opencode.log`):
1. Check the agent log for errors (the log file matches your agent: `claude.log`, `codex.log`, `opencode.log`, or `cursor.log`):
```bash
tail -100 .chief/prds/your-prd/claude.log # or codex.log / opencode.log
tail -100 .chief/prds/your-prd/claude.log # or codex.log / opencode.log / cursor.log
```

2. Manually mark story complete if appropriate:
Expand All @@ -89,7 +96,7 @@ Chief automatically configures the agent for autonomous operation by disabling p

1. Check the agent log for what the agent is doing:
```bash
tail -f .chief/prds/your-prd/claude.log # or codex.log / opencode.log
tail -f .chief/prds/your-prd/claude.log # or codex.log / opencode.log / cursor.log
```

2. Simplify the current story's acceptance criteria
Expand Down Expand Up @@ -118,7 +125,7 @@ Chief automatically configures the agent for autonomous operation by disabling p

2. Or investigate why it's taking so many iterations:
- Story too complex? Split it
- Stuck in a loop? Check the agent log (`claude.log`, `codex.log`, or `opencode.log`)
- Stuck in a loop? Check the agent log (`claude.log`, `codex.log`, `opencode.log`, or `cursor.log`)
- Unclear acceptance criteria? Clarify them

## "No PRD Found"
Expand Down Expand Up @@ -260,4 +267,4 @@ If none of these solutions help:
3. Open a new issue with:
- Chief version (`chief --version`)
- Your `prd.json` (sanitized)
- Relevant agent log excerpts (e.g. `claude.log`, `codex.log`, or `opencode.log`)
- Relevant agent log excerpts (e.g. `claude.log`, `codex.log`, `opencode.log`, or `cursor.log`)
113 changes: 113 additions & 0 deletions internal/agent/cursor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package agent

import (
"context"
"encoding/json"
"os/exec"
"strings"

"github.com/minicodemonkey/chief/internal/loop"
)

// CursorProvider implements loop.Provider for the Cursor CLI (agent).
type CursorProvider struct {
cliPath string
}

// NewCursorProvider returns a Provider for the Cursor CLI.
// If cliPath is empty, "agent" is used.
func NewCursorProvider(cliPath string) *CursorProvider {
if cliPath == "" {
cliPath = "agent"
}
return &CursorProvider{cliPath: cliPath}
}

// Name implements loop.Provider.
func (p *CursorProvider) Name() string { return "Cursor" }

// CLIPath implements loop.Provider.
func (p *CursorProvider) CLIPath() string { return p.cliPath }

// LoopCommand implements loop.Provider.
// Prompt is supplied via stdin; Cursor CLI reads it when -p has no argument.
func (p *CursorProvider) LoopCommand(ctx context.Context, prompt, workDir string) *exec.Cmd {
cmd := exec.CommandContext(ctx, p.cliPath,
"-p",
"--output-format", "stream-json",
"--force",
"--workspace", workDir,
"--trust",
)
cmd.Dir = workDir
cmd.Stdin = strings.NewReader(prompt)
return cmd
}

// InteractiveCommand implements loop.Provider.
func (p *CursorProvider) InteractiveCommand(workDir, prompt string) *exec.Cmd {
cmd := exec.Command(p.cliPath, prompt)
cmd.Dir = workDir
return cmd
}

// ConvertCommand implements loop.Provider.
// Prompt is supplied via stdin.
func (p *CursorProvider) ConvertCommand(workDir, prompt string) (*exec.Cmd, loop.OutputMode, string, error) {
cmd := exec.Command(p.cliPath, "-p", "--output-format", "text", "--workspace", workDir)
cmd.Dir = workDir
cmd.Stdin = strings.NewReader(prompt)
return cmd, loop.OutputStdout, "", nil
}

// FixJSONCommand implements loop.Provider.
// Prompt is supplied via stdin.
func (p *CursorProvider) FixJSONCommand(prompt string) (*exec.Cmd, loop.OutputMode, string, error) {
cmd := exec.Command(p.cliPath, "-p", "--output-format", "text")
cmd.Stdin = strings.NewReader(prompt)
return cmd, loop.OutputStdout, "", nil
}

// ParseLine implements loop.Provider.
func (p *CursorProvider) ParseLine(line string) *loop.Event {
return loop.ParseLineCursor(line)
}

// LogFileName implements loop.Provider.
func (p *CursorProvider) LogFileName() string { return "cursor.log" }

// CleanOutput extracts the result from Cursor's json or stream-json output.
// For stream-json, finds the last type "result", subtype "success" and returns its result field.
// For single-line json, parses and returns result.
func (p *CursorProvider) CleanOutput(output string) string {
output = strings.TrimSpace(output)
if output == "" {
return output
}
// Try single JSON object (json output format)
var single struct {
Type string `json:"type"`
Subtype string `json:"subtype,omitempty"`
Result string `json:"result,omitempty"`
}
if json.Unmarshal([]byte(output), &single) == nil && single.Type == "result" && single.Subtype == "success" && single.Result != "" {
return single.Result
}
// NDJSON: find last result/success line
lines := strings.Split(output, "\n")
for i := len(lines) - 1; i >= 0; i-- {
line := strings.TrimSpace(lines[i])
if line == "" {
continue
}
var ev struct {
Type string `json:"type"`
Subtype string `json:"subtype,omitempty"`
Result string `json:"result,omitempty"`
}
if json.Unmarshal([]byte(line), &ev) == nil && ev.Type == "result" && ev.Subtype == "success" && ev.Result != "" {
return ev.Result
}
}
return output
}
Loading