oh-my-opencode 4.4.0 → 4.5.1

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 (218) hide show
  1. package/.agents/command/get-unpublished-changes.md +148 -0
  2. package/.agents/command/omomomo.md +37 -0
  3. package/.agents/command/publish.md +376 -0
  4. package/.agents/command/remove-deadcode.md +221 -0
  5. package/.agents/command/security-research.md +16 -0
  6. package/.agents/skills/get-unpublished-changes/SKILL.md +24 -0
  7. package/.agents/skills/github-triage/SKILL.md +587 -0
  8. package/.agents/skills/github-triage/scripts/gh_fetch.py +398 -0
  9. package/.agents/skills/hyperplan/SKILL.md +450 -0
  10. package/.agents/skills/omomomo/SKILL.md +36 -0
  11. package/.agents/skills/pre-publish-review/SKILL.md +407 -0
  12. package/.agents/skills/publish/SKILL.md +428 -0
  13. package/.agents/skills/remove-deadcode/SKILL.md +216 -0
  14. package/.agents/skills/security-research/SKILL.md +204 -0
  15. package/.agents/skills/work-with-pr/SKILL.md +360 -0
  16. package/.agents/skills/work-with-pr-workspace/evals/evals.json +76 -0
  17. package/.agents/skills/work-with-pr-workspace/iteration-1/benchmark.json +138 -0
  18. package/.agents/skills/work-with-pr-workspace/iteration-1/benchmark.md +42 -0
  19. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-1/eval_metadata.json +57 -0
  20. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-1/with_skill/grading.json +15 -0
  21. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-1/with_skill/outputs/code-changes.md +454 -0
  22. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-1/with_skill/outputs/execution-plan.md +136 -0
  23. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-1/with_skill/outputs/pr-description.md +47 -0
  24. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-1/with_skill/outputs/verification-strategy.md +163 -0
  25. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-1/with_skill/timing.json +1 -0
  26. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-1/without_skill/grading.json +15 -0
  27. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-1/without_skill/outputs/code-changes.md +615 -0
  28. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-1/without_skill/outputs/execution-plan.md +99 -0
  29. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-1/without_skill/outputs/pr-description.md +50 -0
  30. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-1/without_skill/outputs/verification-strategy.md +111 -0
  31. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-1/without_skill/timing.json +1 -0
  32. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-2/eval_metadata.json +37 -0
  33. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-2/with_skill/grading.json +11 -0
  34. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-2/with_skill/outputs/code-changes.md +205 -0
  35. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-2/with_skill/outputs/execution-plan.md +78 -0
  36. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-2/with_skill/outputs/pr-description.md +42 -0
  37. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-2/with_skill/outputs/verification-strategy.md +87 -0
  38. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-2/with_skill/timing.json +1 -0
  39. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-2/without_skill/grading.json +11 -0
  40. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-2/without_skill/outputs/code-changes.md +334 -0
  41. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-2/without_skill/outputs/execution-plan.md +86 -0
  42. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-2/without_skill/outputs/pr-description.md +23 -0
  43. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-2/without_skill/outputs/verification-strategy.md +119 -0
  44. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-2/without_skill/timing.json +1 -0
  45. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-3/eval_metadata.json +32 -0
  46. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-3/with_skill/grading.json +10 -0
  47. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-3/with_skill/outputs/code-changes.md +221 -0
  48. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-3/with_skill/outputs/execution-plan.md +104 -0
  49. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-3/with_skill/outputs/pr-description.md +41 -0
  50. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-3/with_skill/outputs/verification-strategy.md +84 -0
  51. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-3/with_skill/timing.json +1 -0
  52. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-3/without_skill/grading.json +10 -0
  53. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-3/without_skill/outputs/code-changes.md +342 -0
  54. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-3/without_skill/outputs/execution-plan.md +131 -0
  55. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-3/without_skill/outputs/pr-description.md +39 -0
  56. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-3/without_skill/outputs/verification-strategy.md +128 -0
  57. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-3/without_skill/timing.json +1 -0
  58. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-4/eval_metadata.json +32 -0
  59. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-4/with_skill/grading.json +10 -0
  60. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-4/with_skill/outputs/code-changes.md +143 -0
  61. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-4/with_skill/outputs/execution-plan.md +82 -0
  62. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-4/with_skill/outputs/pr-description.md +51 -0
  63. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-4/with_skill/outputs/verification-strategy.md +69 -0
  64. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-4/with_skill/timing.json +1 -0
  65. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-4/without_skill/grading.json +10 -0
  66. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-4/without_skill/outputs/code-changes.md +252 -0
  67. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-4/without_skill/outputs/execution-plan.md +83 -0
  68. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-4/without_skill/outputs/pr-description.md +33 -0
  69. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-4/without_skill/outputs/verification-strategy.md +101 -0
  70. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-4/without_skill/timing.json +1 -0
  71. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-5/eval_metadata.json +32 -0
  72. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-5/with_skill/grading.json +10 -0
  73. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-5/with_skill/outputs/code-changes.md +387 -0
  74. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-5/with_skill/outputs/execution-plan.md +112 -0
  75. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-5/with_skill/outputs/pr-description.md +51 -0
  76. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-5/with_skill/outputs/verification-strategy.md +75 -0
  77. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-5/with_skill/timing.json +1 -0
  78. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-5/without_skill/grading.json +10 -0
  79. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-5/without_skill/outputs/code-changes.md +529 -0
  80. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-5/without_skill/outputs/execution-plan.md +127 -0
  81. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-5/without_skill/outputs/pr-description.md +42 -0
  82. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-5/without_skill/outputs/verification-strategy.md +120 -0
  83. package/.agents/skills/work-with-pr-workspace/iteration-1/eval-5/without_skill/timing.json +1 -0
  84. package/.agents/skills/work-with-pr-workspace/iteration-1/review.html +1326 -0
  85. package/.opencode/command/get-unpublished-changes.md +148 -0
  86. package/.opencode/command/omomomo.md +37 -0
  87. package/.opencode/command/publish.md +376 -0
  88. package/.opencode/command/remove-deadcode.md +221 -0
  89. package/.opencode/command/security-research.md +16 -0
  90. package/.opencode/skills/github-triage/SKILL.md +587 -0
  91. package/.opencode/skills/github-triage/scripts/gh_fetch.py +398 -0
  92. package/.opencode/skills/hyperplan/SKILL.md +450 -0
  93. package/.opencode/skills/pre-publish-review/SKILL.md +407 -0
  94. package/.opencode/skills/work-with-pr/SKILL.md +360 -0
  95. package/.opencode/skills/work-with-pr-workspace/evals/evals.json +76 -0
  96. package/.opencode/skills/work-with-pr-workspace/iteration-1/benchmark.json +138 -0
  97. package/.opencode/skills/work-with-pr-workspace/iteration-1/benchmark.md +42 -0
  98. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-1/eval_metadata.json +57 -0
  99. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-1/with_skill/grading.json +15 -0
  100. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-1/with_skill/outputs/code-changes.md +454 -0
  101. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-1/with_skill/outputs/execution-plan.md +136 -0
  102. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-1/with_skill/outputs/pr-description.md +47 -0
  103. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-1/with_skill/outputs/verification-strategy.md +163 -0
  104. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-1/with_skill/timing.json +1 -0
  105. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-1/without_skill/grading.json +15 -0
  106. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-1/without_skill/outputs/code-changes.md +615 -0
  107. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-1/without_skill/outputs/execution-plan.md +99 -0
  108. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-1/without_skill/outputs/pr-description.md +50 -0
  109. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-1/without_skill/outputs/verification-strategy.md +111 -0
  110. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-1/without_skill/timing.json +1 -0
  111. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-2/eval_metadata.json +37 -0
  112. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-2/with_skill/grading.json +11 -0
  113. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-2/with_skill/outputs/code-changes.md +205 -0
  114. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-2/with_skill/outputs/execution-plan.md +78 -0
  115. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-2/with_skill/outputs/pr-description.md +42 -0
  116. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-2/with_skill/outputs/verification-strategy.md +87 -0
  117. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-2/with_skill/timing.json +1 -0
  118. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-2/without_skill/grading.json +11 -0
  119. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-2/without_skill/outputs/code-changes.md +334 -0
  120. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-2/without_skill/outputs/execution-plan.md +86 -0
  121. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-2/without_skill/outputs/pr-description.md +23 -0
  122. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-2/without_skill/outputs/verification-strategy.md +119 -0
  123. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-2/without_skill/timing.json +1 -0
  124. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-3/eval_metadata.json +32 -0
  125. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-3/with_skill/grading.json +10 -0
  126. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-3/with_skill/outputs/code-changes.md +221 -0
  127. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-3/with_skill/outputs/execution-plan.md +104 -0
  128. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-3/with_skill/outputs/pr-description.md +41 -0
  129. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-3/with_skill/outputs/verification-strategy.md +84 -0
  130. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-3/with_skill/timing.json +1 -0
  131. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-3/without_skill/grading.json +10 -0
  132. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-3/without_skill/outputs/code-changes.md +342 -0
  133. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-3/without_skill/outputs/execution-plan.md +131 -0
  134. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-3/without_skill/outputs/pr-description.md +39 -0
  135. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-3/without_skill/outputs/verification-strategy.md +128 -0
  136. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-3/without_skill/timing.json +1 -0
  137. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-4/eval_metadata.json +32 -0
  138. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-4/with_skill/grading.json +10 -0
  139. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-4/with_skill/outputs/code-changes.md +143 -0
  140. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-4/with_skill/outputs/execution-plan.md +82 -0
  141. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-4/with_skill/outputs/pr-description.md +51 -0
  142. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-4/with_skill/outputs/verification-strategy.md +69 -0
  143. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-4/with_skill/timing.json +1 -0
  144. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-4/without_skill/grading.json +10 -0
  145. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-4/without_skill/outputs/code-changes.md +252 -0
  146. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-4/without_skill/outputs/execution-plan.md +83 -0
  147. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-4/without_skill/outputs/pr-description.md +33 -0
  148. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-4/without_skill/outputs/verification-strategy.md +101 -0
  149. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-4/without_skill/timing.json +1 -0
  150. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-5/eval_metadata.json +32 -0
  151. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-5/with_skill/grading.json +10 -0
  152. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-5/with_skill/outputs/code-changes.md +387 -0
  153. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-5/with_skill/outputs/execution-plan.md +112 -0
  154. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-5/with_skill/outputs/pr-description.md +51 -0
  155. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-5/with_skill/outputs/verification-strategy.md +75 -0
  156. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-5/with_skill/timing.json +1 -0
  157. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-5/without_skill/grading.json +10 -0
  158. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-5/without_skill/outputs/code-changes.md +529 -0
  159. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-5/without_skill/outputs/execution-plan.md +127 -0
  160. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-5/without_skill/outputs/pr-description.md +42 -0
  161. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-5/without_skill/outputs/verification-strategy.md +120 -0
  162. package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-5/without_skill/timing.json +1 -0
  163. package/.opencode/skills/work-with-pr-workspace/iteration-1/review.html +1326 -0
  164. package/README.ja.md +1 -1
  165. package/README.ko.md +1 -1
  166. package/README.md +1 -1
  167. package/README.ru.md +1 -1
  168. package/README.zh-cn.md +1 -1
  169. package/dist/agents/atlas/agent.d.ts +6 -6
  170. package/dist/agents/prometheus/gemini.d.ts +0 -11
  171. package/dist/agents/prometheus/gpt.d.ts +0 -10
  172. package/dist/agents/prometheus/system-prompt.d.ts +2 -20
  173. package/dist/agents/types.d.ts +1 -16
  174. package/dist/cli/index.js +178 -129
  175. package/dist/config/schema/agent-names.d.ts +3 -3
  176. package/dist/config/schema/agent-overrides.d.ts +208 -208
  177. package/dist/config/schema/categories.d.ts +28 -28
  178. package/dist/config/schema/fallback-models.d.ts +20 -20
  179. package/dist/config/schema/oh-my-opencode-config.d.ts +208 -208
  180. package/dist/features/background-agent/parent-wake-notifier.d.ts +8 -1
  181. package/dist/help/schema/acp.d.ts +95 -0
  182. package/dist/help/schema/doctor.d.ts +147 -0
  183. package/dist/help/schema/sandbox.d.ts +74 -0
  184. package/dist/help/schema/status.d.ts +139 -0
  185. package/dist/hooks/keyword-detector/analyze/default.d.ts +1 -1
  186. package/dist/hooks/keyword-detector/hyperplan/default.d.ts +1 -1
  187. package/dist/hooks/keyword-detector/search/default.d.ts +1 -1
  188. package/dist/hooks/keyword-detector/team/default.d.ts +2 -7
  189. package/dist/hooks/keyword-detector/ultrawork/default.d.ts +1 -9
  190. package/dist/hooks/keyword-detector/ultrawork/gemini.d.ts +1 -16
  191. package/dist/hooks/keyword-detector/ultrawork/gpt.d.ts +1 -10
  192. package/dist/hooks/keyword-detector/ultrawork/planner.d.ts +1 -5
  193. package/dist/hooks/ralph-loop/no-progress-turn-detector.d.ts +7 -0
  194. package/dist/hooks/ralph-loop/pending-verification-handler.d.ts +1 -0
  195. package/dist/hooks/ralph-loop/types.d.ts +1 -0
  196. package/dist/hooks/runtime-fallback/error-classifier.d.ts +1 -0
  197. package/dist/index.js +52205 -50528
  198. package/dist/shared/prompt-async-gate/pending-tool-turn.d.ts +1 -0
  199. package/dist/shared/prompt-async-gate/types.d.ts +4 -3
  200. package/package.json +19 -13
  201. package/dist/agents/atlas/default-prompt-sections.d.ts +0 -6
  202. package/dist/agents/atlas/default.d.ts +0 -2
  203. package/dist/agents/atlas/gemini-prompt-sections.d.ts +0 -6
  204. package/dist/agents/atlas/gemini.d.ts +0 -2
  205. package/dist/agents/atlas/gpt-prompt-sections.d.ts +0 -6
  206. package/dist/agents/atlas/gpt.d.ts +0 -2
  207. package/dist/agents/atlas/kimi-prompt-sections.d.ts +0 -6
  208. package/dist/agents/atlas/kimi.d.ts +0 -2
  209. package/dist/agents/atlas/opus-4-7-prompt-sections.d.ts +0 -6
  210. package/dist/agents/atlas/opus-4-7.d.ts +0 -2
  211. package/dist/agents/atlas/shared-prompt.d.ts +0 -9
  212. package/dist/agents/prometheus/behavioral-summary.d.ts +0 -6
  213. package/dist/agents/prometheus/high-accuracy-mode.d.ts +0 -6
  214. package/dist/agents/prometheus/identity-constraints.d.ts +0 -7
  215. package/dist/agents/prometheus/interview-mode.d.ts +0 -7
  216. package/dist/agents/prometheus/plan-generation.d.ts +0 -7
  217. package/dist/agents/prometheus/plan-template.d.ts +0 -7
  218. package/dist/agents/prometheus/spec-driven-mode.d.ts +0 -7
