Skip to content
Merged
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
19 changes: 17 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,25 @@ jobs:
bun test src/plugins/trackers/builtin/beads/index.test.ts --coverage --coverage-reporter=text --coverage-reporter=lcov 2>&1 | tee -a coverage-output.txt
cp coverage/lcov.info coverage-parts/beads.lcov

echo "=== Running src/plugins/ (excluding beads-bv and beads-rust) ===" | tee -a coverage-output.txt
echo "=== Running src/plugins/ (excluding beads-bv, beads-rust, and linear) ===" | tee -a coverage-output.txt
bun test src/plugins/agents/ src/plugins/trackers/builtin/beads.test.ts --coverage --coverage-reporter=text --coverage-reporter=lcov 2>&1 | tee -a coverage-output.txt || true
cp coverage/lcov.info coverage-parts/plugins.lcov || true

# Linear tracker tests: client.test.ts and index.test.ts both use mock.module()
# (client mocks @linear/sdk, index mocks ./client.js) so they must run in separate processes.
# body.test.ts is pure functions with no mocks and can batch with convert.test.ts.
echo "=== Running linear body.test.ts + convert.test.ts ===" | tee -a coverage-output.txt
bun test src/plugins/trackers/builtin/linear/body.test.ts src/commands/convert.test.ts --coverage --coverage-reporter=text --coverage-reporter=lcov 2>&1 | tee -a coverage-output.txt
cp coverage/lcov.info coverage-parts/linear-body.lcov

echo "=== Running linear client.test.ts (isolated) ===" | tee -a coverage-output.txt
bun test src/plugins/trackers/builtin/linear/client.test.ts --coverage --coverage-reporter=text --coverage-reporter=lcov 2>&1 | tee -a coverage-output.txt
cp coverage/lcov.info coverage-parts/linear-client.lcov

echo "=== Running linear index.test.ts (isolated) ===" | tee -a coverage-output.txt
bun test src/plugins/trackers/builtin/linear/index.test.ts --coverage --coverage-reporter=text --coverage-reporter=lcov 2>&1 | tee -a coverage-output.txt
cp coverage/lcov.info coverage-parts/linear-index.lcov

echo "=== Running src/session/ ===" | tee -a coverage-output.txt
bun test src/session/ --coverage --coverage-reporter=text --coverage-reporter=lcov 2>&1 | tee -a coverage-output.txt
cp coverage/lcov.info coverage-parts/session.lcov
Expand Down Expand Up @@ -207,7 +222,7 @@ jobs:
uses: codecov/codecov-action@v4
with:
# Upload all batch coverage files - Codecov will merge them correctly
files: ./coverage-parts/tests.lcov,./coverage-parts/tests-beads-rust-bv.lcov,./coverage-parts/tests-info.lcov,./coverage-parts/doctor.lcov,./coverage-parts/info.lcov,./coverage-parts/skills.lcov,./coverage-parts/run.lcov,./coverage-parts/config.lcov,./coverage-parts/engine.lcov,./coverage-parts/beads-bv.lcov,./coverage-parts/beads-rust.lcov,./coverage-parts/beads.lcov,./coverage-parts/plugins.lcov,./coverage-parts/session.lcov,./coverage-parts/sandbox.lcov,./coverage-parts/wizard.lcov,./coverage-parts/setup.lcov,./coverage-parts/skill-installer-spawn.lcov,./coverage-parts/migration-install.lcov,./coverage-parts/templates.lcov,./coverage-parts/tui.lcov,./coverage-parts/prd.lcov,./coverage-parts/chat.lcov,./coverage-parts/parallel.lcov
files: ./coverage-parts/tests.lcov,./coverage-parts/tests-beads-rust-bv.lcov,./coverage-parts/tests-info.lcov,./coverage-parts/doctor.lcov,./coverage-parts/info.lcov,./coverage-parts/skills.lcov,./coverage-parts/run.lcov,./coverage-parts/config.lcov,./coverage-parts/engine.lcov,./coverage-parts/beads-bv.lcov,./coverage-parts/beads-rust.lcov,./coverage-parts/beads.lcov,./coverage-parts/plugins.lcov,./coverage-parts/linear-body.lcov,./coverage-parts/linear-client.lcov,./coverage-parts/linear-index.lcov,./coverage-parts/session.lcov,./coverage-parts/sandbox.lcov,./coverage-parts/wizard.lcov,./coverage-parts/setup.lcov,./coverage-parts/skill-installer-spawn.lcov,./coverage-parts/migration-install.lcov,./coverage-parts/templates.lcov,./coverage-parts/tui.lcov,./coverage-parts/prd.lcov,./coverage-parts/chat.lcov,./coverage-parts/parallel.lcov
fail_ci_if_error: false
verbose: true
env:
Expand Down
8 changes: 7 additions & 1 deletion bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

