erk 0.4.5__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 (331) hide show
  1. erk/__init__.py +12 -0
  2. erk/__main__.py +6 -0
  3. erk/agent_docs/__init__.py +5 -0
  4. erk/agent_docs/models.py +123 -0
  5. erk/agent_docs/operations.py +666 -0
  6. erk/artifacts/__init__.py +5 -0
  7. erk/artifacts/artifact_health.py +623 -0
  8. erk/artifacts/detection.py +16 -0
  9. erk/artifacts/discovery.py +343 -0
  10. erk/artifacts/models.py +63 -0
  11. erk/artifacts/staleness.py +56 -0
  12. erk/artifacts/state.py +100 -0
  13. erk/artifacts/sync.py +624 -0
  14. erk/cli/__init__.py +0 -0
  15. erk/cli/activation.py +132 -0
  16. erk/cli/alias.py +53 -0
  17. erk/cli/cli.py +221 -0
  18. erk/cli/commands/__init__.py +0 -0
  19. erk/cli/commands/admin.py +153 -0
  20. erk/cli/commands/artifact/__init__.py +1 -0
  21. erk/cli/commands/artifact/check.py +260 -0
  22. erk/cli/commands/artifact/group.py +31 -0
  23. erk/cli/commands/artifact/list_cmd.py +89 -0
  24. erk/cli/commands/artifact/show.py +62 -0
  25. erk/cli/commands/artifact/sync_cmd.py +39 -0
  26. erk/cli/commands/branch/__init__.py +26 -0
  27. erk/cli/commands/branch/assign_cmd.py +152 -0
  28. erk/cli/commands/branch/checkout_cmd.py +357 -0
  29. erk/cli/commands/branch/create_cmd.py +161 -0
  30. erk/cli/commands/branch/list_cmd.py +82 -0
  31. erk/cli/commands/branch/unassign_cmd.py +197 -0
  32. erk/cli/commands/cc/__init__.py +15 -0
  33. erk/cli/commands/cc/jsonl_cmd.py +20 -0
  34. erk/cli/commands/cc/session/AGENTS.md +30 -0
  35. erk/cli/commands/cc/session/CLAUDE.md +1 -0
  36. erk/cli/commands/cc/session/__init__.py +15 -0
  37. erk/cli/commands/cc/session/list_cmd.py +167 -0
  38. erk/cli/commands/cc/session/show_cmd.py +175 -0
  39. erk/cli/commands/completion.py +89 -0
  40. erk/cli/commands/completions.py +165 -0
  41. erk/cli/commands/config.py +327 -0
  42. erk/cli/commands/docs/__init__.py +1 -0
  43. erk/cli/commands/docs/group.py +16 -0
  44. erk/cli/commands/docs/sync.py +121 -0
  45. erk/cli/commands/docs/validate.py +102 -0
  46. erk/cli/commands/doctor.py +243 -0
  47. erk/cli/commands/down.py +171 -0
  48. erk/cli/commands/exec/__init__.py +1 -0
  49. erk/cli/commands/exec/group.py +164 -0
  50. erk/cli/commands/exec/scripts/AGENTS.md +79 -0
  51. erk/cli/commands/exec/scripts/CLAUDE.md +1 -0
  52. erk/cli/commands/exec/scripts/__init__.py +5 -0
  53. erk/cli/commands/exec/scripts/add_reaction_to_comment.py +69 -0
  54. erk/cli/commands/exec/scripts/add_remote_execution_note.py +68 -0
  55. erk/cli/commands/exec/scripts/check_impl.py +152 -0
  56. erk/cli/commands/exec/scripts/ci_update_pr_body.py +294 -0
  57. erk/cli/commands/exec/scripts/create_extraction_branch.py +138 -0
  58. erk/cli/commands/exec/scripts/create_extraction_plan.py +242 -0
  59. erk/cli/commands/exec/scripts/create_issue_from_session.py +103 -0
  60. erk/cli/commands/exec/scripts/create_plan_from_context.py +103 -0
  61. erk/cli/commands/exec/scripts/create_worker_impl_from_issue.py +93 -0
  62. erk/cli/commands/exec/scripts/detect_trunk_branch.py +121 -0
  63. erk/cli/commands/exec/scripts/exit_plan_mode_hook.py +777 -0
  64. erk/cli/commands/exec/scripts/extract_latest_plan.py +49 -0
  65. erk/cli/commands/exec/scripts/extract_session_from_issue.py +150 -0
  66. erk/cli/commands/exec/scripts/find_project_dir.py +214 -0
  67. erk/cli/commands/exec/scripts/generate_pr_summary.py +112 -0
  68. erk/cli/commands/exec/scripts/get_closing_text.py +98 -0
  69. erk/cli/commands/exec/scripts/get_embedded_prompt.py +62 -0
  70. erk/cli/commands/exec/scripts/get_plan_metadata.py +95 -0
  71. erk/cli/commands/exec/scripts/get_pr_body_footer.py +70 -0
  72. erk/cli/commands/exec/scripts/get_pr_discussion_comments.py +149 -0
  73. erk/cli/commands/exec/scripts/get_pr_review_comments.py +155 -0
  74. erk/cli/commands/exec/scripts/impl_init.py +158 -0
  75. erk/cli/commands/exec/scripts/impl_signal.py +375 -0
  76. erk/cli/commands/exec/scripts/impl_verify.py +49 -0
  77. erk/cli/commands/exec/scripts/issue_title_to_filename.py +34 -0
  78. erk/cli/commands/exec/scripts/list_sessions.py +296 -0
  79. erk/cli/commands/exec/scripts/mark_impl_ended.py +188 -0
  80. erk/cli/commands/exec/scripts/mark_impl_started.py +188 -0
  81. erk/cli/commands/exec/scripts/marker.py +163 -0
  82. erk/cli/commands/exec/scripts/objective_save_to_issue.py +109 -0
  83. erk/cli/commands/exec/scripts/plan_save_to_issue.py +269 -0
  84. erk/cli/commands/exec/scripts/plan_update_issue.py +147 -0
  85. erk/cli/commands/exec/scripts/post_extraction_comment.py +237 -0
  86. erk/cli/commands/exec/scripts/post_or_update_pr_summary.py +133 -0
  87. erk/cli/commands/exec/scripts/post_pr_inline_comment.py +143 -0
  88. erk/cli/commands/exec/scripts/post_workflow_started_comment.py +168 -0
  89. erk/cli/commands/exec/scripts/preprocess_session.py +777 -0
  90. erk/cli/commands/exec/scripts/quick_submit.py +32 -0
  91. erk/cli/commands/exec/scripts/rebase_with_conflict_resolution.py +260 -0
  92. erk/cli/commands/exec/scripts/reply_to_discussion_comment.py +173 -0
  93. erk/cli/commands/exec/scripts/resolve_review_thread.py +170 -0
  94. erk/cli/commands/exec/scripts/session_id_injector_hook.py +52 -0
  95. erk/cli/commands/exec/scripts/setup_impl_from_issue.py +159 -0
  96. erk/cli/commands/exec/scripts/slot_objective.py +102 -0
  97. erk/cli/commands/exec/scripts/tripwires_reminder_hook.py +20 -0
  98. erk/cli/commands/exec/scripts/update_dispatch_info.py +116 -0
  99. erk/cli/commands/exec/scripts/user_prompt_hook.py +113 -0
  100. erk/cli/commands/exec/scripts/validate_plan_content.py +98 -0
  101. erk/cli/commands/exec/scripts/wrap_plan_in_metadata_block.py +34 -0
  102. erk/cli/commands/implement.py +695 -0
  103. erk/cli/commands/implement_shared.py +649 -0
  104. erk/cli/commands/info/__init__.py +14 -0
  105. erk/cli/commands/info/release_notes_cmd.py +128 -0
  106. erk/cli/commands/init.py +801 -0
  107. erk/cli/commands/land_cmd.py +690 -0
  108. erk/cli/commands/log_cmd.py +137 -0
  109. erk/cli/commands/md/__init__.py +5 -0
  110. erk/cli/commands/md/check.py +118 -0
  111. erk/cli/commands/md/group.py +14 -0
  112. erk/cli/commands/navigation_helpers.py +430 -0
  113. erk/cli/commands/objective/__init__.py +16 -0
  114. erk/cli/commands/objective/list_cmd.py +47 -0
  115. erk/cli/commands/objective_helpers.py +132 -0
  116. erk/cli/commands/plan/__init__.py +32 -0
  117. erk/cli/commands/plan/check_cmd.py +174 -0
  118. erk/cli/commands/plan/close_cmd.py +69 -0
  119. erk/cli/commands/plan/create_cmd.py +120 -0
  120. erk/cli/commands/plan/docs/__init__.py +18 -0
  121. erk/cli/commands/plan/docs/extract_cmd.py +53 -0
  122. erk/cli/commands/plan/docs/unextract_cmd.py +38 -0
  123. erk/cli/commands/plan/docs/unextracted_cmd.py +72 -0
  124. erk/cli/commands/plan/extraction/__init__.py +16 -0
  125. erk/cli/commands/plan/extraction/complete_cmd.py +101 -0
  126. erk/cli/commands/plan/extraction/create_raw_cmd.py +63 -0
  127. erk/cli/commands/plan/get.py +71 -0
  128. erk/cli/commands/plan/list_cmd.py +754 -0
  129. erk/cli/commands/plan/log_cmd.py +440 -0
  130. erk/cli/commands/plan/start_cmd.py +459 -0
  131. erk/cli/commands/planner/__init__.py +40 -0
  132. erk/cli/commands/planner/configure_cmd.py +73 -0
  133. erk/cli/commands/planner/connect_cmd.py +96 -0
  134. erk/cli/commands/planner/create_cmd.py +148 -0
  135. erk/cli/commands/planner/list_cmd.py +51 -0
  136. erk/cli/commands/planner/register_cmd.py +105 -0
  137. erk/cli/commands/planner/set_default_cmd.py +23 -0
  138. erk/cli/commands/planner/unregister_cmd.py +43 -0
  139. erk/cli/commands/pr/__init__.py +23 -0
  140. erk/cli/commands/pr/check_cmd.py +112 -0
  141. erk/cli/commands/pr/checkout_cmd.py +165 -0
  142. erk/cli/commands/pr/fix_conflicts_cmd.py +82 -0
  143. erk/cli/commands/pr/parse_pr_reference.py +10 -0
  144. erk/cli/commands/pr/submit_cmd.py +360 -0
  145. erk/cli/commands/pr/sync_cmd.py +181 -0
  146. erk/cli/commands/prepare_cwd_recovery.py +60 -0
  147. erk/cli/commands/project/__init__.py +16 -0
  148. erk/cli/commands/project/init_cmd.py +91 -0
  149. erk/cli/commands/run/__init__.py +17 -0
  150. erk/cli/commands/run/list_cmd.py +189 -0
  151. erk/cli/commands/run/logs_cmd.py +54 -0
  152. erk/cli/commands/run/shared.py +19 -0
  153. erk/cli/commands/shell_integration.py +29 -0
  154. erk/cli/commands/slot/__init__.py +23 -0
  155. erk/cli/commands/slot/check_cmd.py +277 -0
  156. erk/cli/commands/slot/common.py +314 -0
  157. erk/cli/commands/slot/init_pool_cmd.py +157 -0
  158. erk/cli/commands/slot/list_cmd.py +228 -0
  159. erk/cli/commands/slot/repair_cmd.py +190 -0
  160. erk/cli/commands/stack/__init__.py +23 -0
  161. erk/cli/commands/stack/consolidate_cmd.py +470 -0
  162. erk/cli/commands/stack/list_cmd.py +79 -0
  163. erk/cli/commands/stack/move_cmd.py +309 -0
  164. erk/cli/commands/stack/split_old/README.md +64 -0
  165. erk/cli/commands/stack/split_old/__init__.py +5 -0
  166. erk/cli/commands/stack/split_old/command.py +233 -0
  167. erk/cli/commands/stack/split_old/display.py +116 -0
  168. erk/cli/commands/stack/split_old/plan.py +216 -0
  169. erk/cli/commands/status.py +58 -0
  170. erk/cli/commands/submit.py +768 -0
  171. erk/cli/commands/up.py +154 -0
  172. erk/cli/commands/upgrade.py +82 -0
  173. erk/cli/commands/wt/__init__.py +29 -0
  174. erk/cli/commands/wt/checkout_cmd.py +110 -0
  175. erk/cli/commands/wt/create_cmd.py +998 -0
  176. erk/cli/commands/wt/current_cmd.py +35 -0
  177. erk/cli/commands/wt/delete_cmd.py +573 -0
  178. erk/cli/commands/wt/list_cmd.py +332 -0
  179. erk/cli/commands/wt/rename_cmd.py +66 -0
  180. erk/cli/config.py +242 -0
  181. erk/cli/constants.py +29 -0
  182. erk/cli/core.py +65 -0
  183. erk/cli/debug.py +9 -0
  184. erk/cli/ensure-conversion-tasks.md +288 -0
  185. erk/cli/ensure.py +628 -0
  186. erk/cli/github_parsing.py +96 -0
  187. erk/cli/graphite.py +81 -0
  188. erk/cli/graphite_command.py +80 -0
  189. erk/cli/help_formatter.py +345 -0
  190. erk/cli/output.py +361 -0
  191. erk/cli/presets/dagster.toml +12 -0
  192. erk/cli/presets/generic.toml +12 -0
  193. erk/cli/prompt_hooks_templates/README.md +68 -0
  194. erk/cli/script_output.py +32 -0
  195. erk/cli/shell_integration/bash_wrapper.sh +32 -0
  196. erk/cli/shell_integration/fish_wrapper.fish +39 -0
  197. erk/cli/shell_integration/handler.py +338 -0
  198. erk/cli/shell_integration/zsh_wrapper.sh +32 -0
  199. erk/cli/shell_utils.py +171 -0
  200. erk/cli/subprocess_utils.py +92 -0
  201. erk/cli/uvx_detection.py +59 -0
  202. erk/core/__init__.py +0 -0
  203. erk/core/claude_executor.py +511 -0
  204. erk/core/claude_settings.py +317 -0
  205. erk/core/command_log.py +406 -0
  206. erk/core/commit_message_generator.py +234 -0
  207. erk/core/completion.py +10 -0
  208. erk/core/consolidation_utils.py +177 -0
  209. erk/core/context.py +570 -0
  210. erk/core/display/__init__.py +4 -0
  211. erk/core/display/abc.py +24 -0
  212. erk/core/display/real.py +30 -0
  213. erk/core/display_utils.py +526 -0
  214. erk/core/file_utils.py +87 -0
  215. erk/core/health_checks.py +1315 -0
  216. erk/core/health_checks_dogfooder/__init__.py +85 -0
  217. erk/core/health_checks_dogfooder/deprecated_dot_agent_config.py +64 -0
  218. erk/core/health_checks_dogfooder/legacy_claude_docs.py +69 -0
  219. erk/core/health_checks_dogfooder/legacy_config_locations.py +122 -0
  220. erk/core/health_checks_dogfooder/legacy_erk_docs_agent.py +61 -0
  221. erk/core/health_checks_dogfooder/legacy_erk_kits_folder.py +60 -0
  222. erk/core/health_checks_dogfooder/legacy_hook_settings.py +104 -0
  223. erk/core/health_checks_dogfooder/legacy_kit_yaml.py +78 -0
  224. erk/core/health_checks_dogfooder/legacy_kits_toml.py +43 -0
  225. erk/core/health_checks_dogfooder/outdated_erk_skill.py +43 -0
  226. erk/core/implementation_queue/__init__.py +1 -0
  227. erk/core/implementation_queue/github/__init__.py +8 -0
  228. erk/core/implementation_queue/github/abc.py +7 -0
  229. erk/core/implementation_queue/github/noop.py +38 -0
  230. erk/core/implementation_queue/github/printing.py +43 -0
  231. erk/core/implementation_queue/github/real.py +119 -0
  232. erk/core/init_utils.py +227 -0
  233. erk/core/output_filter.py +338 -0
  234. erk/core/plan_store/__init__.py +6 -0
  235. erk/core/planner/__init__.py +1 -0
  236. erk/core/planner/registry_abc.py +8 -0
  237. erk/core/planner/registry_fake.py +129 -0
  238. erk/core/planner/registry_real.py +195 -0
  239. erk/core/planner/types.py +7 -0
  240. erk/core/pr_utils.py +30 -0
  241. erk/core/release_notes.py +263 -0
  242. erk/core/repo_discovery.py +126 -0
  243. erk/core/script_writer.py +41 -0
  244. erk/core/services/__init__.py +1 -0
  245. erk/core/services/plan_list_service.py +94 -0
  246. erk/core/shell.py +51 -0
  247. erk/core/user_feedback.py +11 -0
  248. erk/core/version_check.py +55 -0
  249. erk/core/workflow_display.py +75 -0
  250. erk/core/worktree_pool.py +190 -0
  251. erk/core/worktree_utils.py +300 -0
  252. erk/data/CHANGELOG.md +438 -0
  253. erk/data/__init__.py +1 -0
  254. erk/data/claude/agents/devrun.md +180 -0
  255. erk/data/claude/commands/erk/__init__.py +0 -0
  256. erk/data/claude/commands/erk/create-extraction-plan.md +360 -0
  257. erk/data/claude/commands/erk/fix-conflicts.md +25 -0
  258. erk/data/claude/commands/erk/git-pr-push.md +345 -0
  259. erk/data/claude/commands/erk/implement-stacked-plan.md +96 -0
  260. erk/data/claude/commands/erk/land.md +193 -0
  261. erk/data/claude/commands/erk/objective-create.md +370 -0
  262. erk/data/claude/commands/erk/objective-list.md +34 -0
  263. erk/data/claude/commands/erk/objective-next-plan.md +220 -0
  264. erk/data/claude/commands/erk/objective-update-with-landed-pr.md +216 -0
  265. erk/data/claude/commands/erk/plan-implement.md +202 -0
  266. erk/data/claude/commands/erk/plan-save.md +45 -0
  267. erk/data/claude/commands/erk/plan-submit.md +39 -0
  268. erk/data/claude/commands/erk/pr-address.md +367 -0
  269. erk/data/claude/commands/erk/pr-submit.md +58 -0
  270. erk/data/claude/skills/dignified-python/SKILL.md +48 -0
  271. erk/data/claude/skills/dignified-python/cli-patterns.md +155 -0
  272. erk/data/claude/skills/dignified-python/dignified-python-core.md +1190 -0
  273. erk/data/claude/skills/dignified-python/subprocess.md +99 -0
  274. erk/data/claude/skills/dignified-python/versions/python-3.10.md +517 -0
  275. erk/data/claude/skills/dignified-python/versions/python-3.11.md +536 -0
  276. erk/data/claude/skills/dignified-python/versions/python-3.12.md +662 -0
  277. erk/data/claude/skills/dignified-python/versions/python-3.13.md +653 -0
  278. erk/data/claude/skills/erk-diff-analysis/SKILL.md +27 -0
  279. erk/data/claude/skills/erk-diff-analysis/references/commit-message-prompt.md +78 -0
  280. erk/data/claude/skills/learned-docs/SKILL.md +362 -0
  281. erk/data/github/actions/setup-claude-erk/action.yml +11 -0
  282. erk/data/github/prompts/dignified-python-review.md +125 -0
  283. erk/data/github/workflows/dignified-python-review.yml +61 -0
  284. erk/data/github/workflows/erk-impl.yml +251 -0
  285. erk/hooks/__init__.py +1 -0
  286. erk/hooks/decorators.py +319 -0
  287. erk/status/__init__.py +8 -0
  288. erk/status/collectors/__init__.py +9 -0
  289. erk/status/collectors/base.py +52 -0
  290. erk/status/collectors/git.py +76 -0
  291. erk/status/collectors/github.py +81 -0
  292. erk/status/collectors/graphite.py +80 -0
  293. erk/status/collectors/impl.py +145 -0
  294. erk/status/models/__init__.py +4 -0
  295. erk/status/models/status_data.py +404 -0
  296. erk/status/orchestrator.py +169 -0
  297. erk/status/renderers/__init__.py +5 -0
  298. erk/status/renderers/simple.py +322 -0
  299. erk/tui/AGENTS.md +193 -0
  300. erk/tui/CLAUDE.md +1 -0
  301. erk/tui/__init__.py +1 -0
  302. erk/tui/app.py +1404 -0
  303. erk/tui/commands/__init__.py +1 -0
  304. erk/tui/commands/executor.py +66 -0
  305. erk/tui/commands/provider.py +165 -0
  306. erk/tui/commands/real_executor.py +63 -0
  307. erk/tui/commands/registry.py +121 -0
  308. erk/tui/commands/types.py +36 -0
  309. erk/tui/data/__init__.py +1 -0
  310. erk/tui/data/provider.py +492 -0
  311. erk/tui/data/types.py +104 -0
  312. erk/tui/filtering/__init__.py +1 -0
  313. erk/tui/filtering/logic.py +43 -0
  314. erk/tui/filtering/types.py +55 -0
  315. erk/tui/jsonl_viewer/__init__.py +1 -0
  316. erk/tui/jsonl_viewer/app.py +61 -0
  317. erk/tui/jsonl_viewer/models.py +208 -0
  318. erk/tui/jsonl_viewer/widgets.py +204 -0
  319. erk/tui/sorting/__init__.py +6 -0
  320. erk/tui/sorting/logic.py +55 -0
  321. erk/tui/sorting/types.py +68 -0
  322. erk/tui/styles/dash.tcss +95 -0
  323. erk/tui/widgets/__init__.py +1 -0
  324. erk/tui/widgets/command_output.py +112 -0
  325. erk/tui/widgets/plan_table.py +276 -0
  326. erk/tui/widgets/status_bar.py +116 -0
  327. erk-0.4.5.dist-info/METADATA +376 -0
  328. erk-0.4.5.dist-info/RECORD +331 -0
  329. erk-0.4.5.dist-info/WHEEL +4 -0
  330. erk-0.4.5.dist-info/entry_points.txt +2 -0
  331. erk-0.4.5.dist-info/licenses/LICENSE.md +3 -0
