#!/usr/bin/env bash # plan-refine — One-command plan iteration: ChatGPT review + Claude integration # Usage: plan-refine [options] # # Step 1: Assembles evaluation prompt + plan content, copies to clipboard. # User pastes into ChatGPT, copies response, saves to feedback file. # Step 2: Claude CLI integrates feedback back into the plan file. 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 CLAUDE_MODEL="" DRY_RUN=false NO_INTEGRATE=false PLAN_FILE="" INIT_ONLY=false STEP="" usage() { cat <<'EOF' plan-refine — Semi-automated plan iteration: ChatGPT review + Claude integration Usage: plan-refine [options] Workflow per iteration: 1. plan-refine Copies ChatGPT prompt to clipboard 2. Paste into ChatGPT, copy response, save to .feedback-N.md 3. plan-refine --integrate Claude integrates feedback into plan Options: --integrate Run Claude integration (Step 2) --no-integrate Skip Claude integration, just update frontmatter --claude-model Claude model for integration (default: your default) --dry-run Preview what would happen --init Add plan frontmatter to file without running anything -h, --help Show this help EOF exit 0 } while [[ $# -gt 0 ]]; do case "$1" in --integrate) STEP="integrate"; shift ;; --no-integrate) NO_INTEGRATE=true; shift ;; --dry-run) DRY_RUN=true; shift ;; --claude-model) CLAUDE_MODEL="$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 " >&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)) PLAN_DIR=$(dirname "$PLAN_FILE") # Build paths FEEDBACK_FILE="${PLAN_FILE%.md}.feedback-${NEXT_ITERATION}.md" # Read prompts EVAL_PROMPT=$(cat "$PROMPTS_DIR/chatgpt-eval.md") INTEGRATE_PROMPT=$(cat "$PROMPTS_DIR/claude-integrate.md") # 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 # ────────────────────────────────────────────── # Route to the right step # ────────────────────────────────────────────── if [[ "$STEP" == "integrate" ]]; then # ────────────────────────────────────────── # Step 2: Claude integration # ────────────────────────────────────────── # Find the latest feedback file if [[ -f "$FEEDBACK_FILE" ]]; then FOUND_FEEDBACK="$FEEDBACK_FILE" else # Search for any feedback file for the current iteration FOUND_FEEDBACK=$(ls -t "${PLAN_FILE%.md}".feedback-*.md 2>/dev/null | head -1) fi if [[ -z "$FOUND_FEEDBACK" || ! -f "$FOUND_FEEDBACK" ]]; then echo "Error: No feedback file found." >&2 echo "Expected: $FEEDBACK_FILE" >&2 echo "" >&2 echo "Run step 1 first: plan-refine $PLAN_FILE" >&2 echo "Then save ChatGPT's response to: $FEEDBACK_FILE" >&2 exit 1 fi echo "=== plan-refine --integrate ===" echo " Plan: $(basename "$PLAN_FILE")" echo " Feedback: $(basename "$FOUND_FEEDBACK")" echo " Iteration: $ITERATION -> $NEXT_ITERATION (target: $TARGET)" echo "" if [[ "$DRY_RUN" == "true" ]]; then echo "=== DRY RUN ===" echo "Would run:" echo " claude -p \\" echo " --allowedTools \"Read,Edit,Write\" \\" echo " --permission-mode acceptEdits \\" echo " --add-dir \"$PLAN_DIR\"" [[ -n "$CLAUDE_MODEL" ]] && echo " --model $CLAUDE_MODEL" exit 0 fi if [[ "$NO_INTEGRATE" == "true" ]]; then echo "Skipping Claude integration (--no-integrate)." else echo "Claude integrating feedback into plan..." CLAUDE_PROMPT="Read the original plan at: ${PLAN_FILE} Read ChatGPT's feedback at: ${FOUND_FEEDBACK} ${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_MODEL_ARGS=() if [[ -n "$CLAUDE_MODEL" ]]; then CLAUDE_MODEL_ARGS+=(--model "$CLAUDE_MODEL") fi 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: $FOUND_FEEDBACK" >&2 echo "You can integrate manually in Claude Code." >&2 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 else # ────────────────────────────────────────── # Step 1: Prepare ChatGPT prompt # ────────────────────────────────────────── echo "=== plan-refine ===" echo " Plan: $(basename "$PLAN_FILE")" echo " Status: $STATUS" echo " Iteration: $ITERATION -> $NEXT_ITERATION (target: $TARGET)" echo "" # Use Oracle's --render --copy to bundle prompt + file content if command -v oracle &>/dev/null; then if [[ "$DRY_RUN" == "true" ]]; then echo "=== DRY RUN ===" echo "Would run: oracle --render --copy -p --file $PLAN_FILE" echo "" echo "=== PROMPT ===" echo "$EVAL_PROMPT" exit 0 fi oracle --render --copy \ -p "$EVAL_PROMPT" \ --file "$PLAN_FILE" 2>/dev/null echo "ChatGPT prompt copied to clipboard (assembled by Oracle)." else # Fallback: manual clipboard assembly if [[ "$DRY_RUN" == "true" ]]; then echo "=== DRY RUN ===" echo "Would copy evaluation prompt + plan content to clipboard." exit 0 fi PLAN_CONTENT=$(cat "$PLAN_FILE") CLIPBOARD="${EVAL_PROMPT} --- ${PLAN_CONTENT}" echo "$CLIPBOARD" | pbcopy echo "ChatGPT prompt copied to clipboard." fi echo "" echo "Next steps:" echo " 1. Paste into ChatGPT (Cmd+V)" echo " 2. Wait for response" echo " 3. Copy ChatGPT's full response" echo " 4. Save it to: $FEEDBACK_FILE" echo " 5. Run: plan-refine $(basename "$PLAN_FILE") --integrate" fi