From defb1fb08e0a388ff672f16cacfa735a3dfd126f Mon Sep 17 00:00:00 2001 From: Taylor Eernisse Date: Sat, 7 Feb 2026 13:03:28 -0500 Subject: [PATCH] Initial plan-tools: plan-refine and plan-status Semi-automated plan refinement pipeline using Oracle for ChatGPT browser automation. Includes frontmatter-based tracking and a dashboard scanner for plan status across projects. Co-Authored-By: Claude Opus 4.6 --- bin/plan-refine | 200 ++++++++++++++++++++++++++++++++++++ bin/plan-status | 184 +++++++++++++++++++++++++++++++++ install.sh | 72 +++++++++++++ lib/frontmatter.sh | 103 +++++++++++++++++++ prompts/chatgpt-eval.md | 1 + prompts/claude-integrate.md | 1 + 6 files changed, 561 insertions(+) create mode 100755 bin/plan-refine create mode 100755 bin/plan-status create mode 100755 install.sh create mode 100644 lib/frontmatter.sh create mode 100644 prompts/chatgpt-eval.md create mode 100644 prompts/claude-integrate.md diff --git a/bin/plan-refine b/bin/plan-refine new file mode 100755 index 0000000..741d1e5 --- /dev/null +++ b/bin/plan-refine @@ -0,0 +1,200 @@ +#!/usr/bin/env bash +# plan-refine — Run one ChatGPT evaluation iteration on a plan file via Oracle +# Usage: plan-refine [options] + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +PROMPTS_DIR="$SCRIPT_DIR/prompts" + +source "$SCRIPT_DIR/lib/frontmatter.sh" + +# Defaults +MODEL_STRATEGY="current" +ORACLE_MODEL="" +DRY_RUN=false +SKIP_CLIPBOARD=false +BROWSER_TIMEOUT="1200s" +PLAN_FILE="" + +usage() { + cat <<'EOF' +plan-refine — Send a plan to ChatGPT for adversarial review via Oracle + +Usage: plan-refine [options] + +After Oracle captures ChatGPT's feedback, the Claude integration +prompt is copied to your clipboard. Paste it into Claude Code. + +Options: + --dry-run Preview prompt without sending + --model Force Oracle model (e.g. gpt-5.2-pro) + --model-strategy Browser model strategy: current|select|ignore (default: current) + --timeout Browser timeout (default: 1200s) + --no-clipboard Skip copying integration prompt to clipboard + --init Add plan frontmatter to file without running Oracle + -h, --help Show this help +EOF + exit 0 +} + +INIT_ONLY=false + +while [[ $# -gt 0 ]]; do + case "$1" in + --dry-run) DRY_RUN=true; shift ;; + --model) ORACLE_MODEL="$2"; shift 2 ;; + --model-strategy) MODEL_STRATEGY="$2"; shift 2 ;; + --timeout) BROWSER_TIMEOUT="$2"; shift 2 ;; + --no-clipboard) SKIP_CLIPBOARD=true; shift ;; + --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)) + +# 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 feedback output path +FEEDBACK_FILE="${PLAN_FILE%.md}.feedback-${NEXT_ITERATION}.md" + +# Read evaluation prompt +EVAL_PROMPT=$(cat "$PROMPTS_DIR/chatgpt-eval.md") + +# Build Oracle model args +ORACLE_MODEL_ARGS=() +if [[ -n "$ORACLE_MODEL" ]]; then + ORACLE_MODEL_ARGS+=(--model "$ORACLE_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")" +echo "" + +if [[ "$DRY_RUN" == "true" ]]; then + echo "=== DRY RUN — would send this to Oracle ===" + echo "" + 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 -1)...\" \\" + echo " --file \"$PLAN_FILE\" \\" + echo " --write-output \"$FEEDBACK_FILE\"" + echo "" + echo "=== FULL PROMPT ===" + echo "$EVAL_PROMPT" + exit 0 +fi + +echo "Sending to ChatGPT via Oracle..." +echo "(This may take a few minutes. Oracle will automate the browser.)" +echo "" + +# Call Oracle +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 + +# 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 "Feedback saved to: $FEEDBACK_FILE" +echo "" + +# Build and copy Claude integration prompt +if [[ "$SKIP_CLIPBOARD" != "true" ]] && command -v pbcopy &>/dev/null; then + INTEGRATE_PROMPT=$(cat "$PROMPTS_DIR/claude-integrate.md") + + 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 "Claude integration prompt copied to clipboard." + echo "Paste it into Claude Code to integrate the feedback." +else + echo "Next: Tell Claude to integrate the feedback." + echo " Plan: $PLAN_FILE" + echo " Feedback: $FEEDBACK_FILE" +fi + +# Progress indicator +if [[ $NEXT_ITERATION -ge $TARGET ]]; then + echo "" + echo "Target iterations reached ($NEXT_ITERATION/$TARGET)." + echo "Consider: plan-refine $PLAN_FILE --init (to update status to 'splitting')" +fi diff --git a/bin/plan-status b/bin/plan-status new file mode 100755 index 0000000..16e3831 --- /dev/null +++ b/bin/plan-status @@ -0,0 +1,184 @@ +#!/usr/bin/env bash +# plan-status — Scan projects for plan files and show pipeline status +# Usage: plan-status [search-root] [options] + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +source "$SCRIPT_DIR/lib/frontmatter.sh" + +SEARCH_ROOT="${HOME}/projects" +JSON_OUTPUT=false +FILTER_STATUS="" + +usage() { + cat <<'EOF' +plan-status — Dashboard for plan files across projects + +Usage: plan-status [search-root] [options] + +Scans for markdown files with plan: true frontmatter and shows +their position in the refinement pipeline. + +Pipeline: drafting -> iterating -> splitting -> refining -> ready -> implementing -> completed + +Options: + --json Machine-readable JSON output + --status Filter to plans with this status + --root Search root (default: ~/projects) + -h, --help Show this help +EOF + exit 0 +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --json) JSON_OUTPUT=true; shift ;; + --status) FILTER_STATUS="$2"; shift 2 ;; + --root) SEARCH_ROOT="$2"; shift 2 ;; + -h|--help) usage ;; + -*) echo "Unknown option: $1" >&2; exit 2 ;; + *) SEARCH_ROOT="$1"; shift ;; + esac +done + +if [[ ! -d "$SEARCH_ROOT" ]]; then + echo "Error: Search root not found: $SEARCH_ROOT" >&2 + exit 1 +fi + +# Collect plan files +declare -a PLAN_FILES=() +while IFS= read -r -d '' file; do + if is_plan_file "$file"; then + PLAN_FILES+=("$file") + fi +done < <(find "$SEARCH_ROOT" \ + -name "*.md" \ + -not -path "*/node_modules/*" \ + -not -path "*/.git/*" \ + -not -path "*/.jj/*" \ + -not -path "*/target/*" \ + -not -name "*.feedback-*" \ + -print0 2>/dev/null) + +if [[ ${#PLAN_FILES[@]} -eq 0 ]]; then + if [[ "$JSON_OUTPUT" == "true" ]]; then + echo '{"plans":[],"summary":{"total":0}}' + else + echo "No plan files found under $SEARCH_ROOT" + echo "Initialize a plan: plan-refine --init" + fi + exit 0 +fi + +if [[ "$JSON_OUTPUT" == "true" ]]; then + # JSON output + echo '{"plans":[' + first=true + for file in "${PLAN_FILES[@]}"; do + status=$(get_frontmatter "$file" "status" "unknown") + [[ -n "$FILTER_STATUS" && "$status" != "$FILTER_STATUS" ]] && continue + + title=$(get_frontmatter "$file" "title" "$(basename "$file" .md)") + iteration=$(get_frontmatter "$file" "iteration" "0") + target=$(get_frontmatter "$file" "target_iterations" "") + beads_rev=$(get_frontmatter "$file" "beads_revision" "0") + updated=$(get_frontmatter "$file" "updated" "") + project=$(echo "$file" | sed "s|${SEARCH_ROOT}/||" | cut -d/ -f1) + + # Count feedback files + feedback_count=$(ls "${file%.md}".feedback-*.md 2>/dev/null | wc -l | tr -d ' ') + + [[ "$first" != "true" ]] && echo "," + first=false + + cat < iterating -> splitting -> refining -> ready -> implementing -> completed" + echo "" +fi diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..8cff2d8 --- /dev/null +++ b/install.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# install.sh — Set up plan-tools on this machine +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +BIN_DIR="$SCRIPT_DIR/bin" + +echo "plan-tools setup" +echo "════════════════" +echo "" + +# Make scripts executable +chmod +x "$BIN_DIR/plan-refine" +chmod +x "$BIN_DIR/plan-status" +echo "Made scripts executable." + +# Check for Oracle +if command -v oracle &>/dev/null; then + echo "Oracle: $(oracle --version 2>/dev/null || echo 'installed')" +else + echo "Oracle not found. Installing via npm..." + npm install -g @steipete/oracle +fi + +# Set up Oracle browser profile (first-time login) +ORACLE_PROFILE="$HOME/.oracle/browser-profile" +if [[ ! -d "$ORACLE_PROFILE" ]]; then + echo "" + echo "Oracle needs a one-time browser login to ChatGPT." + echo "This will open Chrome — log into ChatGPT, then close the browser." + read -rp "Run browser login now? (y/N) " confirm + if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then + oracle --engine browser \ + --browser-manual-login \ + --browser-keep-browser \ + -p "Hello, this is a test. Reply with: Oracle setup complete." \ + --write-output /dev/null || true + echo "Browser profile saved." + else + echo "Skipped. Run this later:" + echo " oracle --engine browser --browser-manual-login --browser-keep-browser -p 'test'" + fi +fi + +# Symlink to PATH +echo "" +TARGET_BIN="$HOME/.local/bin" +mkdir -p "$TARGET_BIN" + +for script in plan-refine plan-status; do + target="$TARGET_BIN/$script" + if [[ -L "$target" ]]; then + rm "$target" + fi + ln -s "$BIN_DIR/$script" "$target" + echo "Linked: $script -> $target" +done + +# Check PATH +if ! echo "$PATH" | tr ':' '\n' | grep -q "^${TARGET_BIN}$"; then + echo "" + echo "NOTE: $TARGET_BIN is not in your PATH." + echo "Add this to your shell profile:" + echo " export PATH=\"\$HOME/.local/bin:\$PATH\"" +fi + +echo "" +echo "Setup complete. Available commands:" +echo " plan-refine Run one ChatGPT evaluation iteration" +echo " plan-refine --init Add plan frontmatter to a file" +echo " plan-status Show all plans and their pipeline stage" +echo "" diff --git a/lib/frontmatter.sh b/lib/frontmatter.sh new file mode 100644 index 0000000..c9a4bed --- /dev/null +++ b/lib/frontmatter.sh @@ -0,0 +1,103 @@ +#!/usr/bin/env bash +# frontmatter.sh — Read/write YAML frontmatter in plan files + +# Read a value from YAML frontmatter +# Usage: get_frontmatter [default] +get_frontmatter() { + local file="$1" + local key="$2" + local default="${3:-}" + + if ! head -1 "$file" | grep -q '^---$'; then + echo "$default" + return + fi + + local value + value=$(awk '/^---$/{n++; next} n==1{print}' "$file" \ + | grep "^${key}:" \ + | head -1 \ + | sed "s/^${key}: *//; s/^ *//; s/ *$//; s/^[\"']//; s/[\"']$//") + echo "${value:-$default}" +} + +# Set a value in YAML frontmatter (adds frontmatter if missing) +# Usage: set_frontmatter +set_frontmatter() { + local file="$1" + local key="$2" + local value="$3" + + # If file has no frontmatter, add it + if ! head -1 "$file" | grep -q '^---$'; then + local tmp + tmp=$(mktemp) + { + echo "---" + echo "${key}: ${value}" + echo "---" + cat "$file" + } > "$tmp" + mv "$tmp" "$file" + return + fi + + if grep -q "^${key}:" "$file"; then + # Replace existing key (using | as delimiter to avoid path issues) + sed -i '' "s|^${key}:.*|${key}: ${value}|" "$file" + else + # Insert before closing --- (second occurrence) + awk -v key="$key" -v val="$value" ' + BEGIN { count=0; inserted=0 } + /^---$/ { count++ } + count == 2 && !inserted { print key ": " val; inserted=1 } + { print } + ' "$file" > "${file}.tmp" && mv "${file}.tmp" "$file" + fi +} + +# Check if a file has plan frontmatter +# Usage: is_plan_file +is_plan_file() { + local file="$1" + [[ -f "$file" ]] && head -20 "$file" | grep -q "^plan: true" +} + +# Initialize frontmatter on a plan file that doesn't have it +# Usage: init_frontmatter [title] +init_frontmatter() { + local file="$1" + local title="${2:-$(basename "$file" .md)}" + local today + today=$(date +%Y-%m-%d) + + if head -1 "$file" | grep -q '^---$'; then + # Has frontmatter, just ensure plan fields exist + set_frontmatter "$file" "plan" "true" + [[ -z "$(get_frontmatter "$file" "status")" ]] && set_frontmatter "$file" "status" "drafting" + [[ -z "$(get_frontmatter "$file" "iteration")" ]] && set_frontmatter "$file" "iteration" "0" + [[ -z "$(get_frontmatter "$file" "target_iterations")" ]] && set_frontmatter "$file" "target_iterations" "8" + [[ -z "$(get_frontmatter "$file" "created")" ]] && set_frontmatter "$file" "created" "$today" + set_frontmatter "$file" "updated" "$today" + else + # No frontmatter, add full block + local tmp + tmp=$(mktemp) + { + echo "---" + echo "plan: true" + echo "title: \"${title}\"" + echo "status: drafting" + echo "iteration: 0" + echo "target_iterations: 8" + echo "beads_revision: 0" + echo "related_plans: []" + echo "created: ${today}" + echo "updated: ${today}" + echo "---" + echo "" + cat "$file" + } > "$tmp" + mv "$tmp" "$file" + fi +} diff --git a/prompts/chatgpt-eval.md b/prompts/chatgpt-eval.md new file mode 100644 index 0000000..0c7018a --- /dev/null +++ b/prompts/chatgpt-eval.md @@ -0,0 +1 @@ +Carefully review this entire plan for me and come up with your best revisions in terms of better architecture, new features, changed features, etc. to make it better, more robust/reliable, more performant, more compelling/useful, etc. For each proposed change, give me your detailed analysis and rationale/justification for why it would make the project better along with the git-diff style change versus the original plan shown below. diff --git a/prompts/claude-integrate.md b/prompts/claude-integrate.md new file mode 100644 index 0000000..cfdab0c --- /dev/null +++ b/prompts/claude-integrate.md @@ -0,0 +1 @@ +I asked ChatGPT to review your plan. I want you to REALLY carefully analyze their plan with an open mind and be intellectually honest about what they did that's better than your plan. Then I want you to come up with the best possible revisions to your plan (you should simply update your existing document for your original plan with the revisions) that artfully and skillfully blends the "best of all worlds" to create a true, ultimate, superior hybrid version of the plan that best achieves our stated goals and will work the best in real-world practice to solve the problems we are facing and our overarching goals while ensuring the extreme success of the enterprise as best as possible; you should provide me with a complete series of git-diff style changes to your original plan to turn it into the new, enhanced, much longer and detailed plan that integrates the best of all the plans with every good idea included.