erk-shared 0.2.6__tar.gz

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 (197) hide show
  1. erk_shared-0.2.6/.gitignore +22 -0
  2. erk_shared-0.2.6/PKG-INFO +43 -0
  3. erk_shared-0.2.6/README.md +33 -0
  4. erk_shared-0.2.6/pyproject.toml +24 -0
  5. erk_shared-0.2.6/src/erk_shared/__init__.py +20 -0
  6. erk_shared-0.2.6/src/erk_shared/context/__init__.py +10 -0
  7. erk_shared-0.2.6/src/erk_shared/context/context.py +177 -0
  8. erk_shared-0.2.6/src/erk_shared/context/factories.py +141 -0
  9. erk_shared-0.2.6/src/erk_shared/context/helpers.py +314 -0
  10. erk_shared-0.2.6/src/erk_shared/context/testing.py +129 -0
  11. erk_shared-0.2.6/src/erk_shared/context/types.py +99 -0
  12. erk_shared-0.2.6/src/erk_shared/core/__init__.py +38 -0
  13. erk_shared-0.2.6/src/erk_shared/core/claude_executor.py +395 -0
  14. erk_shared-0.2.6/src/erk_shared/core/config_store.py +54 -0
  15. erk_shared-0.2.6/src/erk_shared/core/fakes.py +234 -0
  16. erk_shared-0.2.6/src/erk_shared/core/plan_list_service.py +62 -0
  17. erk_shared-0.2.6/src/erk_shared/core/planner_registry.py +141 -0
  18. erk_shared-0.2.6/src/erk_shared/core/script_writer.py +143 -0
  19. erk_shared-0.2.6/src/erk_shared/debug.py +33 -0
  20. erk_shared-0.2.6/src/erk_shared/debug_timing.py +24 -0
  21. erk_shared-0.2.6/src/erk_shared/env.py +23 -0
  22. erk_shared-0.2.6/src/erk_shared/extraction/__init__.py +1 -0
  23. erk_shared-0.2.6/src/erk_shared/extraction/claude_code_session_store/__init__.py +29 -0
  24. erk_shared-0.2.6/src/erk_shared/extraction/claude_code_session_store/abc.py +122 -0
  25. erk_shared-0.2.6/src/erk_shared/extraction/claude_code_session_store/fake.py +179 -0
  26. erk_shared-0.2.6/src/erk_shared/extraction/claude_code_session_store/real.py +178 -0
  27. erk_shared-0.2.6/src/erk_shared/extraction/llm_distillation.py +106 -0
  28. erk_shared-0.2.6/src/erk_shared/extraction/local_plans.py +49 -0
  29. erk_shared-0.2.6/src/erk_shared/extraction/raw_extraction.py +264 -0
  30. erk_shared-0.2.6/src/erk_shared/extraction/session_context.py +144 -0
  31. erk_shared-0.2.6/src/erk_shared/extraction/session_discovery.py +154 -0
  32. erk_shared-0.2.6/src/erk_shared/extraction/session_preprocessing.py +427 -0
  33. erk_shared-0.2.6/src/erk_shared/extraction/session_selection.py +63 -0
  34. erk_shared-0.2.6/src/erk_shared/extraction/types.py +36 -0
  35. erk_shared-0.2.6/src/erk_shared/git/__init__.py +6 -0
  36. erk_shared-0.2.6/src/erk_shared/git/abc.py +717 -0
  37. erk_shared-0.2.6/src/erk_shared/git/dry_run.py +300 -0
  38. erk_shared-0.2.6/src/erk_shared/git/fake.py +957 -0
  39. erk_shared-0.2.6/src/erk_shared/git/printing.py +294 -0
  40. erk_shared-0.2.6/src/erk_shared/git/real.py +881 -0
  41. erk_shared-0.2.6/src/erk_shared/github/__init__.py +10 -0
  42. erk_shared-0.2.6/src/erk_shared/github/abc.py +550 -0
  43. erk_shared-0.2.6/src/erk_shared/github/dry_run.py +245 -0
  44. erk_shared-0.2.6/src/erk_shared/github/emoji.py +115 -0
  45. erk_shared-0.2.6/src/erk_shared/github/fake.py +703 -0
  46. erk_shared-0.2.6/src/erk_shared/github/graphql_queries.py +153 -0
  47. erk_shared-0.2.6/src/erk_shared/github/issues/__init__.py +19 -0
  48. erk_shared-0.2.6/src/erk_shared/github/issues/abc.py +258 -0
  49. erk_shared-0.2.6/src/erk_shared/github/issues/dry_run.py +105 -0
  50. erk_shared-0.2.6/src/erk_shared/github/issues/fake.py +368 -0
  51. erk_shared-0.2.6/src/erk_shared/github/issues/label_cache.py +215 -0
  52. erk_shared-0.2.6/src/erk_shared/github/issues/real.py +361 -0
  53. erk_shared-0.2.6/src/erk_shared/github/issues/types.py +68 -0
  54. erk_shared-0.2.6/src/erk_shared/github/metadata.py +2206 -0
  55. erk_shared-0.2.6/src/erk_shared/github/metadata_blocks.py +628 -0
  56. erk_shared-0.2.6/src/erk_shared/github/parsing.py +321 -0
  57. erk_shared-0.2.6/src/erk_shared/github/plan_issues.py +227 -0
  58. erk_shared-0.2.6/src/erk_shared/github/pr_footer.py +44 -0
  59. erk_shared-0.2.6/src/erk_shared/github/printing.py +258 -0
  60. erk_shared-0.2.6/src/erk_shared/github/real.py +1633 -0
  61. erk_shared-0.2.6/src/erk_shared/github/status_history.py +121 -0
  62. erk_shared-0.2.6/src/erk_shared/github/types.py +293 -0
  63. erk_shared-0.2.6/src/erk_shared/hooks/__init__.py +1 -0
  64. erk_shared-0.2.6/src/erk_shared/hooks/logging.py +184 -0
  65. erk_shared-0.2.6/src/erk_shared/hooks/types.py +65 -0
  66. erk_shared-0.2.6/src/erk_shared/impl_folder.py +574 -0
  67. erk_shared-0.2.6/src/erk_shared/integrations/__init__.py +1 -0
  68. erk_shared-0.2.6/src/erk_shared/integrations/browser/__init__.py +7 -0
  69. erk_shared-0.2.6/src/erk_shared/integrations/browser/abc.py +23 -0
  70. erk_shared-0.2.6/src/erk_shared/integrations/browser/fake.py +57 -0
  71. erk_shared-0.2.6/src/erk_shared/integrations/browser/real.py +25 -0
  72. erk_shared-0.2.6/src/erk_shared/integrations/clipboard/__init__.py +7 -0
  73. erk_shared-0.2.6/src/erk_shared/integrations/clipboard/abc.py +24 -0
  74. erk_shared-0.2.6/src/erk_shared/integrations/clipboard/fake.py +57 -0
  75. erk_shared-0.2.6/src/erk_shared/integrations/clipboard/real.py +33 -0
  76. erk_shared-0.2.6/src/erk_shared/integrations/completion/__init__.py +5 -0
  77. erk_shared-0.2.6/src/erk_shared/integrations/completion/abc.py +72 -0
  78. erk_shared-0.2.6/src/erk_shared/integrations/completion/fake.py +106 -0
  79. erk_shared-0.2.6/src/erk_shared/integrations/completion/real.py +71 -0
  80. erk_shared-0.2.6/src/erk_shared/integrations/erk_wt/__init__.py +23 -0
  81. erk_shared-0.2.6/src/erk_shared/integrations/erk_wt/abc.py +141 -0
  82. erk_shared-0.2.6/src/erk_shared/integrations/erk_wt/fake.py +155 -0
  83. erk_shared-0.2.6/src/erk_shared/integrations/erk_wt/real.py +211 -0
  84. erk_shared-0.2.6/src/erk_shared/integrations/feedback/__init__.py +6 -0
  85. erk_shared-0.2.6/src/erk_shared/integrations/feedback/abc.py +50 -0
  86. erk_shared-0.2.6/src/erk_shared/integrations/feedback/fake.py +70 -0
  87. erk_shared-0.2.6/src/erk_shared/integrations/feedback/real.py +42 -0
  88. erk_shared-0.2.6/src/erk_shared/integrations/graphite/__init__.py +11 -0
  89. erk_shared-0.2.6/src/erk_shared/integrations/graphite/abc.py +346 -0
  90. erk_shared-0.2.6/src/erk_shared/integrations/graphite/dry_run.py +95 -0
  91. erk_shared-0.2.6/src/erk_shared/integrations/graphite/fake.py +313 -0
  92. erk_shared-0.2.6/src/erk_shared/integrations/graphite/parsing.py +138 -0
  93. erk_shared-0.2.6/src/erk_shared/integrations/graphite/printing.py +75 -0
  94. erk_shared-0.2.6/src/erk_shared/integrations/graphite/real.py +394 -0
  95. erk_shared-0.2.6/src/erk_shared/integrations/graphite/types.py +70 -0
  96. erk_shared-0.2.6/src/erk_shared/integrations/gt/__init__.py +15 -0
  97. erk_shared-0.2.6/src/erk_shared/integrations/gt/abc.py +64 -0
  98. erk_shared-0.2.6/src/erk_shared/integrations/gt/cli.py +63 -0
  99. erk_shared-0.2.6/src/erk_shared/integrations/gt/commit_message_prompt.md +59 -0
  100. erk_shared-0.2.6/src/erk_shared/integrations/gt/events.py +42 -0
  101. erk_shared-0.2.6/src/erk_shared/integrations/gt/fake.py +7 -0
  102. erk_shared-0.2.6/src/erk_shared/integrations/gt/operations/__init__.py +29 -0
  103. erk_shared-0.2.6/src/erk_shared/integrations/gt/operations/finalize.py +167 -0
  104. erk_shared-0.2.6/src/erk_shared/integrations/gt/operations/land_pr.py +164 -0
  105. erk_shared-0.2.6/src/erk_shared/integrations/gt/operations/pre_analysis.py +314 -0
  106. erk_shared-0.2.6/src/erk_shared/integrations/gt/operations/preflight.py +379 -0
  107. erk_shared-0.2.6/src/erk_shared/integrations/gt/operations/restack_continue.py +99 -0
  108. erk_shared-0.2.6/src/erk_shared/integrations/gt/operations/restack_finalize.py +145 -0
  109. erk_shared-0.2.6/src/erk_shared/integrations/gt/operations/restack_preflight.py +113 -0
  110. erk_shared-0.2.6/src/erk_shared/integrations/gt/operations/squash.py +56 -0
  111. erk_shared-0.2.6/src/erk_shared/integrations/gt/prompts.py +27 -0
  112. erk_shared-0.2.6/src/erk_shared/integrations/gt/real.py +45 -0
  113. erk_shared-0.2.6/src/erk_shared/integrations/gt/types.py +317 -0
  114. erk_shared-0.2.6/src/erk_shared/integrations/parallel/__init__.py +6 -0
  115. erk_shared-0.2.6/src/erk_shared/integrations/parallel/abc.py +34 -0
  116. erk_shared-0.2.6/src/erk_shared/integrations/parallel/real.py +65 -0
  117. erk_shared-0.2.6/src/erk_shared/integrations/pr/__init__.py +18 -0
  118. erk_shared-0.2.6/src/erk_shared/integrations/pr/diff_extraction.py +61 -0
  119. erk_shared-0.2.6/src/erk_shared/integrations/pr/graphite_enhance.py +256 -0
  120. erk_shared-0.2.6/src/erk_shared/integrations/pr/submit.py +247 -0
  121. erk_shared-0.2.6/src/erk_shared/integrations/pr/types.py +62 -0
  122. erk_shared-0.2.6/src/erk_shared/integrations/shell/__init__.py +6 -0
  123. erk_shared-0.2.6/src/erk_shared/integrations/shell/abc.py +97 -0
  124. erk_shared-0.2.6/src/erk_shared/integrations/shell/fake.py +122 -0
  125. erk_shared-0.2.6/src/erk_shared/integrations/shell/real.py +113 -0
  126. erk_shared-0.2.6/src/erk_shared/integrations/time/__init__.py +6 -0
  127. erk_shared-0.2.6/src/erk_shared/integrations/time/abc.py +30 -0
  128. erk_shared-0.2.6/src/erk_shared/integrations/time/fake.py +57 -0
  129. erk_shared-0.2.6/src/erk_shared/integrations/time/real.py +26 -0
  130. erk_shared-0.2.6/src/erk_shared/naming.py +536 -0
  131. erk_shared-0.2.6/src/erk_shared/non_ideal_state.py +94 -0
  132. erk_shared-0.2.6/src/erk_shared/objectives/__init__.py +11 -0
  133. erk_shared-0.2.6/src/erk_shared/objectives/parser.py +226 -0
  134. erk_shared-0.2.6/src/erk_shared/objectives/storage.py +304 -0
  135. erk_shared-0.2.6/src/erk_shared/objectives/turn.py +186 -0
  136. erk_shared-0.2.6/src/erk_shared/objectives/types.py +90 -0
  137. erk_shared-0.2.6/src/erk_shared/output/__init__.py +5 -0
  138. erk_shared-0.2.6/src/erk_shared/output/next_steps.py +77 -0
  139. erk_shared-0.2.6/src/erk_shared/output/output.py +66 -0
  140. erk_shared-0.2.6/src/erk_shared/plan_store/__init__.py +11 -0
  141. erk_shared-0.2.6/src/erk_shared/plan_store/fake.py +113 -0
  142. erk_shared-0.2.6/src/erk_shared/plan_store/github.py +209 -0
  143. erk_shared-0.2.6/src/erk_shared/plan_store/store.py +69 -0
  144. erk_shared-0.2.6/src/erk_shared/plan_store/types.py +57 -0
  145. erk_shared-0.2.6/src/erk_shared/plan_utils.py +230 -0
  146. erk_shared-0.2.6/src/erk_shared/printing/__init__.py +5 -0
  147. erk_shared-0.2.6/src/erk_shared/printing/base.py +54 -0
  148. erk_shared-0.2.6/src/erk_shared/project_discovery.py +86 -0
  149. erk_shared-0.2.6/src/erk_shared/prompt_executor/__init__.py +14 -0
  150. erk_shared-0.2.6/src/erk_shared/prompt_executor/abc.py +65 -0
  151. erk_shared-0.2.6/src/erk_shared/prompt_executor/fake.py +91 -0
  152. erk_shared-0.2.6/src/erk_shared/prompt_executor/real.py +59 -0
  153. erk_shared-0.2.6/src/erk_shared/scratch/__init__.py +8 -0
  154. erk_shared-0.2.6/src/erk_shared/scratch/markers.py +75 -0
  155. erk_shared-0.2.6/src/erk_shared/scratch/scratch.py +131 -0
  156. erk_shared-0.2.6/src/erk_shared/shell_utils/__init__.py +5 -0
  157. erk_shared-0.2.6/src/erk_shared/shell_utils/temp_files.py +64 -0
  158. erk_shared-0.2.6/src/erk_shared/subprocess_utils.py +148 -0
  159. erk_shared-0.2.6/src/erk_shared/worker_impl_folder.py +140 -0
  160. erk_shared-0.2.6/tests/__init__.py +1 -0
  161. erk_shared-0.2.6/tests/unit/__init__.py +1 -0
  162. erk_shared-0.2.6/tests/unit/extraction/__init__.py +1 -0
  163. erk_shared-0.2.6/tests/unit/extraction/claude_code_session_store/__init__.py +1 -0
  164. erk_shared-0.2.6/tests/unit/extraction/claude_code_session_store/test_fake.py +445 -0
  165. erk_shared-0.2.6/tests/unit/extraction/test_llm_distillation.py +186 -0
  166. erk_shared-0.2.6/tests/unit/extraction/test_raw_extraction.py +344 -0
  167. erk_shared-0.2.6/tests/unit/extraction/test_session_context.py +269 -0
  168. erk_shared-0.2.6/tests/unit/extraction/test_session_discovery.py +220 -0
  169. erk_shared-0.2.6/tests/unit/extraction/test_session_preprocessing.py +336 -0
  170. erk_shared-0.2.6/tests/unit/extraction/test_session_selection.py +197 -0
  171. erk_shared-0.2.6/tests/unit/github/__init__.py +0 -0
  172. erk_shared-0.2.6/tests/unit/github/test_issues_fake.py +113 -0
  173. erk_shared-0.2.6/tests/unit/github/test_parsing.py +140 -0
  174. erk_shared-0.2.6/tests/unit/github/test_plan_issues.py +394 -0
  175. erk_shared-0.2.6/tests/unit/github/test_pr_footer.py +81 -0
  176. erk_shared-0.2.6/tests/unit/github/test_pr_review_threads.py +248 -0
  177. erk_shared-0.2.6/tests/unit/github/test_session_content.py +433 -0
  178. erk_shared-0.2.6/tests/unit/hooks/__init__.py +1 -0
  179. erk_shared-0.2.6/tests/unit/hooks/test_logging.py +169 -0
  180. erk_shared-0.2.6/tests/unit/hooks/test_types.py +22 -0
  181. erk_shared-0.2.6/tests/unit/integrations/__init__.py +0 -0
  182. erk_shared-0.2.6/tests/unit/integrations/gt/__init__.py +0 -0
  183. erk_shared-0.2.6/tests/unit/integrations/gt/cli/__init__.py +0 -0
  184. erk_shared-0.2.6/tests/unit/integrations/gt/cli/test_render_events.py +60 -0
  185. erk_shared-0.2.6/tests/unit/integrations/gt/operations/__init__.py +0 -0
  186. erk_shared-0.2.6/tests/unit/integrations/gt/operations/test_finalize.py +150 -0
  187. erk_shared-0.2.6/tests/unit/integrations/gt/operations/test_pre_analysis.py +595 -0
  188. erk_shared-0.2.6/tests/unit/integrations/pr/__init__.py +1 -0
  189. erk_shared-0.2.6/tests/unit/integrations/pr/test_diff_extraction.py +121 -0
  190. erk_shared-0.2.6/tests/unit/integrations/pr/test_graphite_enhance.py +267 -0
  191. erk_shared-0.2.6/tests/unit/integrations/pr/test_submit.py +261 -0
  192. erk_shared-0.2.6/tests/unit/plan_store/__init__.py +1 -0
  193. erk_shared-0.2.6/tests/unit/plan_store/test_fake_plan_store.py +333 -0
  194. erk_shared-0.2.6/tests/unit/scratch/__init__.py +1 -0
  195. erk_shared-0.2.6/tests/unit/scratch/test_scratch.py +186 -0
  196. erk_shared-0.2.6/tests/unit/test_plan_utils.py +327 -0
  197. erk_shared-0.2.6/tests/unit/test_project_discovery.py +114 -0