119 changes: 119 additions & 0 deletions docs/linear-tracker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Linear Tracker Plugin

Use Linear as a task tracker for Ralph TUI. Tasks are managed as child issues under a parent (epic) issue in Linear.

## Setup

### 1. Get a Linear API Key

Create a personal API key at: Settings > API > Personal API Keys

### 2. Configure Authentication

Set the `LINEAR_API_KEY` environment variable:

```bash
export LINEAR_API_KEY="lin_api_..."
```

Or add it to your project config (`.ralph-tui/config.toml`):

```toml
[[trackers]]
name = "linear"
plugin = "linear"

[trackers.options]
apiKey = "lin_api_..."
```

Auth precedence: explicit config `apiKey` overrides `LINEAR_API_KEY` env var.

## Converting a PRD to Linear Issues

Use `convert --to linear` to import a PRD into Linear as a parent issue with child issues:

```bash
# Basic: create parent + children in the ENG team
ralph-tui convert --to linear --team ENG ./prd.md

# Use an existing parent issue
ralph-tui convert --to linear --team ENG --parent ENG-123 ./prd.md

# With project and labels
ralph-tui convert --to linear --team ENG --project "Q1 Sprint" --labels "backend,mvp" ./prd.md
```

### Options

| Flag | Required | Description |
|------|----------|-------------|
| `--team <key>` | Yes | Linear team key (e.g., `ENG`) |
| `--parent <issue>` | No | Existing parent issue key or UUID. Auto-creates if omitted. |
| `--project <name>` | No | Linear project name or UUID |
| `--labels <list>` | No | Comma-separated labels to apply |

Each PRD user story becomes a child issue with:
- Title: `<story-id>: <story-title>`
- Structured markdown body with Ralph metadata, description, and acceptance criteria
- Native Linear blocking relations from PRD `dependsOn` fields

## Running Tasks from Linear

Run Ralph against child issues of a parent (epic) issue:

```bash
ralph-tui run --tracker linear --epic ENG-123
```

The `--epic` flag is required for the Linear tracker in MVP. It accepts either an issue key (`ENG-123`) or a UUID.

### How It Works

1. Ralph fetches all child issues under the specified parent
2. Tasks are ordered by `Ralph Priority` metadata embedded in issue bodies (ascending, lower = higher priority)
3. In-progress tasks are preferred over open tasks
4. Dependency-blocked tasks are excluded from selection
5. On task completion, Ralph moves the issue to the "completed" workflow state and posts a comment

### Status Mapping

| Linear State Type | Ralph Status |
|-------------------|-------------|
| `triage`, `backlog`, `unstarted` | `open` |
| `started` | `in_progress` |
| `completed` | `completed` |
| `canceled` | `cancelled` |

### Priority Model

PRD story priorities are preserved as unbounded integers in the issue body metadata (`Ralph Priority`). These are mapped to Linear's coarse 0-4 scale for compatibility:

```text
coarse_priority = min(4, max(0, ralph_priority - 1))
```

Task selection uses the full `Ralph Priority` value for fine-grained ordering.

## Configuration Reference

```toml
# .ralph-tui/config.toml

# Set linear as default tracker
tracker = "linear"

# Or configure with options
[[trackers]]
name = "linear"
plugin = "linear"

[trackers.options]
apiKey = "lin_api_..." # Optional if LINEAR_API_KEY is set
```

Run with:

```bash
ralph-tui run --tracker linear --epic ENG-123
```
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,12 @@
"typescript": "^5.9.3"
},
"dependencies": {
"@linear/sdk": "^76.0.0",
"@opentui/core": "^0.1.72",
"@opentui/react": "^0.1.72",
"react": "^19.2.3",
"handlebars": "^4.7.8",
"node-notifier": "^8.0.2",
"react": "^19.2.3",
"smol-toml": "^1.6.0",
"yaml": "^2.8.2",
"zod": "^4.3.5"
Expand Down
Loading
Loading