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 <noreply@anthropic.com>
This commit is contained in:
200
bin/plan-refine
Executable file
200
bin/plan-refine
Executable file
@@ -0,0 +1,200 @@
|
||||
#!/usr/bin/env bash
|
||||
# plan-refine — Run one ChatGPT evaluation iteration on a plan file via Oracle
|
||||
# Usage: plan-refine <plan-file> [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 <plan-file> [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 <model> Force Oracle model (e.g. gpt-5.2-pro)
|
||||
--model-strategy <s> Browser model strategy: current|select|ignore (default: current)
|
||||
--timeout <duration> 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 <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 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
|
||||
184
bin/plan-status
Executable file
184
bin/plan-status
Executable file
@@ -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 <status> Filter to plans with this status
|
||||
--root <path> 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 <file.md> --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 <<ENTRY
|
||||
{"path":"$file","project":"$project","title":"$title","status":"$status","iteration":$iteration,"target_iterations":${target:-null},"beads_revision":$beads_rev,"feedback_files":$feedback_count,"updated":"$updated"}
|
||||
ENTRY
|
||||
done
|
||||
echo ']}'
|
||||
else
|
||||
# Human-readable table
|
||||
echo ""
|
||||
echo " Plan Status Dashboard"
|
||||
echo " $(date '+%Y-%m-%d %H:%M')"
|
||||
echo " ════════════════════════════════════════════════════════════════════════"
|
||||
printf " %-18s %-28s %-12s %-14s %s\n" "Project" "Plan" "Status" "Iterations" "Updated"
|
||||
echo " ────────────────────────────────────────────────────────────────────────"
|
||||
|
||||
# Counters
|
||||
total=0
|
||||
by_status_drafting=0
|
||||
by_status_iterating=0
|
||||
by_status_splitting=0
|
||||
by_status_refining=0
|
||||
by_status_ready=0
|
||||
by_status_implementing=0
|
||||
by_status_completed=0
|
||||
|
||||
for file in "${PLAN_FILES[@]}"; do
|
||||
status=$(get_frontmatter "$file" "status" "unknown")
|
||||
[[ -n "$FILTER_STATUS" && "$status" != "$FILTER_STATUS" ]] && continue
|
||||
|
||||
title=$(get_frontmatter "$file" "title" "")
|
||||
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)
|
||||
plan_name=$(basename "$file" .md)
|
||||
|
||||
# Truncate long names
|
||||
[[ ${#project} -gt 18 ]] && project="${project:0:15}..."
|
||||
[[ ${#plan_name} -gt 28 ]] && plan_name="${plan_name:0:25}..."
|
||||
|
||||
# Format iteration progress
|
||||
if [[ -n "$target" && "$target" != "0" ]]; then
|
||||
iter_display="${iteration}/${target}"
|
||||
else
|
||||
iter_display="${iteration}"
|
||||
fi
|
||||
|
||||
# Add beads revision if applicable
|
||||
if [[ "$beads_rev" != "0" ]]; then
|
||||
iter_display="${iter_display} br:${beads_rev}"
|
||||
fi
|
||||
|
||||
printf " %-18s %-28s %-12s %-14s %s\n" \
|
||||
"$project" "$plan_name" "$status" "$iter_display" "$updated"
|
||||
|
||||
total=$((total + 1))
|
||||
case "$status" in
|
||||
drafting) by_status_drafting=$((by_status_drafting + 1)) ;;
|
||||
iterating) by_status_iterating=$((by_status_iterating + 1)) ;;
|
||||
splitting) by_status_splitting=$((by_status_splitting + 1)) ;;
|
||||
refining) by_status_refining=$((by_status_refining + 1)) ;;
|
||||
ready) by_status_ready=$((by_status_ready + 1)) ;;
|
||||
implementing) by_status_implementing=$((by_status_implementing + 1)) ;;
|
||||
completed) by_status_completed=$((by_status_completed + 1)) ;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo " ════════════════════════════════════════════════════════════════════════"
|
||||
echo ""
|
||||
|
||||
# Summary line
|
||||
summary_parts=()
|
||||
[[ $by_status_drafting -gt 0 ]] && summary_parts+=("${by_status_drafting} drafting")
|
||||
[[ $by_status_iterating -gt 0 ]] && summary_parts+=("${by_status_iterating} iterating")
|
||||
[[ $by_status_splitting -gt 0 ]] && summary_parts+=("${by_status_splitting} splitting")
|
||||
[[ $by_status_refining -gt 0 ]] && summary_parts+=("${by_status_refining} refining")
|
||||
[[ $by_status_ready -gt 0 ]] && summary_parts+=("${by_status_ready} ready")
|
||||
[[ $by_status_implementing -gt 0 ]] && summary_parts+=("${by_status_implementing} implementing")
|
||||
[[ $by_status_completed -gt 0 ]] && summary_parts+=("${by_status_completed} completed")
|
||||
|
||||
IFS=", "
|
||||
echo " ${total} plans: ${summary_parts[*]}"
|
||||
echo ""
|
||||
|
||||
# Pipeline visualization
|
||||
echo " Pipeline: drafting -> iterating -> splitting -> refining -> ready -> implementing -> completed"
|
||||
echo ""
|
||||
fi
|
||||
Reference in New Issue
Block a user