| name | plan-next-feature |
| description | Evaluate what to build next based on VISION.md and Linear history, then create the next feature as a parent issue with subtasks in Linear. |
/plan-next-feature - Plan and Create the Next Feature
Usage
/plan-next-feature
Fully autonomous — no user input needed. Reads all context from the project.
Behavior
- Read Config: Read
linear-team and linear-label from .ralph.toml in the project root
- Read Vision: Read
VISION.md from the project root for the north-star feature list
- Fetch Done Issues: Query Linear for all issues with the configured label in "Done" state
- Fetch In-Progress Issues: Query Linear for issues in "In Progress" or "Todo" states
- Decide Next Feature: Based on the vision, completed work, and in-progress work, pick the single most logical next feature following natural dependency order
- Create Parent Issue: Create a parent issue in Linear with a full PRD description
- Create Subtask Issues: Create 2-5 child issues for implementation tasks
Config
Read from .ralph.toml at the project root:
linear-team = "SAB"
linear-label = "agent:ralph"
If either is missing, default to SAB and agent:ralph.
Linear API
- Endpoint:
https://api.linear.app/graphql
- Auth header:
Authorization: $LINEAR_API_KEY (no "Bearer" prefix — the key is sent raw)
Step 1: Resolve team ID
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d '{"query":"query { teams(filter: { key: { eq: \"TEAM_KEY\" } }) { nodes { id name key } } }"}'
Extract data.teams.nodes[0].id as teamId.
Step 2: Resolve label ID
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d '{"query":"query { issueLabels(filter: { name: { eq: \"LABEL_NAME\" } }) { nodes { id name } } }"}'
Extract data.issueLabels.nodes[0].id as labelId.
Step 3: Fetch completed issues (Done state)
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d '{
"query": "query { issues(filter: { team: { key: { eq: \"TEAM_KEY\" } }, labels: { name: { eq: \"LABEL_NAME\" } }, state: { name: { in: [\"Done\"] } } }, first: 50) { nodes { identifier title description state { name } } } }"
}'
Step 4: Fetch in-progress/todo issues
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d '{
"query": "query { issues(filter: { team: { key: { eq: \"TEAM_KEY\" } }, labels: { name: { eq: \"LABEL_NAME\" } }, state: { name: { in: [\"In Progress\", \"Todo\"] } } }, first: 50) { nodes { identifier title description state { name } } } }"
}'
Step 5: Resolve the "Todo" state ID
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d "{\"query\":\"query { team(id: \\\"TEAM_ID\\\") { states { nodes { id name } } } }\"}"
Use the state named "Todo" for new issues.
Step 6: Create parent issue
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d '{
"query": "mutation CreateIssue($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier title url } } }",
"variables": {
"input": {
"teamId": "TEAM_ID",
"title": "<feature title>",
"description": "<full PRD markdown>",
"priority": 2,
"labelIds": ["LABEL_ID"],
"stateId": "STATE_ID"
}
}
}'
Step 7: Create subtask issues
For each subtask, create a child issue linked to the parent:
curl -s -X POST https://api.linear.app/graphql \
-H "Content-Type: application/json" \
-H "Authorization: $LINEAR_API_KEY" \
-d '{
"query": "mutation CreateIssue($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id identifier title url } } }",
"variables": {
"input": {
"teamId": "TEAM_ID",
"parentId": "PARENT_ISSUE_ID",
"title": "<subtask title>",
"description": "<subtask details and acceptance criteria>",
"priority": 3,
"stateId": "STATE_ID"
}
}
}'
Planning Rules
- Dependency order: Pick features that build on what's already done. Foundation before advanced.
- No duplicates: Never pick a feature that is already Done, In Progress, or Todo.
- 2-5 subtasks: Break the feature into focused, implementable units of work.
- One feature at a time: Only plan a single feature per invocation.
Parent Issue Description Template
## Overview
Brief 2-3 sentence description of what this feature does and why it's needed.
## Goals
- Primary goal
- Secondary goals
## Requirements
### Functional Requirements
1. Requirement with clear acceptance criteria
### Technical Approach
- Models, controllers, routes, migrations needed
- Reference existing patterns in the codebase
## Subtasks
1. Subtask 1 — description
2. Subtask 2 — description
Output
After creating all issues, display a summary:
[planner] Created feature: TEAM-123 — Feature title
URL: https://linear.app/...
Subtasks:
TEAM-124 — Subtask 1
TEAM-125 — Subtask 2
[planner] Done. Ready for Ralph to pick up.
Important Notes
- Use
$LINEAR_API_KEY environment variable — never hardcode the key
- All
curl output should be parsed with jq for readability
- If any API call fails, don't stop, try and fix the error
- This skill runs autonomously as part of a perpetual loop — do not ask for user input
- Labels on parent issues only: Do not add
labelIds to subtask/child issues — Linear does not support labels on sub-issues. Only the parent issue should have labels applied.