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,367 @@
1
+ ---
2
+ description: Address PR review comments on current branch
3
+ ---
4
+
5
+ # /erk:pr-address
6
+
7
+ ## Description
8
+
9
+ Fetches unresolved PR review comments AND PR discussion comments from the current branch's PR and addresses them using holistic analysis with smart batching. Comments are grouped by complexity and relationship, then processed batch-by-batch with incremental commits and resolution.
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ /erk:pr-address
15
+ /erk:pr-address --all # Include resolved threads (for reference)
16
+ ```
17
+
18
+ ## Agent Instructions
19
+
20
+ > **CRITICAL: Use ONLY `erk exec` Commands**
21
+ >
22
+ > - ❌ DO NOT use raw `gh api` calls
23
+ > - ❌ DO NOT use `gh pr` commands directly
24
+ > - ✅ ONLY use `erk exec get-pr-review-comments`, `erk exec resolve-review-thread`, etc.
25
+ >
26
+ > The `erk exec` commands handle thread resolution correctly. Raw API calls only reply without resolving.
27
+
28
+ ### Phase 1: Fetch & Analyze
29
+
30
+ #### Step 1.1: Fetch All Comments
31
+
32
+ Run both CLI commands to get review comments AND discussion comments:
33
+
34
+ ```bash
35
+ erk exec get-pr-review-comments
36
+ erk exec get-pr-discussion-comments
37
+ ```
38
+
39
+ **Review Comments JSON:**
40
+
41
+ ```json
42
+ {
43
+ "success": true,
44
+ "pr_number": 123,
45
+ "pr_url": "https://github.com/owner/repo/pull/123",
46
+ "pr_title": "Feature: Add new capability",
47
+ "threads": [
48
+ {
49
+ "id": "PRRT_abc123",
50
+ "path": "src/foo.py",
51
+ "line": 42,
52
+ "is_outdated": false,
53
+ "comments": [
54
+ {
55
+ "author": "reviewer",
56
+ "body": "This should use LBYL pattern instead of try/except",
57
+ "created_at": "2024-01-01T10:00:00Z"
58
+ }
59
+ ]
60
+ }
61
+ ]
62
+ }
63
+ ```
64
+
65
+ **Discussion Comments JSON:**
66
+
67
+ ```json
68
+ {
69
+ "success": true,
70
+ "pr_number": 123,
71
+ "pr_url": "https://github.com/owner/repo/pull/123",
72
+ "pr_title": "Feature: Add new capability",
73
+ "comments": [
74
+ {
75
+ "id": 12345,
76
+ "author": "reviewer",
77
+ "body": "Please also update the docs",
78
+ "url": "https://github.com/owner/repo/pull/123#issuecomment-12345"
79
+ }
80
+ ]
81
+ }
82
+ ```
83
+
84
+ #### Step 1.2: Handle No Comments Case
85
+
86
+ If both `threads` is empty AND `comments` is empty, display: "No unresolved review comments or discussion comments on PR #123."
87
+
88
+ #### Step 1.3: Holistic Analysis
89
+
90
+ Analyze ALL comments together to understand relationships and complexity. Classify each comment:
91
+
92
+ - **Local fix**: Single comment → single location change (e.g., "Fix typo", "Add type annotation")
93
+ - **Multi-location in same file**: Single comment → changes in multiple spots in one file
94
+ - **Cross-cutting**: Single comment → changes across multiple files
95
+ - **Related comments**: Multiple comments that inform a single unified change (e.g., two comments about the same refactor)
96
+
97
+ #### Step 1.4: Batch and Prioritize
98
+
99
+ Group comments into batches ordered by complexity (simplest first):
100
+
101
+ | Batch | Complexity | Description | Example |
102
+ | ----- | -------------------------- | ----------------------------------- | --------------------------------------------------------- |
103
+ | 1 | Local fixes | One file, one location per comment | "Use LBYL pattern at line 42" |
104
+ | 2 | Single-file multi-location | One file, multiple locations | "Rename this variable everywhere in this file" |
105
+ | 3 | Cross-cutting | Multiple files affected | "Update all callers of this function" |
106
+ | 4 | Complex/Related | Multiple comments inform one change | "Fold validate into prepare" + "Use union types for this" |
107
+
108
+ **Note**: Discussion comments that require doc updates or non-code changes go in Batch 3 (cross-cutting) since they often affect multiple files.
109
+
110
+ ### Phase 2: Display Batched Plan
111
+
112
+ Show the user the batched execution plan:
113
+
114
+ ```
115
+ ## Execution Plan
116
+
117
+ ### Batch 1: Local Fixes (3 comments)
118
+ | # | Location | Summary |
119
+ |---|----------|---------|
120
+ | 1 | foo.py:42 | Use LBYL pattern |
121
+ | 2 | bar.py:15 | Add type annotation |
122
+ | 3 | baz.py:99 | Fix typo |
123
+
124
+ ### Batch 2: Single-File Changes (1 comment)
125
+ | # | Location | Summary |
126
+ |---|----------|---------|
127
+ | 4 | impl.py (multiple) | Rename `old_name` to `new_name` throughout |
128
+
129
+ ### Batch 3: Cross-Cutting Changes (2 comments)
130
+ | # | Location | Summary |
131
+ |---|----------|---------|
132
+ | 5 | Multiple files | Update all callers of deprecated function |
133
+ | 6 | docs/ | Update documentation per reviewer request |
134
+
135
+ ### Batch 4: Complex Changes (2 comments → 1 unified change)
136
+ | # | Location | Summary |
137
+ |---|----------|---------|
138
+ | 7 | impl.py:50 | Fold validate into prepare with union types |
139
+ | 8 | cmd.py:100 | (related to #7 - same refactor) |
140
+ ```
141
+
142
+ **User confirmation flow:**
143
+
144
+ - **Batch 1-2 (simple)**: Auto-proceed without confirmation
145
+ - **Batch 3-4 (complex)**: Show plan and wait for user approval before executing
146
+
147
+ ### Phase 3: Execute by Batch
148
+
149
+ For each batch, execute this workflow:
150
+
151
+ #### Step 3.1: Address All Comments in the Batch
152
+
153
+ For each comment in the batch:
154
+
155
+ **For Review Threads:**
156
+
157
+ 1. Read the file to understand context:
158
+ - If `line` is specified: Read around that line number
159
+ - If `line` is null (outdated thread): Read the entire file or search for relevant code mentioned in the comment
160
+ 2. Make the fix following the reviewer's feedback
161
+ 3. Track the change for the batch commit message
162
+
163
+ **For Discussion Comments:**
164
+
165
+ 1. Determine if action is needed:
166
+ - If it's a request (e.g., "Please update docs"), take the requested action
167
+ - If it's a question, provide an answer or make clarifying changes
168
+ - If it's architectural feedback/suggestion, investigate the codebase to understand implications
169
+ - If it's just acknowledgment/thanks, note it and move on
170
+ 2. **Investigate the codebase** when the comment requires understanding existing code:
171
+ - Search for relevant patterns, existing implementations, or related code
172
+ - Note any interesting findings that inform your decision
173
+ - Record these findings - they become permanent documentation in the reply
174
+ 3. Take action if needed
175
+
176
+ **Handling False Positives from Automated Reviewers:**
177
+
178
+ Automated review bots (like `dignified-python-review`, linters, or security scanners) can flag false positives. Before making code changes:
179
+
180
+ 1. **Read the flagged code carefully** - understand what the bot is complaining about
181
+ 2. **Verify if it's a false positive** by checking:
182
+ - Is the pattern the bot wants already implemented nearby? (e.g., LBYL check already exists on a preceding line)
183
+ - Is the bot misunderstanding the code structure?
184
+ - Is the bot applying a rule that doesn't fit this specific context?
185
+ 3. **If it's a false positive**, do NOT make unnecessary code changes. Instead:
186
+ - Reply to the comment explaining why it's a false positive
187
+ - Reference specific line numbers where the correct pattern already exists
188
+ - Resolve the thread
189
+
190
+ **Example reply for a false positive:**
191
+
192
+ ```bash
193
+ erk exec resolve-review-thread --thread-id "PRRT_abc123" --comment "False positive: The LBYL check the bot is requesting already exists on line 344 where we check \`.exists()\` before the operation on line 353. No code change needed."
194
+ ```
195
+
196
+ **For Outdated Review Threads** (`is_outdated: true`):
197
+
198
+ Outdated threads have `line: null` because the code has changed since the comment was made.
199
+
200
+ 1. **Read the file** at the path (ignore line number - search for relevant code)
201
+ 2. **Check if the issue is already fixed** in the current code
202
+ 3. **Take action:**
203
+ - If already fixed → Proceed directly to Step 3.4 to resolve the thread
204
+ - If not fixed → Apply the fix, then proceed to Step 3.4
205
+
206
+ **IMPORTANT**: Outdated threads MUST still be resolved via `erk exec resolve-review-thread`.
207
+ Do not skip resolution just because no code change was needed.
208
+
209
+ #### Step 3.2: Run CI Checks
210
+
211
+ After making all changes in the batch:
212
+
213
+ ```bash
214
+ # Run relevant CI checks for changed files
215
+ # (This may vary by project - use project's test commands)
216
+ ```
217
+
218
+ If CI fails, fix the issues before proceeding.
219
+
220
+ #### Step 3.3: Commit the Batch
221
+
222
+ Create a single commit for all changes in the batch:
223
+
224
+ ```bash
225
+ git add <changed files>
226
+ git commit -m "Address PR review comments (batch N/M)
227
+
228
+ - <summary of comment 1>
229
+ - <summary of comment 2>
230
+ ..."
231
+ ```
232
+
233
+ #### Step 3.4: Resolve All Threads in the Batch (MANDATORY)
234
+
235
+ **This step is NOT optional.** Every thread must be resolved using `erk exec resolve-review-thread`.
236
+
237
+ > **IMPORTANT: Replying ≠ Resolving**
238
+ >
239
+ > - **Replying** (via raw `gh api .../replies`): Adds a comment but thread stays OPEN
240
+ > - **Resolving** (via `erk exec resolve-review-thread`): Adds a comment AND marks thread as RESOLVED
241
+ >
242
+ > Always use `erk exec resolve-review-thread` - it does both in one operation.
243
+
244
+ After committing, resolve each review thread and mark each discussion comment:
245
+
246
+ **For Review Threads:**
247
+
248
+ ```bash
249
+ erk exec resolve-review-thread --thread-id "PRRT_abc123" --comment "Resolved via /erk:pr-address at $(date '+%Y-%m-%d %I:%M %p %Z')"
250
+ ```
251
+
252
+ **Resolving already-fixed outdated threads:**
253
+
254
+ ```bash
255
+ erk exec resolve-review-thread --thread-id "PRRT_abc123" --comment "Already addressed in current code - this outdated thread can be resolved."
256
+ ```
257
+
258
+ **For Discussion Comments:**
259
+
260
+ Post a substantive reply that quotes the original comment and explains what action was taken:
261
+
262
+ ```bash
263
+ erk exec reply-to-discussion-comment --comment-id 12345 --reply "**Action taken:** <substantive summary>"
264
+ ```
265
+
266
+ **Writing substantive replies:**
267
+
268
+ The `--reply` argument should include meaningful findings, not just generic acknowledgments:
269
+
270
+ ❌ **Bad (too generic):**
271
+
272
+ ```bash
273
+ --reply "**Action taken:** Noted for future consideration."
274
+ --reply "**Action taken:** Added to backlog."
275
+ ```
276
+
277
+ ✅ **Good (includes investigation findings):**
278
+
279
+ ```bash
280
+ --reply "**Action taken:** Investigated the gateway pattern suggestion. The current artifact sync implementation uses direct function calls rather than a gateway ABC pattern. This is intentional - artifact operations are file-based and don't require the testability benefits of gateway injection that external APIs need. Filed as backlog consideration for if we add remote artifact fetching."
281
+ ```
282
+
283
+ ✅ **Good (explains why no code change):**
284
+
285
+ ```bash
286
+ --reply "**Action taken:** Reviewed the suggestion to add caching here. After checking the call sites, this function is only called once per CLI invocation (in main.py:45), so caching wouldn't provide measurable benefit. The perceived slowness is actually from the subprocess call inside, not repeated invocations."
287
+ ```
288
+
289
+ The reply becomes a permanent record in the PR - make it useful for future readers who wonder "what happened with this feedback?"
290
+
291
+ #### Step 3.5: Report Progress
292
+
293
+ After completing the batch, report:
294
+
295
+ ```
296
+ ## Batch N Complete
297
+
298
+ Addressed:
299
+ - ✅ foo.py:42 - Used LBYL pattern
300
+ - ✅ bar.py:15 - Added type annotation
301
+
302
+ Committed: abc1234 "Address PR review comments (batch 1/3)"
303
+
304
+ Resolved threads: 2
305
+ Remaining batches: 2
306
+ ```
307
+
308
+ Then proceed to the next batch.
309
+
310
+ ### Phase 4: Final Verification
311
+
312
+ After all batches complete:
313
+
314
+ #### Step 4.1: Verify All Threads Resolved
315
+
316
+ Re-fetch comments to confirm nothing was missed:
317
+
318
+ ```bash
319
+ erk exec get-pr-review-comments
320
+ erk exec get-pr-discussion-comments
321
+ ```
322
+
323
+ If any unresolved threads remain, report them.
324
+
325
+ #### Step 4.2: Report Summary
326
+
327
+ ```
328
+ ## All PR Comments Addressed
329
+
330
+ Total comments: 8
331
+ Batches: 4
332
+ Commits: 4
333
+
334
+ All review threads resolved.
335
+ All discussion comments marked with reaction.
336
+
337
+ Next steps:
338
+ 1. Push changes: `git push`
339
+ 2. Wait for CI to pass
340
+ 3. Request re-review if needed
341
+ ```
342
+
343
+ #### Step 4.3: Handle Any Skipped Comments
344
+
345
+ If the user explicitly skipped any comments during the process, list them:
346
+
347
+ ```
348
+ ## Skipped Comments (user choice)
349
+ - #5: src/legacy.py:100 - "Refactor this module" (user deferred)
350
+ ```
351
+
352
+ ### Common Mistakes to Avoid
353
+
354
+ | Mistake | Why It's Wrong | Correct Approach |
355
+ | ---------------------------------------------- | --------------------------------- | ------------------------------------- |
356
+ | Using `gh api repos/.../comments/{id}/replies` | Only replies, doesn't resolve | Use `erk exec resolve-review-thread` |
357
+ | Using `gh pr comment` | Doesn't resolve threads | Use `erk exec resolve-review-thread` |
358
+ | Skipping resolution for outdated threads | Threads stay open in PR | Always resolve, even if already fixed |
359
+ | Not re-fetching after resolution | Can't verify all threads resolved | Always run Step 4.1 verification |
360
+
361
+ ### Error Handling
362
+
363
+ **No PR for branch:** Display error and suggest creating a PR with `gt create` or `gh pr create`
364
+
365
+ **GitHub API error:** Display error and suggest checking `gh auth status` and repository access
366
+
367
+ **CI failure during batch:** Stop, display the failure, and let the user decide whether to fix and continue or abort
@@ -0,0 +1,58 @@
1
+ ---
2
+ description:
3
+ Create git commit and submit current branch with Graphite (squashes commits
4
+ and rebases stack)
5
+ argument-hint: <description>
6
+ ---
7
+
8
+ # Submit PR
9
+
10
+ Automatically create a git commit with a helpful summary message and submit the current branch as a pull request.
11
+
12
+ **Note:** This command squashes commits and rebases the stack. If you prefer a simpler workflow that preserves your commit history, use `/erk:git-pr-push` instead.
13
+
14
+ ## Usage
15
+
16
+ ```bash
17
+ # Invoke the command (description argument is optional but recommended)
18
+ /erk:pr-submit "Add user authentication feature"
19
+
20
+ # Without argument (will analyze changes automatically)
21
+ /erk:pr-submit
22
+ ```
23
+
24
+ ## Implementation
25
+
26
+ This command delegates to `erk pr submit` which handles everything:
27
+
28
+ - Preflight checks (auth, squash commits, submit to Graphite)
29
+ - AI-powered commit message generation
30
+ - PR metadata update with title and body
31
+ - PR validation
32
+
33
+ ### Execute
34
+
35
+ Run the full submit workflow:
36
+
37
+ ```bash
38
+ erk pr submit
39
+ ```
40
+
41
+ ### Report Results
42
+
43
+ The command outputs:
44
+
45
+ - PR URL
46
+ - Graphite URL
47
+ - Success message
48
+
49
+ ## Error Handling
50
+
51
+ If `erk pr submit` fails, display the error and stop. The Python implementation handles all error cases including:
52
+
53
+ - Authentication issues (Graphite/GitHub)
54
+ - Merge conflicts
55
+ - No commits to submit
56
+ - Submission failures
57
+
58
+ Do NOT attempt to auto-resolve errors. Let the user fix issues and re-run.
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: dignified-python
3
+ description: Python coding standards with automatic version detection.
4
+ Use when writing, reviewing, or refactoring Python to ensure adherence to LBYL exception
5
+ handling patterns, modern type syntax (list[str], str | None), pathlib operations,
6
+ ABC-based interfaces, absolute imports, and explicit error boundaries at CLI level.
7
+ Also provides production-tested code smell patterns from Dagster Labs for API design,
8
+ parameter complexity, and code organization. Essential for maintaining erk's dignified
9
+ Python standards.
10
+ ---
11
+
12
+ # Dignified Python Coding Standards
13
+
14
+ ## Core Knowledge (ALWAYS Loaded)
15
+
16
+ @dignified-python-core.md
17
+
18
+ ## Version Detection
19
+
20
+ **Identify the project's minimum Python version** by checking (in order):
21
+
22
+ 1. `pyproject.toml` - Look for `requires-python` field (e.g., `requires-python = ">=3.12"`)
23
+ 2. `setup.py` or `setup.cfg` - Look for `python_requires`
24
+ 3. `.python-version` file - Contains version like `3.12` or `3.12.0`
25
+ 4. Default to Python 3.12 if no version specifier found
26
+
27
+ **Once identified, load the appropriate version-specific file:**
28
+
29
+ - Python 3.10: Load `versions/python-3.10.md`
30
+ - Python 3.11: Load `versions/python-3.11.md`
31
+ - Python 3.12: Load `versions/python-3.12.md`
32
+ - Python 3.13: Load `versions/python-3.13.md`
33
+
34
+ ## Conditional Loading (Load Based on Task Patterns)
35
+
36
+ Core files above cover 80%+ of Python code patterns. Only load these additional files when you detect specific patterns:
37
+
38
+ Pattern detection examples:
39
+
40
+ - If task mentions "click" or "CLI" -> Load `cli-patterns.md`
41
+ - If task mentions "subprocess" -> Load `subprocess.md`
42
+
43
+ ## How to Use This Skill
44
+
45
+ 1. **Core knowledge** is loaded automatically (LBYL, pathlib, ABC, imports, exceptions)
46
+ 2. **Version detection** happens once - identify the minimum Python version and load the appropriate version file
47
+ 3. **Additional patterns** may require extra loading (CLI patterns, subprocess)
48
+ 4. **Each file is self-contained** with complete guidance for its domain
@@ -0,0 +1,155 @@
1
+ ---
2
+ ---
3
+
4
+ # CLI Patterns - Click Best Practices
5
+
6
+ ## Core Rules
7
+
8
+ 1. **Use `click.echo()` for output, NEVER `print()`**
9
+ 2. **Exit with `raise SystemExit(1)` for CLI errors**
10
+ 3. **Error boundaries at command level**
11
+ 4. **Use `err=True` for error output**
12
+ 5. **Use `user_confirm()` for confirmation prompts** (handles stderr flushing)
13
+
14
+ ## Basic Click Patterns
15
+
16
+ ```python
17
+ import click
18
+ from pathlib import Path
19
+
20
+ # ✅ CORRECT: Use click.echo for output
21
+ @click.command()
22
+ @click.argument("name")
23
+ def greet(name: str) -> None:
24
+ """Greet the user."""
25
+ click.echo(f"Hello, {name}!")
26
+
27
+ # ❌ WRONG: Using print()
28
+ @click.command()
29
+ def greet(name: str) -> None:
30
+ print(f"Hello, {name}!") # NEVER use print in CLI
31
+ ```
32
+
33
+ ## Error Handling in CLI
34
+
35
+ ```python
36
+ # ✅ CORRECT: CLI command error boundary
37
+ @click.command("create")
38
+ @click.pass_obj
39
+ def create(ctx: ErkContext, name: str) -> None:
40
+ """Create a worktree."""
41
+ try:
42
+ create_worktree(ctx, name)
43
+ except subprocess.CalledProcessError as e:
44
+ click.echo(f"Error: Git command failed: {e.stderr}", err=True)
45
+ raise SystemExit(1)
46
+ except ValueError as e:
47
+ click.echo(f"Error: {e}", err=True)
48
+ raise SystemExit(1)
49
+ ```
50
+
51
+ ## Output Patterns
52
+
53
+ ```python
54
+ # Regular output to stdout
55
+ click.echo("Processing complete")
56
+
57
+ # Error output to stderr
58
+ click.echo("Error: Operation failed", err=True)
59
+
60
+ # Colored output
61
+ click.echo(click.style("Success!", fg="green"))
62
+ click.echo(click.style("Warning!", fg="yellow", bold=True))
63
+
64
+ # Progress indication
65
+ with click.progressbar(items) as bar:
66
+ for item in bar:
67
+ process(item)
68
+ ```
69
+
70
+ ## Command Structure
71
+
72
+ ```python
73
+ @click.group()
74
+ @click.pass_context
75
+ def cli(ctx: click.Context) -> None:
76
+ """Main CLI entry point."""
77
+ ctx.ensure_object(dict)
78
+ ctx.obj["config"] = load_config()
79
+
80
+ @cli.command()
81
+ @click.option("--dry-run", is_flag=True, help="Perform dry run")
82
+ @click.argument("path", type=click.Path(exists=True))
83
+ @click.pass_obj
84
+ def sync(obj: dict, path: str, dry_run: bool) -> None:
85
+ """Sync the repository."""
86
+ config = obj["config"]
87
+
88
+ if dry_run:
89
+ click.echo("DRY RUN: Would sync...")
90
+ else:
91
+ perform_sync(Path(path), config)
92
+ click.echo("✓ Sync complete")
93
+ ```
94
+
95
+ ## User Interaction
96
+
97
+ ```python
98
+ from erk_shared.output import user_output, user_confirm
99
+
100
+ # ✅ CORRECT: Use user_confirm() for confirmation prompts
101
+ # This ensures stderr is flushed before prompting
102
+ user_output("Warning: This operation is destructive!")
103
+ if user_confirm("Are you sure?"):
104
+ perform_dangerous_operation()
105
+
106
+ # ❌ WRONG: Direct click.confirm() after user_output()
107
+ # This can hang because stderr isn't flushed before the prompt
108
+ user_output("Warning: This operation is destructive!")
109
+ if click.confirm("Are you sure?"): # BAD: buffering hang
110
+ perform_dangerous_operation()
111
+
112
+ # User input (for non-boolean prompts, use click.prompt directly)
113
+ name = click.prompt("Enter your name", default="User")
114
+
115
+ # Password input
116
+ password = click.prompt("Password", hide_input=True)
117
+
118
+ # Choice selection
119
+ choice = click.prompt(
120
+ "Select option",
121
+ type=click.Choice(["option1", "option2"]),
122
+ default="option1"
123
+ )
124
+ ```
125
+
126
+ ## Path Handling
127
+
128
+ ```python
129
+ @click.command()
130
+ @click.argument(
131
+ "input_file",
132
+ type=click.Path(exists=True, file_okay=True, dir_okay=False)
133
+ )
134
+ @click.argument(
135
+ "output_dir",
136
+ type=click.Path(exists=False, file_okay=False, dir_okay=True)
137
+ )
138
+ def process(input_file: str, output_dir: str) -> None:
139
+ """Process input file to output directory."""
140
+ input_path = Path(input_file)
141
+ output_path = Path(output_dir)
142
+
143
+ if not output_path.exists():
144
+ output_path.mkdir(parents=True)
145
+
146
+ click.echo(f"Processing {input_path} → {output_path}")
147
+ ```
148
+
149
+ ## Key Takeaways
150
+
151
+ 1. **Always click.echo()**: Never use print() in CLI code
152
+ 2. **Error to stderr**: Use `err=True` for error messages
153
+ 3. **Exit cleanly**: Use `raise SystemExit(1)` for errors
154
+ 4. **User-friendly**: Provide clear messages and confirmations
155
+ 5. **Type paths**: Use `click.Path()` for path arguments