Consolidate into single self-contained script
Collapse the entire multi-file structure (bin/, lib/, prompts/) into one executable bash script at the repo root. Everything is inlined: frontmatter helpers, prompt strings, and the Claude stream-json progress display (now a function calling node --input-type=module). - plan-status is now `plan-refine status` subcommand - Remove dead Puppeteer/Brave CDP code (chatgpt-send.mjs, plan-refine-brave) — superseded by Codex CLI approach - Remove npm artifacts (package.json, package-lock.json) — sole dependency was puppeteer-core for the dead browser automation - Simplify install.sh to just symlink + prereq checks - Update .gitignore: node_modules/ -> *.feedback-*.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1 @@
|
||||
node_modules/
|
||||
*.feedback-*.md
|
||||
|
||||
257
bin/plan-refine
257
bin/plan-refine
@@ -1,257 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# plan-refine — Fully automated plan iteration: ChatGPT review + Claude integration
|
||||
# Usage: plan-refine <plan-file> [options]
|
||||
#
|
||||
# Connects to your running Brave browser via CDP, sends the plan to ChatGPT,
|
||||
# captures the response, then runs Claude CLI to integrate the feedback.
|
||||
# Zero copy/paste required.
|
||||
|
||||
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"
|
||||
LIB_DIR="$SCRIPT_DIR/lib"
|
||||
|
||||
source "$SCRIPT_DIR/lib/frontmatter.sh"
|
||||
|
||||
# Defaults
|
||||
CLAUDE_MODEL=""
|
||||
DRY_RUN=false
|
||||
NO_INTEGRATE=false
|
||||
PLAN_FILE=""
|
||||
INIT_ONLY=false
|
||||
CDP_PORT="${CHATGPT_CDP_PORT:-9222}"
|
||||
CHATGPT_TIMEOUT=600
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
plan-refine — Fully automated plan iteration via Brave + Claude CLI
|
||||
|
||||
Usage: plan-refine <plan-file> [options]
|
||||
|
||||
Runs the full cycle automatically:
|
||||
1. Sends plan to ChatGPT via your running Brave browser (CDP)
|
||||
2. Captures ChatGPT's response
|
||||
3. Claude CLI integrates feedback back into the plan file
|
||||
|
||||
Requires: Brave running with --remote-debugging-port=9222
|
||||
Relaunch: open -a "Brave Browser" --args --remote-debugging-port=9222
|
||||
|
||||
Options:
|
||||
--dry-run Preview what would happen
|
||||
--no-integrate Get ChatGPT feedback only, skip Claude integration
|
||||
--claude-model <m> Claude model for integration (default: your default)
|
||||
--timeout <seconds> ChatGPT response timeout (default: 600)
|
||||
--cdp-port <port> Brave CDP port (default: 9222, or CHATGPT_CDP_PORT env)
|
||||
--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 ;;
|
||||
--claude-model) CLAUDE_MODEL="$2"; shift 2 ;;
|
||||
--timeout) CHATGPT_TIMEOUT="$2"; shift 2 ;;
|
||||
--cdp-port) CDP_PORT="$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
|
||||
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
|
||||
|
||||
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"
|
||||
else
|
||||
echo " Mode: Full cycle (ChatGPT + Claude integration)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
echo "=== DRY RUN ==="
|
||||
echo ""
|
||||
echo "Step 1: ChatGPT via Brave CDP (port $CDP_PORT)"
|
||||
echo " node $LIB_DIR/chatgpt-send.mjs <prompt-file> $FEEDBACK_FILE --timeout $CHATGPT_TIMEOUT"
|
||||
echo ""
|
||||
if [[ "$NO_INTEGRATE" != "true" ]]; then
|
||||
echo "Step 2: Claude CLI integration"
|
||||
echo " claude -p <integration-prompt> --allowedTools Read,Edit,Write --permission-mode acceptEdits"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# Check Brave CDP connectivity
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
if ! curl -s "http://127.0.0.1:${CDP_PORT}/json/version" >/dev/null 2>&1; then
|
||||
echo "Error: Cannot connect to Brave on port $CDP_PORT" >&2
|
||||
echo "" >&2
|
||||
echo "Brave needs to be running with CDP enabled. Do this once:" >&2
|
||||
echo " 1. Quit Brave (Cmd+Q)" >&2
|
||||
echo " 2. open -a \"Brave Browser\" --args --remote-debugging-port=$CDP_PORT" >&2
|
||||
echo "" >&2
|
||||
echo "Or set CHATGPT_CDP_PORT if using a different port." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# Step 1: Send to ChatGPT via Brave
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
echo "[Step 1] Sending plan to ChatGPT via Brave..."
|
||||
|
||||
# Build the full prompt file (eval prompt + plan content)
|
||||
PROMPT_TMPFILE=$(mktemp /tmp/plan-refine-prompt-XXXXXX.md)
|
||||
{
|
||||
echo "$EVAL_PROMPT"
|
||||
echo ""
|
||||
echo "---"
|
||||
echo ""
|
||||
cat "$PLAN_FILE"
|
||||
} > "$PROMPT_TMPFILE"
|
||||
|
||||
CHATGPT_CDP_PORT="$CDP_PORT" node "$LIB_DIR/chatgpt-send.mjs" \
|
||||
"$PROMPT_TMPFILE" \
|
||||
"$FEEDBACK_FILE" \
|
||||
--timeout "$CHATGPT_TIMEOUT"
|
||||
|
||||
CHATGPT_EXIT=$?
|
||||
rm -f "$PROMPT_TMPFILE"
|
||||
|
||||
if [[ $CHATGPT_EXIT -ne 0 ]]; then
|
||||
echo "Error: ChatGPT send failed (exit $CHATGPT_EXIT)" >&2
|
||||
exit $CHATGPT_EXIT
|
||||
fi
|
||||
|
||||
echo "ChatGPT feedback saved to: $FEEDBACK_FILE"
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# Step 2: Claude integration
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
if [[ "$NO_INTEGRATE" == "true" ]]; then
|
||||
echo ""
|
||||
echo "Skipping integration (--no-integrate)."
|
||||
echo "Run manually: plan-refine $(basename "$PLAN_FILE") --integrate-only"
|
||||
else
|
||||
echo ""
|
||||
echo "[Step 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_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 integration failed (exit $CLAUDE_EXIT)" >&2
|
||||
echo "Feedback still available at: $FEEDBACK_FILE" >&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."
|
||||
fi
|
||||
191
bin/plan-status
191
bin/plan-status
@@ -1,191 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# plan-status — Scan projects for plan files and show pipeline status
|
||||
# Usage: plan-status [search-root] [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)"
|
||||
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
|
||||
75
install.sh
75
install.sh
@@ -1,72 +1,9 @@
|
||||
#!/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 <plan.md> Run one ChatGPT evaluation iteration"
|
||||
echo " plan-refine <plan.md> --init Add plan frontmatter to a file"
|
||||
echo " plan-status Show all plans and their pipeline stage"
|
||||
echo ""
|
||||
mkdir -p ~/.local/bin
|
||||
ln -sf "$SCRIPT_DIR/plan-refine" ~/.local/bin/plan-refine
|
||||
echo "Linked: plan-refine -> ~/.local/bin/plan-refine"
|
||||
[[ ":$PATH:" != *":$HOME/.local/bin:"* ]] && echo "NOTE: Add ~/.local/bin to your PATH"
|
||||
command -v codex &>/dev/null || echo "NOTE: Install codex CLI (npm i -g @openai/codex)"
|
||||
command -v claude &>/dev/null || echo "NOTE: Install claude CLI"
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
// chatgpt-send.mjs — Send a prompt to ChatGPT via the user's running Brave browser
|
||||
// Connects via Chrome DevTools Protocol to an already-authenticated session.
|
||||
//
|
||||
// Usage: node chatgpt-send.mjs <prompt-file> <output-file> [--timeout <seconds>]
|
||||
//
|
||||
// Requires: Brave running with --remote-debugging-port=9222
|
||||
|
||||
import puppeteer from 'puppeteer-core';
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
|
||||
const CDP_URL = `http://127.0.0.1:${process.env.CHATGPT_CDP_PORT || '9222'}`;
|
||||
const DEFAULT_TIMEOUT_SEC = 600; // 10 minutes for long responses
|
||||
|
||||
function parseArgs() {
|
||||
const args = process.argv.slice(2);
|
||||
let promptFile = null;
|
||||
let outputFile = null;
|
||||
let timeoutSec = DEFAULT_TIMEOUT_SEC;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--timeout' && args[i + 1]) {
|
||||
timeoutSec = parseInt(args[i + 1], 10);
|
||||
i++;
|
||||
} else if (!promptFile) {
|
||||
promptFile = args[i];
|
||||
} else if (!outputFile) {
|
||||
outputFile = args[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (!promptFile || !outputFile) {
|
||||
console.error('Usage: node chatgpt-send.mjs <prompt-file> <output-file> [--timeout <seconds>]');
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
return { promptFile, outputFile, timeoutSec };
|
||||
}
|
||||
|
||||
async function waitForSelector(page, selector, timeout) {
|
||||
return page.waitForSelector(selector, { timeout });
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const { promptFile, outputFile, timeoutSec } = parseArgs();
|
||||
const prompt = readFileSync(promptFile, 'utf-8').trim();
|
||||
const timeoutMs = timeoutSec * 1000;
|
||||
|
||||
// Connect to running Brave
|
||||
let browser;
|
||||
try {
|
||||
browser = await puppeteer.connect({ browserURL: CDP_URL });
|
||||
} catch (err) {
|
||||
console.error(`Cannot connect to Brave at ${CDP_URL}`);
|
||||
console.error('Make sure Brave is running with: --remote-debugging-port=9222');
|
||||
console.error('');
|
||||
console.error('Relaunch Brave:');
|
||||
console.error(' 1. Quit Brave (Cmd+Q)');
|
||||
console.error(' 2. Run: open -a "Brave Browser" --args --remote-debugging-port=9222');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Open new tab for ChatGPT
|
||||
const page = await browser.newPage();
|
||||
|
||||
try {
|
||||
console.error('Navigating to ChatGPT...');
|
||||
await page.goto('https://chatgpt.com/', { waitUntil: 'networkidle2', timeout: 30000 });
|
||||
|
||||
// Verify we're logged in by checking for the composer
|
||||
const composerSelector = '#prompt-textarea, [id="prompt-textarea"], div[contenteditable="true"][data-placeholder]';
|
||||
try {
|
||||
await waitForSelector(page, composerSelector, 15000);
|
||||
} catch {
|
||||
// Check if login button is present
|
||||
const loginBtn = await page.$('button[data-testid="login-button"], a[href*="auth"]');
|
||||
if (loginBtn) {
|
||||
console.error('ERROR: Not logged into ChatGPT. Log in via Brave first.');
|
||||
process.exit(1);
|
||||
}
|
||||
console.error('ERROR: Could not find ChatGPT composer. The UI may have changed.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.error('Logged in. Sending prompt...');
|
||||
|
||||
// Find and focus the composer
|
||||
const composer = await page.$(composerSelector);
|
||||
await composer.click();
|
||||
|
||||
// Type the prompt — use clipboard for large prompts
|
||||
await page.evaluate(async (text) => {
|
||||
const composer = document.querySelector('#prompt-textarea, [id="prompt-textarea"], div[contenteditable="true"][data-placeholder]');
|
||||
if (composer) {
|
||||
// Use execCommand for contenteditable divs
|
||||
composer.focus();
|
||||
// Clear existing content
|
||||
document.execCommand('selectAll', false, null);
|
||||
// Insert via clipboard API for reliability with large text
|
||||
const clipItem = new ClipboardItem({
|
||||
'text/plain': new Blob([text], { type: 'text/plain' })
|
||||
});
|
||||
await navigator.clipboard.write([clipItem]);
|
||||
document.execCommand('paste');
|
||||
}
|
||||
}, prompt);
|
||||
|
||||
// Small delay to ensure content is rendered
|
||||
await new Promise(r => setTimeout(r, 500));
|
||||
|
||||
// Verify content was entered
|
||||
const composerText = await page.evaluate(() => {
|
||||
const el = document.querySelector('#prompt-textarea, [id="prompt-textarea"], div[contenteditable="true"][data-placeholder]');
|
||||
return el ? el.textContent.length : 0;
|
||||
});
|
||||
|
||||
if (composerText < 10) {
|
||||
// Fallback: type directly (slower but more reliable)
|
||||
console.error('Clipboard paste failed, typing directly...');
|
||||
await composer.click({ clickCount: 3 }); // select all
|
||||
await page.keyboard.type(prompt, { delay: 1 });
|
||||
}
|
||||
|
||||
// Find and click the send button
|
||||
const sendSelector = 'button[data-testid="send-button"], button[aria-label="Send prompt"], button[aria-label*="Send"]';
|
||||
const sendBtn = await page.$(sendSelector);
|
||||
if (sendBtn) {
|
||||
await sendBtn.click();
|
||||
} else {
|
||||
// Fallback: press Enter
|
||||
await page.keyboard.press('Enter');
|
||||
}
|
||||
|
||||
console.error('Prompt sent. Waiting for response...');
|
||||
|
||||
// Wait for the response to complete
|
||||
// Strategy: watch for the stop button to appear then disappear
|
||||
const stopSelector = 'button[data-testid="stop-button"], button[aria-label="Stop generating"], button[aria-label*="Stop"]';
|
||||
|
||||
// Wait for generation to start (stop button appears)
|
||||
try {
|
||||
await waitForSelector(page, stopSelector, 30000);
|
||||
console.error('Generating...');
|
||||
} catch {
|
||||
// Stop button might not appear for very fast responses
|
||||
console.error('Response may have completed quickly.');
|
||||
}
|
||||
|
||||
// Wait for generation to finish (stop button disappears)
|
||||
await page.waitForFunction(
|
||||
(sel) => !document.querySelector(sel),
|
||||
{ timeout: timeoutMs, polling: 1000 },
|
||||
stopSelector
|
||||
);
|
||||
|
||||
// Small delay for final rendering
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
|
||||
console.error('Response complete. Extracting...');
|
||||
|
||||
// Extract the last assistant message
|
||||
const response = await page.evaluate(() => {
|
||||
// ChatGPT renders assistant messages in article elements or divs with specific data attributes
|
||||
const messages = document.querySelectorAll(
|
||||
'[data-message-author-role="assistant"], article[data-testid*="conversation-turn"]'
|
||||
);
|
||||
if (messages.length === 0) return null;
|
||||
|
||||
const lastMessage = messages[messages.length - 1];
|
||||
|
||||
// Try to get markdown content via the copy button's data
|
||||
// Fallback to innerText
|
||||
const markdownEl = lastMessage.querySelector('.markdown, .prose');
|
||||
if (markdownEl) return markdownEl.innerText;
|
||||
return lastMessage.innerText;
|
||||
});
|
||||
|
||||
if (!response) {
|
||||
console.error('ERROR: Could not extract response from page.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
writeFileSync(outputFile, response, 'utf-8');
|
||||
console.error(`Response saved to: ${outputFile} (${response.length} chars)`);
|
||||
|
||||
} finally {
|
||||
// Close the tab we opened, don't close the browser
|
||||
await page.close();
|
||||
browser.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error(`Fatal: ${err.message}`);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,103 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# frontmatter.sh — Read/write YAML frontmatter in plan files
|
||||
|
||||
# Read a value from YAML frontmatter
|
||||
# Usage: get_frontmatter <file> <key> [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 <file> <key> <value>
|
||||
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 <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 <file> [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
|
||||
}
|
||||
927
package-lock.json
generated
927
package-lock.json
generated
@@ -1,927 +0,0 @@
|
||||
{
|
||||
"name": "plan-tools",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "plan-tools",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"puppeteer-core": "^24.37.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@puppeteer/browsers": {
|
||||
"version": "2.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.12.0.tgz",
|
||||
"integrity": "sha512-Xuq42yxcQJ54ti8ZHNzF5snFvtpgXzNToJ1bXUGQRaiO8t+B6UM8sTUJfvV+AJnqtkJU/7hdy6nbKyA12aHtRw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"debug": "^4.4.3",
|
||||
"extract-zip": "^2.0.1",
|
||||
"progress": "^2.0.3",
|
||||
"proxy-agent": "^6.5.0",
|
||||
"semver": "^7.7.3",
|
||||
"tar-fs": "^3.1.1",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"bin": {
|
||||
"browsers": "lib/cjs/main-cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@tootallnate/quickjs-emscripten": {
|
||||
"version": "0.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
|
||||
"integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.1.tgz",
|
||||
"integrity": "sha512-CPrnr8voK8vC6eEtyRzvMpgp3VyVRhgclonE7qYi6P9sXwYb59ucfrnmFBTaP0yUi8Gk4yZg/LlTJULGxvTNsg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/yauzl": {
|
||||
"version": "2.10.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
|
||||
"integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "7.1.4",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
|
||||
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/ast-types": {
|
||||
"version": "0.13.4",
|
||||
"resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
|
||||
"integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/b4a": {
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz",
|
||||
"integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==",
|
||||
"license": "Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"react-native-b4a": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-native-b4a": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/bare-events": {
|
||||
"version": "2.8.2",
|
||||
"resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz",
|
||||
"integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"bare-abort-controller": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bare-abort-controller": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/bare-fs": {
|
||||
"version": "4.5.3",
|
||||
"resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.3.tgz",
|
||||
"integrity": "sha512-9+kwVx8QYvt3hPWnmb19tPnh38c6Nihz8Lx3t0g9+4GoIf3/fTgYwM4Z6NxgI+B9elLQA7mLE9PpqcWtOMRDiQ==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"bare-events": "^2.5.4",
|
||||
"bare-path": "^3.0.0",
|
||||
"bare-stream": "^2.6.4",
|
||||
"bare-url": "^2.2.2",
|
||||
"fast-fifo": "^1.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"bare": ">=1.16.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bare-buffer": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bare-buffer": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/bare-os": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz",
|
||||
"integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"bare": ">=1.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bare-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz",
|
||||
"integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"bare-os": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/bare-stream": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz",
|
||||
"integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"streamx": "^2.21.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bare-buffer": "*",
|
||||
"bare-events": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bare-buffer": {
|
||||
"optional": true
|
||||
},
|
||||
"bare-events": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/bare-url": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz",
|
||||
"integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"bare-path": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/basic-ftp": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.1.0.tgz",
|
||||
"integrity": "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-crc32": {
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
|
||||
"integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/chromium-bidi": {
|
||||
"version": "13.1.1",
|
||||
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-13.1.1.tgz",
|
||||
"integrity": "sha512-zB9MpoPd7VJwjowQqiW3FKOvQwffFMjQ8Iejp5ZW+sJaKLRhZX1sTxzl3Zt22TDB4zP0OOqs8lRoY7eAW5geyQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"mitt": "^3.0.1",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"devtools-protocol": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/data-uri-to-buffer": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
|
||||
"integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/degenerator": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz",
|
||||
"integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ast-types": "^0.13.4",
|
||||
"escodegen": "^2.1.0",
|
||||
"esprima": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/devtools-protocol": {
|
||||
"version": "0.0.1566079",
|
||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1566079.tgz",
|
||||
"integrity": "sha512-MJfAEA1UfVhSs7fbSQOG4czavUp1ajfg6prlAN0+cmfa2zNjaIbvq8VneP7do1WAQQIvgNJWSMeP6UyI90gIlQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/end-of-stream": {
|
||||
"version": "1.4.5",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
||||
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
||||
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/escodegen": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
|
||||
"integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"esprima": "^4.0.1",
|
||||
"estraverse": "^5.2.0",
|
||||
"esutils": "^2.0.2"
|
||||
},
|
||||
"bin": {
|
||||
"escodegen": "bin/escodegen.js",
|
||||
"esgenerate": "bin/esgenerate.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"source-map": "~0.6.1"
|
||||
}
|
||||
},
|
||||
"node_modules/esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||
"license": "BSD-2-Clause",
|
||||
"bin": {
|
||||
"esparse": "bin/esparse.js",
|
||||
"esvalidate": "bin/esvalidate.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/estraverse": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
|
||||
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/esutils": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
|
||||
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/events-universal": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
|
||||
"integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"bare-events": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/extract-zip": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
|
||||
"integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1",
|
||||
"get-stream": "^5.1.0",
|
||||
"yauzl": "^2.10.0"
|
||||
},
|
||||
"bin": {
|
||||
"extract-zip": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.17.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@types/yauzl": "^2.9.1"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-fifo": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
|
||||
"integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fd-slicer": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
|
||||
"integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pend": "~1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "6.* || 8.* || >= 10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/get-stream": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
|
||||
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pump": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/get-uri": {
|
||||
"version": "6.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz",
|
||||
"integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"basic-ftp": "^5.0.2",
|
||||
"data-uri-to-buffer": "^6.0.2",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/http-proxy-agent": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
|
||||
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"agent-base": "^7.1.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/https-proxy-agent": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
|
||||
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"agent-base": "^7.1.2",
|
||||
"debug": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/ip-address": {
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
|
||||
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "7.18.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
|
||||
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/mitt": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
|
||||
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/netmask": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
|
||||
"integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/pac-proxy-agent": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz",
|
||||
"integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tootallnate/quickjs-emscripten": "^0.23.0",
|
||||
"agent-base": "^7.1.2",
|
||||
"debug": "^4.3.4",
|
||||
"get-uri": "^6.0.1",
|
||||
"http-proxy-agent": "^7.0.0",
|
||||
"https-proxy-agent": "^7.0.6",
|
||||
"pac-resolver": "^7.0.1",
|
||||
"socks-proxy-agent": "^8.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/pac-resolver": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz",
|
||||
"integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"degenerator": "^5.0.0",
|
||||
"netmask": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/pend": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
||||
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/progress": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
||||
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-agent": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz",
|
||||
"integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"agent-base": "^7.1.2",
|
||||
"debug": "^4.3.4",
|
||||
"http-proxy-agent": "^7.0.1",
|
||||
"https-proxy-agent": "^7.0.6",
|
||||
"lru-cache": "^7.14.1",
|
||||
"pac-proxy-agent": "^7.1.0",
|
||||
"proxy-from-env": "^1.1.0",
|
||||
"socks-proxy-agent": "^8.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pump": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
|
||||
"integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"end-of-stream": "^1.1.0",
|
||||
"once": "^1.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/puppeteer-core": {
|
||||
"version": "24.37.2",
|
||||
"resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.37.2.tgz",
|
||||
"integrity": "sha512-nN8qwE3TGF2vA/+xemPxbesntTuqD9vCGOiZL2uh8HES3pPzLX20MyQjB42dH2rhQ3W3TljZ4ZaKZ0yX/abQuw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@puppeteer/browsers": "2.12.0",
|
||||
"chromium-bidi": "13.1.1",
|
||||
"debug": "^4.4.3",
|
||||
"devtools-protocol": "0.0.1566079",
|
||||
"typed-query-selector": "^2.12.0",
|
||||
"webdriver-bidi-protocol": "0.4.0",
|
||||
"ws": "^8.19.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/smart-buffer": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
||||
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 6.0.0",
|
||||
"npm": ">= 3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socks": {
|
||||
"version": "2.8.7",
|
||||
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
|
||||
"integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ip-address": "^10.0.1",
|
||||
"smart-buffer": "^4.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.0.0",
|
||||
"npm": ">= 3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socks-proxy-agent": {
|
||||
"version": "8.0.5",
|
||||
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
|
||||
"integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"agent-base": "^7.1.2",
|
||||
"debug": "^4.3.4",
|
||||
"socks": "^2.8.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"license": "BSD-3-Clause",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/streamx": {
|
||||
"version": "2.23.0",
|
||||
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz",
|
||||
"integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"events-universal": "^1.0.0",
|
||||
"fast-fifo": "^1.3.2",
|
||||
"text-decoder": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/tar-fs": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz",
|
||||
"integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pump": "^3.0.0",
|
||||
"tar-stream": "^3.1.5"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"bare-fs": "^4.0.1",
|
||||
"bare-path": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tar-stream": {
|
||||
"version": "3.1.7",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
|
||||
"integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"b4a": "^1.6.4",
|
||||
"fast-fifo": "^1.2.0",
|
||||
"streamx": "^2.15.0"
|
||||
}
|
||||
},
|
||||
"node_modules/text-decoder": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz",
|
||||
"integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"b4a": "^1.6.4"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/typed-query-selector": {
|
||||
"version": "2.12.0",
|
||||
"resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz",
|
||||
"integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/webdriver-bidi-protocol": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.0.tgz",
|
||||
"integrity": "sha512-U9VIlNRrq94d1xxR9JrCEAx5Gv/2W7ERSv8oWRoNe/QYbfccS0V3h/H6qeNeCRJxXGMhhnkqvwNrvPAYeuP9VA==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.19.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
|
||||
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs-parser": {
|
||||
"version": "21.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/yauzl": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
|
||||
"integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer-crc32": "~0.2.3",
|
||||
"fd-slicer": "~1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.25.76",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
package.json
19
package.json
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"name": "plan-tools",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
"lib": "lib"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"dependencies": {
|
||||
"puppeteer-core": "^24.37.2"
|
||||
}
|
||||
}
|
||||
467
plan-refine
Executable file
467
plan-refine
Executable file
@@ -0,0 +1,467 @@
|
||||
#!/usr/bin/env bash
|
||||
# plan-refine — Automated plan iteration: Codex (ChatGPT) review + Claude integration
|
||||
# Usage: plan-refine <plan-file> [options]
|
||||
# plan-refine status [options]
|
||||
set -euo pipefail
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# Prompts (inlined)
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
EVAL_PROMPT="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."
|
||||
|
||||
INTEGRATE_PROMPT="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."
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# Frontmatter helpers
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
get_frontmatter() {
|
||||
local file="$1" key="$2" 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_frontmatter() {
|
||||
local file="$1" key="$2" value="$3"
|
||||
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
|
||||
sed -i '' "s|^${key}:.*|${key}: ${value}|" "$file"
|
||||
else
|
||||
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
|
||||
}
|
||||
|
||||
is_plan_file() {
|
||||
local file="$1"
|
||||
[[ -f "$file" ]] && head -20 "$file" | grep -q "^plan: true"
|
||||
}
|
||||
|
||||
init_frontmatter() {
|
||||
local file="$1" title="${2:-$(basename "$file" .md)}"
|
||||
local today; today=$(date +%Y-%m-%d)
|
||||
if head -1 "$file" | grep -q '^---$'; then
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# Claude stream-json progress display
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
claude_progress() {
|
||||
node --input-type=module -e '
|
||||
import { createInterface } from "readline";
|
||||
const rl = createInterface({ input: process.stdin });
|
||||
let toolCount = 0;
|
||||
rl.on("line", (line) => {
|
||||
try {
|
||||
const event = JSON.parse(line);
|
||||
if (event.type === "assistant" && event.message?.content) {
|
||||
for (const block of event.message.content) {
|
||||
if (block.type === "text" && block.text) {
|
||||
process.stderr.write(block.text);
|
||||
if (!block.text.endsWith("\n")) process.stderr.write("\n");
|
||||
}
|
||||
if (block.type === "tool_use") {
|
||||
toolCount++;
|
||||
const input = block.input || {};
|
||||
const file = input.file_path || input.path || input.pattern || "";
|
||||
const short = file ? file.split("/").pop() : "";
|
||||
process.stderr.write(" [" + block.name + (short ? ": " + short : "") + "]\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event.type === "result") {
|
||||
const cost = event.cost_usd ? " ($" + event.cost_usd.toFixed(4) + ")" : "";
|
||||
process.stderr.write("\n [Done — " + (event.num_turns || "?") + " turns, " + toolCount + " tool calls" + cost + "]\n");
|
||||
}
|
||||
} catch {}
|
||||
});'
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# Subcommand: status
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
cmd_status() {
|
||||
local SEARCH_ROOT="${HOME}/projects"
|
||||
local JSON_OUTPUT=false
|
||||
local FILTER_STATUS=""
|
||||
|
||||
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)
|
||||
cat <<'EOF'
|
||||
plan-refine status — Dashboard for plan files across projects
|
||||
|
||||
Usage: plan-refine status [options]
|
||||
|
||||
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 ;;
|
||||
-*) 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
|
||||
|
||||
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
|
||||
echo '{"plans":['
|
||||
local first=true
|
||||
for file in "${PLAN_FILES[@]}"; do
|
||||
local status; status=$(get_frontmatter "$file" "status" "unknown")
|
||||
[[ -n "$FILTER_STATUS" && "$status" != "$FILTER_STATUS" ]] && continue
|
||||
local title; title=$(get_frontmatter "$file" "title" "$(basename "$file" .md)")
|
||||
local iteration; iteration=$(get_frontmatter "$file" "iteration" "0")
|
||||
local target; target=$(get_frontmatter "$file" "target_iterations" "")
|
||||
local beads_rev; beads_rev=$(get_frontmatter "$file" "beads_revision" "0")
|
||||
local updated; updated=$(get_frontmatter "$file" "updated" "")
|
||||
local project; project=$(echo "$file" | sed "s|${SEARCH_ROOT}/||" | cut -d/ -f1)
|
||||
local feedback_count; feedback_count=$(ls "${file%.md}".feedback-*.md 2>/dev/null | wc -l | tr -d ' ')
|
||||
[[ "$first" != "true" ]] && echo ","
|
||||
first=false
|
||||
echo " {\"path\":\"$file\",\"project\":\"$project\",\"title\":\"$title\",\"status\":\"$status\",\"iteration\":$iteration,\"target_iterations\":${target:-null},\"beads_revision\":$beads_rev,\"feedback_files\":$feedback_count,\"updated\":\"$updated\"}"
|
||||
done
|
||||
echo ']}'
|
||||
else
|
||||
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 " ────────────────────────────────────────────────────────────────────────"
|
||||
|
||||
local total=0
|
||||
local by_status_drafting=0 by_status_iterating=0 by_status_splitting=0
|
||||
local by_status_refining=0 by_status_ready=0 by_status_implementing=0 by_status_completed=0
|
||||
|
||||
for file in "${PLAN_FILES[@]}"; do
|
||||
local status; status=$(get_frontmatter "$file" "status" "unknown")
|
||||
[[ -n "$FILTER_STATUS" && "$status" != "$FILTER_STATUS" ]] && continue
|
||||
local title; title=$(get_frontmatter "$file" "title" "")
|
||||
local iteration; iteration=$(get_frontmatter "$file" "iteration" "0")
|
||||
local target; target=$(get_frontmatter "$file" "target_iterations" "")
|
||||
local beads_rev; beads_rev=$(get_frontmatter "$file" "beads_revision" "0")
|
||||
local updated; updated=$(get_frontmatter "$file" "updated" "")
|
||||
local project; project=$(echo "$file" | sed "s|${SEARCH_ROOT}/||" | cut -d/ -f1)
|
||||
local plan_name; plan_name=$(basename "$file" .md)
|
||||
[[ ${#project} -gt 18 ]] && project="${project:0:15}..."
|
||||
[[ ${#plan_name} -gt 28 ]] && plan_name="${plan_name:0:25}..."
|
||||
|
||||
local iter_display
|
||||
if [[ -n "$target" && "$target" != "0" ]]; then
|
||||
iter_display="${iteration}/${target}"
|
||||
else
|
||||
iter_display="${iteration}"
|
||||
fi
|
||||
[[ "$beads_rev" != "0" ]] && iter_display="${iter_display} br:${beads_rev}"
|
||||
|
||||
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 ""
|
||||
local 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 ""
|
||||
echo " Pipeline: drafting -> iterating -> splitting -> refining -> ready -> implementing -> completed"
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# Subcommand: refine (default)
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
cmd_refine() {
|
||||
local CLAUDE_MODEL="" CODEX_MODEL="" DRY_RUN=false NO_INTEGRATE=false PLAN_FILE="" INIT_ONLY=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--dry-run) DRY_RUN=true; shift ;;
|
||||
--no-integrate) NO_INTEGRATE=true; shift ;;
|
||||
--claude-model) CLAUDE_MODEL="$2"; shift 2 ;;
|
||||
--codex-model) CODEX_MODEL="$2"; shift 2 ;;
|
||||
--init) INIT_ONLY=true; shift ;;
|
||||
-h|--help)
|
||||
cat <<'EOF'
|
||||
plan-refine — Automated plan iteration via Codex CLI + Claude CLI
|
||||
|
||||
Usage: plan-refine <plan-file> [options]
|
||||
plan-refine status [options]
|
||||
|
||||
Runs the full cycle automatically:
|
||||
1. Sends plan to ChatGPT via Codex CLI
|
||||
2. Captures ChatGPT's response
|
||||
3. Claude CLI integrates feedback back into the plan file
|
||||
|
||||
Requires: codex CLI (codex login) and claude CLI
|
||||
|
||||
Options:
|
||||
--dry-run Preview what would happen
|
||||
--no-integrate Get ChatGPT feedback only, skip Claude integration
|
||||
--claude-model <m> Claude model for integration (default: your default)
|
||||
--codex-model <m> Codex/ChatGPT model (default: codex default)
|
||||
--init Add plan frontmatter to file without running anything
|
||||
-h, --help Show this help
|
||||
|
||||
Subcommands:
|
||||
status Show dashboard of all plan files
|
||||
EOF
|
||||
exit 0 ;;
|
||||
-*) 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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
local ITERATION TARGET STATUS NEXT_ITERATION PLAN_DIR FEEDBACK_FILE
|
||||
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")
|
||||
FEEDBACK_FILE="${PLAN_FILE%.md}.feedback-${NEXT_ITERATION}.md"
|
||||
|
||||
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
|
||||
|
||||
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"
|
||||
else
|
||||
echo " Mode: Full cycle (ChatGPT + Claude integration)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
echo "=== DRY RUN ==="
|
||||
echo ""
|
||||
echo "Step 1: ChatGPT via Codex CLI"
|
||||
echo " codex exec <prompt> -o $FEEDBACK_FILE --skip-git-repo-check"
|
||||
echo ""
|
||||
if [[ "$NO_INTEGRATE" != "true" ]]; then
|
||||
echo "Step 2: Claude CLI integration"
|
||||
echo " claude -p <integration-prompt> --allowedTools Read,Edit,Write --permission-mode acceptEdits"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Step 1: ChatGPT via Codex CLI
|
||||
if [[ -f "$FEEDBACK_FILE" && -s "$FEEDBACK_FILE" ]]; then
|
||||
echo "[Step 1] Feedback file already exists, skipping ChatGPT step."
|
||||
echo " $(basename "$FEEDBACK_FILE") ($(wc -c < "$FEEDBACK_FILE" | tr -d ' ') bytes)"
|
||||
echo " Delete it to re-run ChatGPT: rm $(basename "$FEEDBACK_FILE")"
|
||||
else
|
||||
echo "[Step 1] Sending plan to ChatGPT via Codex CLI..."
|
||||
|
||||
local FULL_PROMPT="${EVAL_PROMPT}
|
||||
|
||||
---
|
||||
|
||||
$(cat "$PLAN_FILE")"
|
||||
|
||||
local CODEX_ARGS=(codex exec -o "$FEEDBACK_FILE" --skip-git-repo-check -s read-only)
|
||||
[[ -n "$CODEX_MODEL" ]] && CODEX_ARGS+=(-m "$CODEX_MODEL")
|
||||
|
||||
local CODEX_EXIT=0
|
||||
echo "$FULL_PROMPT" | "${CODEX_ARGS[@]}" - || CODEX_EXIT=$?
|
||||
|
||||
if [[ $CODEX_EXIT -ne 0 ]]; then
|
||||
echo "Error: Codex send failed (exit $CODEX_EXIT)" >&2; exit $CODEX_EXIT
|
||||
fi
|
||||
|
||||
if [[ ! -s "$FEEDBACK_FILE" ]]; then
|
||||
echo "Error: Codex produced empty output" >&2; exit 1
|
||||
fi
|
||||
|
||||
echo "ChatGPT feedback saved to: $FEEDBACK_FILE ($(wc -c < "$FEEDBACK_FILE" | tr -d ' ') bytes)"
|
||||
fi
|
||||
|
||||
# Step 2: Claude integration
|
||||
if [[ "$NO_INTEGRATE" == "true" ]]; then
|
||||
echo ""
|
||||
echo "Skipping integration (--no-integrate)."
|
||||
echo "Run manually: plan-refine $(basename "$PLAN_FILE")"
|
||||
else
|
||||
echo ""
|
||||
echo "[Step 2] Claude integrating feedback into plan..."
|
||||
|
||||
local CLAUDE_PROMPT="${INTEGRATE_PROMPT}
|
||||
|
||||
The original plan is at: ${PLAN_FILE}
|
||||
ChatGPT's feedback is at: ${FEEDBACK_FILE}
|
||||
|
||||
You have Read and Edit tools. Edit the plan file directly — do NOT output the plan content to stdout.
|
||||
- Preserve the YAML frontmatter block (between the --- delimiters) at the top unchanged.
|
||||
- Only modify the content below the frontmatter.
|
||||
- Print a brief summary to stdout as you work: which changes you're accepting, rejecting, or modifying, and why. Keep it concise — bullet points, not essays."
|
||||
|
||||
local CLAUDE_STREAM_ARGS=(claude -p "$CLAUDE_PROMPT"
|
||||
--allowedTools "Read,Edit,Write"
|
||||
--permission-mode acceptEdits
|
||||
--add-dir "$PLAN_DIR"
|
||||
--output-format stream-json
|
||||
--verbose)
|
||||
[[ -n "$CLAUDE_MODEL" ]] && CLAUDE_STREAM_ARGS+=(--model "$CLAUDE_MODEL")
|
||||
|
||||
set +o pipefail
|
||||
"${CLAUDE_STREAM_ARGS[@]}" | claude_progress
|
||||
local CLAUDE_EXIT=${PIPESTATUS[0]}
|
||||
set -o pipefail
|
||||
|
||||
if [[ $CLAUDE_EXIT -ne 0 ]]; then
|
||||
echo "Error: Claude integration failed (exit $CLAUDE_EXIT)" >&2
|
||||
echo "Feedback still available at: $FEEDBACK_FILE" >&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)"
|
||||
[[ "$STATUS" == "drafting" ]] && set_frontmatter "$PLAN_FILE" "status" "iterating"
|
||||
|
||||
echo ""
|
||||
echo "=== Iteration $NEXT_ITERATION/$TARGET complete ==="
|
||||
[[ $NEXT_ITERATION -ge $TARGET ]] && echo "" && echo "Target iterations reached. Plan may be ready for bead splitting."
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# Dispatch
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
case "${1:-}" in
|
||||
status) shift; cmd_status "$@" ;;
|
||||
-h|--help) cmd_refine --help ;;
|
||||
*) cmd_refine "$@" ;;
|
||||
esac
|
||||
@@ -1 +0,0 @@
|
||||
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.
|
||||
@@ -1 +0,0 @@
|
||||
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.
|
||||
Reference in New Issue
Block a user