agent-toolkit-cli 1.11.0__py3-none-win_amd64.whl

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 (438) hide show
  1. agent_toolkit/__init__.py +4 -0
  2. agent_toolkit/__main__.py +5 -0
  3. agent_toolkit/_paths.py +145 -0
  4. agent_toolkit/bin/.gitkeep +0 -0
  5. agent_toolkit/bin/agent-toolkit.exe +0 -0
  6. agent_toolkit/cli/__init__.py +0 -0
  7. agent_toolkit/cli/__main__.py +5 -0
  8. agent_toolkit/cli/build.py +278 -0
  9. agent_toolkit/cli/completion.py +297 -0
  10. agent_toolkit/cli/context_budget.py +455 -0
  11. agent_toolkit/cli/devcompanion.py +923 -0
  12. agent_toolkit/cli/devcompanion_queue.py +235 -0
  13. agent_toolkit/cli/diff.py +176 -0
  14. agent_toolkit/cli/doctor.py +1052 -0
  15. agent_toolkit/cli/insights.py +771 -0
  16. agent_toolkit/cli/install.py +706 -0
  17. agent_toolkit/cli/main.py +171 -0
  18. agent_toolkit/cli/mcp.py +537 -0
  19. agent_toolkit/cli/memory.py +739 -0
  20. agent_toolkit/cli/plugin.py +224 -0
  21. agent_toolkit/cli/project.py +459 -0
  22. agent_toolkit/cli/release.py +178 -0
  23. agent_toolkit/cli/skills.py +461 -0
  24. agent_toolkit/cli/uninstall.py +166 -0
  25. agent_toolkit/cli/update.py +298 -0
  26. agent_toolkit/cli/workspace.py +1713 -0
  27. agent_toolkit/compiler/hook_registry.py +110 -0
  28. agent_toolkit/compiler/loader.py +338 -0
  29. agent_toolkit/compiler/mcp_registry.py +87 -0
  30. agent_toolkit/compiler/model.py +182 -0
  31. agent_toolkit/compiler/provenance.py +140 -0
  32. agent_toolkit/compiler/registry_emit.py +263 -0
  33. agent_toolkit/compiler/target_registry.py +122 -0
  34. agent_toolkit/compiler/targets/__init__.py +1 -0
  35. agent_toolkit/compiler/targets/agent_plugins.py +160 -0
  36. agent_toolkit/compiler/targets/base.py +229 -0
  37. agent_toolkit/compiler/targets/claude_code.py +241 -0
  38. agent_toolkit/compiler/targets/codex.py +261 -0
  39. agent_toolkit/compiler/targets/copilot.py +275 -0
  40. agent_toolkit/compiler/targets/cursor.py +207 -0
  41. agent_toolkit/compiler/targets/gemini_cli.py +213 -0
  42. agent_toolkit/compiler/targets/muse_code.py +113 -0
  43. agent_toolkit/compiler/targets/opencode.py +204 -0
  44. agent_toolkit/compiler/targets/pi.py +263 -0
  45. agent_toolkit/compiler/targets/windsurf.py +267 -0
  46. agent_toolkit/compiler/tool_mapping.py +83 -0
  47. agent_toolkit/data/.gitignore +4 -0
  48. agent_toolkit/data/__init__.py +1 -0
  49. agent_toolkit/data/agents/agentic-security-reviewer/AGENT.md +60 -0
  50. agent_toolkit/data/agents/architect/AGENT.md +30 -0
  51. agent_toolkit/data/agents/assistant/AGENT.md +59 -0
  52. agent_toolkit/data/agents/build-error-resolver/AGENT.md +30 -0
  53. agent_toolkit/data/agents/client-workflow-bootstrap/AGENT.md +60 -0
  54. agent_toolkit/data/agents/code-reviewer/AGENT.md +49 -0
  55. agent_toolkit/data/agents/database-reviewer/AGENT.md +44 -0
  56. agent_toolkit/data/agents/docs-lookup/AGENT.md +28 -0
  57. agent_toolkit/data/agents/e2e-runner/AGENT.md +38 -0
  58. agent_toolkit/data/agents/performance-optimizer/AGENT.md +52 -0
  59. agent_toolkit/data/agents/planner/AGENT.md +41 -0
  60. agent_toolkit/data/agents/refactor-cleaner/AGENT.md +43 -0
  61. agent_toolkit/data/agents/reference-lookup/AGENT.md +33 -0
  62. agent_toolkit/data/agents/security-reviewer/AGENT.md +47 -0
  63. agent_toolkit/data/agents/tdd-guide/AGENT.md +58 -0
  64. agent_toolkit/data/agents/tech-assistant/AGENT.md +11 -0
  65. agent_toolkit/data/agents/typescript-reviewer/AGENT.md +61 -0
  66. agent_toolkit/data/capabilities/hooks/README.md +6 -0
  67. agent_toolkit/data/capabilities/hooks/pre-commit-validate.yaml +26 -0
  68. agent_toolkit/data/capabilities/hooks/scripts/pre-commit-validate.sh +41 -0
  69. agent_toolkit/data/capabilities/hooks/scripts/session-start-context.sh +11 -0
  70. agent_toolkit/data/capabilities/hooks/session-start-context.yaml +23 -0
  71. agent_toolkit/data/capabilities/targets/registry.yaml +90 -0
  72. agent_toolkit/data/capabilities/upstream.lock +118 -0
  73. agent_toolkit/data/catalogs/agent-catalog.yaml +81 -0
  74. agent_toolkit/data/catalogs/loop-catalog.yaml +55 -0
  75. agent_toolkit/data/catalogs/skill-catalog.yaml +520 -0
  76. agent_toolkit/data/catalogs/skills-layout.json +391 -0
  77. agent_toolkit/data/distributions/products.yaml +190 -0
  78. agent_toolkit/data/distributions/targets/claude-code.yaml +47 -0
  79. agent_toolkit/data/distributions/targets/cursor.yaml +42 -0
  80. agent_toolkit/data/distributions/targets/opencode.yaml +55 -0
  81. agent_toolkit/data/loops/changelog-drafter/loop.yaml +40 -0
  82. agent_toolkit/data/loops/ci-sweeper/loop.yaml +53 -0
  83. agent_toolkit/data/loops/daily-triage/loop.yaml +47 -0
  84. agent_toolkit/data/loops/dep-sweeper/loop.yaml +46 -0
  85. agent_toolkit/data/loops/issue-triage/loop.yaml +40 -0
  86. agent_toolkit/data/loops/oss-daily-briefing/loop.yaml +65 -0
  87. agent_toolkit/data/loops/oss-pr-monitor/loop.yaml +86 -0
  88. agent_toolkit/data/loops/oss-triage/loop.yaml +67 -0
  89. agent_toolkit/data/loops/post-merge-cleanup/loop.yaml +44 -0
  90. agent_toolkit/data/loops/pr-babysitter/loop.yaml +46 -0
  91. agent_toolkit/data/mcp/registry/chrome-devtools.yaml +61 -0
  92. agent_toolkit/data/mcp/registry/clickup.yaml +48 -0
  93. agent_toolkit/data/mcp/registry/figma.yaml +44 -0
  94. agent_toolkit/data/mcp/registry/github.yaml +52 -0
  95. agent_toolkit/data/mcp/registry/linear.yaml +47 -0
  96. agent_toolkit/data/mcp/registry/notion.yaml +48 -0
  97. agent_toolkit/data/mcp/registry/slack.yaml +46 -0
  98. agent_toolkit/data/mcp/templates/chrome-devtools/README.md +57 -0
  99. agent_toolkit/data/mcp/templates/chrome-devtools/config.template.json +6 -0
  100. agent_toolkit/data/mcp/templates/clickup/README.md +45 -0
  101. agent_toolkit/data/mcp/templates/clickup/config.template.json +8 -0
  102. agent_toolkit/data/mcp/templates/clickup/wrapper.sh +23 -0
  103. agent_toolkit/data/mcp/templates/figma/README.md +54 -0
  104. agent_toolkit/data/mcp/templates/figma/config.template.json +9 -0
  105. agent_toolkit/data/mcp/templates/github/README.md +16 -0
  106. agent_toolkit/data/mcp/templates/github/config.template.json +15 -0
  107. agent_toolkit/data/mcp/templates/github/wrapper.sh +6 -0
  108. agent_toolkit/data/mcp/templates/linear/README.md +49 -0
  109. agent_toolkit/data/mcp/templates/linear/config.template.json +10 -0
  110. agent_toolkit/data/mcp/templates/notion/README.md +7 -0
  111. agent_toolkit/data/mcp/templates/notion/config.template.json +8 -0
  112. agent_toolkit/data/mcp/templates/notion/wrapper.sh +23 -0
  113. agent_toolkit/data/mcp/templates/slack/README.md +8 -0
  114. agent_toolkit/data/mcp/templates/slack/config.template.json +9 -0
  115. agent_toolkit/data/mcp/templates/slack/wrapper.sh +23 -0
  116. agent_toolkit/data/packs/README.md +66 -0
  117. agent_toolkit/data/packs/agentic-security/README.md +19 -0
  118. agent_toolkit/data/packs/agentic-security/config.yaml +25 -0
  119. agent_toolkit/data/packs/architecture/README.md +22 -0
  120. agent_toolkit/data/packs/architecture/config.yaml +27 -0
  121. agent_toolkit/data/packs/code-quality/README.md +19 -0
  122. agent_toolkit/data/packs/code-quality/config.yaml +21 -0
  123. agent_toolkit/data/packs/delivery-discipline/README.md +19 -0
  124. agent_toolkit/data/packs/delivery-discipline/config.yaml +42 -0
  125. agent_toolkit/data/packs/design-engineering/README.md +19 -0
  126. agent_toolkit/data/packs/design-engineering/config.yaml +41 -0
  127. agent_toolkit/data/packs/engineering-workflow/README.md +21 -0
  128. agent_toolkit/data/packs/engineering-workflow/config.yaml +57 -0
  129. agent_toolkit/data/packs/oss-maintenance/README.md +245 -0
  130. agent_toolkit/data/packs/oss-maintenance/config.yaml +75 -0
  131. agent_toolkit/data/profiles/claude-code/CLAUDE.md +110 -0
  132. agent_toolkit/data/profiles/claude-code/agents/assistant.md +59 -0
  133. agent_toolkit/data/profiles/claude-code/agents/build-error-resolver.md +30 -0
  134. agent_toolkit/data/profiles/claude-code/agents/client-workflow-bootstrap.md +60 -0
  135. agent_toolkit/data/profiles/claude-code/agents/contribution-planner.md +39 -0
  136. agent_toolkit/data/profiles/claude-code/agents/database-reviewer.md +44 -0
  137. agent_toolkit/data/profiles/claude-code/agents/docs-lookup.md +28 -0
  138. agent_toolkit/data/profiles/claude-code/agents/e2e-runner.md +38 -0
  139. agent_toolkit/data/profiles/claude-code/agents/performance-optimizer.md +52 -0
  140. agent_toolkit/data/profiles/claude-code/agents/planner.md +41 -0
  141. agent_toolkit/data/profiles/claude-code/agents/refactor-cleaner.md +43 -0
  142. agent_toolkit/data/profiles/claude-code/agents/reference-lookup.md +33 -0
  143. agent_toolkit/data/profiles/claude-code/agents/security-reviewer.md +47 -0
  144. agent_toolkit/data/profiles/claude-code/agents/tdd-guide.md +58 -0
  145. agent_toolkit/data/profiles/claude-code/agents/typescript-reviewer.md +61 -0
  146. agent_toolkit/data/profiles/claude-code/settings.json +86 -0
  147. agent_toolkit/data/profiles/copilot/copilot-instructions.md +15 -0
  148. agent_toolkit/data/profiles/cursor/README.md +33 -0
  149. agent_toolkit/data/profiles/cursor/rules/architect.mdc +23 -0
  150. agent_toolkit/data/profiles/cursor/rules/assistant.mdc +61 -0
  151. agent_toolkit/data/profiles/cursor/rules/build-error-resolver.mdc +20 -0
  152. agent_toolkit/data/profiles/cursor/rules/code-reviewer.mdc +21 -0
  153. agent_toolkit/data/profiles/cursor/rules/database-reviewer.mdc +29 -0
  154. agent_toolkit/data/profiles/cursor/rules/docs-lookup.mdc +15 -0
  155. agent_toolkit/data/profiles/cursor/rules/e2e-runner.mdc +30 -0
  156. agent_toolkit/data/profiles/cursor/rules/performance-optimizer.mdc +27 -0
  157. agent_toolkit/data/profiles/cursor/rules/planner.mdc +28 -0
  158. agent_toolkit/data/profiles/cursor/rules/refactor-cleaner.mdc +26 -0
  159. agent_toolkit/data/profiles/cursor/rules/reference-lookup.mdc +20 -0
  160. agent_toolkit/data/profiles/cursor/rules/security-reviewer.mdc +20 -0
  161. agent_toolkit/data/profiles/cursor/rules/tdd-guide.mdc +26 -0
  162. agent_toolkit/data/profiles/cursor/rules/typescript-reviewer.mdc +28 -0
  163. agent_toolkit/data/profiles/muse-code/README.md +35 -0
  164. agent_toolkit/data/profiles/opencode/README.md +44 -0
  165. agent_toolkit/data/profiles/opencode/agents/architect.md +30 -0
  166. agent_toolkit/data/profiles/opencode/agents/assistant.md +96 -0
  167. agent_toolkit/data/profiles/opencode/agents/build-error-resolver.md +31 -0
  168. agent_toolkit/data/profiles/opencode/agents/client-workflow-bootstrap.md +66 -0
  169. agent_toolkit/data/profiles/opencode/agents/code-reviewer.md +47 -0
  170. agent_toolkit/data/profiles/opencode/agents/contribution-planner.md +44 -0
  171. agent_toolkit/data/profiles/opencode/agents/database-reviewer.md +43 -0
  172. agent_toolkit/data/profiles/opencode/agents/docs-lookup.md +28 -0
  173. agent_toolkit/data/profiles/opencode/agents/e2e-runner.md +33 -0
  174. agent_toolkit/data/profiles/opencode/agents/performance-optimizer.md +34 -0
  175. agent_toolkit/data/profiles/opencode/agents/planner.md +27 -0
  176. agent_toolkit/data/profiles/opencode/agents/refactor-cleaner.md +27 -0
  177. agent_toolkit/data/profiles/opencode/agents/reference-lookup.md +27 -0
  178. agent_toolkit/data/profiles/opencode/agents/security-reviewer.md +27 -0
  179. agent_toolkit/data/profiles/opencode/agents/tdd-guide.md +27 -0
  180. agent_toolkit/data/profiles/opencode/agents/typescript-reviewer.md +30 -0
  181. agent_toolkit/data/profiles/opencode/opencode.json +3 -0
  182. agent_toolkit/data/profiles/pi/skills/architect/skill.md +5 -0
  183. agent_toolkit/data/profiles/pi/skills/assistant/skill.md +3 -0
  184. agent_toolkit/data/profiles/pi/skills/code-reviewer/skill.md +5 -0
  185. agent_toolkit/data/profiles/pi/skills/planner/skill.md +3 -0
  186. agent_toolkit/data/profiles/pi/skills/security-reviewer/skill.md +3 -0
  187. agent_toolkit/data/profiles/windsurf/memories/global_rules.md +114 -0
  188. agent_toolkit/data/profiles/windsurf/rules/architect.mdc +23 -0
  189. agent_toolkit/data/profiles/windsurf/rules/assistant.mdc +61 -0
  190. agent_toolkit/data/profiles/windsurf/rules/build-error-resolver.mdc +20 -0
  191. agent_toolkit/data/profiles/windsurf/rules/code-reviewer.mdc +21 -0
  192. agent_toolkit/data/profiles/windsurf/rules/database-reviewer.mdc +29 -0
  193. agent_toolkit/data/profiles/windsurf/rules/docs-lookup.mdc +15 -0
  194. agent_toolkit/data/profiles/windsurf/rules/e2e-runner.mdc +30 -0
  195. agent_toolkit/data/profiles/windsurf/rules/performance-optimizer.mdc +27 -0
  196. agent_toolkit/data/profiles/windsurf/rules/planner.mdc +28 -0
  197. agent_toolkit/data/profiles/windsurf/rules/refactor-cleaner.mdc +26 -0
  198. agent_toolkit/data/profiles/windsurf/rules/reference-lookup.mdc +20 -0
  199. agent_toolkit/data/profiles/windsurf/rules/security-reviewer.mdc +20 -0
  200. agent_toolkit/data/profiles/windsurf/rules/tdd-guide.mdc +26 -0
  201. agent_toolkit/data/profiles/windsurf/rules/typescript-reviewer.mdc +28 -0
  202. agent_toolkit/data/skills/accessibility/review/SKILL.md +116 -0
  203. agent_toolkit/data/skills/accessibility/review/references/a11y-checklist.md +25 -0
  204. agent_toolkit/data/skills/accessibility/review/references/a11y-findings-template.md +56 -0
  205. agent_toolkit/data/skills/accessibility/review/references/research.md +47 -0
  206. agent_toolkit/data/skills/agentic-security/mcp-audit/SKILL.md +116 -0
  207. agent_toolkit/data/skills/agentic-security/mcp-audit/references/mcp-audit-template.md +51 -0
  208. agent_toolkit/data/skills/agentic-security/owasp-agentic-review/SKILL.md +73 -0
  209. agent_toolkit/data/skills/agentic-security/owasp-agentic-review/references/owasp-agentic-template.md +32 -0
  210. agent_toolkit/data/skills/agentic-security/supply-chain-audit/SKILL.md +142 -0
  211. agent_toolkit/data/skills/agentic-security/threat-modeling/SKILL.md +100 -0
  212. agent_toolkit/data/skills/agentic-security/threat-modeling/references/threat-model-template.md +55 -0
  213. agent_toolkit/data/skills/architecture/c4-model/SKILL.md +49 -0
  214. agent_toolkit/data/skills/cloud/aws-well-architected-review/SKILL.md +37 -0
  215. agent_toolkit/data/skills/cloud/cloud-design-patterns/SKILL.md +38 -0
  216. agent_toolkit/data/skills/core/assistant/SKILL.md +216 -0
  217. agent_toolkit/data/skills/core/assistant/references/AGENTS_TEMPLATE.md +0 -0
  218. agent_toolkit/data/skills/core/assistant/references/ORCHESTRATION.md +0 -0
  219. agent_toolkit/data/skills/core/assistant/references/REPO_INSPECTION.md +0 -0
  220. agent_toolkit/data/skills/core/dev-companion/SKILL.md +65 -0
  221. agent_toolkit/data/skills/core/dev-companion/references/LOOP_GUARDRAILS.md +0 -0
  222. agent_toolkit/data/skills/core/onboarding/SKILL.md +122 -0
  223. agent_toolkit/data/skills/core/output-handshake/SKILL.md +26 -0
  224. agent_toolkit/data/skills/core/pr-fallback/SKILL.md +35 -0
  225. agent_toolkit/data/skills/core/pr-fallback/references/pr-body-default.md +0 -0
  226. agent_toolkit/data/skills/core/project/SKILL.md +89 -0
  227. agent_toolkit/data/skills/core/workspace/SKILL.md +103 -0
  228. agent_toolkit/data/skills/core/workspace-knowledge-sync/SKILL.md +140 -0
  229. agent_toolkit/data/skills/data/dbt-validation/SKILL.md +41 -0
  230. agent_toolkit/data/skills/data/snowflake-validation/SKILL.md +38 -0
  231. agent_toolkit/data/skills/delivery/adr/SKILL.md +56 -0
  232. agent_toolkit/data/skills/delivery/adr/references/default-template.md +35 -0
  233. agent_toolkit/data/skills/delivery/adr/references/example-001-graphql-adoption.md +66 -0
  234. agent_toolkit/data/skills/delivery/agreement/SKILL.md +24 -0
  235. agent_toolkit/data/skills/delivery/agreement/references/default-template.md +17 -0
  236. agent_toolkit/data/skills/delivery/agreement/references/example-api-versioning.md +29 -0
  237. agent_toolkit/data/skills/delivery/bug/SKILL.md +26 -0
  238. agent_toolkit/data/skills/delivery/bug/references/default-template.md +26 -0
  239. agent_toolkit/data/skills/delivery/bug/references/example-search-filter-bug.md +58 -0
  240. agent_toolkit/data/skills/delivery/decision-log/SKILL.md +28 -0
  241. agent_toolkit/data/skills/delivery/decision-log/references/default-template.md +17 -0
  242. agent_toolkit/data/skills/delivery/decision-log/references/example-repository-pattern.md +27 -0
  243. agent_toolkit/data/skills/delivery/development-workflow/SKILL.md +34 -0
  244. agent_toolkit/data/skills/delivery/development-workflow/references/default-workflow-checklist.md +44 -0
  245. agent_toolkit/data/skills/delivery/epic/SKILL.md +22 -0
  246. agent_toolkit/data/skills/delivery/epic/references/default-template.md +26 -0
  247. agent_toolkit/data/skills/delivery/epic/references/example-unified-dashboard.md +40 -0
  248. agent_toolkit/data/skills/delivery/incident/SKILL.md +24 -0
  249. agent_toolkit/data/skills/delivery/incident/references/default-template.md +29 -0
  250. agent_toolkit/data/skills/delivery/incident/references/example-payment-outage.md +62 -0
  251. agent_toolkit/data/skills/delivery/management-unit-assessment/SKILL.md +104 -0
  252. agent_toolkit/data/skills/delivery/management-unit-assessment/references/default-template.md +24 -0
  253. agent_toolkit/data/skills/delivery/management-unit-assessment/references/example-team-assessment.md +126 -0
  254. agent_toolkit/data/skills/delivery/meeting-minutes/SKILL.md +25 -0
  255. agent_toolkit/data/skills/delivery/meeting-minutes/references/default-template.md +22 -0
  256. agent_toolkit/data/skills/delivery/meeting-minutes/references/example-weekly-sync.md +72 -0
  257. agent_toolkit/data/skills/delivery/planning/SKILL.md +31 -0
  258. agent_toolkit/data/skills/delivery/planning/references/default-template.md +25 -0
  259. agent_toolkit/data/skills/delivery/planning/references/example-sprint-planning.md +62 -0
  260. agent_toolkit/data/skills/delivery/prd/SKILL.md +46 -0
  261. agent_toolkit/data/skills/delivery/prd/references/default-template.md +31 -0
  262. agent_toolkit/data/skills/delivery/prd/references/example-unified-api.md +71 -0
  263. agent_toolkit/data/skills/delivery/project-assessment/SKILL.md +90 -0
  264. agent_toolkit/data/skills/delivery/project-assessment/references/default-template.md +26 -0
  265. agent_toolkit/data/skills/delivery/project-assessment/references/example-platform-assessment.md +122 -0
  266. agent_toolkit/data/skills/delivery/project-assessment-evidence/SKILL.md +88 -0
  267. agent_toolkit/data/skills/delivery/project-assessment-evidence/references/default-template.md +24 -0
  268. agent_toolkit/data/skills/delivery/project-assessment-evidence/references/example-evidence-map.md +46 -0
  269. agent_toolkit/data/skills/delivery/spike/SKILL.md +24 -0
  270. agent_toolkit/data/skills/delivery/spike/references/default-template.md +29 -0
  271. agent_toolkit/data/skills/delivery/spike/references/example-graphql-federation.md +79 -0
  272. agent_toolkit/data/skills/delivery/task/SKILL.md +21 -0
  273. agent_toolkit/data/skills/delivery/task/references/default-template.md +21 -0
  274. agent_toolkit/data/skills/delivery/task/references/example-nav-system.md +33 -0
  275. agent_toolkit/data/skills/delivery/technical-unit-assessment/SKILL.md +58 -0
  276. agent_toolkit/data/skills/delivery/technical-unit-assessment/references/default-template.md +26 -0
  277. agent_toolkit/data/skills/delivery/technical-unit-assessment/references/example-frontend-assessment.md +112 -0
  278. agent_toolkit/data/skills/delivery/technical-unit-assessment/references/indicator-groups.md +65 -0
  279. agent_toolkit/data/skills/delivery/trd/SKILL.md +49 -0
  280. agent_toolkit/data/skills/delivery/trd/references/default-template.md +31 -0
  281. agent_toolkit/data/skills/delivery/trd/references/example-api-migration.md +122 -0
  282. agent_toolkit/data/skills/delivery/user-story/SKILL.md +21 -0
  283. agent_toolkit/data/skills/delivery/user-story/references/default-template.md +21 -0
  284. agent_toolkit/data/skills/delivery/user-story/references/example-advanced-filtering.md +37 -0
  285. agent_toolkit/data/skills/delivery/work-item/SKILL.md +31 -0
  286. agent_toolkit/data/skills/delivery/work-item/references/default-template.md +21 -0
  287. agent_toolkit/data/skills/delivery/work-item/references/example-routing-dashboard.md +23 -0
  288. agent_toolkit/data/skills/delivery/workflow-client-bootstrap/SKILL.md +148 -0
  289. agent_toolkit/data/skills/delivery/workflow-client-bootstrap/questions.yaml +165 -0
  290. agent_toolkit/data/skills/delivery/workflow-generic-project/SKILL.md +79 -0
  291. agent_toolkit/data/skills/design/design-assessment/SKILL.md +150 -0
  292. agent_toolkit/data/skills/design/design-assessment/references/design-scorecard-template.md +78 -0
  293. agent_toolkit/data/skills/design/design-improvement/SKILL.md +167 -0
  294. agent_toolkit/data/skills/design/design-improvement/references/improvement-plan-template.md +113 -0
  295. agent_toolkit/data/skills/design/figma/LICENSE.txt +2 -0
  296. agent_toolkit/data/skills/design/figma/NOTICE.txt +25 -0
  297. agent_toolkit/data/skills/design/figma/SKILL.md +98 -0
  298. agent_toolkit/data/skills/design/figma/references/figma-mcp-config.md +82 -0
  299. agent_toolkit/data/skills/design/figma/references/figma-tools-and-prompts.md +34 -0
  300. agent_toolkit/data/skills/design/figma-code-connect-components/LICENSE.txt +2 -0
  301. agent_toolkit/data/skills/design/figma-code-connect-components/NOTICE.txt +18 -0
  302. agent_toolkit/data/skills/design/figma-code-connect-components/SKILL.md +354 -0
  303. agent_toolkit/data/skills/design/figma-code-connect-components/references/mapping-checklist.md +7 -0
  304. agent_toolkit/data/skills/design/figma-code-connect-components/scripts/normalize_node_id.py +25 -0
  305. agent_toolkit/data/skills/design/figma-create-design-system-rules/LICENSE.txt +2 -0
  306. agent_toolkit/data/skills/design/figma-create-design-system-rules/NOTICE.txt +19 -0
  307. agent_toolkit/data/skills/design/figma-create-design-system-rules/SKILL.md +554 -0
  308. agent_toolkit/data/skills/design/figma-create-design-system-rules/references/rule-template.md +15 -0
  309. agent_toolkit/data/skills/design/figma-create-design-system-rules/scripts/check_agents_md.sh +9 -0
  310. agent_toolkit/data/skills/design/figma-create-new-file/LICENSE.txt +2 -0
  311. agent_toolkit/data/skills/design/figma-create-new-file/NOTICE.txt +17 -0
  312. agent_toolkit/data/skills/design/figma-create-new-file/SKILL.md +72 -0
  313. agent_toolkit/data/skills/design/figma-implement-design/LICENSE.txt +2 -0
  314. agent_toolkit/data/skills/design/figma-implement-design/NOTICE.txt +19 -0
  315. agent_toolkit/data/skills/design/figma-implement-design/SKILL.md +268 -0
  316. agent_toolkit/data/skills/design/frontend-design/LICENSE.txt +177 -0
  317. agent_toolkit/data/skills/design/frontend-design/SKILL.md +82 -0
  318. agent_toolkit/data/skills/design/frontend-design/UPSTREAM.md +14 -0
  319. agent_toolkit/data/skills/design/frontend-design-review/LICENSE +21 -0
  320. agent_toolkit/data/skills/design/frontend-design-review/SKILL.md +194 -0
  321. agent_toolkit/data/skills/design/frontend-design-review/references/pattern-examples.md +21 -0
  322. agent_toolkit/data/skills/design/frontend-design-review/references/quick-checklist.md +38 -0
  323. agent_toolkit/data/skills/design/frontend-design-review/references/review-output-format.md +68 -0
  324. agent_toolkit/data/skills/design/frontend-design-review/references/review-type-modifiers.md +31 -0
  325. agent_toolkit/data/skills/design/web-design-guidelines/SKILL.md +78 -0
  326. agent_toolkit/data/skills/design/web-design-guidelines/references/LICENSE +21 -0
  327. agent_toolkit/data/skills/design/web-design-guidelines/references/web-interface-guidelines.md +180 -0
  328. agent_toolkit/data/skills/forge/gh-address-comments/LICENSE.txt +202 -0
  329. agent_toolkit/data/skills/forge/gh-address-comments/NOTICE.txt +20 -0
  330. agent_toolkit/data/skills/forge/gh-address-comments/SKILL.md +72 -0
  331. agent_toolkit/data/skills/forge/gh-address-comments/scripts/fetch_comments.py +239 -0
  332. agent_toolkit/data/skills/forge/gh-contribution-planner/SKILL.md +160 -0
  333. agent_toolkit/data/skills/forge/gh-contribution-planner/scripts/inspect_contributions.py +818 -0
  334. agent_toolkit/data/skills/forge/gh-fix-ci/LICENSE.txt +201 -0
  335. agent_toolkit/data/skills/forge/gh-fix-ci/NOTICE.txt +20 -0
  336. agent_toolkit/data/skills/forge/gh-fix-ci/SKILL.md +74 -0
  337. agent_toolkit/data/skills/forge/gh-fix-ci/scripts/inspect_pr_checks.py +509 -0
  338. agent_toolkit/data/skills/forge/github-cli-workflow/SKILL.md +49 -0
  339. agent_toolkit/data/skills/forge/gitlab-cli-workflow/SKILL.md +44 -0
  340. agent_toolkit/data/skills/forge/workflow-client-bootstrap/SKILL.md +14 -0
  341. agent_toolkit/data/skills/forge/workflow-generic-project/SKILL.md +14 -0
  342. agent_toolkit/data/skills/forge/worktree/SKILL.md +113 -0
  343. agent_toolkit/data/skills/integrations/clickup-cli/SKILL.md +78 -0
  344. agent_toolkit/data/skills/integrations/clickup-cli/references/docs-management.md +57 -0
  345. agent_toolkit/data/skills/integrations/clickup-cli/references/task-management.md +131 -0
  346. agent_toolkit/data/skills/integrations/clickup-cli/references/time-tracking.md +38 -0
  347. agent_toolkit/data/skills/integrations/linear/LICENSE.txt +202 -0
  348. agent_toolkit/data/skills/integrations/linear/NOTICE.txt +23 -0
  349. agent_toolkit/data/skills/integrations/linear/SKILL.md +134 -0
  350. agent_toolkit/data/skills/integrations/mcp/SKILL.md +88 -0
  351. agent_toolkit/data/skills/integrations/slack-assistant/SKILL.md +138 -0
  352. agent_toolkit/data/skills/integrations/slack-cli/SKILL.md +109 -0
  353. agent_toolkit/data/skills/integrations/slack-cli/references/INSTALLATION.md +42 -0
  354. agent_toolkit/data/skills/loops/loop-runner/SKILL.md +92 -0
  355. agent_toolkit/data/skills/ops/docs-generator/SKILL.md +120 -0
  356. agent_toolkit/data/skills/ops/llm-cost-advisor/SKILL.md +87 -0
  357. agent_toolkit/data/skills/ops/swarm/SKILL.md +108 -0
  358. agent_toolkit/data/skills/ops/swarm-handoff/SKILL.md +112 -0
  359. agent_toolkit/data/skills/ops/swarm-observer/SKILL.md +102 -0
  360. agent_toolkit/data/skills/ops/triage/SKILL.md +119 -0
  361. agent_toolkit/data/skills/quality/codeql/SKILL.md +106 -0
  362. agent_toolkit/data/skills/quality/codeql/references/codeql-queries.md +43 -0
  363. agent_toolkit/data/skills/quality/megalinter/SKILL.md +162 -0
  364. agent_toolkit/data/skills/quality/megalinter/references/megalinter-images.md +36 -0
  365. agent_toolkit/data/skills/quality/megalinter/references/megalinter-license.md +41 -0
  366. agent_toolkit/data/skills/quality/megalinter/references/megalinter-targets.md +66 -0
  367. agent_toolkit/data/skills/tooling/chrome-devtools/SKILL.md +146 -0
  368. agent_toolkit/data/skills/tooling/herdr/SKILL.md +109 -0
  369. agent_toolkit/data/skills/tooling/inventory/SKILL.md +81 -0
  370. agent_toolkit/data/skills/tooling/jupyter-notebook/LICENSE.txt +201 -0
  371. agent_toolkit/data/skills/tooling/jupyter-notebook/NOTICE.txt +27 -0
  372. agent_toolkit/data/skills/tooling/jupyter-notebook/SKILL.md +145 -0
  373. agent_toolkit/data/skills/tooling/jupyter-notebook/assets/experiment-template.ipynb +117 -0
  374. agent_toolkit/data/skills/tooling/jupyter-notebook/assets/tutorial-template.ipynb +115 -0
  375. agent_toolkit/data/skills/tooling/jupyter-notebook/references/experiment-patterns.md +10 -0
  376. agent_toolkit/data/skills/tooling/jupyter-notebook/references/notebook-structure.md +17 -0
  377. agent_toolkit/data/skills/tooling/jupyter-notebook/references/quality-checklist.md +11 -0
  378. agent_toolkit/data/skills/tooling/jupyter-notebook/references/tutorial-patterns.md +9 -0
  379. agent_toolkit/data/skills/tooling/jupyter-notebook/scripts/new_notebook.py +132 -0
  380. agent_toolkit/data/skills/tooling/mermaid/SKILL.md +36 -0
  381. agent_toolkit/data/skills/tooling/playwright-cli/LICENSE.txt +201 -0
  382. agent_toolkit/data/skills/tooling/playwright-cli/NOTICE.txt +29 -0
  383. agent_toolkit/data/skills/tooling/playwright-cli/SKILL.md +154 -0
  384. agent_toolkit/data/skills/tooling/playwright-cli/references/cli.md +116 -0
  385. agent_toolkit/data/skills/tooling/playwright-cli/references/workflows.md +98 -0
  386. agent_toolkit/data/skills/tooling/playwright-cli/scripts/executable_playwright_cli.sh +25 -0
  387. agent_toolkit/data_cache.py +79 -0
  388. agent_toolkit/data_sync.py +144 -0
  389. agent_toolkit/installer/__init__.py +0 -0
  390. agent_toolkit/installer/merge.py +59 -0
  391. agent_toolkit/installer/receipt.py +89 -0
  392. agent_toolkit/installer/sources.py +92 -0
  393. agent_toolkit/installer/tracking.py +63 -0
  394. agent_toolkit/launcher.py +69 -0
  395. agent_toolkit/loop/__init__.py +0 -0
  396. agent_toolkit/loop/budget.py +107 -0
  397. agent_toolkit/loop/gh_gate.py +923 -0
  398. agent_toolkit/loop/loop-gh-gate +923 -0
  399. agent_toolkit/loop/pack.py +65 -0
  400. agent_toolkit/loop/runner.py +2722 -0
  401. agent_toolkit/runner/__init__.py +0 -0
  402. agent_toolkit/runner/dispatcher.py +163 -0
  403. agent_toolkit/runner/policy.py +356 -0
  404. agent_toolkit/runner/providers/__init__.py +21 -0
  405. agent_toolkit/runner/providers/anthropic_provider.py +109 -0
  406. agent_toolkit/runner/providers/base.py +53 -0
  407. agent_toolkit/runner/providers/ollama_provider.py +108 -0
  408. agent_toolkit/runner/providers/openai_provider.py +103 -0
  409. agent_toolkit/runner/providers/opencode_provider.py +127 -0
  410. agent_toolkit/swarm/__init__.py +5 -0
  411. agent_toolkit/swarm/approvals.py +121 -0
  412. agent_toolkit/swarm/backends/__init__.py +596 -0
  413. agent_toolkit/swarm/budget.py +83 -0
  414. agent_toolkit/swarm/cli.py +2448 -0
  415. agent_toolkit/swarm/config.py +351 -0
  416. agent_toolkit/swarm/handoff.py +174 -0
  417. agent_toolkit/swarm/models.py +230 -0
  418. agent_toolkit/swarm/prompts.py +177 -0
  419. agent_toolkit/swarm/recipes.py +304 -0
  420. agent_toolkit/swarm/runner.py +267 -0
  421. agent_toolkit/swarm/state.py +93 -0
  422. agent_toolkit/swarm/store.py +148 -0
  423. agent_toolkit/swarm/worktree.py +105 -0
  424. agent_toolkit/templates/workspace/.gitignore +19 -0
  425. agent_toolkit/templates/workspace/AGENTS.md +82 -0
  426. agent_toolkit/templates/workspace/knowledge/README.md +22 -0
  427. agent_toolkit/templates/workspace/knowledge/learnings/general.md +3 -0
  428. agent_toolkit/templates/workspace/knowledge/todos/pending.md +7 -0
  429. agent_toolkit/templates/workspace/packs/README.md +24 -0
  430. agent_toolkit/templates/workspace/personas/architect.md +12 -0
  431. agent_toolkit/templates/workspace/personas/implementer.md +12 -0
  432. agent_toolkit/templates/workspace/personas/researcher.md +12 -0
  433. agent_toolkit/templates/workspace/personas/reviewer.md +12 -0
  434. agent_toolkit_cli-1.11.0.dist-info/METADATA +363 -0
  435. agent_toolkit_cli-1.11.0.dist-info/RECORD +438 -0
  436. agent_toolkit_cli-1.11.0.dist-info/WHEEL +4 -0
  437. agent_toolkit_cli-1.11.0.dist-info/entry_points.txt +4 -0
  438. agent_toolkit_cli-1.11.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,818 @@
