gsd-pi 2.10.12 → 2.12.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 (504) hide show
  1. package/dist/bundled-extension-paths.d.ts +2 -0
  2. package/dist/bundled-extension-paths.js +10 -0
  3. package/dist/loader.js +45 -28
  4. package/dist/onboarding.js +100 -0
  5. package/dist/resource-loader.js +12 -3
  6. package/dist/resources/GSD-WORKFLOW.md +661 -0
  7. package/dist/resources/agents/researcher.md +29 -0
  8. package/dist/resources/agents/scout.md +56 -0
  9. package/dist/resources/agents/worker.md +31 -0
  10. package/dist/resources/extensions/ask-user-questions.ts +289 -0
  11. package/dist/resources/extensions/async-jobs/async-bash-tool.ts +211 -0
  12. package/dist/resources/extensions/async-jobs/await-tool.ts +101 -0
  13. package/dist/resources/extensions/async-jobs/cancel-job-tool.ts +34 -0
  14. package/dist/resources/extensions/async-jobs/index.ts +133 -0
  15. package/dist/resources/extensions/async-jobs/job-manager.ts +211 -0
  16. package/dist/resources/extensions/bg-shell/index.ts +3179 -0
  17. package/dist/resources/extensions/browser-tools/BROWSER-TOOLS-V2-PROPOSAL.md +1277 -0
  18. package/dist/resources/extensions/browser-tools/capture.ts +165 -0
  19. package/dist/resources/extensions/browser-tools/core.d.ts +205 -0
  20. package/dist/resources/extensions/browser-tools/core.js +1057 -0
  21. package/dist/resources/extensions/browser-tools/evaluate-helpers.ts +184 -0
  22. package/dist/resources/extensions/browser-tools/index.ts +51 -0
  23. package/dist/resources/extensions/browser-tools/lifecycle.ts +270 -0
  24. package/dist/resources/extensions/browser-tools/package.json +24 -0
  25. package/dist/resources/extensions/browser-tools/refs.ts +264 -0
  26. package/dist/resources/extensions/browser-tools/settle.ts +197 -0
  27. package/dist/resources/extensions/browser-tools/state.ts +408 -0
  28. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +652 -0
  29. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +614 -0
  30. package/dist/resources/extensions/browser-tools/tools/assertions.ts +342 -0
  31. package/dist/resources/extensions/browser-tools/tools/forms.ts +801 -0
  32. package/dist/resources/extensions/browser-tools/tools/inspection.ts +492 -0
  33. package/dist/resources/extensions/browser-tools/tools/intent.ts +614 -0
  34. package/dist/resources/extensions/browser-tools/tools/interaction.ts +865 -0
  35. package/dist/resources/extensions/browser-tools/tools/navigation.ts +232 -0
  36. package/dist/resources/extensions/browser-tools/tools/pages.ts +303 -0
  37. package/dist/resources/extensions/browser-tools/tools/refs.ts +541 -0
  38. package/dist/resources/extensions/browser-tools/tools/screenshot.ts +83 -0
  39. package/dist/resources/extensions/browser-tools/tools/session.ts +400 -0
  40. package/dist/resources/extensions/browser-tools/tools/wait.ts +247 -0
  41. package/dist/resources/extensions/browser-tools/utils.ts +660 -0
  42. package/dist/resources/extensions/context7/index.ts +428 -0
  43. package/dist/resources/extensions/context7/package.json +11 -0
  44. package/dist/resources/extensions/get-secrets-from-user.ts +625 -0
  45. package/dist/resources/extensions/google-search/index.ts +336 -0
  46. package/dist/resources/extensions/google-search/package.json +9 -0
  47. package/dist/resources/extensions/gsd/activity-log.ts +116 -0
  48. package/dist/resources/extensions/gsd/auto.ts +3732 -0
  49. package/dist/resources/extensions/gsd/commands.ts +544 -0
  50. package/dist/resources/extensions/gsd/crash-recovery.ts +105 -0
  51. package/dist/resources/extensions/gsd/dashboard-overlay.ts +564 -0
  52. package/dist/resources/extensions/gsd/dispatch-guard.ts +78 -0
  53. package/dist/resources/extensions/gsd/docs/preferences-reference.md +278 -0
  54. package/dist/resources/extensions/gsd/doctor.ts +766 -0
  55. package/dist/resources/extensions/gsd/exit-command.ts +18 -0
  56. package/dist/resources/extensions/gsd/files.ts +857 -0
  57. package/dist/resources/extensions/gsd/git-service.ts +904 -0
  58. package/dist/resources/extensions/gsd/gitignore.ts +200 -0
  59. package/dist/resources/extensions/gsd/guided-flow.ts +1054 -0
  60. package/dist/resources/extensions/gsd/index.ts +735 -0
  61. package/dist/resources/extensions/gsd/metrics.ts +374 -0
  62. package/dist/resources/extensions/gsd/migrate/command.ts +218 -0
  63. package/dist/resources/extensions/gsd/migrate/index.ts +42 -0
  64. package/dist/resources/extensions/gsd/migrate/parser.ts +323 -0
  65. package/dist/resources/extensions/gsd/migrate/parsers.ts +624 -0
  66. package/dist/resources/extensions/gsd/migrate/preview.ts +48 -0
  67. package/dist/resources/extensions/gsd/migrate/transformer.ts +346 -0
  68. package/dist/resources/extensions/gsd/migrate/types.ts +370 -0
  69. package/dist/resources/extensions/gsd/migrate/validator.ts +55 -0
  70. package/dist/resources/extensions/gsd/migrate/writer.ts +539 -0
  71. package/dist/resources/extensions/gsd/native-git-bridge.ts +181 -0
  72. package/dist/resources/extensions/gsd/native-parser-bridge.ts +135 -0
  73. package/dist/resources/extensions/gsd/observability-validator.ts +408 -0
  74. package/dist/resources/extensions/gsd/package.json +11 -0
  75. package/dist/resources/extensions/gsd/paths.ts +339 -0
  76. package/dist/resources/extensions/gsd/post-unit-hooks.ts +449 -0
  77. package/dist/resources/extensions/gsd/preferences.ts +988 -0
  78. package/dist/resources/extensions/gsd/prompt-loader.ts +92 -0
  79. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +25 -0
  80. package/dist/resources/extensions/gsd/prompts/complete-slice.md +27 -0
  81. package/dist/resources/extensions/gsd/prompts/discuss.md +237 -0
  82. package/dist/resources/extensions/gsd/prompts/doctor-heal.md +29 -0
  83. package/dist/resources/extensions/gsd/prompts/execute-task.md +63 -0
  84. package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +3 -0
  85. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +5 -0
  86. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +61 -0
  87. package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +3 -0
  88. package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +29 -0
  89. package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +3 -0
  90. package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +13 -0
  91. package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -0
  92. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +78 -0
  93. package/dist/resources/extensions/gsd/prompts/plan-slice.md +55 -0
  94. package/dist/resources/extensions/gsd/prompts/queue.md +100 -0
  95. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +48 -0
  96. package/dist/resources/extensions/gsd/prompts/replan-slice.md +39 -0
  97. package/dist/resources/extensions/gsd/prompts/research-milestone.md +37 -0
  98. package/dist/resources/extensions/gsd/prompts/research-slice.md +28 -0
  99. package/dist/resources/extensions/gsd/prompts/review-migration.md +66 -0
  100. package/dist/resources/extensions/gsd/prompts/run-uat.md +109 -0
  101. package/dist/resources/extensions/gsd/prompts/system.md +190 -0
  102. package/dist/resources/extensions/gsd/prompts/worktree-merge.md +123 -0
  103. package/dist/resources/extensions/gsd/roadmap-slices.ts +50 -0
  104. package/dist/resources/extensions/gsd/session-forensics.ts +500 -0
  105. package/dist/resources/extensions/gsd/skill-discovery.ts +137 -0
  106. package/dist/resources/extensions/gsd/state.ts +569 -0
  107. package/dist/resources/extensions/gsd/templates/context.md +76 -0
  108. package/dist/resources/extensions/gsd/templates/decisions.md +8 -0
  109. package/dist/resources/extensions/gsd/templates/milestone-summary.md +73 -0
  110. package/dist/resources/extensions/gsd/templates/plan.md +131 -0
  111. package/dist/resources/extensions/gsd/templates/preferences.md +23 -0
  112. package/dist/resources/extensions/gsd/templates/project.md +31 -0
  113. package/dist/resources/extensions/gsd/templates/reassessment.md +28 -0
  114. package/dist/resources/extensions/gsd/templates/requirements.md +81 -0
  115. package/dist/resources/extensions/gsd/templates/research.md +46 -0
  116. package/dist/resources/extensions/gsd/templates/roadmap.md +118 -0
  117. package/dist/resources/extensions/gsd/templates/secrets-manifest.md +22 -0
  118. package/dist/resources/extensions/gsd/templates/slice-context.md +58 -0
  119. package/dist/resources/extensions/gsd/templates/slice-summary.md +99 -0
  120. package/dist/resources/extensions/gsd/templates/state.md +19 -0
  121. package/dist/resources/extensions/gsd/templates/task-plan.md +52 -0
  122. package/dist/resources/extensions/gsd/templates/task-summary.md +57 -0
  123. package/dist/resources/extensions/gsd/templates/uat.md +54 -0
  124. package/dist/resources/extensions/gsd/tests/activity-log-prune.test.ts +297 -0
  125. package/dist/resources/extensions/gsd/tests/activity-log-save.test.ts +127 -0
  126. package/dist/resources/extensions/gsd/tests/auto-draft-pause.test.ts +109 -0
  127. package/dist/resources/extensions/gsd/tests/auto-preflight.test.ts +45 -0
  128. package/dist/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +196 -0
  129. package/dist/resources/extensions/gsd/tests/auto-supervisor.test.mjs +53 -0
  130. package/dist/resources/extensions/gsd/tests/collect-from-manifest.test.ts +469 -0
  131. package/dist/resources/extensions/gsd/tests/complete-milestone.test.ts +200 -0
  132. package/dist/resources/extensions/gsd/tests/cost-projection.test.ts +134 -0
  133. package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +312 -0
  134. package/dist/resources/extensions/gsd/tests/derive-state-draft.test.ts +299 -0
  135. package/dist/resources/extensions/gsd/tests/derive-state.test.ts +660 -0
  136. package/dist/resources/extensions/gsd/tests/discuss-prompt.test.ts +27 -0
  137. package/dist/resources/extensions/gsd/tests/dispatch-guard.test.ts +91 -0
  138. package/dist/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +170 -0
  139. package/dist/resources/extensions/gsd/tests/doctor.test.ts +594 -0
  140. package/dist/resources/extensions/gsd/tests/draft-promotion.test.ts +169 -0
  141. package/dist/resources/extensions/gsd/tests/exit-command.test.ts +50 -0
  142. package/dist/resources/extensions/gsd/tests/git-service.test.ts +1788 -0
  143. package/dist/resources/extensions/gsd/tests/idle-recovery.test.ts +577 -0
  144. package/dist/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +580 -0
  145. package/dist/resources/extensions/gsd/tests/manifest-status.test.ts +283 -0
  146. package/dist/resources/extensions/gsd/tests/metrics-io.test.ts +176 -0
  147. package/dist/resources/extensions/gsd/tests/metrics.test.ts +188 -0
  148. package/dist/resources/extensions/gsd/tests/migrate-command.test.ts +369 -0
  149. package/dist/resources/extensions/gsd/tests/migrate-parser.test.ts +757 -0
  150. package/dist/resources/extensions/gsd/tests/migrate-transformer.test.ts +635 -0
  151. package/dist/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +414 -0
  152. package/dist/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +297 -0
  153. package/dist/resources/extensions/gsd/tests/migrate-writer.test.ts +398 -0
  154. package/dist/resources/extensions/gsd/tests/must-have-parser.test.ts +291 -0
  155. package/dist/resources/extensions/gsd/tests/network-error-fallback.test.ts +54 -0
  156. package/dist/resources/extensions/gsd/tests/next-milestone-id.test.ts +66 -0
  157. package/dist/resources/extensions/gsd/tests/orphaned-branch.test.ts +353 -0
  158. package/dist/resources/extensions/gsd/tests/parsers.test.ts +1664 -0
  159. package/dist/resources/extensions/gsd/tests/plan-milestone.test.ts +133 -0
  160. package/dist/resources/extensions/gsd/tests/plan-quality-validator.test.ts +363 -0
  161. package/dist/resources/extensions/gsd/tests/post-unit-hooks.test.ts +297 -0
  162. package/dist/resources/extensions/gsd/tests/preferences-hooks.test.ts +226 -0
  163. package/dist/resources/extensions/gsd/tests/queue-draft-detection.test.ts +126 -0
  164. package/dist/resources/extensions/gsd/tests/reassess-prompt.test.ts +142 -0
  165. package/dist/resources/extensions/gsd/tests/regex-hardening.test.ts +281 -0
  166. package/dist/resources/extensions/gsd/tests/remote-questions.test.ts +155 -0
  167. package/dist/resources/extensions/gsd/tests/remote-status.test.ts +99 -0
  168. package/dist/resources/extensions/gsd/tests/replan-slice.test.ts +493 -0
  169. package/dist/resources/extensions/gsd/tests/requirements.test.ts +106 -0
  170. package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +35 -0
  171. package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +11 -0
  172. package/dist/resources/extensions/gsd/tests/roadmap-slices.test.ts +41 -0
  173. package/dist/resources/extensions/gsd/tests/run-uat.test.ts +316 -0
  174. package/dist/resources/extensions/gsd/tests/secure-env-collect.test.ts +185 -0
  175. package/dist/resources/extensions/gsd/tests/smart-entry-draft.test.ts +123 -0
  176. package/dist/resources/extensions/gsd/tests/test-helpers.ts +61 -0
  177. package/dist/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +219 -0
  178. package/dist/resources/extensions/gsd/tests/unit-runtime.test.ts +234 -0
  179. package/dist/resources/extensions/gsd/tests/workspace-index.test.ts +75 -0
  180. package/dist/resources/extensions/gsd/tests/worktree-integration.test.ts +234 -0
  181. package/dist/resources/extensions/gsd/tests/worktree-manager.test.ts +141 -0
  182. package/dist/resources/extensions/gsd/tests/worktree.test.ts +597 -0
  183. package/dist/resources/extensions/gsd/tests/write-gate.test.ts +122 -0
  184. package/dist/resources/extensions/gsd/types.ts +296 -0
  185. package/dist/resources/extensions/gsd/unit-runtime.ts +184 -0
  186. package/dist/resources/extensions/gsd/workspace-index.ts +189 -0
  187. package/dist/resources/extensions/gsd/worktree-command.ts +845 -0
  188. package/dist/resources/extensions/gsd/worktree-manager.ts +459 -0
  189. package/dist/resources/extensions/gsd/{worktree.js → worktree.ts} +92 -61
  190. package/dist/resources/extensions/mac-tools/index.ts +852 -0
  191. package/dist/resources/extensions/mac-tools/swift-cli/Package.swift +22 -0
  192. package/dist/resources/extensions/mac-tools/swift-cli/Sources/main.swift +1318 -0
  193. package/dist/resources/extensions/mcporter/index.ts +429 -0
  194. package/dist/resources/extensions/remote-questions/config.ts +81 -0
  195. package/dist/resources/extensions/remote-questions/discord-adapter.ts +128 -0
  196. package/dist/resources/extensions/remote-questions/format.ts +163 -0
  197. package/dist/resources/extensions/remote-questions/manager.ts +192 -0
  198. package/dist/resources/extensions/remote-questions/remote-command.ts +361 -0
  199. package/dist/resources/extensions/remote-questions/slack-adapter.ts +92 -0
  200. package/dist/resources/extensions/remote-questions/status.ts +31 -0
  201. package/dist/resources/extensions/remote-questions/store.ts +77 -0
  202. package/dist/resources/extensions/remote-questions/types.ts +75 -0
  203. package/dist/resources/extensions/search-the-web/cache.ts +78 -0
  204. package/dist/resources/extensions/search-the-web/command-search-provider.ts +99 -0
  205. package/dist/resources/extensions/search-the-web/format.ts +258 -0
  206. package/dist/resources/extensions/search-the-web/http.ts +238 -0
  207. package/dist/resources/extensions/search-the-web/index.ts +65 -0
  208. package/dist/resources/extensions/search-the-web/native-search.ts +165 -0
  209. package/dist/resources/extensions/search-the-web/provider.ts +136 -0
  210. package/dist/resources/extensions/search-the-web/tavily.ts +116 -0
  211. package/dist/resources/extensions/search-the-web/tool-fetch-page.ts +581 -0
  212. package/dist/resources/extensions/search-the-web/tool-llm-context.ts +620 -0
  213. package/dist/resources/extensions/search-the-web/tool-search.ts +635 -0
  214. package/dist/resources/extensions/search-the-web/url-utils.ts +91 -0
  215. package/dist/resources/extensions/shared/bundled-extension-paths.ts +11 -0
  216. package/dist/resources/extensions/shared/confirm-ui.ts +126 -0
  217. package/dist/resources/extensions/shared/interview-ui.ts +613 -0
  218. package/dist/resources/extensions/shared/next-action-ui.ts +197 -0
  219. package/dist/resources/extensions/shared/progress-widget.ts +282 -0
  220. package/dist/resources/extensions/shared/terminal.ts +23 -0
  221. package/dist/resources/extensions/shared/thinking-widget.ts +107 -0
  222. package/dist/resources/extensions/shared/ui.ts +400 -0
  223. package/dist/resources/extensions/shared/wizard-ui.ts +551 -0
  224. package/dist/resources/extensions/slash-commands/audit.ts +88 -0
  225. package/dist/resources/extensions/slash-commands/clear.ts +10 -0
  226. package/dist/resources/extensions/slash-commands/create-extension.ts +297 -0
  227. package/dist/resources/extensions/slash-commands/create-slash-command.ts +234 -0
  228. package/dist/resources/extensions/slash-commands/index.ts +12 -0
  229. package/dist/resources/extensions/subagent/agents.ts +126 -0
  230. package/dist/resources/extensions/subagent/index.ts +1072 -0
  231. package/dist/resources/extensions/subagent/isolation.ts +498 -0
  232. package/dist/resources/extensions/ttsr/index.ts +163 -0
  233. package/dist/resources/extensions/ttsr/rule-loader.ts +121 -0
  234. package/dist/resources/extensions/ttsr/ttsr-interrupt.md +6 -0
  235. package/dist/resources/extensions/ttsr/ttsr-manager.ts +430 -0
  236. package/dist/resources/extensions/universal-config/discovery.ts +78 -0
  237. package/dist/resources/extensions/universal-config/format.ts +160 -0
  238. package/dist/resources/extensions/universal-config/index.ts +118 -0
  239. package/dist/resources/extensions/universal-config/package.json +11 -0
  240. package/dist/resources/extensions/universal-config/scanners.ts +579 -0
  241. package/dist/resources/extensions/universal-config/tests/discovery.test.ts +111 -0
  242. package/dist/resources/extensions/universal-config/tests/format.test.ts +111 -0
  243. package/dist/resources/extensions/universal-config/tests/scanners.test.ts +438 -0
  244. package/dist/resources/extensions/universal-config/tools.ts +60 -0
  245. package/dist/resources/extensions/universal-config/types.ts +116 -0
  246. package/dist/resources/extensions/voice/index.ts +271 -0
  247. package/dist/resources/extensions/voice/speech-recognizer.py +504 -0
  248. package/dist/resources/extensions/voice/speech-recognizer.swift +154 -0
  249. package/dist/resources/skills/debug-like-expert/SKILL.md +231 -0
  250. package/dist/resources/skills/debug-like-expert/references/debugging-mindset.md +253 -0
  251. package/dist/resources/skills/debug-like-expert/references/hypothesis-testing.md +373 -0
  252. package/dist/resources/skills/debug-like-expert/references/investigation-techniques.md +337 -0
  253. package/dist/resources/skills/debug-like-expert/references/verification-patterns.md +425 -0
  254. package/dist/resources/skills/debug-like-expert/references/when-to-research.md +361 -0
  255. package/dist/resources/skills/frontend-design/SKILL.md +45 -0
  256. package/dist/resources/skills/github-workflows/SKILL.md +92 -0
  257. package/dist/resources/skills/github-workflows/references/gh/SKILL.md +255 -0
  258. package/dist/resources/skills/github-workflows/references/gh/references/issue-stories.md +204 -0
  259. package/dist/resources/skills/github-workflows/references/gh/references/labels.md +170 -0
  260. package/dist/resources/skills/github-workflows/references/gh/references/milestones.md +158 -0
  261. package/dist/resources/skills/github-workflows/references/gh/references/projects-v2.md +177 -0
  262. package/dist/resources/skills/github-workflows/references/gh/scripts/experiment_cleanup.py +191 -0
  263. package/dist/resources/skills/github-workflows/references/gh/scripts/github_project_setup.py +799 -0
  264. package/dist/resources/skills/github-workflows/references/gh/tests/__init__.py +0 -0
  265. package/dist/resources/skills/github-workflows/references/gh/tests/test_github_project_setup.py +608 -0
  266. package/dist/resources/skills/swiftui/SKILL.md +208 -0
  267. package/dist/resources/skills/swiftui/references/animations.md +921 -0
  268. package/dist/resources/skills/swiftui/references/architecture.md +1561 -0
  269. package/dist/resources/skills/swiftui/references/layout-system.md +1186 -0
  270. package/dist/resources/skills/swiftui/references/navigation.md +1492 -0
  271. package/dist/resources/skills/swiftui/references/networking-async.md +214 -0
  272. package/dist/resources/skills/swiftui/references/performance.md +1706 -0
  273. package/dist/resources/skills/swiftui/references/platform-integration.md +204 -0
  274. package/dist/resources/skills/swiftui/references/state-management.md +1443 -0
  275. package/dist/resources/skills/swiftui/references/swiftdata.md +297 -0
  276. package/dist/resources/skills/swiftui/references/testing-debugging.md +247 -0
  277. package/dist/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
  278. package/dist/resources/skills/swiftui/workflows/add-feature.md +191 -0
  279. package/dist/resources/skills/swiftui/workflows/build-new-app.md +311 -0
  280. package/dist/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
  281. package/dist/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
  282. package/dist/resources/skills/swiftui/workflows/ship-app.md +203 -0
  283. package/dist/resources/skills/swiftui/workflows/write-tests.md +235 -0
  284. package/dist/wizard.js +1 -0
  285. package/package.json +8 -6
  286. package/packages/native/dist/fd/index.d.ts +1 -0
  287. package/packages/native/dist/fd/index.js +1 -0
  288. package/packages/native/dist/grep/index.d.ts +2 -1
  289. package/packages/native/dist/grep/index.js +1 -0
  290. package/packages/native/src/__tests__/fd.test.mjs +35 -0
  291. package/packages/native/src/__tests__/grep.test.mjs +26 -9
  292. package/packages/native/src/fd/index.ts +1 -0
  293. package/packages/native/src/grep/index.ts +3 -2
  294. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  295. package/packages/pi-agent-core/dist/agent-loop.js +169 -55
  296. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  297. package/packages/pi-agent-core/dist/agent.d.ts +13 -1
  298. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  299. package/packages/pi-agent-core/dist/agent.js +16 -0
  300. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  301. package/packages/pi-agent-core/dist/types.d.ts +91 -1
  302. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  303. package/packages/pi-agent-core/dist/types.js.map +1 -1
  304. package/packages/pi-agent-core/src/agent-loop.ts +273 -63
  305. package/packages/pi-agent-core/src/agent.ts +24 -0
  306. package/packages/pi-agent-core/src/types.ts +98 -0
  307. package/packages/pi-ai/dist/env-api-keys.js +2 -0
  308. package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
  309. package/packages/pi-ai/dist/models.generated.d.ts +314 -0
  310. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  311. package/packages/pi-ai/dist/models.generated.js +236 -0
  312. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  313. package/packages/pi-ai/dist/providers/google-gemini-cli.d.ts.map +1 -1
  314. package/packages/pi-ai/dist/providers/google-gemini-cli.js +6 -0
  315. package/packages/pi-ai/dist/providers/google-gemini-cli.js.map +1 -1
  316. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  317. package/packages/pi-ai/dist/providers/openai-completions.js +2 -0
  318. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  319. package/packages/pi-ai/dist/types.d.ts +1 -1
  320. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  321. package/packages/pi-ai/dist/types.js.map +1 -1
  322. package/packages/pi-ai/src/env-api-keys.ts +2 -0
  323. package/packages/pi-ai/src/models.generated.ts +236 -0
  324. package/packages/pi-ai/src/providers/google-gemini-cli.ts +8 -0
  325. package/packages/pi-ai/src/providers/openai-completions.ts +3 -0
  326. package/packages/pi-ai/src/types.ts +2 -1
  327. package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
  328. package/packages/pi-coding-agent/dist/cli/args.js +1 -0
  329. package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
  330. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +26 -0
  331. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  332. package/packages/pi-coding-agent/dist/core/agent-session.js +137 -22
  333. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  334. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +20 -0
  335. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  336. package/packages/pi-coding-agent/dist/core/auth-storage.js +43 -0
  337. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  338. package/packages/pi-coding-agent/dist/core/fallback-resolver.d.ts +51 -0
  339. package/packages/pi-coding-agent/dist/core/fallback-resolver.d.ts.map +1 -0
  340. package/packages/pi-coding-agent/dist/core/fallback-resolver.js +119 -0
  341. package/packages/pi-coding-agent/dist/core/fallback-resolver.js.map +1 -0
  342. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.d.ts +2 -0
  343. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.d.ts.map +1 -0
  344. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js +183 -0
  345. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js.map +1 -0
  346. package/packages/pi-coding-agent/dist/core/index.d.ts +1 -0
  347. package/packages/pi-coding-agent/dist/core/index.d.ts.map +1 -1
  348. package/packages/pi-coding-agent/dist/core/index.js +1 -0
  349. package/packages/pi-coding-agent/dist/core/index.js.map +1 -1
  350. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  351. package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
  352. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  353. package/packages/pi-coding-agent/dist/core/session-manager.d.ts +12 -1
  354. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  355. package/packages/pi-coding-agent/dist/core/session-manager.js +37 -1
  356. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  357. package/packages/pi-coding-agent/dist/core/session-manager.test.d.ts +2 -0
  358. package/packages/pi-coding-agent/dist/core/session-manager.test.d.ts.map +1 -0
  359. package/packages/pi-coding-agent/dist/core/session-manager.test.js +61 -0
  360. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -0
  361. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +20 -0
  362. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  363. package/packages/pi-coding-agent/dist/core/settings-manager.js +47 -0
  364. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  365. package/packages/pi-coding-agent/dist/core/tools/find.d.ts.map +1 -1
  366. package/packages/pi-coding-agent/dist/core/tools/find.js +1 -0
  367. package/packages/pi-coding-agent/dist/core/tools/find.js.map +1 -1
  368. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  369. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +6 -15
  370. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  371. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts +18 -1
  372. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  373. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +46 -4
  374. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  375. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  376. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +25 -2
  377. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  378. package/packages/pi-coding-agent/src/cli/args.ts +1 -0
  379. package/packages/pi-coding-agent/src/core/agent-session.ts +167 -22
  380. package/packages/pi-coding-agent/src/core/auth-storage.ts +45 -0
  381. package/packages/pi-coding-agent/src/core/fallback-resolver.test.ts +229 -0
  382. package/packages/pi-coding-agent/src/core/fallback-resolver.ts +165 -0
  383. package/packages/pi-coding-agent/src/core/index.ts +1 -0
  384. package/packages/pi-coding-agent/src/core/model-resolver.ts +1 -0
  385. package/packages/pi-coding-agent/src/core/session-manager.test.ts +65 -0
  386. package/packages/pi-coding-agent/src/core/session-manager.ts +51 -1
  387. package/packages/pi-coding-agent/src/core/settings-manager.ts +66 -0
  388. package/packages/pi-coding-agent/src/core/tools/find.ts +1 -0
  389. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +6 -16
  390. package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +49 -4
  391. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +28 -2
  392. package/packages/pi-tui/dist/autocomplete.d.ts.map +1 -1
  393. package/packages/pi-tui/dist/autocomplete.js +3 -4
  394. package/packages/pi-tui/dist/autocomplete.js.map +1 -1
  395. package/packages/pi-tui/dist/components/editor.d.ts +7 -0
  396. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  397. package/packages/pi-tui/dist/components/editor.js +57 -50
  398. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  399. package/packages/pi-tui/src/autocomplete.ts +3 -5
  400. package/packages/pi-tui/src/components/editor.ts +70 -49
  401. package/pkg/dist/core/export-html/ansi-to-html.d.ts +22 -0
  402. package/pkg/dist/core/export-html/ansi-to-html.d.ts.map +1 -0
  403. package/pkg/dist/core/export-html/ansi-to-html.js +249 -0
  404. package/pkg/dist/core/export-html/ansi-to-html.js.map +1 -0
  405. package/pkg/dist/core/export-html/index.d.ts +37 -0
  406. package/pkg/dist/core/export-html/index.d.ts.map +1 -0
  407. package/pkg/dist/core/export-html/index.js +223 -0
  408. package/pkg/dist/core/export-html/index.js.map +1 -0
  409. package/pkg/dist/core/export-html/template.css +971 -0
  410. package/pkg/dist/core/export-html/template.html +54 -0
  411. package/pkg/dist/core/export-html/template.js +1583 -0
  412. package/pkg/dist/core/export-html/tool-renderer.d.ts +38 -0
  413. package/pkg/dist/core/export-html/tool-renderer.d.ts.map +1 -0
  414. package/pkg/dist/core/export-html/tool-renderer.js +70 -0
  415. package/pkg/dist/core/export-html/tool-renderer.js.map +1 -0
  416. package/pkg/dist/core/export-html/vendor/highlight.min.js +1213 -0
  417. package/pkg/dist/core/export-html/vendor/marked.min.js +6 -0
  418. package/scripts/postinstall.js +30 -17
  419. package/src/resources/extensions/bg-shell/index.ts +51 -7
  420. package/src/resources/extensions/gsd/activity-log.ts +61 -14
  421. package/src/resources/extensions/gsd/auto.ts +288 -19
  422. package/src/resources/extensions/gsd/commands.ts +9 -3
  423. package/src/resources/extensions/gsd/dashboard-overlay.ts +55 -12
  424. package/src/resources/extensions/gsd/docs/preferences-reference.md +25 -0
  425. package/src/resources/extensions/gsd/doctor.ts +60 -3
  426. package/src/resources/extensions/gsd/files.ts +45 -1
  427. package/src/resources/extensions/gsd/git-service.ts +46 -41
  428. package/src/resources/extensions/gsd/guided-flow.ts +81 -9
  429. package/src/resources/extensions/gsd/index.ts +62 -1
  430. package/src/resources/extensions/gsd/native-git-bridge.ts +181 -0
  431. package/src/resources/extensions/gsd/paths.ts +35 -4
  432. package/src/resources/extensions/gsd/post-unit-hooks.ts +449 -0
  433. package/src/resources/extensions/gsd/preferences.ts +232 -1
  434. package/src/resources/extensions/gsd/prompt-loader.ts +45 -3
  435. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  436. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -3
  437. package/src/resources/extensions/gsd/prompts/discuss.md +10 -8
  438. package/src/resources/extensions/gsd/prompts/execute-task.md +4 -2
  439. package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +3 -1
  440. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -1
  441. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
  442. package/src/resources/extensions/gsd/prompts/guided-execute-task.md +3 -1
  443. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +4 -2
  444. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +3 -1
  445. package/src/resources/extensions/gsd/prompts/guided-research-slice.md +3 -1
  446. package/src/resources/extensions/gsd/prompts/plan-milestone.md +9 -12
  447. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -3
  448. package/src/resources/extensions/gsd/prompts/queue.md +3 -1
  449. package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  450. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  451. package/src/resources/extensions/gsd/state.ts +130 -18
  452. package/src/resources/extensions/gsd/templates/context.md +1 -1
  453. package/src/resources/extensions/gsd/templates/state.md +3 -3
  454. package/src/resources/extensions/gsd/tests/activity-log-save.test.ts +127 -0
  455. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +5 -1
  456. package/src/resources/extensions/gsd/tests/doctor.test.ts +115 -1
  457. package/src/resources/extensions/gsd/tests/draft-promotion.test.ts +6 -2
  458. package/src/resources/extensions/gsd/tests/git-service.test.ts +35 -35
  459. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +8 -8
  460. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +4 -1
  461. package/src/resources/extensions/gsd/tests/network-error-fallback.test.ts +54 -0
  462. package/src/resources/extensions/gsd/tests/orphaned-branch.test.ts +5 -5
  463. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +297 -0
  464. package/src/resources/extensions/gsd/tests/preferences-hooks.test.ts +226 -0
  465. package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +12 -0
  466. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +6 -0
  467. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +4 -4
  468. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +4 -4
  469. package/src/resources/extensions/gsd/tests/worktree.test.ts +19 -19
  470. package/src/resources/extensions/gsd/types.ts +109 -0
  471. package/src/resources/extensions/gsd/worktree-manager.ts +72 -13
  472. package/src/resources/extensions/gsd/worktree.ts +5 -4
  473. package/src/resources/extensions/search-the-web/command-search-provider.ts +8 -4
  474. package/src/resources/extensions/search-the-web/native-search.ts +32 -23
  475. package/src/resources/extensions/search-the-web/provider.ts +19 -2
  476. package/src/resources/extensions/search-the-web/tool-fetch-page.ts +62 -0
  477. package/src/resources/extensions/search-the-web/tool-llm-context.ts +62 -3
  478. package/src/resources/extensions/search-the-web/tool-search.ts +62 -3
  479. package/src/resources/extensions/shared/bundled-extension-paths.ts +11 -0
  480. package/src/resources/extensions/subagent/index.ts +2 -1
  481. package/src/resources/skills/github-workflows/SKILL.md +5 -0
  482. package/dist/resources/extensions/gsd/git-service.d.ts +0 -225
  483. package/dist/resources/extensions/gsd/git-service.js +0 -756
  484. package/dist/resources/extensions/gsd/preferences.d.ts +0 -122
  485. package/dist/resources/extensions/gsd/preferences.js +0 -616
  486. package/dist/resources/extensions/gsd/worktree.d.ts +0 -106
  487. package/dist/resources/extensions/remote-questions/config.d.ts +0 -14
  488. package/dist/resources/extensions/remote-questions/config.js +0 -68
  489. package/dist/resources/extensions/remote-questions/discord-adapter.d.ts +0 -17
  490. package/dist/resources/extensions/remote-questions/discord-adapter.js +0 -118
  491. package/dist/resources/extensions/remote-questions/format.d.ts +0 -38
  492. package/dist/resources/extensions/remote-questions/format.js +0 -119
  493. package/dist/resources/extensions/remote-questions/manager.d.ts +0 -23
  494. package/dist/resources/extensions/remote-questions/manager.js +0 -161
  495. package/dist/resources/extensions/remote-questions/remote-command.d.ts +0 -6
  496. package/dist/resources/extensions/remote-questions/remote-command.js +0 -358
  497. package/dist/resources/extensions/remote-questions/slack-adapter.d.ts +0 -15
  498. package/dist/resources/extensions/remote-questions/slack-adapter.js +0 -82
  499. package/dist/resources/extensions/remote-questions/status.d.ts +0 -9
  500. package/dist/resources/extensions/remote-questions/status.js +0 -25
  501. package/dist/resources/extensions/remote-questions/store.d.ts +0 -11
  502. package/dist/resources/extensions/remote-questions/store.js +0 -66
  503. package/dist/resources/extensions/remote-questions/types.d.ts +0 -68
  504. package/dist/resources/extensions/remote-questions/types.js +0 -4
