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,237 @@
|
|
|
1
|
+
"""Post extraction workflow status comments to GitHub issues.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
erk exec post-extraction-comment \
|
|
5
|
+
--issue-number 123 \
|
|
6
|
+
--status started \
|
|
7
|
+
--workflow-run-url "https://..."
|
|
8
|
+
|
|
9
|
+
Status options:
|
|
10
|
+
- started: Extraction workflow has begun
|
|
11
|
+
- failed: Extraction failed with error
|
|
12
|
+
- complete: Extraction succeeded with PR
|
|
13
|
+
- no-changes: No documentation changes needed
|
|
14
|
+
|
|
15
|
+
Output:
|
|
16
|
+
JSON with success status and comment_url
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
from datetime import UTC, datetime
|
|
21
|
+
|
|
22
|
+
import click
|
|
23
|
+
|
|
24
|
+
from erk_shared.context.helpers import require_issues as require_github_issues
|
|
25
|
+
from erk_shared.context.helpers import require_repo_root
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _format_started_comment(workflow_run_url: str | None) -> str:
|
|
29
|
+
"""Format the started status comment."""
|
|
30
|
+
started_at = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
31
|
+
|
|
32
|
+
lines = [
|
|
33
|
+
"⚙️ **Documentation extraction started**",
|
|
34
|
+
"",
|
|
35
|
+
"<details>",
|
|
36
|
+
"<summary>📋 Metadata</summary>",
|
|
37
|
+
"",
|
|
38
|
+
"```yaml",
|
|
39
|
+
"status: started",
|
|
40
|
+
f"started_at: {started_at}",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
if workflow_run_url:
|
|
44
|
+
lines.append(f"workflow_run_url: {workflow_run_url}")
|
|
45
|
+
|
|
46
|
+
lines.extend(
|
|
47
|
+
[
|
|
48
|
+
"```",
|
|
49
|
+
"",
|
|
50
|
+
"</details>",
|
|
51
|
+
"",
|
|
52
|
+
"---",
|
|
53
|
+
"",
|
|
54
|
+
"Extracting session data from issue comments...",
|
|
55
|
+
]
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if workflow_run_url:
|
|
59
|
+
lines.extend(["", f"[View workflow run]({workflow_run_url})"])
|
|
60
|
+
|
|
61
|
+
return "\n".join(lines)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _format_failed_comment(
|
|
65
|
+
workflow_run_url: str | None,
|
|
66
|
+
error_message: str | None,
|
|
67
|
+
) -> str:
|
|
68
|
+
"""Format the failed status comment."""
|
|
69
|
+
lines = ["❌ **Documentation extraction failed**", ""]
|
|
70
|
+
|
|
71
|
+
if error_message:
|
|
72
|
+
lines.extend([f"**Error:** {error_message}", ""])
|
|
73
|
+
|
|
74
|
+
lines.extend(
|
|
75
|
+
[
|
|
76
|
+
"No session content was found in the issue comments. This may happen if:",
|
|
77
|
+
"- The raw extraction issue was created without session XML",
|
|
78
|
+
"- The session content blocks are malformed",
|
|
79
|
+
]
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
if workflow_run_url:
|
|
83
|
+
lines.extend(["", f"[View workflow run]({workflow_run_url})"])
|
|
84
|
+
|
|
85
|
+
lines.extend(
|
|
86
|
+
[
|
|
87
|
+
"",
|
|
88
|
+
"To retry, run the extraction manually:",
|
|
89
|
+
"```",
|
|
90
|
+
"/erk:create-extraction-plan",
|
|
91
|
+
"```",
|
|
92
|
+
]
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
return "\n".join(lines)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _format_complete_comment(pr_url: str | None) -> str:
|
|
99
|
+
"""Format the complete status comment."""
|
|
100
|
+
completed_at = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
101
|
+
|
|
102
|
+
lines = [
|
|
103
|
+
"✅ **Documentation extraction complete**",
|
|
104
|
+
"",
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
if pr_url:
|
|
108
|
+
lines.extend([f"**PR:** {pr_url}", ""])
|
|
109
|
+
|
|
110
|
+
lines.extend(
|
|
111
|
+
[
|
|
112
|
+
"<details>",
|
|
113
|
+
"<summary>📋 Metadata</summary>",
|
|
114
|
+
"",
|
|
115
|
+
"```yaml",
|
|
116
|
+
"status: complete",
|
|
117
|
+
f"completed_at: {completed_at}",
|
|
118
|
+
]
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
if pr_url:
|
|
122
|
+
lines.append(f"pr_url: {pr_url}")
|
|
123
|
+
|
|
124
|
+
lines.extend(
|
|
125
|
+
[
|
|
126
|
+
"```",
|
|
127
|
+
"",
|
|
128
|
+
"</details>",
|
|
129
|
+
"",
|
|
130
|
+
"---",
|
|
131
|
+
"",
|
|
132
|
+
"The extraction has created documentation improvements. Please review the PR.",
|
|
133
|
+
]
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
return "\n".join(lines)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _format_no_changes_comment() -> str:
|
|
140
|
+
"""Format the no-changes status comment."""
|
|
141
|
+
return "\n".join(
|
|
142
|
+
[
|
|
143
|
+
"ℹ️ **No documentation changes needed**",
|
|
144
|
+
"",
|
|
145
|
+
"The extraction analysis did not produce any documentation changes.",
|
|
146
|
+
"This may happen if the session did not contain extractable patterns.",
|
|
147
|
+
]
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@click.command(name="post-extraction-comment")
|
|
152
|
+
@click.option(
|
|
153
|
+
"--issue-number",
|
|
154
|
+
type=int,
|
|
155
|
+
required=True,
|
|
156
|
+
help="GitHub issue number",
|
|
157
|
+
)
|
|
158
|
+
@click.option(
|
|
159
|
+
"--status",
|
|
160
|
+
type=click.Choice(["started", "failed", "complete", "no-changes"]),
|
|
161
|
+
required=True,
|
|
162
|
+
help="Extraction status",
|
|
163
|
+
)
|
|
164
|
+
@click.option(
|
|
165
|
+
"--workflow-run-url",
|
|
166
|
+
type=str,
|
|
167
|
+
default=None,
|
|
168
|
+
help="URL to the workflow run (for started/failed)",
|
|
169
|
+
)
|
|
170
|
+
@click.option(
|
|
171
|
+
"--error-message",
|
|
172
|
+
type=str,
|
|
173
|
+
default=None,
|
|
174
|
+
help="Error message (for failed status)",
|
|
175
|
+
)
|
|
176
|
+
@click.option(
|
|
177
|
+
"--pr-url",
|
|
178
|
+
type=str,
|
|
179
|
+
default=None,
|
|
180
|
+
help="PR URL (for complete status)",
|
|
181
|
+
)
|
|
182
|
+
@click.pass_context
|
|
183
|
+
def post_extraction_comment(
|
|
184
|
+
ctx: click.Context,
|
|
185
|
+
issue_number: int,
|
|
186
|
+
status: str,
|
|
187
|
+
workflow_run_url: str | None,
|
|
188
|
+
error_message: str | None,
|
|
189
|
+
pr_url: str | None,
|
|
190
|
+
) -> None:
|
|
191
|
+
"""Post extraction workflow status comment to a GitHub issue.
|
|
192
|
+
|
|
193
|
+
Posts a formatted status comment based on the extraction workflow stage.
|
|
194
|
+
"""
|
|
195
|
+
github = require_github_issues(ctx)
|
|
196
|
+
repo_root = require_repo_root(ctx)
|
|
197
|
+
|
|
198
|
+
# Format the comment based on status
|
|
199
|
+
if status == "started":
|
|
200
|
+
comment_body = _format_started_comment(workflow_run_url)
|
|
201
|
+
elif status == "failed":
|
|
202
|
+
comment_body = _format_failed_comment(workflow_run_url, error_message)
|
|
203
|
+
elif status == "complete":
|
|
204
|
+
comment_body = _format_complete_comment(pr_url)
|
|
205
|
+
elif status == "no-changes":
|
|
206
|
+
comment_body = _format_no_changes_comment()
|
|
207
|
+
else:
|
|
208
|
+
click.echo(json.dumps({"success": False, "error": f"Unknown status: {status}"}))
|
|
209
|
+
raise SystemExit(1)
|
|
210
|
+
|
|
211
|
+
# Post the comment
|
|
212
|
+
try:
|
|
213
|
+
github.add_comment(repo_root, issue_number, comment_body)
|
|
214
|
+
except RuntimeError as e:
|
|
215
|
+
click.echo(
|
|
216
|
+
json.dumps(
|
|
217
|
+
{
|
|
218
|
+
"success": False,
|
|
219
|
+
"error": f"Failed to post comment: {e}",
|
|
220
|
+
}
|
|
221
|
+
)
|
|
222
|
+
)
|
|
223
|
+
raise SystemExit(1) from e
|
|
224
|
+
|
|
225
|
+
click.echo(
|
|
226
|
+
json.dumps(
|
|
227
|
+
{
|
|
228
|
+
"success": True,
|
|
229
|
+
"issue_number": issue_number,
|
|
230
|
+
"status": status,
|
|
231
|
+
}
|
|
232
|
+
)
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
if __name__ == "__main__":
|
|
237
|
+
post_extraction_comment()
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Post or update a PR summary comment with a unique marker.
|
|
2
|
+
|
|
3
|
+
This exec command finds an existing PR comment containing a marker,
|
|
4
|
+
updates it if found, or creates a new comment if not found.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
erk exec post-or-update-pr-summary --pr-number 123 \\
|
|
8
|
+
--marker "<!-- my-marker -->" --body "Summary text"
|
|
9
|
+
|
|
10
|
+
Output:
|
|
11
|
+
JSON with success status, action taken (created/updated), and comment ID
|
|
12
|
+
|
|
13
|
+
Exit Codes:
|
|
14
|
+
0: Always (even on error, to support || true pattern)
|
|
15
|
+
|
|
16
|
+
Examples:
|
|
17
|
+
$ erk exec post-or-update-pr-summary --pr-number 123 \\
|
|
18
|
+
--marker "<!-- review -->" --body "# Review"
|
|
19
|
+
{"success": true, "action": "created", "comment_id": 12345}
|
|
20
|
+
|
|
21
|
+
$ erk exec post-or-update-pr-summary --pr-number 123 \\
|
|
22
|
+
--marker "<!-- review -->" --body "# Updated"
|
|
23
|
+
{"success": true, "action": "updated", "comment_id": 12345}
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
import json
|
|
27
|
+
from dataclasses import asdict, dataclass
|
|
28
|
+
|
|
29
|
+
import click
|
|
30
|
+
|
|
31
|
+
from erk_shared.context.helpers import require_github, require_repo_root
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class SummaryCommentSuccess:
|
|
36
|
+
"""Success response for summary comment posting."""
|
|
37
|
+
|
|
38
|
+
success: bool
|
|
39
|
+
action: str # "created" or "updated"
|
|
40
|
+
comment_id: int
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass(frozen=True)
|
|
44
|
+
class SummaryCommentError:
|
|
45
|
+
"""Error response for summary comment posting."""
|
|
46
|
+
|
|
47
|
+
success: bool
|
|
48
|
+
error_type: str
|
|
49
|
+
message: str
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@click.command(name="post-or-update-pr-summary")
|
|
53
|
+
@click.option("--pr-number", required=True, type=int, help="PR number to comment on")
|
|
54
|
+
@click.option("--marker", required=True, help="HTML marker to identify the comment")
|
|
55
|
+
@click.option("--body", required=True, help="Comment body text (must include marker)")
|
|
56
|
+
@click.pass_context
|
|
57
|
+
def post_or_update_pr_summary(
|
|
58
|
+
ctx: click.Context,
|
|
59
|
+
pr_number: int,
|
|
60
|
+
marker: str,
|
|
61
|
+
body: str,
|
|
62
|
+
) -> None:
|
|
63
|
+
"""Post or update a PR summary comment.
|
|
64
|
+
|
|
65
|
+
Finds an existing comment containing the marker and updates it,
|
|
66
|
+
or creates a new comment if none found. The body should include
|
|
67
|
+
the marker for future lookups.
|
|
68
|
+
|
|
69
|
+
PR_NUMBER: The PR to comment on
|
|
70
|
+
MARKER: HTML marker to identify the comment (e.g., <!-- my-review -->)
|
|
71
|
+
BODY: Comment text (should include the marker for future updates)
|
|
72
|
+
"""
|
|
73
|
+
repo_root = require_repo_root(ctx)
|
|
74
|
+
github = require_github(ctx)
|
|
75
|
+
|
|
76
|
+
# Ensure body contains the marker for future lookups
|
|
77
|
+
if marker not in body:
|
|
78
|
+
result = SummaryCommentError(
|
|
79
|
+
success=False,
|
|
80
|
+
error_type="marker_not_in_body",
|
|
81
|
+
message=f"The body must contain the marker '{marker}' for future lookups",
|
|
82
|
+
)
|
|
83
|
+
click.echo(json.dumps(asdict(result), indent=2))
|
|
84
|
+
raise SystemExit(0)
|
|
85
|
+
|
|
86
|
+
# Try to find existing comment with marker
|
|
87
|
+
try:
|
|
88
|
+
existing_id = github.find_pr_comment_by_marker(repo_root, pr_number, marker)
|
|
89
|
+
except RuntimeError as e:
|
|
90
|
+
result = SummaryCommentError(
|
|
91
|
+
success=False,
|
|
92
|
+
error_type="find_failed",
|
|
93
|
+
message=f"Failed to search for existing comment: {e}",
|
|
94
|
+
)
|
|
95
|
+
click.echo(json.dumps(asdict(result), indent=2))
|
|
96
|
+
raise SystemExit(0) from None
|
|
97
|
+
|
|
98
|
+
if existing_id is not None:
|
|
99
|
+
# Update existing comment
|
|
100
|
+
try:
|
|
101
|
+
github.update_pr_comment(repo_root, existing_id, body)
|
|
102
|
+
result_success = SummaryCommentSuccess(
|
|
103
|
+
success=True,
|
|
104
|
+
action="updated",
|
|
105
|
+
comment_id=existing_id,
|
|
106
|
+
)
|
|
107
|
+
click.echo(json.dumps(asdict(result_success), indent=2))
|
|
108
|
+
except RuntimeError as e:
|
|
109
|
+
result = SummaryCommentError(
|
|
110
|
+
success=False,
|
|
111
|
+
error_type="update_failed",
|
|
112
|
+
message=str(e),
|
|
113
|
+
)
|
|
114
|
+
click.echo(json.dumps(asdict(result), indent=2))
|
|
115
|
+
else:
|
|
116
|
+
# Create new comment
|
|
117
|
+
try:
|
|
118
|
+
comment_id = github.create_pr_comment(repo_root, pr_number, body)
|
|
119
|
+
result_success = SummaryCommentSuccess(
|
|
120
|
+
success=True,
|
|
121
|
+
action="created",
|
|
122
|
+
comment_id=comment_id,
|
|
123
|
+
)
|
|
124
|
+
click.echo(json.dumps(asdict(result_success), indent=2))
|
|
125
|
+
except RuntimeError as e:
|
|
126
|
+
result = SummaryCommentError(
|
|
127
|
+
success=False,
|
|
128
|
+
error_type="create_failed",
|
|
129
|
+
message=str(e),
|
|
130
|
+
)
|
|
131
|
+
click.echo(json.dumps(asdict(result), indent=2))
|
|
132
|
+
|
|
133
|
+
raise SystemExit(0)
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""Post an inline review comment on a specific line of a PR.
|
|
2
|
+
|
|
3
|
+
This exec command creates a pull request review comment attached to a
|
|
4
|
+
specific line of a file in the PR diff.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
erk exec post-pr-inline-comment --pr-number 123 \\
|
|
8
|
+
--path "src/foo.py" --line 42 --body "Comment text"
|
|
9
|
+
|
|
10
|
+
Output:
|
|
11
|
+
JSON with success status and comment ID
|
|
12
|
+
|
|
13
|
+
Exit Codes:
|
|
14
|
+
0: Always (even on error, to support || true pattern)
|
|
15
|
+
|
|
16
|
+
Examples:
|
|
17
|
+
$ erk exec post-pr-inline-comment --pr-number 123 \\
|
|
18
|
+
--path "src/foo.py" --line 42 --body "Use LBYL"
|
|
19
|
+
{"success": true, "comment_id": 12345}
|
|
20
|
+
|
|
21
|
+
$ erk exec post-pr-inline-comment --pr-number 123 \\
|
|
22
|
+
--path "bad.py" --line 999 --body "Comment"
|
|
23
|
+
{"success": false, "error_type": "github_api_failed", "message": "..."}
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
import json
|
|
27
|
+
from dataclasses import asdict, dataclass
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
import click
|
|
31
|
+
|
|
32
|
+
from erk_shared.context.helpers import require_github, require_repo_root
|
|
33
|
+
from erk_shared.github.parsing import execute_gh_command
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True)
|
|
37
|
+
class InlineCommentSuccess:
|
|
38
|
+
"""Success response for inline comment posting."""
|
|
39
|
+
|
|
40
|
+
success: bool
|
|
41
|
+
comment_id: int
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class InlineCommentError:
|
|
46
|
+
"""Error response for inline comment posting."""
|
|
47
|
+
|
|
48
|
+
success: bool
|
|
49
|
+
error_type: str
|
|
50
|
+
message: str
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _get_pr_head_sha(repo_root: Path, pr_number: int) -> str:
|
|
54
|
+
"""Get the head commit SHA for a PR.
|
|
55
|
+
|
|
56
|
+
Uses gh CLI REST API to fetch the PR head ref SHA.
|
|
57
|
+
Uses REST API instead of GraphQL (`gh pr view`) to avoid hitting
|
|
58
|
+
GraphQL rate limits. GraphQL and REST have separate quotas.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
repo_root: Repository root directory
|
|
62
|
+
pr_number: PR number to query
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
The head commit SHA as a string
|
|
66
|
+
|
|
67
|
+
Raises:
|
|
68
|
+
RuntimeError: If gh command fails
|
|
69
|
+
"""
|
|
70
|
+
# GH-API-AUDIT: REST - GET pulls/{number}
|
|
71
|
+
cmd = [
|
|
72
|
+
"gh",
|
|
73
|
+
"api",
|
|
74
|
+
f"repos/{{owner}}/{{repo}}/pulls/{pr_number}",
|
|
75
|
+
"--jq",
|
|
76
|
+
".head.sha",
|
|
77
|
+
]
|
|
78
|
+
stdout = execute_gh_command(cmd, repo_root)
|
|
79
|
+
return stdout.strip()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@click.command(name="post-pr-inline-comment")
|
|
83
|
+
@click.option("--pr-number", required=True, type=int, help="PR number to comment on")
|
|
84
|
+
@click.option("--path", required=True, help="File path relative to repo root")
|
|
85
|
+
@click.option("--line", required=True, type=int, help="Line number in the diff")
|
|
86
|
+
@click.option("--body", required=True, help="Comment body text")
|
|
87
|
+
@click.pass_context
|
|
88
|
+
def post_pr_inline_comment(
|
|
89
|
+
ctx: click.Context,
|
|
90
|
+
pr_number: int,
|
|
91
|
+
path: str,
|
|
92
|
+
line: int,
|
|
93
|
+
body: str,
|
|
94
|
+
) -> None:
|
|
95
|
+
"""Post an inline review comment on a PR.
|
|
96
|
+
|
|
97
|
+
Creates a pull request review comment attached to a specific line
|
|
98
|
+
of a file in the PR diff. Automatically fetches the PR head commit SHA.
|
|
99
|
+
|
|
100
|
+
PR_NUMBER: The PR to comment on
|
|
101
|
+
PATH: File path relative to repository root
|
|
102
|
+
LINE: Line number in the diff to attach comment to
|
|
103
|
+
BODY: Comment text (markdown supported)
|
|
104
|
+
"""
|
|
105
|
+
repo_root = require_repo_root(ctx)
|
|
106
|
+
github = require_github(ctx)
|
|
107
|
+
|
|
108
|
+
# Get the PR head commit SHA
|
|
109
|
+
try:
|
|
110
|
+
commit_sha = _get_pr_head_sha(repo_root, pr_number)
|
|
111
|
+
except RuntimeError as e:
|
|
112
|
+
result = InlineCommentError(
|
|
113
|
+
success=False,
|
|
114
|
+
error_type="pr-not-found",
|
|
115
|
+
message=f"Could not get PR head commit: {e}",
|
|
116
|
+
)
|
|
117
|
+
click.echo(json.dumps(asdict(result), indent=2))
|
|
118
|
+
raise SystemExit(0) from None
|
|
119
|
+
|
|
120
|
+
# Create the inline comment
|
|
121
|
+
try:
|
|
122
|
+
comment_id = github.create_pr_review_comment(
|
|
123
|
+
repo_root,
|
|
124
|
+
pr_number,
|
|
125
|
+
body,
|
|
126
|
+
commit_sha,
|
|
127
|
+
path,
|
|
128
|
+
line,
|
|
129
|
+
)
|
|
130
|
+
result_success = InlineCommentSuccess(
|
|
131
|
+
success=True,
|
|
132
|
+
comment_id=comment_id,
|
|
133
|
+
)
|
|
134
|
+
click.echo(json.dumps(asdict(result_success), indent=2))
|
|
135
|
+
except RuntimeError as e:
|
|
136
|
+
result = InlineCommentError(
|
|
137
|
+
success=False,
|
|
138
|
+
error_type="github-api-failed",
|
|
139
|
+
message=str(e),
|
|
140
|
+
)
|
|
141
|
+
click.echo(json.dumps(asdict(result), indent=2))
|
|
142
|
+
|
|
143
|
+
raise SystemExit(0)
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Post a "workflow started" comment to a GitHub issue with YAML metadata block.
|
|
3
|
+
|
|
4
|
+
This command posts a structured comment to a GitHub issue indicating that a
|
|
5
|
+
GitHub Actions workflow has started. The comment includes a YAML metadata block
|
|
6
|
+
that can be parsed programmatically.
|
|
7
|
+
|
|
8
|
+
This replaces ~40 lines of bash heredoc template assembly in GitHub Actions workflows.
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
erk exec post-workflow-started-comment \\
|
|
12
|
+
--issue-number 123 \\
|
|
13
|
+
--branch-name my-feature-branch \\
|
|
14
|
+
--pr-number 456 \\
|
|
15
|
+
--run-id 12345678 \\
|
|
16
|
+
--run-url https://github.com/owner/repo/actions/runs/12345678 \\
|
|
17
|
+
--repository owner/repo
|
|
18
|
+
|
|
19
|
+
Output:
|
|
20
|
+
JSON object with success status
|
|
21
|
+
|
|
22
|
+
Exit Codes:
|
|
23
|
+
0: Success (comment posted)
|
|
24
|
+
1: Error (GitHub API failed)
|
|
25
|
+
|
|
26
|
+
Examples:
|
|
27
|
+
$ erk exec post-workflow-started-comment \\
|
|
28
|
+
--issue-number 123 \\
|
|
29
|
+
--branch-name feat-auth \\
|
|
30
|
+
--pr-number 456 \\
|
|
31
|
+
--run-id 99999 \\
|
|
32
|
+
--run-url https://github.com/acme/app/actions/runs/99999 \\
|
|
33
|
+
--repository acme/app
|
|
34
|
+
{
|
|
35
|
+
"success": true,
|
|
36
|
+
"issue_number": 123
|
|
37
|
+
}
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
import json
|
|
41
|
+
from dataclasses import asdict, dataclass
|
|
42
|
+
from datetime import UTC, datetime
|
|
43
|
+
|
|
44
|
+
import click
|
|
45
|
+
|
|
46
|
+
from erk_shared.context.helpers import require_issues as require_github_issues
|
|
47
|
+
from erk_shared.context.helpers import require_repo_root
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(frozen=True)
|
|
51
|
+
class PostSuccess:
|
|
52
|
+
"""Success result when comment is posted."""
|
|
53
|
+
|
|
54
|
+
success: bool
|
|
55
|
+
issue_number: int
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass(frozen=True)
|
|
59
|
+
class PostError:
|
|
60
|
+
"""Error result when comment posting fails."""
|
|
61
|
+
|
|
62
|
+
success: bool
|
|
63
|
+
error: str
|
|
64
|
+
message: str
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _build_workflow_started_comment(
|
|
68
|
+
issue_number: int,
|
|
69
|
+
branch_name: str,
|
|
70
|
+
pr_number: int,
|
|
71
|
+
run_id: str,
|
|
72
|
+
run_url: str,
|
|
73
|
+
repository: str,
|
|
74
|
+
) -> str:
|
|
75
|
+
"""Build the workflow started comment body.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
issue_number: GitHub issue number
|
|
79
|
+
branch_name: Git branch name
|
|
80
|
+
pr_number: Pull request number
|
|
81
|
+
run_id: GitHub Actions workflow run ID
|
|
82
|
+
run_url: Full URL to the workflow run
|
|
83
|
+
repository: Repository in owner/repo format
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Formatted markdown comment body
|
|
87
|
+
"""
|
|
88
|
+
started_at = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
89
|
+
|
|
90
|
+
return f"""⚙️ GitHub Action Started
|
|
91
|
+
|
|
92
|
+
<details>
|
|
93
|
+
<summary>📋 Metadata</summary>
|
|
94
|
+
|
|
95
|
+
<!-- erk:metadata-block:workflow-started -->
|
|
96
|
+
```yaml
|
|
97
|
+
schema: workflow-started
|
|
98
|
+
status: started
|
|
99
|
+
started_at: {started_at}
|
|
100
|
+
workflow_run_id: "{run_id}"
|
|
101
|
+
workflow_run_url: {run_url}
|
|
102
|
+
branch_name: {branch_name}
|
|
103
|
+
issue_number: {issue_number}
|
|
104
|
+
```
|
|
105
|
+
<!-- /erk:metadata-block:workflow-started -->
|
|
106
|
+
|
|
107
|
+
</details>
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
Setup completed successfully.
|
|
112
|
+
|
|
113
|
+
**Branch:** `{branch_name}`
|
|
114
|
+
**PR:** [#{pr_number}](https://github.com/{repository}/pull/{pr_number})
|
|
115
|
+
**Status:** Ready for implementation
|
|
116
|
+
|
|
117
|
+
[View workflow run]({run_url})
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@click.command(name="post-workflow-started-comment")
|
|
122
|
+
@click.option("--issue-number", type=int, required=True, help="GitHub issue number")
|
|
123
|
+
@click.option("--branch-name", type=str, required=True, help="Git branch name")
|
|
124
|
+
@click.option("--pr-number", type=int, required=True, help="Pull request number")
|
|
125
|
+
@click.option("--run-id", type=str, required=True, help="GitHub Actions workflow run ID")
|
|
126
|
+
@click.option("--run-url", type=str, required=True, help="Full URL to workflow run")
|
|
127
|
+
@click.option("--repository", type=str, required=True, help="Repository in owner/repo format")
|
|
128
|
+
@click.pass_context
|
|
129
|
+
def post_workflow_started_comment(
|
|
130
|
+
ctx: click.Context,
|
|
131
|
+
issue_number: int,
|
|
132
|
+
branch_name: str,
|
|
133
|
+
pr_number: int,
|
|
134
|
+
run_id: str,
|
|
135
|
+
run_url: str,
|
|
136
|
+
repository: str,
|
|
137
|
+
) -> None:
|
|
138
|
+
"""Post a workflow started comment to a GitHub issue.
|
|
139
|
+
|
|
140
|
+
Posts a structured comment with YAML metadata block indicating that a
|
|
141
|
+
GitHub Actions workflow has started processing the issue.
|
|
142
|
+
"""
|
|
143
|
+
github = require_github_issues(ctx)
|
|
144
|
+
repo_root = require_repo_root(ctx)
|
|
145
|
+
|
|
146
|
+
# Build comment body
|
|
147
|
+
comment_body = _build_workflow_started_comment(
|
|
148
|
+
issue_number=issue_number,
|
|
149
|
+
branch_name=branch_name,
|
|
150
|
+
pr_number=pr_number,
|
|
151
|
+
run_id=run_id,
|
|
152
|
+
run_url=run_url,
|
|
153
|
+
repository=repository,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Post comment
|
|
157
|
+
try:
|
|
158
|
+
github.add_comment(repo_root, issue_number, comment_body)
|
|
159
|
+
result = PostSuccess(success=True, issue_number=issue_number)
|
|
160
|
+
click.echo(json.dumps(asdict(result), indent=2))
|
|
161
|
+
except RuntimeError as e:
|
|
162
|
+
result = PostError(
|
|
163
|
+
success=False,
|
|
164
|
+
error="github-api-failed",
|
|
165
|
+
message=str(e),
|
|
166
|
+
)
|
|
167
|
+
click.echo(json.dumps(asdict(result), indent=2))
|
|
168
|
+
raise SystemExit(1) from e
|