@@ -0,0 +1,398 @@
1
+ #!/usr/bin/env -S uv run --script
2
+ # /// script
3
+ # requires-python = ">=3.11"
4
+ # dependencies = [
5
+ # "typer>=0.12.0",
6
+ # "rich>=13.0.0",
7
+ # ]
8
+ # ///
9
+ """
10
+ GitHub Issues/PRs Fetcher with Exhaustive Pagination.
11
+
12
+ Fetches ALL issues and/or PRs from a GitHub repository using gh CLI.
13
+ Implements proper pagination to ensure no items are missed.
14
+
15
+ Usage:
16
+ ./gh_fetch.py issues # Fetch all issues
17
+ ./gh_fetch.py prs # Fetch all PRs
18
+ ./gh_fetch.py all # Fetch both issues and PRs
19
+ ./gh_fetch.py issues --hours 48 # Issues from last 48 hours
20
+ ./gh_fetch.py prs --state open # Only open PRs
21
+ ./gh_fetch.py all --repo owner/repo # Specify repository
22
+ """
23
+
24
+ import asyncio
25
+ import json
26
+ from datetime import UTC, datetime, timedelta
27
+ from enum import Enum
28
+ from typing import Annotated
29
+
30
+ import typer
31
+ from rich.console import Console
32
+ from rich.panel import Panel
33
+ from rich.progress import Progress, TaskID
34
+ from rich.table import Table
35
+
36
+ app = typer.Typer(
37
+ name="gh_fetch",
38
+ help="Fetch GitHub issues/PRs with exhaustive pagination.",
39
+ no_args_is_help=True,
40
+ )
41
+ console = Console()
42
+
43
+ BATCH_SIZE = 500 # Maximum allowed by GitHub API
44
+
45
+
46
+ class ItemState(str, Enum):
47
+ ALL = "all"
48
+ OPEN = "open"
49
+ CLOSED = "closed"
50
+
51
+
52
+ class OutputFormat(str, Enum):
53
+ JSON = "json"
54
+ TABLE = "table"
55
+ COUNT = "count"
56
+
57
+
58
+ async def run_gh_command(args: list[str]) -> tuple[str, str, int]:
59
+ """Run gh CLI command asynchronously."""
60
+ proc = await asyncio.create_subprocess_exec(
61
+ "gh",
62
+ *args,
63
+ stdout=asyncio.subprocess.PIPE,
64
+ stderr=asyncio.subprocess.PIPE,
65
+ )
66
+ stdout, stderr = await proc.communicate()
67
+ return stdout.decode(), stderr.decode(), proc.returncode or 0
68
+
69
+
70
+ async def get_current_repo() -> str:
71
+ """Get the current repository from gh CLI."""
72
+ stdout, stderr, code = await run_gh_command(
73
+ ["repo", "view", "--json", "nameWithOwner", "-q", ".nameWithOwner"]
74
+ )
75
+ if code != 0:
76
+ console.print(f"[red]Error getting current repo: {stderr}[/red]")
77
+ raise typer.Exit(1)
78
+ return stdout.strip()
79
+
80
+
81
+ async def fetch_items_page(
82
+ repo: str,
83
+ item_type: str, # "issue" or "pr"
84
+ state: str,
85
+ limit: int,
86
+ search_filter: str = "",
87
+ ) -> list[dict]:
88
+ """Fetch a single page of issues or PRs."""
89
+ cmd = [
90
+ item_type,
91
+ "list",
92
+ "--repo",
93
+ repo,
94
+ "--state",
95
+ state,
96
+ "--limit",
97
+ str(limit),
98
+ "--json",
99
+ "number,title,state,createdAt,updatedAt,labels,author,body",
100
+ ]
101
+ if search_filter:
102
+ cmd.extend(["--search", search_filter])
103
+
104
+ stdout, stderr, code = await run_gh_command(cmd)
105
+ if code != 0:
106
+ console.print(f"[red]Error fetching {item_type}s: {stderr}[/red]")
107
+ return []
108
+
109
+ try:
110
+ return json.loads(stdout) if stdout.strip() else []
111
+ except json.JSONDecodeError:
112
+ console.print(f"[red]Error parsing {item_type} response[/red]")
113
+ return []
114
+
115
+
116
+ async def fetch_all_items(
117
+ repo: str,
118
+ item_type: str,
119
+ state: str,
120
+ hours: int | None,
121
+ progress: Progress,
122
+ task_id: TaskID,
123
+ ) -> list[dict]:
124
+ """Fetch ALL items with exhaustive pagination."""
125
+ all_items: list[dict] = []
126
+ page = 1
127
+
128
+ progress.update(task_id, description=f"[cyan]Fetching {item_type}s page {page}...")
129
+ items = await fetch_items_page(repo, item_type, state, BATCH_SIZE)
130
+ fetched_count = len(items)
131
+ all_items.extend(items)
132
+
133
+ console.print(f"[dim]Page {page}: fetched {fetched_count} {item_type}s[/dim]")
134
+
135
+ while fetched_count == BATCH_SIZE:
136
+ page += 1
137
+ progress.update(
138
+ task_id, description=f"[cyan]Fetching {item_type}s page {page}..."
139
+ )
140
+
141
+ last_created = all_items[-1].get("createdAt", "")
142
+ if not last_created:
143
+ break
144
+
145
+ search_filter = f"created:<{last_created}"
146
+ items = await fetch_items_page(
147
+ repo, item_type, state, BATCH_SIZE, search_filter
148
+ )
149
+ fetched_count = len(items)
150
+
151
+ if fetched_count == 0:
152
+ break
153
+
154
+ existing_numbers = {item["number"] for item in all_items}
155
+ new_items = [item for item in items if item["number"] not in existing_numbers]
156
+ all_items.extend(new_items)
157
+
158
+ console.print(
159
+ f"[dim]Page {page}: fetched {fetched_count}, added {len(new_items)} new (total: {len(all_items)})[/dim]"
160
+ )
161
+
162
+ if page > 20:
163
+ console.print("[yellow]Safety limit reached (20 pages)[/yellow]")
164
+ break
165
+
166
+ if hours is not None:
167
+ cutoff = datetime.now(UTC) - timedelta(hours=hours)
168
+ cutoff_str = cutoff.isoformat()
169
+
170
+ original_count = len(all_items)
171
+ all_items = [
172
+ item
173
+ for item in all_items
174
+ if item.get("createdAt", "") >= cutoff_str
175
+ or item.get("updatedAt", "") >= cutoff_str
176
+ ]
177
+ filtered_count = original_count - len(all_items)
178
+ if filtered_count > 0:
179
+ console.print(
180
+ f"[dim]Filtered out {filtered_count} items older than {hours} hours[/dim]"
181
+ )
182
+
183
+ return all_items
184
+
185
+
186
+ def display_table(items: list[dict], item_type: str) -> None:
187
+ """Display items in a Rich table."""
188
+ table = Table(title=f"{item_type.upper()}s ({len(items)} total)")
189
+ table.add_column("#", style="cyan", width=6)
190
+ table.add_column("Title", style="white", max_width=50)
191
+ table.add_column("State", style="green", width=8)
192
+ table.add_column("Author", style="yellow", width=15)
193
+ table.add_column("Labels", style="magenta", max_width=30)
194
+ table.add_column("Updated", style="dim", width=12)
195
+
196
+ for item in items[:50]:
197
+ labels = ", ".join(label.get("name", "") for label in item.get("labels", []))
198
+ updated = item.get("updatedAt", "")[:10]
199
+ author = item.get("author", {}).get("login", "unknown")
200
+
201
+ table.add_row(
202
+ str(item.get("number", "")),
203
+ (item.get("title", "")[:47] + "...")
204
+ if len(item.get("title", "")) > 50
205
+ else item.get("title", ""),
206
+ item.get("state", ""),
207
+ author,
208
+ (labels[:27] + "...") if len(labels) > 30 else labels,
209
+ updated,
210
+ )
211
+
212
+ console.print(table)
213
+ if len(items) > 50:
214
+ console.print(f"[dim]... and {len(items) - 50} more items[/dim]")
215
+
216
+
217
+ @app.command()
218
+ def issues(
219
+ repo: Annotated[
220
+ str | None, typer.Option("--repo", "-r", help="Repository (owner/repo)")
221
+ ] = None,
222
+ state: Annotated[
223
+ ItemState, typer.Option("--state", "-s", help="Issue state filter")
224
+ ] = ItemState.ALL,
225
+ hours: Annotated[
226
+ int | None,
227
+ typer.Option(
228
+ "--hours", "-h", help="Only issues from last N hours (created or updated)"
229
+ ),
230
+ ] = None,
231
+ output: Annotated[
232
+ OutputFormat, typer.Option("--output", "-o", help="Output format")
233
+ ] = OutputFormat.TABLE,
234
+ ) -> None:
235
+ """Fetch all issues with exhaustive pagination."""
236
+
237
+ async def async_main() -> None:
238
+ target_repo = repo or await get_current_repo()
239
+
240
+ console.print(f"""
241
+ [cyan]Repository:[/cyan] {target_repo}
242
+ [cyan]State:[/cyan] {state.value}
243
+ [cyan]Time filter:[/cyan] {f"Last {hours} hours" if hours else "All time"}
244
+ """)
245
+
246
+ with Progress(console=console) as progress:
247
+ task: TaskID = progress.add_task("[cyan]Fetching issues...", total=None)
248
+ items = await fetch_all_items(
249
+ target_repo, "issue", state.value, hours, progress, task
250
+ )
251
+ progress.update(
252
+ task, description="[green]Complete!", completed=100, total=100
253
+ )
254
+
255
+ console.print(
256
+ Panel(f"[green]Found {len(items)} issues[/green]", border_style="green")
257
+ )
258
+
259
+ if output == OutputFormat.JSON:
260
+ console.print(json.dumps(items, indent=2, ensure_ascii=False))
261
+ elif output == OutputFormat.TABLE:
262
+ display_table(items, "issue")
263
+ else:
264
+ console.print(f"Total issues: {len(items)}")
265
+
266
+ asyncio.run(async_main())
267
+
268
+
269
+ @app.command()
270
+ def prs(
271
+ repo: Annotated[
272
+ str | None, typer.Option("--repo", "-r", help="Repository (owner/repo)")
273
+ ] = None,
274
+ state: Annotated[
275
+ ItemState, typer.Option("--state", "-s", help="PR state filter")
276
+ ] = ItemState.OPEN,
277
+ hours: Annotated[
278
+ int | None,
279
+ typer.Option(
280
+ "--hours", "-h", help="Only PRs from last N hours (created or updated)"
281
+ ),
282
+ ] = None,
283
+ output: Annotated[
284
+ OutputFormat, typer.Option("--output", "-o", help="Output format")
285
+ ] = OutputFormat.TABLE,
286
+ ) -> None:
287
+ """Fetch all PRs with exhaustive pagination."""
288
+
289
+ async def async_main() -> None:
290
+ target_repo = repo or await get_current_repo()
291
+
292
+ console.print(f"""
293
+ [cyan]Repository:[/cyan] {target_repo}
294
+ [cyan]State:[/cyan] {state.value}
295
+ [cyan]Time filter:[/cyan] {f"Last {hours} hours" if hours else "All time"}
296
+ """)
297
+
298
+ with Progress(console=console) as progress:
299
+ task: TaskID = progress.add_task("[cyan]Fetching PRs...", total=None)
300
+ items = await fetch_all_items(
301
+ target_repo, "pr", state.value, hours, progress, task
302
+ )
303
+ progress.update(
304
+ task, description="[green]Complete!", completed=100, total=100
305
+ )
306
+
307
+ console.print(
308
+ Panel(f"[green]Found {len(items)} PRs[/green]", border_style="green")
309
+ )
310
+
311
+ if output == OutputFormat.JSON:
312
+ console.print(json.dumps(items, indent=2, ensure_ascii=False))
313
+ elif output == OutputFormat.TABLE:
314
+ display_table(items, "pr")
315
+ else:
316
+ console.print(f"Total PRs: {len(items)}")
317
+
318
+ asyncio.run(async_main())
319
+
320
+
321
+ @app.command(name="all")
322
+ def fetch_all(
323
+ repo: Annotated[
324
+ str | None, typer.Option("--repo", "-r", help="Repository (owner/repo)")
325
+ ] = None,
326
+ state: Annotated[
327
+ ItemState, typer.Option("--state", "-s", help="State filter")
328
+ ] = ItemState.ALL,
329
+ hours: Annotated[
330
+ int | None,
331
+ typer.Option(
332
+ "--hours", "-h", help="Only items from last N hours (created or updated)"
333
+ ),
334
+ ] = None,
335
+ output: Annotated[
336
+ OutputFormat, typer.Option("--output", "-o", help="Output format")
337
+ ] = OutputFormat.TABLE,
338
+ ) -> None:
339
+ """Fetch all issues AND PRs with exhaustive pagination."""
340
+
341
+ async def async_main() -> None:
342
+ target_repo = repo or await get_current_repo()
343
+
344
+ console.print(f"""
345
+ [cyan]Repository:[/cyan] {target_repo}
346
+ [cyan]State:[/cyan] {state.value}
347
+ [cyan]Time filter:[/cyan] {f"Last {hours} hours" if hours else "All time"}
348
+ [cyan]Fetching:[/cyan] Issues AND PRs
349
+ """)
350
+
351
+ with Progress(console=console) as progress:
352
+ issues_task: TaskID = progress.add_task(
353
+ "[cyan]Fetching issues...", total=None
354
+ )
355
+ prs_task: TaskID = progress.add_task("[cyan]Fetching PRs...", total=None)
356
+
357
+ issues_items, prs_items = await asyncio.gather(
358
+ fetch_all_items(
359
+ target_repo, "issue", state.value, hours, progress, issues_task
360
+ ),
361
+ fetch_all_items(
362
+ target_repo, "pr", state.value, hours, progress, prs_task
363
+ ),
364
+ )
365
+
366
+ progress.update(
367
+ issues_task,
368
+ description="[green]Issues complete!",
369
+ completed=100,
370
+ total=100,
371
+ )
372
+ progress.update(
373
+ prs_task, description="[green]PRs complete!", completed=100, total=100
374
+ )
375
+
376
+ console.print(
377
+ Panel(
378
+ f"[green]Found {len(issues_items)} issues and {len(prs_items)} PRs[/green]",
379
+ border_style="green",
380
+ )
381
+ )
382
+
383
+ if output == OutputFormat.JSON:
384
+ result = {"issues": issues_items, "prs": prs_items}
385
+ console.print(json.dumps(result, indent=2, ensure_ascii=False))
386
+ elif output == OutputFormat.TABLE:
387
+ display_table(issues_items, "issue")
388
+ console.print("")
389
+ display_table(prs_items, "pr")
390
+ else:
391
+ console.print(f"Total issues: {len(issues_items)}")
392
+ console.print(f"Total PRs: {len(prs_items)}")
393
+
394
+ asyncio.run(async_main())
395
+
396
+
397
+ if __name__ == "__main__":
398
+ app()