@@ -0,0 +1,799 @@
1
+ #!/usr/bin/env -S uv --quiet run --active --script
2
+ # /// script
3
+ # requires-python = ">=3.11"
4
+ # dependencies = [
5
+ # "typer>=0.21.0",
6
+ # "PyGithub>=2.1.1",
7
+ # ]
8
+ # ///
9
+ """GitHub Project Setup — multi-step project management automation.
10
+
11
+ Orchestrates: label creation, milestone management, project setup,
12
+ Projects V2 status updates, and backlog item issue import using the
13
+ PyGithub native library. Projects V2 GraphQL mutations use the gh CLI.
14
+
15
+ Authentication: reads GITHUB_TOKEN from environment.
16
+
17
+ Usage:
18
+ github_project_setup.py setup --repo OWNER/REPO [--project-title TITLE]
19
+ github_project_setup.py labels --repo OWNER/REPO [--force]
20
+ github_project_setup.py milestone create --repo OWNER/REPO --title TITLE [--due YYYY-MM-DD]
21
+ github_project_setup.py milestone list --repo OWNER/REPO
22
+ github_project_setup.py milestone start --repo OWNER/REPO --number N [--dry-run]
23
+ github_project_setup.py milestone close --repo OWNER/REPO --number N [--dry-run]
24
+ github_project_setup.py issue create --repo OWNER/REPO --title TITLE [options]
25
+ github_project_setup.py issue list --repo OWNER/REPO [--priority p1] [--state open]
26
+ github_project_setup.py project update-status --project-number N --issue-number N --status STATUS
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import json
32
+ import os
33
+ import shutil
34
+ import subprocess
35
+ from datetime import UTC, datetime
36
+ from typing import TYPE_CHECKING, Annotated
37
+
38
+ import typer
39
+ from github import Auth, Github, GithubException
40
+
41
+ if TYPE_CHECKING:
42
+ from github.Issue import Issue
43
+ from github.Label import Label
44
+ from github.Milestone import Milestone
45
+ from github.Repository import Repository
46
+
47
+ app = typer.Typer(help="GitHub Project management automation via PyGithub")
48
+ milestone_app = typer.Typer(help="Milestone operations")
49
+ issue_app = typer.Typer(help="Issue operations")
50
+ project_app = typer.Typer(help="GitHub Projects V2 operations")
51
+ app.add_typer(milestone_app, name="milestone")
52
+ app.add_typer(issue_app, name="issue")
53
+ app.add_typer(project_app, name="project")
54
+
55
+ DEFAULT_REPO = "Jamie-BitFlight/claude_skills"
56
+
57
+ # Standard label taxonomy
58
+ LABELS: list[dict[str, str]] = [
59
+ # Priority
60
+ {"name": "priority:p0", "color": "D73A4A", "description": "Critical — blocks work or production"},
61
+ {"name": "priority:p1", "color": "E99695", "description": "High — should be done next"},
62
+ {"name": "priority:p2", "color": "F9D0C4", "description": "Medium — do when P0/P1 are clear"},
63
+ {"name": "priority:idea", "color": "BFD4F2", "description": "Unscoped — future consideration"},
64
+ # Type
65
+ {"name": "type:feature", "color": "0E8A16", "description": "New capability or skill"},
66
+ {"name": "type:bug", "color": "B60205", "description": "Something is broken"},
67
+ {"name": "type:refactor", "color": "5319E7", "description": "Internal improvement, no behavior change"},
68
+ {"name": "type:docs", "color": "0075CA", "description": "Documentation only"},
69
+ {"name": "type:chore", "color": "EDEDED", "description": "Maintenance, tooling, CI"},
70
+ # Status (all 8 state-machine states + legacy needs-review)
71
+ {"name": "status:needs-grooming", "color": "FEF2C0", "description": "Captured but not yet groomed"},
72
+ {"name": "status:groomed", "color": "C2E0C6", "description": "Grooming complete, RT-ICA APPROVED"},
73
+ {"name": "status:blocked", "color": "B60205", "description": "RT-ICA BLOCKED or AC verification FAIL"},
74
+ {"name": "status:in-milestone", "color": "BFD4F2", "description": "Assigned to an active milestone"},
75
+ {"name": "status:in-progress", "color": "1D76DB", "description": "Actively being worked on"},
76
+ {"name": "status:done", "color": "0E8A16", "description": "Implementation complete, AC verified PASS"},
77
+ {"name": "status:resolved", "color": "6B737B", "description": "Closed without full implementation"},
78
+ {"name": "status:closed", "color": "EDEDED", "description": "Terminal — milestone archived"},
79
+ # Legacy label retained for backwards compatibility — not part of state machine
80
+ {"name": "status:needs-review", "color": "D876E3", "description": "Implementation done, needs review"},
81
+ ]
82
+
83
+ PRIORITY_LABEL_MAP = {"P0": "priority:p0", "P1": "priority:p1", "P2": "priority:p2", "IDEAS": "priority:idea"}
84
+
85
+ VALID_STATUSES = ("Backlog", "Grooming", "In Progress", "In Review", "Done")
86
+
87
+ # Label-to-status mapping for milestone transitions
88
+ _LABEL_TO_PROJECT_STATUS = {
89
+ "status:in-progress": "In Progress",
90
+ "status:done": "Done",
91
+ "status:needs-grooming": "Grooming",
92
+ "status:needs-review": "In Review",
93
+ "status:blocked": "Backlog",
94
+ }
95
+
96
+
97
+ def get_github() -> Github:
98
+ """Return an authenticated Github client from GITHUB_TOKEN."""
99
+ token = os.environ.get("GITHUB_TOKEN")
100
+ if not token:
101
+ typer.echo("ERROR: GITHUB_TOKEN environment variable not set", err=True)
102
+ raise typer.Exit(1)
103
+ return Github(auth=Auth.Token(token))
104
+
105
+
106
+ def get_repo(gh: Github, repo_slug: str) -> Repository:
107
+ """Return a Repository object, exit on failure.
108
+
109
+ Args:
110
+ gh: Authenticated Github client.
111
+ repo_slug: Repository identifier in ``owner/repo`` format.
112
+
113
+ Returns:
114
+ Repository object for the given slug.
115
+ """
116
+ try:
117
+ return gh.get_repo(repo_slug)
118
+ except GithubException as exc:
119
+ typer.echo(f"ERROR: Cannot access repo '{repo_slug}': {exc}", err=True)
120
+ raise typer.Exit(1) from exc
121
+
122
+
123
+ @app.command()
124
+ def labels(
125
+ repo: Annotated[str, typer.Option("--repo", "-R")] = DEFAULT_REPO,
126
+ force: Annotated[bool, typer.Option("--force")] = False,
127
+ ) -> None:
128
+ """Create standard label taxonomy. Skips labels that already exist unless --force."""
129
+ gh = get_github()
130
+ repository = get_repo(gh, repo)
131
+
132
+ existing = {lbl.name: lbl for lbl in repository.get_labels()}
133
+ created = updated = skipped = 0
134
+
135
+ for spec in LABELS:
136
+ name = spec["name"]
137
+ if name in existing:
138
+ if force:
139
+ existing[name].edit(name=name, color=spec["color"], description=spec["description"])
140
+ typer.echo(f" updated: {name}")
141
+ updated += 1
142
+ else:
143
+ typer.echo(f" exists: {name} (--force to update)")
144
+ skipped += 1
145
+ else:
146
+ repository.create_label(name=name, color=spec["color"], description=spec["description"])
147
+ typer.echo(f" created: {name}")
148
+ created += 1
149
+
150
+ typer.echo(f"\nLabels: {created} created, {updated} updated, {skipped} skipped")
151
+
152
+
153
+ @milestone_app.command("create")
154
+ def milestone_create(
155
+ title: Annotated[str, typer.Option("--title")],
156
+ repo: Annotated[str, typer.Option("--repo", "-R")] = DEFAULT_REPO,
157
+ description: Annotated[str, typer.Option("--description")] = "",
158
+ due: Annotated[str | None, typer.Option("--due", help="Due date YYYY-MM-DD")] = None,
159
+ ) -> None:
160
+ """Create a milestone."""
161
+ gh = get_github()
162
+ repository = get_repo(gh, repo)
163
+
164
+ due_dt = datetime.strptime(due, "%Y-%m-%d").replace(tzinfo=UTC) if due else None
165
+ if due_dt is not None and description:
166
+ milestone = repository.create_milestone(title=title, description=description, due_on=due_dt)
167
+ elif due_dt is not None:
168
+ milestone = repository.create_milestone(title=title, due_on=due_dt)
169
+ elif description:
170
+ milestone = repository.create_milestone(title=title, description=description)
171
+ else:
172
+ milestone = repository.create_milestone(title=title)
173
+ typer.echo(f"Created milestone #{milestone.number}: {milestone.title}")
174
+ typer.echo(f" URL: {milestone.html_url}")
175
+
176
+
177
+ @milestone_app.command("list")
178
+ def milestone_list(repo: Annotated[str, typer.Option("--repo", "-R")] = DEFAULT_REPO) -> None:
179
+ """List all open milestones."""
180
+ gh = get_github()
181
+ repository = get_repo(gh, repo)
182
+
183
+ milestones = list(repository.get_milestones(state="all"))
184
+ if not milestones:
185
+ typer.echo("No milestones.")
186
+ return
187
+ for m in milestones:
188
+ due = m.due_on.strftime("%Y-%m-%d") if m.due_on else "no due date"
189
+ typer.echo(
190
+ f" #{m.number:3d} [{m.state}] {m.title} ({m.open_issues} open, {m.closed_issues} closed) due: {due}"
191
+ )
192
+
193
+
194
+ @milestone_app.command("start")
195
+ def milestone_start(
196
+ number: Annotated[int, typer.Option("--number", "-n", help="Milestone number")],
197
+ repo: Annotated[str, typer.Option("--repo", "-R")] = DEFAULT_REPO,
198
+ dry_run: Annotated[bool, typer.Option("--dry-run")] = False,
199
+ project_number: Annotated[int, typer.Option("--project-number", "-p", help="Projects V2 number")] = 0,
200
+ owner: Annotated[str, typer.Option("--owner", help="GitHub owner for Projects V2")] = "Jamie-BitFlight",
201
+ ) -> None:
202
+ """Transition open milestone issues from status:needs-grooming to status:in-progress.
203
+
204
+ When --project-number is set, also updates the Projects V2 Status field
205
+ to "In Progress" for each transitioned issue.
206
+ """
207
+ gh = get_github()
208
+ repository = get_repo(gh, repo)
209
+ milestone = _get_open_milestone(repository, number)
210
+
211
+ if milestone.open_issues == 0:
212
+ typer.echo(
213
+ f"WARNING: Milestone #{number} '{milestone.title}' has no open issues. "
214
+ "Add items first with /group-items-to-milestone."
215
+ )
216
+ raise typer.Exit(0)
217
+
218
+ open_issues = list(repository.get_issues(milestone=milestone, state="open"))
219
+ typer.echo(f"Milestone #{milestone.number}: {milestone.title}")
220
+ typer.echo(f" {milestone.open_issues} open issue(s) — transitioning labels:\n")
221
+
222
+ for issue in open_issues:
223
+ label_names = [lbl.name for lbl in issue.labels]
224
+ typer.echo(f" #{issue.number:4d} {issue.title[:60]:<60} [{', '.join(label_names)}]")
225
+
226
+ if dry_run:
227
+ typer.echo("\n[dry-run] No changes made.")
228
+ if project_number:
229
+ typer.echo("\nProjects V2 status updates (dry-run):")
230
+ _bulk_update_project_status(owner, project_number, open_issues, "In Progress", dry_run=True)
231
+ return
232
+
233
+ in_progress_label = _ensure_label(repository, "status:in-progress", "1D76DB", "Actively being worked on")
234
+ succeeded, skipped, failed = _transition_issues(open_issues, in_progress_label)
235
+
236
+ # Update Projects V2 Status if project specified
237
+ v2_succeeded = v2_failed = 0
238
+ if project_number:
239
+ typer.echo("\nUpdating Projects V2 Status → In Progress:")
240
+ v2_succeeded, v2_failed = _bulk_update_project_status(owner, project_number, open_issues, "In Progress")
241
+
242
+ typer.echo(
243
+ f"\nMilestone #{milestone.number} '{milestone.title}' started.\n"
244
+ f" {succeeded} transitioned, {skipped} already in-progress, {failed} failed."
245
+ )
246
+ if project_number:
247
+ typer.echo(f" Projects V2: {v2_succeeded} updated, {v2_failed} failed.")
248
+ typer.echo(
249
+ f"\nWork on individual items:\n"
250
+ f" /work-backlog-item {{title}}\n"
251
+ f"\nTrack progress:\n"
252
+ f" uv run .claude/skills/gh/scripts/github_project_setup.py issue list "
253
+ f"--repo {repo}"
254
+ )
255
+ if failed:
256
+ raise typer.Exit(1)
257
+
258
+
259
+ @milestone_app.command("close")
260
+ def milestone_close(
261
+ number: Annotated[int, typer.Option("--number", "-n", help="Milestone number")],
262
+ repo: Annotated[str, typer.Option("--repo", "-R")] = DEFAULT_REPO,
263
+ dry_run: Annotated[bool, typer.Option("--dry-run")] = False,
264
+ project_number: Annotated[int, typer.Option("--project-number", "-p", help="Projects V2 number")] = 0,
265
+ owner: Annotated[str, typer.Option("--owner", help="GitHub owner for Projects V2")] = "Jamie-BitFlight",
266
+ ) -> None:
267
+ """Close a milestone: transition open issues to status:done and close the milestone.
268
+
269
+ When --project-number is set, also updates the Projects V2 Status field
270
+ to "Done" for each issue in the milestone (both open and already-closed).
271
+ """
272
+ gh = get_github()
273
+ repository = get_repo(gh, repo)
274
+ milestone = _get_open_milestone(repository, number)
275
+
276
+ open_issues = list(repository.get_issues(milestone=milestone, state="open"))
277
+ closed_issues = list(repository.get_issues(milestone=milestone, state="closed"))
278
+ total = len(open_issues) + len(closed_issues)
279
+
280
+ typer.echo(f"Milestone #{milestone.number}: {milestone.title}")
281
+ typer.echo(f" {len(closed_issues)} closed, {len(open_issues)} open\n")
282
+
283
+ if open_issues:
284
+ typer.echo("Open issues (will be transitioned to status:done):")
285
+ for issue in open_issues:
286
+ label_names = [lbl.name for lbl in issue.labels]
287
+ typer.echo(f" #{issue.number:4d} {issue.title[:60]:<60} [{', '.join(label_names)}]")
288
+ typer.echo()
289
+
290
+ if dry_run:
291
+ typer.echo("[dry-run] No changes made.")
292
+ if project_number:
293
+ all_issues = open_issues + closed_issues
294
+ typer.echo("\nProjects V2 status updates (dry-run):")
295
+ _bulk_update_project_status(owner, project_number, all_issues, "Done", dry_run=True)
296
+ return
297
+
298
+ succeeded = skipped = failed = 0
299
+ if open_issues:
300
+ done_label = _ensure_label(repository, "status:done", "0E8A16", "Work complete, milestone closing")
301
+ succeeded, skipped, failed = _transition_to_done(open_issues, done_label)
302
+
303
+ # Close the milestone
304
+ milestone.edit(title=milestone.title, state="closed")
305
+ typer.echo(f"\nMilestone #{milestone.number} '{milestone.title}' closed.")
306
+ if open_issues:
307
+ typer.echo(f" {succeeded} transitioned to status:done, {skipped} already done, {failed} failed.")
308
+ typer.echo(f" {len(closed_issues)}/{total} issues were closed before milestone close.")
309
+
310
+ # Update Projects V2 Status for all issues in milestone
311
+ v2_succeeded = v2_failed = 0
312
+ if project_number:
313
+ all_issues = open_issues + closed_issues
314
+ typer.echo("\nUpdating Projects V2 Status → Done:")
315
+ v2_succeeded, v2_failed = _bulk_update_project_status(owner, project_number, all_issues, "Done")
316
+ typer.echo(f" Projects V2: {v2_succeeded} updated, {v2_failed} failed.")
317
+
318
+ if failed:
319
+ raise typer.Exit(1)
320
+
321
+
322
+ def _transition_to_done(open_issues: list[Issue], done_label: Label) -> tuple[int, int, int]:
323
+ """Apply status:done label to each open issue.
324
+
325
+ Returns:
326
+ Tuple of (succeeded, skipped, failed) counts.
327
+ """
328
+ status_labels_to_remove = {"status:in-progress", "status:needs-grooming"}
329
+ succeeded = failed = skipped = 0
330
+ typer.echo()
331
+ for issue in open_issues:
332
+ label_names = [lbl.name for lbl in issue.labels]
333
+ if "status:done" in label_names:
334
+ typer.echo(f" #{issue.number} already has status:done — skipped")
335
+ skipped += 1
336
+ continue
337
+ try:
338
+ new_label_names = [lbl.name for lbl in issue.labels if lbl.name not in status_labels_to_remove]
339
+ new_label_names.append(done_label.name)
340
+ issue.edit(labels=new_label_names)
341
+ typer.echo(f" #{issue.number} {issue.title[:60]} → status:done")
342
+ succeeded += 1
343
+ except GithubException as exc:
344
+ typer.echo(f" #{issue.number} FAILED: {exc}", err=True)
345
+ failed += 1
346
+ return succeeded, skipped, failed
347
+
348
+
349
+ def _get_open_milestone(repository: Repository, number: int) -> Milestone:
350
+ """Fetch a milestone and verify it is open.
351
+
352
+ Args:
353
+ repository: GitHub repository object.
354
+ number: Milestone number.
355
+
356
+ Returns:
357
+ The Milestone object.
358
+
359
+ Raises:
360
+ typer.Exit: If the milestone is not found or already closed.
361
+ """
362
+ try:
363
+ milestone = repository.get_milestone(number)
364
+ except GithubException as exc:
365
+ typer.echo(f"ERROR: Milestone #{number} not found.", err=True)
366
+ open_milestones = list(repository.get_milestones(state="open"))
367
+ if open_milestones:
368
+ typer.echo("Open milestones:", err=True)
369
+ for m in open_milestones:
370
+ typer.echo(f" #{m.number} {m.title}", err=True)
371
+ raise typer.Exit(1) from exc
372
+
373
+ if milestone.state == "closed":
374
+ typer.echo(f"ERROR: Milestone #{number} '{milestone.title}' is already closed.", err=True)
375
+ raise typer.Exit(1)
376
+
377
+ return milestone
378
+
379
+
380
+ def _find_gh_cli() -> str:
381
+ """Locate the gh CLI binary.
382
+
383
+ Returns:
384
+ Path to gh binary.
385
+
386
+ Raises:
387
+ typer.Exit: If gh is not found on PATH.
388
+ """
389
+ gh_path = shutil.which("gh")
390
+ if not gh_path:
391
+ typer.echo("ERROR: gh CLI not found. Install gh via your system package manager (brew/winget/apt).", err=True)
392
+ raise typer.Exit(1)
393
+ return gh_path
394
+
395
+
396
+ def _gh_graphql(query: str) -> dict:
397
+ """Execute a GraphQL query via the gh CLI.
398
+
399
+ Args:
400
+ query: GraphQL query string.
401
+
402
+ Returns:
403
+ Parsed JSON response from the GitHub GraphQL API.
404
+
405
+ Raises:
406
+ typer.Exit: If the gh CLI call fails.
407
+ """
408
+ gh_path = _find_gh_cli()
409
+ try:
410
+ result = subprocess.run(
411
+ [gh_path, "api", "graphql", "-f", f"query={query}"], capture_output=True, text=True, check=True
412
+ )
413
+ except subprocess.CalledProcessError as exc:
414
+ typer.echo(f"ERROR: GraphQL query failed: {exc.stderr or exc}", err=True)
415
+ raise typer.Exit(1) from exc
416
+ return json.loads(result.stdout)
417
+
418
+
419
+ def _discover_project_fields(owner: str, project_number: int) -> tuple[str, str, dict[str, str]]:
420
+ """Discover project ID, Status field ID, and option IDs via GraphQL.
421
+
422
+ Args:
423
+ owner: GitHub user or organization login.
424
+ project_number: The project number (visible in the URL).
425
+
426
+ Returns:
427
+ Tuple of (project_id, status_field_id, option_map) where
428
+ option_map maps status name to option ID.
429
+
430
+ Raises:
431
+ typer.Exit: If project or Status field not found.
432
+ """
433
+ query = (
434
+ '{ user(login: "' + owner + '") { projectV2(number: ' + str(project_number) + ") { id fields(first: 30) {"
435
+ " nodes { ... on ProjectV2SingleSelectField {"
436
+ " id name options { id name } } } } } } }"
437
+ )
438
+ resp = _gh_graphql(query)
439
+
440
+ project = resp.get("data", {}).get("user", {}).get("projectV2")
441
+ if not project:
442
+ typer.echo(f"ERROR: Project #{project_number} not found for user '{owner}'.", err=True)
443
+ raise typer.Exit(1)
444
+
445
+ project_id = project["id"]
446
+ for field in project["fields"]["nodes"]:
447
+ if field.get("name") == "Status":
448
+ field_id = field["id"]
449
+ option_map = {opt["name"]: opt["id"] for opt in field["options"]}
450
+ return project_id, field_id, option_map
451
+
452
+ typer.echo(f"ERROR: No 'Status' field found in project #{project_number}.", err=True)
453
+ raise typer.Exit(1)
454
+
455
+
456
+ def _find_project_item_id(project_id: str, issue_node_id: str) -> str:
457
+ """Find or create the project item for a given issue.
458
+
459
+ Adds the issue to the project if not already present. The
460
+ ``addProjectV2ItemById`` mutation is idempotent — it returns the
461
+ existing item if the issue is already on the board.
462
+
463
+ Args:
464
+ project_id: GraphQL node ID of the project.
465
+ issue_node_id: GraphQL node ID of the issue.
466
+
467
+ Returns:
468
+ The project item ID.
469
+
470
+ Raises:
471
+ typer.Exit: If the mutation fails to return an item ID.
472
+ """
473
+ query = (
474
+ "mutation { addProjectV2ItemById(input: {"
475
+ f'projectId: "{project_id}", contentId: "{issue_node_id}"'
476
+ "}) { item { id } } }"
477
+ )
478
+ resp = _gh_graphql(query)
479
+ item_id = resp.get("data", {}).get("addProjectV2ItemById", {}).get("item", {}).get("id")
480
+ if not item_id:
481
+ typer.echo("ERROR: Failed to add issue to project.", err=True)
482
+ raise typer.Exit(1)
483
+ return item_id
484
+
485
+
486
+ def _set_project_field(project_id: str, item_id: str, field_id: str, option_id: str) -> None:
487
+ """Set a single-select field value on a project item.
488
+
489
+ Args:
490
+ project_id: GraphQL node ID of the project.
491
+ item_id: GraphQL node ID of the project item.
492
+ field_id: GraphQL node ID of the field.
493
+ option_id: GraphQL node ID of the option to set.
494
+ """
495
+ query = (
496
+ "mutation { updateProjectV2ItemFieldValue(input: {"
497
+ f'projectId: "{project_id}", itemId: "{item_id}", '
498
+ f'fieldId: "{field_id}", '
499
+ f'value: {{singleSelectOptionId: "{option_id}"}}'
500
+ "}) { projectV2Item { id } } }"
501
+ )
502
+ _gh_graphql(query)
503
+
504
+
505
+ def _update_project_status(
506
+ owner: str, project_number: int, issue_node_id: str, status: str, *, dry_run: bool = False, issue_label: str = ""
507
+ ) -> bool:
508
+ """Update the Projects V2 Status field for a single issue.
509
+
510
+ This is the shared implementation used by both the ``project update-status``
511
+ command and the milestone start/close integration.
512
+
513
+ Args:
514
+ owner: GitHub user or organization login.
515
+ project_number: The project number.
516
+ issue_node_id: GraphQL node ID of the issue.
517
+ status: Target status value (must be in VALID_STATUSES).
518
+ dry_run: If True, report what would happen without mutating.
519
+ issue_label: Optional label for dry-run output (e.g. "#42 title").
520
+
521
+ Returns:
522
+ True if the update succeeded (or would succeed in dry-run).
523
+ """
524
+ if status not in VALID_STATUSES:
525
+ typer.echo(f"ERROR: Invalid status '{status}'. Valid values: {', '.join(VALID_STATUSES)}", err=True)
526
+ return False
527
+
528
+ project_id, field_id, option_map = _discover_project_fields(owner, project_number)
529
+
530
+ option_id = option_map.get(status)
531
+ if not option_id:
532
+ typer.echo(
533
+ f"ERROR: Status '{status}' not found in project options. Available: {', '.join(option_map)}", err=True
534
+ )
535
+ return False
536
+
537
+ if dry_run:
538
+ label = issue_label or issue_node_id
539
+ typer.echo(f" [dry-run] Would set {label} → Status: {status}")
540
+ return True
541
+
542
+ item_id = _find_project_item_id(project_id, issue_node_id)
543
+ _set_project_field(project_id, item_id, field_id, option_id)
544
+
545
+ label = issue_label or issue_node_id
546
+ typer.echo(f" {label} → Status: {status}")
547
+ return True
548
+
549
+
550
+ def _bulk_update_project_status(
551
+ owner: str, project_number: int, issues: list[Issue], status: str, *, dry_run: bool = False
552
+ ) -> tuple[int, int]:
553
+ """Update Projects V2 Status for multiple issues.
554
+
555
+ Discovers project fields once and reuses for all issues.
556
+
557
+ Args:
558
+ owner: GitHub user or organization login.
559
+ project_number: The project number.
560
+ issues: List of PyGithub Issue objects.
561
+ status: Target status value.
562
+ dry_run: If True, report without mutating.
563
+
564
+ Returns:
565
+ Tuple of (succeeded, failed) counts.
566
+ """
567
+ if status not in VALID_STATUSES:
568
+ typer.echo(f"ERROR: Invalid status '{status}'. Valid values: {', '.join(VALID_STATUSES)}", err=True)
569
+ return 0, len(issues)
570
+
571
+ project_id, field_id, option_map = _discover_project_fields(owner, project_number)
572
+ option_id = option_map.get(status)
573
+ if not option_id:
574
+ typer.echo(
575
+ f"ERROR: Status '{status}' not found in project options. Available: {', '.join(option_map)}", err=True
576
+ )
577
+ return 0, len(issues)
578
+
579
+ succeeded = failed = 0
580
+ for issue in issues:
581
+ label = f"#{issue.number} {issue.title[:50]}"
582
+ if dry_run:
583
+ typer.echo(f" [dry-run] Would set {label} → Status: {status}")
584
+ succeeded += 1
585
+ continue
586
+ try:
587
+ item_id = _find_project_item_id(project_id, issue.node_id)
588
+ _set_project_field(project_id, item_id, field_id, option_id)
589
+ typer.echo(f" {label} → Status: {status}")
590
+ succeeded += 1
591
+ except (typer.Exit, subprocess.CalledProcessError) as exc:
592
+ typer.echo(f" {label} FAILED: {exc}", err=True)
593
+ failed += 1
594
+ return succeeded, failed
595
+
596
+
597
+ def _ensure_label(repository: Repository, name: str, color: str, description: str) -> Label:
598
+ """Return the label, creating it if it does not exist.
599
+
600
+ Args:
601
+ repository: GitHub repository object.
602
+ name: Label name to find or create.
603
+ color: Hex color code for the label (without ``#`` prefix).
604
+ description: Human-readable label description.
605
+
606
+ Returns:
607
+ The existing or newly created Label object.
608
+ """
609
+ try:
610
+ return repository.get_label(name)
611
+ except GithubException:
612
+ label = repository.create_label(name=name, color=color, description=description)
613
+ typer.echo(f"\n Created label: {name}")
614
+ return label
615
+
616
+
617
+ def _transition_issues(open_issues: list[Issue], in_progress_label: Label) -> tuple[int, int, int]:
618
+ """Apply label transition from ``status:needs-grooming`` to ``status:in-progress``.
619
+
620
+ Args:
621
+ open_issues: List of open Issue objects to transition.
622
+ in_progress_label: The ``status:in-progress`` Label to apply.
623
+
624
+ Returns:
625
+ Tuple of (succeeded, skipped, failed) counts.
626
+ """
627
+ succeeded = failed = skipped = 0
628
+ typer.echo()
629
+ for issue in open_issues:
630
+ label_names = [lbl.name for lbl in issue.labels]
631
+ if "status:in-progress" in label_names:
632
+ typer.echo(f" #{issue.number} already has status:in-progress — skipped")
633
+ skipped += 1
634
+ continue
635
+ try:
636
+ new_label_names = [lbl.name for lbl in issue.labels if lbl.name != "status:needs-grooming"]
637
+ new_label_names.append(in_progress_label.name)
638
+ issue.edit(labels=new_label_names)
639
+ typer.echo(f" #{issue.number} {issue.title[:60]} → status:in-progress")
640
+ succeeded += 1
641
+ except GithubException as exc:
642
+ typer.echo(f" #{issue.number} FAILED: {exc}", err=True)
643
+ failed += 1
644
+ return succeeded, skipped, failed
645
+
646
+
647
+ @project_app.command("update-status")
648
+ def project_update_status(
649
+ issue_number: Annotated[int, typer.Option("--issue-number", "-i", help="GitHub issue number")],
650
+ status: Annotated[str, typer.Option("--status", "-s", help="Target status value")],
651
+ project_number: Annotated[int, typer.Option("--project-number", "-p", help="Project number")] = 1,
652
+ owner: Annotated[str, typer.Option("--owner")] = "Jamie-BitFlight",
653
+ repo: Annotated[str, typer.Option("--repo", "-R")] = DEFAULT_REPO,
654
+ dry_run: Annotated[bool, typer.Option("--dry-run")] = False,
655
+ ) -> None:
656
+ """Set the Projects V2 Status field for a GitHub issue.
657
+
658
+ Discovers field IDs dynamically via GraphQL, then updates the Status
659
+ single-select field. The issue is added to the project if not already present.
660
+
661
+ Valid statuses: Backlog, Grooming, In Progress, In Review, Done
662
+ """
663
+ if status not in VALID_STATUSES:
664
+ typer.echo(f"ERROR: Invalid status '{status}'. Valid values: {', '.join(VALID_STATUSES)}", err=True)
665
+ raise typer.Exit(1)
666
+
667
+ gh = get_github()
668
+ repository = get_repo(gh, repo)
669
+
670
+ try:
671
+ issue = repository.get_issue(issue_number)
672
+ except GithubException as exc:
673
+ typer.echo(f"ERROR: Issue #{issue_number} not found: {exc}", err=True)
674
+ raise typer.Exit(1) from exc
675
+
676
+ typer.echo(f"Issue #{issue.number}: {issue.title}")
677
+ typer.echo(f" Project: {owner}/projects/{project_number}")
678
+ typer.echo(f" Target status: {status}")
679
+
680
+ ok = _update_project_status(
681
+ owner=owner,
682
+ project_number=project_number,
683
+ issue_node_id=issue.raw_data["node_id"],
684
+ status=status,
685
+ dry_run=dry_run,
686
+ issue_label=f"#{issue.number} {issue.title[:50]}",
687
+ )
688
+ if not ok:
689
+ raise typer.Exit(1)
690
+ if not dry_run:
691
+ typer.echo(" Done.")
692
+
693
+
694
+ @issue_app.command("create")
695
+ def issue_create(
696
+ repo: Annotated[str, typer.Option("--repo", "-R")] = DEFAULT_REPO,
697
+ title: Annotated[str, typer.Option("--title")] = "",
698
+ body: Annotated[str, typer.Option("--body")] = "",
699
+ priority_label: Annotated[str, typer.Option("--priority-label")] = "",
700
+ type_label: Annotated[str, typer.Option("--type-label")] = "",
701
+ milestone_number: Annotated[int, typer.Option("--milestone")] = 0,
702
+ ) -> None:
703
+ """Create a GitHub issue with priority/type labels and optional milestone."""
704
+ if not title:
705
+ typer.echo("ERROR: --title is required", err=True)
706
+ raise typer.Exit(1)
707
+
708
+ gh = get_github()
709
+ repository = get_repo(gh, repo)
710
+
711
+ label_names = ["status:needs-grooming"]
712
+ if priority_label:
713
+ label_names.append(priority_label)
714
+ if type_label:
715
+ label_names.append(type_label)
716
+
717
+ label_objects = []
718
+ for lbl_name in label_names:
719
+ try:
720
+ label_objects.append(repository.get_label(lbl_name))
721
+ except GithubException:
722
+ typer.echo(f" WARNING: label '{lbl_name}' not found — skipping", err=True)
723
+
724
+ milestone_obj = None
725
+ if milestone_number:
726
+ try:
727
+ milestone_obj = repository.get_milestone(milestone_number)
728
+ except GithubException:
729
+ typer.echo(f" WARNING: milestone #{milestone_number} not found — skipping", err=True)
730
+
731
+ if milestone_obj is not None:
732
+ issue = repository.create_issue(title=title, body=body or "", labels=label_objects, milestone=milestone_obj)
733
+ else:
734
+ issue = repository.create_issue(title=title, body=body or "", labels=label_objects)
735
+ typer.echo(f"Created issue #{issue.number}: {issue.title}")
736
+ typer.echo(f" URL: {issue.html_url}")
737
+
738
+
739
+ @issue_app.command("list")
740
+ def issue_list(
741
+ repo: Annotated[str, typer.Option("--repo", "-R")] = DEFAULT_REPO,
742
+ priority: Annotated[str, typer.Option("--priority")] = "",
743
+ state: Annotated[str, typer.Option("--state")] = "open",
744
+ ) -> None:
745
+ """List issues, optionally filtered by priority."""
746
+ gh = get_github()
747
+ repository = get_repo(gh, repo)
748
+
749
+ kwargs: dict = {"state": state}
750
+ if priority:
751
+ label_name = PRIORITY_LABEL_MAP.get(priority.upper(), f"priority:{priority.lower()}")
752
+ try:
753
+ kwargs["labels"] = [repository.get_label(label_name)]
754
+ except GithubException:
755
+ typer.echo(f"Label '{label_name}' not found", err=True)
756
+
757
+ issues = list(repository.get_issues(**kwargs))
758
+ if not issues:
759
+ typer.echo("No issues found.")
760
+ return
761
+ for issue in issues:
762
+ milestone_title = issue.milestone.title if issue.milestone else "—"
763
+ label_names = ", ".join(lbl.name for lbl in issue.labels)
764
+ typer.echo(f" #{issue.number:4d} {issue.title[:55]:<55} [{label_names}] {milestone_title}")
765
+
766
+
767
+ @app.command()
768
+ def setup(
769
+ repo: Annotated[str, typer.Option("--repo", "-R")] = DEFAULT_REPO,
770
+ project_title: Annotated[str, typer.Option("--project-title")] = "claude_skills Backlog",
771
+ ) -> None:
772
+ """Full project setup: create label taxonomy and report next steps."""
773
+ typer.echo(f"Setting up GitHub project for {repo}...")
774
+ typer.echo("\n1. Creating label taxonomy...")
775
+
776
+ gh = get_github()
777
+ repository = get_repo(gh, repo)
778
+
779
+ existing = {lbl.name: lbl for lbl in repository.get_labels()}
780
+ created = skipped = 0
781
+ for spec in LABELS:
782
+ if spec["name"] not in existing:
783
+ repository.create_label(name=spec["name"], color=spec["color"], description=spec["description"])
784
+ typer.echo(f" created: {spec['name']}")
785
+ created += 1
786
+ else:
787
+ skipped += 1
788
+
789
+ typer.echo(f" Labels: {created} created, {skipped} already existed")
790
+
791
+ typer.echo(f"\n2. Project '{project_title}' — create via gh CLI:")
792
+ typer.echo(f' gh project create --owner {repo.split("/")[0]} --title "{project_title}"')
793
+ typer.echo("\nNote: GitHub Projects V2 requires project OAuth scope.")
794
+ typer.echo(" Use gh project commands or the GraphQL API for project creation.")
795
+ typer.echo(" See .claude/skills/gh/references/projects-v2.md for field setup commands.")
796
+
797
+
798
+ if __name__ == "__main__":
799
+ app()