Files
plan-tools/bin/plan-refine
Taylor Eernisse c0409579ac Fix symlink resolution for SCRIPT_DIR in both scripts
Scripts now resolve symlinks before computing SCRIPT_DIR, so
they work correctly when invoked via ~/.local/bin/ symlinks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 14:06:50 -05:00

280 lines
8.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# plan-refine — Run one full ChatGPT + Claude integration iteration on a plan
# Usage: plan-refine <plan-file> [options]
set -euo pipefail
# Resolve symlinks to find real script location
SOURCE="${BASH_SOURCE[0]}"
while [[ -L "$SOURCE" ]]; do
DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
done
SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE")/.." && pwd)"
PROMPTS_DIR="$SCRIPT_DIR/prompts"
source "$SCRIPT_DIR/lib/frontmatter.sh"
# Defaults
MODEL_STRATEGY="current"
ORACLE_MODEL=""
CLAUDE_MODEL=""
DRY_RUN=false
NO_INTEGRATE=false
BROWSER_TIMEOUT="1200s"
PLAN_FILE=""
INIT_ONLY=false
usage() {
cat <<'EOF'
plan-refine — One-command plan iteration: ChatGPT review + Claude integration
Usage: plan-refine <plan-file> [options]
Runs both steps automatically:
1. Oracle sends plan to ChatGPT for adversarial review
2. Claude CLI integrates feedback back into the plan file
Options:
--dry-run Preview what would run without executing
--no-integrate Only run ChatGPT step, skip Claude integration
--model <model> Force Oracle/ChatGPT model (e.g. gpt-5.2-pro)
--model-strategy <s> Browser model strategy: current|select|ignore (default: current)
--claude-model <m> Claude model for integration (default: uses your default)
--timeout <duration> Browser timeout (default: 1200s)
--init Add plan frontmatter to file without running anything
-h, --help Show this help
EOF
exit 0
}
while [[ $# -gt 0 ]]; do
case "$1" in
--dry-run) DRY_RUN=true; shift ;;
--no-integrate) NO_INTEGRATE=true; shift ;;
--model) ORACLE_MODEL="$2"; shift 2 ;;
--model-strategy) MODEL_STRATEGY="$2"; shift 2 ;;
--claude-model) CLAUDE_MODEL="$2"; shift 2 ;;
--timeout) BROWSER_TIMEOUT="$2"; shift 2 ;;
--init) INIT_ONLY=true; shift ;;
-h|--help) usage ;;
-*) echo "Unknown option: $1" >&2; exit 2 ;;
*) PLAN_FILE="$1"; shift ;;
esac
done
if [[ -z "$PLAN_FILE" ]]; then
echo "Error: No plan file specified" >&2
echo "Usage: plan-refine <plan-file>" >&2
exit 2
fi
# Resolve to absolute path
PLAN_FILE="$(cd "$(dirname "$PLAN_FILE")" && pwd)/$(basename "$PLAN_FILE")"
if [[ ! -f "$PLAN_FILE" ]]; then
echo "Error: Plan file not found: $PLAN_FILE" >&2
exit 1
fi
# Ensure frontmatter exists
if ! head -1 "$PLAN_FILE" | grep -q '^---$'; then
echo "No frontmatter found. Initializing..."
init_frontmatter "$PLAN_FILE"
fi
if ! is_plan_file "$PLAN_FILE"; then
echo "Adding plan: true to frontmatter..."
set_frontmatter "$PLAN_FILE" "plan" "true"
fi
if [[ "$INIT_ONLY" == "true" ]]; then
init_frontmatter "$PLAN_FILE"
echo "Frontmatter initialized for: $PLAN_FILE"
exit 0
fi
# Read current state
ITERATION=$(get_frontmatter "$PLAN_FILE" "iteration" "0")
TARGET=$(get_frontmatter "$PLAN_FILE" "target_iterations" "8")
STATUS=$(get_frontmatter "$PLAN_FILE" "status" "drafting")
NEXT_ITERATION=$((ITERATION + 1))
# Status check
if [[ "$STATUS" == "ready" || "$STATUS" == "implementing" || "$STATUS" == "completed" ]]; then
echo "Warning: Plan status is '$STATUS' -- already past iteration phase."
echo "Continue anyway? (y/N)"
read -r confirm
[[ "$confirm" != "y" && "$confirm" != "Y" ]] && exit 0
fi
# Build paths
FEEDBACK_FILE="${PLAN_FILE%.md}.feedback-${NEXT_ITERATION}.md"
PLAN_DIR=$(dirname "$PLAN_FILE")
# Read prompts
EVAL_PROMPT=$(cat "$PROMPTS_DIR/chatgpt-eval.md")
INTEGRATE_PROMPT=$(cat "$PROMPTS_DIR/claude-integrate.md")
# Build Oracle model args
ORACLE_MODEL_ARGS=()
if [[ -n "$ORACLE_MODEL" ]]; then
ORACLE_MODEL_ARGS+=(--model "$ORACLE_MODEL")
fi
# Build Claude model args
CLAUDE_MODEL_ARGS=()
if [[ -n "$CLAUDE_MODEL" ]]; then
CLAUDE_MODEL_ARGS+=(--model "$CLAUDE_MODEL")
fi
echo "=== plan-refine ==="
echo " Plan: $(basename "$PLAN_FILE")"
echo " Status: $STATUS"
echo " Iteration: $ITERATION -> $NEXT_ITERATION (target: $TARGET)"
echo " Feedback: $(basename "$FEEDBACK_FILE")"
if [[ "$NO_INTEGRATE" == "true" ]]; then
echo " Mode: ChatGPT review only (--no-integrate)"
else
echo " Mode: Full cycle (ChatGPT review + Claude integration)"
fi
echo ""
if [[ "$DRY_RUN" == "true" ]]; then
echo "=== DRY RUN ==="
echo ""
echo "Step 1: Oracle -> ChatGPT"
echo " oracle --engine browser \\"
echo " --browser-manual-login \\"
echo " --browser-model-strategy $MODEL_STRATEGY \\"
echo " --browser-timeout $BROWSER_TIMEOUT \\"
[[ -n "$ORACLE_MODEL" ]] && echo " --model $ORACLE_MODEL \\"
echo " -p \"$(echo "$EVAL_PROMPT" | head -c 80)...\" \\"
echo " --file \"$PLAN_FILE\" \\"
echo " --write-output \"$FEEDBACK_FILE\""
echo ""
if [[ "$NO_INTEGRATE" != "true" ]]; then
echo "Step 2: Claude CLI -> Integration"
echo " claude -p <integration-prompt> \\"
echo " --allowedTools \"Read,Edit,Write\" \\"
echo " --permission-mode acceptEdits \\"
echo " --add-dir \"$PLAN_DIR\""
[[ -n "$CLAUDE_MODEL" ]] && echo " --model $CLAUDE_MODEL"
fi
echo ""
echo "=== ChatGPT PROMPT ==="
echo "$EVAL_PROMPT"
echo ""
echo "=== Claude INTEGRATION PROMPT ==="
echo "$INTEGRATE_PROMPT"
exit 0
fi
# ──────────────────────────────────────────────
# Step 1: ChatGPT evaluation via Oracle
# ──────────────────────────────────────────────
echo "[Step 1/2] Sending to ChatGPT via Oracle..."
echo "(Oracle will automate the browser. This may take a few minutes.)"
echo ""
oracle --engine browser \
--browser-manual-login \
--browser-model-strategy "$MODEL_STRATEGY" \
--browser-timeout "$BROWSER_TIMEOUT" \
"${ORACLE_MODEL_ARGS[@]}" \
-p "$EVAL_PROMPT" \
--file "$PLAN_FILE" \
--write-output "$FEEDBACK_FILE"
ORACLE_EXIT=$?
if [[ $ORACLE_EXIT -ne 0 ]]; then
echo "Error: Oracle exited with code $ORACLE_EXIT" >&2
exit $ORACLE_EXIT
fi
if [[ ! -f "$FEEDBACK_FILE" ]]; then
echo "Error: Expected feedback file not found: $FEEDBACK_FILE" >&2
exit 1
fi
echo ""
echo "ChatGPT feedback saved to: $FEEDBACK_FILE"
# ──────────────────────────────────────────────
# Step 2: Claude integration
# ──────────────────────────────────────────────
if [[ "$NO_INTEGRATE" == "true" ]]; then
# Clipboard fallback for manual integration
if command -v pbcopy &>/dev/null; then
CLIPBOARD_CONTENT="Read the plan at: $PLAN_FILE
Read ChatGPT's feedback at: $FEEDBACK_FILE
${INTEGRATE_PROMPT}
Write the updated plan back to: $PLAN_FILE"
echo "$CLIPBOARD_CONTENT" | pbcopy
echo ""
echo "Integration prompt copied to clipboard. Paste into Claude Code."
else
echo ""
echo "Skipped integration. Run manually:"
echo " Plan: $PLAN_FILE"
echo " Feedback: $FEEDBACK_FILE"
fi
else
echo ""
echo "[Step 2/2] Claude integrating feedback into plan..."
CLAUDE_PROMPT="Read the original plan at: ${PLAN_FILE}
Read ChatGPT's feedback at: ${FEEDBACK_FILE}
${INTEGRATE_PROMPT}
Important instructions:
- Write the updated plan back to: ${PLAN_FILE}
- Preserve the YAML frontmatter block (between the --- delimiters) at the top unchanged.
- Only modify the content below the frontmatter.
- Do NOT output the plan to stdout. Write it directly to the file."
claude -p "$CLAUDE_PROMPT" \
--allowedTools "Read,Edit,Write" \
--permission-mode acceptEdits \
--add-dir "$PLAN_DIR" \
"${CLAUDE_MODEL_ARGS[@]}"
CLAUDE_EXIT=$?
if [[ $CLAUDE_EXIT -ne 0 ]]; then
echo "Error: Claude CLI exited with code $CLAUDE_EXIT" >&2
echo "Feedback is still available at: $FEEDBACK_FILE"
echo "You can integrate manually in Claude Code."
exit $CLAUDE_EXIT
fi
echo "Integration complete. Plan updated."
fi
# ──────────────────────────────────────────────
# Update frontmatter
# ──────────────────────────────────────────────
set_frontmatter "$PLAN_FILE" "iteration" "$NEXT_ITERATION"
set_frontmatter "$PLAN_FILE" "updated" "$(date +%Y-%m-%d)"
if [[ "$STATUS" == "drafting" ]]; then
set_frontmatter "$PLAN_FILE" "status" "iterating"
fi
echo ""
echo "=== Iteration $NEXT_ITERATION/$TARGET complete ==="
if [[ $NEXT_ITERATION -ge $TARGET ]]; then
echo ""
echo "Target iterations reached. Plan may be ready for bead splitting."
echo "To advance status: set 'status: splitting' in the frontmatter."
fi