From 7731a3833b264f471e25a9e445ff8c73dffb51f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CTiago?= Date: Mon, 12 Jan 2026 14:20:56 +0000 Subject: [PATCH 1/7] feat(ralph): add user-level storage option Add support for storing Ralph scripts in user home directory (~/.config/flow-next/ralph/) instead of project-local scripts/ralph/. Benefits: - Scripts shared across multiple projects - User config provides defaults, project config overrides - Auto-updates via /flow-next:sync - Project keeps only config.env (overrides) and runs/ Config loading: 1. User config (~/.config/flow-next/ralph/config.env) - defaults 2. Project config (scripts/ralph/config.env) - overrides Changes: - ralph.sh: detect user-level dir, load user+project config - ralph-init SKILL: support --user flag for user-level install - Add flow-next-sync skill for updating from plugin - Add VERSION file for tracking plugin version Co-Authored-By: Claude Opus 4.5 --- plugins/flow-next/VERSION | 1 + .../skills/flow-next-ralph-init/SKILL.md | 83 +++++++++++--- .../flow-next-ralph-init/templates/ralph.sh | 96 +++++++++++++++- .../templates/ralph_once.sh | 5 + .../flow-next/skills/flow-next-sync/SKILL.md | 108 ++++++++++++++++++ 5 files changed, 269 insertions(+), 24 deletions(-) create mode 100644 plugins/flow-next/VERSION create mode 100644 plugins/flow-next/skills/flow-next-sync/SKILL.md diff --git a/plugins/flow-next/VERSION b/plugins/flow-next/VERSION new file mode 100644 index 00000000..1d0ba9ea --- /dev/null +++ b/plugins/flow-next/VERSION @@ -0,0 +1 @@ +0.4.0 diff --git a/plugins/flow-next/skills/flow-next-ralph-init/SKILL.md b/plugins/flow-next/skills/flow-next-ralph-init/SKILL.md index 847ece5d..a90354cc 100644 --- a/plugins/flow-next/skills/flow-next-ralph-init/SKILL.md +++ b/plugins/flow-next/skills/flow-next-ralph-init/SKILL.md @@ -1,24 +1,48 @@ --- name: flow-next-ralph-init -description: Scaffold repo-local Ralph autonomous harness under scripts/ralph/. Use when user runs /flow-next:ralph-init. +description: Scaffold Ralph autonomous harness. Supports project-local (scripts/ralph/) or user-level (~/.config/flow-next/ralph/) modes. Use when user runs /flow-next:ralph-init. --- # Ralph init -Scaffold repo-local Ralph harness. Opt-in only. +Scaffold Ralph autonomous harness. Opt-in only. + +## Installation Modes + +### Project-local (default) +- Everything in `scripts/ralph/` in the current repo +- Scripts, config, and runs all in one place +- Good for: single project, team sharing via git + +### User-level (`--user` flag) +- Scripts in `~/.config/flow-next/ralph/` (shared across projects) +- Project gets `scripts/ralph/config.env` and symlinks +- Runs stay in project `scripts/ralph/runs/` +- Good for: multiple projects, personal workflow, auto-updates via `/flow-next:sync` ## Rules -- Only create `scripts/ralph/` in the current repo. -- If `scripts/ralph/` already exists, stop and ask the user to remove it first. -- Copy templates from `templates/` into `scripts/ralph/`. -- Copy `flowctl` and `flowctl.py` from `${CLAUDE_PLUGIN_ROOT}/scripts/` into `scripts/ralph/`. -- Set executable bit on `scripts/ralph/ralph.sh`, `scripts/ralph/ralph_once.sh`, and `scripts/ralph/flowctl`. +### Project-local mode +- Create `scripts/ralph/` in the current repo +- If exists, stop and ask user to remove it first +- Copy all templates from `templates/` into `scripts/ralph/` +- Copy `flowctl` and `flowctl.py` from `${CLAUDE_PLUGIN_ROOT}/scripts/` +- Set executable bits + +### User-level mode (`--user`) +- Create `~/.config/flow-next/ralph/` if not exists +- If exists, ask user if they want to update (runs /flow-next:sync) or skip +- Copy scripts to user dir: `ralph.sh`, `ralph_once.sh`, `flowctl`, `flowctl.py`, `watch-filter.py`, `prompt_*.md` +- Write VERSION file to track plugin version +- In project, create `scripts/ralph/` with: + - `config.env` (project-specific config) + - Symlinks: `ralph.sh -> ~/.config/flow-next/ralph/ralph.sh` etc. + - `runs/` directory for run logs ## Workflow -1. Resolve repo root: `git rev-parse --show-toplevel` -2. Check `scripts/ralph/` does not exist. +1. Parse arguments: check for `--user` flag +2. Resolve repo root: `git rev-parse --show-toplevel` 3. Detect available review backends: ```bash HAVE_RP=$(which rp-cli >/dev/null 2>&1 && echo 1 || echo 0) @@ -37,12 +61,35 @@ Scaffold repo-local Ralph harness. Opt-in only. - If only rp-cli available: use `rp` - If only codex available: use `codex` - If neither available: use `none` -5. Write `scripts/ralph/config.env` with: - - `PLAN_REVIEW=` and `WORK_REVIEW=` - - replace `{{PLAN_REVIEW}}` and `{{WORK_REVIEW}}` placeholders in the template -6. Copy templates and flowctl files. -7. Print next steps (run from terminal, NOT inside Claude Code): - - Edit `scripts/ralph/config.env` to customize settings - - `./scripts/ralph/ralph_once.sh` (one iteration, observe) - - `./scripts/ralph/ralph.sh` (full loop, AFK) - - Uninstall: `rm -rf scripts/ralph/` + +### If project-local mode (no --user): +5. Check `scripts/ralph/` does not exist +6. Copy templates to `scripts/ralph/` +7. Copy flowctl files +8. Replace `{{PLAN_REVIEW}}` and `{{WORK_REVIEW}}` in config.env +9. Set executable bits + +### If user-level mode (--user): +5. Check/create `~/.config/flow-next/ralph/` +6. Copy scripts to user dir (skip config.env) +7. Write `~/.config/flow-next/ralph/VERSION` with plugin version +8. In project `scripts/ralph/`: + - Copy config.env template, replace placeholders + - Create symlinks to user scripts + - Create `runs/` directory +9. Set executable bits on user scripts + +## Print next steps + +### Project-local: +- Edit `scripts/ralph/config.env` to customize settings +- `./scripts/ralph/ralph_once.sh` (one iteration, observe) +- `./scripts/ralph/ralph.sh` (full loop, AFK) +- Uninstall: `rm -rf scripts/ralph/` + +### User-level: +- Edit `scripts/ralph/config.env` for project-specific settings +- `./scripts/ralph/ralph.sh` (runs from user-level scripts) +- Update scripts: `/flow-next:sync` (backs up changes, updates from plugin) +- Uninstall project: `rm -rf scripts/ralph/` +- Uninstall user-level: `rm -rf ~/.config/flow-next/ralph/` diff --git a/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph.sh b/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph.sh index 6bba5d73..459819b2 100644 --- a/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph.sh +++ b/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph.sh @@ -1,11 +1,92 @@ #!/usr/bin/env bash set -euo pipefail -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" -CONFIG="$SCRIPT_DIR/config.env" +# ───────────────────────────────────────────────────────────────────────────── +# Path resolution: supports user-level or project-local installation +# ───────────────────────────────────────────────────────────────────────────── +# User-level mode: +# - Scripts in ~/.config/flow-next/ralph/ +# - User config in ~/.config/flow-next/ralph/config.env (defaults) +# - Project config in scripts/ralph/config.env (overrides) +# - Runs in scripts/ralph/runs/ (always project-local) +# +# Project-local mode: everything in scripts/ralph/ (original behavior) +# +# Detection order: +# 1. RALPH_USER_DIR env var (explicit user-level path) +# 2. ~/.config/flow-next/ralph/ if exists +# 3. Fall back to project-local (script's directory) +# ───────────────────────────────────────────────────────────────────────────── + +_resolve_script_dir() { + local self_dir + self_dir="$(cd "$(dirname "$0")" && pwd)" + + # Check if we're a symlink to user-level scripts + if [[ -L "$0" ]]; then + local target + target="$(readlink -f "$0")" + self_dir="$(dirname "$target")" + fi + + echo "$self_dir" +} + +_resolve_user_dir() { + # Explicit env var takes precedence + if [[ -n "${RALPH_USER_DIR:-}" ]]; then + echo "$RALPH_USER_DIR" + return + fi + # Default user-level location + local default_user_dir="${HOME}/.config/flow-next/ralph" + if [[ -d "$default_user_dir" ]]; then + echo "$default_user_dir" + return + fi + echo "" +} + +_resolve_project_dir() { + # Project scripts/ralph/ directory (for config and runs) + local root="$1" + echo "$root/scripts/ralph" +} + +# Determine actual script source (may be user-level) +SCRIPT_DIR="$(_resolve_script_dir)" +USER_DIR="$(_resolve_user_dir)" +ROOT_DIR="$(git rev-parse --show-toplevel 2>/dev/null)" || ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" +PROJECT_DIR="$(_resolve_project_dir "$ROOT_DIR")" + +# Scripts come from user-level if available, else project-local +if [[ -n "$USER_DIR" && -f "$USER_DIR/ralph.sh" ]]; then + SCRIPT_DIR="$USER_DIR" +fi + +# Config: user-level defaults + project overrides +# User config provides defaults, project config overrides +USER_CONFIG="" +PROJECT_CONFIG="" +if [[ -n "$USER_DIR" && -f "$USER_DIR/config.env" ]]; then + USER_CONFIG="$USER_DIR/config.env" +fi +if [[ -f "$PROJECT_DIR/config.env" ]]; then + PROJECT_CONFIG="$PROJECT_DIR/config.env" +fi +# For compatibility, CONFIG points to the primary config (user or project) +if [[ -n "$USER_CONFIG" ]]; then + CONFIG="$USER_CONFIG" +elif [[ -n "$PROJECT_CONFIG" ]]; then + CONFIG="$PROJECT_CONFIG" +else + CONFIG="$SCRIPT_DIR/config.env" +fi FLOWCTL="$SCRIPT_DIR/flowctl" +# Ensure runs directory exists in project +mkdir -p "$PROJECT_DIR/runs" 2>/dev/null || true + fail() { echo "ralph: $*" >&2; exit 1; } log() { # Machine-readable logs: only show when UI disabled @@ -264,12 +345,15 @@ ui_waiting() { ui " ${C_DIM}⏳ Claude working...${C_RESET}" } -[[ -f "$CONFIG" ]] || fail "missing config.env" +# Require at least one config file +[[ -n "$USER_CONFIG" || -n "$PROJECT_CONFIG" ]] || fail "missing config.env (user or project)" [[ -x "$FLOWCTL" ]] || fail "missing flowctl" +# Load config: user-level first (defaults), then project (overrides) # shellcheck disable=SC1090 set -a -source "$CONFIG" +[[ -n "$USER_CONFIG" && -f "$USER_CONFIG" ]] && source "$USER_CONFIG" +[[ -n "$PROJECT_CONFIG" && -f "$PROJECT_CONFIG" ]] && source "$PROJECT_CONFIG" set +a MAX_ITERATIONS="${MAX_ITERATIONS:-25}" @@ -421,7 +505,7 @@ PY } RUN_ID="$(date -u +%Y%m%dT%H%M%SZ)-$(hostname -s 2>/dev/null || hostname)-$(sanitize_id "$(get_actor)")-$$-$(rand4)" -RUN_DIR="$SCRIPT_DIR/runs/$RUN_ID" +RUN_DIR="$PROJECT_DIR/runs/$RUN_ID" mkdir -p "$RUN_DIR" ATTEMPTS_FILE="$RUN_DIR/attempts.json" ensure_attempts_file "$ATTEMPTS_FILE" diff --git a/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph_once.sh b/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph_once.sh index 2ce3112e..408f4628 100644 --- a/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph_once.sh +++ b/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph_once.sh @@ -3,7 +3,12 @@ # Use this to observe behavior before going fully autonomous set -euo pipefail + +# Resolve script directory (follows symlinks for user-level mode) SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +if [[ -L "$0" ]]; then + SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" +fi export MAX_ITERATIONS=1 exec "$SCRIPT_DIR/ralph.sh" "$@" diff --git a/plugins/flow-next/skills/flow-next-sync/SKILL.md b/plugins/flow-next/skills/flow-next-sync/SKILL.md new file mode 100644 index 00000000..25bc65ae --- /dev/null +++ b/plugins/flow-next/skills/flow-next-sync/SKILL.md @@ -0,0 +1,108 @@ +--- +name: flow-next-sync +description: Sync user-level Ralph scripts from plugin. Backs up local changes before updating. Use when user runs /flow-next:sync. +--- + +# Flow-Next Sync + +Update user-level Ralph scripts from plugin templates. Creates diff backups of local changes. + +## Overview + +When using user-level Ralph installation (`~/.config/flow-next/ralph/`), this command: +1. Compares current scripts with plugin templates +2. Backs up any local modifications +3. Updates scripts to latest plugin version +4. Reports what changed + +## Rules + +- Only operates on user-level installation (`~/.config/flow-next/ralph/`) +- If user-level dir doesn't exist, suggest running `/flow-next:ralph-init --user` +- Always create backup before overwriting modified files +- Backup location: `~/.config/flow-next/ralph/backups//` +- Update VERSION file after sync + +## Workflow + +1. Check `~/.config/flow-next/ralph/` exists + - If not: print "No user-level Ralph found. Run `/flow-next:ralph-init --user` first." + +2. Read current VERSION from `~/.config/flow-next/ralph/VERSION` + - If missing, assume version "0.0.0" + +3. Read plugin VERSION from `${CLAUDE_PLUGIN_ROOT}/VERSION` + +4. Compare versions: + - If same: print "Already up to date (version X.Y.Z)" + - If different: proceed with sync + +5. For each script file, check if modified: + ```bash + # Files to sync: + ralph.sh + ralph_once.sh + watch-filter.py + prompt_plan.md + prompt_work.md + flowctl + flowctl.py + ``` + +6. Create backup directory if any files modified: + ```bash + BACKUP_DIR="$HOME/.config/flow-next/ralph/backups/$(date +%Y%m%dT%H%M%S)" + mkdir -p "$BACKUP_DIR" + ``` + +7. For each modified file: + - Create diff: `diff -u > $BACKUP_DIR/.diff` + - Copy user file to backup: `cp $BACKUP_DIR/` + - Print: "Backed up: (modified)" + +8. Copy all template files from plugin: + - From: `${CLAUDE_PLUGIN_ROOT}/skills/flow-next-ralph-init/templates/` + - To: `~/.config/flow-next/ralph/` + - Skip: `config.env`, `runs/`, `.gitignore` + +9. Copy flowctl files from plugin: + - From: `${CLAUDE_PLUGIN_ROOT}/scripts/flowctl`, `flowctl.py` + - To: `~/.config/flow-next/ralph/` + +10. Update VERSION file: + ```bash + cp "${CLAUDE_PLUGIN_ROOT}/VERSION" "$HOME/.config/flow-next/ralph/VERSION" + ``` + +11. Set executable bits: + ```bash + chmod +x ~/.config/flow-next/ralph/ralph.sh + chmod +x ~/.config/flow-next/ralph/ralph_once.sh + chmod +x ~/.config/flow-next/ralph/flowctl + chmod +x ~/.config/flow-next/ralph/watch-filter.py + ``` + +12. Print summary: + ``` + Synced to version X.Y.Z + Updated: + Backups: ~/.config/flow-next/ralph/backups// + ``` + +## Detecting Modifications + +Compare file checksums (md5sum or sha256sum): +```bash +PLUGIN_HASH=$(md5sum "$PLUGIN_FILE" | cut -d' ' -f1) +USER_HASH=$(md5sum "$USER_FILE" | cut -d' ' -f1) +if [[ "$PLUGIN_HASH" != "$USER_HASH" ]]; then + # File was modified +fi +``` + +## Force Mode + +If user says "force" or passes `--force`: +- Skip version check +- Always sync all files +- Still create backups of modified files From 23e301f5ffb282270f9fe19e1cf5b7646330f7f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CTiago?= Date: Mon, 12 Jan 2026 16:25:14 +0000 Subject: [PATCH 2/7] feat(sync): add /flow-next:sync command Co-Authored-By: Claude Opus 4.5 --- plugins/flow-next/commands/flow-next/sync.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 plugins/flow-next/commands/flow-next/sync.md diff --git a/plugins/flow-next/commands/flow-next/sync.md b/plugins/flow-next/commands/flow-next/sync.md new file mode 100644 index 00000000..f286b359 --- /dev/null +++ b/plugins/flow-next/commands/flow-next/sync.md @@ -0,0 +1,10 @@ +--- +name: flow-next:sync +description: Sync user-level Ralph scripts from plugin with diff backup +--- + +# IMPORTANT: This command MUST invoke the skill `flow-next-sync` + +The ONLY purpose of this command is to call the `flow-next-sync` skill. You MUST use that skill now. + +Updates `~/.config/flow-next/ralph/` scripts from plugin templates. Creates backups of local modifications before updating. From 95ba67210f28447e9a0675a2afef15eb7fee3364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CTiago?= Date: Mon, 12 Jan 2026 16:32:06 +0000 Subject: [PATCH 3/7] refactor: remove sync, add --user to setup - Remove /flow-next:sync (redundant) - Add --user flag to /flow-next:setup for user-level install - Update ralph-init to handle updates on re-run - Both setup and ralph-init now idempotent with backup support Update flow: - /flow-next:setup --user (flowctl to ~/.config/flow-next/bin/) - /flow-next:ralph-init --user (ralph to ~/.config/flow-next/ralph/) - Re-run either after plugin updates to refresh Co-Authored-By: Claude Opus 4.5 --- plugins/flow-next/commands/flow-next/sync.md | 10 -- .../skills/flow-next-ralph-init/SKILL.md | 41 ++++--- .../flow-next/skills/flow-next-setup/SKILL.md | 23 +++- .../skills/flow-next-setup/workflow.md | 109 +++++++++++++++--- .../flow-next/skills/flow-next-sync/SKILL.md | 108 ----------------- 5 files changed, 138 insertions(+), 153 deletions(-) delete mode 100644 plugins/flow-next/commands/flow-next/sync.md delete mode 100644 plugins/flow-next/skills/flow-next-sync/SKILL.md diff --git a/plugins/flow-next/commands/flow-next/sync.md b/plugins/flow-next/commands/flow-next/sync.md deleted file mode 100644 index f286b359..00000000 --- a/plugins/flow-next/commands/flow-next/sync.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: flow-next:sync -description: Sync user-level Ralph scripts from plugin with diff backup ---- - -# IMPORTANT: This command MUST invoke the skill `flow-next-sync` - -The ONLY purpose of this command is to call the `flow-next-sync` skill. You MUST use that skill now. - -Updates `~/.config/flow-next/ralph/` scripts from plugin templates. Creates backups of local modifications before updating. diff --git a/plugins/flow-next/skills/flow-next-ralph-init/SKILL.md b/plugins/flow-next/skills/flow-next-ralph-init/SKILL.md index a90354cc..6d915106 100644 --- a/plugins/flow-next/skills/flow-next-ralph-init/SKILL.md +++ b/plugins/flow-next/skills/flow-next-ralph-init/SKILL.md @@ -5,7 +5,7 @@ description: Scaffold Ralph autonomous harness. Supports project-local (scripts/ # Ralph init -Scaffold Ralph autonomous harness. Opt-in only. +Scaffold Ralph autonomous harness. Opt-in only. Safe to re-run for updates. ## Installation Modes @@ -16,26 +16,27 @@ Scaffold Ralph autonomous harness. Opt-in only. ### User-level (`--user` flag) - Scripts in `~/.config/flow-next/ralph/` (shared across projects) -- Project gets `scripts/ralph/config.env` and symlinks +- User config in `~/.config/flow-next/ralph/config.env` (defaults) +- Project config in `scripts/ralph/config.env` (overrides) - Runs stay in project `scripts/ralph/runs/` -- Good for: multiple projects, personal workflow, auto-updates via `/flow-next:sync` +- Good for: multiple projects, personal workflow, easy updates ## Rules ### Project-local mode - Create `scripts/ralph/` in the current repo -- If exists, stop and ask user to remove it first +- If exists, ask user if they want to update or skip - Copy all templates from `templates/` into `scripts/ralph/` - Copy `flowctl` and `flowctl.py` from `${CLAUDE_PLUGIN_ROOT}/scripts/` - Set executable bits ### User-level mode (`--user`) - Create `~/.config/flow-next/ralph/` if not exists -- If exists, ask user if they want to update (runs /flow-next:sync) or skip -- Copy scripts to user dir: `ralph.sh`, `ralph_once.sh`, `flowctl`, `flowctl.py`, `watch-filter.py`, `prompt_*.md` +- If exists, backup modified files then update from plugin +- Copy scripts to user dir: `ralph.sh`, `ralph_once.sh`, `flowctl`, `flowctl.py`, `watch-filter.py`, `prompt_*.md`, `config.env` - Write VERSION file to track plugin version - In project, create `scripts/ralph/` with: - - `config.env` (project-specific config) + - `config.env` (project-specific overrides) - Symlinks: `ralph.sh -> ~/.config/flow-next/ralph/ralph.sh` etc. - `runs/` directory for run logs @@ -63,21 +64,22 @@ Scaffold Ralph autonomous harness. Opt-in only. - If neither available: use `none` ### If project-local mode (no --user): -5. Check `scripts/ralph/` does not exist +5. Check `scripts/ralph/` - if exists, ask "Update existing? (y/n)" 6. Copy templates to `scripts/ralph/` 7. Copy flowctl files -8. Replace `{{PLAN_REVIEW}}` and `{{WORK_REVIEW}}` in config.env +8. Replace `{{PLAN_REVIEW}}` and `{{WORK_REVIEW}}` in config.env (only on first install) 9. Set executable bits ### If user-level mode (--user): -5. Check/create `~/.config/flow-next/ralph/` -6. Copy scripts to user dir (skip config.env) -7. Write `~/.config/flow-next/ralph/VERSION` with plugin version -8. In project `scripts/ralph/`: - - Copy config.env template, replace placeholders - - Create symlinks to user scripts +5. Check `~/.config/flow-next/ralph/VERSION` for existing version +6. If exists: backup modified files to `~/.config/flow-next/ralph/backups//` +7. Copy scripts to user dir +8. Update VERSION file with plugin version +9. In project `scripts/ralph/`: + - Create config.env if not exists (project overrides only) + - Create/update symlinks to user scripts - Create `runs/` directory -9. Set executable bits on user scripts +10. Set executable bits on user scripts ## Print next steps @@ -85,11 +87,14 @@ Scaffold Ralph autonomous harness. Opt-in only. - Edit `scripts/ralph/config.env` to customize settings - `./scripts/ralph/ralph_once.sh` (one iteration, observe) - `./scripts/ralph/ralph.sh` (full loop, AFK) +- Update: re-run `/flow-next:ralph-init` - Uninstall: `rm -rf scripts/ralph/` ### User-level: -- Edit `scripts/ralph/config.env` for project-specific settings +- Edit `~/.config/flow-next/ralph/config.env` for user defaults +- Edit `scripts/ralph/config.env` for project-specific overrides - `./scripts/ralph/ralph.sh` (runs from user-level scripts) -- Update scripts: `/flow-next:sync` (backs up changes, updates from plugin) +- Update: re-run `/flow-next:ralph-init --user` after plugin updates +- Backups: `~/.config/flow-next/ralph/backups/` (if files were modified) - Uninstall project: `rm -rf scripts/ralph/` - Uninstall user-level: `rm -rf ~/.config/flow-next/ralph/` diff --git a/plugins/flow-next/skills/flow-next-setup/SKILL.md b/plugins/flow-next/skills/flow-next-setup/SKILL.md index bd115a9b..84e07aa0 100644 --- a/plugins/flow-next/skills/flow-next-setup/SKILL.md +++ b/plugins/flow-next/skills/flow-next-setup/SKILL.md @@ -1,24 +1,39 @@ --- name: flow-next-setup -description: Optional local install of flowctl CLI and CLAUDE.md/AGENTS.md instructions. Use when user runs /flow-next:setup. +description: Optional local install of flowctl CLI and CLAUDE.md/AGENTS.md instructions. Supports --user for user-level install. Use when user runs /flow-next:setup. --- # Flow-Next Setup (Optional) Install flowctl locally and add instructions to project docs. **Fully optional** - flow-next works without this via the plugin. +## Installation Modes + +### Project-local (default) +- Installs to `.flow/bin/` in current project +- Good for: team sharing via git, portable projects + +### User-level (`--user` flag) +- Installs to `~/.config/flow-next/` (shared across projects) +- Project gets symlinks to user-level scripts +- Good for: multiple projects, personal workflow, easy updates + ## Benefits -- `flowctl` accessible from command line (add `.flow/bin` to PATH) +- `flowctl` accessible from command line - Other AI agents (Codex, Cursor, etc.) can read instructions from CLAUDE.md/AGENTS.md - Works without Claude Code plugin installed ## Workflow -Read [workflow.md](workflow.md) and follow each step in order. +1. Parse arguments: check for `--user` flag +2. Read [workflow.md](workflow.md) and follow each step in order +3. Adapt paths based on mode: + - Project-local: `.flow/bin/` + - User-level: `~/.config/flow-next/bin/` with symlinks in `.flow/bin/` ## Notes - **Fully optional** - standard plugin usage works without local setup -- Copies scripts (not symlinks) for portability across environments - Safe to re-run - will detect existing setup and offer to update +- User-level mode creates backups before updating modified files diff --git a/plugins/flow-next/skills/flow-next-setup/workflow.md b/plugins/flow-next/skills/flow-next-setup/workflow.md index 335a2276..77ace9d5 100644 --- a/plugins/flow-next/skills/flow-next-setup/workflow.md +++ b/plugins/flow-next/skills/flow-next-setup/workflow.md @@ -2,7 +2,9 @@ Follow these steps in order. This workflow is **idempotent** - safe to re-run. -## Step 0: Resolve plugin path +## Step 0: Parse arguments and resolve paths + +Check if `--user` flag was passed. Set `USER_MODE=true` if so. The plugin root is the parent of this skill's directory. From this SKILL.md location, go up to find `scripts/` and `.claude-plugin/`. @@ -10,6 +12,10 @@ Example: if this file is at `~/.claude/plugins/cache/.../flow-next/0.3.12/skills Store this as `PLUGIN_ROOT` for use in later steps. +Set paths based on mode: +- **Project-local mode**: `BIN_DIR=".flow/bin"` +- **User-level mode**: `USER_BIN_DIR="$HOME/.config/flow-next/bin"`, `BIN_DIR=".flow/bin"` (for symlinks) + ## Step 1: Check .flow/ exists Check if `.flow/` directory exists (use Bash `ls .flow/` or check for `.flow/meta.json`). @@ -29,30 +35,82 @@ fi ## Step 2: Check existing setup -Read `.flow/meta.json` and check for `setup_version` field. +Read `${PLUGIN_ROOT}/.claude-plugin/plugin.json` to get current plugin version. -Also read `${PLUGIN_ROOT}/.claude-plugin/plugin.json` to get current plugin version. +**For user-level mode:** +- Check `~/.config/flow-next/VERSION` for existing version +- If exists and same version: "Already up to date (v). Update anyway? (y/n)" +- If exists and different: "Updating from v to v" -**If `setup_version` exists (already set up):** -- If **same version**: tell user "Already set up with v. Re-run to update docs only? (y/n)" - - If yes: skip to Step 6 (docs) - - If no: done -- If **older version**: tell user "Updating from v to v" and continue +**For project-local mode:** +- Read `.flow/meta.json` and check for `setup_version` field +- If `setup_version` exists (already set up): + - If **same version**: tell user "Already set up with v. Re-run to update docs only? (y/n)" + - If yes: skip to Step 6 (docs) + - If no: done + - If **older version**: tell user "Updating from v to v" and continue +- If no `setup_version`: continue (first-time setup) -**If no `setup_version`:** continue (first-time setup) +## Step 3: Create directories -## Step 3: Create .flow/bin/ +**User-level mode:** +```bash +mkdir -p ~/.config/flow-next/bin +mkdir -p .flow/bin +``` +**Project-local mode:** ```bash mkdir -p .flow/bin ``` -## Step 4: Copy files +## Step 4: Copy/link files **IMPORTANT: Do NOT read flowctl.py - it's too large. Just copy it.** -Copy using Bash `cp` with absolute paths: +### User-level mode + +First, backup any modified files: +```bash +if [[ -d ~/.config/flow-next/bin ]]; then + BACKUP_DIR="$HOME/.config/flow-next/backups/$(date +%Y%m%dT%H%M%S)" + # Check if files differ from plugin + for f in flowctl flowctl.py; do + if [[ -f ~/.config/flow-next/bin/$f ]]; then + PLUGIN_HASH=$(md5sum "${PLUGIN_ROOT}/scripts/$f" | cut -d' ' -f1) + USER_HASH=$(md5sum ~/.config/flow-next/bin/$f | cut -d' ' -f1) + if [[ "$PLUGIN_HASH" != "$USER_HASH" ]]; then + mkdir -p "$BACKUP_DIR" + cp ~/.config/flow-next/bin/$f "$BACKUP_DIR/" + diff -u "${PLUGIN_ROOT}/scripts/$f" ~/.config/flow-next/bin/$f > "$BACKUP_DIR/$f.diff" 2>/dev/null || true + echo "Backed up: $f (modified)" + fi + fi + done +fi +``` + +Copy to user-level: +```bash +cp "${PLUGIN_ROOT}/scripts/flowctl" ~/.config/flow-next/bin/flowctl +cp "${PLUGIN_ROOT}/scripts/flowctl.py" ~/.config/flow-next/bin/flowctl.py +chmod +x ~/.config/flow-next/bin/flowctl +``` + +Create symlinks in project: +```bash +ln -sf ~/.config/flow-next/bin/flowctl .flow/bin/flowctl +ln -sf ~/.config/flow-next/bin/flowctl.py .flow/bin/flowctl.py +``` + +Update user-level VERSION: +```bash +echo "" > ~/.config/flow-next/VERSION +``` + +### Project-local mode +Copy using Bash `cp` with absolute paths: ```bash cp "${PLUGIN_ROOT}/scripts/flowctl" .flow/bin/flowctl cp "${PLUGIN_ROOT}/scripts/flowctl.py" .flow/bin/flowctl.py @@ -68,7 +126,8 @@ Read current `.flow/meta.json`, add/update these fields (preserve all others): ```json { "setup_version": "", - "setup_date": "" + "setup_date": "", + "setup_mode": "" } ``` @@ -118,6 +177,30 @@ Wait for response, then for each chosen file: ## Step 7: Print summary +**User-level mode:** +``` +Flow-Next setup complete! (user-level) + +Installed to ~/.config/flow-next/bin/: +- flowctl (v) +- flowctl.py + +Project symlinks in .flow/bin/ + +To use from command line: + export PATH="$HOME/.config/flow-next/bin:$PATH" + flowctl --help + +Documentation updated: +- + +Notes: +- Re-run /flow-next:setup --user after plugin updates +- Scripts shared across all projects +- Backups: ~/.config/flow-next/backups/ (if files were modified) +``` + +**Project-local mode:** ``` Flow-Next setup complete! diff --git a/plugins/flow-next/skills/flow-next-sync/SKILL.md b/plugins/flow-next/skills/flow-next-sync/SKILL.md deleted file mode 100644 index 25bc65ae..00000000 --- a/plugins/flow-next/skills/flow-next-sync/SKILL.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -name: flow-next-sync -description: Sync user-level Ralph scripts from plugin. Backs up local changes before updating. Use when user runs /flow-next:sync. ---- - -# Flow-Next Sync - -Update user-level Ralph scripts from plugin templates. Creates diff backups of local changes. - -## Overview - -When using user-level Ralph installation (`~/.config/flow-next/ralph/`), this command: -1. Compares current scripts with plugin templates -2. Backs up any local modifications -3. Updates scripts to latest plugin version -4. Reports what changed - -## Rules - -- Only operates on user-level installation (`~/.config/flow-next/ralph/`) -- If user-level dir doesn't exist, suggest running `/flow-next:ralph-init --user` -- Always create backup before overwriting modified files -- Backup location: `~/.config/flow-next/ralph/backups//` -- Update VERSION file after sync - -## Workflow - -1. Check `~/.config/flow-next/ralph/` exists - - If not: print "No user-level Ralph found. Run `/flow-next:ralph-init --user` first." - -2. Read current VERSION from `~/.config/flow-next/ralph/VERSION` - - If missing, assume version "0.0.0" - -3. Read plugin VERSION from `${CLAUDE_PLUGIN_ROOT}/VERSION` - -4. Compare versions: - - If same: print "Already up to date (version X.Y.Z)" - - If different: proceed with sync - -5. For each script file, check if modified: - ```bash - # Files to sync: - ralph.sh - ralph_once.sh - watch-filter.py - prompt_plan.md - prompt_work.md - flowctl - flowctl.py - ``` - -6. Create backup directory if any files modified: - ```bash - BACKUP_DIR="$HOME/.config/flow-next/ralph/backups/$(date +%Y%m%dT%H%M%S)" - mkdir -p "$BACKUP_DIR" - ``` - -7. For each modified file: - - Create diff: `diff -u > $BACKUP_DIR/.diff` - - Copy user file to backup: `cp $BACKUP_DIR/` - - Print: "Backed up: (modified)" - -8. Copy all template files from plugin: - - From: `${CLAUDE_PLUGIN_ROOT}/skills/flow-next-ralph-init/templates/` - - To: `~/.config/flow-next/ralph/` - - Skip: `config.env`, `runs/`, `.gitignore` - -9. Copy flowctl files from plugin: - - From: `${CLAUDE_PLUGIN_ROOT}/scripts/flowctl`, `flowctl.py` - - To: `~/.config/flow-next/ralph/` - -10. Update VERSION file: - ```bash - cp "${CLAUDE_PLUGIN_ROOT}/VERSION" "$HOME/.config/flow-next/ralph/VERSION" - ``` - -11. Set executable bits: - ```bash - chmod +x ~/.config/flow-next/ralph/ralph.sh - chmod +x ~/.config/flow-next/ralph/ralph_once.sh - chmod +x ~/.config/flow-next/ralph/flowctl - chmod +x ~/.config/flow-next/ralph/watch-filter.py - ``` - -12. Print summary: - ``` - Synced to version X.Y.Z - Updated: - Backups: ~/.config/flow-next/ralph/backups// - ``` - -## Detecting Modifications - -Compare file checksums (md5sum or sha256sum): -```bash -PLUGIN_HASH=$(md5sum "$PLUGIN_FILE" | cut -d' ' -f1) -USER_HASH=$(md5sum "$USER_FILE" | cut -d' ' -f1) -if [[ "$PLUGIN_HASH" != "$USER_HASH" ]]; then - # File was modified -fi -``` - -## Force Mode - -If user says "force" or passes `--force`: -- Skip version check -- Always sync all files -- Still create backups of modified files From 29940b10f8aeaa9ced0cb2574bbad54f42d78fd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CTiago?= Date: Tue, 13 Jan 2026 05:01:02 +0000 Subject: [PATCH 4/7] feat(flowctl): add user-level config support - Add --user flag to `config set` for user-level defaults - Config hierarchy: defaults < user (~/.config/flow-next/) < project (.flow/) - config get reads merged config, works without .flow/ - Update setup workflow to document --user config - Update usage.md template with config section Co-Authored-By: Claude Opus 4.5 --- plugins/flow-next/scripts/flowctl.py | 108 ++++++++++++++---- .../skills/flow-next-setup/templates/usage.md | 17 +++ .../skills/flow-next-setup/workflow.md | 10 +- 3 files changed, 109 insertions(+), 26 deletions(-) diff --git a/plugins/flow-next/scripts/flowctl.py b/plugins/flow-next/scripts/flowctl.py index e1561aa4..05dbbde9 100755 --- a/plugins/flow-next/scripts/flowctl.py +++ b/plugins/flow-next/scripts/flowctl.py @@ -66,6 +66,11 @@ def get_flow_dir() -> Path: return get_repo_root() / FLOW_DIR +def get_user_config_dir() -> Path: + """Get user-level config directory (~/.config/flow-next/).""" + return Path.home() / ".config" / "flow-next" + + def ensure_flow_exists() -> bool: """Check if .flow/ exists.""" return get_flow_dir().exists() @@ -76,17 +81,48 @@ def get_default_config() -> dict: return {"memory": {"enabled": False}} -def load_flow_config() -> dict: - """Load .flow/config.json, returning defaults if missing.""" - config_path = get_flow_dir() / CONFIG_FILE - defaults = get_default_config() +def deep_merge(base: dict, override: dict) -> dict: + """Deep merge two dicts. Override values take precedence.""" + result = base.copy() + for key, value in override.items(): + if key in result and isinstance(result[key], dict) and isinstance(value, dict): + result[key] = deep_merge(result[key], value) + else: + result[key] = value + return result + + +def load_user_config() -> dict: + """Load user-level config from ~/.config/flow-next/config.json.""" + config_path = get_user_config_dir() / CONFIG_FILE if not config_path.exists(): - return defaults + return {} try: data = json.loads(config_path.read_text(encoding="utf-8")) - return data if isinstance(data, dict) else defaults + return data if isinstance(data, dict) else {} except (json.JSONDecodeError, Exception): - return defaults + return {} + + +def load_flow_config() -> dict: + """Load config, merging user-level with project-level (project overrides user).""" + defaults = get_default_config() + user_config = load_user_config() + + # Start with defaults, overlay user config + config = deep_merge(defaults, user_config) + + # Overlay project config if .flow/ exists + config_path = get_flow_dir() / CONFIG_FILE + if config_path.exists(): + try: + project_config = json.loads(config_path.read_text(encoding="utf-8")) + if isinstance(project_config, dict): + config = deep_merge(config, project_config) + except (json.JSONDecodeError, Exception): + pass + + return config def get_config(key: str, default=None): @@ -101,16 +137,28 @@ def get_config(key: str, default=None): return config if config != {} else default -def set_config(key: str, value) -> dict: - """Set nested config value and return updated config.""" - config_path = get_flow_dir() / CONFIG_FILE +def set_config(key: str, value, user_level: bool = False) -> dict: + """Set nested config value and return updated config. + + Args: + key: Config key (e.g., 'memory.enabled') + value: Value to set + user_level: If True, set in ~/.config/flow-next/config.json instead of .flow/config.json + """ + if user_level: + config_dir = get_user_config_dir() + config_dir.mkdir(parents=True, exist_ok=True) + config_path = config_dir / CONFIG_FILE + else: + config_path = get_flow_dir() / CONFIG_FILE + if config_path.exists(): try: config = json.loads(config_path.read_text(encoding="utf-8")) except (json.JSONDecodeError, Exception): - config = get_default_config() + config = {} else: - config = get_default_config() + config = {} # Navigate/create nested path parts = key.split(".") @@ -1135,12 +1183,8 @@ def cmd_detect(args: argparse.Namespace) -> None: def cmd_config_get(args: argparse.Namespace) -> None: - """Get a config value.""" - if not ensure_flow_exists(): - error_exit( - ".flow/ does not exist. Run 'flowctl init' first.", use_json=args.json - ) - + """Get a config value (merges user-level + project-level).""" + # Config get works even without .flow/ (reads user config) value = get_config(args.key) if args.json: json_output({"key": args.key, "value": value}) @@ -1154,19 +1198,32 @@ def cmd_config_get(args: argparse.Namespace) -> None: def cmd_config_set(args: argparse.Namespace) -> None: - """Set a config value.""" - if not ensure_flow_exists(): + """Set a config value (use --user for user-level config).""" + user_level = getattr(args, "user", False) + + if not user_level and not ensure_flow_exists(): error_exit( - ".flow/ does not exist. Run 'flowctl init' first.", use_json=args.json + ".flow/ does not exist. Use --user for user-level config or run 'flowctl init' first.", + use_json=args.json, ) - set_config(args.key, args.value) - new_value = get_config(args.key) + set_config(args.key, args.value, user_level=user_level) + + # Parse the value the same way set_config does for display + display_value = args.value + if isinstance(display_value, str): + if display_value.lower() == "true": + display_value = True + elif display_value.lower() == "false": + display_value = False + elif display_value.isdigit(): + display_value = int(display_value) + location = "user-level (~/.config/flow-next/)" if user_level else "project (.flow/)" if args.json: - json_output({"key": args.key, "value": new_value, "message": f"{args.key} set"}) + json_output({"key": args.key, "value": display_value, "location": location, "message": f"{args.key} set"}) else: - print(f"{args.key} set to {new_value}") + print(f"{args.key} = {display_value} ({location})") MEMORY_TEMPLATES = { @@ -3617,6 +3674,7 @@ def main() -> None: p_config_set = config_sub.add_parser("set", help="Set config value") p_config_set.add_argument("key", help="Config key (e.g., memory.enabled)") p_config_set.add_argument("value", help="Config value") + p_config_set.add_argument("--user", action="store_true", help="Set in user-level config (~/.config/flow-next/)") p_config_set.add_argument("--json", action="store_true", help="JSON output") p_config_set.set_defaults(func=cmd_config_set) diff --git a/plugins/flow-next/skills/flow-next-setup/templates/usage.md b/plugins/flow-next/skills/flow-next-setup/templates/usage.md index f77848cf..e5189e7b 100644 --- a/plugins/flow-next/skills/flow-next-setup/templates/usage.md +++ b/plugins/flow-next/skills/flow-next-setup/templates/usage.md @@ -70,6 +70,23 @@ Task tracking for AI agents. All state lives in `.flow/`. {"commits": ["abc123"], "tests": ["npm test"], "prs": []} ``` +## Configuration + +Config hierarchy (project overrides user): +1. `~/.config/flow-next/config.json` - user-level defaults +2. `.flow/config.json` - project-level overrides + +```bash +# Get config (reads merged user + project) +.flow/bin/flowctl config get memory.enabled + +# Set project-level config +.flow/bin/flowctl config set memory.enabled true + +# Set user-level config (applies to all projects) +.flow/bin/flowctl config set memory.enabled true --user +``` + ## More Info - Human docs: https://github.com/gmickel/gmickel-claude-marketplace/blob/main/plugins/flow-next/docs/flowctl.md diff --git a/plugins/flow-next/skills/flow-next-setup/workflow.md b/plugins/flow-next/skills/flow-next-setup/workflow.md index 77ace9d5..964e6832 100644 --- a/plugins/flow-next/skills/flow-next-setup/workflow.md +++ b/plugins/flow-next/skills/flow-next-setup/workflow.md @@ -194,6 +194,13 @@ To use from command line: Documentation updated: - +Config hierarchy (project overrides user): + 1. ~/.config/flow-next/config.json (user defaults) + 2. .flow/config.json (project overrides) + +Set user-level defaults: + flowctl config set memory.enabled true --user + Notes: - Re-run /flow-next:setup --user after plugin updates - Scripts shared across all projects @@ -217,7 +224,8 @@ Documentation updated: - Memory system: disabled by default -Enable with: flowctl config set memory.enabled true +Enable for this project: flowctl config set memory.enabled true +Enable for all projects: flowctl config set memory.enabled true --user Notes: - Re-run /flow-next:setup after plugin updates to refresh scripts From 905ef554cc7e8aa7bbb87a5b30015ad971df1edf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CTiago?= Date: Tue, 13 Jan 2026 05:03:22 +0000 Subject: [PATCH 5/7] fix(flowctl): init creates empty config to inherit user defaults - init now creates {} instead of {memory:{enabled:false}} - Projects inherit user config by default - Projects can still override with config set - Update setup workflow to match Co-Authored-By: Claude Opus 4.5 --- plugins/flow-next/scripts/flowctl.py | 4 ++-- plugins/flow-next/skills/flow-next-setup/workflow.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/flow-next/scripts/flowctl.py b/plugins/flow-next/scripts/flowctl.py index 05dbbde9..1d4b6d45 100755 --- a/plugins/flow-next/scripts/flowctl.py +++ b/plugins/flow-next/scripts/flowctl.py @@ -1125,8 +1125,8 @@ def cmd_init(args: argparse.Namespace) -> None: meta = {"schema_version": SCHEMA_VERSION, "next_epic": 1} atomic_write_json(flow_dir / META_FILE, meta) - # Create config.json with defaults - atomic_write_json(flow_dir / CONFIG_FILE, get_default_config()) + # Create empty config.json (inherits from user config) + atomic_write_json(flow_dir / CONFIG_FILE, {}) if args.json: json_output({"message": ".flow/ initialized", "path": str(flow_dir)}) diff --git a/plugins/flow-next/skills/flow-next-setup/workflow.md b/plugins/flow-next/skills/flow-next-setup/workflow.md index 964e6832..4627863d 100644 --- a/plugins/flow-next/skills/flow-next-setup/workflow.md +++ b/plugins/flow-next/skills/flow-next-setup/workflow.md @@ -26,10 +26,10 @@ Check if `.flow/` directory exists (use Bash `ls .flow/` or check for `.flow/met {"schema_version": 2, "next_epic": 1} ``` -Also ensure `.flow/config.json` exists with defaults: +Also ensure `.flow/config.json` exists (empty to inherit user config): ```bash if [ ! -f .flow/config.json ]; then - echo '{"memory":{"enabled":false}}' > .flow/config.json + echo '{}' > .flow/config.json fi ``` From 34adf49a8a0df66dcc4d1370e342486adb040b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CTiago?= Date: Tue, 13 Jan 2026 07:36:35 +0000 Subject: [PATCH 6/7] fix: portable readlink for macOS symlink resolution readlink -f doesn't exist on macOS. Add _portable_realpath() that tries readlink -f -> greadlink -f -> realpath -> python3 Tested on Linux and macOS (python3 fallback). Co-Authored-By: Claude Opus 4.5 --- .../flow-next-ralph-init/templates/ralph.sh | 17 ++++++++++++++++- .../templates/ralph_once.sh | 16 +++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph.sh b/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph.sh index 459819b2..1697a758 100644 --- a/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph.sh +++ b/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph.sh @@ -18,6 +18,21 @@ set -euo pipefail # 3. Fall back to project-local (script's directory) # ───────────────────────────────────────────────────────────────────────────── +_portable_realpath() { + # Portable realpath: resolve symlinks to absolute path + # Works on Linux (readlink -f) and macOS (greadlink/python fallback) + local path="$1" + if readlink -f "$path" 2>/dev/null; then + return + elif command -v greadlink >/dev/null 2>&1; then + greadlink -f "$path" + elif command -v realpath >/dev/null 2>&1; then + realpath "$path" + else + python3 -c "import os; print(os.path.realpath('$path'))" + fi +} + _resolve_script_dir() { local self_dir self_dir="$(cd "$(dirname "$0")" && pwd)" @@ -25,7 +40,7 @@ _resolve_script_dir() { # Check if we're a symlink to user-level scripts if [[ -L "$0" ]]; then local target - target="$(readlink -f "$0")" + target="$(_portable_realpath "$0")" self_dir="$(dirname "$target")" fi diff --git a/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph_once.sh b/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph_once.sh index 408f4628..43f230d2 100644 --- a/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph_once.sh +++ b/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph_once.sh @@ -4,10 +4,24 @@ set -euo pipefail +# Portable realpath (same as ralph.sh) +_portable_realpath() { + local path="$1" + if readlink -f "$path" 2>/dev/null; then + return + elif command -v greadlink >/dev/null 2>&1; then + greadlink -f "$path" + elif command -v realpath >/dev/null 2>&1; then + realpath "$path" + else + python3 -c "import os; print(os.path.realpath('$path'))" + fi +} + # Resolve script directory (follows symlinks for user-level mode) SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" if [[ -L "$0" ]]; then - SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" + SCRIPT_DIR="$(dirname "$(_portable_realpath "$0")")" fi export MAX_ITERATIONS=1 From 0788e1344c533464308c891919d4d9e30162e689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CTiago?= Date: Tue, 13 Jan 2026 07:55:42 +0000 Subject: [PATCH 7/7] chore: remove unused CONFIG variable from ralph.sh CONFIG was set but never used. Config loading happens via explicit USER_CONFIG and PROJECT_CONFIG sourcing. Co-Authored-By: Claude Opus 4.5 --- .flow/epics/fn-5.json | 13 ++++++++++++ .flow/specs/fn-5.md | 20 +++++++++++++++++++ .gitignore | 1 + .../flow-next-ralph-init/templates/ralph.sh | 10 +--------- 4 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 .flow/epics/fn-5.json create mode 100644 .flow/specs/fn-5.md diff --git a/.flow/epics/fn-5.json b/.flow/epics/fn-5.json new file mode 100644 index 00000000..40de4bbe --- /dev/null +++ b/.flow/epics/fn-5.json @@ -0,0 +1,13 @@ +{ + "branch_name": "pr-22-user-flag", + "created_at": "2026-01-13T05:56:37.540724Z", + "depends_on_epics": [], + "id": "fn-5", + "next_task": 1, + "plan_review_status": "unknown", + "plan_reviewed_at": null, + "spec_path": ".flow/specs/fn-5.md", + "status": "open", + "title": "PR 22: User-level storage (--user flag)", + "updated_at": "2026-01-13T05:56:37.540740Z" +} diff --git a/.flow/specs/fn-5.md b/.flow/specs/fn-5.md new file mode 100644 index 00000000..7409386b --- /dev/null +++ b/.flow/specs/fn-5.md @@ -0,0 +1,20 @@ +# fn-5 PR 22: User-level storage (--user flag) + +## Overview +TBD + +## Scope +TBD + +## Approach +TBD + +## Quick commands + +- `# e.g., npm test, bun test, make test` + +## Acceptance +- [ ] TBD + +## References +- TBD diff --git a/.gitignore b/.gitignore index bea5ab6c..250122fc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ plans/ todos/ /docs/ !plugins/*/docs/ +plugins/flow-next/scripts/__pycache__/ diff --git a/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph.sh b/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph.sh index 1697a758..1f0f3773 100644 --- a/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph.sh +++ b/plugins/flow-next/skills/flow-next-ralph-init/templates/ralph.sh @@ -80,7 +80,7 @@ if [[ -n "$USER_DIR" && -f "$USER_DIR/ralph.sh" ]]; then fi # Config: user-level defaults + project overrides -# User config provides defaults, project config overrides +# Both are sourced: user first (defaults), then project (overrides) USER_CONFIG="" PROJECT_CONFIG="" if [[ -n "$USER_DIR" && -f "$USER_DIR/config.env" ]]; then @@ -89,14 +89,6 @@ fi if [[ -f "$PROJECT_DIR/config.env" ]]; then PROJECT_CONFIG="$PROJECT_DIR/config.env" fi -# For compatibility, CONFIG points to the primary config (user or project) -if [[ -n "$USER_CONFIG" ]]; then - CONFIG="$USER_CONFIG" -elif [[ -n "$PROJECT_CONFIG" ]]; then - CONFIG="$PROJECT_CONFIG" -else - CONFIG="$SCRIPT_DIR/config.env" -fi FLOWCTL="$SCRIPT_DIR/flowctl" # Ensure runs directory exists in project