@@ -0,0 +1,164 @@
1
+ """Static exec group for erk scripts.
2
+
3
+ This module provides the `erk exec` command group with all scripts
4
+ statically registered.
5
+ """
6
+
7
+ import click
8
+
9
+ # Import and register all scripts
10
+ from erk.cli.commands.exec.scripts.add_reaction_to_comment import (
11
+ add_reaction_to_comment,
12
+ )
13
+ from erk.cli.commands.exec.scripts.add_remote_execution_note import (
14
+ add_remote_execution_note,
15
+ )
16
+ from erk.cli.commands.exec.scripts.check_impl import check_impl
17
+ from erk.cli.commands.exec.scripts.ci_update_pr_body import ci_update_pr_body
18
+ from erk.cli.commands.exec.scripts.create_extraction_branch import (
19
+ create_extraction_branch,
20
+ )
21
+ from erk.cli.commands.exec.scripts.create_extraction_plan import (
22
+ create_extraction_plan,
23
+ )
24
+ from erk.cli.commands.exec.scripts.create_issue_from_session import (
25
+ create_issue_from_session,
26
+ )
27
+ from erk.cli.commands.exec.scripts.create_plan_from_context import (
28
+ create_plan_from_context,
29
+ )
30
+ from erk.cli.commands.exec.scripts.create_worker_impl_from_issue import (
31
+ create_worker_impl_from_issue,
32
+ )
33
+ from erk.cli.commands.exec.scripts.detect_trunk_branch import detect_trunk_branch
34
+ from erk.cli.commands.exec.scripts.exit_plan_mode_hook import exit_plan_mode_hook
35
+ from erk.cli.commands.exec.scripts.extract_latest_plan import extract_latest_plan
36
+ from erk.cli.commands.exec.scripts.extract_session_from_issue import (
37
+ extract_session_from_issue,
38
+ )
39
+ from erk.cli.commands.exec.scripts.find_project_dir import find_project_dir
40
+ from erk.cli.commands.exec.scripts.generate_pr_summary import generate_pr_summary
41
+ from erk.cli.commands.exec.scripts.get_closing_text import get_closing_text
42
+ from erk.cli.commands.exec.scripts.get_embedded_prompt import get_embedded_prompt
43
+ from erk.cli.commands.exec.scripts.get_plan_metadata import get_plan_metadata
44
+ from erk.cli.commands.exec.scripts.get_pr_body_footer import get_pr_body_footer
45
+ from erk.cli.commands.exec.scripts.get_pr_discussion_comments import (
46
+ get_pr_discussion_comments,
47
+ )
48
+ from erk.cli.commands.exec.scripts.get_pr_review_comments import (
49
+ get_pr_review_comments,
50
+ )
51
+ from erk.cli.commands.exec.scripts.impl_init import impl_init
52
+ from erk.cli.commands.exec.scripts.impl_signal import impl_signal
53
+ from erk.cli.commands.exec.scripts.impl_verify import impl_verify
54
+ from erk.cli.commands.exec.scripts.issue_title_to_filename import (
55
+ issue_title_to_filename,
56
+ )
57
+ from erk.cli.commands.exec.scripts.list_sessions import list_sessions
58
+ from erk.cli.commands.exec.scripts.mark_impl_ended import mark_impl_ended
59
+ from erk.cli.commands.exec.scripts.mark_impl_started import mark_impl_started
60
+ from erk.cli.commands.exec.scripts.marker import marker
61
+ from erk.cli.commands.exec.scripts.objective_save_to_issue import (
62
+ objective_save_to_issue,
63
+ )
64
+ from erk.cli.commands.exec.scripts.plan_save_to_issue import plan_save_to_issue
65
+ from erk.cli.commands.exec.scripts.plan_update_issue import plan_update_issue
66
+ from erk.cli.commands.exec.scripts.post_extraction_comment import (
67
+ post_extraction_comment,
68
+ )
69
+ from erk.cli.commands.exec.scripts.post_or_update_pr_summary import (
70
+ post_or_update_pr_summary,
71
+ )
72
+ from erk.cli.commands.exec.scripts.post_pr_inline_comment import (
73
+ post_pr_inline_comment,
74
+ )
75
+ from erk.cli.commands.exec.scripts.post_workflow_started_comment import (
76
+ post_workflow_started_comment,
77
+ )
78
+ from erk.cli.commands.exec.scripts.preprocess_session import preprocess_session
79
+ from erk.cli.commands.exec.scripts.quick_submit import quick_submit
80
+ from erk.cli.commands.exec.scripts.rebase_with_conflict_resolution import (
81
+ rebase_with_conflict_resolution,
82
+ )
83
+ from erk.cli.commands.exec.scripts.reply_to_discussion_comment import (
84
+ reply_to_discussion_comment,
85
+ )
86
+ from erk.cli.commands.exec.scripts.resolve_review_thread import (
87
+ resolve_review_thread,
88
+ )
89
+ from erk.cli.commands.exec.scripts.session_id_injector_hook import (
90
+ session_id_injector_hook,
91
+ )
92
+ from erk.cli.commands.exec.scripts.setup_impl_from_issue import (
93
+ setup_impl_from_issue,
94
+ )
95
+ from erk.cli.commands.exec.scripts.slot_objective import slot_objective
96
+ from erk.cli.commands.exec.scripts.tripwires_reminder_hook import (
97
+ tripwires_reminder_hook,
98
+ )
99
+ from erk.cli.commands.exec.scripts.update_dispatch_info import update_dispatch_info
100
+ from erk.cli.commands.exec.scripts.user_prompt_hook import user_prompt_hook
101
+ from erk.cli.commands.exec.scripts.validate_plan_content import (
102
+ validate_plan_content,
103
+ )
104
+ from erk.cli.commands.exec.scripts.wrap_plan_in_metadata_block import (
105
+ wrap_plan_in_metadata_block,
106
+ )
107
+
108
+
109
+ # Create the exec group (hidden from top-level help)
110
+ @click.group(name="exec", hidden=True)
111
+ def exec_group() -> None:
112
+ """Execute erk workflow scripts."""
113
+
114
+
115
+ # Register all commands
116
+ exec_group.add_command(add_reaction_to_comment, name="add-reaction-to-comment")
117
+ exec_group.add_command(add_remote_execution_note, name="add-remote-execution-note")
118
+ exec_group.add_command(check_impl, name="check-impl")
119
+ exec_group.add_command(create_extraction_branch, name="create-extraction-branch")
120
+ exec_group.add_command(create_extraction_plan, name="create-extraction-plan")
121
+ exec_group.add_command(create_issue_from_session, name="create-issue-from-session")
122
+ exec_group.add_command(create_plan_from_context, name="create-plan-from-context")
123
+ exec_group.add_command(create_worker_impl_from_issue, name="create-worker-impl-from-issue")
124
+ exec_group.add_command(detect_trunk_branch, name="detect-trunk-branch")
125
+ exec_group.add_command(exit_plan_mode_hook, name="exit-plan-mode-hook")
126
+ exec_group.add_command(extract_latest_plan, name="extract-latest-plan")
127
+ exec_group.add_command(extract_session_from_issue, name="extract-session-from-issue")
128
+ exec_group.add_command(find_project_dir, name="find-project-dir")
129
+ exec_group.add_command(generate_pr_summary, name="generate-pr-summary")
130
+ exec_group.add_command(get_closing_text, name="get-closing-text")
131
+ exec_group.add_command(get_plan_metadata, name="get-plan-metadata")
132
+ exec_group.add_command(get_embedded_prompt, name="get-embedded-prompt")
133
+ exec_group.add_command(get_pr_body_footer, name="get-pr-body-footer")
134
+ exec_group.add_command(get_pr_discussion_comments, name="get-pr-discussion-comments")
135
+ exec_group.add_command(get_pr_review_comments, name="get-pr-review-comments")
136
+ exec_group.add_command(impl_init, name="impl-init")
137
+ exec_group.add_command(impl_signal, name="impl-signal")
138
+ exec_group.add_command(impl_verify, name="impl-verify")
139
+ exec_group.add_command(issue_title_to_filename, name="issue-title-to-filename")
140
+ exec_group.add_command(list_sessions, name="list-sessions")
141
+ exec_group.add_command(mark_impl_ended, name="mark-impl-ended")
142
+ exec_group.add_command(mark_impl_started, name="mark-impl-started")
143
+ exec_group.add_command(marker, name="marker")
144
+ exec_group.add_command(objective_save_to_issue, name="objective-save-to-issue")
145
+ exec_group.add_command(plan_save_to_issue, name="plan-save-to-issue")
146
+ exec_group.add_command(plan_update_issue, name="plan-update-issue")
147
+ exec_group.add_command(post_extraction_comment, name="post-extraction-comment")
148
+ exec_group.add_command(post_or_update_pr_summary, name="post-or-update-pr-summary")
149
+ exec_group.add_command(post_pr_inline_comment, name="post-pr-inline-comment")
150
+ exec_group.add_command(post_workflow_started_comment, name="post-workflow-started-comment")
151
+ exec_group.add_command(preprocess_session, name="preprocess-session")
152
+ exec_group.add_command(quick_submit, name="quick-submit")
153
+ exec_group.add_command(rebase_with_conflict_resolution, name="rebase-with-conflict-resolution")
154
+ exec_group.add_command(resolve_review_thread, name="resolve-review-thread")
155
+ exec_group.add_command(reply_to_discussion_comment, name="reply-to-discussion-comment")
156
+ exec_group.add_command(session_id_injector_hook, name="session-id-injector-hook")
157
+ exec_group.add_command(setup_impl_from_issue, name="setup-impl-from-issue")
158
+ exec_group.add_command(slot_objective, name="slot-objective")
159
+ exec_group.add_command(tripwires_reminder_hook, name="tripwires-reminder-hook")
160
+ exec_group.add_command(update_dispatch_info, name="update-dispatch-info")
161
+ exec_group.add_command(ci_update_pr_body)
162
+ exec_group.add_command(user_prompt_hook, name="user-prompt-hook")
163
+ exec_group.add_command(validate_plan_content, name="validate-plan-content")
164
+ exec_group.add_command(wrap_plan_in_metadata_block, name="wrap-plan-in-metadata-block")
@@ -0,0 +1,79 @@
1
+ # Exec Script Standards
2
+
3
+ ## Required Pattern: Click Context Dependency Injection
4
+
5
+ All exec CLI commands in this folder MUST use Click's context system for dependency injection.
6
+
7
+ ### Required Pattern
8
+
9
+ ```python
10
+ import click
11
+ from erk_shared.context.helpers import require_cwd
12
+
13
+ @click.command(name="my-command")
14
+ @click.pass_context
15
+ def my_command(ctx: click.Context) -> None:
16
+ """Command description."""
17
+ cwd = require_cwd(ctx)
18
+ # Use cwd instead of Path.cwd()
19
+ ```
20
+
21
+ ### Why This Pattern?
22
+
23
+ 1. **Testability**: Tests inject dependencies via `obj=ErkContext.for_test(cwd=tmp_path)`
24
+ 2. **No monkeypatching**: Tests don't need `monkeypatch.chdir()` or filesystem manipulation
25
+ 3. **Explicit dependencies**: All dependencies visible in function signature via context
26
+ 4. **Consistency**: All commands follow the same pattern
27
+
28
+ ### Available Helper Functions
29
+
30
+ From `erk_shared.context.helpers`:
31
+
32
+ | Helper | Returns | Usage |
33
+ | ------------------------ | -------- | ------------------------- |
34
+ | `require_cwd(ctx)` | `Path` | Current working directory |
35
+ | `require_repo_root(ctx)` | `Path` | Repository root path |
36
+ | `require_git(ctx)` | `Git` | Git operations |
37
+ | `require_github(ctx)` | `GitHub` | GitHub operations |
38
+
39
+ ### Anti-Patterns
40
+
41
+ ```python
42
+ # WRONG: bypasses dependency injection
43
+ def my_command() -> None:
44
+ progress_file = Path.cwd() / ".impl" / "progress.md"
45
+
46
+ # CORRECT: uses injected path
47
+ @click.pass_context
48
+ def my_command(ctx: click.Context) -> None:
49
+ cwd = require_cwd(ctx)
50
+ progress_file = cwd / ".impl" / "progress.md"
51
+ ```
52
+
53
+ ## Testing Requirements
54
+
55
+ Every exec CLI command MUST have tests that use context injection:
56
+
57
+ ```python
58
+ from click.testing import CliRunner
59
+ from erk_shared.context import ErkContext
60
+
61
+ def test_my_command(tmp_path: Path) -> None:
62
+ """Test using context injection."""
63
+ runner = CliRunner()
64
+ result = runner.invoke(
65
+ my_command,
66
+ ["--json"],
67
+ obj=ErkContext.for_test(cwd=tmp_path),
68
+ )
69
+ assert result.exit_code == 0
70
+ ```
71
+
72
+ ### Test Location
73
+
74
+ - Unit tests: `tests/unit/cli/commands/exec/scripts/`
75
+
76
+ ## See Also
77
+
78
+ - [Kit CLI Dependency Injection Patterns](/docs/learned/kits/dependency-injection.md)
79
+ - [fake-driven-testing skill](/.claude/skills/fake-driven-testing/)
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -0,0 +1,5 @@
1
+ """Erk exec scripts module.
2
+
3
+ This module contains all the CLI scripts available via `erk exec <command>`.
4
+ Each script is a Click command that can be invoked directly.
5
+ """
@@ -0,0 +1,69 @@
1
+ """Add a reaction to a PR/issue comment to mark it as addressed.
2
+
3
+ This exec command adds a reaction (typically +1) to a PR discussion comment
4
+ to indicate the comment has been addressed.
5
+
6
+ Usage:
7
+ erk exec add-reaction-to-comment --comment-id 12345
8
+ erk exec add-reaction-to-comment --comment-id 12345 --reaction "+1"
9
+ erk exec add-reaction-to-comment --comment-id 12345 --reaction "eyes"
10
+
11
+ Output:
12
+ JSON with success status
13
+
14
+ Exit Codes:
15
+ 0: Always (even on error, to support || true pattern)
16
+ 1: Context not initialized
17
+
18
+ Examples:
19
+ $ erk exec add-reaction-to-comment --comment-id 12345
20
+ {"success": true, "comment_id": 12345, "reaction": "+1"}
21
+
22
+ $ erk exec add-reaction-to-comment --comment-id 12345 --reaction "rocket"
23
+ {"success": true, "comment_id": 12345, "reaction": "rocket"}
24
+ """
25
+
26
+ import json
27
+
28
+ import click
29
+
30
+ from erk.cli.script_output import exit_with_error
31
+ from erk_shared.context.helpers import require_issues as require_github_issues
32
+ from erk_shared.context.helpers import require_repo_root
33
+ from erk_shared.github.checks import GitHubChecks
34
+ from erk_shared.non_ideal_state import GitHubAPIFailed
35
+
36
+
37
+ @click.command(name="add-reaction-to-comment")
38
+ @click.option("--comment-id", required=True, type=int, help="Numeric comment ID")
39
+ @click.option(
40
+ "--reaction",
41
+ default="+1",
42
+ help="Reaction type: +1, -1, laugh, confused, heart, hooray, rocket, eyes",
43
+ )
44
+ @click.pass_context
45
+ def add_reaction_to_comment(ctx: click.Context, comment_id: int, reaction: str) -> None:
46
+ """Add a reaction to a PR/issue comment.
47
+
48
+ Takes a numeric comment ID (from get-pr-discussion-comments output) and
49
+ adds a reaction to mark the comment as addressed. Default reaction is +1.
50
+
51
+ The GitHub API is idempotent - adding the same reaction twice returns
52
+ the existing reaction.
53
+ """
54
+ # Get dependencies from context
55
+ repo_root = require_repo_root(ctx)
56
+ github_issues = require_github_issues(ctx)
57
+
58
+ # Add the reaction (exits on failure)
59
+ reaction_result = GitHubChecks.add_reaction(github_issues, repo_root, comment_id, reaction)
60
+ if isinstance(reaction_result, GitHubAPIFailed):
61
+ exit_with_error(reaction_result.error_type, reaction_result.message)
62
+
63
+ result = {
64
+ "success": True,
65
+ "comment_id": comment_id,
66
+ "reaction": reaction,
67
+ }
68
+ click.echo(json.dumps(result, indent=2))
69
+ raise SystemExit(0)
@@ -0,0 +1,68 @@
1
+ """Add remote execution note to PR body.
2
+
3
+ This exec command appends a remote execution tracking note to a PR body.
4
+ Used by GitHub Actions workflows to track workflow runs that executed against a PR.
5
+
6
+ Usage:
7
+ erk exec add-remote-execution-note --pr-number 123 --run-id 456 --run-url https://...
8
+
9
+ Output:
10
+ Success message on stdout
11
+
12
+ Exit Codes:
13
+ 0: Success
14
+ 1: Error (missing arguments, gh command failed)
15
+
16
+ Examples:
17
+ $ erk exec add-remote-execution-note \\
18
+ --pr-number 1895 \\
19
+ --run-id 12345678 \\
20
+ --run-url https://github.com/owner/repo/actions/runs/12345678
21
+
22
+ Added remote execution note to PR #1895
23
+ """
24
+
25
+ import click
26
+
27
+ from erk_shared.context.helpers import require_github, require_repo_root
28
+ from erk_shared.github.pr_footer import build_remote_execution_note
29
+
30
+
31
+ @click.command(name="add-remote-execution-note")
32
+ @click.option("--pr-number", type=int, required=True, help="PR number to update")
33
+ @click.option("--run-id", type=str, required=True, help="Workflow run ID")
34
+ @click.option("--run-url", type=str, required=True, help="Full URL to workflow run")
35
+ @click.pass_context
36
+ def add_remote_execution_note(
37
+ ctx: click.Context, pr_number: int, run_id: str, run_url: str
38
+ ) -> None:
39
+ """Add remote execution tracking note to PR body.
40
+
41
+ Fetches the current PR body, appends a remote execution note with the
42
+ workflow run link, and updates the PR. This creates a history of all
43
+ workflow runs that executed against the PR.
44
+
45
+ Args:
46
+ ctx: Click context with GitHub interface
47
+ pr_number: The PR number to update
48
+ run_id: The GitHub Actions workflow run ID
49
+ run_url: Full URL to the workflow run
50
+ """
51
+ github = require_github(ctx)
52
+ repo_root = require_repo_root(ctx)
53
+
54
+ # Get current PR body
55
+ current_body = github.get_pr_body(repo_root, pr_number)
56
+ if current_body is None:
57
+ current_body = ""
58
+
59
+ # Build the remote execution note
60
+ remote_note = build_remote_execution_note(run_id, run_url)
61
+
62
+ # Append note to body
63
+ new_body = f"{current_body}{remote_note}"
64
+
65
+ # Update PR body
66
+ github.update_pr_body(repo_root, pr_number, new_body)
67
+
68
+ click.echo(f"Added remote execution note to PR #{pr_number}")
@@ -0,0 +1,152 @@
1
+ """Check .impl/ folder structure and validate prerequisites.
2
+
3
+ This exec command validates that .impl/ folder has required files
4
+ (plan.md, progress.md) and checks for optional GitHub issue tracking.
5
+
6
+ Usage:
7
+ erk exec check-impl
8
+ erk exec check-impl --dry-run
9
+
10
+ Output:
11
+ JSON with validation status and tracking info (dry-run mode)
12
+ Instructions for implementation (normal mode)
13
+
14
+ Exit Codes:
15
+ 0: Success
16
+ 1: Validation error
17
+
18
+ Examples:
19
+ $ erk exec check-impl --dry-run
20
+ {"valid": true, "has_issue_tracking": true, "plan_length": 1234}
21
+
22
+ $ erk exec check-impl
23
+ Plan loaded from .impl/plan.md
24
+ GitHub tracking: ENABLED (issue #123)
25
+ ...
26
+ """
27
+
28
+ import json
29
+ from pathlib import Path
30
+ from typing import NoReturn
31
+
32
+ import click
33
+
34
+ from erk_shared.impl_folder import read_issue_reference
35
+
36
+
37
+ def _error(msg: str) -> NoReturn:
38
+ """Output error message and exit with code 1."""
39
+ click.echo(f"❌ Error: {msg}", err=True)
40
+ raise SystemExit(1)
41
+
42
+
43
+ def _validate_impl_folder() -> Path:
44
+ """Validate .impl/ folder exists and has required files.
45
+
46
+ Returns:
47
+ Path to .impl/ directory
48
+
49
+ Raises:
50
+ SystemExit: If validation fails
51
+ """
52
+ impl_dir = Path.cwd() / ".impl"
53
+
54
+ if not impl_dir.exists():
55
+ _error("No .impl/ folder found in current directory")
56
+
57
+ plan_file = impl_dir / "plan.md"
58
+ if not plan_file.exists():
59
+ _error("No plan.md found in .impl/ folder")
60
+
61
+ progress_file = impl_dir / "progress.md"
62
+ if not progress_file.exists():
63
+ _error("No progress.md found in .impl/ folder")
64
+
65
+ return impl_dir
66
+
67
+
68
+ def _get_issue_reference(impl_dir: Path, *, silent: bool = False) -> dict[str, int | str] | None:
69
+ """Get issue reference if available, None if not (non-fatal).
70
+
71
+ Args:
72
+ impl_dir: Path to .impl/ directory
73
+ silent: If True, don't print info message when tracking disabled
74
+
75
+ Returns:
76
+ Dict with issue_number and issue_url, or None if not available
77
+ """
78
+ issue_ref = read_issue_reference(impl_dir)
79
+
80
+ if issue_ref is None:
81
+ # Not an error - just means no GitHub tracking
82
+ if not silent:
83
+ click.echo(
84
+ "ℹ️ No issue reference found - GitHub progress tracking disabled",
85
+ err=True,
86
+ )
87
+ return None
88
+
89
+ return {
90
+ "issue_number": issue_ref.issue_number,
91
+ "issue_url": issue_ref.issue_url,
92
+ }
93
+
94
+
95
+ def _execute_plan(plan_content: str, issue_info: dict[str, int | str] | None) -> None:
96
+ """Display plan execution instructions.
97
+
98
+ Args:
99
+ plan_content: Content of plan.md
100
+ issue_info: Issue info dict or None
101
+ """
102
+ if issue_info:
103
+ tracking_msg = f"GitHub tracking: ENABLED (issue #{issue_info['issue_number']})"
104
+ else:
105
+ tracking_msg = "GitHub tracking: DISABLED (no issue.json)"
106
+
107
+ msg = f"""
108
+ Plan loaded from .impl/plan.md
109
+
110
+ {tracking_msg}
111
+
112
+ To implement:
113
+ claude --permission-mode acceptEdits "/erk:plan-implement"
114
+
115
+ The /erk:plan-implement slash command will:
116
+ 1. Execute implementation steps
117
+ 2. Update progress.md as steps complete"""
118
+
119
+ if issue_info:
120
+ msg += f"\n 3. Post progress to GitHub issue #{issue_info['issue_number']}"
121
+
122
+ click.echo(msg)
123
+
124
+
125
+ @click.command(name="check-impl")
126
+ @click.option("--dry-run", is_flag=True, help="Validate and output JSON")
127
+ def check_impl(dry_run: bool) -> None:
128
+ """Check .impl/ folder structure and validate prerequisites.
129
+
130
+ Validates that .impl/ folder exists with required files (plan.md, progress.md).
131
+ Checks for optional issue.json to enable GitHub progress tracking.
132
+
133
+ In dry-run mode, outputs JSON with validation status.
134
+ In normal mode, outputs instructions for running the implementation.
135
+ """
136
+ impl_dir = _validate_impl_folder()
137
+ # In dry-run mode, suppress info messages to keep JSON output clean
138
+ issue_info = _get_issue_reference(impl_dir, silent=dry_run)
139
+
140
+ plan_file = impl_dir / "plan.md"
141
+ plan_content = plan_file.read_text(encoding="utf-8")
142
+
143
+ if dry_run:
144
+ result = {
145
+ "valid": True,
146
+ "has_issue_tracking": issue_info is not None,
147
+ "plan_length": len(plan_content),
148
+ }
149
+ click.echo(json.dumps(result))
150
+ return
151
+
152
+ _execute_plan(plan_content, issue_info)