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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Command palette module for erk dash TUI."""
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Abstract interface for executing palette commands."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CommandExecutor(ABC):
|
|
7
|
+
"""Abstract interface for executing palette commands.
|
|
8
|
+
|
|
9
|
+
This ABC defines the operations that commands can perform.
|
|
10
|
+
Real and fake implementations handle the actual execution.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def open_url(self, url: str) -> None:
|
|
15
|
+
"""Open URL in browser.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
url: The URL to open
|
|
19
|
+
"""
|
|
20
|
+
...
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def copy_to_clipboard(self, text: str) -> None:
|
|
24
|
+
"""Copy text to clipboard.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
text: The text to copy
|
|
28
|
+
"""
|
|
29
|
+
...
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
def close_plan(self, issue_number: int, issue_url: str) -> list[int]:
|
|
33
|
+
"""Close plan and linked PRs.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
issue_number: The issue number to close
|
|
37
|
+
issue_url: The issue URL for PR linkage lookup
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
List of PR numbers that were also closed
|
|
41
|
+
"""
|
|
42
|
+
...
|
|
43
|
+
|
|
44
|
+
@abstractmethod
|
|
45
|
+
def notify(self, message: str) -> None:
|
|
46
|
+
"""Show notification to user.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
message: The message to display
|
|
50
|
+
"""
|
|
51
|
+
...
|
|
52
|
+
|
|
53
|
+
@abstractmethod
|
|
54
|
+
def refresh_data(self) -> None:
|
|
55
|
+
"""Trigger data refresh."""
|
|
56
|
+
...
|
|
57
|
+
|
|
58
|
+
@abstractmethod
|
|
59
|
+
def submit_to_queue(self, issue_number: int, issue_url: str) -> None:
|
|
60
|
+
"""Submit plan to queue for remote AI implementation.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
issue_number: The issue number to submit
|
|
64
|
+
issue_url: The issue URL for repository context
|
|
65
|
+
"""
|
|
66
|
+
...
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""Command provider for Textual command palette."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from functools import partial
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from textual.command import DiscoveryHit, Hit, Hits, Provider
|
|
9
|
+
|
|
10
|
+
from erk.tui.commands.registry import get_available_commands
|
|
11
|
+
from erk.tui.commands.types import CommandContext
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from erk.tui.app import ErkDashApp, PlanDetailScreen
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MainListCommandProvider(Provider):
|
|
18
|
+
"""Command provider for main plan list view.
|
|
19
|
+
|
|
20
|
+
Provides commands for the currently selected plan row directly from main list.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def _app(self) -> ErkDashApp:
|
|
25
|
+
"""Get the ErkDashApp instance.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
The ErkDashApp instance
|
|
29
|
+
|
|
30
|
+
Raises:
|
|
31
|
+
AssertionError: If app is not ErkDashApp
|
|
32
|
+
"""
|
|
33
|
+
from erk.tui.app import ErkDashApp
|
|
34
|
+
|
|
35
|
+
app = self.app
|
|
36
|
+
if not isinstance(app, ErkDashApp):
|
|
37
|
+
msg = f"MainListCommandProvider expected ErkDashApp, got {type(app)}"
|
|
38
|
+
raise AssertionError(msg)
|
|
39
|
+
return app
|
|
40
|
+
|
|
41
|
+
def _get_context(self) -> CommandContext | None:
|
|
42
|
+
"""Build command context from selected row.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
CommandContext if a row is selected, None otherwise
|
|
46
|
+
"""
|
|
47
|
+
row = self._app._get_selected_row()
|
|
48
|
+
if row is None:
|
|
49
|
+
return None
|
|
50
|
+
return CommandContext(row=row)
|
|
51
|
+
|
|
52
|
+
async def discover(self) -> Hits:
|
|
53
|
+
"""Show available commands when palette opens.
|
|
54
|
+
|
|
55
|
+
Yields:
|
|
56
|
+
DiscoveryHit for each available command
|
|
57
|
+
"""
|
|
58
|
+
ctx = self._get_context()
|
|
59
|
+
if ctx is None:
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
for cmd in get_available_commands(ctx):
|
|
63
|
+
shortcut = f" [{cmd.shortcut}]" if cmd.shortcut else ""
|
|
64
|
+
yield DiscoveryHit(
|
|
65
|
+
f"{cmd.name}{shortcut}",
|
|
66
|
+
partial(self._app.execute_palette_command, cmd.id),
|
|
67
|
+
help=cmd.description,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
async def search(self, query: str) -> Hits:
|
|
71
|
+
"""Fuzzy search commands.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
query: The search query from user input
|
|
75
|
+
|
|
76
|
+
Yields:
|
|
77
|
+
Hit for each matching command, with fuzzy match score
|
|
78
|
+
"""
|
|
79
|
+
ctx = self._get_context()
|
|
80
|
+
if ctx is None:
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
matcher = self.matcher(query)
|
|
84
|
+
|
|
85
|
+
for cmd in get_available_commands(ctx):
|
|
86
|
+
score = matcher.match(cmd.name)
|
|
87
|
+
if score > 0:
|
|
88
|
+
shortcut = f" [{cmd.shortcut}]" if cmd.shortcut else ""
|
|
89
|
+
yield Hit(
|
|
90
|
+
score,
|
|
91
|
+
matcher.highlight(f"{cmd.name}{shortcut}"),
|
|
92
|
+
partial(self._app.execute_palette_command, cmd.id),
|
|
93
|
+
help=cmd.description,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class PlanCommandProvider(Provider):
|
|
98
|
+
"""Command provider for plan detail modal.
|
|
99
|
+
|
|
100
|
+
Provides commands specific to the selected plan via Textual's command palette.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def _detail_screen(self) -> PlanDetailScreen:
|
|
105
|
+
"""Get the PlanDetailScreen from current screen context.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
The PlanDetailScreen instance
|
|
109
|
+
|
|
110
|
+
Raises:
|
|
111
|
+
AssertionError: If not called from a PlanDetailScreen
|
|
112
|
+
"""
|
|
113
|
+
from erk.tui.app import PlanDetailScreen
|
|
114
|
+
|
|
115
|
+
screen = self.screen
|
|
116
|
+
if not isinstance(screen, PlanDetailScreen):
|
|
117
|
+
msg = f"PlanCommandProvider expected PlanDetailScreen, got {type(screen)}"
|
|
118
|
+
raise AssertionError(msg)
|
|
119
|
+
return screen
|
|
120
|
+
|
|
121
|
+
def _get_context(self) -> CommandContext:
|
|
122
|
+
"""Build command context from current screen state.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
CommandContext with the selected plan's row data
|
|
126
|
+
"""
|
|
127
|
+
return CommandContext(row=self._detail_screen._row)
|
|
128
|
+
|
|
129
|
+
async def discover(self) -> Hits:
|
|
130
|
+
"""Show available commands when palette opens.
|
|
131
|
+
|
|
132
|
+
Yields:
|
|
133
|
+
DiscoveryHit for each available command
|
|
134
|
+
"""
|
|
135
|
+
ctx = self._get_context()
|
|
136
|
+
for cmd in get_available_commands(ctx):
|
|
137
|
+
shortcut = f" [{cmd.shortcut}]" if cmd.shortcut else ""
|
|
138
|
+
yield DiscoveryHit(
|
|
139
|
+
f"{cmd.name}{shortcut}",
|
|
140
|
+
partial(self._detail_screen.execute_command, cmd.id),
|
|
141
|
+
help=cmd.description,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
async def search(self, query: str) -> Hits:
|
|
145
|
+
"""Fuzzy search commands.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
query: The search query from user input
|
|
149
|
+
|
|
150
|
+
Yields:
|
|
151
|
+
Hit for each matching command, with fuzzy match score
|
|
152
|
+
"""
|
|
153
|
+
matcher = self.matcher(query)
|
|
154
|
+
ctx = self._get_context()
|
|
155
|
+
|
|
156
|
+
for cmd in get_available_commands(ctx):
|
|
157
|
+
score = matcher.match(cmd.name)
|
|
158
|
+
if score > 0:
|
|
159
|
+
shortcut = f" [{cmd.shortcut}]" if cmd.shortcut else ""
|
|
160
|
+
yield Hit(
|
|
161
|
+
score,
|
|
162
|
+
matcher.highlight(f"{cmd.name}{shortcut}"),
|
|
163
|
+
partial(self._detail_screen.execute_command, cmd.id),
|
|
164
|
+
help=cmd.description,
|
|
165
|
+
)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Real implementation of CommandExecutor for production use."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from erk.tui.commands.executor import CommandExecutor
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RealCommandExecutor(CommandExecutor):
|
|
10
|
+
"""Production executor using injected dependencies.
|
|
11
|
+
|
|
12
|
+
Uses callables for each operation to decouple from concrete implementations.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
browser_launch: Callable[[str], Any],
|
|
18
|
+
clipboard_copy: Callable[[str], Any],
|
|
19
|
+
close_plan_fn: Callable[[int, str], list[int]],
|
|
20
|
+
notify_fn: Callable[[str], None],
|
|
21
|
+
refresh_fn: Callable[[], None],
|
|
22
|
+
submit_to_queue_fn: Callable[[int, str], None],
|
|
23
|
+
) -> None:
|
|
24
|
+
"""Initialize with dependency functions.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
browser_launch: Function to launch URL in browser (return value ignored)
|
|
28
|
+
clipboard_copy: Function to copy text to clipboard (return value ignored)
|
|
29
|
+
close_plan_fn: Function to close plan and linked PRs
|
|
30
|
+
notify_fn: Function to show notification
|
|
31
|
+
refresh_fn: Function to trigger data refresh
|
|
32
|
+
submit_to_queue_fn: Function to submit plan to implementation queue
|
|
33
|
+
"""
|
|
34
|
+
self._browser_launch = browser_launch
|
|
35
|
+
self._clipboard_copy = clipboard_copy
|
|
36
|
+
self._close_plan_fn = close_plan_fn
|
|
37
|
+
self._notify_fn = notify_fn
|
|
38
|
+
self._refresh_fn = refresh_fn
|
|
39
|
+
self._submit_to_queue_fn = submit_to_queue_fn
|
|
40
|
+
|
|
41
|
+
def open_url(self, url: str) -> None:
|
|
42
|
+
"""Open URL in browser."""
|
|
43
|
+
self._browser_launch(url)
|
|
44
|
+
|
|
45
|
+
def copy_to_clipboard(self, text: str) -> None:
|
|
46
|
+
"""Copy text to clipboard."""
|
|
47
|
+
self._clipboard_copy(text)
|
|
48
|
+
|
|
49
|
+
def close_plan(self, issue_number: int, issue_url: str) -> list[int]:
|
|
50
|
+
"""Close plan and linked PRs."""
|
|
51
|
+
return self._close_plan_fn(issue_number, issue_url)
|
|
52
|
+
|
|
53
|
+
def notify(self, message: str) -> None:
|
|
54
|
+
"""Show notification to user."""
|
|
55
|
+
self._notify_fn(message)
|
|
56
|
+
|
|
57
|
+
def refresh_data(self) -> None:
|
|
58
|
+
"""Trigger data refresh."""
|
|
59
|
+
self._refresh_fn()
|
|
60
|
+
|
|
61
|
+
def submit_to_queue(self, issue_number: int, issue_url: str) -> None:
|
|
62
|
+
"""Submit plan to implementation queue."""
|
|
63
|
+
self._submit_to_queue_fn(issue_number, issue_url)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Command registry for command palette.
|
|
2
|
+
|
|
3
|
+
This module defines all available commands and their availability predicates.
|
|
4
|
+
Commands are organized by category: Actions, Opens, Copies.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from erk.tui.commands.types import CommandContext, CommandDefinition
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_all_commands() -> list[CommandDefinition]:
|
|
11
|
+
"""Return all command definitions.
|
|
12
|
+
|
|
13
|
+
Commands are ordered by category:
|
|
14
|
+
1. Actions (mutative operations)
|
|
15
|
+
2. Opens (browser navigation)
|
|
16
|
+
3. Copies (clipboard operations)
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
List of all available command definitions
|
|
20
|
+
"""
|
|
21
|
+
return [
|
|
22
|
+
# === ACTIONS ===
|
|
23
|
+
CommandDefinition(
|
|
24
|
+
id="close_plan",
|
|
25
|
+
name="Action: Close Plan",
|
|
26
|
+
description="Close issue and linked PRs",
|
|
27
|
+
shortcut=None,
|
|
28
|
+
is_available=lambda _: True,
|
|
29
|
+
),
|
|
30
|
+
CommandDefinition(
|
|
31
|
+
id="submit_to_queue",
|
|
32
|
+
name="Action: Submit to Queue",
|
|
33
|
+
description="Submit plan for remote AI implementation",
|
|
34
|
+
shortcut="s",
|
|
35
|
+
is_available=lambda ctx: ctx.row.issue_url is not None,
|
|
36
|
+
),
|
|
37
|
+
# === OPENS ===
|
|
38
|
+
CommandDefinition(
|
|
39
|
+
id="open_browser",
|
|
40
|
+
name="Open: In Browser",
|
|
41
|
+
description="Open PR (or issue if no PR)",
|
|
42
|
+
shortcut="o",
|
|
43
|
+
is_available=lambda ctx: bool(ctx.row.pr_url or ctx.row.issue_url),
|
|
44
|
+
),
|
|
45
|
+
CommandDefinition(
|
|
46
|
+
id="open_issue",
|
|
47
|
+
name="Open: Issue",
|
|
48
|
+
description="Open issue in browser",
|
|
49
|
+
shortcut="i",
|
|
50
|
+
is_available=lambda ctx: ctx.row.issue_url is not None,
|
|
51
|
+
),
|
|
52
|
+
CommandDefinition(
|
|
53
|
+
id="open_pr",
|
|
54
|
+
name="Open: PR",
|
|
55
|
+
description="Open PR in browser",
|
|
56
|
+
shortcut="p",
|
|
57
|
+
is_available=lambda ctx: ctx.row.pr_url is not None,
|
|
58
|
+
),
|
|
59
|
+
CommandDefinition(
|
|
60
|
+
id="open_run",
|
|
61
|
+
name="Open: Workflow Run",
|
|
62
|
+
description="Open GitHub Actions run",
|
|
63
|
+
shortcut="r",
|
|
64
|
+
is_available=lambda ctx: ctx.row.run_url is not None,
|
|
65
|
+
),
|
|
66
|
+
# === COPIES ===
|
|
67
|
+
CommandDefinition(
|
|
68
|
+
id="copy_checkout",
|
|
69
|
+
name="Copy: erk co <worktree>",
|
|
70
|
+
description="Copy checkout command",
|
|
71
|
+
shortcut="c",
|
|
72
|
+
is_available=lambda ctx: ctx.row.exists_locally,
|
|
73
|
+
),
|
|
74
|
+
CommandDefinition(
|
|
75
|
+
id="copy_pr_checkout",
|
|
76
|
+
name="Copy: erk pr co <number>",
|
|
77
|
+
description="Copy PR checkout command",
|
|
78
|
+
shortcut="e",
|
|
79
|
+
is_available=lambda ctx: ctx.row.pr_number is not None,
|
|
80
|
+
),
|
|
81
|
+
CommandDefinition(
|
|
82
|
+
id="copy_implement",
|
|
83
|
+
name="Copy: erk implement",
|
|
84
|
+
description="Copy implement command",
|
|
85
|
+
shortcut="1",
|
|
86
|
+
is_available=lambda _: True,
|
|
87
|
+
),
|
|
88
|
+
CommandDefinition(
|
|
89
|
+
id="copy_implement_dangerous",
|
|
90
|
+
name="Copy: erk implement --dangerous",
|
|
91
|
+
description="Copy dangerous implement command",
|
|
92
|
+
shortcut="2",
|
|
93
|
+
is_available=lambda _: True,
|
|
94
|
+
),
|
|
95
|
+
CommandDefinition(
|
|
96
|
+
id="copy_implement_yolo",
|
|
97
|
+
name="Copy: erk implement --yolo",
|
|
98
|
+
description="Copy yolo implement command",
|
|
99
|
+
shortcut="3",
|
|
100
|
+
is_available=lambda _: True,
|
|
101
|
+
),
|
|
102
|
+
CommandDefinition(
|
|
103
|
+
id="copy_submit",
|
|
104
|
+
name="Copy: erk plan submit",
|
|
105
|
+
description="Copy submit command",
|
|
106
|
+
shortcut="4",
|
|
107
|
+
is_available=lambda _: True,
|
|
108
|
+
),
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def get_available_commands(ctx: CommandContext) -> list[CommandDefinition]:
|
|
113
|
+
"""Return commands available in current context.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
ctx: Command context containing the plan row data
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
List of commands that are available for the given context
|
|
120
|
+
"""
|
|
121
|
+
return [cmd for cmd in get_all_commands() if cmd.is_available(ctx)]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Data types for command palette."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
from erk.tui.data.types import PlanRowData
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class CommandContext:
|
|
11
|
+
"""Context available to commands.
|
|
12
|
+
|
|
13
|
+
Attributes:
|
|
14
|
+
row: The plan row data for the selected plan
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
row: PlanRowData
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class CommandDefinition:
|
|
22
|
+
"""Definition of a command in the command palette.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
id: Unique identifier for the command (e.g., "close_plan")
|
|
26
|
+
name: Display name (e.g., "Close Plan")
|
|
27
|
+
description: Brief description of what the command does
|
|
28
|
+
shortcut: Optional keyboard shortcut for display (e.g., "c")
|
|
29
|
+
is_available: Predicate function to check if command is available
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
id: str
|
|
33
|
+
name: str
|
|
34
|
+
description: str
|
|
35
|
+
shortcut: str | None
|
|
36
|
+
is_available: Callable[[CommandContext], bool]
|
erk/tui/data/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Data providers and types for TUI components."""
|