mirror of
https://github.com/AmanTahiliani/box-box.git
synced 2026-08-07 11:54:59 -04:00
122 lines
4.2 KiB
Bash
122 lines
4.2 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
# .agents/lib/dispatch.sh — implement a Ready issue with a chosen harness in an
|
||
|
|
# isolated git worktree, run the build gate, and open a PR.
|
||
|
|
#
|
||
|
|
# Source it, then: dispatch <issue#> <harness> [--dry-run] [--base <branch>]
|
||
|
|
# (or use the CLI: .agents/bin/dev implement <issue#> --harness <name> [--dry-run])
|
||
|
|
|
||
|
|
_AGENTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
|
|
# shellcheck source=/dev/null
|
||
|
|
source "$_AGENTS_DIR/lib/gh.sh"
|
||
|
|
# shellcheck source=/dev/null
|
||
|
|
source "$_AGENTS_DIR/harnesses.sh"
|
||
|
|
|
||
|
|
_slug() { echo "$1" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//' | cut -c1-40; }
|
||
|
|
|
||
|
|
_render_prompt() { # <issue#> <title> <body>
|
||
|
|
echo "# Implement: $2"
|
||
|
|
echo
|
||
|
|
cat "$_AGENTS_DIR/prompts/implement.md"
|
||
|
|
echo; echo "---"; echo
|
||
|
|
echo "## Spec — issue #$1"
|
||
|
|
echo
|
||
|
|
echo "$3"
|
||
|
|
}
|
||
|
|
|
||
|
|
_pr_body() { # <issue#> <harness> <gate>
|
||
|
|
cat <<EOF
|
||
|
|
Implements #$1.
|
||
|
|
|
||
|
|
- **Harness:** $2 (dispatched via \`.agents/bin/dev\`)
|
||
|
|
- **Local gate** (\`go build\` + \`tsc --noEmit\`): **$3**
|
||
|
|
- Full test suite + independent review run in CI / by a reviewer harness.
|
||
|
|
|
||
|
|
See #$1 for the groomed spec, Test Plan, and Definition of Done.
|
||
|
|
|
||
|
|
Closes #$1
|
||
|
|
EOF
|
||
|
|
}
|
||
|
|
|
||
|
|
dispatch() { # <issue#> <harness> [--dry-run] [--base <branch>]
|
||
|
|
local issue="$1" harness="$2"; shift 2 || { echo "usage: dispatch <issue#> <harness> [--dry-run] [--base <branch>]"; return 2; }
|
||
|
|
local dry=0 base="main"
|
||
|
|
while [ $# -gt 0 ]; do
|
||
|
|
case "$1" in
|
||
|
|
--dry-run) dry=1 ;;
|
||
|
|
--base) base="$2"; shift ;;
|
||
|
|
*) echo "dispatch: unknown flag '$1'" >&2; return 2 ;;
|
||
|
|
esac; shift
|
||
|
|
done
|
||
|
|
|
||
|
|
# adapter must exist
|
||
|
|
if ! declare -f "harness_$harness" >/dev/null 2>&1; then
|
||
|
|
echo "no adapter for harness '$harness' — add harness_$harness() to .agents/harnesses.sh" >&2; return 2
|
||
|
|
fi
|
||
|
|
|
||
|
|
local repo_root title body slug branch wt prompt
|
||
|
|
repo_root="$(git rev-parse --show-toplevel)" || return 1
|
||
|
|
title="$(issue_title "$issue")" || { echo "issue #$issue not found on $REPO" >&2; return 1; }
|
||
|
|
body="$(issue_body "$issue")"
|
||
|
|
slug="$(_slug "$title")"
|
||
|
|
branch="feat/issue-${issue}-${slug}"
|
||
|
|
wt="$repo_root/.worktrees/issue-${issue}"
|
||
|
|
prompt="$(mktemp "${TMPDIR:-/tmp}/boxbox-prompt-${issue}.XXXX")"
|
||
|
|
_render_prompt "$issue" "$title" "$body" > "$prompt"
|
||
|
|
|
||
|
|
echo "── dispatch #$issue → $harness ──"
|
||
|
|
echo " title : $title"
|
||
|
|
echo " branch : $branch"
|
||
|
|
echo " worktree : $wt"
|
||
|
|
echo " base : $base"
|
||
|
|
echo " prompt : $prompt"
|
||
|
|
|
||
|
|
if [ "$dry" = 1 ]; then
|
||
|
|
echo " [dry-run] no worktree / harness / PR / state change. Prompt preview:"
|
||
|
|
sed 's/^/ | /' "$prompt"
|
||
|
|
return 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
# preflight: warn (don't block) if not Ready
|
||
|
|
local stage; stage="$(get_field "$issue" Stage)"
|
||
|
|
[ "$stage" = "Ready" ] || echo " ⚠ Stage is '$stage' (not Ready) — dispatching anyway"
|
||
|
|
|
||
|
|
# isolated worktree
|
||
|
|
if [ -d "$wt" ]; then
|
||
|
|
echo " worktree exists — reusing"
|
||
|
|
else
|
||
|
|
git -C "$repo_root" worktree add -b "$branch" "$wt" "$base" || return 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
set_stage "$issue" "In Progress"
|
||
|
|
|
||
|
|
echo " running $harness (headless)…"
|
||
|
|
( harness_"$harness" "$wt" "$prompt" ); local hrc=$?
|
||
|
|
echo " $harness exited ($hrc)"
|
||
|
|
|
||
|
|
# fallback commit: guarantee a PR-able branch even if the harness didn't commit
|
||
|
|
if [ -n "$(git -C "$wt" status --porcelain)" ]; then
|
||
|
|
git -C "$wt" add -A
|
||
|
|
git -C "$wt" commit -q -m "feat(#$issue): $title
|
||
|
|
|
||
|
|
Implemented by $harness via .agents/dev dispatch." && echo " committed leftover changes"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# gate
|
||
|
|
local gate="passed"
|
||
|
|
run_gate "$wt" || gate="FAILED"
|
||
|
|
echo " gate: $gate"
|
||
|
|
|
||
|
|
# PR (only if there are commits ahead of base)
|
||
|
|
if [ -n "$(git -C "$wt" log "$base..$branch" --oneline 2>/dev/null)" ]; then
|
||
|
|
git -C "$wt" push -u origin "$branch" || { echo " push failed — inspect $wt" >&2; return 1; }
|
||
|
|
local draft=""; [ "$gate" = "FAILED" ] && draft="--draft"
|
||
|
|
local pr
|
||
|
|
pr="$(gh pr create -R "$REPO" --head "$branch" --base "$base" $draft \
|
||
|
|
--title "$title (#$issue)" --body "$(_pr_body "$issue" "$harness" "$gate")")" || { echo " gh pr create failed" >&2; return 1; }
|
||
|
|
echo " PR: $pr${draft:+ (draft — gate failed)}"
|
||
|
|
set_stage "$issue" "In Review"
|
||
|
|
else
|
||
|
|
echo " no commits on $branch — leaving Stage 'In Progress'. Inspect the worktree: $wt" >&2
|
||
|
|
fi
|
||
|
|
}
|