engsys 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +202 -0
  3. package/core/agents/aaron.md +152 -0
  4. package/core/agents/bert.md +115 -0
  5. package/core/agents/isabelle.md +136 -0
  6. package/core/agents/jody.md +150 -0
  7. package/core/agents/leith.md +111 -0
  8. package/core/agents/marcelo.md +282 -0
  9. package/core/agents/melvin.md +101 -0
  10. package/core/agents/nyx.md +152 -0
  11. package/core/agents/otto.md +168 -0
  12. package/core/agents/patricia.md +283 -0
  13. package/core/commands/design-audit-local.md +155 -0
  14. package/core/commands/design-audit.md +235 -0
  15. package/core/commands/design-critique.md +96 -0
  16. package/core/commands/file-issue.md +22 -0
  17. package/core/commands/generate-project.md +45 -0
  18. package/core/commands/implement-issue.md +37 -0
  19. package/core/commands/implement-project.md +40 -0
  20. package/core/commands/naturalize.md +61 -0
  21. package/core/commands/pre-push.md +29 -0
  22. package/core/commands/prep-review-collect.md +130 -0
  23. package/core/commands/prep-review-finalize.md +121 -0
  24. package/core/commands/prep-review-publish.md +113 -0
  25. package/core/commands/prep-review.md +65 -0
  26. package/core/commands/project-closeout.md +25 -0
  27. package/core/skills/agentic-eval/SKILL.md +195 -0
  28. package/core/skills/chrome-devtools/SKILL.md +97 -0
  29. package/core/skills/code-review/SKILL.md +26 -0
  30. package/core/skills/gh-cli/SKILL.md +2202 -0
  31. package/core/skills/git-commit/SKILL.md +124 -0
  32. package/core/skills/git-workflow-agents/SKILL.md +462 -0
  33. package/core/skills/git-workflow-agents/reference.md +220 -0
  34. package/core/skills/github-actions/SKILL.md +190 -0
  35. package/core/skills/github-issues/SKILL.md +154 -0
  36. package/core/skills/llm-structured-outputs/SKILL.md +323 -0
  37. package/core/skills/llm-structured-outputs/references/provider-details.md +392 -0
  38. package/core/skills/pre-push/SKILL.md +115 -0
  39. package/core/skills/refactor/SKILL.md +645 -0
  40. package/core/skills/web-design-reviewer/SKILL.md +371 -0
  41. package/core/skills/webapp-testing/SKILL.md +127 -0
  42. package/core/skills/webapp-testing/test-helper.js +56 -0
  43. package/core/templates/CLAUDE.md.tmpl +98 -0
  44. package/core/templates/adr-template.md +67 -0
  45. package/core/templates/gh-issue-templates/bug.md +39 -0
  46. package/core/templates/gh-issue-templates/content.md +42 -0
  47. package/core/templates/gh-issue-templates/enhancement.md +36 -0
  48. package/core/templates/gh-issue-templates/feature.md +39 -0
  49. package/core/templates/gh-issue-templates/infrastructure.md +41 -0
  50. package/core/templates/post-edit-reminders.sh.tmpl +19 -0
  51. package/core/templates/settings.json.tmpl +90 -0
  52. package/core/templates/settings.local.json.tmpl +3 -0
  53. package/core/workflows/agent-implementation-workflow.md +346 -0
  54. package/core/workflows/generate-project.md +258 -0
  55. package/core/workflows/implement-project-workflow.md +190 -0
  56. package/core/workflows/issue-tracking.md +89 -0
  57. package/core/workflows/project-closeout-ceremony.md +77 -0
  58. package/core/workflows/review-workflow.md +266 -0
  59. package/engsys.config.example.yaml +46 -0
  60. package/install +202 -0
  61. package/lessons-library/README.md +80 -0
  62. package/lessons-library/async-callbacks-verify-liveness.md +15 -0
  63. package/lessons-library/change-isnt-done-until-every-surface-updated.md +15 -0
  64. package/lessons-library/claim-then-act-for-irreversible-ops.md +16 -0
  65. package/lessons-library/co-commit-entangled-work.md +15 -0
  66. package/lessons-library/dependabot-triage-playbook.md +17 -0
  67. package/lessons-library/deploy-by-digest-and-verify-the-running-revision.md +15 -0
  68. package/lessons-library/enforce-your-guarantee-at-your-boundary.md +16 -0
  69. package/lessons-library/gate-changes-on-measurement-not-vibes.md +15 -0
  70. package/lessons-library/iac-first-no-console-changes.md +15 -0
  71. package/lessons-library/independent-objective-review-gate.md +15 -0
  72. package/lessons-library/keep-an-immutable-source-of-truth.md +15 -0
  73. package/lessons-library/long-agent-runs-checkpoint-not-poll.md +15 -0
  74. package/lessons-library/model-identity-with-stable-ids-and-provenance.md +15 -0
  75. package/lessons-library/operator-choices-are-first-class.md +15 -0
  76. package/lessons-library/prefer-tool-enforced-structured-output.md +15 -0
  77. package/lessons-library/prove-causation-before-acting.md +15 -0
  78. package/lessons-library/re-read-state-before-acting.md +14 -0
  79. package/lessons-library/read-layer-tolerates-unbackfilled-rows.md +15 -0
  80. package/lessons-library/shell-safety-pipefail-and-validate-before-teardown.md +14 -0
  81. package/lessons-library/shift-correctness-left-and-distrust-false-greens.md +15 -0
  82. package/lessons-library/stray-control-bytes-hide-changes.md +14 -0
  83. package/lessons-library/tests-can-assert-the-bug.md +15 -0
  84. package/lessons-library/verify-ground-truth-not-reports.md +15 -0
  85. package/lessons-library/worktrees-need-bootstrap-from-origin-main.md +15 -0
  86. package/lib/commands.js +356 -0
  87. package/lib/generate-team-avatars.mjs +251 -0
  88. package/lib/manifest.js +155 -0
  89. package/lib/render.js +135 -0
  90. package/lib/selftest.js +90 -0
  91. package/lib/util.js +89 -0
  92. package/lib/yaml.js +156 -0
  93. package/optional-agents/gary.md +86 -0
  94. package/optional-agents/jos.md +136 -0
  95. package/optional-agents/sandy.md +101 -0
  96. package/optional-agents/steve.md +161 -0
  97. package/package.json +43 -0
  98. package/stacks/cloud/aws/claude.fragment.md +17 -0
  99. package/stacks/cloud/aws/settings.fragment.json +39 -0
  100. package/stacks/cloud/aws/skills/aws-deployment-preflight/SKILL.md +165 -0
  101. package/stacks/cloud/aws/skills/cloud-architecture-aws/SKILL.md +265 -0
  102. package/stacks/cloud/azure/claude.fragment.md +17 -0
  103. package/stacks/cloud/azure/settings.fragment.json +45 -0
  104. package/stacks/cloud/azure/skills/azure-deployment-preflight/SKILL.md +175 -0
  105. package/stacks/cloud/azure/skills/cloud-architecture-azure/SKILL.md +211 -0
  106. package/stacks/cloud/cloudflare/claude.fragment.md +21 -0
  107. package/stacks/cloud/cloudflare/settings.fragment.json +31 -0
  108. package/stacks/cloud/cloudflare/skills/cloud-architecture-cloudflare/SKILL.md +294 -0
  109. package/stacks/cloud/cloudflare/skills/cloudflare-deployment-preflight/SKILL.md +175 -0
  110. package/stacks/cloud/gcp/claude.fragment.md +17 -0
  111. package/stacks/cloud/gcp/settings.fragment.json +40 -0
  112. package/stacks/cloud/gcp/skills/cloud-architecture-gcp/SKILL.md +208 -0
  113. package/stacks/cloud/gcp/skills/gcp-deployment-preflight/SKILL.md +137 -0
  114. package/stacks/db/mongo/skills/mongo-conventions/SKILL.md +96 -0
  115. package/stacks/db/prisma/claude.fragment.md +49 -0
  116. package/stacks/db/prisma/skills/docker-database-package-copy/SKILL.md +44 -0
  117. package/stacks/db/prisma/skills/prisma-conventions/SKILL.md +37 -0
  118. package/stacks/domain/mobile-growth/skills/apple-ads/SKILL.md +184 -0
  119. package/stacks/domain/mobile-growth/skills/apple-ads/references/benchmark-notes.md +47 -0
  120. package/stacks/domain/mobile-growth/skills/apple-ads/references/official-links.md +53 -0
  121. package/stacks/domain/mobile-growth/skills/google-play-growth/SKILL.md +197 -0
  122. package/stacks/domain/mobile-growth/skills/google-play-growth/references/benchmark-notes.md +47 -0
  123. package/stacks/domain/mobile-growth/skills/google-play-growth/references/official-links.md +45 -0
  124. package/stacks/iac/bicep/claude.fragment.md +14 -0
  125. package/stacks/iac/bicep/settings.fragment.json +20 -0
  126. package/stacks/iac/bicep/skills/iac-bicep/SKILL.md +113 -0
  127. package/stacks/iac/cdk/claude.fragment.md +14 -0
  128. package/stacks/iac/cdk/settings.fragment.json +23 -0
  129. package/stacks/iac/cdk/skills/iac-cdk/SKILL.md +104 -0
  130. package/stacks/iac/terraform/claude.fragment.md +13 -0
  131. package/stacks/iac/terraform/settings.fragment.json +25 -0
  132. package/stacks/iac/terraform/skills/iac-terraform/SKILL.md +93 -0
  133. package/stacks/iac/terraform/skills/terraform-conventions/SKILL.md +87 -0
  134. package/stacks/lang/kotlin/skills/android-testing/SKILL.md +263 -0
  135. package/stacks/lang/kotlin/skills/jetpack-compose/SKILL.md +264 -0
  136. package/stacks/lang/kotlin/skills/kotlin-coroutines/SKILL.md +329 -0
  137. package/stacks/lang/python/skills/python-conventions/SKILL.md +61 -0
  138. package/stacks/lang/shell/skills/shell-scripting/SKILL.md +110 -0
  139. package/stacks/lang/swift/skills/swift-concurrency/SKILL.md +423 -0
  140. package/stacks/lang/swift/skills/swift-concurrency/references/approachable-concurrency.md +80 -0
  141. package/stacks/lang/swift/skills/swift-concurrency/references/concurrency-patterns.md +233 -0
  142. package/stacks/lang/swift/skills/swift-concurrency/references/swiftui-concurrency.md +187 -0
  143. package/stacks/lang/swift/skills/swift-concurrency/references/synchronization-primitives.md +341 -0
  144. package/stacks/lang/swift/skills/swift-testing/SKILL.md +497 -0
  145. package/stacks/lang/swift/skills/swift-testing/references/testing-advanced.md +106 -0
  146. package/stacks/lang/swift/skills/swift-testing/references/testing-patterns.md +504 -0
  147. package/stacks/lang/swift/skills/swiftdata/SKILL.md +334 -0
  148. package/stacks/lang/swift/skills/swiftdata/references/core-data-coexistence.md +504 -0
  149. package/stacks/lang/swift/skills/swiftdata/references/swiftdata-advanced.md +975 -0
  150. package/stacks/lang/swift/skills/swiftdata/references/swiftdata-queries.md +675 -0
  151. package/stacks/lang/swift/skills/swiftui-patterns/SKILL.md +371 -0
  152. package/stacks/lang/swift/skills/swiftui-patterns/references/architecture-patterns.md +486 -0
  153. package/stacks/lang/swift/skills/swiftui-patterns/references/deprecated-migration.md +1097 -0
  154. package/stacks/lang/swift/skills/swiftui-patterns/references/design-polish.md +780 -0
  155. package/stacks/lang/swift/skills/swiftui-patterns/references/platform-and-sharing.md +696 -0
  156. package/stacks/lang/typescript/skills/typescript-conventions/SKILL.md +91 -0
  157. package/stacks/platform/android/claude.fragment.md +40 -0
  158. package/stacks/platform/android/hooks/pre-push-gradle.sh +70 -0
  159. package/stacks/platform/android/settings.fragment.json +13 -0
  160. package/stacks/platform/android/skills/android-build-conventions/SKILL.md +247 -0
  161. package/stacks/platform/ios/claude.fragment.md +24 -0
  162. package/stacks/platform/ios/hooks/pre-push-xcodebuild.sh +82 -0
  163. package/stacks/platform/ios/settings.fragment.json +21 -0
  164. package/stacks/platform/ios/skills/xcodebuildmcp-simulator-logs/SKILL.md +76 -0
  165. package/stacks/platform/web/skills/frontend-testing/SKILL.md +246 -0
  166. package/stacks/platform/web/skills/react-conventions/SKILL.md +261 -0
  167. package/stacks/platform/web/skills/web-platform-conventions/SKILL.md +55 -0
  168. package/stacks/tooling/issue-tracker-github/claude.fragment.md +10 -0
  169. package/stacks/tooling/issue-tracker-github/settings.fragment.json +24 -0
  170. package/stacks/tooling/issue-tracker-github/skills/issue-tracker-github/SKILL.md +278 -0
  171. package/stacks/tooling/issue-tracker-linear/claude.fragment.md +17 -0
  172. package/stacks/tooling/issue-tracker-linear/settings.fragment.json +9 -0
  173. package/stacks/tooling/issue-tracker-linear/skills/issue-tracker-linear/SKILL.md +183 -0
