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/commands/up.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from erk.cli.activation import render_activation_script
|
|
4
|
+
from erk.cli.commands.navigation_helpers import (
|
|
5
|
+
activate_worktree,
|
|
6
|
+
check_clean_working_tree,
|
|
7
|
+
check_pending_extraction_marker,
|
|
8
|
+
resolve_up_navigation,
|
|
9
|
+
unallocate_worktree_and_branch,
|
|
10
|
+
verify_pr_closed_or_merged,
|
|
11
|
+
)
|
|
12
|
+
from erk.cli.core import discover_repo_context
|
|
13
|
+
from erk.cli.ensure import Ensure
|
|
14
|
+
from erk.cli.graphite_command import GraphiteCommandWithHiddenOptions
|
|
15
|
+
from erk.cli.help_formatter import script_option
|
|
16
|
+
from erk.core.context import ErkContext
|
|
17
|
+
from erk.core.worktree_utils import compute_relative_path_in_worktree
|
|
18
|
+
from erk_shared.output.output import machine_output, user_output
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@click.command("up", cls=GraphiteCommandWithHiddenOptions)
|
|
22
|
+
@script_option
|
|
23
|
+
@click.option(
|
|
24
|
+
"--delete-current",
|
|
25
|
+
is_flag=True,
|
|
26
|
+
help="Delete current branch and worktree after navigating up",
|
|
27
|
+
)
|
|
28
|
+
@click.option(
|
|
29
|
+
"-f",
|
|
30
|
+
"--force",
|
|
31
|
+
is_flag=True,
|
|
32
|
+
help="Force deletion even if marker exists or PR is open (prompts)",
|
|
33
|
+
)
|
|
34
|
+
@click.pass_obj
|
|
35
|
+
def up_cmd(ctx: ErkContext, script: bool, delete_current: bool, force: bool) -> None:
|
|
36
|
+
"""Move to child branch in worktree stack.
|
|
37
|
+
|
|
38
|
+
With shell integration (recommended):
|
|
39
|
+
erk up
|
|
40
|
+
|
|
41
|
+
The shell wrapper function automatically activates the worktree.
|
|
42
|
+
Run 'erk init --shell' to set up shell integration.
|
|
43
|
+
|
|
44
|
+
Without shell integration:
|
|
45
|
+
source <(erk up --script)
|
|
46
|
+
|
|
47
|
+
This will cd to the child branch's worktree, create/activate .venv, and load .env variables.
|
|
48
|
+
Requires Graphite to be enabled: 'erk config set use_graphite true'
|
|
49
|
+
"""
|
|
50
|
+
# Validate preconditions upfront (LBYL)
|
|
51
|
+
Ensure.gh_authenticated(ctx)
|
|
52
|
+
|
|
53
|
+
repo = discover_repo_context(ctx, ctx.cwd)
|
|
54
|
+
|
|
55
|
+
# Get current branch
|
|
56
|
+
current_branch = Ensure.not_none(
|
|
57
|
+
ctx.git.get_current_branch(ctx.cwd), "Not currently on a branch (detached HEAD)"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Get all worktrees for checking if target has a worktree
|
|
61
|
+
worktrees = ctx.git.list_worktrees(repo.root)
|
|
62
|
+
|
|
63
|
+
# Get child branches for ambiguity checks
|
|
64
|
+
children = ctx.graphite.get_child_branches(ctx.git, repo.root, current_branch)
|
|
65
|
+
|
|
66
|
+
# Check for navigation ambiguity when --delete-current is set
|
|
67
|
+
Ensure.invariant(
|
|
68
|
+
not (delete_current and len(children) == 0),
|
|
69
|
+
"Cannot navigate up: already at top of stack. Use 'gt branch delete' to delete this branch",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
Ensure.invariant(
|
|
73
|
+
not (delete_current and len(children) > 1),
|
|
74
|
+
"Cannot navigate up: multiple child branches exist. "
|
|
75
|
+
"Use 'gt up' to interactively select a branch",
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Safety checks before navigation (if --delete-current flag is set)
|
|
79
|
+
current_worktree_path = None
|
|
80
|
+
if delete_current:
|
|
81
|
+
# Store current worktree path for later deletion
|
|
82
|
+
current_worktree_path = Ensure.not_none(
|
|
83
|
+
ctx.git.find_worktree_for_branch(repo.root, current_branch),
|
|
84
|
+
f"Could not find worktree for branch '{current_branch}'",
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Validate clean working tree (no uncommitted changes)
|
|
88
|
+
check_clean_working_tree(ctx)
|
|
89
|
+
|
|
90
|
+
# Validate PR is closed or merged on GitHub
|
|
91
|
+
verify_pr_closed_or_merged(ctx, repo.root, current_branch, force)
|
|
92
|
+
|
|
93
|
+
# Check for pending extraction marker
|
|
94
|
+
check_pending_extraction_marker(current_worktree_path, force)
|
|
95
|
+
|
|
96
|
+
# Resolve navigation to get target branch (may auto-create worktree)
|
|
97
|
+
target_name, was_created = resolve_up_navigation(ctx, repo, current_branch, worktrees)
|
|
98
|
+
|
|
99
|
+
# Show creation message if worktree was just created
|
|
100
|
+
if was_created and not script:
|
|
101
|
+
user_output(
|
|
102
|
+
click.style("✓", fg="green")
|
|
103
|
+
+ f" Created worktree for {click.style(target_name, fg='yellow')} and moved to it"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# Resolve target branch to actual worktree path
|
|
107
|
+
target_wt_path = Ensure.not_none(
|
|
108
|
+
ctx.git.find_worktree_for_branch(repo.root, target_name),
|
|
109
|
+
f"Branch '{target_name}' has no worktree. This should not happen.",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
if delete_current and current_worktree_path is not None:
|
|
113
|
+
# Handle activation inline when cleanup is needed
|
|
114
|
+
Ensure.path_exists(ctx, target_wt_path, f"Worktree not found: {target_wt_path}")
|
|
115
|
+
|
|
116
|
+
if script:
|
|
117
|
+
# Generate activation script for shell integration
|
|
118
|
+
activation_script = render_activation_script(
|
|
119
|
+
worktree_path=target_wt_path,
|
|
120
|
+
target_subpath=compute_relative_path_in_worktree(worktrees, ctx.cwd),
|
|
121
|
+
post_cd_commands=None,
|
|
122
|
+
final_message='echo "Activated worktree: $(pwd)"',
|
|
123
|
+
comment="work activate-script",
|
|
124
|
+
)
|
|
125
|
+
result = ctx.script_writer.write_activation_script(
|
|
126
|
+
activation_script,
|
|
127
|
+
command_name="up",
|
|
128
|
+
comment=f"activate {target_wt_path.name}",
|
|
129
|
+
)
|
|
130
|
+
machine_output(str(result.path), nl=False)
|
|
131
|
+
else:
|
|
132
|
+
# Show user message for manual navigation
|
|
133
|
+
user_output(
|
|
134
|
+
"Shell integration not detected. "
|
|
135
|
+
"Run 'erk init --shell' to set up automatic activation."
|
|
136
|
+
)
|
|
137
|
+
user_output("\nOr use: source <(erk up --script)")
|
|
138
|
+
|
|
139
|
+
# Perform cleanup: unallocate worktree (slot-aware) and delete branch
|
|
140
|
+
unallocate_worktree_and_branch(ctx, repo, current_branch, current_worktree_path)
|
|
141
|
+
|
|
142
|
+
# Exit after cleanup
|
|
143
|
+
raise SystemExit(0)
|
|
144
|
+
else:
|
|
145
|
+
# No cleanup needed, use standard activation
|
|
146
|
+
activate_worktree(
|
|
147
|
+
ctx=ctx,
|
|
148
|
+
repo=repo,
|
|
149
|
+
target_path=target_wt_path,
|
|
150
|
+
script=script,
|
|
151
|
+
command_name="up",
|
|
152
|
+
preserve_relative_path=True,
|
|
153
|
+
post_cd_commands=None,
|
|
154
|
+
)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Upgrade command for erk tool installation.
|
|
2
|
+
|
|
3
|
+
Upgrades the user's local erk installation to match the repo's required version.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
from packaging.version import Version
|
|
8
|
+
|
|
9
|
+
from erk.cli.core import discover_repo_context
|
|
10
|
+
from erk.cli.subprocess_utils import run_with_error_reporting
|
|
11
|
+
from erk.core.context import ErkContext
|
|
12
|
+
from erk.core.release_notes import get_current_version
|
|
13
|
+
from erk.core.version_check import get_required_version
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.command("upgrade")
|
|
17
|
+
@click.pass_obj
|
|
18
|
+
def upgrade_cmd(ctx: ErkContext) -> None:
|
|
19
|
+
"""Upgrade local erk installation to match repo requirement.
|
|
20
|
+
|
|
21
|
+
Reads the required version from .erk/required-erk-uv-tool-version
|
|
22
|
+
and upgrades if the installed version is older.
|
|
23
|
+
|
|
24
|
+
Examples:
|
|
25
|
+
|
|
26
|
+
\b
|
|
27
|
+
# Upgrade erk to match repo requirement
|
|
28
|
+
erk upgrade
|
|
29
|
+
"""
|
|
30
|
+
# Find repo context to get repo root
|
|
31
|
+
repo = discover_repo_context(ctx, ctx.cwd)
|
|
32
|
+
|
|
33
|
+
# Read required version from repo
|
|
34
|
+
required = get_required_version(repo.root)
|
|
35
|
+
if required is None:
|
|
36
|
+
click.echo(
|
|
37
|
+
click.style("Error: ", fg="red")
|
|
38
|
+
+ "No version requirement found in this repo (.erk/required-erk-uv-tool-version)"
|
|
39
|
+
)
|
|
40
|
+
raise SystemExit(1)
|
|
41
|
+
|
|
42
|
+
# Get installed version
|
|
43
|
+
installed = get_current_version()
|
|
44
|
+
installed_ver = Version(installed)
|
|
45
|
+
required_ver = Version(required)
|
|
46
|
+
|
|
47
|
+
# Check if upgrade is needed
|
|
48
|
+
if installed_ver == required_ver:
|
|
49
|
+
click.echo(click.style(f"Already up to date ({installed})", fg="green"))
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
if installed_ver > required_ver:
|
|
53
|
+
click.echo(
|
|
54
|
+
click.style(f"Already at {installed}", fg="green") + f" (repo requires {required})"
|
|
55
|
+
)
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
# Upgrade needed: installed < required
|
|
59
|
+
click.echo(f"Upgrade available: {installed} → {required}")
|
|
60
|
+
click.echo(click.style("Command: ", dim=True) + "uv tool upgrade erk")
|
|
61
|
+
click.echo()
|
|
62
|
+
|
|
63
|
+
if not click.confirm("Proceed with upgrade?"):
|
|
64
|
+
click.echo()
|
|
65
|
+
click.echo(click.style("Manual remediation required:", bold=True))
|
|
66
|
+
click.echo(" Run: uv tool upgrade erk")
|
|
67
|
+
click.echo(f" Or: uv tool install erk=={required}")
|
|
68
|
+
raise SystemExit(1)
|
|
69
|
+
|
|
70
|
+
click.echo()
|
|
71
|
+
click.echo("Running uv tool upgrade erk...")
|
|
72
|
+
run_with_error_reporting(
|
|
73
|
+
["uv", "tool", "upgrade", "erk"],
|
|
74
|
+
error_prefix="Upgrade failed",
|
|
75
|
+
troubleshooting=[
|
|
76
|
+
"Ensure uv is installed: curl -LsSf https://astral.sh/uv/install.sh | sh",
|
|
77
|
+
"Try manual upgrade: uv tool upgrade erk",
|
|
78
|
+
f"Or install specific version: uv tool install erk=={required}",
|
|
79
|
+
],
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
click.echo(click.style(f"Successfully upgraded erk to {required}", fg="green"))
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Worktree management commands."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from erk.cli.alias import register_with_aliases
|
|
6
|
+
from erk.cli.commands.status import status_cmd
|
|
7
|
+
from erk.cli.commands.wt.checkout_cmd import wt_checkout
|
|
8
|
+
from erk.cli.commands.wt.create_cmd import create_wt
|
|
9
|
+
from erk.cli.commands.wt.current_cmd import current_wt
|
|
10
|
+
from erk.cli.commands.wt.delete_cmd import delete_wt
|
|
11
|
+
from erk.cli.commands.wt.list_cmd import list_wt
|
|
12
|
+
from erk.cli.commands.wt.rename_cmd import rename_wt
|
|
13
|
+
from erk.cli.help_formatter import ErkCommandGroup
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.group("wt", cls=ErkCommandGroup, grouped=False)
|
|
17
|
+
def wt_group() -> None:
|
|
18
|
+
"""Manage git worktrees."""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Register subcommands
|
|
23
|
+
register_with_aliases(wt_group, wt_checkout)
|
|
24
|
+
wt_group.add_command(create_wt)
|
|
25
|
+
wt_group.add_command(current_wt)
|
|
26
|
+
wt_group.add_command(delete_wt)
|
|
27
|
+
register_with_aliases(wt_group, list_wt)
|
|
28
|
+
wt_group.add_command(rename_wt)
|
|
29
|
+
wt_group.add_command(status_cmd)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Checkout command - navigate directly to a worktree by name."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from erk.cli.alias import alias
|
|
6
|
+
from erk.cli.commands.completions import complete_worktree_names
|
|
7
|
+
from erk.cli.commands.navigation_helpers import activate_root_repo, activate_worktree
|
|
8
|
+
from erk.cli.core import discover_repo_context
|
|
9
|
+
from erk.cli.ensure import Ensure
|
|
10
|
+
from erk.cli.help_formatter import CommandWithHiddenOptions, script_option
|
|
11
|
+
from erk.core.context import ErkContext
|
|
12
|
+
from erk_shared.output.output import user_output
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@alias("co")
|
|
16
|
+
@click.command("checkout", cls=CommandWithHiddenOptions)
|
|
17
|
+
@click.argument("worktree_name", shell_complete=complete_worktree_names)
|
|
18
|
+
@script_option
|
|
19
|
+
@click.pass_obj
|
|
20
|
+
def wt_checkout(ctx: ErkContext, worktree_name: str, script: bool) -> None:
|
|
21
|
+
"""Checkout a worktree by name.
|
|
22
|
+
|
|
23
|
+
With shell integration (recommended):
|
|
24
|
+
erk wt co WORKTREE_NAME
|
|
25
|
+
|
|
26
|
+
The shell wrapper function automatically activates the worktree.
|
|
27
|
+
Run 'erk init --shell' to set up shell integration.
|
|
28
|
+
|
|
29
|
+
Without shell integration:
|
|
30
|
+
source <(erk wt co WORKTREE_NAME --script)
|
|
31
|
+
|
|
32
|
+
This will cd to the worktree, create/activate .venv, and load .env variables.
|
|
33
|
+
|
|
34
|
+
Special keyword:
|
|
35
|
+
erk wt co root # Switch to the root repository
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
erk wt co feature-work # Switch to worktree named "feature-work"
|
|
39
|
+
"""
|
|
40
|
+
# Validate preconditions upfront (LBYL)
|
|
41
|
+
Ensure.gh_authenticated(ctx)
|
|
42
|
+
|
|
43
|
+
repo = discover_repo_context(ctx, ctx.cwd)
|
|
44
|
+
|
|
45
|
+
# Special case: "root" navigates to root repository
|
|
46
|
+
if worktree_name == "root":
|
|
47
|
+
activate_root_repo(ctx, repo, script, "co", post_cd_commands=None)
|
|
48
|
+
return # activate_root_repo raises SystemExit, but explicit return for clarity
|
|
49
|
+
|
|
50
|
+
# Get all worktrees for error messages and lookup
|
|
51
|
+
worktrees = ctx.git.list_worktrees(repo.root)
|
|
52
|
+
|
|
53
|
+
# Validate worktree exists
|
|
54
|
+
worktree_path = repo.worktrees_dir / worktree_name
|
|
55
|
+
|
|
56
|
+
if not ctx.git.path_exists(worktree_path):
|
|
57
|
+
# Show available worktrees (use already-fetched worktrees list)
|
|
58
|
+
available_names = ["root"]
|
|
59
|
+
for wt in worktrees:
|
|
60
|
+
if not wt.is_root:
|
|
61
|
+
available_names.append(wt.path.name)
|
|
62
|
+
|
|
63
|
+
available_list = ", ".join(f"'{name}'" for name in sorted(available_names))
|
|
64
|
+
user_output(
|
|
65
|
+
click.style("Error:", fg="red")
|
|
66
|
+
+ f" Worktree '{worktree_name}' not found.\n\n"
|
|
67
|
+
+ f"Available worktrees: {available_list}\n\n"
|
|
68
|
+
+ "Use 'erk list' to see all worktrees with their branches."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Check if the name looks like a branch (contains '/' or matches known branches)
|
|
72
|
+
if "/" in worktree_name:
|
|
73
|
+
user_output(
|
|
74
|
+
"\nHint: It looks like you provided a branch name. "
|
|
75
|
+
"Use 'erk br co' to switch by branch name."
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
raise SystemExit(1)
|
|
79
|
+
|
|
80
|
+
# Get branch info for this worktree (use already-fetched worktrees list)
|
|
81
|
+
target_worktree = None
|
|
82
|
+
for wt in worktrees:
|
|
83
|
+
if wt.path == worktree_path:
|
|
84
|
+
target_worktree = wt
|
|
85
|
+
break
|
|
86
|
+
|
|
87
|
+
target_worktree = Ensure.not_none(
|
|
88
|
+
target_worktree, f"Worktree '{worktree_name}' not found in git worktree list"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Always navigate to worktree root and preserve relative path
|
|
92
|
+
target_path = worktree_path
|
|
93
|
+
|
|
94
|
+
# Show worktree and branch info (only in non-script mode)
|
|
95
|
+
if not script:
|
|
96
|
+
branch_name = target_worktree.branch or "(detached HEAD)"
|
|
97
|
+
styled_wt = click.style(worktree_name, fg="cyan", bold=True)
|
|
98
|
+
styled_branch = click.style(branch_name, fg="yellow")
|
|
99
|
+
user_output(f"Went to worktree {styled_wt} [{styled_branch}]")
|
|
100
|
+
|
|
101
|
+
# Activate the worktree
|
|
102
|
+
activate_worktree(
|
|
103
|
+
ctx=ctx,
|
|
104
|
+
repo=repo,
|
|
105
|
+
target_path=target_path,
|
|
106
|
+
script=script,
|
|
107
|
+
command_name="co",
|
|
108
|
+
preserve_relative_path=True,
|
|
109
|
+
post_cd_commands=None,
|
|
110
|
+
)
|