1
+ #!/usr/bin/env python3
2
+ """Daily GitHub contribution planner.
3
+
4
+ Inspect the gh-authenticated user's owned repos (forks + sources), open
5
+ pull requests, and recent contributions, then emit a prioritized plan
6
+ grouped by effort/impact buckets. Markdown by default; JSON via --json.
7
+
8
+ Designed to be invoked from the agent-toolkit `gh-contribution-planner` skill.
9
+ Stdlib only — relies exclusively on the `gh` CLI for GitHub access.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import argparse
15
+ import json
16
+ import re
17
+ import subprocess
18
+ import sys
19
+ from concurrent.futures import ThreadPoolExecutor, as_completed
20
+ from datetime import datetime, timedelta, timezone
21
+ from shutil import which
22
+ from typing import Any
23
+
24
+ DEFAULT_SINCE = "90d"
25
+ DEFAULT_MAX_PER_BUCKET = 5
26
+ PARALLEL_FORK_COMPARES = 8
27
+ QUICK_WIN_DORMANT_DAYS = 60
28
+ STALE_ISSUE_DAYS_DEFAULT = 90
29
+ REVIEW_FEEDBACK_AGE_DAYS = 7
30
+
31
+ BUCKETS = (
32
+ "quick_wins",
33
+ "in_flight_prs",
34
+ "review_feedback",
35
+ "forks_to_sync",
36
+ "maintenance",
37
+ "external_contributions",
38
+ )
39
+
40
+ BUCKET_LABELS = {
41
+ "quick_wins": "Quick wins (~15 min)",
42
+ "in_flight_prs": "In-flight PRs",
43
+ "review_feedback": "Review feedback",
44
+ "forks_to_sync": "Forks needing sync",
45
+ "maintenance": "Maintenance",
46
+ "external_contributions": "External contributions",
47
+ }
48
+
49
+ DELEGATES = {
50
+ "quick_wins": "github-cli-workflow",
51
+ "in_flight_prs": "gh-fix-ci or github-cli-workflow",
52
+ "review_feedback": "gh-address-comments",
53
+ "forks_to_sync": "github-cli-workflow (gh repo sync)",
54
+ "maintenance": "github-cli-workflow",
55
+ "external_contributions": "github-cli-workflow",
56
+ }
57
+
58
+ CONTRIBUTIONS_GRAPHQL = """
59
+ query($login: String!, $since: DateTime!) {
60
+ user(login: $login) {
61
+ contributionsCollection(from: $since) {
62
+ commitContributionsByRepository(maxRepositories: 50) {
63
+ repository { nameWithOwner url isPrivate owner { login } }
64
+ contributions { totalCount }
65
+ }
66
+ pullRequestContributionsByRepository(maxRepositories: 50) {
67
+ repository { nameWithOwner url isPrivate owner { login } }
68
+ contributions { totalCount }
69
+ }
70
+ issueContributionsByRepository(maxRepositories: 25) {
71
+ repository { nameWithOwner url isPrivate owner { login } }
72
+ contributions { totalCount }
73
+ }
74
+ }
75
+ }
76
+ }
77
+ """.strip()
78
+
79
+ OPEN_PRS_GRAPHQL = """
80
+ query($queryString: String!) {
81
+ search(query: $queryString, type: ISSUE, first: 100) {
82
+ nodes {
83
+ ... on PullRequest {
84
+ url
85
+ title
86
+ number
87
+ isDraft
88
+ createdAt
89
+ updatedAt
90
+ state
91
+ reviewDecision
92
+ repository { nameWithOwner }
93
+ }
94
+ }
95
+ }
96
+ }
97
+ """.strip()
98
+
99
+
100
+ class GhResult:
101
+ def __init__(self, returncode: int, stdout: str, stderr: str) -> None:
102
+ self.returncode = returncode
103
+ self.stdout = stdout
104
+ self.stderr = stderr
105
+
106
+
107
+ def run_gh(args: list[str], *, dry_run: bool = False) -> GhResult:
108
+ if dry_run:
109
+ print(f"+ gh {' '.join(args)}", file=sys.stderr)
110
+ empty = "[]" if any(a == "--json" for a in args) else ""
111
+ return GhResult(0, empty, "")
112
+ proc = subprocess.run(["gh", *args], text=True, capture_output=True)
113
+ return GhResult(proc.returncode, proc.stdout, proc.stderr)
114
+
115
+
116
+ def _positive_int(value: str) -> int:
117
+ parsed = int(value)
118
+ if parsed <= 0:
119
+ raise argparse.ArgumentTypeError("must be > 0")
120
+ return parsed
121
+
122
+
123
+ def _non_negative_int(value: str) -> int:
124
+ parsed = int(value)
125
+ if parsed < 0:
126
+ raise argparse.ArgumentTypeError("must be >= 0")
127
+ return parsed
128
+
129
+
130
+ def parse_args() -> argparse.Namespace:
131
+ parser = argparse.ArgumentParser(
132
+ description=(
133
+ "Daily GitHub contribution planner. Analyzes the gh-authenticated "
134
+ "user's repos and recent contributions, emitting a prioritized plan."
135
+ ),
136
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
137
+ )
138
+ parser.add_argument(
139
+ "--since",
140
+ default=DEFAULT_SINCE,
141
+ help="Lookback window. Accepts e.g. 30d, 90d, 180d, or ISO date YYYY-MM-DD.",
142
+ )
143
+ parser.add_argument(
144
+ "--max-per-bucket",
145
+ type=_positive_int,
146
+ default=DEFAULT_MAX_PER_BUCKET,
147
+ help="Cap items per bucket (must be > 0).",
148
+ )
149
+ parser.add_argument(
150
+ "--user",
151
+ default=None,
152
+ help="Override the gh-inferred login (testing or org planning).",
153
+ )
154
+ parser.add_argument(
155
+ "--stale-issue-days",
156
+ type=_non_negative_int,
157
+ default=STALE_ISSUE_DAYS_DEFAULT,
158
+ help="Issues older than this many days count as stale (must be >= 0).",
159
+ )
160
+ parser.add_argument(
161
+ "--json",
162
+ action="store_true",
163
+ help="Emit JSON instead of Markdown.",
164
+ )
165
+ parser.add_argument(
166
+ "--dry-run",
167
+ action="store_true",
168
+ help="Print the gh calls that would be issued and exit (no network).",
169
+ )
170
+ return parser.parse_args()
171
+
172
+
173
+ def parse_since(value: str) -> tuple[str, str]:
174
+ """Return (iso_date, iso_datetime_utc) for the lookback window."""
175
+ value = value.strip()
176
+ match = re.fullmatch(r"(\d+)\s*d", value, flags=re.IGNORECASE)
177
+ if match:
178
+ days = int(match.group(1))
179
+ cutoff = datetime.now(timezone.utc) - timedelta(days=days)
180
+ return cutoff.date().isoformat(), cutoff.replace(microsecond=0).isoformat()
181
+ try:
182
+ parsed = datetime.strptime(value, "%Y-%m-%d").replace(tzinfo=timezone.utc)
183
+ except ValueError as exc:
184
+ raise SystemExit(
185
+ f"Invalid --since value: {value!r} (expected 30d/90d/.../YYYY-MM-DD)"
186
+ ) from exc
187
+ return parsed.date().isoformat(), parsed.replace(microsecond=0).isoformat()
188
+
189
+
190
+ def ensure_gh_available() -> bool:
191
+ if which("gh") is None:
192
+ print("Error: gh is not installed or not on PATH.", file=sys.stderr)
193
+ return False
194
+ result = run_gh(["auth", "status"])
195
+ if result.returncode == 0:
196
+ return True
197
+ msg = (result.stderr or result.stdout or "").strip()
198
+ print(msg or "Error: gh is not authenticated. Run `gh auth login`.", file=sys.stderr)
199
+ return False
200
+
201
+
202
+ def resolve_user(override: str | None, *, dry_run: bool) -> str | None:
203
+ if override:
204
+ return override
205
+ if dry_run:
206
+ return "<gh-user>"
207
+ result = run_gh(["api", "user", "--jq", ".login"])
208
+ if result.returncode != 0:
209
+ msg = (result.stderr or result.stdout or "").strip()
210
+ print(msg or "Error: unable to resolve gh user.", file=sys.stderr)
211
+ return None
212
+ login = result.stdout.strip()
213
+ if not login:
214
+ print("Error: gh api user returned an empty login.", file=sys.stderr)
215
+ return None
216
+ return login
217
+
218
+
219
+ def fetch_owned_repos(user: str, *, dry_run: bool) -> list[dict[str, Any]]:
220
+ fields = ",".join(
221
+ [
222
+ "nameWithOwner",
223
+ "name",
224
+ "owner",
225
+ "description",
226
+ "isFork",
227
+ "isPrivate",
228
+ "isArchived",
229
+ "pushedAt",
230
+ "updatedAt",
231
+ "url",
232
+ "defaultBranchRef",
233
+ "primaryLanguage",
234
+ "parent",
235
+ "stargazerCount",
236
+ "forkCount",
237
+ "visibility",
238
+ ]
239
+ )
240
+ result = run_gh(
241
+ ["repo", "list", user, "--no-archived", "--limit", "1000", "--json", fields],
242
+ dry_run=dry_run,
243
+ )
244
+ if result.returncode != 0:
245
+ msg = (result.stderr or result.stdout or "").strip()
246
+ print(f"Warning: gh repo list failed: {msg}", file=sys.stderr)
247
+ return []
248
+ return _parse_json_list(result.stdout)
249
+
250
+
251
+ def fetch_authored_open_prs(user: str, *, dry_run: bool) -> list[dict[str, Any]]:
252
+ query_string = f"is:pr is:open author:{user}"
253
+ if dry_run:
254
+ print(
255
+ f"+ gh api graphql -f query=<openPullRequests> -f queryString='{query_string}'",
256
+ file=sys.stderr,
257
+ )
258
+ return []
259
+ result = run_gh(
260
+ [
261
+ "api",
262
+ "graphql",
263
+ "-f",
264
+ f"query={OPEN_PRS_GRAPHQL}",
265
+ "-f",
266
+ f"queryString={query_string}",
267
+ ]
268
+ )
269
+ if result.returncode != 0:
270
+ msg = (result.stderr or result.stdout or "").strip()
271
+ print(f"Warning: open PRs GraphQL query failed: {msg}", file=sys.stderr)
272
+ return []
273
+ try:
274
+ payload = json.loads(result.stdout or "{}")
275
+ except json.JSONDecodeError:
276
+ return []
277
+ nodes = ((payload.get("data") or {}).get("search") or {}).get("nodes") or []
278
+ return [node for node in nodes if isinstance(node, dict) and node]
279
+
280
+
281
+ def fetch_contributions_collection(
282
+ user: str, since_iso_datetime: str, *, dry_run: bool
283
+ ) -> dict[str, Any]:
284
+ if dry_run:
285
+ print(
286
+ f"+ gh api graphql -f query=<contributionsCollection> -f login={user} "
287
+ f"-f since={since_iso_datetime}",
288
+ file=sys.stderr,
289
+ )
290
+ return {}
291
+ result = run_gh(
292
+ [
293
+ "api",
294
+ "graphql",
295
+ "-f",
296
+ f"query={CONTRIBUTIONS_GRAPHQL}",
297
+ "-f",
298
+ f"login={user}",
299
+ "-f",
300
+ f"since={since_iso_datetime}",
301
+ ]
302
+ )
303
+ if result.returncode != 0:
304
+ msg = (result.stderr or result.stdout or "").strip()
305
+ print(f"Warning: contributionsCollection query failed: {msg}", file=sys.stderr)
306
+ return {}
307
+ try:
308
+ payload = json.loads(result.stdout or "{}")
309
+ except json.JSONDecodeError:
310
+ return {}
311
+ return ((payload.get("data") or {}).get("user") or {}).get("contributionsCollection") or {}
312
+
313
+
314
+ def fetch_owner_open_issues(
315
+ user: str, *, label: str | None, no_assignee: bool, dry_run: bool
316
+ ) -> list[dict[str, Any]]:
317
+ fields = "repository,url,number,title,updatedAt,labels,assignees,author,state"
318
+ args = [
319
+ "search",
320
+ "issues",
321
+ "--owner",
322
+ user,
323
+ "--state",
324
+ "open",
325
+ "--limit",
326
+ "200",
327
+ "--json",
328
+ fields,
329
+ ]
330
+ if label:
331
+ args.extend(["--label", label])
332
+ if no_assignee:
333
+ args.append("--no-assignee")
334
+ result = run_gh(args, dry_run=dry_run)
335
+ if result.returncode != 0:
336
+ msg = (result.stderr or result.stdout or "").strip()
337
+ print(f"Warning: gh search issues failed: {msg}", file=sys.stderr)
338
+ return []
339
+ return _parse_json_list(result.stdout)
340
+
341
+
342
+ def fetch_fork_compare(fork: dict[str, Any]) -> dict[str, Any]:
343
+ parent = fork.get("parent") or {}
344
+ parent_owner = (parent.get("owner") or {}).get("login")
345
+ parent_name = parent.get("name")
346
+ fork_default = (fork.get("defaultBranchRef") or {}).get("name")
347
+ parent_default = (parent.get("defaultBranchRef") or {}).get("name")
348
+ fork_owner = (fork.get("owner") or {}).get("login")
349
+ fork_name = fork.get("name")
350
+ out: dict[str, Any] = {
351
+ "fork": fork.get("nameWithOwner"),
352
+ "fork_url": fork.get("url"),
353
+ "parent": (f"{parent_owner}/{parent_name}" if parent_owner and parent_name else None),
354
+ "behind": None,
355
+ "ahead": None,
356
+ "error": None,
357
+ }
358
+ missing = not (
359
+ parent_owner
360
+ and parent_name
361
+ and fork_default
362
+ and parent_default
363
+ and fork_owner
364
+ and fork_name
365
+ )
366
+ if missing:
367
+ out["error"] = "Missing parent or default branch metadata."
368
+ return out
369
+ endpoint = (
370
+ f"/repos/{parent_owner}/{parent_name}/compare/"
371
+ f"{parent_default}...{fork_owner}:{fork_default}"
372
+ )
373
+ result = run_gh(["api", endpoint, "--jq", "{behind_by, ahead_by}"])
374
+ if result.returncode != 0:
375
+ out["error"] = (result.stderr or result.stdout or "").strip()[:200]
376
+ return out
377
+ try:
378
+ data = json.loads(result.stdout or "{}")
379
+ except json.JSONDecodeError:
380
+ out["error"] = "Unable to parse compare JSON."
381
+ return out
382
+ out["behind"] = data.get("behind_by")
383
+ out["ahead"] = data.get("ahead_by")
384
+ return out
385
+
386
+
387
+ def _parse_json_list(text: str) -> list[dict[str, Any]]:
388
+ try:
389
+ data = json.loads(text or "[]")
390
+ except json.JSONDecodeError:
391
+ return []
392
+ return data if isinstance(data, list) else []
393
+
394
+
395
+ def _iso(value: str | None) -> datetime | None:
396
+ if not value:
397
+ return None
398
+ try:
399
+ return datetime.fromisoformat(value.replace("Z", "+00:00"))
400
+ except ValueError:
401
+ return None
402
+
403
+
404
+ def _label_names(item: dict[str, Any]) -> list[str]:
405
+ labels = item.get("labels") or []
406
+ out: list[str] = []
407
+ for label in labels:
408
+ if isinstance(label, dict):
409
+ name = label.get("name")
410
+ if name:
411
+ out.append(str(name))
412
+ elif isinstance(label, str):
413
+ out.append(label)
414
+ return out
415
+
416
+
417
+ def _repo_full(value: Any) -> str:
418
+ if isinstance(value, dict):
419
+ return value.get("nameWithOwner") or ""
420
+ return ""
421
+
422
+
423
+ def bucketize(
424
+ *,
425
+ user: str,
426
+ since_iso: str,
427
+ repos: list[dict[str, Any]],
428
+ open_prs: list[dict[str, Any]],
429
+ contributions: dict[str, Any],
430
+ issues_good_first: list[dict[str, Any]],
431
+ issues_help_wanted: list[dict[str, Any]],
432
+ issues_owned: list[dict[str, Any]],
433
+ fork_compares: list[dict[str, Any]],
434
+ stale_issue_days: int,
435
+ max_per_bucket: int,
436
+ ) -> dict[str, list[dict[str, Any]]]:
437
+ plan: dict[str, list[dict[str, Any]]] = {bucket: [] for bucket in BUCKETS}
438
+ now = datetime.now(timezone.utc)
439
+
440
+ # --- Filter issues to non-archived repos ---
441
+ # `repos` is fetched with --no-archived so it only contains non-archived
442
+ # repos. `gh search issues --owner <user>` does NOT exclude archived repos,
443
+ # so issues from archived repos leak into the Maintenance and Quick wins
444
+ # buckets. Filter them out here using the non-archived repo set as an
445
+ # allow-list. Guard on non_archived_repos being non-empty so that a failed
446
+ # repos fetch doesn't wipe all issue items (fall back to old behavior).
447
+ non_archived_repos: set[str] = {(repo.get("nameWithOwner") or "") for repo in repos}
448
+ if non_archived_repos:
449
+ issues_good_first = [
450
+ i for i in issues_good_first if _repo_full(i.get("repository")) in non_archived_repos
451
+ ]
452
+ issues_help_wanted = [
453
+ i for i in issues_help_wanted if _repo_full(i.get("repository")) in non_archived_repos
454
+ ]
455
+ issues_owned = [
456
+ i for i in issues_owned if _repo_full(i.get("repository")) in non_archived_repos
457
+ ]
458
+
459
+ # --- Quick wins ---
460
+ quick: list[tuple[int, dict[str, Any]]] = []
461
+ for repo in repos:
462
+ if repo.get("isFork") or repo.get("isArchived"):
463
+ continue
464
+ pushed = _iso(repo.get("pushedAt"))
465
+ if not pushed:
466
+ continue
467
+ age_days = (now - pushed).days
468
+ if age_days >= QUICK_WIN_DORMANT_DAYS:
469
+ quick.append((age_days, repo))
470
+ quick.sort(key=lambda pair: pair[0], reverse=True)
471
+ for age_days, repo in quick[:max_per_bucket]:
472
+ plan["quick_wins"].append(
473
+ {
474
+ "repo": repo.get("nameWithOwner"),
475
+ "url": repo.get("url"),
476
+ "summary": (
477
+ f"Dormant for {age_days} days — README polish, dependency bump, "
478
+ "or CI badge update."
479
+ ),
480
+ "effort": "~15 min",
481
+ "delegate": DELEGATES["quick_wins"],
482
+ "signals": {
483
+ "pushed_days_ago": age_days,
484
+ "stars": repo.get("stargazerCount", 0),
485
+ },
486
+ }
487
+ )
488
+
489
+ # Top up quick_wins with unassigned good-first-issue / help-wanted in own repos.
490
+ # Issues can carry both labels — dedupe by (repo, number) so each slot is unique.
491
+ own_easy_raw = [i for i in issues_good_first if not (i.get("assignees") or [])] + [
492
+ i for i in issues_help_wanted if not (i.get("assignees") or [])
493
+ ]
494
+ own_easy: list[dict[str, Any]] = []
495
+ seen_easy: set[tuple[str, Any]] = set()
496
+ for issue in own_easy_raw:
497
+ key = (_repo_full(issue.get("repository")), issue.get("number"))
498
+ if key in seen_easy:
499
+ continue
500
+ seen_easy.add(key)
501
+ own_easy.append(issue)
502
+ remaining = max(0, max_per_bucket - len(plan["quick_wins"]))
503
+ for issue in own_easy[:remaining]:
504
+ plan["quick_wins"].append(
505
+ {
506
+ "repo": _repo_full(issue.get("repository")),
507
+ "url": issue.get("url"),
508
+ "issue_number": issue.get("number"),
509
+ "summary": f"Unassigned `{', '.join(_label_names(issue))}` issue: {issue.get('title')!r}",
510
+ "effort": "~15 min",
511
+ "delegate": DELEGATES["quick_wins"],
512
+ "signals": {"labels": _label_names(issue)},
513
+ }
514
+ )
515
+
516
+ # --- In-flight PRs ---
517
+ def _pr_sort_key(pr: dict[str, Any]) -> tuple[int, datetime]:
518
+ is_ready = 0 if pr.get("isDraft") else 1
519
+ updated = _iso(pr.get("updatedAt")) or datetime.min.replace(tzinfo=timezone.utc)
520
+ return (is_ready, updated)
521
+
522
+ for pr in sorted(open_prs, key=_pr_sort_key, reverse=True)[:max_per_bucket]:
523
+ updated = _iso(pr.get("updatedAt"))
524
+ age_days = (now - updated).days if updated else None
525
+ summary = pr.get("title") or ""
526
+ if pr.get("isDraft"):
527
+ summary = f"[draft] {summary}"
528
+ plan["in_flight_prs"].append(
529
+ {
530
+ "repo": _repo_full(pr.get("repository")),
531
+ "url": pr.get("url"),
532
+ "pr_number": pr.get("number"),
533
+ "summary": summary,
534
+ "effort": "varies",
535
+ "delegate": DELEGATES["in_flight_prs"],
536
+ "signals": {
537
+ "is_draft": bool(pr.get("isDraft")),
538
+ "updated_days_ago": age_days,
539
+ },
540
+ }
541
+ )
542
+
543
+ # --- Review feedback ---
544
+ # Filter strictly by reviewDecision == CHANGES_REQUESTED so this bucket
545
+ # represents real unaddressed reviewer feedback, not just inactivity.
546
+ candidates: list[tuple[int, dict[str, Any]]] = []
547
+ for pr in open_prs:
548
+ if pr.get("isDraft"):
549
+ continue
550
+ if pr.get("reviewDecision") != "CHANGES_REQUESTED":
551
+ continue
552
+ updated = _iso(pr.get("updatedAt"))
553
+ age_days = (now - updated).days if updated else 0
554
+ candidates.append((age_days, pr))
555
+ candidates.sort(key=lambda pair: pair[0], reverse=True)
556
+ for age_days, pr in candidates[:max_per_bucket]:
557
+ plan["review_feedback"].append(
558
+ {
559
+ "repo": _repo_full(pr.get("repository")),
560
+ "url": pr.get("url"),
561
+ "pr_number": pr.get("number"),
562
+ "summary": (
563
+ f"Reviewer requested changes ({age_days} days since last activity) "
564
+ "— address the feedback."
565
+ ),
566
+ "effort": "~30 min",
567
+ "delegate": DELEGATES["review_feedback"],
568
+ "signals": {
569
+ "review_decision": pr.get("reviewDecision"),
570
+ "updated_days_ago": age_days,
571
+ },
572
+ }
573
+ )
574
+
575
+ # --- Forks needing sync ---
576
+ drift = [fc for fc in fork_compares if isinstance(fc.get("behind"), int) and fc["behind"] > 0]
577
+ drift.sort(key=lambda fc: fc.get("behind") or 0, reverse=True)
578
+ for fc in drift[:max_per_bucket]:
579
+ plan["forks_to_sync"].append(
580
+ {
581
+ "repo": fc.get("fork"),
582
+ "url": fc.get("fork_url"),
583
+ "parent": fc.get("parent"),
584
+ "summary": (
585
+ f"Fork is {fc.get('behind')} commits behind {fc.get('parent')}; "
586
+ f"ahead {fc.get('ahead') or 0}."
587
+ ),
588
+ "effort": "~5 min (clean sync) / ~30 min (conflicts)",
589
+ "delegate": DELEGATES["forks_to_sync"],
590
+ "signals": {
591
+ "behind": fc.get("behind"),
592
+ "ahead": fc.get("ahead"),
593
+ },
594
+ }
595
+ )
596
+
597
+ # --- Maintenance: stale issues in own repos (authored by others) ---
598
+ stale_cutoff = now - timedelta(days=stale_issue_days)
599
+ stale: list[tuple[int, dict[str, Any]]] = []
600
+ for issue in issues_owned:
601
+ updated = _iso(issue.get("updatedAt"))
602
+ if not updated or updated > stale_cutoff:
603
+ continue
604
+ author = (issue.get("author") or {}).get("login", "")
605
+ if author and author.lower() == user.lower():
606
+ continue
607
+ stale.append(((now - updated).days, issue))
608
+ stale.sort(key=lambda pair: pair[0], reverse=True)
609
+ for age_days, issue in stale[:max_per_bucket]:
610
+ plan["maintenance"].append(
611
+ {
612
+ "repo": _repo_full(issue.get("repository")),
613
+ "url": issue.get("url"),
614
+ "issue_number": issue.get("number"),
615
+ "summary": (f"Issue stale for {age_days} days: {issue.get('title')!r}"),
616
+ "effort": "~20 min",
617
+ "delegate": DELEGATES["maintenance"],
618
+ "signals": {
619
+ "updated_days_ago": age_days,
620
+ "labels": _label_names(issue),
621
+ },
622
+ }
623
+ )
624
+
625
+ # --- External contributions ---
626
+ aggregate: dict[str, dict[str, Any]] = {}
627
+
628
+ def _ingest(section: str, weight: int) -> None:
629
+ for node in contributions.get(section) or []:
630
+ repo = node.get("repository") or {}
631
+ repo_full = repo.get("nameWithOwner") or ""
632
+ if not repo_full:
633
+ continue
634
+ owner = (repo.get("owner") or {}).get("login", "")
635
+ if owner and owner.lower() == user.lower():
636
+ continue
637
+ entry = aggregate.setdefault(
638
+ repo_full,
639
+ {
640
+ "repo": repo_full,
641
+ "url": repo.get("url") or f"https://github.com/{repo_full}",
642
+ "score": 0,
643
+ "commits": 0,
644
+ "prs": 0,
645
+ "issues": 0,
646
+ "private": bool(repo.get("isPrivate")),
647
+ },
648
+ )
649
+ count = (node.get("contributions") or {}).get("totalCount", 0)
650
+ entry["score"] += weight * count
651
+ if section.startswith("commit"):
652
+ entry["commits"] += count
653
+ elif section.startswith("pullRequest"):
654
+ entry["prs"] += count
655
+ elif section.startswith("issue"):
656
+ entry["issues"] += count
657
+
658
+ _ingest("pullRequestContributionsByRepository", weight=3)
659
+ _ingest("commitContributionsByRepository", weight=2)
660
+ _ingest("issueContributionsByRepository", weight=1)
661
+
662
+ external = sorted(aggregate.values(), key=lambda entry: entry["score"], reverse=True)
663
+ for entry in external[:max_per_bucket]:
664
+ signals = {
665
+ "recent_commits": entry["commits"],
666
+ "recent_prs": entry["prs"],
667
+ "recent_issues": entry["issues"],
668
+ }
669
+ plan["external_contributions"].append(
670
+ {
671
+ "repo": entry["repo"],
672
+ "url": entry["url"],
673
+ "summary": (
674
+ f"Active externally since {since_iso} "
675
+ f"(commits={entry['commits']}, prs={entry['prs']}, issues={entry['issues']}). "
676
+ "Scan for `good first issue` / `help wanted`."
677
+ ),
678
+ "effort": "varies",
679
+ "delegate": DELEGATES["external_contributions"],
680
+ "signals": signals,
681
+ }
682
+ )
683
+
684
+ plan["quick_wins"] = plan["quick_wins"][:max_per_bucket]
685
+ return plan
686
+
687
+
688
+ def render_markdown(plan: dict[str, list[dict[str, Any]]], *, user: str, since_iso: str) -> None:
689
+ print(f"# Daily GitHub contribution plan — @{user}")
690
+ print()
691
+ print(f"_Lookback window: contributions since `{since_iso}`._")
692
+ print()
693
+ total = sum(len(items) for items in plan.values())
694
+ if total == 0:
695
+ print(
696
+ "No actionable items found. Try widening `--since`, raising "
697
+ "`--max-per-bucket`, or verifying `gh auth status`."
698
+ )
699
+ return
700
+ for bucket in BUCKETS:
701
+ items = plan.get(bucket) or []
702
+ if not items:
703
+ continue
704
+ print(f"## {BUCKET_LABELS[bucket]} ({len(items)})")
705
+ print()
706
+ for item in items:
707
+ repo = item.get("repo", "") or "(unknown)"
708
+ url = item.get("url", "")
709
+ summary = item.get("summary", "")
710
+ effort = item.get("effort", "")
711
+ delegate = item.get("delegate", "")
712
+ line = f"- **{repo}** — {summary}"
713
+ if url:
714
+ line += f" ([link]({url}))"
715
+ print(line)
716
+ print(f" - Effort: {effort} · Delegate: `{delegate}`")
717
+ print()
718
+ print("---")
719
+ print()
720
+ print(
721
+ "After your approval, the host agent should dispatch one sub-agent per "
722
+ "approved item (cap: 5 concurrent). Each sub-agent must open its PR as "
723
+ "**draft** unless explicitly marked ready-for-review."
724
+ )
725
+
726
+
727
+ def render_json(plan: dict[str, list[dict[str, Any]]], *, user: str, since_iso: str) -> None:
728
+ print(
729
+ json.dumps(
730
+ {
731
+ "user": user,
732
+ "since": since_iso,
733
+ "buckets": plan,
734
+ },
735
+ indent=2,
736
+ )
737
+ )
738
+
739
+
740
+ def main() -> int:
741
+ args = parse_args()
742
+
743
+ if not args.dry_run and not ensure_gh_available():
744
+ return 1
745
+
746
+ user = resolve_user(args.user, dry_run=args.dry_run)
747
+ if not user:
748
+ return 1
749
+
750
+ try:
751
+ since_iso, since_iso_datetime = parse_since(args.since)
752
+ except SystemExit as exc:
753
+ print(str(exc), file=sys.stderr)
754
+ return 2
755
+
756
+ repos = fetch_owned_repos(user, dry_run=args.dry_run)
757
+ open_prs = fetch_authored_open_prs(user, dry_run=args.dry_run)
758
+ contributions = fetch_contributions_collection(user, since_iso_datetime, dry_run=args.dry_run)
759
+ issues_good_first = fetch_owner_open_issues(
760
+ user, label="good first issue", no_assignee=True, dry_run=args.dry_run
761
+ )
762
+ issues_help_wanted = fetch_owner_open_issues(
763
+ user, label="help wanted", no_assignee=True, dry_run=args.dry_run
764
+ )
765
+ issues_owned = fetch_owner_open_issues(
766
+ user, label=None, no_assignee=False, dry_run=args.dry_run
767
+ )
768
+
769
+ forks = [r for r in repos if r.get("isFork") and r.get("parent")]
770
+ fork_compares: list[dict[str, Any]] = []
771
+ if forks and not args.dry_run:
772
+ with ThreadPoolExecutor(max_workers=PARALLEL_FORK_COMPARES) as pool:
773
+ futures = [pool.submit(fetch_fork_compare, fork) for fork in forks]
774
+ for future in as_completed(futures):
775
+ try:
776
+ fork_compares.append(future.result())
777
+ except Exception as exc:
778
+ fork_compares.append(
779
+ {
780
+ "fork": None,
781
+ "fork_url": None,
782
+ "parent": None,
783
+ "behind": None,
784
+ "ahead": None,
785
+ "error": f"Fork compare task failed: {exc}",
786
+ }
787
+ )
788
+ elif args.dry_run:
789
+ for fork in forks:
790
+ print(
791
+ f"+ gh api /repos/<parent>/compare/...{fork.get('nameWithOwner')}",
792
+ file=sys.stderr,
793
+ )
794
+
795
+ plan = bucketize(
796
+ user=user,
797
+ since_iso=since_iso,
798
+ repos=repos,
799
+ open_prs=open_prs,
800
+ contributions=contributions,
801
+ issues_good_first=issues_good_first,
802
+ issues_help_wanted=issues_help_wanted,
803
+ issues_owned=issues_owned,
804
+ fork_compares=fork_compares,
805
+ stale_issue_days=args.stale_issue_days,
806
+ max_per_bucket=args.max_per_bucket,
807
+ )
808
+
809
+ if args.json:
810
+ render_json(plan, user=user, since_iso=since_iso)
811
+ else:
812
+ render_markdown(plan, user=user, since_iso=since_iso)
813
+
814
+ return 0
815
+
816
+
817
+ if __name__ == "__main__":
818
+ raise SystemExit(main())