From pm
Interactive sprint worker. Reads status/ready + owner/ai items from GitHub Issues (or local backlog), presents them with context, groups into proposed PRs, and waits for your approval before building. Dispatches parallel sub-agents with self-review and full testing. Reads CONTEXT.md for domain terminology and .pm/out-of-scope/ for negative constraints. Trigger: "let's build", "work the backlog", "what can we ship", "sprint", or /pm:sprint-dev.
How this skill is triggered — by the user, by Claude, or both
Slash command
/pm:sprint-devThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Interactive skill that reads ready items from GitHub Issues (or local backlog), proposes how they should be grouped into PRs, and — with your approval — dispatches parallel sub-agents to implement, review, test, and PR each batch. Updates the issue tracker when work completes.
Interactive skill that reads ready items from GitHub Issues (or local backlog), proposes how they should be grouped into PRs, and — with your approval — dispatches parallel sub-agents to implement, review, test, and PR each batch. Updates the issue tracker when work completes.
Manual only. You decide when to run this and what to build.
spawned-during-sprint.All config values are pre-resolved at skill load time. If you see ERROR: in the output below, stop and tell the user.
!`${CLAUDE_PLUGIN_ROOT}/scripts/discover-config.sh`
Parse the key=value pairs above. The backend value (github or local) determines how items are loaded and updated throughout the rest of this skill. When using the GitHub backend, gh_owner and gh_repo identify the target repository for all gh CLI commands.
Read {research_dir}/research-context.md for project structure, repo info, and tech stack. If missing, tell the user to run /pm:setup.
Read CONTEXT.md from workspace root (multi-repo) or primary repo root (single-repo). Extract domain terms, aliases to avoid, and relationships. These will be passed to sub-agents in their prompts so they use correct terminology.
Read .pm/out-of-scope/ directory. For each .md file, extract the feature name and decision summary. These become negative constraints in sub-agent prompts: "Do NOT implement {feature} — see .pm/out-of-scope/{slug}.md for reasoning."
If neither file/directory exists, continue without them — they're optional.
Iterate repos: from pulse-config.yaml. For each repo, resolve its absolute path relative to {primary_repo_root}'s parent directory, then pull the default branch:
for repo_path in $(yq '.repos[].path' pulse-config.yaml); do
abs="$(realpath "$primary_repo_root/$repo_path")"
echo "=== Pulling $abs ==="
cd "$abs" && git checkout "$default_branch" && git pull origin "$default_branch" || echo "pull failed for $abs"
done
If any pull fails, note it and continue. Single-element repos: is the monorepo case — same loop, one iteration.
If memory.connector is set in pulse-config.yaml (not null), look for MCP tools matching that prefix. If found, search memory for prior sprint-dev runs — known blockers, failed items, in-flight branches. If memory.connector: null or no matching tools are found, skip this phase.
git branch -a | grep pulse/ 2>/dev/null
Note any in-flight branches with open PRs.
status/in-review statusThe standalone Awaiting PR section was retired — items in flight now carry the status/in-review (or status/in-progress) status inline in their sprint-section row. Scan both backlog files:
grep -E '\| (status/in-review|status/in-progress) \|' "$backlog_active" "$backlog_ideas"
For each row with status/in-review, find its PR URL (the row should embed a [#N](https://github.com/...) link) and check the PR state:
gh pr view <PR-URL> --json state,mergedAt 2>/dev/null
## Done (last 7 days) in $backlog_active and remove the row from its sprint sectionstatus/ready in its sprint-section row (or move the row back into $backlog_ideas if it was originally an idea the user promoted)Commit any moves:
git add "$backlog_active" "$backlog_ideas"
git commit -m "backlog: reconcile PRs"
git push origin "$default_branch"
(direct push to default branch is OK here — sprint-dev is interactive only)
Trello backend:
Iterate boards and read cards in LIST_REVIEW. Each card whose description or comments contain a PR URL gets its PR state checked.
echo "$trello_boards_json" | jq -c '.[]' | while read -r board_json; do
eval "$("$CLAUDE_PLUGIN_ROOT/scripts/for-each-board.sh" "[$board_json]")"
# Agent executes:
# mcp__trello__set_active_board({ boardId: $BOARD_ID })
# lists = mcp__trello__get_lists({})
# review_list_id = (find list where name == $LIST_REVIEW).id
# cards = mcp__trello__get_cards_by_list_id({ listId: review_list_id })
# For each card:
# - comments = mcp__trello__get_card_comments({ cardId: card.id })
# - extract first PR URL from card.desc + comments (regex: https://github\.com/[^/]+/[^/]+/pull/\d+)
# - state = `gh pr view <URL> --json state,mergedAt`
# - if merged -> check-transition.sh review done -> mcp__trello__move_card to LIST_DONE
# - if closed -> check-transition.sh review needs_changes -> mcp__trello__move_card to LIST_NEEDS_CHANGES
# -> mcp__trello__add_comment("PR closed without merge — flagged needs_changes")
# - if open -> leave card in review
done
Backwards moves: when a PR is closed unmerged, a card sitting in LIST_REVIEW moves to LIST_NEEDS_CHANGES. From there a human can move it back to LIST_IN_PROGRESS (the statuses map allows needs_changes -> in_progress). This is the explicit Marv-fix.
Load items based on the configured backend.
GitHub backend:
gh issue list --label "status/ready" --label "owner/ai" --state open --json number,title,body,labels --limit 50 --repo "$gh_owner/$gh_repo"
Parse each issue. Extract from the body:
## Acceptance Criteria header)## Code References header)Local backend:
Scan .pm/items/ for files whose labels: contain BOTH status/ready AND owner/ai. Parse YAML.
Trello backend:
Iterate boards and read each board's LIST_READY_FOR_AGENT:
ready_items=()
echo "$trello_boards_json" | jq -c '.[]' | while read -r board_json; do
eval "$("$CLAUDE_PLUGIN_ROOT/scripts/for-each-board.sh" "[$board_json]")"
# mcp__trello__set_active_board({ boardId: $BOARD_ID })
# lists = mcp__trello__get_lists({})
# ready_id = (find name == $LIST_READY_FOR_AGENT).id
# cards = mcp__trello__get_cards_by_list_id({ listId: ready_id })
# For each card append { id, name, desc, labels, board_id, board_name, worker_instructions: $WORKER_INSTRUCTIONS, review_policy: $REVIEW_POLICY }
done
The card's desc IS the spec (written by triage Phase 2). Parse the same ## Acceptance Criteria / ## Code References headers as the GitHub branch — the body structure is identical.
Per-board worker_instructions and review_policy (resolved via for-each-board.sh) flow through to sub-agent prompts in Phase 2B and to the close-vs-comment decision in Phase 2D.5.
Fallback:
If no backend items found, fall back to reading planning/todos.md Ready section (backward compatibility with product-pulse workflow).
Additionally, from {backlog.ideas} ($backlog_ideas), collect S-sized items that could be promoted directly (S items don't need specs). Present these separately as "quick wins available if you want to promote them." Skip the Expired / passed-deadline table — those items are explicitly idle.
Find the most recent *-recommendations.md in {research_dir}/ (search recursively). Extract:
For each ready item that has a spec in {primary_repo_root}/planning/specs/:
git diff {base_sha}..HEAD -- {file_path}
| {today} | sprint-dev | {Green/Yellow/Red} | {summary of changes or "No changes"} |
If a spec has no Code References table or no Base SHA, treat as Yellow with a note.
Primary pool: items from the configured backend with status/ready + owner/ai labels and Green or Yellow freshness.
Quick wins pool: S-sized items from {backlog.ideas} Ideas subsections (present separately as available for user promotion).
Exclusions:
status/in-review or status/in-progress status inlinepulse/* branchesGroup items by relatedness — items that touch the same files, the same domain area, or the same feature scope should be in the same PR. Use the product context to understand the project structure.
General cluster categories (adapt to the project):
Collision detection: items modifying the same files MUST go in the same cluster. Batch size cap: max 8 items per batch.
For multi-repo projects, also route each item to its target repo based on the product context.
Present the full proposal and wait for user approval:
PM — Sprint Proposal
=================================
Weekly Direction: {theme or "No weekly brief"}
Top Priorities: {p1} | {p2} | {p3}
Backend: {github|local}
Items loaded: {N} ready | {N} awaiting PR | {N} ideas
Domain terms: {N} loaded from CONTEXT.md (or "none")
Out-of-scope constraints: {N} loaded from .pm/out-of-scope/
--- Freshness Results ---
Green: {N} items (specs current)
Yellow: {N} items (minor drift — see notes)
Red: {N} items (need re-spec, skipped)
--- Proposed PRs ---
PR 1: {cluster name} ({N} items)
Branch: pulse/{cluster}-{YYYY-MM-DD}
#{n} {item description}
Source: GitHub Issue #{n} | Local .pm/items/{n}-{slug}.yml
Spec: planning/specs/{n}-{slug}.md (if exists)
Freshness: {Green|Yellow} {notes if Yellow}
Size: {S|M|L|XL} | Priority: {priority}
Files likely touched: {file hints}
...
Estimated scope: {small/medium/large}
PR 2: ...
--- Quick Wins (S-sized Ideas — need promotion) ---
#{n} {item description} — {domain}
#{n} {item description} — {domain}
Say "promote #N" to move an idea to Ready for this sprint.
--- Flagged for Re-spec ---
#{n} {item description} — {reason for Red freshness}
--- Not Included ---
{N} items excluded (ideas without promotion, monitor, manual)
Ask: "Which PRs should I build? Say 'all', list specific numbers (e.g. '1 and 3'), or 'none' to just review. You can also promote quick wins or drop individual items."
WAIT FOR RESPONSE. Do not proceed without explicit approval.
For each approved PR, in priority order:
git checkout main && git pull
git checkout -b pulse/{cluster}-{YYYY-MM-DD}
For worktree-capable projects:
git worktree add .claude/worktrees/pulse-{cluster}-{date} -b pulse/{cluster}-{YYYY-MM-DD} main
Build a comprehensive sub-agent prompt with:
Domain terminology (from CONTEXT.md): {Include the Terms table so the agent uses correct names}
Out-of-scope constraints: {For each .pm/out-of-scope/ entry: "Do NOT implement {feature}. Reason: {decision summary}"}
Sub-agent workflow requirements:
spawned-during-sprint with a description of what was found and why it matters. Your definition of done stays fixed to the original spec.Dispatch independent PRs in parallel. Conflicting PRs run sequentially.
Agent(subagent_type="general-purpose", prompt=built_prompt)
After each sub-agent creates its PR, run the code-review skill against that PR. The review scores each issue 0-100 for confidence (0 = false positive, 25 = somewhat confident, 50 = moderately confident, 75 = highly confident, 100 = certain).
Filter: keep issues with a confidence score above 24 (i.e., 25+). Fix everything that clears this bar.
If any issues clear the bar, dispatch a follow-up sub-agent on the same branch:
fix: address code review findingsReport inline:
Code Review: {cluster}
Issues found: {N}
Above threshold (>24): {N}
Fixes pushed: {yes/no} {commit sha if yes}
If nothing clears the threshold, note "clean" and proceed.
After each sub-agent completes, immediately tell the user:
PR Complete: {cluster}
========================
Branch: pulse/{cluster}-{date}
PR: {URL}
Items completed: #{n}, #{n}
Items skipped: #{n} (reason)
Tests: {pass/fail}
Review: {issues found}
Spec compliance: {met/partial/N/A}
For each completed item:
GitHub backend:
# Comment on the issue with PR link
gh issue comment {number} --body "Implemented in PR {pr_url}. Spec compliance: {met/partial}. Tests: {pass/fail}." --repo "$gh_owner/$gh_repo"
# Close the issue if PR is merged
gh issue close {number} --repo "$gh_owner/$gh_repo"
Local backend:
Update .pm/items/{number}-{slug}.yml with status: status/done and pr: {pr_url}.
Trello backend:
For each completed item, the orchestrator does three things on the card's home board:
mcp__trello__set_active_board({ boardId: $card_board_id })
# 1. Comment with the PR link (audit trail).
mcp__trello__add_comment({
cardId: $card_id,
text: "Implemented in PR {pr_url}. Tests: {pass/fail}. Spec compliance: {met/partial}."
})
# 2. Move the card based on PR state and the board's review_policy.
Decision matrix:
| PR state at completion | review_policy=self | review_policy=judge | review_policy=auto |
|---|---|---|---|
| Open (not yet merged) | move to LIST_REVIEW | move to LIST_REVIEW | move to LIST_REVIEW |
| Merged | move to LIST_DONE | move to LIST_REVIEW (human signs off) | move to LIST_DONE |
| Closed unmerged | move to LIST_NEEDS_CHANGES | same | same |
| Skipped/failed | leave in LIST_IN_PROGRESS | same | same |
Always validate first:
"$CLAUDE_PLUGIN_ROOT/scripts/check-transition.sh" \
"$current_status" "$target_status" "$trello_statuses_json" \
|| { echo "transition rejected — leaving card in $current_status"; continue; }
Then:
mcp__trello__move_card({ cardId: $card_id, listId: $target_list_id })
Initial dispatch (before the sub-agent runs) also moves the card from LIST_READY_FOR_AGENT -> LIST_IN_PROGRESS, gated by the same check-transition.sh call.
If the item has a parent epic, check epic progress:
# Count open vs closed sub-issues
gh api graphql -f query='{ node(id: "{epic_node_id}") { ... on Issue { subIssues { totalCount } closedSubIssues: subIssues(states: CLOSED) { totalCount } } } }'
For each item in the batch:
GitHub backend:
## Done (last 7 days) in $backlog_active if one existsLocal backend:
.pm/items/{number}-{slug}.yml with status: status/in-review and pr: {pr_url}.pm/items/{number}-{slug}.yml with status: status/done and pr: {pr_url}For Trello, the
#{number}token inplanning/todos.mdrows is the Trello card's short id (e.g.t-AbCdEfGh) and the embedded PR link is the same[#N](https://github.com/...)form. The sync logic is otherwise identical.
Backlog file sync (both backends):
If $backlog_active and $backlog_ideas exist (backward-compatible with product-pulse workflow):
status/ready -> status/in-review and embed the PR link inline in the item description## Done (last 7 days) in $backlog_activeIf a sprint subsection now has zero status/ready rows left, leave the section header in place unless the whole sprint is complete; in that case delete the entire subsection and summarize it in the commit message.
Commit:
git add "$backlog_active" "$backlog_ideas"
git commit -m "backlog: update — {cluster} batch complete ({N} items)"
git push origin "$default_branch"
Save to memory and clean up worktree if used.
PM — Sprint Summary ({date})
==========================================
Backend: {github|local|trello}
PRs built: {N} of {N} approved
Items completed: {N}
Items skipped: {N}
Issues updated: {N} commented, {N} closed
Spawned issues: {N} (tagged spawned-during-sprint)
Freshness: {N} green, {N} yellow, {N} red (skipped)
PRs created:
- {cluster}: {URL}
Backlog: {N} remaining ready items, {N} ideas
Domain terms applied: {yes/no}
Out-of-scope constraints enforced: {N}
{If Trello: "Cards updated across {N} board(s); {moved_to_in_progress} in-progress, {moved_to_review} in review, {moved_to_done} done, {moved_to_needs_changes} needs-changes."}
npx claudepluginhub studio-moser/skills-n-stuff --plugin pmCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.