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,137 @@
1
+ """Log command for querying erk command history.
2
+
3
+ Provides access to the command audit trail stored in ~/.erk/command_history.jsonl.
4
+ """
5
+
6
+ from datetime import UTC, datetime
7
+
8
+ import click
9
+
10
+ from erk.core.command_log import (
11
+ CommandLogEntry,
12
+ format_relative_time,
13
+ is_iso_datetime_format,
14
+ parse_relative_time,
15
+ read_log_entries,
16
+ )
17
+
18
+
19
+ def _parse_since(value: str | None) -> datetime | None:
20
+ """Parse --since value into datetime.
21
+
22
+ Supports:
23
+ - Relative: "1 hour ago", "2 days ago", "30 minutes ago"
24
+ - ISO format: "2024-01-01T00:00:00"
25
+ """
26
+ if value is None:
27
+ return None
28
+
29
+ value_lower = value.strip().lower()
30
+
31
+ # Try relative time parsing
32
+ delta = parse_relative_time(value_lower)
33
+ if delta is not None:
34
+ return datetime.now(UTC) - delta
35
+
36
+ # Try ISO format
37
+ if not is_iso_datetime_format(value_lower):
38
+ raise click.BadParameter(
39
+ f"Invalid time format: {value}. Use 'N unit ago' (e.g., '1 hour ago') "
40
+ "or ISO format (e.g., '2024-01-01T00:00:00')"
41
+ )
42
+ return datetime.fromisoformat(value_lower)
43
+
44
+
45
+ def _format_entry_line(entry: CommandLogEntry, show_cwd: bool, show_full: bool) -> str:
46
+ """Format a single log entry for display."""
47
+ # Get time display
48
+ if show_full:
49
+ # For full mode, parse and format as full timestamp
50
+ if is_iso_datetime_format(entry.timestamp):
51
+ dt = datetime.fromisoformat(entry.timestamp)
52
+ time_str = dt.strftime("%Y-%m-%d %H:%M:%S")
53
+ else:
54
+ time_str = entry.timestamp[:19]
55
+ else:
56
+ time_str = format_relative_time(entry.timestamp)
57
+
58
+ # Build command display
59
+ cmd_display = entry.command
60
+ if entry.args:
61
+ cmd_display += " " + " ".join(entry.args)
62
+
63
+ # Exit code indicator
64
+ if entry.exit_code is None:
65
+ status = click.style("?", fg="yellow")
66
+ elif entry.exit_code == 0:
67
+ status = click.style("✓", fg="green")
68
+ else:
69
+ status = click.style("✗", fg="red")
70
+
71
+ # Format line
72
+ parts = [
73
+ click.style(f"[{time_str}]", dim=True),
74
+ status,
75
+ cmd_display,
76
+ ]
77
+
78
+ if show_cwd:
79
+ parts.append(click.style(f"({entry.cwd})", dim=True))
80
+
81
+ return " ".join(parts)
82
+
83
+
84
+ @click.command("log")
85
+ @click.option("--since", "-s", help="Show entries since time (e.g., '1 hour ago', '2024-01-01')")
86
+ @click.option("--filter", "-f", "command_filter", help="Filter by command substring")
87
+ @click.option("--cwd", "-c", "cwd_filter", help="Filter by working directory")
88
+ @click.option("--limit", "-n", type=int, help="Maximum entries to show (default: 50)")
89
+ @click.option("--full", is_flag=True, help="Show full timestamps and details")
90
+ @click.option("--show-cwd", is_flag=True, help="Show working directory for each entry")
91
+ def log_cmd(
92
+ since: str | None,
93
+ command_filter: str | None,
94
+ cwd_filter: str | None,
95
+ limit: int | None,
96
+ full: bool,
97
+ show_cwd: bool,
98
+ ) -> None:
99
+ """Show erk command history.
100
+
101
+ Displays recent erk command invocations with their status.
102
+
103
+ \b
104
+ Examples:
105
+ erk log # Show recent commands
106
+ erk log --since "1 hour ago" # Commands from last hour
107
+ erk log --filter "wt delete" # Only worktree deletions
108
+ erk log --cwd /path/to/repo # Commands from specific directory
109
+ erk log -n 10 # Show last 10 commands
110
+ """
111
+ # Parse since option
112
+ since_dt = _parse_since(since)
113
+
114
+ # Default limit
115
+ if limit is None:
116
+ limit = 50
117
+
118
+ # Read entries
119
+ entries = read_log_entries(
120
+ since=since_dt,
121
+ until=None,
122
+ command_filter=command_filter,
123
+ cwd_filter=cwd_filter,
124
+ limit=limit,
125
+ )
126
+
127
+ if not entries:
128
+ click.echo(click.style("No matching entries found.", dim=True))
129
+ return
130
+
131
+ # Display entries
132
+ for entry in entries:
133
+ click.echo(_format_entry_line(entry, show_cwd, full))
134
+
135
+ # Show count if there might be more
136
+ if len(entries) == limit:
137
+ click.echo(click.style(f"\n(Showing {limit} most recent. Use --limit for more.)", dim=True))
@@ -0,0 +1,5 @@
1
+ """Markdown file commands.
2
+
3
+ Import from submodules:
4
+ - group: md_group
5
+ """
@@ -0,0 +1,118 @@
1
+ """Check AGENTS.md standard compliance command.
2
+
3
+ This command validates that repositories follow the AGENTS.md standard where:
4
+ - AGENTS.md is the primary context file
5
+ - CLAUDE.md contains '@AGENTS.md' reference for backwards compatibility
6
+
7
+ See: https://code.claude.com/docs/en/claude-code-on-the-web
8
+ """
9
+
10
+ import subprocess
11
+ from pathlib import Path
12
+
13
+ import click
14
+
15
+ from erk_shared.output.output import user_output
16
+
17
+
18
+ @click.command(name="check")
19
+ def check_command() -> None:
20
+ """Validate AGENTS.md standard compliance in the repository.
21
+
22
+ Checks that:
23
+ - Every CLAUDE.md file has a peer AGENTS.md file
24
+ - Every CLAUDE.md file contains '@AGENTS.md' reference
25
+
26
+ Exit codes:
27
+ - 0: All checks passed
28
+ - 1: Violations found
29
+ """
30
+ # Find repository root
31
+ result = subprocess.run(
32
+ ["git", "rev-parse", "--show-toplevel"],
33
+ check=True,
34
+ capture_output=True,
35
+ text=True,
36
+ )
37
+ repo_root_str = result.stdout.strip()
38
+ repo_root_path = Path(repo_root_str)
39
+
40
+ if not repo_root_path.exists():
41
+ user_output(click.style("✗ Error: Repository root not found", fg="red"))
42
+ raise SystemExit(1)
43
+
44
+ # Find all CLAUDE.md and AGENTS.md files
45
+ claude_files = list(repo_root_path.rglob("CLAUDE.md"))
46
+ agents_files = list(repo_root_path.rglob("AGENTS.md"))
47
+
48
+ if len(claude_files) == 0 and len(agents_files) == 0:
49
+ message = "ℹ️ No CLAUDE.md or AGENTS.md files found in repository"
50
+ user_output(click.style(message, fg="cyan"))
51
+ raise SystemExit(0)
52
+
53
+ # Track violations
54
+ missing_agents: list[Path] = [] # CLAUDE.md without peer AGENTS.md
55
+ missing_claude: list[Path] = [] # AGENTS.md without peer CLAUDE.md
56
+ invalid_content: list[Path] = []
57
+
58
+ for claude_path in claude_files:
59
+ # Check for peer AGENTS.md
60
+ agents_path = claude_path.parent / "AGENTS.md"
61
+ if not agents_path.exists():
62
+ missing_agents.append(claude_path.parent)
63
+ continue
64
+
65
+ # Check CLAUDE.md content
66
+ content = claude_path.read_text(encoding="utf-8")
67
+ if content.strip() != "@AGENTS.md":
68
+ invalid_content.append(claude_path)
69
+
70
+ # Check that all AGENTS.md files have peer CLAUDE.md
71
+ for agents_path in agents_files:
72
+ claude_path = agents_path.parent / "CLAUDE.md"
73
+ if not claude_path.exists():
74
+ missing_claude.append(agents_path.parent)
75
+
76
+ # Report results
77
+ violation_count = len(missing_agents) + len(missing_claude) + len(invalid_content)
78
+ if violation_count == 0:
79
+ user_output(click.style("✓ AGENTS.md standard: PASSED", fg="green", bold=True))
80
+ user_output()
81
+ user_output("All CLAUDE.md and AGENTS.md files properly paired.")
82
+ user_output()
83
+ user_output(f"CLAUDE.md files checked: {len(claude_files)}")
84
+ user_output("Violations: 0")
85
+ raise SystemExit(0)
86
+
87
+ # Found violations
88
+ user_output(click.style("✗ AGENTS.md standard: FAILED", fg="red", bold=True))
89
+ user_output()
90
+ plural = "s" if violation_count != 1 else ""
91
+ user_output(f"Found {violation_count} violation{plural}:")
92
+ user_output()
93
+
94
+ if len(missing_agents) > 0:
95
+ user_output(click.style("Missing AGENTS.md:", fg="yellow"))
96
+ for path in missing_agents:
97
+ rel_path = path.relative_to(repo_root_path)
98
+ user_output(f" • {click.style(str(rel_path) + '/', fg='cyan')}")
99
+ user_output()
100
+
101
+ if len(missing_claude) > 0:
102
+ user_output(click.style("Missing CLAUDE.md:", fg="yellow"))
103
+ for path in missing_claude:
104
+ rel_path = path.relative_to(repo_root_path)
105
+ user_output(f" • {click.style(str(rel_path) + '/', fg='cyan')}")
106
+ user_output()
107
+
108
+ if len(invalid_content) > 0:
109
+ user_output(click.style("Invalid CLAUDE.md content:", fg="yellow"))
110
+ for path in invalid_content:
111
+ rel_path = path.relative_to(repo_root_path)
112
+ content = path.read_text(encoding="utf-8")
113
+ styled_path = click.style(str(rel_path), fg="cyan")
114
+ user_output(f" • {styled_path}: Content is '{content.strip()}', expected '@AGENTS.md'")
115
+ user_output()
116
+
117
+ user_output("Fix these issues and run again.")
118
+ raise SystemExit(1)
@@ -0,0 +1,14 @@
1
+ """Markdown file command group."""
2
+
3
+ import click
4
+
5
+ from erk.cli.commands.md.check import check_command
6
+
7
+
8
+ @click.group(name="md")
9
+ def md_group() -> None:
10
+ """Manage and validate markdown context files."""
11
+
12
+
13
+ # Register commands
14
+ md_group.add_command(check_command)