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,85 @@
1
+ """Early dogfooder health checks for erk doctor command.
2
+
3
+ These checks are for temporary issues that affect early dogfooders and
4
+ can be deleted once the issues are resolved. The subpackage structure
5
+ makes it easy to remove individual checks or the entire package.
6
+
7
+ To delete one check:
8
+ 1. Remove the check's module file
9
+ 2. Remove from imports and EARLY_DOGFOODER_CHECK_NAMES below
10
+
11
+ To delete all dogfooder checks:
12
+ 1. Delete this directory (src/erk/core/health_checks_dogfooder/)
13
+ 2. Remove import from health_checks.py, remove run_early_dogfooder_checks() call
14
+ 3. Remove early dogfooder category handling from doctor.py
15
+ """
16
+
17
+ from pathlib import Path
18
+
19
+ from erk.core.health_checks import CheckResult
20
+ from erk.core.health_checks_dogfooder.deprecated_dot_agent_config import (
21
+ check_deprecated_dot_agent_config,
22
+ )
23
+ from erk.core.health_checks_dogfooder.legacy_claude_docs import (
24
+ check_legacy_claude_docs,
25
+ )
26
+ from erk.core.health_checks_dogfooder.legacy_config_locations import (
27
+ check_legacy_config_locations,
28
+ )
29
+ from erk.core.health_checks_dogfooder.legacy_erk_docs_agent import (
30
+ check_legacy_erk_docs_agent,
31
+ )
32
+ from erk.core.health_checks_dogfooder.legacy_erk_kits_folder import (
33
+ check_legacy_erk_kits_folder,
34
+ )
35
+ from erk.core.health_checks_dogfooder.legacy_hook_settings import (
36
+ check_legacy_hook_settings,
37
+ )
38
+ from erk.core.health_checks_dogfooder.legacy_kit_yaml import (
39
+ check_legacy_kit_yaml,
40
+ )
41
+ from erk.core.health_checks_dogfooder.legacy_kits_toml import (
42
+ check_legacy_kits_toml,
43
+ )
44
+ from erk.core.health_checks_dogfooder.outdated_erk_skill import (
45
+ check_outdated_erk_skill,
46
+ )
47
+
48
+ # Names of all early dogfooder checks - used by doctor.py for category grouping
49
+ EARLY_DOGFOODER_CHECK_NAMES: set[str] = {
50
+ "deprecated-dot-agent-config",
51
+ "legacy-claude-docs",
52
+ "legacy-config",
53
+ "legacy-erk-docs",
54
+ "legacy-erk-kits-folder",
55
+ "legacy-hook-settings",
56
+ "legacy-kit-yaml",
57
+ "legacy-kits-toml",
58
+ "outdated-erk-skill",
59
+ }
60
+
61
+
62
+ def run_early_dogfooder_checks(
63
+ repo_root: Path,
64
+ metadata_dir: Path | None,
65
+ ) -> list[CheckResult]:
66
+ """Run all early dogfooder health checks.
67
+
68
+ Args:
69
+ repo_root: Path to the repository root
70
+ metadata_dir: Path to ~/.erk/repos/<repo>/ metadata directory, if known
71
+
72
+ Returns:
73
+ List of CheckResult objects from all dogfooder checks
74
+ """
75
+ return [
76
+ check_deprecated_dot_agent_config(repo_root),
77
+ check_legacy_claude_docs(repo_root),
78
+ check_legacy_config_locations(repo_root, metadata_dir),
79
+ check_legacy_erk_docs_agent(repo_root),
80
+ check_legacy_erk_kits_folder(repo_root),
81
+ check_legacy_hook_settings(repo_root),
82
+ check_legacy_kit_yaml(repo_root),
83
+ check_legacy_kits_toml(repo_root),
84
+ check_outdated_erk_skill(repo_root),
85
+ ]
@@ -0,0 +1,64 @@
1
+ """Check for deprecated [tool.dot-agent] config in pyproject.toml.
2
+
3
+ This is a temporary check for early dogfooders. Delete this file once
4
+ all users have migrated to [tool.erk].
5
+ """
6
+
7
+ from pathlib import Path
8
+
9
+ import tomli
10
+
11
+ from erk.core.health_checks import CheckResult
12
+
13
+
14
+ def check_deprecated_dot_agent_config(repo_root: Path) -> CheckResult:
15
+ """Check for deprecated [tool.dot-agent] config in pyproject.toml.
16
+
17
+ This check detects the old configuration format and suggests migration
18
+ to [tool.erk].
19
+
20
+ Args:
21
+ repo_root: Path to the repository root
22
+
23
+ Returns:
24
+ CheckResult indicating whether deprecated config was found
25
+ """
26
+ pyproject_path = repo_root / "pyproject.toml"
27
+ if not pyproject_path.exists():
28
+ return CheckResult(
29
+ name="deprecated-dot-agent-config",
30
+ passed=True,
31
+ message="No deprecated [tool.dot-agent] config found",
32
+ )
33
+
34
+ with open(pyproject_path, "rb") as f:
35
+ data = tomli.load(f)
36
+
37
+ # Check for [tool.dot-agent] section
38
+ if "tool" not in data:
39
+ return CheckResult(
40
+ name="deprecated-dot-agent-config",
41
+ passed=True,
42
+ message="No deprecated [tool.dot-agent] config found",
43
+ )
44
+ if "dot-agent" not in data["tool"]:
45
+ return CheckResult(
46
+ name="deprecated-dot-agent-config",
47
+ passed=True,
48
+ message="No deprecated [tool.dot-agent] config found",
49
+ )
50
+
51
+ # Found deprecated config - return failure with remediation
52
+ return CheckResult(
53
+ name="deprecated-dot-agent-config",
54
+ passed=False,
55
+ message="Deprecated [tool.dot-agent] config in pyproject.toml",
56
+ details=(
57
+ "The [tool.dot-agent] section is deprecated.\n"
58
+ "\n"
59
+ "Remediation:\n"
60
+ " 1. Edit pyproject.toml\n"
61
+ " 2. Remove the [tool.dot-agent] section\n"
62
+ " 3. Use [tool.erk] for any erk configuration"
63
+ ),
64
+ )
@@ -0,0 +1,69 @@
1
+ """Check for legacy .claude/docs/ directory.
2
+
3
+ This is a temporary check for early dogfooders. Delete this file once
4
+ all users have migrated their docs to skills.
5
+ """
6
+
7
+ from pathlib import Path
8
+
9
+ from erk.core.health_checks import CheckResult
10
+
11
+
12
+ def check_legacy_claude_docs(repo_root: Path) -> CheckResult:
13
+ """Check for legacy .claude/docs/ directory.
14
+
15
+ Detects .claude/docs/ which is no longer supported. Standalone docs
16
+ should be converted to skills under .claude/skills/<name>/SKILL.md.
17
+
18
+ Args:
19
+ repo_root: Path to the repository root
20
+
21
+ Returns:
22
+ CheckResult with warning if legacy docs directory found
23
+ """
24
+ docs_path = repo_root / ".claude" / "docs"
25
+
26
+ if not docs_path.exists():
27
+ return CheckResult(
28
+ name="legacy-claude-docs",
29
+ passed=True,
30
+ message="No legacy .claude/docs/ found",
31
+ )
32
+
33
+ # Check if it's a directory with markdown files
34
+ if not docs_path.is_dir():
35
+ return CheckResult(
36
+ name="legacy-claude-docs",
37
+ passed=True,
38
+ message="No legacy .claude/docs/ found",
39
+ )
40
+
41
+ # Find markdown files
42
+ md_files = list(docs_path.glob("*.md"))
43
+ if not md_files:
44
+ return CheckResult(
45
+ name="legacy-claude-docs",
46
+ passed=True,
47
+ message="No legacy .claude/docs/ found (empty directory)",
48
+ )
49
+
50
+ # Build details with file list
51
+ details_lines: list[str] = [
52
+ "Standalone docs in .claude/docs/ no longer supported.",
53
+ "",
54
+ "Files found:",
55
+ ]
56
+ for md_file in md_files[:5]: # Limit display to first 5
57
+ details_lines.append(f" - {md_file.name}")
58
+ if len(md_files) > 5:
59
+ details_lines.append(f" ... and {len(md_files) - 5} more")
60
+ details_lines.append("")
61
+ details_lines.append("Convert to skills under .claude/skills/<name>/SKILL.md")
62
+
63
+ return CheckResult(
64
+ name="legacy-claude-docs",
65
+ passed=True, # Warning only, doesn't fail
66
+ warning=True,
67
+ message=f"Found {len(md_files)} doc(s) in .claude/docs/",
68
+ details="\n".join(details_lines),
69
+ )
@@ -0,0 +1,122 @@
1
+ """Check for legacy config.toml files that should be migrated.
2
+
3
+ This is a temporary check for early dogfooders. Delete this file once
4
+ all users have migrated their config to .erk/config.toml.
5
+ """
6
+
7
+ from dataclasses import dataclass
8
+ from pathlib import Path
9
+
10
+ from erk.core.health_checks import CheckResult
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class LegacyConfigLocation:
15
+ """Information about a legacy config.toml location."""
16
+
17
+ path: Path
18
+ description: str
19
+
20
+
21
+ def detect_legacy_config_locations(
22
+ repo_root: Path,
23
+ metadata_dir: Path | None,
24
+ ) -> list[LegacyConfigLocation]:
25
+ """Detect legacy config.toml files at old locations.
26
+
27
+ Checks for config files at:
28
+ - repo_root/config.toml (legacy location)
29
+ - metadata_dir/config.toml (legacy location)
30
+
31
+ Args:
32
+ repo_root: Path to the repository root
33
+ metadata_dir: Path to ~/.erk/repos/<repo>/ metadata directory, if known
34
+
35
+ Returns:
36
+ List of LegacyConfigLocation objects for any legacy configs found
37
+ """
38
+ legacy_locations: list[LegacyConfigLocation] = []
39
+
40
+ # Check for config at repo root (legacy location)
41
+ repo_root_config = repo_root / "config.toml"
42
+ if repo_root_config.exists():
43
+ legacy_locations.append(
44
+ LegacyConfigLocation(
45
+ path=repo_root_config,
46
+ description="repo root (legacy location)",
47
+ )
48
+ )
49
+
50
+ # Check for dot-agent.toml at repo root (legacy config location)
51
+ repo_root_dot_agent = repo_root / "dot-agent.toml"
52
+ if repo_root_dot_agent.exists():
53
+ legacy_locations.append(
54
+ LegacyConfigLocation(
55
+ path=repo_root_dot_agent,
56
+ description="repo root (legacy config)",
57
+ )
58
+ )
59
+
60
+ # Check for config in ~/.erk/repos/<repo>/ (legacy location)
61
+ if metadata_dir is not None:
62
+ metadata_dir_config = metadata_dir / "config.toml"
63
+ if metadata_dir_config.exists():
64
+ legacy_locations.append(
65
+ LegacyConfigLocation(
66
+ path=metadata_dir_config,
67
+ description="~/.erk/repos/ metadata dir (legacy location)",
68
+ )
69
+ )
70
+
71
+ return legacy_locations
72
+
73
+
74
+ def check_legacy_config_locations(
75
+ repo_root: Path,
76
+ metadata_dir: Path | None,
77
+ ) -> CheckResult:
78
+ """Check for legacy config.toml files that should be migrated.
79
+
80
+ Detects config files at old locations (repo root, metadata dir) that should
81
+ be moved to .erk/config.toml. This is a warning-level check for early dogfooders.
82
+
83
+ Args:
84
+ repo_root: Path to the repository root
85
+ metadata_dir: Path to ~/.erk/repos/<repo>/ metadata directory, if known
86
+
87
+ Returns:
88
+ CheckResult with warning if legacy configs found
89
+ """
90
+ # Skip if primary location exists (config already migrated)
91
+ primary_path = repo_root / ".erk" / "config.toml"
92
+ if primary_path.exists():
93
+ return CheckResult(
94
+ name="legacy-config",
95
+ passed=True,
96
+ message="Config at primary location (.erk/config.toml)",
97
+ )
98
+
99
+ # Detect any legacy configs
100
+ legacy_locations = detect_legacy_config_locations(repo_root, metadata_dir)
101
+
102
+ if not legacy_locations:
103
+ return CheckResult(
104
+ name="legacy-config",
105
+ passed=True,
106
+ message="No legacy config files found",
107
+ )
108
+
109
+ # Build details with migration instructions
110
+ details_lines: list[str] = ["Legacy config files found:"]
111
+ for loc in legacy_locations:
112
+ details_lines.append(f" - {loc.path} ({loc.description})")
113
+ details_lines.append("")
114
+ details_lines.append(f"Move to: {primary_path}")
115
+
116
+ return CheckResult(
117
+ name="legacy-config",
118
+ passed=True, # Warning only - doesn't fail the check
119
+ warning=True,
120
+ message=f"Found {len(legacy_locations)} legacy config file(s)",
121
+ details="\n".join(details_lines),
122
+ )
@@ -0,0 +1,61 @@
1
+ """Check for legacy .erk/docs/agent/ directory.
2
+
3
+ This is a temporary check for early dogfooders. Delete this file once
4
+ all users have migrated their docs to docs/learned/.
5
+ """
6
+
7
+ from pathlib import Path
8
+
9
+ from erk.core.health_checks import CheckResult
10
+
11
+
12
+ def check_legacy_erk_docs_agent(repo_root: Path) -> CheckResult:
13
+ """Check for legacy .erk/docs/agent/ directory.
14
+
15
+ Detects .erk/docs/agent/ which should be moved to docs/learned/.
16
+ This is an erk-specific documentation location change.
17
+
18
+ Args:
19
+ repo_root: Path to the repository root
20
+
21
+ Returns:
22
+ CheckResult with warning if legacy docs directory found
23
+ """
24
+ agent_docs_path = repo_root / ".erk" / "docs" / "agent"
25
+
26
+ if not agent_docs_path.exists():
27
+ return CheckResult(
28
+ name="legacy-erk-docs",
29
+ passed=True,
30
+ message="No legacy .erk/docs/agent/ found",
31
+ )
32
+
33
+ if not agent_docs_path.is_dir():
34
+ return CheckResult(
35
+ name="legacy-erk-docs",
36
+ passed=True,
37
+ message="No legacy .erk/docs/agent/ found",
38
+ )
39
+
40
+ # Count files in the directory
41
+ files = list(agent_docs_path.glob("**/*"))
42
+ file_count = len([f for f in files if f.is_file()])
43
+
44
+ if file_count == 0:
45
+ return CheckResult(
46
+ name="legacy-erk-docs",
47
+ passed=True,
48
+ message="No legacy .erk/docs/agent/ found (empty directory)",
49
+ )
50
+
51
+ return CheckResult(
52
+ name="legacy-erk-docs",
53
+ passed=True, # Warning only, doesn't fail
54
+ warning=True,
55
+ message=f"Legacy docs location found ({file_count} file(s))",
56
+ details=(
57
+ f"Directory: {agent_docs_path}\n"
58
+ "Documentation has moved from .erk/docs/agent/ to docs/learned/.\n"
59
+ "Move your documentation files to docs/learned/."
60
+ ),
61
+ )
@@ -0,0 +1,60 @@
1
+ """Check for legacy .erk/kits/ directory.
2
+
3
+ This is a temporary check for early dogfooders. Delete this file once
4
+ all users have removed their kits folder.
5
+ """
6
+
7
+ from pathlib import Path
8
+
9
+ from erk.core.health_checks import CheckResult
10
+
11
+
12
+ def check_legacy_erk_kits_folder(repo_root: Path) -> CheckResult:
13
+ """Check for legacy .erk/kits/ directory.
14
+
15
+ Detects .erk/kits/ which is no longer used. The kit system has been
16
+ removed entirely from erk.
17
+
18
+ Args:
19
+ repo_root: Path to the repository root
20
+
21
+ Returns:
22
+ CheckResult with warning if legacy kits folder found
23
+ """
24
+ kits_path = repo_root / ".erk" / "kits"
25
+
26
+ if not kits_path.exists():
27
+ return CheckResult(
28
+ name="legacy-erk-kits-folder",
29
+ passed=True,
30
+ message="No legacy .erk/kits/ found",
31
+ )
32
+
33
+ if not kits_path.is_dir():
34
+ return CheckResult(
35
+ name="legacy-erk-kits-folder",
36
+ passed=True,
37
+ message="No legacy .erk/kits/ found",
38
+ )
39
+
40
+ # Count items in the directory
41
+ items = list(kits_path.iterdir())
42
+
43
+ if not items:
44
+ return CheckResult(
45
+ name="legacy-erk-kits-folder",
46
+ passed=True,
47
+ message="No legacy .erk/kits/ found (empty directory)",
48
+ )
49
+
50
+ return CheckResult(
51
+ name="legacy-erk-kits-folder",
52
+ passed=True, # Warning only, doesn't fail
53
+ warning=True,
54
+ message=f"Legacy kits folder found ({len(items)} item(s))",
55
+ details=(
56
+ f"Directory: {kits_path}\n"
57
+ "The kit system has been removed from erk.\n"
58
+ "You can safely delete the .erk/kits/ directory."
59
+ ),
60
+ )
@@ -0,0 +1,104 @@
1
+ """Check for legacy hook settings using erk kit exec commands.
2
+
3
+ This is a temporary check for early dogfooders. Delete this file once
4
+ all users have migrated their hooks to the unified hook script.
5
+ """
6
+
7
+ import json
8
+ from pathlib import Path
9
+
10
+ from erk.core.health_checks import CheckResult
11
+
12
+
13
+ def detect_legacy_hook_commands(repo_root: Path) -> list[str]:
14
+ """Detect legacy 'erk kit exec' hook commands in Claude settings.
15
+
16
+ Checks .claude/settings.json for any hooks that use the old
17
+ 'erk kit exec' or 'uvx erk' patterns.
18
+
19
+ Args:
20
+ repo_root: Path to the repository root
21
+
22
+ Returns:
23
+ List of legacy hook command strings found
24
+ """
25
+ settings_path = repo_root / ".claude" / "settings.json"
26
+ if not settings_path.exists():
27
+ return []
28
+
29
+ content = settings_path.read_text(encoding="utf-8")
30
+ settings = json.loads(content)
31
+
32
+ hooks_section = settings.get("hooks", {})
33
+ legacy_commands: list[str] = []
34
+
35
+ # Check all hook types (UserPromptSubmit, etc.)
36
+ for hook_entries in hooks_section.values():
37
+ if not isinstance(hook_entries, list):
38
+ continue
39
+ for hook_entry in hook_entries:
40
+ if not isinstance(hook_entry, dict):
41
+ continue
42
+ # Handle nested structure: {matcher: ..., hooks: [...]}
43
+ nested_hooks = hook_entry.get("hooks", [])
44
+ if nested_hooks:
45
+ for hook in nested_hooks:
46
+ if not isinstance(hook, dict):
47
+ continue
48
+ command = hook.get("command", "")
49
+ if _is_legacy_hook_command(command):
50
+ legacy_commands.append(command)
51
+ # Handle flat structure: {type: command, command: ...}
52
+ command = hook_entry.get("command", "")
53
+ if _is_legacy_hook_command(command):
54
+ legacy_commands.append(command)
55
+
56
+ return legacy_commands
57
+
58
+
59
+ def _is_legacy_hook_command(command: str) -> bool:
60
+ """Check if a hook command uses legacy erk kit exec patterns."""
61
+ if not command:
62
+ return False
63
+ # Check for 'erk kit exec' or 'uvx erk' patterns
64
+ return "erk kit exec" in command or ("uvx" in command and "erk@" in command)
65
+
66
+
67
+ def check_legacy_hook_settings(repo_root: Path) -> CheckResult:
68
+ """Check for legacy hook settings using erk kit exec commands.
69
+
70
+ Detects .claude/settings.json hooks that use old 'erk kit exec' or
71
+ 'uvx erk@X.Y.Z kit exec' patterns. These won't work with current erk.
72
+
73
+ Args:
74
+ repo_root: Path to the repository root
75
+
76
+ Returns:
77
+ CheckResult with failure if legacy hooks found
78
+ """
79
+ legacy_commands = detect_legacy_hook_commands(repo_root)
80
+
81
+ if not legacy_commands:
82
+ return CheckResult(
83
+ name="legacy-hook-settings",
84
+ passed=True,
85
+ message="No legacy hook commands found",
86
+ )
87
+
88
+ # Build details with migration instructions
89
+ details_lines: list[str] = ["Legacy hook commands found:"]
90
+ for cmd in legacy_commands[:5]: # Limit display to first 5
91
+ # Truncate long commands
92
+ truncated = cmd[:80] + "..." if len(cmd) > 80 else cmd
93
+ details_lines.append(f" - {truncated}")
94
+ if len(legacy_commands) > 5:
95
+ details_lines.append(f" ... and {len(legacy_commands) - 5} more")
96
+ details_lines.append("")
97
+ details_lines.append("Run 'erk init' to update hook configuration")
98
+
99
+ return CheckResult(
100
+ name="legacy-hook-settings",
101
+ passed=False, # Failure - hooks won't work
102
+ message=f"Found {len(legacy_commands)} legacy hook command(s)",
103
+ details="\n".join(details_lines),
104
+ )
@@ -0,0 +1,78 @@
1
+ """Check for legacy kit.yaml files.
2
+
3
+ This is a temporary check for early dogfooders. Delete this file once
4
+ all users have removed their kit.yaml files.
5
+ """
6
+
7
+ from pathlib import Path
8
+
9
+ from erk.core.health_checks import CheckResult
10
+
11
+
12
+ def find_legacy_kit_yaml_files(repo_root: Path) -> list[Path]:
13
+ """Find legacy kit.yaml files in the repository.
14
+
15
+ Searches for kit.yaml files in:
16
+ - .claude/**
17
+ - .erk/kits/**
18
+
19
+ Args:
20
+ repo_root: Path to the repository root
21
+
22
+ Returns:
23
+ List of paths to kit.yaml files found
24
+ """
25
+ kit_yaml_files: list[Path] = []
26
+
27
+ # Check .claude/ directory
28
+ claude_dir = repo_root / ".claude"
29
+ if claude_dir.exists():
30
+ kit_yaml_files.extend(claude_dir.glob("**/kit.yaml"))
31
+
32
+ # Check .erk/kits/ directory
33
+ erk_kits_dir = repo_root / ".erk" / "kits"
34
+ if erk_kits_dir.exists():
35
+ kit_yaml_files.extend(erk_kits_dir.glob("**/kit.yaml"))
36
+
37
+ return kit_yaml_files
38
+
39
+
40
+ def check_legacy_kit_yaml(repo_root: Path) -> CheckResult:
41
+ """Check for legacy kit.yaml files.
42
+
43
+ Detects kit.yaml files which are no longer used. Frontmatter-based
44
+ discovery is now used instead of kit manifests.
45
+
46
+ Args:
47
+ repo_root: Path to the repository root
48
+
49
+ Returns:
50
+ CheckResult with warning if legacy kit.yaml files found
51
+ """
52
+ kit_yaml_files = find_legacy_kit_yaml_files(repo_root)
53
+
54
+ if not kit_yaml_files:
55
+ return CheckResult(
56
+ name="legacy-kit-yaml",
57
+ passed=True,
58
+ message="No legacy kit.yaml files found",
59
+ )
60
+
61
+ # Build details with file list
62
+ details_lines: list[str] = ["Legacy kit.yaml manifests found:"]
63
+ for kit_yaml in kit_yaml_files[:5]: # Limit display to first 5
64
+ relative_path = kit_yaml.relative_to(repo_root)
65
+ details_lines.append(f" - {relative_path}")
66
+ if len(kit_yaml_files) > 5:
67
+ details_lines.append(f" ... and {len(kit_yaml_files) - 5} more")
68
+ details_lines.append("")
69
+ details_lines.append("Kit manifests are no longer used (frontmatter discovery is now used).")
70
+ details_lines.append("You can safely delete these files.")
71
+
72
+ return CheckResult(
73
+ name="legacy-kit-yaml",
74
+ passed=True, # Warning only, doesn't fail
75
+ warning=True,
76
+ message=f"Found {len(kit_yaml_files)} legacy kit.yaml file(s)",
77
+ details="\n".join(details_lines),
78
+ )
@@ -0,0 +1,43 @@
1
+ """Check for legacy .erk/kits.toml file.
2
+
3
+ This is a temporary check for early dogfooders. Delete this file once
4
+ all users have removed their kits.toml files.
5
+ """
6
+
7
+ from pathlib import Path
8
+
9
+ from erk.core.health_checks import CheckResult
10
+
11
+
12
+ def check_legacy_kits_toml(repo_root: Path) -> CheckResult:
13
+ """Check for legacy .erk/kits.toml file.
14
+
15
+ Detects .erk/kits.toml which is no longer used. Managed project
16
+ detection now uses .erk/config.toml instead.
17
+
18
+ Args:
19
+ repo_root: Path to the repository root
20
+
21
+ Returns:
22
+ CheckResult with warning if legacy kits.toml found
23
+ """
24
+ kits_toml_path = repo_root / ".erk" / "kits.toml"
25
+
26
+ if not kits_toml_path.exists():
27
+ return CheckResult(
28
+ name="legacy-kits-toml",
29
+ passed=True,
30
+ message="No legacy kits.toml found",
31
+ )
32
+
33
+ return CheckResult(
34
+ name="legacy-kits-toml",
35
+ passed=True, # Warning only, doesn't fail
36
+ warning=True,
37
+ message="Legacy kits.toml found",
38
+ details=(
39
+ f"File: {kits_toml_path}\n"
40
+ "This file is no longer used. Managed project detection now uses .erk/config.toml.\n"
41
+ "You can safely delete this file."
42
+ ),
43
+ )