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