mirror of
https://github.com/garrytan/gstack.git
synced 2026-06-01 15:51:41 +02:00
9562ad4e70
* feat(config): add plan_tune_hooks setting (prompt|yes|no) Registers a new gstack-config key controlling whether ./setup installs the plan-tune Claude Code hooks. Default "prompt". Documented in the config header and surfaced in `gstack-config defaults` / `list`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(setup): make plan-tune hook install non-interactive-safe The plan-tune consent prompt used a blocking `read -r` with no timeout. Under a forwarded/automated TTY (conductor workspace setup, CI with a pty) it hung setup forever. Move the decision into flags + env + saved config with a smart default: --plan-tune-hooks / --no-plan-tune-hooks / --plan-tune-hooks=yes|no|prompt > GSTACK_PLAN_TUNE_HOOKS env > plan_tune_hooks config > prompt-on-real-TTY. Explicit yes/no act non-interactively. The remaining interactive branch is gated on a real (non-quiet) TTY and uses a time-bounded `read -t 10 </dev/tty` that defaults to skip, so it can never hang. A timeout no longer persists a decline marker, so a later hands-on run can still offer the install. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(dev-setup): run setup non-interactively in dev/workspace mode Conductor runs bin/dev-setup under a forwarded pty, so any setup prompt (skill-prefix, plan-tune consent) would hang the workspace. Detach stdin (`setup </dev/null`) so every prompt takes its smart non-interactive default: flat skill names, skip the global plan-tune hook install without writing a decline marker. Saved prefix/config preferences are still honored, and a dev workspace no longer silently mutates ~/.claude/settings.json. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(setup): guard plan-tune hooks stay non-interactive Static + binary-level regression test (free, <1s): asserts the flags are wired, the plan-tune read is time-bounded (no bare blocking read), explicit yes/no decisions short-circuit before the prompt, and gstack-config knows the plan_tune_hooks key. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(setup,config): harden plan-tune decision against bad input Review follow-ups to the non-interactive plan-tune work: - setup now lowercases + whitespace-strips the resolved decision before the case match, so an explicit opt-in via flag/env ("YES", "Yes", " yes") is honored instead of silently falling through to "prompt"/skip. Also accepts on/off and 1/0. - gstack-config rejects out-of-domain plan_tune_hooks values (anything but prompt|yes|no) with a warning + fallback to prompt, matching the existing value-whitelist pattern for explain_level / artifacts_sync_mode. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(dev-setup): never mutate global hooks during workspace setup Closing stdin alone only suppresses the prompt branch; a saved `plan_tune_hooks: yes` or exported GSTACK_PLAN_TUNE_HOOKS=yes would still resolve to "install" and rewrite the user's global ~/.claude/settings.json to point at THIS ephemeral worktree — which breaks once the workspace is deleted. Pass --plan-tune-hooks=prompt (highest precedence) so dev-setup pins resolution to prompt-mode; with stdin closed that is a guaranteed no-op skip (no install, no decline marker). To install the hooks, run ./setup --plan-tune-hooks directly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(setup): isolate config tests from host + cover new guards - Point gstack-config tests at a temp GSTACK_HOME so `get plan_tune_hooks` reads the built-in default, not whatever the host machine has in ~/.gstack/config.yaml (the prior test was non-deterministic). - Add behavioral coverage: yes/no/prompt round-trip, out-of-domain rejection. - Add a normalization guard (decision input is lowercased/trimmed) and a dev-setup guard (runs setup with --plan-tune-hooks=prompt + stdin detached). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: rebaseline parity-suite v1.44.1 -> v1.53.0.0 The frozen v1.44.1 anchor went stale: five planning skills (plan-ceo-review, plan-eng-review, plan-design-review, investigate, office-hours) crept past the 1.05x ceiling via legitimate v1.49-v1.53 growth (brain-aware planning + the v1.53 redaction guard), so `bun test` was red on a clean checkout of main. Capture a fresh baseline at HEAD (bun run scripts/capture-baseline.ts --tag v1.53.0.0) and re-point the test at it. The per-skill 1.05 ratio is kept, so future bloat is still caught; only the anchor moved. Mirrors the earlier skill-size-budget rebase (v1.44.1 -> v1.47.0.0). Historical v1.44.1 / v1.46.0.0 / v1.47.0.0 baselines are retained for the v1->v2 audit trail. The captured skill bytes equal origin/main exactly (this branch left every SKILL.md untouched). Clears the pre-existing failures noted in the v1.53.0.0 CHANGELOG. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(plan-tune): de-flake "derive pushes scope_appetite up" The test was ~25-50% flaky (worse on main). gstack-question-log fires a fire-and-forget background `--derive` after every write; the 5 rapid log writes spawned 5 racing background derives that collided with the test's explicit --derive — a late one that only saw 3 entries could clobber developer-profile.json after the explicit one wrote sample_size=5. Set GSTACK_QUESTION_LOG_NO_DERIVE=1 (the flag the binary documents for exactly this case) so the writes don't spawn background derives. The explicit --derive still runs, so real derive behavior is still asserted. 20/20 green after. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump version and changelog (v1.53.1.0) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: document non-interactive dev-setup + plan-tune hook flags (v1.53.1.0) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
84 lines
3.4 KiB
Bash
Executable File
84 lines
3.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Set up gstack for local development — test skills from within this repo.
|
|
#
|
|
# Creates .claude/skills/gstack → (symlink to repo root) so Claude Code
|
|
# discovers skills from your working tree. Changes take effect immediately.
|
|
#
|
|
# Also copies .env from the main worktree if this is a Conductor workspace
|
|
# or git worktree (so API keys carry over automatically).
|
|
#
|
|
# Usage: bin/dev-setup # set up
|
|
# bin/dev-teardown # clean up
|
|
set -e
|
|
|
|
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
|
|
# 1. Copy .env from main worktree (if we're a worktree and don't have one)
|
|
if [ ! -f "$REPO_ROOT/.env" ]; then
|
|
MAIN_WORKTREE="$(git -C "$REPO_ROOT" worktree list --porcelain 2>/dev/null | head -1 | sed 's/^worktree //')"
|
|
if [ -n "$MAIN_WORKTREE" ] && [ "$MAIN_WORKTREE" != "$REPO_ROOT" ] && [ -f "$MAIN_WORKTREE/.env" ]; then
|
|
cp "$MAIN_WORKTREE/.env" "$REPO_ROOT/.env"
|
|
echo "Copied .env from main worktree ($MAIN_WORKTREE)"
|
|
fi
|
|
fi
|
|
|
|
# 2. Install dependencies
|
|
if [ ! -d "$REPO_ROOT/node_modules" ]; then
|
|
echo "Installing dependencies..."
|
|
(cd "$REPO_ROOT" && bun install)
|
|
fi
|
|
|
|
# 3. Create .claude/skills/ inside the repo
|
|
mkdir -p "$REPO_ROOT/.claude/skills"
|
|
|
|
# 4. Symlink .claude/skills/gstack → repo root
|
|
# This makes setup think it's inside a real .claude/skills/ directory
|
|
GSTACK_LINK="$REPO_ROOT/.claude/skills/gstack"
|
|
if [ -L "$GSTACK_LINK" ]; then
|
|
echo "Updating existing symlink..."
|
|
rm "$GSTACK_LINK"
|
|
elif [ -d "$GSTACK_LINK" ]; then
|
|
echo "Error: .claude/skills/gstack is a real directory, not a symlink." >&2
|
|
echo "Remove it manually if you want to use dev mode." >&2
|
|
exit 1
|
|
fi
|
|
ln -s "$REPO_ROOT" "$GSTACK_LINK"
|
|
|
|
# 5. Create .agents/skills/gstack → repo root (for Codex/Gemini/Cursor)
|
|
mkdir -p "$REPO_ROOT/.agents/skills"
|
|
AGENTS_LINK="$REPO_ROOT/.agents/skills/gstack"
|
|
if [ -L "$AGENTS_LINK" ]; then
|
|
rm "$AGENTS_LINK"
|
|
elif [ -d "$AGENTS_LINK" ]; then
|
|
echo "Warning: .agents/skills/gstack is a real directory, skipping." >&2
|
|
fi
|
|
if [ ! -e "$AGENTS_LINK" ]; then
|
|
ln -s "$REPO_ROOT" "$AGENTS_LINK"
|
|
fi
|
|
|
|
# 6. Run setup via the symlink so it detects .claude/skills/ as its parent.
|
|
#
|
|
# Workspace/dev setup MUST be non-interactive: Conductor runs this under a
|
|
# forwarded pty, so any `read` in setup (skill-prefix prompt, plan-tune hook
|
|
# consent) would hang the workspace forever. Detaching stdin makes every setup
|
|
# prompt take its smart non-interactive default (flat skill names, etc.).
|
|
#
|
|
# `--plan-tune-hooks=prompt` is load-bearing, not redundant: stdin alone only
|
|
# suppresses the *prompt* branch. A saved `plan_tune_hooks: yes` or an exported
|
|
# GSTACK_PLAN_TUNE_HOOKS=yes would still resolve to "install" and rewrite the
|
|
# user's global ~/.claude/settings.json to point at THIS ephemeral worktree —
|
|
# which breaks once the workspace is deleted. The flag has highest precedence,
|
|
# so it pins resolution to "prompt", and closed stdin then makes prompt-mode a
|
|
# no-op skip (no install, no decline marker). A dev workspace must never mutate
|
|
# global settings.json. To install the hooks, run `./setup --plan-tune-hooks`
|
|
# directly (outside dev-setup). Saved prefix/other config preferences still apply.
|
|
"$GSTACK_LINK/setup" --plan-tune-hooks=prompt </dev/null
|
|
|
|
echo ""
|
|
echo "Dev mode active. Skills resolve from this working tree."
|
|
echo " .claude/skills/gstack → $REPO_ROOT"
|
|
echo " .agents/skills/gstack → $REPO_ROOT"
|
|
echo "Edit any SKILL.md and test immediately — no copy/deploy needed."
|
|
echo ""
|
|
echo "To tear down: bin/dev-teardown"
|