diff --git a/CHANGELOG.md b/CHANGELOG.md index 91786cb7..15c2ada9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## [0.15.13.0] - 2026-04-04 — Team Mode + +Teams can now keep every developer on the same gstack version automatically. No more vendoring 342 files into your repo. No more version drift across branches. No more "who upgraded gstack last?" Slack threads. One command, every developer is current. + +Hat tip to Jared Friedman for the design. + +### Added + +- **`./setup --team`.** Registers a `SessionStart` hook in `~/.claude/settings.json` that auto-updates gstack at the start of each Claude Code session. Runs in background (zero latency), throttled to once/hour, network-failure-safe, completely silent. `./setup --no-team` reverses it. +- **`./setup -q` / `--quiet`.** Suppresses all informational output. Used by the session-update hook but also useful for CI and scripted installs. +- **`gstack-team-init` command.** Generates repo-level bootstrap files in two flavors: `optional` (gentle CLAUDE.md suggestion, one-time offer per developer) or `required` (CLAUDE.md enforcement + PreToolUse hook that blocks work without gstack installed). +- **`gstack-settings-hook` helper.** DRY utility for adding/removing hooks in Claude Code's `settings.json`. Atomic writes (.tmp + rename) prevent corruption. +- **`gstack-session-update` script.** The SessionStart hook target. Background fork, PID-based lockfile with stale recovery, `GIT_TERMINAL_PROMPT=0` to prevent credential prompt hangs, debug log at `~/.gstack/analytics/session-update.log`. +- **Vendoring deprecation in preamble.** Every skill now detects vendored gstack copies in the project and offers one-time migration to team mode. "Want me to do it for you?" beats "here are 4 manual steps." + +### Changed + +- **Vendoring is deprecated.** README no longer recommends copying gstack into your repo. Global install + `--team` is the way. `--local` flag still works but prints a deprecation warning. +- **Uninstall cleans up hooks.** `gstack-uninstall` now removes the SessionStart hook from `~/.claude/settings.json`. + ## [0.15.12.0] - 2026-04-06 ### Fixed diff --git a/CLAUDE.md b/CLAUDE.md index 468dc6e7..d7e32100 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -186,10 +186,10 @@ failure modes. The sidebar spans 5 files across 2 codebases (extension + server) with non-obvious ordering dependencies. The doc exists to prevent the kind of silent failures that come from not understanding the cross-component flow. -## Vendored symlink awareness +## Dev symlink awareness When developing gstack, `.claude/skills/gstack` may be a symlink back to this -working directory (gitignored). This means skill changes are **live immediately** — +working directory (gitignored). This means skill changes are **live immediately**, great for rapid iteration, risky during big refactors where half-written skills could break other Claude Code sessions using gstack concurrently. @@ -204,9 +204,11 @@ symlink or a real copy. If it's a symlink to your working directory, be aware th with a SKILL.md symlink inside (e.g., `qa/SKILL.md -> gstack/qa/SKILL.md`). This ensures Claude discovers them as top-level skills, not nested under `gstack/`. Names are either short (`qa`) or namespaced (`gstack-qa`), controlled by -`skill_prefix` in `~/.gstack/config.yaml`. When vendoring into a project, run -`./setup` after symlinking to create the per-skill directories. Pass `--no-prefix` -or `--prefix` to skip the interactive prompt. +`skill_prefix` in `~/.gstack/config.yaml`. Pass `--no-prefix` or `--prefix` to +skip the interactive prompt. + +**Note:** Vendoring gstack into a project's repo is deprecated. Use global install ++ `./setup --team` instead. See README.md for team mode instructions. **For plan reviews:** When reviewing plans that modify skill templates or the gen-skill-docs pipeline, consider whether the changes should be tested in isolation diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 55cdccd0..15378e21 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -367,7 +367,7 @@ to `gstack/...`, so they'll resolve to the global install automatically. ### Switching prefix mode -If you vendored gstack with one prefix setting and want to switch: +If you installed gstack with one prefix setting and want to switch: ```bash cd .claude/skills/gstack && ./setup --no-prefix # switch to /qa, /ship diff --git a/README.md b/README.md index da2f0cf5..22cbde6b 100644 --- a/README.md +++ b/README.md @@ -48,11 +48,23 @@ Open Claude Code and paste this. Claude does the rest. > Install gstack: run **`git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup`** then add a "gstack" section to CLAUDE.md that says to use the /browse skill from gstack for all web browsing, never use mcp\_\_claude-in-chrome\_\_\* tools, and lists the available skills: /office-hours, /plan-ceo-review, /plan-eng-review, /plan-design-review, /design-consultation, /design-shotgun, /design-html, /review, /ship, /land-and-deploy, /canary, /benchmark, /browse, /connect-chrome, /qa, /qa-only, /design-review, /setup-browser-cookies, /setup-deploy, /retro, /investigate, /document-release, /codex, /cso, /autoplan, /plan-devex-review, /devex-review, /careful, /freeze, /guard, /unfreeze, /gstack-upgrade, /learn. Then ask the user if they also want to add gstack to the current project so teammates get it. -### Step 2: Add to your repo so teammates get it (optional) +### Step 2: Team mode — auto-update for shared repos (recommended) -> Add gstack to this project: run **`cp -Rf ~/.claude/skills/gstack .claude/skills/gstack && rm -rf .claude/skills/gstack/.git && cd .claude/skills/gstack && ./setup`** then add a "gstack" section to this project's CLAUDE.md that says to use the /browse skill from gstack for all web browsing, never use mcp\_\_claude-in-chrome\_\_\* tools, lists the available skills: /office-hours, /plan-ceo-review, /plan-eng-review, /plan-design-review, /design-consultation, /design-shotgun, /design-html, /review, /ship, /land-and-deploy, /canary, /benchmark, /browse, /connect-chrome, /qa, /qa-only, /design-review, /setup-browser-cookies, /setup-deploy, /retro, /investigate, /document-release, /codex, /cso, /autoplan, /plan-devex-review, /devex-review, /careful, /freeze, /guard, /unfreeze, /gstack-upgrade, /learn, and tells Claude that if gstack skills aren't working, run `cd .claude/skills/gstack && ./setup` to build the binary and register skills. +Every developer installs globally, updates happen automatically: -Real files get committed to your repo (not a submodule), so `git clone` just works. Everything lives inside `.claude/`. Nothing touches your PATH or runs in the background. +```bash +cd ~/.claude/skills/gstack && ./setup --team +``` + +Then bootstrap your repo so teammates get it: + +```bash +cd +~/.claude/skills/gstack/bin/gstack-team-init required # or: optional +git add .claude/ CLAUDE.md && git commit -m "require gstack for AI-assisted work" +``` + +No vendored files in your repo, no version drift, no manual upgrades. Every Claude Code session starts with a fast auto-update check (throttled to once/hour, network-failure-safe, completely silent). > **Contributing or need full history?** The commands above use `--depth 1` for a fast install. If you plan to contribute or need full git history, do a full clone instead: > ```bash diff --git a/SKILL.md b/SKILL.md index 1301aefa..7838996b 100644 --- a/SKILL.md +++ b/SKILL.md @@ -80,6 +80,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -208,6 +216,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/VERSION b/VERSION index 7937bb24..93c34ea4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.15.12.0 +0.15.13.0 diff --git a/autoplan/SKILL.md b/autoplan/SKILL.md index 4b268832..7b05d620 100644 --- a/autoplan/SKILL.md +++ b/autoplan/SKILL.md @@ -90,6 +90,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -218,6 +226,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/benchmark/SKILL.md b/benchmark/SKILL.md index a13c2e8b..370d09d5 100644 --- a/benchmark/SKILL.md +++ b/benchmark/SKILL.md @@ -83,6 +83,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -211,6 +219,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/bin/gstack-session-update b/bin/gstack-session-update new file mode 100755 index 00000000..66bd4402 --- /dev/null +++ b/bin/gstack-session-update @@ -0,0 +1,116 @@ +#!/usr/bin/env bash +# gstack-session-update — auto-update gstack on session start (team mode) +# +# Called by Claude Code SessionStart hook. Must be fast, silent, non-fatal. +# The entire update runs in background (forked). The hook itself exits +# immediately so session startup is never delayed. +# +# Exit 0 always — errors must never block a Claude Code session. + +set +e + +GSTACK_DIR="${GSTACK_DIR:-$HOME/.claude/skills/gstack}" +STATE_DIR="${GSTACK_STATE_DIR:-$HOME/.gstack}" +THROTTLE_FILE="$STATE_DIR/.last-session-update" +LOCK_DIR="$STATE_DIR/.setup-lock" +LOG_FILE="$STATE_DIR/analytics/session-update.log" +THROTTLE_SECONDS=3600 # 1 hour + +log_entry() { + mkdir -p "$(dirname "$LOG_FILE")" + echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) $1" >> "$LOG_FILE" 2>/dev/null || true +} + +# ── Guard: gstack must be a git repo ── +if [ ! -d "$GSTACK_DIR/.git" ]; then + exit 0 +fi + +# ── Guard: team mode must be enabled ── +AUTO=$("$GSTACK_DIR/bin/gstack-config" get auto_upgrade 2>/dev/null || true) +if [ "$AUTO" != "true" ]; then + exit 0 +fi + +# ── Throttle: skip if checked recently ── +if [ -f "$THROTTLE_FILE" ]; then + LAST=$(cat "$THROTTLE_FILE" 2>/dev/null || echo 0) + NOW=$(date +%s) + ELAPSED=$(( NOW - LAST )) + if [ "$ELAPSED" -lt "$THROTTLE_SECONDS" ]; then + exit 0 + fi +fi + +# ── Fork to background: zero latency on session start ── +( + # Prevent git from prompting for credentials (would hang the background process) + export GIT_TERMINAL_PROMPT=0 + + mkdir -p "$STATE_DIR" + + # ── Acquire lockfile (skip if another session is running setup) ── + if ! mkdir "$LOCK_DIR" 2>/dev/null; then + # Lock exists — check if stale (PID dead) + if [ -f "$LOCK_DIR/pid" ]; then + LOCK_PID=$(cat "$LOCK_DIR/pid" 2>/dev/null || echo 0) + if [ "$LOCK_PID" -gt 0 ] 2>/dev/null && ! kill -0 "$LOCK_PID" 2>/dev/null; then + # Stale lock — remove and re-acquire + rm -rf "$LOCK_DIR" 2>/dev/null + mkdir "$LOCK_DIR" 2>/dev/null || { log_entry "SKIP lock_contested"; exit 0; } + else + log_entry "SKIP locked_by=$LOCK_PID" + exit 0 + fi + else + log_entry "SKIP locked_no_pid" + exit 0 + fi + fi + + # Write PID for stale lock detection + echo $$ > "$LOCK_DIR/pid" 2>/dev/null + + # Clean up lock on exit + trap 'rm -rf "$LOCK_DIR" 2>/dev/null' EXIT + + # ── Pull latest ── + OLD_HEAD=$(git -C "$GSTACK_DIR" rev-parse HEAD 2>/dev/null) + git -C "$GSTACK_DIR" pull --ff-only -q 2>/dev/null + PULL_EXIT=$? + NEW_HEAD=$(git -C "$GSTACK_DIR" rev-parse HEAD 2>/dev/null) + + # Record check time regardless of outcome + date +%s > "$THROTTLE_FILE" 2>/dev/null + + if [ "$PULL_EXIT" -ne 0 ]; then + log_entry "PULL_FAILED exit=$PULL_EXIT" + exit 0 + fi + + # ── If HEAD moved, run setup -q ── + if [ "$OLD_HEAD" != "$NEW_HEAD" ]; then + log_entry "UPDATING old=$OLD_HEAD new=$NEW_HEAD" + + # bun must be available for setup + if command -v bun >/dev/null 2>&1; then + ( cd "$GSTACK_DIR" && ./setup -q ) >/dev/null 2>&1 || { + log_entry "SETUP_FAILED" + } + else + log_entry "SETUP_SKIPPED bun_missing" + fi + + # Write marker so next skill preamble shows "just upgraded" + OLD_VER=$(git -C "$GSTACK_DIR" show "$OLD_HEAD:VERSION" 2>/dev/null || echo "unknown") + echo "$OLD_VER" > "$STATE_DIR/just-upgraded-from" 2>/dev/null + rm -f "$STATE_DIR/last-update-check" 2>/dev/null + rm -f "$STATE_DIR/update-snoozed" 2>/dev/null + + log_entry "UPDATED from=$OLD_VER to=$(cat "$GSTACK_DIR/VERSION" 2>/dev/null || echo unknown)" + else + log_entry "UP_TO_DATE head=$OLD_HEAD" + fi +) & + +exit 0 diff --git a/bin/gstack-settings-hook b/bin/gstack-settings-hook new file mode 100755 index 00000000..93a537f0 --- /dev/null +++ b/bin/gstack-settings-hook @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# gstack-settings-hook — add/remove SessionStart hooks in Claude Code settings.json +# +# Usage: +# gstack-settings-hook add # add SessionStart hook +# gstack-settings-hook remove # remove SessionStart hook +# +# Requires: bun (already a gstack hard dependency) +# Writes atomically: .tmp + rename to prevent corruption on crash/disk-full. + +set -euo pipefail + +ACTION="${1:-}" +HOOK_CMD="${2:-}" +SETTINGS_FILE="${GSTACK_SETTINGS_FILE:-$HOME/.claude/settings.json}" + +if [ -z "$ACTION" ] || [ -z "$HOOK_CMD" ]; then + echo "Usage: gstack-settings-hook {add|remove} " >&2 + exit 1 +fi + +if ! command -v bun >/dev/null 2>&1; then + echo "Error: bun is required but not installed." >&2 + exit 1 +fi + +case "$ACTION" in + add) + bun -e " + const fs = require('fs'); + const settingsPath = '$SETTINGS_FILE'; + const hookCmd = $(printf '%s' "$HOOK_CMD" | bun -e "process.stdout.write(JSON.stringify(require('fs').readFileSync('/dev/stdin','utf8')))"); + + let settings = {}; + try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); } catch {} + + if (!settings.hooks) settings.hooks = {}; + if (!settings.hooks.SessionStart) settings.hooks.SessionStart = []; + + // Dedup: check if hook command already registered + const exists = settings.hooks.SessionStart.some(entry => + entry.hooks && entry.hooks.some(h => h.command && h.command.includes('gstack-session-update')) + ); + + if (!exists) { + settings.hooks.SessionStart.push({ + hooks: [{ type: 'command', command: hookCmd }] + }); + } + + const tmp = settingsPath + '.tmp'; + fs.writeFileSync(tmp, JSON.stringify(settings, null, 2) + '\n'); + fs.renameSync(tmp, settingsPath); + " 2>/dev/null + ;; + remove) + [ -f "$SETTINGS_FILE" ] || exit 0 + bun -e " + const fs = require('fs'); + const settingsPath = '$SETTINGS_FILE'; + + let settings = {}; + try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); } catch { process.exit(0); } + + if (settings.hooks && settings.hooks.SessionStart) { + settings.hooks.SessionStart = settings.hooks.SessionStart.filter(entry => + !(entry.hooks && entry.hooks.some(h => h.command && h.command.includes('gstack-session-update'))) + ); + if (settings.hooks.SessionStart.length === 0) delete settings.hooks.SessionStart; + if (Object.keys(settings.hooks).length === 0) delete settings.hooks; + } + + const tmp = settingsPath + '.tmp'; + fs.writeFileSync(tmp, JSON.stringify(settings, null, 2) + '\n'); + fs.renameSync(tmp, settingsPath); + " 2>/dev/null + ;; + *) + echo "Unknown action: $ACTION (expected add or remove)" >&2 + exit 1 + ;; +esac diff --git a/bin/gstack-team-init b/bin/gstack-team-init new file mode 100755 index 00000000..6195c7b6 --- /dev/null +++ b/bin/gstack-team-init @@ -0,0 +1,172 @@ +#!/usr/bin/env bash +# gstack-team-init — generate repo-level bootstrap files for team mode +# +# Usage: +# gstack-team-init optional # gentle CLAUDE.md suggestion, one-time offer +# gstack-team-init required # CLAUDE.md enforcement + PreToolUse hook +# +# Run from the root of your team's repo (not from the gstack directory). + +set -euo pipefail + +MODE="${1:-}" + +if [ "$MODE" != "optional" ] && [ "$MODE" != "required" ]; then + echo "Usage: gstack-team-init {optional|required}" >&2 + echo "" >&2 + echo " optional — suggest gstack install once per developer (gentle)" >&2 + echo " required — enforce gstack install, block work without it" >&2 + exit 1 +fi + +# Must be in a git repo +if ! git rev-parse --show-toplevel >/dev/null 2>&1; then + echo "Error: not in a git repository. Run from your project root." >&2 + exit 1 +fi + +REPO_ROOT=$(git rev-parse --show-toplevel) +CLAUDE_MD="$REPO_ROOT/CLAUDE.md" +GENERATED=() + +# ── CLAUDE.md snippet ────────────────────────────────────────── + +if [ "$MODE" = "optional" ]; then + SNIPPET='## gstack (recommended) + +This project uses [gstack](https://github.com/garrytan/gstack) for AI-assisted workflows. +Install it for the best experience: + +```bash +git clone --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack +cd ~/.claude/skills/gstack && ./setup --team +``` + +Skills like /qa, /ship, /review, /investigate, and /browse become available after install. +Use /browse for all web browsing. Use ~/.claude/skills/gstack/... for gstack file paths.' +else + SNIPPET='## gstack (REQUIRED — global install) + +**Before doing ANY work, verify gstack is installed:** + +```bash +test -d ~/.claude/skills/gstack/bin && echo "GSTACK_OK" || echo "GSTACK_MISSING" +``` + +If GSTACK_MISSING: STOP. Do not proceed. Tell the user: + +> gstack is required for all AI-assisted work in this repo. +> Install it: +> ```bash +> git clone --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack +> cd ~/.claude/skills/gstack && ./setup --team +> ``` +> Then restart your AI coding tool. + +Do not skip skills, ignore gstack errors, or work around missing gstack. + +Using gstack skills: After install, skills like /qa, /ship, /review, /investigate, +and /browse are available. Use /browse for all web browsing. +Use ~/.claude/skills/gstack/... for gstack file paths (the global path).' +fi + +# Check if CLAUDE.md already has a gstack section +if [ -f "$CLAUDE_MD" ] && grep -q "## gstack" "$CLAUDE_MD" 2>/dev/null; then + echo "CLAUDE.md already has a gstack section. Skipping CLAUDE.md update." + echo " To replace it, remove the existing ## gstack section and re-run." +else + if [ -f "$CLAUDE_MD" ]; then + echo "" >> "$CLAUDE_MD" + fi + echo "$SNIPPET" >> "$CLAUDE_MD" + GENERATED+=("CLAUDE.md") + echo " + CLAUDE.md — added gstack $MODE section" +fi + +# ── Required mode: enforcement hook ──────────────────────────── + +if [ "$MODE" = "required" ]; then + HOOKS_DIR="$REPO_ROOT/.claude/hooks" + SETTINGS="$REPO_ROOT/.claude/settings.json" + + # Create enforcement hook script + mkdir -p "$HOOKS_DIR" + cat > "$HOOKS_DIR/check-gstack.sh" << 'HOOK_EOF' +#!/bin/bash +# Block skill usage when gstack is not installed globally. + +if [ ! -d "$HOME/.claude/skills/gstack/bin" ]; then + cat >&2 <<'MSG' +BLOCKED: gstack is not installed globally. + +gstack is required for AI-assisted work in this repo. + +Install it: + git clone --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack + cd ~/.claude/skills/gstack && ./setup --team + +Then restart your AI coding tool. +MSG + echo '{"permissionDecision":"deny","message":"gstack is required but not installed. See stderr for install instructions."}' + exit 0 +fi + +echo '{}' +HOOK_EOF + chmod +x "$HOOKS_DIR/check-gstack.sh" + GENERATED+=(".claude/hooks/check-gstack.sh") + echo " + .claude/hooks/check-gstack.sh — enforcement hook" + + # Add hook to project-level settings.json + if command -v bun >/dev/null 2>&1; then + bun -e " + const fs = require('fs'); + const settingsPath = '$SETTINGS'; + + let settings = {}; + try { settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); } catch {} + + if (!settings.hooks) settings.hooks = {}; + if (!settings.hooks.PreToolUse) settings.hooks.PreToolUse = []; + + // Dedup + const exists = settings.hooks.PreToolUse.some(entry => + entry.matcher === 'Skill' && + entry.hooks && entry.hooks.some(h => h.command && h.command.includes('check-gstack')) + ); + + if (!exists) { + settings.hooks.PreToolUse.push({ + matcher: 'Skill', + hooks: [{ + type: 'command', + command: '\"\$CLAUDE_PROJECT_DIR/.claude/hooks/check-gstack.sh\"' + }] + }); + } + + const tmp = settingsPath + '.tmp'; + fs.writeFileSync(tmp, JSON.stringify(settings, null, 2) + '\n'); + fs.renameSync(tmp, settingsPath); + " 2>/dev/null + GENERATED+=(".claude/settings.json") + echo " + .claude/settings.json — PreToolUse hook registered" + else + echo " ! bun not found — manually add the PreToolUse hook to .claude/settings.json" + fi +fi + +# ── Summary ──────────────────────────────────────────────────── + +echo "" +echo "Team mode ($MODE) initialized." +echo "" +if [ ${#GENERATED[@]} -gt 0 ]; then + echo "Commit the generated files:" + echo " git add ${GENERATED[*]}" + echo " git commit -m \"chore: require gstack for AI-assisted work\"" +fi +echo "" +echo "Each developer then runs:" +echo " git clone --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack" +echo " cd ~/.claude/skills/gstack && ./setup --team" diff --git a/bin/gstack-uninstall b/bin/gstack-uninstall index 2cf3d528..4f7b0fc1 100755 --- a/bin/gstack-uninstall +++ b/bin/gstack-uninstall @@ -227,6 +227,13 @@ if [ -n "$_GIT_ROOT" ]; then fi fi +# ─── Remove SessionStart hook from Claude Code settings ───── +SETTINGS_HOOK="$(dirname "$0")/gstack-settings-hook" +SESSION_UPDATE="$(dirname "$0")/gstack-session-update" +if [ -x "$SETTINGS_HOOK" ]; then + "$SETTINGS_HOOK" remove "$SESSION_UPDATE" 2>/dev/null && REMOVED+=("SessionStart hook") || true +fi + # ─── Remove global state ──────────────────────────────────── if [ "$KEEP_STATE" -eq 0 ] && [ -d "$STATE_DIR" ]; then rm -rf "$STATE_DIR" diff --git a/browse/SKILL.md b/browse/SKILL.md index 9ab4b383..2aad0cec 100644 --- a/browse/SKILL.md +++ b/browse/SKILL.md @@ -82,6 +82,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -210,6 +218,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/canary/SKILL.md b/canary/SKILL.md index 32cacd51..6cf76203 100644 --- a/canary/SKILL.md +++ b/canary/SKILL.md @@ -82,6 +82,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -210,6 +218,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/checkpoint/SKILL.md b/checkpoint/SKILL.md index 82ecb5e0..22b5d3ad 100644 --- a/checkpoint/SKILL.md +++ b/checkpoint/SKILL.md @@ -85,6 +85,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -213,6 +221,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/codex/SKILL.md b/codex/SKILL.md index bafdf7df..9b40b27e 100644 --- a/codex/SKILL.md +++ b/codex/SKILL.md @@ -84,6 +84,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -212,6 +220,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/cso/SKILL.md b/cso/SKILL.md index 58a65c8b..89f2b13f 100644 --- a/cso/SKILL.md +++ b/cso/SKILL.md @@ -87,6 +87,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -215,6 +223,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/design-consultation/SKILL.md b/design-consultation/SKILL.md index a8b512b5..68e48879 100644 --- a/design-consultation/SKILL.md +++ b/design-consultation/SKILL.md @@ -87,6 +87,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -215,6 +223,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/design-html/SKILL.md b/design-html/SKILL.md index cc5aebcd..10aaece0 100644 --- a/design-html/SKILL.md +++ b/design-html/SKILL.md @@ -89,6 +89,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -217,6 +225,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/design-review/SKILL.md b/design-review/SKILL.md index 684177b1..b87c509d 100644 --- a/design-review/SKILL.md +++ b/design-review/SKILL.md @@ -87,6 +87,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -215,6 +223,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/design-shotgun/SKILL.md b/design-shotgun/SKILL.md index 03223146..d254d9d2 100644 --- a/design-shotgun/SKILL.md +++ b/design-shotgun/SKILL.md @@ -84,6 +84,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -212,6 +220,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/devex-review/SKILL.md b/devex-review/SKILL.md index 70cc3fdf..96575fea 100644 --- a/devex-review/SKILL.md +++ b/devex-review/SKILL.md @@ -87,6 +87,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -215,6 +223,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/document-release/SKILL.md b/document-release/SKILL.md index f4dde2bf..90b84d2d 100644 --- a/document-release/SKILL.md +++ b/document-release/SKILL.md @@ -84,6 +84,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -212,6 +220,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/health/SKILL.md b/health/SKILL.md index 801b2ef4..f8f7b2ae 100644 --- a/health/SKILL.md +++ b/health/SKILL.md @@ -84,6 +84,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -212,6 +220,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/investigate/SKILL.md b/investigate/SKILL.md index ab5b2231..30feccd0 100644 --- a/investigate/SKILL.md +++ b/investigate/SKILL.md @@ -99,6 +99,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -227,6 +235,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/land-and-deploy/SKILL.md b/land-and-deploy/SKILL.md index af239456..64402009 100644 --- a/land-and-deploy/SKILL.md +++ b/land-and-deploy/SKILL.md @@ -81,6 +81,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -209,6 +217,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/learn/SKILL.md b/learn/SKILL.md index 28375e5a..656ae76b 100644 --- a/learn/SKILL.md +++ b/learn/SKILL.md @@ -84,6 +84,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -212,6 +220,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/office-hours/SKILL.md b/office-hours/SKILL.md index 289ae646..9795f1e5 100644 --- a/office-hours/SKILL.md +++ b/office-hours/SKILL.md @@ -91,6 +91,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -219,6 +227,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/open-gstack-browser/SKILL.md b/open-gstack-browser/SKILL.md index fad2736b..126bd5fb 100644 --- a/open-gstack-browser/SKILL.md +++ b/open-gstack-browser/SKILL.md @@ -81,6 +81,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -209,6 +217,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/plan-ceo-review/SKILL.md b/plan-ceo-review/SKILL.md index 716ee652..78e87f4d 100644 --- a/plan-ceo-review/SKILL.md +++ b/plan-ceo-review/SKILL.md @@ -87,6 +87,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -215,6 +223,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/plan-design-review/SKILL.md b/plan-design-review/SKILL.md index 0779ee2c..bc9a1d16 100644 --- a/plan-design-review/SKILL.md +++ b/plan-design-review/SKILL.md @@ -85,6 +85,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -213,6 +221,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/plan-devex-review/SKILL.md b/plan-devex-review/SKILL.md index 4df9a521..56a51ba2 100644 --- a/plan-devex-review/SKILL.md +++ b/plan-devex-review/SKILL.md @@ -89,6 +89,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -217,6 +225,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/plan-eng-review/SKILL.md b/plan-eng-review/SKILL.md index 1634f28f..93f71bd7 100644 --- a/plan-eng-review/SKILL.md +++ b/plan-eng-review/SKILL.md @@ -87,6 +87,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -215,6 +223,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/qa-only/SKILL.md b/qa-only/SKILL.md index a34932b5..f1eeedff 100644 --- a/qa-only/SKILL.md +++ b/qa-only/SKILL.md @@ -83,6 +83,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -211,6 +219,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/qa/SKILL.md b/qa/SKILL.md index fcffb10f..edb475c9 100644 --- a/qa/SKILL.md +++ b/qa/SKILL.md @@ -89,6 +89,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -217,6 +225,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/retro/SKILL.md b/retro/SKILL.md index cbbabdc5..b2f43419 100644 --- a/retro/SKILL.md +++ b/retro/SKILL.md @@ -82,6 +82,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -210,6 +218,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/review/SKILL.md b/review/SKILL.md index 9bf514e2..9e2965db 100644 --- a/review/SKILL.md +++ b/review/SKILL.md @@ -85,6 +85,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -213,6 +221,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/scripts/resolvers/preamble.ts b/scripts/resolvers/preamble.ts index 732a51a4..bacbc0f0 100644 --- a/scripts/resolvers/preamble.ts +++ b/scripts/resolvers/preamble.ts @@ -89,6 +89,14 @@ fi _ROUTING_DECLINED=$(${ctx.paths.binDir}/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true \`\`\``; @@ -228,6 +236,40 @@ Say "No problem. You can add routing rules later by running \`gstack-config set This only happens once per project. If \`HAS_ROUTING\` is \`yes\` or \`ROUTING_DECLINED\` is \`true\`, skip this entirely.`; } +function generateVendoringDeprecation(ctx: TemplateContext): string { + return `If \`VENDORED_GSTACK\` is \`yes\`: This project has a vendored copy of gstack at +\`.claude/skills/gstack/\`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for \`~/.gstack/.vendoring-warned-$SLUG\` marker): + +> This project has gstack vendored in \`.claude/skills/gstack/\`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run \`git rm -r .claude/skills/gstack/\` +2. Run \`echo '.claude/skills/gstack/' >> .gitignore\` +3. Run \`${ctx.paths.binDir}/gstack-team-init required\` (or \`optional\`) +4. Run \`git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"\` +5. Tell the user: "Done. Each developer now runs: \`cd ~/.claude/skills/gstack && ./setup --team\`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +\`\`\`bash +eval "$(${ctx.paths.binDir}/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-\${SLUG:-unknown} +\`\`\` + +This only happens once per project. If the marker file exists, skip entirely.`; +} + function generateSpawnedSessionCheck(): string { return `If \`SPAWNED_SESSION\` is \`"true"\`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: @@ -686,6 +728,7 @@ export function generatePreamble(ctx: TemplateContext): string { generateTelemetryPrompt(ctx), generateProactivePrompt(ctx), generateRoutingInjection(ctx), + generateVendoringDeprecation(ctx), generateSpawnedSessionCheck(), generateVoiceDirective(tier), ...(tier >= 2 ? [generateContextRecovery(ctx), generateAskUserFormat(ctx), generateCompletenessSection()] : []), diff --git a/setup b/setup index fd298140..65da9496 100755 --- a/setup +++ b/setup @@ -28,11 +28,17 @@ case "$(uname -s)" in MINGW*|MSYS*|CYGWIN*|Windows_NT) IS_WINDOWS=1 ;; esac +# ─── Quiet mode helper ──────────────────────────────────────── +QUIET=0 +log() { [ "$QUIET" -eq 0 ] && echo "$@" || true; } + # ─── Parse flags ────────────────────────────────────────────── HOST="claude" LOCAL_INSTALL=0 SKILL_PREFIX=1 SKILL_PREFIX_FLAG=0 +TEAM_MODE=0 +NO_TEAM_MODE=0 while [ $# -gt 0 ]; do case "$1" in --host) [ -z "$2" ] && echo "Missing value for --host (expected claude, codex, kiro, or auto)" >&2 && exit 1; HOST="$2"; shift 2 ;; @@ -40,6 +46,9 @@ while [ $# -gt 0 ]; do --local) LOCAL_INSTALL=1; shift ;; --prefix) SKILL_PREFIX=1; SKILL_PREFIX_FLAG=1; shift ;; --no-prefix) SKILL_PREFIX=0; SKILL_PREFIX_FLAG=1; shift ;; + --team) TEAM_MODE=1; shift ;; + --no-team) NO_TEAM_MODE=1; shift ;; + -q|--quiet) QUIET=1; shift ;; *) shift ;; esac done @@ -72,8 +81,10 @@ if [ "$SKILL_PREFIX_FLAG" -eq 0 ]; then elif [ "$_saved_prefix" = "false" ]; then SKILL_PREFIX=0 else - # No saved preference — prompt interactively (or default flat for non-TTY) - if [ -t 0 ]; then + # No saved preference — prompt interactively (or default flat for non-TTY/quiet) + if [ "$QUIET" -eq 1 ]; then + SKILL_PREFIX=0 + elif [ -t 0 ]; then echo "" echo "Skill naming: how should gstack skills appear?" echo "" @@ -100,8 +111,10 @@ else "$GSTACK_CONFIG" set skill_prefix "$([ "$SKILL_PREFIX" -eq 1 ] && echo true || echo false)" 2>/dev/null || true fi -# --local: install to .claude/skills/ in the current working directory +# --local: install to .claude/skills/ in the current working directory (deprecated) if [ "$LOCAL_INSTALL" -eq 1 ]; then + echo "Warning: --local is deprecated. Use global install + --team instead." >&2 + echo " See: https://github.com/garrytan/gstack#team-mode" >&2 if [ "$HOST" = "codex" ]; then echo "Error: --local is only supported for Claude Code (not Codex)." >&2 exit 1 @@ -151,7 +164,7 @@ migrate_direct_codex_install() { exit 1 fi - echo "Migrating direct Codex install to $migrated_dir to avoid duplicate skill discovery..." + log "Migrating direct Codex install to $migrated_dir to avoid duplicate skill discovery..." mv "$gstack_dir" "$migrated_dir" SOURCE_GSTACK_DIR="$migrated_dir" INSTALL_GSTACK_DIR="$migrated_dir" @@ -192,7 +205,7 @@ elif [ -f "$SOURCE_GSTACK_DIR/bun.lock" ] && [ "$SOURCE_GSTACK_DIR/bun.lock" -nt fi if [ "$NEEDS_BUILD" -eq 1 ]; then - echo "Building browse binary..." + log "Building browse binary..." ( cd "$SOURCE_GSTACK_DIR" bun install @@ -218,7 +231,7 @@ AGENTS_DIR="$SOURCE_GSTACK_DIR/.agents/skills" NEEDS_AGENTS_GEN=1 if [ "$NEEDS_AGENTS_GEN" -eq 1 ] && [ "$NEEDS_BUILD" -eq 0 ]; then - echo "Generating .agents/ skill docs..." + log "Generating .agents/ skill docs..." ( cd "$SOURCE_GSTACK_DIR" bun install --frozen-lockfile 2>/dev/null || bun install @@ -228,7 +241,7 @@ fi # 1c. Generate .factory/ Factory Droid skill docs if [ "$INSTALL_FACTORY" -eq 1 ] && [ "$NEEDS_BUILD" -eq 0 ]; then - echo "Generating .factory/ skill docs..." + log "Generating .factory/ skill docs..." ( cd "$SOURCE_GSTACK_DIR" bun install --frozen-lockfile 2>/dev/null || bun install @@ -625,16 +638,16 @@ if [ "$INSTALL_CLAUDE" -eq 1 ]; then ln -snf "gstack/open-gstack-browser" "$_OGB_LINK" fi if [ "$LOCAL_INSTALL" -eq 1 ]; then - echo "gstack ready (project-local)." - echo " skills: $INSTALL_SKILLS_DIR" + log "gstack ready (project-local)." + log " skills: $INSTALL_SKILLS_DIR" else - echo "gstack ready (claude)." + log "gstack ready (claude)." fi - echo " browse: $BROWSE_BIN" + log " browse: $BROWSE_BIN" else - echo "gstack ready (claude)." - echo " browse: $BROWSE_BIN" - echo " (skipped skill symlinks — not inside .claude/skills/)" + log "gstack ready (claude)." + log " browse: $BROWSE_BIN" + log " (skipped skill symlinks — not inside .claude/skills/)" fi fi @@ -654,9 +667,9 @@ if [ "$INSTALL_CODEX" -eq 1 ]; then # Install generated Codex-format skills (not Claude source dirs) link_codex_skill_dirs "$SOURCE_GSTACK_DIR" "$CODEX_SKILLS" - echo "gstack ready (codex)." - echo " browse: $BROWSE_BIN" - echo " codex skills: $CODEX_SKILLS" + log "gstack ready (codex)." + log " browse: $BROWSE_BIN" + log " codex skills: $CODEX_SKILLS" fi # 6. Install for Kiro CLI (copy from .agents/skills, rewrite paths) @@ -761,7 +774,39 @@ fi # 9. First-time welcome + legacy cleanup if [ ! -f "$HOME/.gstack/.welcome-seen" ]; then - echo " Welcome! Run /gstack-upgrade anytime to stay current." + log " Welcome! Run /gstack-upgrade anytime to stay current." touch "$HOME/.gstack/.welcome-seen" fi rm -f /tmp/gstack-latest-version + +# 10. Team mode: register/unregister SessionStart hook +SETTINGS_HOOK="$SOURCE_GSTACK_DIR/bin/gstack-settings-hook" +HOOK_CMD="$SOURCE_GSTACK_DIR/bin/gstack-session-update" + +if [ "$TEAM_MODE" -eq 1 ]; then + "$GSTACK_CONFIG" set auto_upgrade true 2>/dev/null || true + + # Register SessionStart hook in Claude Code settings + if [ -x "$SETTINGS_HOOK" ]; then + "$SETTINGS_HOOK" add "$HOOK_CMD" 2>/dev/null || true + fi + + log "" + log "Team mode enabled: gstack will auto-update at the start of each Claude Code session." + log " Hook: $HOOK_CMD" + log " To disable: ./setup --no-team" + log "" + log "Bootstrap your repo:" + log " cd && $SOURCE_GSTACK_DIR/bin/gstack-team-init required" +fi + +if [ "$NO_TEAM_MODE" -eq 1 ]; then + "$GSTACK_CONFIG" set auto_upgrade false 2>/dev/null || true + + # Remove SessionStart hook from Claude Code settings + if [ -x "$SETTINGS_HOOK" ]; then + "$SETTINGS_HOOK" remove "$HOOK_CMD" 2>/dev/null || true + fi + + log "Team mode disabled: auto-update hook removed." +fi diff --git a/setup-browser-cookies/SKILL.md b/setup-browser-cookies/SKILL.md index 717d6405..8a369d0e 100644 --- a/setup-browser-cookies/SKILL.md +++ b/setup-browser-cookies/SKILL.md @@ -79,6 +79,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -207,6 +215,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/setup-deploy/SKILL.md b/setup-deploy/SKILL.md index 6791b956..41ba613e 100644 --- a/setup-deploy/SKILL.md +++ b/setup-deploy/SKILL.md @@ -85,6 +85,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -213,6 +221,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/ship/SKILL.md b/ship/SKILL.md index 05fff987..f3bfd626 100644 --- a/ship/SKILL.md +++ b/ship/SKILL.md @@ -86,6 +86,14 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" +# Vendoring deprecation: detect if CWD has a vendored gstack copy +_VENDORED="no" +if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then + if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then + _VENDORED="yes" + fi +fi +echo "VENDORED_GSTACK: $_VENDORED" # Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` @@ -214,6 +222,38 @@ Say "No problem. You can add routing rules later by running `gstack-config set r This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at +`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies +up to date, so this project's gstack will fall behind. + +Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): + +> This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. +> We won't keep this copy up to date, so you'll fall behind on new features and fixes. +> +> Want to migrate to team mode? It takes about 30 seconds. + +Options: +- A) Yes, migrate to team mode now +- B) No, I'll handle it myself + +If A: +1. Run `git rm -r .claude/skills/gstack/` +2. Run `echo '.claude/skills/gstack/' >> .gitignore` +3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`) +4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"` +5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`" + +If B: say "OK, you're on your own to keep the vendored copy up to date." + +Always run (regardless of choice): +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true +touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} +``` + +This only happens once per project. If the marker file exists, skip entirely. + If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: - Do NOT use AskUserQuestion for interactive prompts. Auto-choose the recommended option. diff --git a/test/team-mode.test.ts b/test/team-mode.test.ts new file mode 100644 index 00000000..e2b030fb --- /dev/null +++ b/test/team-mode.test.ts @@ -0,0 +1,276 @@ +import { describe, test, expect, beforeEach, afterEach } from 'bun:test'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; +import { execSync } from 'child_process'; + +const ROOT = path.resolve(import.meta.dir, '..'); +const SETTINGS_HOOK = path.join(ROOT, 'bin', 'gstack-settings-hook'); +const SESSION_UPDATE = path.join(ROOT, 'bin', 'gstack-session-update'); +const TEAM_INIT = path.join(ROOT, 'bin', 'gstack-team-init'); + +function mkTmpDir(): string { + return fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-team-test-')); +} + +function run(cmd: string, opts: { cwd?: string; env?: Record } = {}): { stdout: string; stderr: string; exitCode: number } { + try { + const stdout = execSync(cmd, { + cwd: opts.cwd, + env: { ...process.env, ...opts.env }, + encoding: 'utf-8', + timeout: 10000, + }); + return { stdout, stderr: '', exitCode: 0 }; + } catch (e: any) { + return { stdout: e.stdout || '', stderr: e.stderr || '', exitCode: e.status ?? 1 }; + } +} + +describe('gstack-settings-hook', () => { + let tmpDir: string; + let settingsFile: string; + + beforeEach(() => { + tmpDir = mkTmpDir(); + settingsFile = path.join(tmpDir, 'settings.json'); + }); + + afterEach(() => { + fs.rmSync(tmpDir, { recursive: true, force: true }); + }); + + test('add creates settings.json if missing', () => { + const result = run(`${SETTINGS_HOOK} add /path/to/gstack-session-update`, { + env: { GSTACK_SETTINGS_FILE: settingsFile }, + }); + expect(result.exitCode).toBe(0); + const settings = JSON.parse(fs.readFileSync(settingsFile, 'utf-8')); + expect(settings.hooks.SessionStart).toHaveLength(1); + expect(settings.hooks.SessionStart[0].hooks[0].command).toBe('/path/to/gstack-session-update'); + }); + + test('add preserves existing settings', () => { + fs.writeFileSync(settingsFile, JSON.stringify({ effortLevel: 'high', permissions: { defaultMode: 'auto' } }, null, 2)); + const result = run(`${SETTINGS_HOOK} add /path/to/gstack-session-update`, { + env: { GSTACK_SETTINGS_FILE: settingsFile }, + }); + expect(result.exitCode).toBe(0); + const settings = JSON.parse(fs.readFileSync(settingsFile, 'utf-8')); + expect(settings.effortLevel).toBe('high'); + expect(settings.permissions.defaultMode).toBe('auto'); + expect(settings.hooks.SessionStart).toHaveLength(1); + }); + + test('add deduplicates (running twice does not double-add)', () => { + run(`${SETTINGS_HOOK} add /path/to/gstack-session-update`, { + env: { GSTACK_SETTINGS_FILE: settingsFile }, + }); + run(`${SETTINGS_HOOK} add /path/to/gstack-session-update`, { + env: { GSTACK_SETTINGS_FILE: settingsFile }, + }); + const settings = JSON.parse(fs.readFileSync(settingsFile, 'utf-8')); + expect(settings.hooks.SessionStart).toHaveLength(1); + }); + + test('remove removes the hook', () => { + run(`${SETTINGS_HOOK} add /path/to/gstack-session-update`, { + env: { GSTACK_SETTINGS_FILE: settingsFile }, + }); + const result = run(`${SETTINGS_HOOK} remove /path/to/gstack-session-update`, { + env: { GSTACK_SETTINGS_FILE: settingsFile }, + }); + expect(result.exitCode).toBe(0); + const settings = JSON.parse(fs.readFileSync(settingsFile, 'utf-8')); + expect(settings.hooks).toBeUndefined(); + }); + + test('remove is safe when settings.json does not exist', () => { + const result = run(`${SETTINGS_HOOK} remove /path/to/gstack-session-update`, { + env: { GSTACK_SETTINGS_FILE: settingsFile }, + }); + expect(result.exitCode).toBe(0); + }); + + test('remove preserves other hooks', () => { + fs.writeFileSync(settingsFile, JSON.stringify({ + hooks: { + SessionStart: [ + { hooks: [{ type: 'command', command: '/path/to/gstack-session-update' }] }, + { hooks: [{ type: 'command', command: '/other/hook' }] }, + ], + }, + }, null, 2)); + run(`${SETTINGS_HOOK} remove /path/to/gstack-session-update`, { + env: { GSTACK_SETTINGS_FILE: settingsFile }, + }); + const settings = JSON.parse(fs.readFileSync(settingsFile, 'utf-8')); + expect(settings.hooks.SessionStart).toHaveLength(1); + expect(settings.hooks.SessionStart[0].hooks[0].command).toBe('/other/hook'); + }); + + test('atomic write (no partial file on success)', () => { + run(`${SETTINGS_HOOK} add /path/to/gstack-session-update`, { + env: { GSTACK_SETTINGS_FILE: settingsFile }, + }); + // .tmp file should not exist after successful write + expect(fs.existsSync(settingsFile + '.tmp')).toBe(false); + // File should be valid JSON + expect(() => JSON.parse(fs.readFileSync(settingsFile, 'utf-8'))).not.toThrow(); + }); +}); + +describe('gstack-session-update', () => { + let tmpDir: string; + let gstackDir: string; + let stateDir: string; + + beforeEach(() => { + tmpDir = mkTmpDir(); + gstackDir = path.join(tmpDir, 'gstack'); + stateDir = path.join(tmpDir, 'state'); + fs.mkdirSync(gstackDir, { recursive: true }); + fs.mkdirSync(stateDir, { recursive: true }); + + // Init a git repo to pass the .git guard + execSync('git init', { cwd: gstackDir }); + execSync('git commit --allow-empty -m "init"', { cwd: gstackDir }); + fs.writeFileSync(path.join(gstackDir, 'VERSION'), '0.1.0'); + + // Create a minimal gstack-config that returns auto_upgrade=true + const binDir = path.join(gstackDir, 'bin'); + fs.mkdirSync(binDir, { recursive: true }); + fs.writeFileSync(path.join(binDir, 'gstack-config'), '#!/bin/bash\necho "true"'); + fs.chmodSync(path.join(binDir, 'gstack-config'), 0o755); + }); + + afterEach(() => { + fs.rmSync(tmpDir, { recursive: true, force: true }); + }); + + test('exits 0 when .git is missing', () => { + fs.rmSync(path.join(gstackDir, '.git'), { recursive: true }); + const result = run(SESSION_UPDATE, { + env: { GSTACK_DIR: gstackDir, GSTACK_STATE_DIR: stateDir }, + }); + expect(result.exitCode).toBe(0); + }); + + test('exits 0 when auto_upgrade is not true', () => { + // Override gstack-config to return false + fs.writeFileSync(path.join(gstackDir, 'bin', 'gstack-config'), '#!/bin/bash\necho "false"'); + const result = run(SESSION_UPDATE, { + env: { GSTACK_DIR: gstackDir, GSTACK_STATE_DIR: stateDir }, + }); + expect(result.exitCode).toBe(0); + }); + + test('throttle: skips when checked recently', () => { + // Write a recent throttle timestamp + const throttleFile = path.join(stateDir, '.last-session-update'); + fs.writeFileSync(throttleFile, String(Math.floor(Date.now() / 1000))); + + const result = run(SESSION_UPDATE, { + env: { GSTACK_DIR: gstackDir, GSTACK_STATE_DIR: stateDir }, + }); + expect(result.exitCode).toBe(0); + // No log file should be created (throttled before forking) + }); + + test('always exits 0 (non-fatal)', () => { + // Even with a broken setup, should exit 0 + const result = run(SESSION_UPDATE, { + env: { GSTACK_DIR: '/nonexistent/path', GSTACK_STATE_DIR: stateDir }, + }); + expect(result.exitCode).toBe(0); + }); +}); + +describe('gstack-team-init', () => { + let tmpDir: string; + + beforeEach(() => { + tmpDir = mkTmpDir(); + execSync('git init', { cwd: tmpDir }); + execSync('git commit --allow-empty -m "init"', { cwd: tmpDir }); + }); + + afterEach(() => { + fs.rmSync(tmpDir, { recursive: true, force: true }); + }); + + test('errors without a mode argument', () => { + const result = run(TEAM_INIT, { cwd: tmpDir }); + expect(result.exitCode).not.toBe(0); + expect(result.stderr).toContain('Usage'); + }); + + test('errors outside a git repo', () => { + const nonGitDir = mkTmpDir(); + const result = run(`${TEAM_INIT} optional`, { cwd: nonGitDir }); + expect(result.exitCode).not.toBe(0); + expect(result.stderr).toContain('not in a git repository'); + fs.rmSync(nonGitDir, { recursive: true, force: true }); + }); + + test('optional: creates CLAUDE.md with recommended section', () => { + const result = run(`${TEAM_INIT} optional`, { cwd: tmpDir }); + expect(result.exitCode).toBe(0); + const claude = fs.readFileSync(path.join(tmpDir, 'CLAUDE.md'), 'utf-8'); + expect(claude).toContain('## gstack (recommended)'); + expect(claude).toContain('./setup --team'); + }); + + test('required: creates CLAUDE.md with required section', () => { + const result = run(`${TEAM_INIT} required`, { cwd: tmpDir }); + expect(result.exitCode).toBe(0); + const claude = fs.readFileSync(path.join(tmpDir, 'CLAUDE.md'), 'utf-8'); + expect(claude).toContain('## gstack (REQUIRED'); + expect(claude).toContain('GSTACK_MISSING'); + }); + + test('required: creates enforcement hook', () => { + run(`${TEAM_INIT} required`, { cwd: tmpDir }); + const hookPath = path.join(tmpDir, '.claude', 'hooks', 'check-gstack.sh'); + expect(fs.existsSync(hookPath)).toBe(true); + const hook = fs.readFileSync(hookPath, 'utf-8'); + expect(hook).toContain('BLOCKED: gstack is not installed'); + // Should be executable + const stat = fs.statSync(hookPath); + expect(stat.mode & 0o111).toBeGreaterThan(0); + }); + + test('required: creates project settings.json with PreToolUse hook', () => { + run(`${TEAM_INIT} required`, { cwd: tmpDir }); + const settingsPath = path.join(tmpDir, '.claude', 'settings.json'); + expect(fs.existsSync(settingsPath)).toBe(true); + const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8')); + expect(settings.hooks.PreToolUse).toHaveLength(1); + expect(settings.hooks.PreToolUse[0].matcher).toBe('Skill'); + expect(settings.hooks.PreToolUse[0].hooks[0].command).toContain('check-gstack'); + }); + + test('idempotent: running twice does not duplicate CLAUDE.md section', () => { + run(`${TEAM_INIT} optional`, { cwd: tmpDir }); + run(`${TEAM_INIT} optional`, { cwd: tmpDir }); + const claude = fs.readFileSync(path.join(tmpDir, 'CLAUDE.md'), 'utf-8'); + const matches = claude.match(/## gstack/g); + expect(matches).toHaveLength(1); + }); +}); + +describe('setup --team / --no-team / -q', () => { + test('setup -q produces no stdout', () => { + const result = run(`${path.join(ROOT, 'setup')} -q`, { cwd: ROOT }); + // -q should suppress informational output (may still have some output from build) + // The key test is that the "Skill naming:" prompt and "gstack ready" messages are suppressed + expect(result.stdout).not.toContain('Skill naming:'); + expect(result.stdout).not.toContain('gstack ready'); + }); + + test('setup --local prints deprecation warning', () => { + // stderr capture: run via bash redirect so we can capture stderr + const result = run(`bash -c '${path.join(ROOT, 'setup')} --local -q 2>&1'`, { cwd: ROOT }); + expect(result.stdout).toContain('deprecated'); + }); +});