@@ -0,0 +1,278 @@
1
+ ---
2
+ name: issue-tracker-github
3
+ description: Issue tracking and project-board operations on GitHub Issues + GitHub Projects (ProjectV2). Implements the shared issue-tracker operation contract (create/list/get/update/comment/close issue; create/add-to/query board; set board field; link PR) with concrete `gh` CLI and `gh api graphql` commands. Activate when the active tracker is GitHub and the work involves filing issues, managing a project board (Phase/Priority/Owner), or linking a PR to close a work item.
4
+ ---
5
+
6
+ # Issue tracker: GitHub Issues + Projects
7
+
8
+ This skill is the GitHub implementation of the **shared issue-tracker operation
9
+ contract**. Commands and agents call these operations *by name* without knowing the
10
+ backend; the Linear pack (`issue-tracker-linear`) implements the same names so the two
11
+ are swappable. Keep the operation names below stable.
12
+
13
+ Builds on the knowledge already in the core `gh-cli` and `github-issues` skills — read
14
+ those for full `gh` flag coverage and issue-body templates. This skill does not repeat
15
+ them; it maps the **contract operations** onto concrete commands.
16
+
17
+ **Identifiers:** GitHub issues are referenced as `#123`. Boards are GitHub ProjectV2
18
+ ("Projects"). The repo (`<owner>/<repo>`) and the Project number/owner are project facts —
19
+ read them from `CLAUDE.md`.
20
+
21
+ ## Discipline: bodies come from tmp/ files, never heredocs
22
+
23
+ Every operation that writes multi-line markdown (issue body, comment, PR body) writes
24
+ the content to a `tmp/` file first and passes it with `--body-file`. Do **not** inline
25
+ multi-line bodies via heredoc/`echo` — it mangles backticks, quotes, and `$`. One-line
26
+ bodies may use `--body "..."`.
27
+
28
+ ```bash
29
+ # Write the body, then reference it.
30
+ # tmp/issue-body-<slug>.md
31
+ # tmp/comment-<slug>.md
32
+ # tmp/pr-body-<slug>.md
33
+ ```
34
+
35
+ ## Contract operations
36
+
37
+ ### create-issue(title, body-from-tmp-file, type/label, [project, phase]) -> id + URL
38
+
39
+ ```bash
40
+ # 1. Write the body to tmp/issue-body-<slug>.md (see github-issues for templates).
41
+ # 2. Create. --type/--label carries the contract "type/label". Prints the issue URL.
42
+ gh issue create \
43
+ --repo <owner>/<repo> \
44
+ --title "[Feature] <title>" \
45
+ --body-file tmp/issue-body-<slug>.md \
46
+ --label "<type-or-label>"
47
+ # Capture the number for downstream ops:
48
+ ISSUE_URL=$(gh issue create --repo <owner>/<repo> --title "<title>" \
49
+ --body-file tmp/issue-body-<slug>.md --label "<label>")
50
+ ISSUE_NUM=$(basename "$ISSUE_URL")
51
+ ```
52
+
53
+ If `[project, phase]` were supplied, follow with **add-to-board** below.
54
+
55
+ ### list-issues(filter: state | label/type | assignee | project/phase)
56
+
57
+ ```bash
58
+ gh issue list --repo <owner>/<repo> --state open
59
+ gh issue list --repo <owner>/<repo> --label bug
60
+ gh issue list --repo <owner>/<repo> --assignee @me
61
+ gh issue list --repo <owner>/<repo> --search "is:open label:bug" \
62
+ --json number,title,labels,assignees
63
+ # Filter by project/phase => read the board (see query-board); Phase is a Project field,
64
+ # not an issue field, so it is queried through the Project, not `gh issue list`.
65
+ ```
66
+
67
+ ### get-issue(id)
68
+
69
+ ```bash
70
+ gh issue view <id> --repo <owner>/<repo> \
71
+ --json number,title,body,state,labels,assignees,comments
72
+ ```
73
+
74
+ ### update-issue(id, fields | labels | assignee)
75
+
76
+ ```bash
77
+ gh issue edit <id> --repo <owner>/<repo> --title "<new title>"
78
+ gh issue edit <id> --repo <owner>/<repo> --body-file tmp/issue-body-<slug>.md
79
+ gh issue edit <id> --repo <owner>/<repo> --add-label ready --remove-label triage
80
+ gh issue edit <id> --repo <owner>/<repo> --add-assignee <user>
81
+ ```
82
+
83
+ ### comment-issue(id, body) — records local-review findings on the work item
84
+
85
+ ```bash
86
+ # Write findings to tmp/comment-<slug>.md first, then:
87
+ gh issue comment <id> --repo <owner>/<repo> --body-file tmp/comment-<slug>.md
88
+ ```
89
+
90
+ ### close-issue(id)
91
+
92
+ ```bash
93
+ gh issue close <id> --repo <owner>/<repo>
94
+ gh issue close <id> --repo <owner>/<repo> --comment "Resolved by #<pr>"
95
+ ```
96
+
97
+ ## Boards (GitHub ProjectV2)
98
+
99
+ The GitHub MCP does **not** support Projects — always use `gh project` / `gh api graphql`
100
+ for board operations. ProjectV2 lives at the **user or org** level (not the repo), so
101
+ board ops take `--owner <owner>` and the project **number**. Custom fields (Phase,
102
+ Priority, Owner) are ProjectV2 fields.
103
+
104
+ ### create-board(name, fields: Phase, Priority, Owner)
105
+
106
+ ```bash
107
+ # Create the project (board). --format json yields its number + id.
108
+ gh project create --owner <owner> --title "<name>" --format json
109
+ PROJECT_NUM=<number from output>
110
+
111
+ # Add the three contract fields.
112
+ # Phase + Priority => single-select (their options are the allowed values)
113
+ # Owner => single-select of usernames, or text
114
+ gh project field-create <PROJECT_NUM> --owner <owner> \
115
+ --name "Phase" --data-type SINGLE_SELECT \
116
+ --single-select-options "Phase 1,Phase 2,Phase 3,Phase 4"
117
+ gh project field-create <PROJECT_NUM> --owner <owner> \
118
+ --name "Priority" --data-type SINGLE_SELECT \
119
+ --single-select-options "P0,P1,P2,P3"
120
+ gh project field-create <PROJECT_NUM> --owner <owner> \
121
+ --name "Owner" --data-type TEXT
122
+ gh project field-list <PROJECT_NUM> --owner <owner> # capture field ids + option ids
123
+ ```
124
+
125
+ ### add-to-board(issue, phase, priority, owner)
126
+
127
+ ```bash
128
+ # Add the issue to the board; capture the item id it returns.
129
+ gh project item-add <PROJECT_NUM> --owner <owner> \
130
+ --url https://github.com/<owner>/<repo>/issues/<issue>
131
+ ITEM_ID=<item id from item-add --format json>
132
+
133
+ # Set the three fields on that item (see set-board-field for the field/option ids).
134
+ ```
135
+
136
+ ### set-board-field(item, field, value)
137
+
138
+ ```bash
139
+ # Single-select field (Phase, Priority): pass the option id.
140
+ gh project item-edit --id <ITEM_ID> --project-id <PROJECT_ID> \
141
+ --field-id <PHASE_FIELD_ID> --single-select-option-id <PHASE_OPTION_ID>
142
+ # Text field (Owner):
143
+ gh project item-edit --id <ITEM_ID> --project-id <PROJECT_ID> \
144
+ --field-id <OWNER_FIELD_ID> --text "<owner-username>"
145
+ ```
146
+
147
+ `add-to-board` = `item-add` followed by one `set-board-field` per field (Phase, Priority,
148
+ Owner). Get the `<PROJECT_ID>`, field ids, and option ids from `field-list --format json`
149
+ once and reuse them.
150
+
151
+ ### query-board(board) -> items grouped by Phase
152
+
153
+ The "pick the lowest open phase" read. `gh project item-list` flattens fields; group by
154
+ the Phase value. For full fidelity use GraphQL:
155
+
156
+ ```bash
157
+ gh project item-list <PROJECT_NUM> --owner <owner> --format json \
158
+ --jq '.items
159
+ | map({num: .content.number, title: .content.title, phase: .phase, status: .status})
160
+ | group_by(.phase)'
161
+
162
+ # GraphQL equivalent (open items, their Phase field value):
163
+ gh api graphql -f query='
164
+ query($org:String!, $num:Int!){
165
+ organization(login:$org){
166
+ projectV2(number:$num){
167
+ items(first:100){
168
+ nodes{
169
+ content{ ... on Issue { number title state } }
170
+ fieldValueByName(name:"Phase"){
171
+ ... on ProjectV2ItemFieldSingleSelectValue { name }
172
+ }
173
+ }
174
+ }
175
+ }
176
+ }
177
+ }' -f org=<owner> -F num=<PROJECT_NUM>
178
+ ```
179
+
180
+ To pick the lowest open phase: filter to open issues, take the minimum `Phase` value
181
+ present.
182
+
183
+ ### close-board(board) — close the project once every issue is done
184
+
185
+ Close the ProjectV2 so the board drops out of the active list — but **only when every
186
+ issue on it is closed**. The project `item-list` `state` field is UNRELIABLE (often
187
+ returns null/`?`), so do not trust it: intersect the board's issue numbers with the
188
+ actually-open issues and require the intersection to be empty.
189
+
190
+ ```bash
191
+ # issue numbers on the board…
192
+ gh project item-list <PROJECT_NUM> --owner <owner> --format json --limit 400 \
193
+ | jq -r '.items[].content | select(.type=="Issue") | .number' | sort -u > /tmp/proj_issues.txt
194
+ # …cross-checked against actually-open issues (NOT the board's state field)
195
+ gh issue list --repo <owner>/<repo> --state open --limit 900 --json number --jq '.[].number' \
196
+ | sort -u > /tmp/open_issues.txt
197
+ comm -12 /tmp/proj_issues.txt /tmp/open_issues.txt # ← MUST be empty before closing
198
+
199
+ # if the intersection is empty:
200
+ gh project close <PROJECT_NUM> --owner <owner>
201
+ ```
202
+
203
+ - **Gate:** any issue still open → do NOT close (the project isn't done; stop and
204
+ report). Draft cards / notes (non-issue items) do **not** gate — only real issues do.
205
+ - Reversible: `gh project close <PROJECT_NUM> --owner <owner> --undo` reopens it.
206
+ Closing archives the board from the active view; it does not delete the project or its
207
+ items.
208
+
209
+ ## link-pr(issue, pr) — how a merged PR closes/links the work item
210
+
211
+ GitHub auto-closes linked issues when a PR merges if the PR body contains a closing
212
+ keyword. **One keyword per line — never comma-separated** (GitHub closes only the first
213
+ issue on a comma-separated line).
214
+
215
+ ```bash
216
+ # Write the PR body to tmp/pr-body-<slug>.md with one closing keyword per line:
217
+ # Closes #<issue>
218
+ # Closes #<other-issue>
219
+ gh pr create --repo <owner>/<repo> --title "<title>" \
220
+ --body-file tmp/pr-body-<slug>.md
221
+ ```
222
+
223
+ On merge, GitHub closes each referenced issue and records the PR link on it. PRs and CI
224
+ stay on GitHub (`gh`).
225
+
226
+ ## Hard-won lessons
227
+
228
+ GitHub Issues/Projects API gotchas that fail silently or destructively. Each is
229
+ Symptom / Cause / Fix.
230
+
231
+ ### Verify a label exists before filing an issue with it
232
+
233
+ - **Symptom:** `gh issue create --label X` errors with `could not add label: 'X' not found`
234
+ and the issue is **not** created — no partial success. Batch issue-filing runs waste every
235
+ `gh` call when an assumed label (`dashboard`, `recommendation-engine`) isn't the repo's
236
+ actual label (`frontend`, `recommendation`).
237
+ - **Cause:** `gh` rejects the whole create when any `--label` doesn't exist in the repo.
238
+ - **Fix:** List labels first and map your intent → real names before filing:
239
+ ```bash
240
+ gh label list --repo <owner>/<repo> --limit 100 --json name --jq '.[].name' | sort
241
+ ```
242
+ Don't trust cached label lists from prior runs — query the live list each time.
243
+
244
+ ### ProjectV2 single-select fields are GraphQL-only
245
+
246
+ - **Symptom:** A fresh `gh project create` board has only built-in fields (`Title`,
247
+ `Assignees`, `Status`, `Labels`, …). There's no `gh` shortcut to add a single-select field
248
+ with options (Phase / Priority / Owner), so operators end up configuring them by hand in the
249
+ UI for every issue.
250
+ - **Cause:** The `gh` CLI has no subcommand to create or edit a single-select field's option
251
+ list — `field-create` makes a field, `field-delete` removes one, `item-edit` sets a value on
252
+ an item; none append/rename options on an existing single-select field.
253
+ - **Fix:** Create and edit single-select fields with `gh api graphql` (`createProjectV2Field`
254
+ with `dataType: SINGLE_SELECT` and `singleSelectOptions`). Verify with
255
+ `gh project field-list <num> --owner <owner>` before assuming `Status` is the only field.
256
+
257
+ ### updateProjectV2Field preserves option IDs verbatim — pass the full set
258
+
259
+ - **Symptom:** Adding one new option to an existing single-select field (e.g. a new Phase)
260
+ silently orphans **every** previously-categorized item to "(no phase)" — the value vanishes
261
+ from each item even though the field still exists. Observed: 21 items lost their Phase on one
262
+ add.
263
+ - **Cause:** `updateProjectV2Field` treats `singleSelectOptions` as the **authoritative state**,
264
+ not a delta. Omitting existing options regenerates all option IDs, orphaning every board item
265
+ that referenced an old ID.
266
+ - **Fix:** First fetch the existing options + IDs, then call `updateProjectV2Field` with the
267
+ **full** array — every existing option's `id` reused verbatim plus the new one appended.
268
+ Afterward audit immediately and re-set any orphans:
269
+ ```bash
270
+ gh project item-list <num> --owner <owner> --format json \
271
+ | jq '[.items[] | select(.phase == null)] | length' # must be 0
272
+ ```
273
+
274
+ ## See also
275
+
276
+ - core `gh-cli` — full `gh` command/flag reference.
277
+ - core `github-issues` — issue-body templates, labels, MCP fallback.
278
+ - core `github-actions` — workflow expression/injection/draft-gate/rate-limit lessons.
@@ -0,0 +1,17 @@
1
+ ## Issue tracking
2
+
3
+ - **Active tracker: Linear.** Agents use the `issue-tracker-linear` skill for all issue
4
+ and board operations (via the Linear MCP): create/list/get/update/comment/close issue;
5
+ create/add-to/query board; set board field; link PR.
6
+ - PRs and CI stay on GitHub via `gh`; Linear auto-links them through its GitHub
7
+ integration (magic words like `Fixes ABC-123` in the PR title/description, or a branch
8
+ named `<team>-<n>-slug`).
9
+ - The board maps to a Linear **Project**; **Phase**→milestone, **Priority**→native issue
10
+ priority, **Owner**→assignee.
11
+
12
+ The Linear MCP connector (`https://mcp.linear.app/mcp`, configured in this pack's
13
+ `settings.fragment.json`) must be enabled and authorized in the session for the skill to
14
+ reach Linear.
15
+
16
+ <!-- naturalize: set the Linear team key (e.g. ABC) and the project that holds the board.
17
+ team-key: <TEAM_KEY> -->
@@ -0,0 +1,9 @@
1
+ {
2
+ "permissions": {},
3
+ "mcpServers": {
4
+ "linear": {
5
+ "type": "http",
6
+ "url": "https://mcp.linear.app/mcp"
7
+ }
8
+ }
9
+ }
@@ -0,0 +1,183 @@
1
+ ---
2
+ name: issue-tracker-linear
3
+ description: Issue tracking and project-board operations on Linear, via the Linear MCP. Implements the shared issue-tracker operation contract (create/list/get/update/comment/close issue; create/add-to/query board; set board field; link PR) mapped onto Linear issues, projects, milestones, priorities, and assignees. Activate when the active tracker is Linear and the work involves filing issues, managing a Linear project/board (Phase/Priority/Owner), or linking a PR to close a work item.
4
+ ---
5
+
6
+ # Issue tracker: Linear
7
+
8
+ This skill is the Linear implementation of the **shared issue-tracker operation
9
+ contract**. Commands and agents call these operations *by name* without knowing the
10
+ backend; the GitHub pack (`issue-tracker-github`) implements the same names, so the two
11
+ are swappable. Keep the operation names below stable.
12
+
13
+ Linear is reached through the **Linear MCP** (tools prefixed `mcp__claude_ai_Linear__`).
14
+ There is no Linear CLI — every operation is an MCP tool call, so this pack adds no Bash
15
+ permissions. If the tools are not visible in the session, discover them via tool search;
16
+ the MCP connector must be enabled (see the pack's `claude.fragment.md`).
17
+
18
+ **Note — no separate create tool:** `save_issue`, `save_project`, and `save_milestone`
19
+ each do **both create and update** (omit the id to create, pass it to update).
20
+
21
+ ## Project facts (naturalize)
22
+
23
+ The Linear **team** and **project** are project facts. Read the **team key** (e.g.
24
+ `ABC`) from `CLAUDE.md`. Resolve the team/project to their ids once via `list_teams` /
25
+ `list_projects` and reuse them.
26
+
27
+ ### Identifier mapping
28
+
29
+ | Backend | Issue id form |
30
+ | ------- | -------------------- |
31
+ | GitHub | `#123` |
32
+ | Linear | `ABC-123` (team-prefixed) |
33
+
34
+ Callers may pass either the human identifier (`ABC-123`) or the issue's internal id;
35
+ `get_issue` / `save_issue` accept the identifier.
36
+
37
+ ## Contract -> Linear mapping (board fields)
38
+
39
+ A contract "board" is a **Linear Project**. The board's three custom fields map onto
40
+ native Linear concepts — there are no ad-hoc custom fields:
41
+
42
+ | Contract field | Linear concept | Tool to set it |
43
+ | -------------- | ------------------------------------- | --------------------------------------- |
44
+ | Board | **Project** | `save_project` |
45
+ | Phase | **Milestone** (project milestone) | `save_milestone` + `save_issue` (milestone) |
46
+ | Priority | **Issue priority** (native, 0–4) | `save_issue` (priority) |
47
+ | Owner | **Assignee** | `save_issue` (assignee) |
48
+ | State/Status | **Workflow state** (Todo/Done/…) | `save_issue` (state via `list_issue_statuses`) |
49
+
50
+ (Phase may alternatively map to a **label set** instead of milestones; prefer milestones
51
+ — they are ordered, which makes "lowest open phase" a natural sort.)
52
+
53
+ ## Contract operations
54
+
55
+ ### create-issue(title, body-from-tmp-file, type/label, [project, phase]) -> id + URL
56
+
57
+ `save_issue` with no id creates. Set `team` (the team key), `title`, `description`
58
+ (the body), `labels` (the contract type/label), and optionally `assignee`, `project`,
59
+ `priority`. Read the long markdown body from `tmp/issue-body-<slug>.md` and pass its
60
+ contents as `description` (real newlines, not `\n`).
61
+
62
+ ```text
63
+ save_issue(team: "ABC", title: "<title>", description: <contents of tmp/issue-body-<slug>.md>,
64
+ labels: ["<type-or-label>"], assignee: <owner?>, project: <project?>)
65
+ -> returns the issue identifier (ABC-123) and URL.
66
+ ```
67
+
68
+ If `[project, phase]` were supplied, follow with **add-to-board**.
69
+
70
+ ### list-issues(filter: state | label/type | assignee | project/phase)
71
+
72
+ `list_issues` with the matching filter (filter by `team`, `project`, `state`/status,
73
+ `assignee`, `label`, or `cycle`). For a project/phase filter, list by `project` and
74
+ read each issue's milestone (see query-board).
75
+
76
+ ### get-issue(id)
77
+
78
+ `get_issue(id: "ABC-123")` -> full issue (title, description, state, labels, assignee,
79
+ priority, project, milestone).
80
+
81
+ ### update-issue(id, fields | labels | assignee)
82
+
83
+ `save_issue` **with the id** updates only the passed fields:
84
+
85
+ ```text
86
+ save_issue(id: "ABC-123", title?: ..., description?: ..., labels?: [...], assignee?: ...)
87
+ ```
88
+
89
+ ### comment-issue(id, body) — records local-review findings on the work item
90
+
91
+ `save_comment(issueId: "ABC-123", body: <contents of tmp/comment-<slug>.md>)`.
92
+
93
+ ### close-issue(id)
94
+
95
+ Linear has no "close" verb — set the issue to a **Done** (or **Canceled**) workflow
96
+ state. Resolve the state id once via `list_issue_statuses(team: "ABC")`, then:
97
+
98
+ ```text
99
+ save_issue(id: "ABC-123", state: "Done") # or "Canceled"
100
+ ```
101
+
102
+ ## Boards (Linear Projects + Milestones)
103
+
104
+ ### create-board(name, fields: Phase, Priority, Owner)
105
+
106
+ `save_project(name: "<name>", teamId: <team>)` creates the project (board). Then create
107
+ one **milestone per Phase** (these are the Phase field's values):
108
+
109
+ ```text
110
+ save_project(name: "<name>", teams: ["ABC"]) -> project id
111
+ save_milestone(projectId: <project>, name: "Phase 1")
112
+ save_milestone(projectId: <project>, name: "Phase 2")
113
+ save_milestone(projectId: <project>, name: "Phase 3")
114
+ ```
115
+
116
+ Priority and Owner need no setup — they are native issue fields (priority, assignee).
117
+
118
+ ### add-to-board(issue, phase, priority, owner)
119
+
120
+ One `save_issue` setting all four:
121
+
122
+ ```text
123
+ save_issue(id: "ABC-123",
124
+ project: <project>,
125
+ milestone: <milestone for "phase">, # Phase
126
+ priority: <0–4>, # Priority (1=Urgent … 4=Low, 0=None)
127
+ assignee: "<owner>") # Owner
128
+ ```
129
+
130
+ Resolve the milestone for a phase via `list_milestones(projectId: <project>)`.
131
+
132
+ ### set-board-field(item, field, value)
133
+
134
+ All board fields are issue fields, so this is `save_issue` with the one field:
135
+
136
+ ```text
137
+ save_issue(id: "ABC-123", milestone: <…>) # Phase
138
+ save_issue(id: "ABC-123", priority: <0–4>) # Priority
139
+ save_issue(id: "ABC-123", assignee: <…>) # Owner
140
+ ```
141
+
142
+ ### query-board(board) -> items grouped by Phase
143
+
144
+ `list_issues(project: <project>)`, then group the results by each issue's **milestone**
145
+ (Phase). To "pick the lowest open phase": filter to non-Done issues, group by milestone,
146
+ and take the earliest milestone (milestones are ordered) that still has open issues.
147
+ Use `list_milestones(projectId: <project>)` for the canonical phase order.
148
+
149
+ ### close-board(board) — close the project once every issue is done
150
+
151
+ Mark the Linear **project** Completed so it drops off the active board — but **only when
152
+ every issue in it is Done or Canceled**. Check with `list_issues(project: <project>)` and
153
+ confirm none remain in a non-terminal state (use `list_issue_statuses(team: "ABC")` to
154
+ know which states are terminal); the count of non-terminal issues must be zero.
155
+
156
+ ```
157
+ save_project(id: <project>, state: "completed") # Linear project status → Completed
158
+ ```
159
+
160
+ - **Gate:** any issue still open / in progress → do NOT complete the project (it isn't
161
+ done; stop and report).
162
+ - Reversible: re-open by setting the project state back (e.g. `"started"`). Completing
163
+ drops it from the active view; it does not delete the project or its issues.
164
+
165
+ ## link-pr(issue, pr) — how a merged PR closes/links the work item
166
+
167
+ PRs and CI stay on **GitHub** (`gh`). Linear's GitHub integration auto-links and
168
+ auto-closes the issue via **magic words**, no MCP call needed:
169
+
170
+ - Branch named `<team>-<n>-slug` (e.g. `abc-123-fix-login`) — Linear links it
171
+ automatically, and
172
+ - a closing magic word in the **PR title or description**: `Fixes ABC-123`,
173
+ `Closes ABC-123`, `Resolves ABC-123`.
174
+
175
+ When the PR merges, Linear moves `ABC-123` to its Done state and records the PR link.
176
+ So `link-pr` is realized by naming the branch / writing the magic word when the PR is
177
+ created via `gh` — there is no separate Linear tool call.
178
+
179
+ ## See also
180
+
181
+ - The GitHub pack `issue-tracker-github` implements the same contract on GitHub Issues +
182
+ Projects — same operation names, swappable backend.
183
+ - core `gh-cli` — PR creation (`gh pr create`) still goes through GitHub.