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,653 @@
1
+ ---
2
+ ---
3
+
4
+ # Type Annotations - Python 3.13
5
+
6
+ This document provides complete, canonical type annotation guidance for Python 3.13. Python 3.13 implements PEP 649 (Deferred Evaluation of Annotations), fundamentally changing how annotations are evaluated.
7
+
8
+ ## Overview
9
+
10
+ **The key change: forward references and circular imports work naturally without `from __future__ import annotations`.**
11
+
12
+ All type features from previous versions (3.10-3.12) continue to work.
13
+
14
+ **What's new in 3.13:**
15
+
16
+ - PEP 649 deferred annotation evaluation
17
+ - Forward references work naturally (no quotes, no `from __future__`)
18
+ - Circular imports no longer cause annotation errors
19
+ - **DO NOT use `from __future__ import annotations`**
20
+
21
+ **Available from 3.12:**
22
+
23
+ - PEP 695 type parameter syntax: `def func[T](x: T) -> T`
24
+ - `type` statement for better type aliases
25
+
26
+ **Available from 3.11:**
27
+
28
+ - `Self` type for self-returning methods
29
+
30
+ ## Universal Philosophy
31
+
32
+ **Code Clarity:**
33
+
34
+ - Types serve as inline documentation
35
+ - Make function contracts explicit
36
+ - Reduce cognitive load when reading code
37
+ - Help understand data flow without tracing through implementation
38
+
39
+ **IDE Support:**
40
+
41
+ - Enable autocomplete and intelligent suggestions
42
+ - Catch typos and attribute errors before runtime
43
+ - Support refactoring tools (rename, move, extract)
44
+ - Provide jump-to-definition for typed objects
45
+
46
+ **Bug Prevention:**
47
+
48
+ - Catch type mismatches during static analysis
49
+ - Prevent None-related errors with explicit optional types
50
+ - Document expected input/output without running code
51
+ - Enable early detection of API contract violations
52
+
53
+ ## Consistency Rules
54
+
55
+ **All public APIs:**
56
+
57
+ - 🔴 MUST: Type all function parameters (except `self` and `cls`)
58
+ - 🔴 MUST: Type all function return values
59
+ - 🔴 MUST: Type all class attributes
60
+ - 🟡 SHOULD: Type module-level constants
61
+
62
+ **Internal code:**
63
+
64
+ - 🟡 SHOULD: Type function signatures where helpful for clarity
65
+ - 🟢 MAY: Type complex local variables where type isn't obvious
66
+ - 🟢 MAY: Omit types for obvious cases (e.g., `count = 0`)
67
+
68
+ ## Basic Collection Types
69
+
70
+ ✅ **PREFERRED** - Use built-in generic types:
71
+
72
+ ```python
73
+ names: list[str] = []
74
+ mapping: dict[str, int] = {}
75
+ unique_ids: set[str] = set()
76
+ coordinates: tuple[int, int] = (0, 0)
77
+ ```
78
+
79
+ ❌ **WRONG** - Don't use typing module equivalents:
80
+
81
+ ```python
82
+ from typing import List, Dict, Set, Tuple # Don't do this
83
+ names: List[str] = []
84
+ ```
85
+
86
+ **Why**: Built-in types are more concise, don't require imports, and are the modern Python standard (available since 3.10).
87
+
88
+ ## Union Types
89
+
90
+ ✅ **PREFERRED** - Use `|` operator:
91
+
92
+ ```python
93
+ def process(value: str | int) -> str:
94
+ return str(value)
95
+
96
+ def find_config(name: str) -> dict[str, str] | dict[str, int]:
97
+ ...
98
+
99
+ # Multiple unions
100
+ def parse(input: str | int | float) -> str:
101
+ return str(input)
102
+ ```
103
+
104
+ ❌ **WRONG** - Don't use `typing.Union`:
105
+
106
+ ```python
107
+ from typing import Union
108
+ def process(value: Union[str, int]) -> str: # Don't do this
109
+ ...
110
+ ```
111
+
112
+ ## Optional Types
113
+
114
+ ✅ **PREFERRED** - Use `X | None`:
115
+
116
+ ```python
117
+ def find_user(id: str) -> User | None:
118
+ """Returns user or None if not found."""
119
+ if id in users:
120
+ return users[id]
121
+ return None
122
+ ```
123
+
124
+ ❌ **WRONG** - Don't use `typing.Optional`:
125
+
126
+ ```python
127
+ from typing import Optional
128
+ def find_user(id: str) -> Optional[User]: # Don't do this
129
+ ...
130
+ ```
131
+
132
+ ## Callable Types
133
+
134
+ ✅ **PREFERRED** - Use `collections.abc.Callable`:
135
+
136
+ ```python
137
+ from collections.abc import Callable
138
+
139
+ # Function that takes int, returns str
140
+ processor: Callable[[int], str] = str
141
+
142
+ # Function with no args, returns None
143
+ callback: Callable[[], None] = lambda: None
144
+
145
+ # Function with multiple args
146
+ validator: Callable[[str, int], bool] = lambda s, i: len(s) > i
147
+ ```
148
+
149
+ ## Interfaces: ABC vs Protocol
150
+
151
+ ✅ **PREFERRED** - Use ABC for interfaces:
152
+
153
+ ```python
154
+ from abc import ABC, abstractmethod
155
+
156
+ class Repository(ABC):
157
+ @abstractmethod
158
+ def get(self, id: str) -> User | None:
159
+ """Get user by ID."""
160
+
161
+ @abstractmethod
162
+ def save(self, user: User) -> None:
163
+ """Save user."""
164
+ ```
165
+
166
+ 🟡 **VALID** - Use Protocol only for structural typing:
167
+
168
+ ```python
169
+ from typing import Protocol
170
+
171
+ class Drawable(Protocol):
172
+ def draw(self) -> None: ...
173
+
174
+ def render(obj: Drawable) -> None:
175
+ obj.draw()
176
+ ```
177
+
178
+ **Dignified Python prefers ABC** because it makes inheritance and intent explicit.
179
+
180
+ ## Self Type for Self-Returning Methods (3.11+)
181
+
182
+ ✅ **PREFERRED** - Use Self for methods that return the instance:
183
+
184
+ ```python
185
+ from typing import Self
186
+
187
+ class Builder:
188
+ def set_name(self, name: str) -> Self:
189
+ self.name = name
190
+ return self
191
+
192
+ def set_value(self, value: int) -> Self:
193
+ self.value = value
194
+ return self
195
+ ```
196
+
197
+ ## Generic Functions with PEP 695 (3.12+)
198
+
199
+ ✅ **PREFERRED** - Use PEP 695 type parameter syntax:
200
+
201
+ ```python
202
+ def first[T](items: list[T]) -> T | None:
203
+ """Return first item or None if empty."""
204
+ if not items:
205
+ return None
206
+ return items[0]
207
+
208
+ def identity[T](value: T) -> T:
209
+ """Return value unchanged."""
210
+ return value
211
+
212
+ # Multiple type parameters
213
+ def zip_dicts[K, V](keys: list[K], values: list[V]) -> dict[K, V]:
214
+ """Create dict from separate key and value lists."""
215
+ return dict(zip(keys, values))
216
+ ```
217
+
218
+ 🟡 **VALID** - TypeVar still works:
219
+
220
+ ```python
221
+ from typing import TypeVar
222
+
223
+ T = TypeVar("T")
224
+
225
+ def first(items: list[T]) -> T | None:
226
+ if not items:
227
+ return None
228
+ return items[0]
229
+ ```
230
+
231
+ **Note**: Prefer PEP 695 syntax for simple generics. TypeVar is still needed for constraints/bounds.
232
+
233
+ ## Generic Classes with PEP 695 (3.12+)
234
+
235
+ ✅ **PREFERRED** - Use PEP 695 class syntax:
236
+
237
+ ```python
238
+ class Stack[T]:
239
+ """A generic stack data structure."""
240
+
241
+ def __init__(self) -> None:
242
+ self._items: list[T] = []
243
+
244
+ def push(self, item: T) -> Self:
245
+ self._items.append(item)
246
+ return self
247
+
248
+ def pop(self) -> T | None:
249
+ if not self._items:
250
+ return None
251
+ return self._items.pop()
252
+
253
+ # Usage
254
+ int_stack = Stack[int]()
255
+ int_stack.push(42).push(43)
256
+ ```
257
+
258
+ 🟡 **VALID** - Generic with TypeVar still works:
259
+
260
+ ```python
261
+ from typing import Generic, TypeVar
262
+
263
+ T = TypeVar("T")
264
+
265
+ class Stack(Generic[T]):
266
+ def __init__(self) -> None:
267
+ self._items: list[T] = []
268
+ # ... rest of implementation
269
+ ```
270
+
271
+ **Note**: PEP 695 is cleaner - no imports needed, type parameter scope is local to class.
272
+
273
+ ## Type Parameter Bounds (3.12+)
274
+
275
+ ✅ **Use bounds with PEP 695**:
276
+
277
+ ```python
278
+ class Comparable:
279
+ def compare(self, other: object) -> int:
280
+ ...
281
+
282
+ def max_value[T: Comparable](items: list[T]) -> T:
283
+ """Get maximum value from comparable items."""
284
+ return max(items, key=lambda x: x)
285
+ ```
286
+
287
+ ## Constrained TypeVars (Still Use TypeVar)
288
+
289
+ ✅ **Use TypeVar for specific type constraints**:
290
+
291
+ ```python
292
+ from typing import TypeVar
293
+
294
+ # Constrained to specific types - must use TypeVar
295
+ Numeric = TypeVar("Numeric", int, float)
296
+
297
+ def add(a: Numeric, b: Numeric) -> Numeric:
298
+ return a + b
299
+ ```
300
+
301
+ ❌ **WRONG** - PEP 695 doesn't support constraints:
302
+
303
+ ```python
304
+ # This doesn't constrain to int|float
305
+ def add[Numeric](a: Numeric, b: Numeric) -> Numeric:
306
+ return a + b
307
+ ```
308
+
309
+ ## Type Aliases with type Statement (3.12+)
310
+
311
+ ✅ **PREFERRED** - Use `type` statement:
312
+
313
+ ```python
314
+ # Simple alias
315
+ type UserId = str
316
+ type Config = dict[str, str | int | bool]
317
+
318
+ # Generic type alias
319
+ type Result[T] = tuple[T, str | None]
320
+
321
+ def process(value: str) -> Result[int]:
322
+ try:
323
+ return (int(value), None)
324
+ except ValueError as e:
325
+ return (0, str(e))
326
+ ```
327
+
328
+ 🟡 **VALID** - Simple assignment still works:
329
+
330
+ ```python
331
+ UserId = str # Still valid
332
+ Config = dict[str, str | int | bool] # Still valid
333
+ ```
334
+
335
+ **Note**: `type` statement is more explicit and works better with generics.
336
+
337
+ ## Forward References and Circular Imports (NEW in 3.13)
338
+
339
+ ✅ **CORRECT** - Just works naturally with PEP 649:
340
+
341
+ ```python
342
+ # Forward reference - no quotes needed!
343
+ class Node:
344
+ def __init__(self, value: int, parent: Node | None = None):
345
+ self.value = value
346
+ self.parent = parent
347
+
348
+ # Circular imports - just works!
349
+ # a.py
350
+ from b import B
351
+
352
+ class A:
353
+ def method(self) -> B:
354
+ ...
355
+
356
+ # b.py
357
+ from a import A
358
+
359
+ class B:
360
+ def method(self) -> A:
361
+ ...
362
+
363
+ # Recursive types - no future needed!
364
+ type JsonValue = dict[str, JsonValue] | list[JsonValue] | str | int | float | bool | None
365
+ ```
366
+
367
+ ❌ **WRONG** - Don't use `from __future__ import annotations`:
368
+
369
+ ```python
370
+ from __future__ import annotations # DON'T DO THIS in Python 3.13
371
+
372
+ class Node:
373
+ def __init__(self, value: int, parent: Node | None = None):
374
+ ...
375
+ ```
376
+
377
+ **Why avoid `from __future__ import annotations` in 3.13:**
378
+
379
+ - Unnecessary - PEP 649 provides better default behavior
380
+ - Can cause confusion
381
+ - Masks the native 3.13 deferred evaluation
382
+ - Prevents you from leveraging improvements
383
+
384
+ ## Complete Examples
385
+
386
+ ### Tree Structure with Natural Forward References
387
+
388
+ ```python
389
+ from typing import Self
390
+ from collections.abc import Callable
391
+
392
+ class Node[T]:
393
+ """Tree node - forward reference works naturally in 3.13!"""
394
+
395
+ def __init__(
396
+ self,
397
+ value: T,
398
+ parent: Node[T] | None = None, # Forward ref, no quotes!
399
+ children: list[Node[T]] | None = None, # Forward ref, no quotes!
400
+ ) -> None:
401
+ self.value = value
402
+ self.parent = parent
403
+ self.children = children or []
404
+
405
+ def add_child(self, child: Node[T]) -> Self:
406
+ """Add child and return self for chaining."""
407
+ self.children.append(child)
408
+ child.parent = self
409
+ return self
410
+
411
+ def find(self, predicate: Callable[[T], bool]) -> Node[T] | None:
412
+ """Find first node matching predicate."""
413
+ if predicate(self.value):
414
+ return self
415
+
416
+ for child in self.children:
417
+ result = child.find(predicate)
418
+ if result:
419
+ return result
420
+
421
+ return None
422
+
423
+ # Usage - all type-safe with no __future__ import!
424
+ root = Node[int](1)
425
+ root.add_child(Node[int](2)).add_child(Node[int](3))
426
+ ```
427
+
428
+ ### Generic Repository with PEP 695
429
+
430
+ ```python
431
+ from abc import ABC, abstractmethod
432
+ from typing import Self
433
+
434
+ class Entity[T]:
435
+ """Base class for entities."""
436
+
437
+ def __init__(self, id: T) -> None:
438
+ self.id = id
439
+
440
+ class Repository[T](ABC):
441
+ """Generic repository interface."""
442
+
443
+ @abstractmethod
444
+ def get(self, id: str) -> T | None:
445
+ """Get entity by ID."""
446
+
447
+ @abstractmethod
448
+ def save(self, entity: T) -> None:
449
+ """Save entity."""
450
+
451
+ @abstractmethod
452
+ def delete(self, id: str) -> bool:
453
+ """Delete entity, return True if deleted."""
454
+
455
+ class User(Entity[str]):
456
+ def __init__(self, id: str, name: str) -> None:
457
+ super().__init__(id)
458
+ self.name = name
459
+
460
+ class UserRepository(Repository[User]):
461
+ def __init__(self) -> None:
462
+ self._users: dict[str, User] = {}
463
+
464
+ def get(self, id: str) -> User | None:
465
+ if id not in self._users:
466
+ return None
467
+ return self._users[id]
468
+
469
+ def save(self, entity: User) -> None:
470
+ self._users[entity.id] = entity
471
+
472
+ def delete(self, id: str) -> bool:
473
+ if id not in self._users:
474
+ return False
475
+ del self._users[id]
476
+ return True
477
+ ```
478
+
479
+ ## General Best Practices
480
+
481
+ **Prefer specificity:**
482
+
483
+ ```python
484
+ # ✅ GOOD - Specific
485
+ def get_config() -> dict[str, str | int]:
486
+ ...
487
+
488
+ # ❌ WRONG - Too vague
489
+ def get_config() -> dict:
490
+ ...
491
+ ```
492
+
493
+ **Use Union sparingly:**
494
+
495
+ ```python
496
+ # ✅ GOOD - Union only when necessary
497
+ def process(value: str | int) -> str:
498
+ ...
499
+
500
+ # ❌ WRONG - Too permissive
501
+ def process(value: str | int | list | dict) -> str | None | list:
502
+ ...
503
+ ```
504
+
505
+ **Be explicit with None:**
506
+
507
+ ```python
508
+ # ✅ GOOD - Explicit optional
509
+ def find_user(id: str) -> User | None:
510
+ ...
511
+
512
+ # ❌ WRONG - Implicit None return
513
+ def find_user(id: str) -> User:
514
+ return None # Type checker error!
515
+ ```
516
+
517
+ **Avoid Any when possible:**
518
+
519
+ ```python
520
+ # ✅ GOOD - Specific type
521
+ def serialize(obj: User | Config) -> str:
522
+ ...
523
+
524
+ # ❌ WRONG - Defeats purpose of types
525
+ from typing import Any
526
+ def serialize(obj: Any) -> str:
527
+ ...
528
+ ```
529
+
530
+ ## When to Use Types
531
+
532
+ **Always type:**
533
+
534
+ - Public function signatures (parameters + return)
535
+ - Class attributes (including private ones)
536
+ - Function parameters that cross module boundaries
537
+ - Return values that aren't immediately obvious
538
+
539
+ **Type when helpful:**
540
+
541
+ - Complex local variables
542
+ - Closures and nested functions
543
+ - Lambda expressions used as callbacks
544
+
545
+ **Can skip:**
546
+
547
+ - Obvious cases: `count = 0`, `name = "example"`
548
+ - Trivial private helpers
549
+ - Test fixture setup code (if types add no clarity)
550
+
551
+ ## Type Checking with ty
552
+
553
+ Dignified Python uses ty for static type checking:
554
+
555
+ ```bash
556
+ # Check all files
557
+ ty check
558
+
559
+ # Check specific file
560
+ ty check src/mymodule.py
561
+
562
+ # Check with specific Python version
563
+ ty check --python-version 3.13
564
+ ```
565
+
566
+ **Configuration** (in `pyproject.toml`):
567
+
568
+ ```toml
569
+ [tool.ty.environment]
570
+ python-version = "3.13"
571
+ ```
572
+
573
+ ## Anti-Patterns
574
+
575
+ **❌ Don't ignore type errors with `# type: ignore`**
576
+
577
+ ```python
578
+ # ❌ WRONG - Hiding type error
579
+ result = unsafe_function() # type: ignore
580
+
581
+ # ✅ CORRECT - Fix the type error
582
+ result: Expected = cast(Expected, unsafe_function())
583
+ ```
584
+
585
+ **❌ Don't use bare Exception in type hints**
586
+
587
+ ```python
588
+ # ❌ WRONG - No value from typing exception
589
+ def risky() -> str | Exception:
590
+ ...
591
+
592
+ # ✅ CORRECT - Let exceptions bubble
593
+ def risky() -> str:
594
+ ... # Raises ValueError on error
595
+ ```
596
+
597
+ **❌ Don't over-type simple cases**
598
+
599
+ ```python
600
+ # ❌ WRONG - Obvious from context
601
+ def add_numbers(a: int, b: int) -> int:
602
+ result: int = a + b # Unnecessary type annotation
603
+ return result
604
+
605
+ # ✅ CORRECT - Type only signature
606
+ def add_numbers(a: int, b: int) -> int:
607
+ result = a + b # Type is obvious
608
+ return result
609
+ ```
610
+
611
+ ## Migration from 3.10/3.11
612
+
613
+ If migrating from Python 3.10/3.11:
614
+
615
+ 1. **Remove `from __future__ import annotations`** - No longer needed
616
+ 2. **Consider upgrading to PEP 695 syntax** - Cleaner generics
617
+ 3. **Use `type` statement for aliases** - More explicit than assignment
618
+ 4. **Remove quoted forward references** - They work naturally now
619
+
620
+ ```python
621
+ # Python 3.10/3.11
622
+ from __future__ import annotations
623
+ from typing import TypeVar, Generic
624
+
625
+ T = TypeVar("T")
626
+
627
+ class Node(Generic[T]):
628
+ def __init__(self, value: T, parent: "Node[T] | None" = None):
629
+ ...
630
+
631
+ # Python 3.13
632
+ from typing import Self
633
+
634
+ class Node[T]:
635
+ def __init__(self, value: T, parent: Node[T] | None = None):
636
+ ...
637
+ ```
638
+
639
+ ## What typing imports are still needed?
640
+
641
+ **Very rare:**
642
+
643
+ - `TypeVar` - Only for constrained/bounded type variables
644
+ - `Any` - Use sparingly when type truly unknown
645
+ - `Protocol` - Structural typing (prefer ABC)
646
+ - `TYPE_CHECKING` - Conditional imports to avoid circular dependencies
647
+
648
+ **Never needed:**
649
+
650
+ - `List`, `Dict`, `Set`, `Tuple` - Use built-in types
651
+ - `Union` - Use `|` operator
652
+ - `Optional` - Use `X | None`
653
+ - `Generic` - Use PEP 695 class syntax
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: erk-diff-analysis
3
+ description: Internal skill for commit message generation. Only load when explicitly requested by name or invoked by commands.
4
+ ---
5
+
6
+ # Diff Analysis
7
+
8
+ This skill provides the commit message generation prompt used by PR submission commands.
9
+
10
+ ## When to Use
11
+
12
+ Only load this skill when:
13
+
14
+ - Explicitly requested by name (`erk-diff-analysis`)
15
+ - Invoked by commands like `/erk:git-pr-push`
16
+
17
+ ## Usage
18
+
19
+ Load `references/commit-message-prompt.md` when analyzing a diff, then apply its principles to generate output.
20
+
21
+ ## Key Principles
22
+
23
+ - Be concise and strategic - focus on significant changes
24
+ - Use component-level descriptions - reference modules/components, not individual functions
25
+ - Highlight breaking changes prominently
26
+ - Note test coverage patterns
27
+ - Use relative paths from repository root