468 lines
21 KiB
Bash
Executable File
468 lines
21 KiB
Bash
Executable File
#!/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 "s|^${key}:.*|${key}: ${value}|" "$file" > "${file}.tmp" && mv "${file}.tmp" "$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,mcp__morph-fast-tools__edit_file --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, Edit, and the Morph edit_file MCP tool. Prefer the Morph edit_file tool (mcp__morph-fast-tools__edit_file) for edits — it's faster and handles large changes better than legacy Edit. 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,mcp__morph-fast-tools__edit_file,mcp__morph-fast-tools__warpgrep_codebase_search"
|
|
--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
|