
Project Tracking
- 39 installs
- 1 repo stars
- Updated July 19, 2026
- fearovex/claude-config
Initialize and operate GitHub Projects V2 tracking for epics and issues using gh CLI and GraphQL from the agent.
About
project-tracking is a command-reference skill for Claude agents that turns GitHub Projects V2 and Issues into a repeatable delivery backbone for solo builders. SKILL.md carries decision logic—when to confirm steps, how to handle unsupported issue types—while this package stores literal gh and GraphQL invocations you run by step ID. Pre-flight resolves owner and repo from the origin remote so you never hard-code the wrong namespace. track-init probes whether the active gh token can reach Projects V2, separating “not logged in” from fine-grained PAT scope gaps so the agent suggests the right fix instead of generic errors. plan-epic and plan-migrate share issue-type discovery and degrade gracefully to labels when the API returns empty nodes. For indie teams shipping on GitHub, it reduces ad-hoc project setup into auditable, copy-paste-ready blocks that pair with engram-style project numbering. Use when you already live in gh CLI and want the agent to drive init and migration without re-deriving GraphQL each session.
- Pre-flight detects owner/repo from git remote before any GraphQL
- track-init classifies gh auth vs fine-grained PAT Project V2 permission failures
- Shared issue-type detection query with label-only fallback when types unsupported
- Executable command blocks referenced by step ID (e.g. track-init §1)
- Engram config placeholders for owner, repo, and project_number
Project Tracking by the numbers
- 39 all-time installs (skills.sh)
- Ranked #1,730 of 3,273 Productivity & Planning skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 24, 2026 (Skillselion catalog sync)
npx skills add https://github.com/fearovex/claude-config --skill project-trackingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 39 |
|---|---|
| repo stars | ★ 1 |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 19, 2026 |
| Repository | fearovex/claude-config ↗ |
What it does
Initialize and operate GitHub Projects V2 tracking for epics and issues using gh CLI and GraphQL from the agent.
Files
Triggers: /track-init, /plan-epic <description>, /plan-migrate, /track <description>, /backlog, "qué tenemos pendiente?", "what's pending?", "acordate que hay que...", "remember to...", "vamos a planear", "armemos la épica", bug found during implementation, SDD tasks generated by sdd-tasks.
---
Hierarchy model — Epic / Story / Task / Bug
This skill enforces a three-level hierarchy (plus Bug as an orthogonal type) modeled with GitHub Issue Types (native) and sub-issues (native). Labels are kept for backwards compatibility but Issue Types are authoritative.
| Level | GitHub Issue Type | Lifetime | Sub-issues of | Has sub-issues |
|---|---|---|---|---|
| Epic | Epic | Weeks | none | Stories |
| Story | Feature | Days | Epic | Tasks |
| Task | Task | Hours | Story (or Epic for hotfixes) | none |
| Bug | Bug | Hours–Days | Epic (Stability) or Story it breaks | none |
Rules of thumb:
- One Task = one Claude session. If it does not fit, it is a Story disguised as a Task. Split it.
- A Story without sub-issues is allowed only when its acceptance criteria already serve as the task list (truly small).
- An Epic without sub-issues is a smell — likely it is actually a Story.
- A Bug can be standalone (no parent) when it does not belong to a planned epic.
Issue Types vs labels
The skill writes BOTH the Issue Type (when the org has Issue Types enabled) AND the equivalent legacy label (type/epic, type/feature, type/task, type/bug). This lets old views, filters, and the existing /track flow keep working while the new hierarchy is adopted incrementally.
Detection of Issue Type availability is done once per session via the Issue-Type detection block in COMMANDS.md. If the response is empty or the field is unsupported, fall back to label-only mode and warn the user once per session:
"Issue Types are not enabled on this org. Falling back to labels — type/epic,type/feature,type/task,type/bug. To enable native types, go to
Org settings → Issue Types."
All literal `gh` / GraphQL command blocks live in [`COMMANDS.md`](./COMMANDS.md),
referenced below by step ID (e.g. [track-init §1]). This file holds the decisionlogic; COMMANDS.md holds the exact invocations.Process
Pre-flight: Detect Repo and Config
Before any flow, resolve owner/repo (run Pre-flight — detect repo in COMMANDS.md). Then search engram for the project config:
mem_searchwith querytracking/{repo-name}/config- If found, call
mem_get_observationto get project number and field IDs - If NOT found and the flow requires project operations → warn user to run
/track-initfirst
---
Flow 1: Bootstrap — /track-init
Run once per repo. Idempotent — safe to run again.
Step 0 — Detect token capabilities
Run [track-init §0]. On failure, classify the cause using the table there — never assume every failure is the token. Distinct causes route to distinct actions: gh-not-authenticated (§0-auth), fine-grained PAT (§0-manual), missing project scope (gh auth refresh -s project), a transient GitHub/network error (stop and retry later, do NOT touch the token), or — when REST works but Projects/GraphQL returns FORBIDDEN — a misconfigured token env var (§0-token-env: wrong token type, or the right token under a name gh never reads).
Most common (your case): fine-grained PATs do NOT support Projects V2 on personal accounts (only orgs). [track-init §0-manual] offers two exits: fix the token (Classic PAT + gh auth, recommended — unblocks automatic creation for every repo) or create the board manually (fast fallback). Recommend the token fix. If the user fixes the token, re-run Step 0 then go to Step 1. If they create it manually, capture the project number and skip to Step 2 (labels).
Step 1 — Create GitHub Project board (automatic path)
Only if Step 0 confirms Projects V2 support: run [track-init §1] (creates the project + the Status / Priority / Type single-select fields).
Step 2 — Create labels
Run [track-init §2] (type / priority / status labels). Idempotent — a label that already exists makes gh label create fail; catch and continue.
Step 3 — Capture field and option IDs
Whether the board was created automatically (Step 1) or manually (Step 0), run [track-init §3] to capture status_field_id, priority_field_id, type_field_id and the option IDs for each value. Required by gh project item-edit.
Step 4 — Save config to engram
Call mem_save with:
- title: "Bootstrap project-tracking config for {repo-name}"
- type: config
- topic_key:
tracking/{repo-name}/config - content: project_number, owner, repo, all field IDs and option IDs as a structured map
Confirm to user: "Board linked. Project #{project_number} for {owner}/{repo}. Config saved to engram."
---
Flow 2: Plan Epic — /plan-epic <description>
Used to plan a large feature or initiative end-to-end: one Epic, its Stories, and the Tasks of each Story. Nothing is written to GitHub until the user confirms explicitly.
Run /track-init first if no engram config exists for this repo.
Step 1 — Read templates
Load the body templates from this skill directory:
templates/epic.mdtemplates/story.mdtemplates/task.md
These are the contract — every issue created in this flow follows them.
Step 2 — Detect Issue Type support
Run Issue-Type detection (COMMANDS.md). Cache the result for the session. Decide: native Issue Types mode OR label-fallback mode.
Step 3 — Propose the hierarchy as Markdown in chat
Produce a single chat message structured as:
# Epic: {Title}
Type: Epic | Priority: {P0..P3}
[Epic body following templates/epic.md]
---
## Story 1: {Title}
Type: Feature | Priority: {P0..P3} | Parent: Epic above
[Story body following templates/story.md]
### Task 1.1: {Title}
Type: Task | Parent: Story 1
[Task body following templates/task.md]
### Task 1.2: {Title}
...
---
## Story 2: {Title}
...Show the full proposal in ONE message. Do not call any GitHub API yet.
Step 4 — Wait for confirmation
End the proposal message with EXACTLY this prompt:
Review the plan above. Reply:
- "dale" / "go" / "create" → I create all N issues + sub-issue links
- "edit <X>" / "no <X>" → tell me what to change, I revise the plan
- "abort" → drop the plan, nothing is createdIf the user replies anything other than a confirmation keyword, treat it as edit instructions and produce a revised proposal. Re-prompt. Never create issues without an explicit confirmation keyword from this list.
Step 5 — Execute creation in order
Create issues in the order: Epic → Story 1 → Task 1.1 → Task 1.2 → Story 2 → ...
For each issue:
1. Call mcp__github__create_issue with title, body, labels, and Issue Type (when supported). 2. Capture the returned issue number. 3. If the issue has a parent in the proposal, link as sub-issue with [sub-issue link]. 4. Add the issue to the Project board ([track §3]). 5. Set Project fields ([track §4]): Status=Backlog, Priority=as proposed, Type=as proposed.
Step 6 — Report back
Print a single summary block:
## Created
Epic #N: {title}
├── Story #N+1: {title}
│ ├── Task #N+2: {title}
│ ├── Task #N+3: {title}
│ └── Task #N+4: {title}
└── Story #N+5: {title}
├── Task #N+6: {title}
└── Task #N+7: {title}
Total: 1 epic, 2 stories, 5 tasks. All added to project board as Backlog.Save an engram observation:
mem_savewith type: architecture, topic_key: `tracking/{repo}/epic-{N}`,
content: epic title, child story+task numbers, source of the plan (user request, SDD spec ref, etc.).
---
Flow 3: Plan Migrate — /plan-migrate
Used when an existing repo has a flat board (issues without parent/child relationships) and needs to be reorganized into the Epic/Story/Task hierarchy.
Step 1 — Fetch the current state
Run [migrate-fetch]. Group issues by likely level using these heuristics:
| Signal | Likely level |
|---|---|
Title starts with epic: or label type/epic or Type=Epic | Epic |
Title starts with feat: or label type/feature or Type=Feature | Story |
Title starts with bug: or label type/bug or Type=Bug | Bug |
Title starts with chore:/task: or no prefix | Task |
Step 2 — Propose the hierarchy in chat
Produce a single message:
# Migration proposal — {N} open issues
## Epics
- #10 epic: Payments & Monetization
Will own:
- #28 feat: Stripe integration for guide bookings
- {other feats that thematically belong}
- #14 epic: Push Notifications & Engagement
Will own:
- #30 feat: push notifications (Expo / Firebase)
## Bugs (orphan — no epic owner)
- #18, #19, #20, ...
Recommended: create epic "Stability & Bugfixes" to own all of these.
OR leave as standalone (no parent). Pick one.
## Unclassified
- #X, #Y — could not infer parent confidently. Please tell me.End with the same confirmation prompt as /plan-epic Step 4.
Step 3 — Apply on confirmation
For each parent → child pair, run [sub-issue link]. For issues that need an Issue Type set retroactively, run [set-issue-type]. If Issue Types are not supported, ensure the legacy type/* label is present instead.
Step 4 — Report
Print the same tree summary as /plan-epic Step 6. Save engram observation type: architecture, topic_key: `tracking/{repo}/migration-{date}`.
---
Flow 4: Track — Create Issues and Add to Board
Trigger model (hybrid)
| Situation | Behavior |
|---|---|
/track <description> | Create issue immediately, no confirmation |
/track with multiple items or backlog session | Create all, show list, ask confirmation before executing |
sdd-tasks generates tasks | Propose creating one issue per task, ask confirmation |
| User mentions future work ("habría que...", "falta...", "después hay que...") | Collect internally. At session end propose: "Detected N trackable items: [list]. Create as issues?" |
| "acordate que hay que..." / "remember to..." | Propose creating an issue immediately |
| Bug found during implementation | Propose creating issue with type/bug label immediately |
Issue creation steps
Step 1 — Deduplicate
Run [track §1]. If a similar issue exists, show it and ask: "Issue #N already exists: '{title}'. Create a new one anyway?"
Step 2 — Create issue via MCP
Run [track §2] — mcp__github__create_issue with owner, repo, conventional title (type: description), the body template, and applicable labels. The body template lives in [track §2].
Step 3 — Add to project board
Run [track §3] (captures item_id).
Step 4 — Set project fields
Run [track §4] — load field/option IDs from engram config (tracking/{repo-name}/config), then set Status / Priority / Type.
Step 5 — Confirm to user
Created #14: feat: add OAuth with Google — added to board as Backlog/P1-High [Feature]---
Flow 5: Query — /backlog and status questions
Default: full board summary
Run [backlog §default], then group items by Status field value. Output:
## Project Board: {project-name}
### In Progress (2)
- #14 feat: OAuth with Google [P1-High]
- #12 fix: Email validation [P0-Critical]
### Review (1)
- #13 feat: User profile page [P1-High]
### Backlog (3)
- #15 feat: Dashboard redesign [P2-Medium]
- #16 chore: Update dependencies [P3-Low]
- #17 feat: Export to PDF [P2-Medium]
### Done (last 5)
- #11 fix: Login redirect loop [P1-High]Show statuses in order: In Progress → Review → Todo → Backlog → Done (last 5 only to avoid noise).
Filtered queries
| Command | Filter applied |
|---|---|
/backlog bugs | Only items with Type = Bug |
/backlog high | Only items with Priority = P0-Critical or P1-High |
/backlog in-progress | Only items with Status = In Progress |
/backlog todo | Only items with Status = Todo |
/backlog review | Only items with Status = Review |
---
Status Transitions
Manual triggers: /track move #14 in-progress, /track move #14 review, /track move #14 done. Underlying command: [status-move].
Automatic integration points:
- Claude starts working on issue → move to "In Progress"
- Claude opens PR for issue → move to "Review" (integrates with
branch-prskill) - PR merged / issue closed → move to "Done"
---
Rules
- GitHub Issues + Projects is the source of truth for WHAT needs to be done — backlog, tasks, stories, bugs
- Engram remains the source of truth for decisions, discoveries, and technical context — these are complementary, not competing
- Never create duplicate issues — always run
gh issue list --searchbefore creating - Issue titles follow conventional format:
type: description(e.g.,feat: add OAuth,fix: email validation) - Labels use lowercase with `/` separator:
type/feature,priority/high,status/backlog - Every issue MUST have at least the Type field set on the project board
- `/track-init` is idempotent — running it twice must not create duplicate labels or projects; handle errors from
gh label creategracefully - Project config (IDs) MUST be saved to engram after bootstrap with
topic_key: tracking/{repo-name}/config - Before any `gh project item-edit` call, load field IDs from engram; if not found re-derive with
gh project field-list - Batch creation requires confirmation — when proposing multiple issues at once, show the full list and wait for user approval before creating any
- Propose, don't assume type/priority — when auto-detecting trackable items from conversation, infer reasonable defaults but show them to the user before creating
- Prefer MCP for issue creation (
mcp__github__create_issue) andghCLI for all project board operations - When `/track-init` has not been run (no engram config, no linked project), detect this and suggest running it before any board operation
- Fine-grained PATs do NOT support Projects V2 on personal accounts — only Classic PATs or org-scoped tokens work. When detected, offer both exits (
§0-manual): recommend fixing the token (Classic PAT +gh auth, unblocks every repo) over the manual-board fallback. Never present manual creation as the only option - `gh` reads its token only from `GH_TOKEN` > `GITHUB_TOKEN` (in that order), and an env var token overrides `gh auth login`. A correct token under any other name (e.g.
GITHUB_CLASSIC_TOKEN) is silently ignored. When REST works but Projects/GraphQL returnsFORBIDDEN, suspect a misconfigured env var (§0-token-env): put the Classic PAT inGH_TOKEN, verify with the one-line GraphQLviewer.projectsV2query. Document the rule — token type, variable name, verification — never the secret value - Both automatic and manual board creation paths converge at Step 3 (capture IDs) — the rest of the workflow is identical regardless of how the board was created
- `/plan-epic` is confirm-first: the full hierarchy (Epic + Stories + Tasks) is proposed as Markdown in chat; NO
create_issuecall fires until the user replies with an explicit confirmation keyword (dale,go,create). Anything else is treated as edit instructions, not approval. - Issue Types are authoritative when available; legacy
type/*labels are written alongside for backwards compatibility. Detect support via GraphQLissueTypesquery, cache the result for the session, and fall back to label-only mode with a one-time warning when types are unsupported. - Sub-issue links use the `addSubIssue` GraphQL mutation — never simulate hierarchy with text in the body ("parent: #N" alone does not create the link).
- One Task = one Claude session. If a Task does not fit in one session, it is a Story disguised as a Task — propose splitting it before creating.
- `/plan-migrate` never modifies issues without confirmation — same confirm-first contract as
/plan-epic. Unclassified issues are flagged for the user, not auto-assigned. - Bug issues can be standalone (no parent epic) or owned by a "Stability & Bugfixes" epic. Pick one approach per repo and keep it consistent — the choice goes into the engram config under
tracking/{repo}/config. - Body templates live in `skills/project-tracking/templates/` (
epic.md,story.md,task.md,bug.md) and are the ONLY source of truth for issue body structure. Do not improvise alternative formats.
project-tracking — Command Reference
Executable command blocks for project-tracking/SKILL.md. The SKILL.md holds thedecision logic (what each step does, when to confirm); this file holds the literal
gh/ GraphQL invocations. Referenced by step ID, e.g. "run[track-init §1]".
Replace{owner},{repo},{project_number}, and{...-id}placeholders with the
values resolved in pre-flight and the engram config.
---
Pre-flight — detect repo
# Detect owner/repo from git remote
git remote get-url origin
# Parse: https://github.com/owner/repo.git → owner=owner, repo=repo
# Parse: git@github.com:owner/repo.git → sameIssue-Type detection (shared by plan-epic, plan-migrate)
gh api graphql -f query='query { repository(owner:"OWNER", name:"REPO") { issueTypes(first:10) { nodes { id name } } } }'Empty / unsupported → fall back to label-only mode, warn once per session.
---
track-init
[track-init §0] — detect token capabilities
gh auth status 2>&1 # is gh logged in at all?
gh project list --owner {owner} --limit 1 2>&1 # can the token reach Projects V2?Classify the failure by what the output says — do NOT assume it's always the token:
| Symptom in output | Cause | Action |
|---|---|---|
gh auth status says "not logged in" / no token | gh not authenticated | Run [track-init §0-auth] |
github_pat_... token + permission error | Fine-grained PAT (no Projects V2 on personal accounts) | Run [track-init §0-manual], lead with Exit A |
Classic token but missing required scope 'project' / read:project | Token lacks the project scope | gh auth refresh -s project then re-run §0 |
HTTP 5xx, timeout, rate limit, network error | GitHub / network, not the token | Stop. Tell the user it's a transient GitHub/network error; retry §0 later. Do NOT touch the token |
REST works (issues OK) but Projects/GraphQL says FORBIDDEN / Resource not accessible | gh is using a token from a misconfigured env var (wrong type, or the right token under a name gh never reads) | Run [track-init §0-token-env] |
| Empty list, no error | Token is fine, account just has no boards yet | Proceed to Step 1 (create board automatically) |
[track-init §0-auth] — gh is not authenticated
The gh CLI isn't logged in (gh auth status reported no token).
Authenticate first, then I'll retry detection:
gh auth login # GitHub.com → choose token or browser
After it succeeds, tell me "listo" and I re-run detection (§0).[track-init §0-token-env] — configure the token gh actually reads
Symptom: issues work (REST) but any Projects/board operation returns FORBIDDEN / Resource not accessible by personal access token. Projects v2 is GraphQL-only, so a token that is fine for REST can still be blocked here. Two independent things have to be right — token TYPE and the env var NAME.
1. The token must be a Classic PAT. Fine-grained PATs (github_pat_...) do not reliably reach Projects v2 over GraphQL on personal accounts — REST works, GraphQL is blocked, which is exactly why the failure looks intermittent. Use a Classic PAT (ghp_...) with scopes repo + project.
2. The token must live in a variable gh reads. gh reads an auth token from exactly two env vars, in this order of precedence:
GH_TOKEN > GITHUB_TOKENAny other name (GITHUB_CLASSIC_TOKEN, MY_PAT, …) is a dead variable — gh never looks at it, so the token is present but ignored. Worse: an env var token overrides `gh auth login` ("takes precedence over previously stored credentials"), so the wrong token in GITHUB_TOKEN silently wins over a correct gh auth login.
The fix (canonical): put the Classic PAT in `GH_TOKEN` — the highest-precedence name. Unset or correct any other GitHub token var so a stale one can't win.
# Windows (PowerShell, persists for the user):
[Environment]::SetEnvironmentVariable("GH_TOKEN", "ghp_xxx", "User")
# macOS / Linux (add to your shell profile):
export GH_TOKEN="ghp_xxx"Verify in one line — distinguishes a good token from a bad one with no ambiguity:
gh api graphql -f query='{viewer{projectsV2(first:1){nodes{title}}}}'FORBIDDEN → wrong token type or wrong variable, re-check both points above. Returns a title (or an empty nodes list with no error) → token is correct; re-run §0.
Never paste the real token value into chat or commit it. If it was exposed,
rotate it at https://github.com/settings/tokens. This skill documents the
rule (type + variable + verify), never the secret.
[track-init §0-manual] — token-blocked guidance (two exits)
Your token can't manage GitHub Projects V2 on this personal account. Fine-grained PATs (github_pat_...) don't support Projects V2 on personal accounts — only Classic PATs or org-scoped tokens do. Two ways out:
Exit A — fix the token (recommended; one-time, unblocks every repo).
1. Create a Classic PAT: https://github.com/settings/tokens/new
- Scopes: check `project` AND `repo`
- Expiration: your call (90 days or no expiration)
2. Make gh use it. Prefer the env var gh reads first:
set GH_TOKEN to the Classic PAT (see [track-init §0-token-env])
— an env var overrides gh auth login, so this is the reliable path.
Alternatively re-auth interactively (only if no GH_TOKEN/GITHUB_TOKEN is set,
or it will be ignored):
gh auth login # choose: GitHub.com → paste an authentication token
(or, to swap only the scope on an existing login:
gh auth refresh -s project)
3. Verify it worked:
gh project list --owner {owner} --limit 1
No error → tell me "listo" and I create the board automatically (§1).Exit B — create the board manually (fast fallback, ~30s, per repo).
1. Go to https://github.com/users/{owner}/projects → "New Project"
2. Choose "Board" layout
3. Name it "{repo-name} Board"
4. Add these custom fields (Single Select):
- Status: Backlog, Todo, In Progress, Review, Done
- Priority: P0-Critical, P1-High, P2-Medium, P3-Low
- Type: Epic, Feature, Bug, Chore
5. Tell me the project number (visible in the URL: /projects/N)Either way I handle the rest — labels, capturing IDs, saving the config. Recommend Exit A unless the user wants to avoid touching their token.
[track-init §1] — create board (automatic path)
# Create project linked to the repo
gh project create --owner owner --title "{repo-name} Board" --format json
# Capture: .number → project_number# Status field (single select)
gh project field-create {project_number} --owner owner \
--name "Status" --data-type "SINGLE_SELECT" \
--single-select-options "Backlog,Todo,In Progress,Review,Done"
# Priority field (single select)
gh project field-create {project_number} --owner owner \
--name "Priority" --data-type "SINGLE_SELECT" \
--single-select-options "P0-Critical,P1-High,P2-Medium,P3-Low"
# Type field (single select)
gh project field-create {project_number} --owner owner \
--name "Type" --data-type "SINGLE_SELECT" \
--single-select-options "Epic,Feature,Bug,Chore"[track-init §2] — create labels (idempotent; ignore "already exists" errors)
# Type labels
gh label create "type/epic" --color "6e40c9" --description "Epic" --repo owner/repo
gh label create "type/feature" --color "0075ca" --description "Feature" --repo owner/repo
gh label create "type/bug" --color "d73a4a" --description "Bug" --repo owner/repo
gh label create "type/chore" --color "e4e669" --description "Chore" --repo owner/repo
# Priority labels
gh label create "priority/high" --color "b60205" --description "P1 High" --repo owner/repo
gh label create "priority/medium" --color "fbca04" --description "P2 Medium" --repo owner/repo
gh label create "priority/low" --color "0e8a16" --description "P3 Low" --repo owner/repo
# Status labels
gh label create "status/backlog" --color "ededed" --description "In backlog" --repo owner/repo
gh label create "status/in-progress" --color "0052cc" --description "In progress" --repo owner/repo
gh label create "status/review" --color "e99695" --description "In review" --repo owner/repo
gh label create "status/done" --color "cfd3d7" --description "Done" --repo owner/repo[track-init §3] — capture field and option IDs
gh project field-list {project_number} --owner owner --format jsonParse to extract status_field_id, priority_field_id, type_field_id, and the option IDs for each value. Required by gh project item-edit.
---
plan-epic / plan-migrate — shared mutations
[sub-issue link] — link child to parent
gh api graphql -f query='
mutation($parentId:ID!, $childId:ID!) {
addSubIssue(input:{issueId:$parentId, subIssueId:$childId}) {
issue { number }
}
}' -F parentId={parent-node-id} -F childId={child-node-id}Fetch an issue's node-id once:
gh issue view {number} --repo {owner}/{repo} --json id[set-issue-type] — set Issue Type retroactively (plan-migrate)
gh api graphql -f query='
mutation($issueId:ID!, $typeId:ID!) {
updateIssueIssueType(input:{issueId:$issueId, issueTypeId:$typeId}) {
issue { number }
}
}' -F issueId={node-id} -F typeId={type-node-id}Issue Types unsupported → ensure the legacy type/* label is present instead.
[migrate-fetch] — fetch current flat board (plan-migrate §1)
gh issue list --repo {owner}/{repo} --state open --limit 200 \
--json number,title,labels,body,issueType---
track — create issue + add to board
[track §1] — deduplicate
gh issue list --repo owner/repo --search "{title keywords}" --json number,titleSimilar issue exists → show it, ask before creating a new one.
[track §2] — create issue via MCP
mcp__github__create_issue with owner, repo, title (conventional type: description), body (template below), labels (e.g. ["type/feature", "priority/medium", "status/backlog"]).
## Description
{description}
## Context
- Source: {how this was identified — user request, SDD task, conversation, bug found}
- Session: {date}
- Related: {any related issues or PRs, if known}
## Acceptance Criteria
- [ ] {criterion 1}
- [ ] {criterion 2}[track §3] — add to project board
gh project item-add {project_number} --owner owner \
--url https://github.com/owner/repo/issues/{issue_number} \
--format json
# Capture: .id → item_id[track §4] — set project fields
Load field IDs + option IDs from engram config (tracking/{repo-name}/config); if not cached, re-derive with gh project field-list {project_number} --owner owner --format json. Then set each field (Status / Priority / Type) with the same call shape, varying --field-id and --single-select-option-id:
gh project item-edit --project-id {project_number} --owner owner \
--id {item_id} \
--field-id {FIELD_id} \
--single-select-option-id {OPTION_id}---
query / transitions
[backlog §default] — full board
gh project item-list {project_number} --owner owner --format json --limit 100Group items by Status; show In Progress → Review → Todo → Backlog → Done (last 5).
[status-move] — move an issue on the board
gh project item-edit --project-id {project_number} --owner owner \
--id {item_id} \
--field-id {status_field_id} \
--single-select-option-id {target_status_option_id}Bug body template
Used when creating a Bug-level issue (GitHub Issue Type: Bug).
A Bug describes broken behavior with enough detail to reproduce and verify.
## Summary
{One sentence: what is broken, in user terms.}
## Steps to reproduce
1. {Step 1 — concrete, copy-pasteable when possible}
2. {Step 2}
3. {Step 3}
## Expected behavior
{What should happen.}
## Actual behavior
{What happens instead. Include error messages, stack traces, screenshots links.}
## Environment
- Platform: {iOS / Android / Web / API}
- Version / commit: {git sha or release tag}
- Other: {browser, device, locale — only what matters}
## Root cause (if known)
{Skip if not investigated yet. Otherwise: one paragraph explaining WHY.}
## Fix approach (if known)
{Skip if not decided yet. Otherwise: short description of the fix direction.}
## Done when
- [ ] {Verifiable check — e.g., "Reproducing steps above no longer trigger the bug"}
- [ ] {Regression test added covering this case}
## Links
- Parent epic: {#N if this bug belongs to an epic, else "none"}
- Related: {#N, or "none"}Epic body template
Used when creating an Epic-level issue. An Epic is a multi-week initiative
that groups Stories. It is not directly executable — it tracks aggregate
progress through its sub-issues.
## Goal
{One paragraph: what business or product outcome this epic delivers.}
## Why
{The motivation — user request, strategic bet, compliance, performance.
Be explicit about what changes for the user or the business when this lands.}
## Success criteria
- [ ] {Observable outcome 1 — measurable, not vague}
- [ ] {Observable outcome 2}
- [ ] {Observable outcome 3}
## Out of scope
- {Things explicitly NOT in this epic — protect the boundary}
- {Future work that belongs to a different epic}
## Stories
Sub-issues track delivery. The list below is the planned breakdown at creation
time; the source of truth is the sub-issue relationship in GitHub.
- [ ] {Story title 1} — to be created as sub-issue
- [ ] {Story title 2}
- [ ] {Story title 3}
## Links
- Spec: {path/to/spec or "n/a"}
- Design: {path/to/design or "n/a"}
- Related epics: {#N, or "none"}Story body template
Used when creating a Story-level issue (GitHub Issue Type: Feature).
A Story is a user-visible increment of value, parent of Tasks.
## User story
As a {role}, I want {capability} so that {outcome}.
## Acceptance criteria
- [ ] {Behavior 1 — written so a tester can verify it}
- [ ] {Behavior 2}
- [ ] {Behavior 3}
## Tasks
Sub-issues track execution. The list below is the planned breakdown at
creation time; the source of truth is the sub-issue relationship in GitHub.
- [ ] {Task title 1} — to be created as sub-issue
- [ ] {Task title 2}
- [ ] {Task title 3}
## Notes
{Constraints, decisions already made, things that affect the approach.
Skip if none.}
## Links
- Parent epic: #{epic-number}
- Spec: {path/to/spec or "n/a"}
- Design: {path/to/design or "n/a"}Task body template
Used when creating a Task-level issue (GitHub Issue Type: Task).
A Task is the unit of execution — one Task should fit one Claude session.
If it does not, it is a Story disguised as a Task — split it.
## What
{One sentence: what gets done. Imperative form: "Add OAuth callback handler".}
## Where
Files or paths that will be touched. Be specific.
- `apps/api/src/auth/oauth/callback.ts` (new)
- `apps/api/src/auth/routes.ts` (modify)
- `apps/api/src/auth/__tests__/oauth.test.ts` (new)
## How
{Short approach paragraph. Algorithm choice, library to use, pattern to follow.
Skip if the implementation is mechanical and "What" + "Where" already imply it.}
## Done when
- [ ] {Verifiable check 1 — e.g., "POST /auth/google/callback returns 200 with valid code"}
- [ ] {Verifiable check 2 — e.g., "unit tests cover success + expired-code + invalid-state"}
- [ ] {Verifiable check 3}
## Depends on
- #{task-number} — {why this dependency blocks us}
## Links
- Parent story: #{story-number}
- Related: {#N, or "none"}Related skills
FAQ
Is Project Tracking safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.