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.
- erk/__init__.py +12 -0
- erk/__main__.py +6 -0
- erk/agent_docs/__init__.py +5 -0
- erk/agent_docs/models.py +123 -0
- erk/agent_docs/operations.py +666 -0
- erk/artifacts/__init__.py +5 -0
- erk/artifacts/artifact_health.py +623 -0
- erk/artifacts/detection.py +16 -0
- erk/artifacts/discovery.py +343 -0
- erk/artifacts/models.py +63 -0
- erk/artifacts/staleness.py +56 -0
- erk/artifacts/state.py +100 -0
- erk/artifacts/sync.py +624 -0
- erk/cli/__init__.py +0 -0
- erk/cli/activation.py +132 -0
- erk/cli/alias.py +53 -0
- erk/cli/cli.py +221 -0
- erk/cli/commands/__init__.py +0 -0
- erk/cli/commands/admin.py +153 -0
- erk/cli/commands/artifact/__init__.py +1 -0
- erk/cli/commands/artifact/check.py +260 -0
- erk/cli/commands/artifact/group.py +31 -0
- erk/cli/commands/artifact/list_cmd.py +89 -0
- erk/cli/commands/artifact/show.py +62 -0
- erk/cli/commands/artifact/sync_cmd.py +39 -0
- erk/cli/commands/branch/__init__.py +26 -0
- erk/cli/commands/branch/assign_cmd.py +152 -0
- erk/cli/commands/branch/checkout_cmd.py +357 -0
- erk/cli/commands/branch/create_cmd.py +161 -0
- erk/cli/commands/branch/list_cmd.py +82 -0
- erk/cli/commands/branch/unassign_cmd.py +197 -0
- erk/cli/commands/cc/__init__.py +15 -0
- erk/cli/commands/cc/jsonl_cmd.py +20 -0
- erk/cli/commands/cc/session/AGENTS.md +30 -0
- erk/cli/commands/cc/session/CLAUDE.md +1 -0
- erk/cli/commands/cc/session/__init__.py +15 -0
- erk/cli/commands/cc/session/list_cmd.py +167 -0
- erk/cli/commands/cc/session/show_cmd.py +175 -0
- erk/cli/commands/completion.py +89 -0
- erk/cli/commands/completions.py +165 -0
- erk/cli/commands/config.py +327 -0
- erk/cli/commands/docs/__init__.py +1 -0
- erk/cli/commands/docs/group.py +16 -0
- erk/cli/commands/docs/sync.py +121 -0
- erk/cli/commands/docs/validate.py +102 -0
- erk/cli/commands/doctor.py +243 -0
- erk/cli/commands/down.py +171 -0
- erk/cli/commands/exec/__init__.py +1 -0
- erk/cli/commands/exec/group.py +164 -0
- erk/cli/commands/exec/scripts/AGENTS.md +79 -0
- erk/cli/commands/exec/scripts/CLAUDE.md +1 -0
- erk/cli/commands/exec/scripts/__init__.py +5 -0
- erk/cli/commands/exec/scripts/add_reaction_to_comment.py +69 -0
- erk/cli/commands/exec/scripts/add_remote_execution_note.py +68 -0
- erk/cli/commands/exec/scripts/check_impl.py +152 -0
- erk/cli/commands/exec/scripts/ci_update_pr_body.py +294 -0
- erk/cli/commands/exec/scripts/create_extraction_branch.py +138 -0
- erk/cli/commands/exec/scripts/create_extraction_plan.py +242 -0
- erk/cli/commands/exec/scripts/create_issue_from_session.py +103 -0
- erk/cli/commands/exec/scripts/create_plan_from_context.py +103 -0
- erk/cli/commands/exec/scripts/create_worker_impl_from_issue.py +93 -0
- erk/cli/commands/exec/scripts/detect_trunk_branch.py +121 -0
- erk/cli/commands/exec/scripts/exit_plan_mode_hook.py +777 -0
- erk/cli/commands/exec/scripts/extract_latest_plan.py +49 -0
- erk/cli/commands/exec/scripts/extract_session_from_issue.py +150 -0
- erk/cli/commands/exec/scripts/find_project_dir.py +214 -0
- erk/cli/commands/exec/scripts/generate_pr_summary.py +112 -0
- erk/cli/commands/exec/scripts/get_closing_text.py +98 -0
- erk/cli/commands/exec/scripts/get_embedded_prompt.py +62 -0
- erk/cli/commands/exec/scripts/get_plan_metadata.py +95 -0
- erk/cli/commands/exec/scripts/get_pr_body_footer.py +70 -0
- erk/cli/commands/exec/scripts/get_pr_discussion_comments.py +149 -0
- erk/cli/commands/exec/scripts/get_pr_review_comments.py +155 -0
- erk/cli/commands/exec/scripts/impl_init.py +158 -0
- erk/cli/commands/exec/scripts/impl_signal.py +375 -0
- erk/cli/commands/exec/scripts/impl_verify.py +49 -0
- erk/cli/commands/exec/scripts/issue_title_to_filename.py +34 -0
- erk/cli/commands/exec/scripts/list_sessions.py +296 -0
- erk/cli/commands/exec/scripts/mark_impl_ended.py +188 -0
- erk/cli/commands/exec/scripts/mark_impl_started.py +188 -0
- erk/cli/commands/exec/scripts/marker.py +163 -0
- erk/cli/commands/exec/scripts/objective_save_to_issue.py +109 -0
- erk/cli/commands/exec/scripts/plan_save_to_issue.py +269 -0
- erk/cli/commands/exec/scripts/plan_update_issue.py +147 -0
- erk/cli/commands/exec/scripts/post_extraction_comment.py +237 -0
- erk/cli/commands/exec/scripts/post_or_update_pr_summary.py +133 -0
- erk/cli/commands/exec/scripts/post_pr_inline_comment.py +143 -0
- erk/cli/commands/exec/scripts/post_workflow_started_comment.py +168 -0
- erk/cli/commands/exec/scripts/preprocess_session.py +777 -0
- erk/cli/commands/exec/scripts/quick_submit.py +32 -0
- erk/cli/commands/exec/scripts/rebase_with_conflict_resolution.py +260 -0
- erk/cli/commands/exec/scripts/reply_to_discussion_comment.py +173 -0
- erk/cli/commands/exec/scripts/resolve_review_thread.py +170 -0
- erk/cli/commands/exec/scripts/session_id_injector_hook.py +52 -0
- erk/cli/commands/exec/scripts/setup_impl_from_issue.py +159 -0
- erk/cli/commands/exec/scripts/slot_objective.py +102 -0
- erk/cli/commands/exec/scripts/tripwires_reminder_hook.py +20 -0
- erk/cli/commands/exec/scripts/update_dispatch_info.py +116 -0
- erk/cli/commands/exec/scripts/user_prompt_hook.py +113 -0
- erk/cli/commands/exec/scripts/validate_plan_content.py +98 -0
- erk/cli/commands/exec/scripts/wrap_plan_in_metadata_block.py +34 -0
- erk/cli/commands/implement.py +695 -0
- erk/cli/commands/implement_shared.py +649 -0
- erk/cli/commands/info/__init__.py +14 -0
- erk/cli/commands/info/release_notes_cmd.py +128 -0
- erk/cli/commands/init.py +801 -0
- erk/cli/commands/land_cmd.py +690 -0
- erk/cli/commands/log_cmd.py +137 -0
- erk/cli/commands/md/__init__.py +5 -0
- erk/cli/commands/md/check.py +118 -0
- erk/cli/commands/md/group.py +14 -0
- erk/cli/commands/navigation_helpers.py +430 -0
- erk/cli/commands/objective/__init__.py +16 -0
- erk/cli/commands/objective/list_cmd.py +47 -0
- erk/cli/commands/objective_helpers.py +132 -0
- erk/cli/commands/plan/__init__.py +32 -0
- erk/cli/commands/plan/check_cmd.py +174 -0
- erk/cli/commands/plan/close_cmd.py +69 -0
- erk/cli/commands/plan/create_cmd.py +120 -0
- erk/cli/commands/plan/docs/__init__.py +18 -0
- erk/cli/commands/plan/docs/extract_cmd.py +53 -0
- erk/cli/commands/plan/docs/unextract_cmd.py +38 -0
- erk/cli/commands/plan/docs/unextracted_cmd.py +72 -0
- erk/cli/commands/plan/extraction/__init__.py +16 -0
- erk/cli/commands/plan/extraction/complete_cmd.py +101 -0
- erk/cli/commands/plan/extraction/create_raw_cmd.py +63 -0
- erk/cli/commands/plan/get.py +71 -0
- erk/cli/commands/plan/list_cmd.py +754 -0
- erk/cli/commands/plan/log_cmd.py +440 -0
- erk/cli/commands/plan/start_cmd.py +459 -0
- erk/cli/commands/planner/__init__.py +40 -0
- erk/cli/commands/planner/configure_cmd.py +73 -0
- erk/cli/commands/planner/connect_cmd.py +96 -0
- erk/cli/commands/planner/create_cmd.py +148 -0
- erk/cli/commands/planner/list_cmd.py +51 -0
- erk/cli/commands/planner/register_cmd.py +105 -0
- erk/cli/commands/planner/set_default_cmd.py +23 -0
- erk/cli/commands/planner/unregister_cmd.py +43 -0
- erk/cli/commands/pr/__init__.py +23 -0
- erk/cli/commands/pr/check_cmd.py +112 -0
- erk/cli/commands/pr/checkout_cmd.py +165 -0
- erk/cli/commands/pr/fix_conflicts_cmd.py +82 -0
- erk/cli/commands/pr/parse_pr_reference.py +10 -0
- erk/cli/commands/pr/submit_cmd.py +360 -0
- erk/cli/commands/pr/sync_cmd.py +181 -0
- erk/cli/commands/prepare_cwd_recovery.py +60 -0
- erk/cli/commands/project/__init__.py +16 -0
- erk/cli/commands/project/init_cmd.py +91 -0
- erk/cli/commands/run/__init__.py +17 -0
- erk/cli/commands/run/list_cmd.py +189 -0
- erk/cli/commands/run/logs_cmd.py +54 -0
- erk/cli/commands/run/shared.py +19 -0
- erk/cli/commands/shell_integration.py +29 -0
- erk/cli/commands/slot/__init__.py +23 -0
- erk/cli/commands/slot/check_cmd.py +277 -0
- erk/cli/commands/slot/common.py +314 -0
- erk/cli/commands/slot/init_pool_cmd.py +157 -0
- erk/cli/commands/slot/list_cmd.py +228 -0
- erk/cli/commands/slot/repair_cmd.py +190 -0
- erk/cli/commands/stack/__init__.py +23 -0
- erk/cli/commands/stack/consolidate_cmd.py +470 -0
- erk/cli/commands/stack/list_cmd.py +79 -0
- erk/cli/commands/stack/move_cmd.py +309 -0
- erk/cli/commands/stack/split_old/README.md +64 -0
- erk/cli/commands/stack/split_old/__init__.py +5 -0
- erk/cli/commands/stack/split_old/command.py +233 -0
- erk/cli/commands/stack/split_old/display.py +116 -0
- erk/cli/commands/stack/split_old/plan.py +216 -0
- erk/cli/commands/status.py +58 -0
- erk/cli/commands/submit.py +768 -0
- erk/cli/commands/up.py +154 -0
- erk/cli/commands/upgrade.py +82 -0
- erk/cli/commands/wt/__init__.py +29 -0
- erk/cli/commands/wt/checkout_cmd.py +110 -0
- erk/cli/commands/wt/create_cmd.py +998 -0
- erk/cli/commands/wt/current_cmd.py +35 -0
- erk/cli/commands/wt/delete_cmd.py +573 -0
- erk/cli/commands/wt/list_cmd.py +332 -0
- erk/cli/commands/wt/rename_cmd.py +66 -0
- erk/cli/config.py +242 -0
- erk/cli/constants.py +29 -0
- erk/cli/core.py +65 -0
- erk/cli/debug.py +9 -0
- erk/cli/ensure-conversion-tasks.md +288 -0
- erk/cli/ensure.py +628 -0
- erk/cli/github_parsing.py +96 -0
- erk/cli/graphite.py +81 -0
- erk/cli/graphite_command.py +80 -0
- erk/cli/help_formatter.py +345 -0
- erk/cli/output.py +361 -0
- erk/cli/presets/dagster.toml +12 -0
- erk/cli/presets/generic.toml +12 -0
- erk/cli/prompt_hooks_templates/README.md +68 -0
- erk/cli/script_output.py +32 -0
- erk/cli/shell_integration/bash_wrapper.sh +32 -0
- erk/cli/shell_integration/fish_wrapper.fish +39 -0
- erk/cli/shell_integration/handler.py +338 -0
- erk/cli/shell_integration/zsh_wrapper.sh +32 -0
- erk/cli/shell_utils.py +171 -0
- erk/cli/subprocess_utils.py +92 -0
- erk/cli/uvx_detection.py +59 -0
- erk/core/__init__.py +0 -0
- erk/core/claude_executor.py +511 -0
- erk/core/claude_settings.py +317 -0
- erk/core/command_log.py +406 -0
- erk/core/commit_message_generator.py +234 -0
- erk/core/completion.py +10 -0
- erk/core/consolidation_utils.py +177 -0
- erk/core/context.py +570 -0
- erk/core/display/__init__.py +4 -0
- erk/core/display/abc.py +24 -0
- erk/core/display/real.py +30 -0
- erk/core/display_utils.py +526 -0
- erk/core/file_utils.py +87 -0
- erk/core/health_checks.py +1315 -0
- erk/core/health_checks_dogfooder/__init__.py +85 -0
- erk/core/health_checks_dogfooder/deprecated_dot_agent_config.py +64 -0
- erk/core/health_checks_dogfooder/legacy_claude_docs.py +69 -0
- erk/core/health_checks_dogfooder/legacy_config_locations.py +122 -0
- erk/core/health_checks_dogfooder/legacy_erk_docs_agent.py +61 -0
- erk/core/health_checks_dogfooder/legacy_erk_kits_folder.py +60 -0
- erk/core/health_checks_dogfooder/legacy_hook_settings.py +104 -0
- erk/core/health_checks_dogfooder/legacy_kit_yaml.py +78 -0
- erk/core/health_checks_dogfooder/legacy_kits_toml.py +43 -0
- erk/core/health_checks_dogfooder/outdated_erk_skill.py +43 -0
- erk/core/implementation_queue/__init__.py +1 -0
- erk/core/implementation_queue/github/__init__.py +8 -0
- erk/core/implementation_queue/github/abc.py +7 -0
- erk/core/implementation_queue/github/noop.py +38 -0
- erk/core/implementation_queue/github/printing.py +43 -0
- erk/core/implementation_queue/github/real.py +119 -0
- erk/core/init_utils.py +227 -0
- erk/core/output_filter.py +338 -0
- erk/core/plan_store/__init__.py +6 -0
- erk/core/planner/__init__.py +1 -0
- erk/core/planner/registry_abc.py +8 -0
- erk/core/planner/registry_fake.py +129 -0
- erk/core/planner/registry_real.py +195 -0
- erk/core/planner/types.py +7 -0
- erk/core/pr_utils.py +30 -0
- erk/core/release_notes.py +263 -0
- erk/core/repo_discovery.py +126 -0
- erk/core/script_writer.py +41 -0
- erk/core/services/__init__.py +1 -0
- erk/core/services/plan_list_service.py +94 -0
- erk/core/shell.py +51 -0
- erk/core/user_feedback.py +11 -0
- erk/core/version_check.py +55 -0
- erk/core/workflow_display.py +75 -0
- erk/core/worktree_pool.py +190 -0
- erk/core/worktree_utils.py +300 -0
- erk/data/CHANGELOG.md +438 -0
- erk/data/__init__.py +1 -0
- erk/data/claude/agents/devrun.md +180 -0
- erk/data/claude/commands/erk/__init__.py +0 -0
- erk/data/claude/commands/erk/create-extraction-plan.md +360 -0
- erk/data/claude/commands/erk/fix-conflicts.md +25 -0
- erk/data/claude/commands/erk/git-pr-push.md +345 -0
- erk/data/claude/commands/erk/implement-stacked-plan.md +96 -0
- erk/data/claude/commands/erk/land.md +193 -0
- erk/data/claude/commands/erk/objective-create.md +370 -0
- erk/data/claude/commands/erk/objective-list.md +34 -0
- erk/data/claude/commands/erk/objective-next-plan.md +220 -0
- erk/data/claude/commands/erk/objective-update-with-landed-pr.md +216 -0
- erk/data/claude/commands/erk/plan-implement.md +202 -0
- erk/data/claude/commands/erk/plan-save.md +45 -0
- erk/data/claude/commands/erk/plan-submit.md +39 -0
- erk/data/claude/commands/erk/pr-address.md +367 -0
- erk/data/claude/commands/erk/pr-submit.md +58 -0
- erk/data/claude/skills/dignified-python/SKILL.md +48 -0
- erk/data/claude/skills/dignified-python/cli-patterns.md +155 -0
- erk/data/claude/skills/dignified-python/dignified-python-core.md +1190 -0
- erk/data/claude/skills/dignified-python/subprocess.md +99 -0
- erk/data/claude/skills/dignified-python/versions/python-3.10.md +517 -0
- erk/data/claude/skills/dignified-python/versions/python-3.11.md +536 -0
- erk/data/claude/skills/dignified-python/versions/python-3.12.md +662 -0
- erk/data/claude/skills/dignified-python/versions/python-3.13.md +653 -0
- erk/data/claude/skills/erk-diff-analysis/SKILL.md +27 -0
- erk/data/claude/skills/erk-diff-analysis/references/commit-message-prompt.md +78 -0
- erk/data/claude/skills/learned-docs/SKILL.md +362 -0
- erk/data/github/actions/setup-claude-erk/action.yml +11 -0
- erk/data/github/prompts/dignified-python-review.md +125 -0
- erk/data/github/workflows/dignified-python-review.yml +61 -0
- erk/data/github/workflows/erk-impl.yml +251 -0
- erk/hooks/__init__.py +1 -0
- erk/hooks/decorators.py +319 -0
- erk/status/__init__.py +8 -0
- erk/status/collectors/__init__.py +9 -0
- erk/status/collectors/base.py +52 -0
- erk/status/collectors/git.py +76 -0
- erk/status/collectors/github.py +81 -0
- erk/status/collectors/graphite.py +80 -0
- erk/status/collectors/impl.py +145 -0
- erk/status/models/__init__.py +4 -0
- erk/status/models/status_data.py +404 -0
- erk/status/orchestrator.py +169 -0
- erk/status/renderers/__init__.py +5 -0
- erk/status/renderers/simple.py +322 -0
- erk/tui/AGENTS.md +193 -0
- erk/tui/CLAUDE.md +1 -0
- erk/tui/__init__.py +1 -0
- erk/tui/app.py +1404 -0
- erk/tui/commands/__init__.py +1 -0
- erk/tui/commands/executor.py +66 -0
- erk/tui/commands/provider.py +165 -0
- erk/tui/commands/real_executor.py +63 -0
- erk/tui/commands/registry.py +121 -0
- erk/tui/commands/types.py +36 -0
- erk/tui/data/__init__.py +1 -0
- erk/tui/data/provider.py +492 -0
- erk/tui/data/types.py +104 -0
- erk/tui/filtering/__init__.py +1 -0
- erk/tui/filtering/logic.py +43 -0
- erk/tui/filtering/types.py +55 -0
- erk/tui/jsonl_viewer/__init__.py +1 -0
- erk/tui/jsonl_viewer/app.py +61 -0
- erk/tui/jsonl_viewer/models.py +208 -0
- erk/tui/jsonl_viewer/widgets.py +204 -0
- erk/tui/sorting/__init__.py +6 -0
- erk/tui/sorting/logic.py +55 -0
- erk/tui/sorting/types.py +68 -0
- erk/tui/styles/dash.tcss +95 -0
- erk/tui/widgets/__init__.py +1 -0
- erk/tui/widgets/command_output.py +112 -0
- erk/tui/widgets/plan_table.py +276 -0
- erk/tui/widgets/status_bar.py +116 -0
- erk-0.4.5.dist-info/METADATA +376 -0
- erk-0.4.5.dist-info/RECORD +331 -0
- erk-0.4.5.dist-info/WHEEL +4 -0
- erk-0.4.5.dist-info/entry_points.txt +2 -0
- erk-0.4.5.dist-info/licenses/LICENSE.md +3 -0
erk/cli/activation.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""Shell activation script generation for worktree environments.
|
|
2
|
+
|
|
3
|
+
This module provides utilities for generating shell scripts that activate
|
|
4
|
+
worktree environments by setting up virtual environments and loading .env files.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import shlex
|
|
8
|
+
from collections.abc import Sequence
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _render_logging_helper() -> str:
|
|
13
|
+
"""Return shell helper functions for transparency logging.
|
|
14
|
+
|
|
15
|
+
These helpers handle ERK_QUIET and ERK_VERBOSE environment variables
|
|
16
|
+
to control output verbosity during worktree activation.
|
|
17
|
+
|
|
18
|
+
Normal mode (default): Shows brief progress indicators
|
|
19
|
+
Quiet mode (ERK_QUIET=1): Suppresses transparency output (errors still shown)
|
|
20
|
+
Verbose mode (ERK_VERBOSE=1): Shows full details with paths
|
|
21
|
+
"""
|
|
22
|
+
return """# Transparency logging helper
|
|
23
|
+
__erk_log() {
|
|
24
|
+
[ -n "$ERK_QUIET" ] && return
|
|
25
|
+
local prefix="$1" msg="$2"
|
|
26
|
+
if [ -t 2 ]; then
|
|
27
|
+
printf '\\033[0;36m%s\\033[0m %s\\n' "$prefix" "$msg" >&2
|
|
28
|
+
else
|
|
29
|
+
printf '%s %s\\n' "$prefix" "$msg" >&2
|
|
30
|
+
fi
|
|
31
|
+
}
|
|
32
|
+
__erk_log_verbose() {
|
|
33
|
+
[ -z "$ERK_VERBOSE" ] && return
|
|
34
|
+
__erk_log "$1" "$2"
|
|
35
|
+
}"""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def render_activation_script(
|
|
39
|
+
*,
|
|
40
|
+
worktree_path: Path,
|
|
41
|
+
target_subpath: Path | None,
|
|
42
|
+
post_cd_commands: Sequence[str] | None,
|
|
43
|
+
final_message: str,
|
|
44
|
+
comment: str,
|
|
45
|
+
) -> str:
|
|
46
|
+
"""Return shell code that activates a worktree's venv and .env.
|
|
47
|
+
|
|
48
|
+
The script:
|
|
49
|
+
- cds into the worktree (optionally to a subpath within it)
|
|
50
|
+
- creates .venv with `uv sync` if not present
|
|
51
|
+
- sources `.venv/bin/activate` if present
|
|
52
|
+
- exports variables from `.env` if present
|
|
53
|
+
- runs optional post-activation commands (e.g., git pull)
|
|
54
|
+
Works in bash and zsh.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
worktree_path: Path to the worktree directory
|
|
58
|
+
target_subpath: Optional relative path within the worktree to cd to.
|
|
59
|
+
If the subpath doesn't exist, a warning is shown and the script
|
|
60
|
+
falls back to the worktree root.
|
|
61
|
+
post_cd_commands: Optional sequence of shell commands to run after venv
|
|
62
|
+
activation, before final message. Useful for git pull after landing a PR.
|
|
63
|
+
Pass None if no post-cd commands are needed.
|
|
64
|
+
final_message: Shell command for final echo message
|
|
65
|
+
comment: Comment line for script identification
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Shell script as a string with newlines
|
|
69
|
+
|
|
70
|
+
Example:
|
|
71
|
+
>>> script = render_activation_script(
|
|
72
|
+
... worktree_path=Path("/path/to/worktree"),
|
|
73
|
+
... target_subpath=Path("src/lib"),
|
|
74
|
+
... post_cd_commands=None,
|
|
75
|
+
... final_message='echo "Ready: $(pwd)"',
|
|
76
|
+
... comment="work activate-script",
|
|
77
|
+
... )
|
|
78
|
+
"""
|
|
79
|
+
wt = shlex.quote(str(worktree_path))
|
|
80
|
+
venv_dir = shlex.quote(str(worktree_path / ".venv"))
|
|
81
|
+
venv_activate = shlex.quote(str(worktree_path / ".venv" / "bin" / "activate"))
|
|
82
|
+
|
|
83
|
+
# Generate the cd command with optional subpath handling
|
|
84
|
+
if target_subpath is not None:
|
|
85
|
+
subpath_quoted = shlex.quote(str(target_subpath))
|
|
86
|
+
# Check if subpath exists in target worktree, fall back to root with warning
|
|
87
|
+
cd_command = f"""__erk_log "->" "cd {worktree_path}"
|
|
88
|
+
cd {wt}
|
|
89
|
+
# Try to preserve relative directory position
|
|
90
|
+
if [ -d {subpath_quoted} ]; then
|
|
91
|
+
cd {subpath_quoted}
|
|
92
|
+
else
|
|
93
|
+
echo "Warning: '{target_subpath}' doesn't exist in target, using worktree root" >&2
|
|
94
|
+
fi"""
|
|
95
|
+
else:
|
|
96
|
+
cd_command = f"""__erk_log "->" "cd {worktree_path}"
|
|
97
|
+
cd {wt}"""
|
|
98
|
+
|
|
99
|
+
logging_helper = _render_logging_helper()
|
|
100
|
+
|
|
101
|
+
# Build optional post-activation commands section
|
|
102
|
+
post_activation_section = ""
|
|
103
|
+
if post_cd_commands:
|
|
104
|
+
post_activation_section = (
|
|
105
|
+
"# Post-activation commands\n" + "\n".join(post_cd_commands) + "\n"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
return f"""# {comment}
|
|
109
|
+
{logging_helper}
|
|
110
|
+
{cd_command}
|
|
111
|
+
# Unset VIRTUAL_ENV to avoid conflicts with previous activations
|
|
112
|
+
unset VIRTUAL_ENV
|
|
113
|
+
# Create venv if it doesn't exist
|
|
114
|
+
if [ ! -d {venv_dir} ]; then
|
|
115
|
+
echo 'Creating virtual environment with uv sync...'
|
|
116
|
+
uv sync
|
|
117
|
+
fi
|
|
118
|
+
if [ -f {venv_activate} ]; then
|
|
119
|
+
. {venv_activate}
|
|
120
|
+
__py_ver=$(python -c 'import sys; print(".".join(map(str, sys.version_info[:3])))')
|
|
121
|
+
__erk_log "->" "Activating venv: {worktree_path / ".venv"} ($__py_ver)"
|
|
122
|
+
fi
|
|
123
|
+
# Load .env into the environment (allexport)
|
|
124
|
+
set -a
|
|
125
|
+
if [ -f ./.env ]; then
|
|
126
|
+
__erk_log "->" "Loading .env"
|
|
127
|
+
. ./.env
|
|
128
|
+
fi
|
|
129
|
+
set +a
|
|
130
|
+
{post_activation_section}# Optional: show where we are
|
|
131
|
+
{final_message}
|
|
132
|
+
"""
|
erk/cli/alias.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Command alias decorator for CLI commands."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from typing import TypeVar
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
F = TypeVar("F", bound=click.Command)
|
|
9
|
+
C = TypeVar("C", bound=Callable[..., object])
|
|
10
|
+
|
|
11
|
+
# Store alias metadata on command objects
|
|
12
|
+
ALIAS_ATTR = "_erk_aliases"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def alias(*names: str) -> Callable[[F], F]:
|
|
16
|
+
"""Decorator to declare aliases for a Click command.
|
|
17
|
+
|
|
18
|
+
Must be applied BEFORE @click.command (i.e., listed above it in the decorator stack).
|
|
19
|
+
This is because decorators are applied bottom-to-top, so @alias runs AFTER @click.command
|
|
20
|
+
creates the Command object.
|
|
21
|
+
|
|
22
|
+
Usage:
|
|
23
|
+
@alias("co")
|
|
24
|
+
@click.command("checkout")
|
|
25
|
+
def checkout_cmd(...):
|
|
26
|
+
...
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def decorator(cmd: F) -> F:
|
|
30
|
+
existing = getattr(cmd, ALIAS_ATTR, [])
|
|
31
|
+
setattr(cmd, ALIAS_ATTR, existing + list(names))
|
|
32
|
+
return cmd
|
|
33
|
+
|
|
34
|
+
return decorator
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_aliases(cmd: click.Command) -> list[str]:
|
|
38
|
+
"""Get aliases declared on a command."""
|
|
39
|
+
return getattr(cmd, ALIAS_ATTR, [])
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def register_with_aliases(group: click.Group, cmd: click.Command, name: str | None = None) -> None:
|
|
43
|
+
"""Register a command and its declared aliases with a group.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
group: The Click group to register the command with
|
|
47
|
+
cmd: The command to register
|
|
48
|
+
name: Optional explicit name (defaults to cmd.name)
|
|
49
|
+
"""
|
|
50
|
+
cmd_name = name or cmd.name
|
|
51
|
+
group.add_command(cmd, name=cmd_name)
|
|
52
|
+
for alias_name in get_aliases(cmd):
|
|
53
|
+
group.add_command(cmd, name=alias_name)
|
erk/cli/cli.py
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
from erk.cli.alias import register_with_aliases
|
|
9
|
+
from erk.cli.commands.admin import admin_group
|
|
10
|
+
from erk.cli.commands.artifact.group import artifact_group
|
|
11
|
+
from erk.cli.commands.branch import branch_group
|
|
12
|
+
from erk.cli.commands.cc import cc_group
|
|
13
|
+
from erk.cli.commands.completion import completion_group
|
|
14
|
+
from erk.cli.commands.config import config_group
|
|
15
|
+
from erk.cli.commands.docs.group import docs_group
|
|
16
|
+
from erk.cli.commands.doctor import doctor_cmd
|
|
17
|
+
from erk.cli.commands.down import down_cmd
|
|
18
|
+
from erk.cli.commands.exec.group import exec_group
|
|
19
|
+
from erk.cli.commands.implement import implement
|
|
20
|
+
from erk.cli.commands.info import info_group
|
|
21
|
+
from erk.cli.commands.init import init_cmd
|
|
22
|
+
from erk.cli.commands.land_cmd import land
|
|
23
|
+
from erk.cli.commands.log_cmd import log_cmd
|
|
24
|
+
from erk.cli.commands.md.group import md_group
|
|
25
|
+
from erk.cli.commands.objective import objective_group
|
|
26
|
+
from erk.cli.commands.plan import plan_group
|
|
27
|
+
from erk.cli.commands.plan.list_cmd import dash
|
|
28
|
+
from erk.cli.commands.planner import planner_group
|
|
29
|
+
from erk.cli.commands.pr import pr_group
|
|
30
|
+
from erk.cli.commands.prepare_cwd_recovery import prepare_cwd_recovery_cmd
|
|
31
|
+
from erk.cli.commands.project import project_group
|
|
32
|
+
from erk.cli.commands.run import run_group
|
|
33
|
+
from erk.cli.commands.shell_integration import hidden_shell_cmd
|
|
34
|
+
from erk.cli.commands.slot import slot_group
|
|
35
|
+
from erk.cli.commands.stack import stack_group
|
|
36
|
+
from erk.cli.commands.up import up_cmd
|
|
37
|
+
from erk.cli.commands.upgrade import upgrade_cmd
|
|
38
|
+
from erk.cli.commands.wt import wt_group
|
|
39
|
+
from erk.cli.help_formatter import ErkCommandGroup
|
|
40
|
+
from erk.core.command_log import get_cli_args, log_command_start, register_exit_handler
|
|
41
|
+
from erk.core.context import create_context
|
|
42
|
+
from erk.core.release_notes import check_for_version_change, get_current_version
|
|
43
|
+
from erk.core.version_check import (
|
|
44
|
+
format_version_warning,
|
|
45
|
+
get_required_version,
|
|
46
|
+
is_version_mismatch,
|
|
47
|
+
)
|
|
48
|
+
from erk_shared.gateway.erk_installation.real import RealErkInstallation
|
|
49
|
+
from erk_shared.git.real import RealGit
|
|
50
|
+
|
|
51
|
+
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"]) # terse help flags
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _show_version_change_banner() -> None:
|
|
55
|
+
"""Show upgrade banner with full release notes if version has changed.
|
|
56
|
+
|
|
57
|
+
Displays all release notes since the last seen version and prompts user
|
|
58
|
+
to confirm before continuing. This function is designed to never fail -
|
|
59
|
+
exceptions are logged but don't break the CLI.
|
|
60
|
+
"""
|
|
61
|
+
try:
|
|
62
|
+
erk_installation = RealErkInstallation()
|
|
63
|
+
changed, releases = check_for_version_change(erk_installation)
|
|
64
|
+
if not changed or not releases:
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
current = get_current_version()
|
|
68
|
+
|
|
69
|
+
# Build banner header
|
|
70
|
+
click.echo(file=sys.stderr)
|
|
71
|
+
click.echo(
|
|
72
|
+
click.style(f" ✨ erk updated to v{current}", fg="green", bold=True), file=sys.stderr
|
|
73
|
+
)
|
|
74
|
+
click.echo(click.style(" " + "─" * 50, dim=True), file=sys.stderr)
|
|
75
|
+
click.echo(file=sys.stderr)
|
|
76
|
+
|
|
77
|
+
# Show all releases with their items grouped by category
|
|
78
|
+
for release in releases:
|
|
79
|
+
# Skip releases with no items
|
|
80
|
+
if not release.items:
|
|
81
|
+
continue
|
|
82
|
+
|
|
83
|
+
# Version header
|
|
84
|
+
header = f" [{release.version}]"
|
|
85
|
+
if release.date:
|
|
86
|
+
header += f" - {release.date}"
|
|
87
|
+
click.echo(click.style(header, bold=True), file=sys.stderr)
|
|
88
|
+
|
|
89
|
+
# Show items grouped by category if available
|
|
90
|
+
if release.categories:
|
|
91
|
+
for category, category_items in release.categories.items():
|
|
92
|
+
if not category_items:
|
|
93
|
+
continue
|
|
94
|
+
click.echo(click.style(f" {category}", dim=True), file=sys.stderr)
|
|
95
|
+
for item_text, indent_level in category_items:
|
|
96
|
+
# Base indent (6 spaces) + extra indent per nesting level (2 spaces)
|
|
97
|
+
indent = " " + (" " * indent_level)
|
|
98
|
+
click.echo(f"{indent}• {item_text}", file=sys.stderr)
|
|
99
|
+
else:
|
|
100
|
+
# Fallback to flat list for releases without categories
|
|
101
|
+
for item_text, indent_level in release.items:
|
|
102
|
+
indent = " " + (" " * indent_level)
|
|
103
|
+
click.echo(f"{indent}• {item_text}", file=sys.stderr)
|
|
104
|
+
click.echo(file=sys.stderr)
|
|
105
|
+
|
|
106
|
+
click.echo(click.style(" " + "─" * 50, dim=True), file=sys.stderr)
|
|
107
|
+
click.echo(file=sys.stderr)
|
|
108
|
+
|
|
109
|
+
# Prompt user to continue (only if stdin is a TTY)
|
|
110
|
+
if sys.stdin.isatty():
|
|
111
|
+
click.pause(info=click.style(" Press Enter to continue...", dim=True), err=True)
|
|
112
|
+
except click.Abort:
|
|
113
|
+
# User pressed Ctrl+C or declined - exit gracefully
|
|
114
|
+
raise SystemExit(0) from None
|
|
115
|
+
except Exception as e:
|
|
116
|
+
# Never let release notes break the CLI, but warn so issues can be diagnosed
|
|
117
|
+
logging.warning("Failed to show version change banner: %s", e)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _show_version_warning() -> None:
|
|
121
|
+
"""Show warning if installed erk version doesn't match repo-required version.
|
|
122
|
+
|
|
123
|
+
This is designed to never fail - exceptions are logged but don't break the CLI.
|
|
124
|
+
"""
|
|
125
|
+
# Skip if user has disabled version checking
|
|
126
|
+
if os.environ.get("ERK_SKIP_VERSION_CHECK") == "1":
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
# Find git repo root (if in a git repo)
|
|
131
|
+
git = RealGit()
|
|
132
|
+
repo_root = git.get_repository_root(Path.cwd())
|
|
133
|
+
if repo_root is None:
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
# Read required version from repo
|
|
137
|
+
required = get_required_version(repo_root)
|
|
138
|
+
if required is None:
|
|
139
|
+
return
|
|
140
|
+
|
|
141
|
+
# Compare versions
|
|
142
|
+
installed = get_current_version()
|
|
143
|
+
if not is_version_mismatch(installed, required):
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
# Show warning
|
|
147
|
+
click.echo(format_version_warning(installed, required), err=True)
|
|
148
|
+
click.echo(file=sys.stderr)
|
|
149
|
+
except RuntimeError as e:
|
|
150
|
+
# Expected for global commands outside git repos
|
|
151
|
+
if "get repository root" in str(e):
|
|
152
|
+
logging.debug("Version check skipped: not in git repo")
|
|
153
|
+
return
|
|
154
|
+
logging.warning("Failed to check version: %s", e)
|
|
155
|
+
except Exception as e:
|
|
156
|
+
# Never let version checking break the CLI, but warn so issues can be diagnosed
|
|
157
|
+
logging.warning("Failed to check version: %s", e)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@click.group(cls=ErkCommandGroup, context_settings=CONTEXT_SETTINGS)
|
|
161
|
+
@click.version_option(package_name="erk")
|
|
162
|
+
@click.option("--debug", is_flag=True, help="Enable debug logging")
|
|
163
|
+
@click.pass_context
|
|
164
|
+
def cli(ctx: click.Context, debug: bool) -> None:
|
|
165
|
+
"""Manage git worktrees in a global worktrees directory."""
|
|
166
|
+
if debug:
|
|
167
|
+
logging.basicConfig(level=logging.DEBUG, format="%(name)s - %(levelname)s - %(message)s")
|
|
168
|
+
|
|
169
|
+
# Show version change banner (only on actual CLI runs, not completions)
|
|
170
|
+
if not ctx.resilient_parsing:
|
|
171
|
+
_show_version_change_banner()
|
|
172
|
+
_show_version_warning()
|
|
173
|
+
|
|
174
|
+
# Only create context if not already provided (e.g., by tests)
|
|
175
|
+
if ctx.obj is None:
|
|
176
|
+
ctx.obj = create_context(dry_run=False)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# Register all commands
|
|
180
|
+
# Commands with @alias decorators use register_with_aliases() to auto-register aliases
|
|
181
|
+
cli.add_command(admin_group)
|
|
182
|
+
cli.add_command(artifact_group)
|
|
183
|
+
register_with_aliases(cli, branch_group) # Has @alias("br")
|
|
184
|
+
cli.add_command(cc_group)
|
|
185
|
+
cli.add_command(completion_group)
|
|
186
|
+
cli.add_command(config_group)
|
|
187
|
+
cli.add_command(doctor_cmd)
|
|
188
|
+
cli.add_command(down_cmd)
|
|
189
|
+
register_with_aliases(cli, implement) # Has @alias("impl")
|
|
190
|
+
cli.add_command(init_cmd)
|
|
191
|
+
cli.add_command(land)
|
|
192
|
+
admin_group.add_command(log_cmd)
|
|
193
|
+
cli.add_command(dash)
|
|
194
|
+
cli.add_command(plan_group)
|
|
195
|
+
cli.add_command(planner_group)
|
|
196
|
+
cli.add_command(pr_group)
|
|
197
|
+
cli.add_command(info_group)
|
|
198
|
+
cli.add_command(objective_group)
|
|
199
|
+
cli.add_command(project_group)
|
|
200
|
+
cli.add_command(slot_group)
|
|
201
|
+
cli.add_command(run_group)
|
|
202
|
+
cli.add_command(stack_group)
|
|
203
|
+
cli.add_command(up_cmd)
|
|
204
|
+
cli.add_command(upgrade_cmd)
|
|
205
|
+
cli.add_command(wt_group)
|
|
206
|
+
cli.add_command(hidden_shell_cmd)
|
|
207
|
+
cli.add_command(prepare_cwd_recovery_cmd)
|
|
208
|
+
|
|
209
|
+
# Additional command groups
|
|
210
|
+
cli.add_command(docs_group)
|
|
211
|
+
cli.add_command(exec_group)
|
|
212
|
+
cli.add_command(md_group)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def main() -> None:
|
|
216
|
+
"""CLI entry point used by the `erk` console script."""
|
|
217
|
+
# Log command start and register exit handler for completion logging
|
|
218
|
+
entry_id = log_command_start(get_cli_args(), Path.cwd())
|
|
219
|
+
register_exit_handler(entry_id)
|
|
220
|
+
|
|
221
|
+
cli()
|
|
File without changes
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""Admin commands for repository configuration."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from erk.cli.core import discover_repo_context
|
|
8
|
+
from erk.core.context import ErkContext
|
|
9
|
+
from erk.core.implementation_queue.github.real import RealGitHubAdmin
|
|
10
|
+
from erk_shared.github.types import GitHubRepoLocation
|
|
11
|
+
from erk_shared.output.output import user_output
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@click.group("admin")
|
|
15
|
+
def admin_group() -> None:
|
|
16
|
+
"""Administrative commands for repository configuration."""
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@admin_group.command("github-pr-setting")
|
|
21
|
+
@click.option(
|
|
22
|
+
"--enable",
|
|
23
|
+
"action",
|
|
24
|
+
flag_value="enable",
|
|
25
|
+
help="Enable PR creation for GitHub Actions workflows",
|
|
26
|
+
)
|
|
27
|
+
@click.option(
|
|
28
|
+
"--disable",
|
|
29
|
+
"action",
|
|
30
|
+
flag_value="disable",
|
|
31
|
+
help="Disable PR creation for GitHub Actions workflows",
|
|
32
|
+
)
|
|
33
|
+
@click.pass_obj
|
|
34
|
+
def github_pr_setting(ctx: ErkContext, action: Literal["enable", "disable"] | None) -> None:
|
|
35
|
+
"""Manage GitHub Actions workflow permission for PR creation.
|
|
36
|
+
|
|
37
|
+
Without flags: Display current setting
|
|
38
|
+
With --enable: Enable PR creation for workflows
|
|
39
|
+
With --disable: Disable PR creation for workflows
|
|
40
|
+
|
|
41
|
+
This setting controls whether GitHub Actions workflows can create
|
|
42
|
+
and approve pull requests in your repository.
|
|
43
|
+
|
|
44
|
+
GitHub UI location: Settings > Actions > General > Workflow permissions
|
|
45
|
+
"""
|
|
46
|
+
# Discover repository context
|
|
47
|
+
repo = discover_repo_context(ctx, ctx.cwd)
|
|
48
|
+
|
|
49
|
+
# Check for GitHub identity
|
|
50
|
+
if repo.github is None:
|
|
51
|
+
user_output(click.style("Error: ", fg="red") + "Not a GitHub repository")
|
|
52
|
+
user_output("This command requires the repository to have a GitHub remote configured.")
|
|
53
|
+
raise SystemExit(1)
|
|
54
|
+
|
|
55
|
+
# Create admin interface
|
|
56
|
+
# TODO: Use injected admin from context when dry-run support is added
|
|
57
|
+
admin = RealGitHubAdmin()
|
|
58
|
+
location = GitHubRepoLocation(root=repo.root, repo_id=repo.github)
|
|
59
|
+
|
|
60
|
+
if action is None:
|
|
61
|
+
# Display current setting
|
|
62
|
+
try:
|
|
63
|
+
perms = admin.get_workflow_permissions(location)
|
|
64
|
+
enabled = perms.get("can_approve_pull_request_reviews", False)
|
|
65
|
+
|
|
66
|
+
user_output(click.style("GitHub Actions PR Creation Setting", bold=True))
|
|
67
|
+
user_output("")
|
|
68
|
+
|
|
69
|
+
status_text = "Enabled" if enabled else "Disabled"
|
|
70
|
+
status_color = "green" if enabled else "red"
|
|
71
|
+
user_output(f"Current status: {click.style(status_text, fg=status_color)}")
|
|
72
|
+
user_output("")
|
|
73
|
+
|
|
74
|
+
if enabled:
|
|
75
|
+
user_output("Workflows can create and approve pull requests in this repository.")
|
|
76
|
+
else:
|
|
77
|
+
user_output("Workflows cannot create pull requests in this repository.")
|
|
78
|
+
|
|
79
|
+
user_output("")
|
|
80
|
+
user_output(click.style("GitHub UI location:", fg="white", dim=True))
|
|
81
|
+
user_output(
|
|
82
|
+
click.style(
|
|
83
|
+
" Settings > Actions > General > Workflow permissions",
|
|
84
|
+
fg="white",
|
|
85
|
+
dim=True,
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
except RuntimeError as e:
|
|
90
|
+
user_output(click.style("Error: ", fg="red") + str(e))
|
|
91
|
+
raise SystemExit(1) from e
|
|
92
|
+
|
|
93
|
+
elif action == "enable":
|
|
94
|
+
# Enable PR creation
|
|
95
|
+
try:
|
|
96
|
+
admin.set_workflow_pr_permissions(location, enabled=True)
|
|
97
|
+
|
|
98
|
+
user_output(
|
|
99
|
+
click.style("✓", fg="green") + " Enabled PR creation for GitHub Actions workflows"
|
|
100
|
+
)
|
|
101
|
+
user_output("")
|
|
102
|
+
user_output("Workflows can now create and approve pull requests.")
|
|
103
|
+
|
|
104
|
+
except RuntimeError as e:
|
|
105
|
+
user_output(click.style("Error: ", fg="red") + str(e))
|
|
106
|
+
raise SystemExit(1) from e
|
|
107
|
+
|
|
108
|
+
elif action == "disable":
|
|
109
|
+
# Disable PR creation
|
|
110
|
+
try:
|
|
111
|
+
admin.set_workflow_pr_permissions(location, enabled=False)
|
|
112
|
+
|
|
113
|
+
user_output(
|
|
114
|
+
click.style("✓", fg="green") + " Disabled PR creation for GitHub Actions workflows"
|
|
115
|
+
)
|
|
116
|
+
user_output("")
|
|
117
|
+
user_output("Workflows can no longer create pull requests.")
|
|
118
|
+
|
|
119
|
+
except RuntimeError as e:
|
|
120
|
+
user_output(click.style("Error: ", fg="red") + str(e))
|
|
121
|
+
raise SystemExit(1) from e
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@admin_group.command("upgrade-repo")
|
|
125
|
+
@click.pass_obj
|
|
126
|
+
def upgrade_repo(ctx: ErkContext) -> None:
|
|
127
|
+
"""Upgrade repo to match installed erk version.
|
|
128
|
+
|
|
129
|
+
Updates .erk/required-erk-uv-tool-version and prints next steps.
|
|
130
|
+
"""
|
|
131
|
+
from erk.core.release_notes import get_current_version
|
|
132
|
+
|
|
133
|
+
repo = discover_repo_context(ctx, ctx.cwd)
|
|
134
|
+
current_version = get_current_version()
|
|
135
|
+
|
|
136
|
+
# Check if this is an erk-managed repository
|
|
137
|
+
erk_dir = repo.root / ".erk"
|
|
138
|
+
if not erk_dir.exists():
|
|
139
|
+
user_output(click.style("Error: ", fg="red") + "Not an erk-managed repository")
|
|
140
|
+
user_output(f"The directory {repo.root} does not contain a .erk directory.")
|
|
141
|
+
user_output("This command only works in repositories initialized with erk.")
|
|
142
|
+
raise SystemExit(1)
|
|
143
|
+
|
|
144
|
+
# Update version file
|
|
145
|
+
version_file = erk_dir / "required-erk-uv-tool-version"
|
|
146
|
+
version_file.write_text(f"{current_version}\n", encoding="utf-8")
|
|
147
|
+
user_output(f"Updated required version to {current_version}")
|
|
148
|
+
|
|
149
|
+
# Print next steps
|
|
150
|
+
user_output("")
|
|
151
|
+
user_output("Next steps:")
|
|
152
|
+
user_output(" erk artifact sync # Sync skills, commands, hooks")
|
|
153
|
+
user_output(" erk doctor # Verify the upgrade")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Empty file
|