@@ -0,0 +1,22 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Testing
10
+ .pytest_cache
11
+ .coverage
12
+
13
+ # Virtual environments
14
+ .venv
15
+ activate.sh
16
+
17
+ # Local implementation and configuration (not tracked in git)
18
+ .impl/
19
+ .erk/scratch/
20
+ .env
21
+
22
+ CLAUDE.local.md
@@ -0,0 +1,43 @@
1
+ Metadata-Version: 2.4
2
+ Name: erk-shared
3
+ Version: 0.2.6
4
+ Summary: Shared utilities and interfaces for erk and erk-kits
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: click>=8.1.7
7
+ Requires-Dist: python-frontmatter>=1.0.0
8
+ Requires-Dist: pyyaml>=6.0
9
+ Description-Content-Type: text/markdown
10
+
11
+ # erk-shared
12
+
13
+ Shared utilities and interfaces for erk and erk-kits packages.
14
+
15
+ This package provides:
16
+
17
+ - **GitHub Issues Interface**: ABC with Real/Fake implementations
18
+ - **Naming Utilities**: Filename and worktree name transformations
19
+ - **Metadata Blocks**: GitHub comment formatting utilities
20
+ - **Impl Folder Utilities**: Issue reference management and progress parsing
21
+
22
+ ## Purpose
23
+
24
+ This package exists to break the circular dependency between `erk` and `erk-kits`:
25
+
26
+ - `erk` imports kit utilities from `erk-kits`
27
+ - `erk-kits` imports interfaces and utilities from `erk`
28
+
29
+ By extracting shared code to `erk-shared`, we create an acyclic dependency graph:
30
+
31
+ ```
32
+ erk-shared (no dependencies)
33
+
34
+ |
35
+ erk-kits (depends on: erk-shared)
36
+
37
+ |
38
+ erk (depends on: erk-kits, erk-shared)
39
+ ```
40
+
41
+ ## Note
42
+
43
+ This is an internal workspace package, not published to PyPI.
@@ -0,0 +1,33 @@
1
+ # erk-shared
2
+
3
+ Shared utilities and interfaces for erk and erk-kits packages.
4
+
5
+ This package provides:
6
+
7
+ - **GitHub Issues Interface**: ABC with Real/Fake implementations
8
+ - **Naming Utilities**: Filename and worktree name transformations
9
+ - **Metadata Blocks**: GitHub comment formatting utilities
10
+ - **Impl Folder Utilities**: Issue reference management and progress parsing
11
+
12
+ ## Purpose
13
+
14
+ This package exists to break the circular dependency between `erk` and `erk-kits`:
15
+
16
+ - `erk` imports kit utilities from `erk-kits`
17
+ - `erk-kits` imports interfaces and utilities from `erk`
18
+
19
+ By extracting shared code to `erk-shared`, we create an acyclic dependency graph:
20
+
21
+ ```
22
+ erk-shared (no dependencies)
23
+
24
+ |
25
+ erk-kits (depends on: erk-shared)
26
+
27
+ |
28
+ erk (depends on: erk-kits, erk-shared)
29
+ ```
30
+
31
+ ## Note
32
+
33
+ This is an internal workspace package, not published to PyPI.
@@ -0,0 +1,24 @@
1
+ [project]
2
+ name = "erk-shared"
3
+ version = "0.2.6"
4
+ description = "Shared utilities and interfaces for erk and erk-kits"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ dependencies = [
8
+ "pyyaml>=6.0",
9
+ "python-frontmatter>=1.0.0",
10
+ "click>=8.1.7",
11
+ ]
12
+
13
+ [build-system]
14
+ requires = ["hatchling"]
15
+ build-backend = "hatchling.build"
16
+
17
+ [tool.hatch.build.targets.wheel]
18
+ packages = ["src/erk_shared"]
19
+
20
+ [tool.pyright]
21
+ include = ["src", "tests"]
22
+ pythonVersion = "3.11"
23
+ typeCheckingMode = "strict"
24
+ reportPrivateUsage = false
@@ -0,0 +1,20 @@
1
+ """Shared utilities and interfaces for erk and erk-kits.
2
+
3
+ Import from submodules:
4
+ - env: in_github_actions
5
+ - git.abc: Git, WorktreeInfo, find_worktree_for_branch
6
+ - git.real: RealGit
7
+ - github.abc: GitHub
8
+ - github.types: PRInfo, PRMergeability, PRState, PullRequestInfo, WorkflowRun
9
+ - github.issues: GitHubIssues, RealGitHubIssues, FakeGitHubIssues, etc.
10
+ - github.metadata: MetadataBlock, create_metadata_block, etc.
11
+ - impl_folder: IssueReference, read_issue_reference, etc.
12
+ - naming: sanitize_worktree_name, generate_filename_from_title
13
+ - output.output: user_output, machine_output, format_duration
14
+ - subprocess_utils: run_subprocess_with_context
15
+ - integrations.graphite.*: Graphite, RealGraphite, FakeGraphite, etc.
16
+ - integrations.time.*: Time, RealTime
17
+ - integrations.parallel.*: ParallelTaskRunner, RealParallelTaskRunner
18
+ """
19
+
20
+ __version__ = "0.1.0"
@@ -0,0 +1,10 @@
1
+ """Unified context for erk and erk-kits.
2
+
3
+ This module provides the canonical ErkContext that holds all dependencies
4
+ for erk and erk-kits operations.
5
+
6
+ Note: Import helpers from erk_shared.context.helpers and types from
7
+ erk_shared.context.types directly.
8
+ """
9
+
10
+ from erk_shared.context.context import ErkContext as ErkContext
@@ -0,0 +1,177 @@
1
+ """Unified context for erk and erk-kits operations.
2
+
3
+ This module provides ErkContext - the unified context that holds all dependencies
4
+ for erk and erk-kits operations.
5
+
6
+ The ABCs for erk-specific services (ClaudeExecutor, ConfigStore, ScriptWriter,
7
+ PlannerRegistry, PlanListService) are defined in erk_shared.core, enabling
8
+ proper type hints without circular imports. Real implementations remain in erk.
9
+ """
10
+
11
+ from dataclasses import dataclass
12
+ from pathlib import Path
13
+
14
+ from erk_shared.context.types import (
15
+ GlobalConfig,
16
+ LoadedConfig,
17
+ NoRepoSentinel,
18
+ RepoContext,
19
+ )
20
+ from erk_shared.core.claude_executor import ClaudeExecutor
21
+ from erk_shared.core.config_store import ConfigStore
22
+ from erk_shared.core.plan_list_service import PlanListService
23
+ from erk_shared.core.planner_registry import PlannerRegistry
24
+ from erk_shared.core.script_writer import ScriptWriter
25
+ from erk_shared.extraction.claude_code_session_store import ClaudeCodeSessionStore
26
+ from erk_shared.git.abc import Git
27
+ from erk_shared.github.abc import GitHub
28
+ from erk_shared.github.issues import GitHubIssues
29
+ from erk_shared.github.types import RepoInfo
30
+ from erk_shared.integrations.completion import Completion
31
+ from erk_shared.integrations.feedback import UserFeedback
32
+ from erk_shared.integrations.graphite.abc import Graphite
33
+ from erk_shared.integrations.shell import Shell
34
+ from erk_shared.integrations.time.abc import Time
35
+ from erk_shared.objectives.storage import ObjectiveStore
36
+ from erk_shared.plan_store.store import PlanStore
37
+ from erk_shared.project_discovery import ProjectContext
38
+ from erk_shared.prompt_executor import PromptExecutor
39
+
40
+
41
+ @dataclass(frozen=True)
42
+ class ErkContext:
43
+ """Immutable context holding all dependencies for erk and erk-kits operations.
44
+
45
+ Created at CLI entry point and threaded through the application via Click's
46
+ context system. Frozen to prevent accidental modification at runtime.
47
+
48
+ This unified context replaces both the old ErkContext (from erk.core.context)
49
+ and DotAgentContext (from erk_kits.context).
50
+
51
+ Note:
52
+ - global_config may be None only during init command before config is created.
53
+ All other commands should have a valid GlobalConfig.
54
+
55
+ DotAgentContext Compatibility:
56
+ - github_issues -> issues (renamed for consistency)
57
+ - repo_root property -> repo.root (access via repo property or require_repo_root helper)
58
+ - debug field -> debug (preserved)
59
+ """
60
+
61
+ # Gateway integrations (from erk_shared)
62
+ git: Git
63
+ github: GitHub
64
+ issues: GitHubIssues # Note: ErkContext naming (was github_issues in DotAgentContext)
65
+ graphite: Graphite
66
+ time: Time
67
+ session_store: ClaudeCodeSessionStore
68
+ plan_store: PlanStore
69
+ objectives: ObjectiveStore
70
+ prompt_executor: PromptExecutor # From DotAgentContext
71
+
72
+ # Shell/CLI integrations (moved to erk_shared)
73
+ shell: Shell
74
+ completion: Completion
75
+ feedback: UserFeedback
76
+
77
+ # Erk-specific services (ABCs now in erk_shared.core for proper type hints)
78
+ claude_executor: ClaudeExecutor
79
+ config_store: ConfigStore
80
+ script_writer: ScriptWriter
81
+ planner_registry: PlannerRegistry
82
+ plan_list_service: PlanListService
83
+
84
+ # Paths
85
+ cwd: Path # Current working directory at CLI invocation
86
+
87
+ # Repository context
88
+ repo: RepoContext | NoRepoSentinel
89
+ project: ProjectContext | None # None if not in a project subdirectory
90
+ repo_info: RepoInfo | None # None when not in a GitHub repo
91
+
92
+ # Configuration
93
+ global_config: GlobalConfig | None
94
+ local_config: LoadedConfig
95
+
96
+ # Mode flags
97
+ dry_run: bool
98
+ debug: bool # From DotAgentContext
99
+
100
+ @property
101
+ def repo_root(self) -> Path:
102
+ """DotAgentContext compatibility - get repo root from repo.
103
+
104
+ Raises:
105
+ RuntimeError: If not in a git repository
106
+ """
107
+ if isinstance(self.repo, NoRepoSentinel):
108
+ raise RuntimeError("Not in a git repository")
109
+ return self.repo.root
110
+
111
+ @property
112
+ def trunk_branch(self) -> str | None:
113
+ """Get the trunk branch name from git detection.
114
+
115
+ Returns None if not in a repository, otherwise uses git to detect trunk.
116
+ """
117
+ if isinstance(self.repo, NoRepoSentinel):
118
+ return None
119
+ return self.git.detect_trunk_branch(self.repo.root)
120
+
121
+ @property
122
+ def github_issues(self) -> GitHubIssues:
123
+ """DotAgentContext compatibility - alias for issues field.
124
+
125
+ Deprecated: Use ctx.issues instead. This property is provided for
126
+ backward compatibility with code written for the old DotAgentContext.
127
+ """
128
+ return self.issues
129
+
130
+ @staticmethod
131
+ def for_test(
132
+ github_issues: GitHubIssues | None = None,
133
+ git: Git | None = None,
134
+ github: GitHub | None = None,
135
+ session_store: ClaudeCodeSessionStore | None = None,
136
+ prompt_executor: PromptExecutor | None = None,
137
+ debug: bool = False,
138
+ repo_root: Path | None = None,
139
+ cwd: Path | None = None,
140
+ ) -> "ErkContext":
141
+ """Create test context with optional pre-configured implementations.
142
+
143
+ Provides full control over all context parameters with sensible test defaults
144
+ for any unspecified values. Uses fakes by default to avoid subprocess calls.
145
+
146
+ Args:
147
+ github_issues: Optional GitHubIssues implementation. If None, creates FakeGitHubIssues.
148
+ git: Optional Git implementation. If None, creates FakeGit.
149
+ github: Optional GitHub implementation. If None, creates FakeGitHub.
150
+ session_store: Optional SessionStore. If None, creates FakeClaudeCodeSessionStore.
151
+ prompt_executor: Optional PromptExecutor. If None, creates FakePromptExecutor.
152
+ debug: Whether to enable debug mode (default False).
153
+ repo_root: Repository root path (defaults to Path("/fake/repo"))
154
+ cwd: Current working directory (defaults to Path("/fake/worktree"))
155
+
156
+ Returns:
157
+ ErkContext configured with provided values and test defaults
158
+
159
+ Example:
160
+ >>> from erk_shared.github.issues import FakeGitHubIssues
161
+ >>> from erk_shared.git.fake import FakeGit
162
+ >>> github = FakeGitHubIssues()
163
+ >>> git_ops = FakeGit()
164
+ >>> ctx = ErkContext.for_test(github_issues=github, git=git_ops, debug=True)
165
+ """
166
+ from erk_shared.context.testing import context_for_test
167
+
168
+ return context_for_test(
169
+ github_issues=github_issues,
170
+ git=git,
171
+ github=github,
172
+ session_store=session_store,
173
+ prompt_executor=prompt_executor,
174
+ debug=debug,
175
+ repo_root=repo_root,
176
+ cwd=cwd,
177
+ )
@@ -0,0 +1,141 @@
1
+ """Factory functions for creating ErkContext instances.
2
+
3
+ This module provides factory functions for creating production contexts
4
+ with real implementations. Used by CLI entry points.
5
+ """
6
+
7
+ import subprocess
8
+ from pathlib import Path
9
+ from typing import TYPE_CHECKING
10
+
11
+ from erk_shared.core.fakes import (
12
+ FakeClaudeExecutor,
13
+ FakeConfigStore,
14
+ FakePlanListService,
15
+ FakePlannerRegistry,
16
+ FakeScriptWriter,
17
+ )
18
+
19
+ if TYPE_CHECKING:
20
+ from erk_shared.context.context import ErkContext
21
+ from erk_shared.git.abc import Git
22
+ from erk_shared.github.types import RepoInfo
23
+
24
+
25
+ def get_repo_info(git: "Git", repo_root: Path) -> "RepoInfo | None":
26
+ """Detect repository info from git remote URL.
27
+
28
+ Parses the origin remote URL to extract owner/name for GitHub API calls.
29
+ Returns None if no origin remote is configured or URL cannot be parsed.
30
+
31
+ Args:
32
+ git: Git interface for operations
33
+ repo_root: Repository root path
34
+
35
+ Returns:
36
+ RepoInfo with owner/name, or None if not determinable
37
+ """
38
+ from erk_shared.github.parsing import parse_git_remote_url
39
+ from erk_shared.github.types import RepoInfo
40
+
41
+ try:
42
+ remote_url = git.get_remote_url(repo_root)
43
+ owner, name = parse_git_remote_url(remote_url)
44
+ return RepoInfo(owner=owner, name=name)
45
+ except ValueError:
46
+ return None
47
+
48
+
49
+ def create_minimal_context(*, debug: bool, cwd: Path | None = None) -> "ErkContext":
50
+ """Create production context with real implementations for erk-kits.
51
+
52
+ This factory creates a minimal context suitable for erk-kits commands.
53
+ It uses real implementations for GitHub, git, and session store, but uses
54
+ fake implementations for erk-specific services (ClaudeExecutor, etc.) that
55
+ erk-kits doesn't need.
56
+
57
+ Detects repository root using git rev-parse. Returns context with
58
+ NoRepoSentinel if not in a git repository.
59
+
60
+ Args:
61
+ debug: If True, enable debug mode (full stack traces in error handling)
62
+ cwd: Current working directory (defaults to Path.cwd())
63
+
64
+ Returns:
65
+ ErkContext with real GitHub integrations and detected repo context
66
+
67
+ Example:
68
+ >>> ctx = create_minimal_context(debug=False)
69
+ >>> issue_number = ctx.issues.create_issue(ctx.repo_root, title, body, labels)
70
+ """
71
+ from erk_shared.context.context import ErkContext
72
+ from erk_shared.context.types import LoadedConfig, NoRepoSentinel, RepoContext
73
+ from erk_shared.extraction.claude_code_session_store import RealClaudeCodeSessionStore
74
+ from erk_shared.git.real import RealGit
75
+ from erk_shared.github.issues import RealGitHubIssues
76
+ from erk_shared.github.real import RealGitHub
77
+ from erk_shared.integrations.completion import FakeCompletion
78
+ from erk_shared.integrations.feedback import SuppressedFeedback
79
+ from erk_shared.integrations.graphite.fake import FakeGraphite
80
+ from erk_shared.integrations.shell import FakeShell
81
+ from erk_shared.integrations.time.fake import FakeTime
82
+ from erk_shared.integrations.time.real import RealTime
83
+ from erk_shared.objectives.storage import FakeObjectiveStore
84
+ from erk_shared.plan_store.fake import FakePlanStore
85
+ from erk_shared.prompt_executor.real import RealPromptExecutor
86
+
87
+ resolved_cwd = cwd if cwd is not None else Path.cwd()
88
+
89
+ # Detect repo root using git rev-parse
90
+ result = subprocess.run(
91
+ ["git", "rev-parse", "--show-toplevel"],
92
+ capture_output=True,
93
+ text=True,
94
+ check=False,
95
+ )
96
+
97
+ # Create git instance
98
+ git = RealGit()
99
+
100
+ if result.returncode != 0:
101
+ # Not in a git repository
102
+ repo: RepoContext | NoRepoSentinel = NoRepoSentinel()
103
+ repo_info = None
104
+ else:
105
+ repo_root = Path(result.stdout.strip())
106
+ repo_info = get_repo_info(git, repo_root)
107
+ repo = RepoContext(
108
+ root=repo_root,
109
+ repo_name=repo_root.name,
110
+ repo_dir=Path.home() / ".erk" / "repos" / repo_root.name,
111
+ worktrees_dir=Path.home() / ".erk" / "repos" / repo_root.name / "worktrees",
112
+ )
113
+
114
+ # Use fake implementations for erk-specific services that erk-kits doesn't need
115
+ return ErkContext(
116
+ git=git,
117
+ github=RealGitHub(time=RealTime(), repo_info=repo_info),
118
+ issues=RealGitHubIssues(),
119
+ session_store=RealClaudeCodeSessionStore(),
120
+ prompt_executor=RealPromptExecutor(),
121
+ graphite=FakeGraphite(),
122
+ time=FakeTime(),
123
+ plan_store=FakePlanStore(),
124
+ objectives=FakeObjectiveStore(),
125
+ shell=FakeShell(),
126
+ completion=FakeCompletion(),
127
+ feedback=SuppressedFeedback(),
128
+ claude_executor=FakeClaudeExecutor(),
129
+ config_store=FakeConfigStore(),
130
+ script_writer=FakeScriptWriter(),
131
+ planner_registry=FakePlannerRegistry(),
132
+ plan_list_service=FakePlanListService(),
133
+ cwd=resolved_cwd,
134
+ repo=repo,
135
+ project=None,
136
+ repo_info=repo_info,
137
+ global_config=None,
138
+ local_config=LoadedConfig(env={}, post_create_commands=[], post_create_shell=None),
139
+ dry_run=False,
140
+ debug=debug,
141
+ )