Add a second reviewer workflow prompt to the root MCP operator menu: "Skip already-reviewed stale REQUEST_CHANGES PR and hand off to author". show_reviewer_prompts becomes a small 2-option submenu (mirroring the existing show_author_prompts idiom): option 1 keeps the standard PR review prompt; option 2 is the new prompt. It instructs the reviewer to determine whether the current head already carries a non-stale REQUEST_CHANGES verdict and, if so, to submit no new terminal review mutation, run only handoff diagnostics, and return an author-ready fix prompt with the full 8-field handoff. Stacked on PR #479 (feat/issue-478-mcp-menu-shell); the menu is not yet on master. Diff limited to the new prompt: mcp-menu.sh, docs/mcp-menu.md, and a discoverability test in tests/test_mcp_menu_script.py. Closes #482. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
269 lines
11 KiB
Bash
Executable File
269 lines
11 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# mcp-menu.sh — Repository-root operator menu for MCP/Gitea workflow onboarding.
|
|
#
|
|
# Safe by default: read-only status and copy-paste prompts unless an action is
|
|
# explicitly labeled and confirmed. No branch deletion, force-push, lock-file
|
|
# editing, or raw API bypass.
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$SCRIPT_DIR"
|
|
|
|
pause() {
|
|
read -r -p "Press Enter to return to the menu..."
|
|
}
|
|
|
|
print_banner() {
|
|
printf '\n=== Gitea-Tools MCP Operator Menu ===\n'
|
|
printf 'Repository: %s\n' "$REPO_ROOT"
|
|
printf 'Safe by default — destructive actions require explicit confirmation.\n\n'
|
|
}
|
|
|
|
show_root_checkout_health() {
|
|
printf '\n--- Project status / root checkout health ---\n\n'
|
|
printf 'Current directory: %s\n' "$(pwd)"
|
|
local branch head_sha prgs_master_sha dirty
|
|
branch="$(git -C "$REPO_ROOT" branch --show-current 2>/dev/null || true)"
|
|
if [[ -z "$branch" ]]; then
|
|
branch="(detached HEAD)"
|
|
fi
|
|
printf 'Current branch: %s\n' "$branch"
|
|
printf '\nGit status (short, branch):\n'
|
|
git -C "$REPO_ROOT" status --short --branch || true
|
|
head_sha="$(git -C "$REPO_ROOT" rev-parse HEAD 2>/dev/null || echo 'unknown')"
|
|
printf '\nHEAD SHA: %s\n' "$head_sha"
|
|
if git -C "$REPO_ROOT" rev-parse --verify prgs/master >/dev/null 2>&1; then
|
|
prgs_master_sha="$(git -C "$REPO_ROOT" rev-parse prgs/master)"
|
|
printf 'prgs/master SHA: %s\n' "$prgs_master_sha"
|
|
if [[ "$head_sha" != "$prgs_master_sha" ]]; then
|
|
printf '\nWARNING: root checkout HEAD does not match prgs/master.\n'
|
|
printf 'Keep the stable control checkout on master/prgs/master.\n'
|
|
fi
|
|
else
|
|
printf 'prgs/master SHA: unavailable (remote ref not fetched)\n'
|
|
fi
|
|
if [[ -n "$(git -C "$REPO_ROOT" status --porcelain 2>/dev/null || true)" ]]; then
|
|
printf '\nWARNING: root checkout has uncommitted changes (dirty).\n'
|
|
printf 'Author mutations belong in a session worktree under branches/.\n'
|
|
fi
|
|
if [[ "$branch" != "master" && "$branch" != "main" && "$branch" != "dev" ]]; then
|
|
printf '\nWARNING: root checkout is not on a stable base branch (master/main/dev).\n'
|
|
printf 'Return to master before using the control checkout.\n'
|
|
fi
|
|
pause
|
|
}
|
|
|
|
print_prompt_block() {
|
|
local title="$1"
|
|
local body="$2"
|
|
printf '\n--- %s ---\n\n' "$title"
|
|
printf '%s\n' "$body"
|
|
printf '\n(Copy the prompt above into your LLM session.)\n'
|
|
pause
|
|
}
|
|
|
|
show_author_prompts() {
|
|
while true; do
|
|
printf '\n--- Author workflow prompts ---\n'
|
|
printf ' 1) Work issue (author/coder)\n'
|
|
printf ' 2) Conflict-fix author session\n'
|
|
printf ' 3) Root checkout recovery session\n'
|
|
printf ' 0) Back\n'
|
|
read -r -p 'Choice: ' choice
|
|
case "$choice" in
|
|
1)
|
|
print_prompt_block "Author — work issue" \
|
|
"You are the AUTHOR session for <org>/<repo>.
|
|
|
|
Goal: implement issue #<N> only.
|
|
|
|
Workflow:
|
|
1. Preflight: prove identity, work_issue/create_pr capability, clean session worktree under branches/.
|
|
2. gitea_lock_issue for issue #<N> and branch feat/issue-<N>-<short-desc>.
|
|
3. Implement in the locked worktree only — never mutate the root control checkout.
|
|
4. Validate, commit, push, gitea_create_pr. Final report with issue, branch, SHA, PR, tests, mutation ledger."
|
|
;;
|
|
2)
|
|
print_prompt_block "Author — conflict-fix session" \
|
|
"You are the AUTHOR session for <org>/<repo> in conflict-fix mode.
|
|
|
|
Goal: resolve merge conflicts on PR #<P> / branch <branch> only.
|
|
|
|
Workflow:
|
|
1. Preflight: prove author identity and exact push/commit capability for the locked PR branch.
|
|
2. Confirm conflict-fix lease and stale-head protection before pushing.
|
|
3. Work only in the session-owned worktree under branches/ — never the root checkout.
|
|
4. Rebase or merge target branch, run tests, push, update PR. No force-push without explicit operator approval.
|
|
5. Final report: conflict resolution proof, new HEAD SHA, tests, mutation ledger."
|
|
;;
|
|
3)
|
|
print_prompt_block "Author — root checkout recovery" \
|
|
"You are a RECOVERY session for <org>/<repo>.
|
|
|
|
Goal: restore the stable root control checkout to clean master/prgs/master.
|
|
|
|
Workflow:
|
|
1. Inspect root checkout: branch, git status --short --branch, HEAD vs prgs/master.
|
|
2. Do not implement features from the root checkout. Stash or move work to branches/<session> first.
|
|
3. Return root to master (or main/dev per project policy) matching prgs/master with no dirty tracked files.
|
|
4. Report before/after branch, SHA, dirty state, and safe next action for author worktree creation."
|
|
;;
|
|
0) return ;;
|
|
*) printf 'Invalid choice.\n' ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
show_reviewer_prompts() {
|
|
while true; do
|
|
printf '\n--- Reviewer workflow prompts ---\n'
|
|
printf ' 1) Standard PR review\n'
|
|
printf ' 2) Skip already-reviewed stale REQUEST_CHANGES PR and hand off to author\n'
|
|
printf ' 0) Back\n'
|
|
read -r -p 'Choice: ' choice
|
|
case "$choice" in
|
|
1)
|
|
print_prompt_block "Reviewer — PR review" \
|
|
"You are the REVIEWER session for <org>/<repo>.
|
|
|
|
Goal: review PR #<P> only — do not merge unless explicitly switched to merger mode.
|
|
|
|
Workflow:
|
|
1. Load canonical workflow: skills/llm-project-workflow/workflows/review-merge-pr.md
|
|
2. Preflight: prove reviewer identity, review_pr capability, clean review worktree.
|
|
3. gitea_view_pr, validate scope, run required checks in the correct worktree.
|
|
4. gitea_review_pr with approve, request-changes, or comment as warranted.
|
|
5. Final report: PR head SHA, verdict, validation evidence, mutation ledger. No merge in reviewer-only runs."
|
|
;;
|
|
2)
|
|
print_prompt_block "Reviewer — skip already-reviewed stale REQUEST_CHANGES PR and hand off to author" \
|
|
"You are the REVIEWER session for <org>/<repo>.
|
|
|
|
Goal: review PR #<P> only far enough to determine whether the current head already has a non-stale REQUEST_CHANGES verdict. If it does, do NOT submit another terminal review mutation — produce an author handoff and stop.
|
|
|
|
Workflow:
|
|
1. gitea_view_pr; pin the current head SHA.
|
|
2. Load prior reviews; find the latest REQUEST_CHANGES and the head SHA it was bound to.
|
|
3. If that REQUEST_CHANGES is still bound to the current head (non-stale), do not submit another terminal review mutation. Confirm the binding and summarize the blockers.
|
|
4. Run only the diagnostics needed to produce a useful author handoff — no full re-review.
|
|
5. Duplicate/supersession check: confirm no newer canonical PR or superseding head changes the decision.
|
|
6. Stop — no new review mutation.
|
|
|
|
Return:
|
|
- selected PR and issue
|
|
- current head SHA
|
|
- prior REQUEST_CHANGES proof (review id + bound head SHA)
|
|
- duplicate/supersession analysis
|
|
- validation summary
|
|
- why no new review mutation was submitted
|
|
- corrected mutation ledger, including local worktree create/remove if used
|
|
- author-ready fix prompt"
|
|
;;
|
|
0) return ;;
|
|
*) printf 'Invalid choice.\n' ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
show_merger_prompts() {
|
|
print_prompt_block "Merger — PR merge" \
|
|
"You are the MERGER session for <org>/<repo>.
|
|
|
|
Goal: merge PR #<P> only after every gate passes.
|
|
|
|
Workflow:
|
|
1. Load canonical workflow: skills/llm-project-workflow/workflows/review-merge-pr.md
|
|
2. Preflight: prove merger identity and exact merge_pr capability for the current PR head SHA.
|
|
3. Confirm approval pins the current head SHA; re-validate if the branch moved.
|
|
4. gitea_merge_pr only on explicit operator approval after all gates pass.
|
|
5. Final report: merged SHA, cleanup handoff, mutation ledger."
|
|
}
|
|
|
|
show_reconciler_prompts() {
|
|
print_prompt_block "Reconciler — already-landed / closed PR cleanup" \
|
|
"You are the RECONCILER session for <org>/<repo>.
|
|
|
|
Goal: reconcile already-landed open PRs — close or comment only when exact capability is proven.
|
|
|
|
Workflow:
|
|
1. Load canonical workflow: skills/llm-project-workflow/workflows/reconcile-landed-pr.md
|
|
2. Preflight: prove reconciler identity and gitea.pr.close (or authorized close) capability.
|
|
3. Do not review, merge, implement code, or create branches.
|
|
4. gitea_scan_already_landed_open_prs / gitea_reconcile_already_landed_pr as appropriate.
|
|
5. Final report: PR numbers handled, close proof, mutation ledger."
|
|
}
|
|
|
|
show_onboarding_prompt() {
|
|
print_prompt_block "Onboarding — new project to MCP workflow" \
|
|
"Onboard <org>/<repo> into the MCP Control Plane workflow.
|
|
|
|
Checklist:
|
|
1. Prove identity and task capability via gitea_whoami and gitea_resolve_task_capability.
|
|
2. Configure separate MCP namespaces/profiles: author, reviewer, merger/reconciler as needed.
|
|
3. Register gitea-tools (and jenkins-mcp / glitchtip-mcp if applicable) in the client MCP config.
|
|
4. Copy skills/llm-project-workflow/SKILL.md guidance into the target repo or ECC install.
|
|
5. Verify gitea_get_runtime_context, gitea_lock_issue, and worktree rules under branches/.
|
|
6. Run ./mcp-menu.sh for day-to-day prompts; use docs/mcp-menu.md and docs/llm-workflow-runbooks.md.
|
|
|
|
Canonical router: skills/llm-project-workflow/SKILL.md"
|
|
}
|
|
|
|
show_proxmox_placeholder() {
|
|
printf '\n--- Proxmox deployment (placeholder) ---\n\n'
|
|
printf 'Push this project to Proxmox — TODO / issue-backed\n'
|
|
printf 'Create Proxmox LXC — TODO / issue-backed\n\n'
|
|
printf 'These actions are NOT implemented yet.\n'
|
|
printf 'Track deployment automation in dedicated Gitea issues before enabling here.\n'
|
|
printf 'This menu will not run deploy scripts until sanctioned tooling exists.\n'
|
|
pause
|
|
}
|
|
|
|
run_tests() {
|
|
printf '\n--- Run tests ---\n\n'
|
|
if [[ -x "$REPO_ROOT/run-tests.sh" ]]; then
|
|
printf 'Running ./run-tests.sh ...\n\n'
|
|
(cd "$REPO_ROOT" && ./run-tests.sh)
|
|
pause
|
|
return
|
|
fi
|
|
if [[ -x "$REPO_ROOT/venv/bin/python" ]]; then
|
|
printf 'run-tests.sh not found; falling back to venv/bin/python -m pytest\n\n'
|
|
(cd "$REPO_ROOT" && ./venv/bin/python -m pytest)
|
|
pause
|
|
return
|
|
fi
|
|
printf 'ERROR: No test runner available (fail closed).\n' >&2
|
|
printf 'Expected ./run-tests.sh or venv/bin/python for pytest fallback.\n' >&2
|
|
exit 1
|
|
}
|
|
|
|
main_menu() {
|
|
while true; do
|
|
print_banner
|
|
printf ' 1) Project status / root checkout health\n'
|
|
printf ' 2) Author workflow prompts\n'
|
|
printf ' 3) Reviewer workflow prompts\n'
|
|
printf ' 4) Merger workflow prompts\n'
|
|
printf ' 5) Reconciler workflow prompts\n'
|
|
printf ' 6) Onboarding new project to this MCP workflow\n'
|
|
printf ' 7) Proxmox deployment menu placeholder\n'
|
|
printf ' 8) Create Proxmox LXC placeholder\n'
|
|
printf ' 9) Run tests\n'
|
|
printf ' 0) Exit\n'
|
|
read -r -p 'Choice: ' choice
|
|
case "$choice" in
|
|
1) show_root_checkout_health ;;
|
|
2) show_author_prompts ;;
|
|
3) show_reviewer_prompts ;;
|
|
4) show_merger_prompts ;;
|
|
5) show_reconciler_prompts ;;
|
|
6) show_onboarding_prompt ;;
|
|
7|8) show_proxmox_placeholder ;;
|
|
9) run_tests ;;
|
|
0) printf 'Goodbye.\n'; exit 0 ;;
|
|
*) printf 'Invalid choice.\n'; pause ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
main_menu |