diff --git a/SKILL.md b/SKILL.md index d63e8a835..717c97816 100644 --- a/SKILL.md +++ b/SKILL.md @@ -36,6 +36,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) diff --git a/autoplan/SKILL.md b/autoplan/SKILL.md index ba72af72f..cd65b4c0d 100644 --- a/autoplan/SKILL.md +++ b/autoplan/SKILL.md @@ -45,6 +45,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) @@ -568,9 +573,11 @@ Execute every other section at full depth. When the loaded skill's instructions After /office-hours completes, re-run the design doc check: ```bash setopt +o nomatch 2>/dev/null || true # zsh compat -SLUG=$(~/.claude/skills/gstack/browse/bin/remote-slug 2>/dev/null || basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)") -BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-' || echo 'no-branch') -DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" +DESIGN=$(ls -t "$PROJECT_DATA_DIR"/designs/*-$BRANCH-design-*.md 2>/dev/null | head -1) +[ -z "$DESIGN" ] && DESIGN=$(ls -t "$PROJECT_DATA_DIR"/designs/*-design-*.md 2>/dev/null | head -1) +# Fallback: legacy global path +[ -z "$DESIGN" ] && DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) [ -z "$DESIGN" ] && DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null | head -1) [ -n "$DESIGN" ] && echo "Design doc found: $DESIGN" || echo "No design doc found" ``` @@ -708,10 +715,10 @@ instructions instead of reviewing the plan. Before doing anything, save the plan file's current state to an external file: ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR" BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-') DATETIME=$(date +%Y%m%d-%H%M%S) -echo "RESTORE_PATH=$HOME/.gstack/projects/$SLUG/${BRANCH}-autoplan-restore-${DATETIME}.md" +echo "RESTORE_PATH=$PROJECT_DATA_DIR/plans/${BRANCH}-autoplan-restore-${DATETIME}.md" ``` Write the plan file's full contents to the restore path with this header: @@ -733,7 +740,7 @@ Then prepend a one-line HTML comment to the plan file: ### Step 2: Read context - Read CLAUDE.md, TODOS.md, git log -30, git diff against the base branch --stat -- Discover design docs: `ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null | head -1` +- Discover design docs: `ls -t "$PROJECT_DATA_DIR"/designs/*-design-*.md 2>/dev/null || ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null | head -1` - Detect UI scope: grep the plan for view/rendering terms (component, screen, form, button, modal, layout, dashboard, sidebar, nav, dialog). Require 2+ matches. Exclude false positives ("page" alone, "UI" in acronyms). @@ -1004,7 +1011,7 @@ Override: every AskUserQuestion → auto-decide using the 6 principles. - Architecture choices: explicit over clever (P5). If codex disagrees with valid reason → TASTE DECISION. Scope changes both models agree on → USER CHALLENGE. - Evals: always include all relevant suites (P1) -- Test plan: generate artifact at `~/.gstack/projects/$SLUG/{user}-{branch}-test-plan-{datetime}.md` +- Test plan: generate artifact at `.gstack/plans/{user}-{branch}-test-plan-{datetime}.md` - TODOS.md: collect all deferred scope expansions from Phase 1, auto-write **Required execution checklist (Eng):** @@ -1108,7 +1115,7 @@ produced. Check the plan file and conversation for each item. - [ ] Scope challenge with actual code analysis (not just "scope is fine") - [ ] Architecture ASCII diagram produced - [ ] Test diagram mapping codepaths to test coverage -- [ ] Test plan artifact written to disk at ~/.gstack/projects/$SLUG/ +- [ ] Test plan artifact written to disk at .gstack/ - [ ] "NOT in scope" section written - [ ] "What already exists" section written - [ ] Failure modes registry with critical gap assessment diff --git a/autoplan/SKILL.md.tmpl b/autoplan/SKILL.md.tmpl index 41a1d4b34..3d4a702a9 100644 --- a/autoplan/SKILL.md.tmpl +++ b/autoplan/SKILL.md.tmpl @@ -162,7 +162,7 @@ Before doing anything, save the plan file's current state to an external file: {{SLUG_SETUP}} BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-') DATETIME=$(date +%Y%m%d-%H%M%S) -echo "RESTORE_PATH=$HOME/.gstack/projects/$SLUG/${BRANCH}-autoplan-restore-${DATETIME}.md" +echo "RESTORE_PATH=$PROJECT_DATA_DIR/plans/${BRANCH}-autoplan-restore-${DATETIME}.md" ``` Write the plan file's full contents to the restore path with this header: @@ -184,7 +184,7 @@ Then prepend a one-line HTML comment to the plan file: ### Step 2: Read context - Read CLAUDE.md, TODOS.md, git log -30, git diff against the base branch --stat -- Discover design docs: `ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null | head -1` +- Discover design docs: `ls -t "$PROJECT_DATA_DIR"/designs/*-design-*.md 2>/dev/null || ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null | head -1` - Detect UI scope: grep the plan for view/rendering terms (component, screen, form, button, modal, layout, dashboard, sidebar, nav, dialog). Require 2+ matches. Exclude false positives ("page" alone, "UI" in acronyms). @@ -455,7 +455,7 @@ Override: every AskUserQuestion → auto-decide using the 6 principles. - Architecture choices: explicit over clever (P5). If codex disagrees with valid reason → TASTE DECISION. Scope changes both models agree on → USER CHALLENGE. - Evals: always include all relevant suites (P1) -- Test plan: generate artifact at `~/.gstack/projects/$SLUG/{user}-{branch}-test-plan-{datetime}.md` +- Test plan: generate artifact at `.gstack/plans/{user}-{branch}-test-plan-{datetime}.md` - TODOS.md: collect all deferred scope expansions from Phase 1, auto-write **Required execution checklist (Eng):** @@ -559,7 +559,7 @@ produced. Check the plan file and conversation for each item. - [ ] Scope challenge with actual code analysis (not just "scope is fine") - [ ] Architecture ASCII diagram produced - [ ] Test diagram mapping codepaths to test coverage -- [ ] Test plan artifact written to disk at ~/.gstack/projects/$SLUG/ +- [ ] Test plan artifact written to disk at .gstack/ - [ ] "NOT in scope" section written - [ ] "What already exists" section written - [ ] Failure modes registry with critical gap assessment diff --git a/benchmark/SKILL.md b/benchmark/SKILL.md index ea0305be3..044db197f 100644 --- a/benchmark/SKILL.md +++ b/benchmark/SKILL.md @@ -38,6 +38,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) diff --git a/bin/gstack-global-discover b/bin/gstack-global-discover index ebffeeb9e..002ce4cf1 100755 Binary files a/bin/gstack-global-discover and b/bin/gstack-global-discover differ diff --git a/bin/gstack-migrate-local b/bin/gstack-migrate-local new file mode 100755 index 000000000..85552be99 --- /dev/null +++ b/bin/gstack-migrate-local @@ -0,0 +1,198 @@ +#!/usr/bin/env bash +# gstack-migrate-local — migrate + reorganize project data into .gstack/ subdirectories +# +# Handles two migration scenarios: +# 1. Legacy global: ~/.gstack/projects/$SLUG/* → .gstack/{designs,plans,...}/ +# 2. Local flat: .gstack/*-design-*.md → .gstack/designs/ (reorganize in place) +# +# Idempotent — safe to run multiple times. Non-destructive (skips existing files). +# +# Usage: gstack-migrate-local [--dry-run] +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +eval "$("$SCRIPT_DIR/gstack-slug" 2>/dev/null)" + +OLD="$HOME/.gstack/projects/$SLUG" +NEW="$PROJECT_DATA_DIR" +DRY_RUN=false +[ "${1:-}" = "--dry-run" ] && DRY_RUN=true + +MOVED=0 + +migrate() { + local src="$1" dst_dir="$2" + [ -e "$src" ] || return 0 + local name + name=$(basename "$src") + # Skip if source and destination are the same path + local abs_src abs_dst + abs_src=$(cd "$(dirname "$src")" && pwd)/$(basename "$src") + abs_dst="$dst_dir/$name" + [ "$abs_src" = "$abs_dst" ] && return 0 + if [ -e "$dst_dir/$name" ]; then + # Target exists — remove source duplicate if it's a file (dedup) + if [ -f "$src" ] && [ -f "$dst_dir/$name" ]; then + $DRY_RUN || rm -f "$src" + fi + return 0 + fi + if $DRY_RUN; then + echo " WOULD MOVE: $name → $dst_dir/" + else + mkdir -p "$dst_dir" + mv "$src" "$dst_dir/" + fi + MOVED=$((MOVED + 1)) +} + +migrate_dir() { + local src="$1" dst="$2" + [ -d "$src" ] || return 0 + if [ -d "$dst" ]; then + # Merge contents + for f in "$src"/*; do + [ -e "$f" ] && migrate "$f" "$dst" + done + $DRY_RUN || rmdir "$src" 2>/dev/null || true + else + if $DRY_RUN; then + echo " WOULD MOVE: $(basename "$src")/ → $(dirname "$dst")/" + else + mkdir -p "$(dirname "$dst")" + mv "$src" "$dst" + fi + MOVED=$((MOVED + 1)) + fi +} + +# ═══════════════════════════════════════════════════════════ +# Phase 1: Migrate from legacy global ~/.gstack/projects/$SLUG/ +# ═══════════════════════════════════════════════════════════ + +if [ -d "$OLD" ]; then + # Design docs + for f in "$OLD"/*-design-*.md "$OLD"/*-design-audit-*.md; do + [ -e "$f" ] && migrate "$f" "$NEW/designs" + done + + # Test plans, autoplan, eng review + for f in "$OLD"/*-test-plan-*.md "$OLD"/*-eng-review-*.md "$OLD"/*-autoplan-restore-*.md "$OLD"/*-ship-test-plan-*.md; do + [ -e "$f" ] && migrate "$f" "$NEW/plans" + done + + # CEO plans directory + [ -d "$OLD/ceo-plans" ] && migrate_dir "$OLD/ceo-plans" "$NEW/plans/ceo-plans" + + # Review logs + for f in "$OLD"/*-reviews.jsonl; do + [ -e "$f" ] && migrate "$f" "$NEW" + done + + # Test outcomes + for f in "$OLD"/*-test-outcome-*.md; do + [ -e "$f" ] && migrate "$f" "$NEW" + done + + # Evals directory + [ -d "$OLD/evals" ] && migrate_dir "$OLD/evals" "$NEW/evals" + + # Greptile history + [ -e "$OLD/greptile-history.md" ] && migrate "$OLD/greptile-history.md" "$NEW" + + # Deploy confirmation fingerprint + [ -e "$OLD/land-deploy-confirmed" ] && migrate "$OLD/land-deploy-confirmed" "$NEW" + + # CEO handoff files + for f in "$OLD"/*-ceo-handoff-*.md; do + [ -e "$f" ] && migrate "$f" "$NEW" + done + + # repo-mode.json → .gstack/local/ (machine-local cache) + [ -e "$OLD/repo-mode.json" ] && migrate "$OLD/repo-mode.json" "$NEW/local" + + # Clean up empty legacy directory + $DRY_RUN || rmdir "$OLD" 2>/dev/null || true +fi + +# ═══════════════════════════════════════════════════════════ +# Phase 2: Reorganize flat files in .gstack/ root → subdirectories +# ═══════════════════════════════════════════════════════════ + +if [ -d "$NEW" ]; then + # Design docs at root → designs/ + for f in "$NEW"/*-design-*.md "$NEW"/*-design-audit-*.md "$NEW"/*-architecture*.md; do + [ -e "$f" ] && migrate "$f" "$NEW/designs" + done + + # Test plans at root → plans/ + for f in "$NEW"/*-test-plan-*.md "$NEW"/*-eng-review-*.md "$NEW"/*-autoplan-restore-*.md "$NEW"/*-ship-test-plan-*.md; do + [ -e "$f" ] && migrate "$f" "$NEW/plans" + done + + # Implementation plans / roadmaps at root → plans/ + for f in "$NEW"/*-implementation-plan*.md "$NEW"/*-delivery-roadmap*.md; do + [ -e "$f" ] && migrate "$f" "$NEW/plans" + done + + # CEO plans at root → plans/ceo-plans/ + [ -d "$NEW/ceo-plans" ] && [ "$NEW/ceo-plans" != "$NEW/plans/ceo-plans" ] && \ + migrate_dir "$NEW/ceo-plans" "$NEW/plans/ceo-plans" + + # repo-mode.json at root → local/ + [ -e "$NEW/repo-mode.json" ] && migrate "$NEW/repo-mode.json" "$NEW/local" + + # browse.json at root → local/ (old browse daemon location) + [ -e "$NEW/browse.json" ] && migrate "$NEW/browse.json" "$NEW/local" + for f in "$NEW"/browse-console.log "$NEW"/browse-network.log "$NEW"/browse-dialog.log "$NEW"/browse-server.log; do + [ -e "$f" ] && migrate "$f" "$NEW/local" + done +fi + +# ═══════════════════════════════════════════════════════════ +# Phase 3: Ensure .gitignore and project .gitignore are correct +# ═══════════════════════════════════════════════════════════ + +if ! $DRY_RUN; then + mkdir -p "$NEW" + + # Ensure .gstack/.gitignore has local/ and .migrated + GITIGNORE="$NEW/.gitignore" + CONTENT="" + [ -f "$GITIGNORE" ] && CONTENT=$(cat "$GITIGNORE") + UPDATED=false + if ! echo "$CONTENT" | grep -q '^local/$' 2>/dev/null; then + CONTENT="${CONTENT:+$CONTENT +}local/" + UPDATED=true + fi + if ! echo "$CONTENT" | grep -q '^\.migrated$' 2>/dev/null; then + CONTENT="${CONTENT:+$CONTENT +}.migrated" + UPDATED=true + fi + $UPDATED && echo "$CONTENT" > "$GITIGNORE" + + # Remove blanket .gstack/ from project .gitignore + GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || echo "") + if [ -n "$GIT_ROOT" ] && [ -f "$GIT_ROOT/.gitignore" ]; then + if grep -q '^\.gstack/\?$' "$GIT_ROOT/.gitignore" 2>/dev/null; then + sed -i.bak '/^\.gstack\/\?$/d' "$GIT_ROOT/.gitignore" + rm -f "$GIT_ROOT/.gitignore.bak" + fi + fi +fi + +# ═══════════════════════════════════════════════════════════ +# Summary +# ═══════════════════════════════════════════════════════════ + +if [ "$MOVED" -eq 0 ]; then + exit 0 +fi + +if $DRY_RUN; then + echo "Dry run: $MOVED items would be migrated. Run without --dry-run to execute." +else + echo "gstack: migrated $MOVED items to $NEW" +fi diff --git a/bin/gstack-repo-mode b/bin/gstack-repo-mode index 0b4d6da64..c6ee94a65 100755 --- a/bin/gstack-repo-mode +++ b/bin/gstack-repo-mode @@ -34,7 +34,12 @@ if [ -n "$OVERRIDE" ] && [ "$OVERRIDE" != "null" ]; then fi # Check cache (7-day TTL) -CACHE_DIR="$HOME/.gstack/projects/$SLUG" +GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || echo "") +if [ -n "$GIT_ROOT" ]; then + CACHE_DIR="$GIT_ROOT/.gstack/local" +else + CACHE_DIR="$HOME/.gstack/projects/$SLUG" +fi CACHE_FILE="$CACHE_DIR/repo-mode.json" if [ -f "$CACHE_FILE" ]; then CACHE_AGE=$(( $(date +%s) - $(stat -f %m "$CACHE_FILE" 2>/dev/null || stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0) )) diff --git a/bin/gstack-review-log b/bin/gstack-review-log index 62c9e1719..8c7250d24 100755 --- a/bin/gstack-review-log +++ b/bin/gstack-review-log @@ -4,8 +4,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" eval "$("$SCRIPT_DIR/gstack-slug" 2>/dev/null)" -GSTACK_HOME="${GSTACK_HOME:-$HOME/.gstack}" -mkdir -p "$GSTACK_HOME/projects/$SLUG" +mkdir -p "$PROJECT_DATA_DIR" # Validate: input must be parseable JSON (reject malformed or injection attempts) INPUT="$1" @@ -15,4 +14,4 @@ if ! printf '%s' "$INPUT" | bun -e "JSON.parse(await Bun.stdin.text())" 2>/dev/n exit 1 fi -echo "$INPUT" >> "$GSTACK_HOME/projects/$SLUG/$BRANCH-reviews.jsonl" +echo "$INPUT" >> "$PROJECT_DATA_DIR/$BRANCH-reviews.jsonl" diff --git a/bin/gstack-review-read b/bin/gstack-review-read index ccf1d70f6..cb15040a9 100755 --- a/bin/gstack-review-read +++ b/bin/gstack-review-read @@ -4,8 +4,14 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" eval "$("$SCRIPT_DIR/gstack-slug" 2>/dev/null)" -GSTACK_HOME="${GSTACK_HOME:-$HOME/.gstack}" -cat "$GSTACK_HOME/projects/$SLUG/$BRANCH-reviews.jsonl" 2>/dev/null || echo "NO_REVIEWS" +# Try project-local first, fall back to legacy global path +if [ -f "$PROJECT_DATA_DIR/$BRANCH-reviews.jsonl" ]; then + cat "$PROJECT_DATA_DIR/$BRANCH-reviews.jsonl" +elif [ -f "$HOME/.gstack/projects/$SLUG/$BRANCH-reviews.jsonl" ]; then + cat "$HOME/.gstack/projects/$SLUG/$BRANCH-reviews.jsonl" +else + echo "NO_REVIEWS" +fi echo "---CONFIG---" "$SCRIPT_DIR/gstack-config" get skip_eng_review 2>/dev/null || echo "false" echo "---HEAD---" diff --git a/bin/gstack-slug b/bin/gstack-slug index baa1403f3..04e8f928b 100755 --- a/bin/gstack-slug +++ b/bin/gstack-slug @@ -14,5 +14,12 @@ BRANCH=$(printf '%s' "${RAW_BRANCH:-}" | tr -cd 'a-zA-Z0-9._-') # Fallback when git context is absent SLUG="${SLUG:-$(basename "$PWD" | tr -cd 'a-zA-Z0-9._-')}" BRANCH="${BRANCH:-unknown}" +GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || echo "") +if [ -n "$GIT_ROOT" ]; then + PROJECT_DATA_DIR="$GIT_ROOT/.gstack" +else + PROJECT_DATA_DIR="$HOME/.gstack/projects/$SLUG" +fi echo "SLUG=$SLUG" echo "BRANCH=$BRANCH" +echo "PROJECT_DATA_DIR=$PROJECT_DATA_DIR" diff --git a/browse/SKILL.md b/browse/SKILL.md index f9af93e5e..e8f8bc931 100644 --- a/browse/SKILL.md +++ b/browse/SKILL.md @@ -38,6 +38,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) diff --git a/browse/src/config.ts b/browse/src/config.ts index 04f166433..7258458be 100644 --- a/browse/src/config.ts +++ b/browse/src/config.ts @@ -59,7 +59,7 @@ export function resolveConfig( projectDir = path.dirname(stateDir); // parent of .gstack/ } else { projectDir = getGitRoot() || process.cwd(); - stateDir = path.join(projectDir, '.gstack'); + stateDir = path.join(projectDir, '.gstack', 'local'); stateFile = path.join(stateDir, 'browse.json'); } @@ -90,26 +90,34 @@ export function ensureStateDir(config: BrowseConfig): void { throw err; } - // Ensure .gstack/ is in the project's .gitignore - const gitignorePath = path.join(config.projectDir, '.gitignore'); + // Ensure .gstack/.gitignore exists with "local/" (machine-local state) + const gstackDir = path.resolve(config.stateDir, '..'); + const innerGitignore = path.join(gstackDir, '.gitignore'); try { - const content = fs.readFileSync(gitignorePath, 'utf-8'); - if (!content.match(/^\.gstack\/?$/m)) { - const separator = content.endsWith('\n') ? '' : '\n'; - fs.appendFileSync(gitignorePath, `${separator}.gstack/\n`); + let content = ''; + try { content = fs.readFileSync(innerGitignore, 'utf-8'); } catch { /* new file */ } + let updated = false; + if (!content.match(/^local\/?$/m)) { + const separator = content.length > 0 && !content.endsWith('\n') ? '\n' : ''; + content = content + separator + 'local/\n'; + updated = true; } - } catch (err: any) { - if (err.code !== 'ENOENT') { - // Write warning to server log (visible even in daemon mode) - const logPath = path.join(config.stateDir, 'browse-server.log'); - try { - fs.appendFileSync(logPath, `[${new Date().toISOString()}] Warning: could not update .gitignore at ${gitignorePath}: ${err.message}\n`); - } catch { - // stateDir write failed too — nothing more we can do - } + if (!content.match(/^\.migrated$/m)) { + content = content + '.migrated\n'; + updated = true; } - // ENOENT (no .gitignore) — skip silently - } + if (updated) fs.writeFileSync(innerGitignore, content); + } catch { /* non-fatal */ } + + // Migration: remove blanket .gstack/ from project .gitignore (if present) + const projectGitignore = path.join(config.projectDir, '.gitignore'); + try { + const content = fs.readFileSync(projectGitignore, 'utf-8'); + if (content.match(/^\.gstack\/?$/m)) { + const newContent = content.replace(/^\.gstack\/?$/m, '').replace(/\n{3,}/g, '\n\n'); + fs.writeFileSync(projectGitignore, newContent); + } + } catch { /* non-fatal — file may not exist or may be unwritable */ } } /** diff --git a/canary/SKILL.md b/canary/SKILL.md index b72a13eb3..da6befee6 100644 --- a/canary/SKILL.md +++ b/canary/SKILL.md @@ -38,6 +38,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) @@ -704,7 +709,7 @@ Log the result for the review dashboard: ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -mkdir -p ~/.gstack/projects/$SLUG +mkdir -p "$PROJECT_DATA_DIR" ``` Write a JSONL entry: `{"skill":"canary","timestamp":"","status":"","url":"","duration_min":,"alerts":}` diff --git a/canary/SKILL.md.tmpl b/canary/SKILL.md.tmpl index 412183040..de5647fc8 100644 --- a/canary/SKILL.md.tmpl +++ b/canary/SKILL.md.tmpl @@ -194,7 +194,7 @@ Log the result for the review dashboard: ```bash {{SLUG_EVAL}} -mkdir -p ~/.gstack/projects/$SLUG +mkdir -p "$PROJECT_DATA_DIR" ``` Write a JSONL entry: `{"skill":"canary","timestamp":"","status":"","url":"","duration_min":,"alerts":}` diff --git a/checkpoint/SKILL.md b/checkpoint/SKILL.md index baa40e1a0..8837ee540 100644 --- a/checkpoint/SKILL.md +++ b/checkpoint/SKILL.md @@ -41,6 +41,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) @@ -490,7 +495,7 @@ use it as the checkpoint title. Otherwise, infer a title from the current work. ### Step 1: Gather state ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR" ``` Collect the current working state: @@ -547,7 +552,7 @@ checkpoint file. ### Step 4: Write checkpoint file ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR" CHECKPOINT_DIR="$HOME/.gstack/projects/$SLUG/checkpoints" mkdir -p "$CHECKPOINT_DIR" TIMESTAMP=$(date +%Y%m%d-%H%M%S) @@ -614,7 +619,7 @@ Duration: {duration or "unknown"} ### Step 1: Find checkpoints ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR" CHECKPOINT_DIR="$HOME/.gstack/projects/$SLUG/checkpoints" if [ -d "$CHECKPOINT_DIR" ]; then find "$CHECKPOINT_DIR" -maxdepth 1 -name "*.md" -type f 2>/dev/null | xargs ls -1t 2>/dev/null | head -20 @@ -676,7 +681,7 @@ If A, summarize the first remaining work item and suggest starting there. ### Step 1: Gather checkpoints ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR" CHECKPOINT_DIR="$HOME/.gstack/projects/$SLUG/checkpoints" if [ -d "$CHECKPOINT_DIR" ]; then echo "CHECKPOINT_DIR=$CHECKPOINT_DIR" diff --git a/codex/SKILL.md b/codex/SKILL.md index 77384bdc5..03607cea7 100644 --- a/codex/SKILL.md +++ b/codex/SKILL.md @@ -39,6 +39,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) diff --git a/connect-chrome/SKILL.md b/connect-chrome/SKILL.md index 48970f807..f49bb7863 100644 --- a/connect-chrome/SKILL.md +++ b/connect-chrome/SKILL.md @@ -36,6 +36,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) diff --git a/cso/SKILL.md b/cso/SKILL.md index b4f093ad8..763eb23f1 100644 --- a/cso/SKILL.md +++ b/cso/SKILL.md @@ -42,6 +42,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) diff --git a/design-consultation/SKILL.md b/design-consultation/SKILL.md index 7052ba7d9..451156acf 100644 --- a/design-consultation/SKILL.md +++ b/design-consultation/SKILL.md @@ -43,6 +43,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) @@ -512,7 +517,7 @@ Look for office-hours output: ```bash setopt +o nomatch 2>/dev/null || true # zsh compat eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -ls ~/.gstack/projects/$SLUG/*office-hours* 2>/dev/null | head -5 +ls "$PROJECT_DATA_DIR/"*office-hours* 2>/dev/null || ls ~/.gstack/projects/$SLUG/*office-hours* 2>/dev/null | head -5 ls .context/*office-hours* .context/attachments/*office-hours* 2>/dev/null | head -5 ``` diff --git a/design-consultation/SKILL.md.tmpl b/design-consultation/SKILL.md.tmpl index 247b63e20..a679171a1 100644 --- a/design-consultation/SKILL.md.tmpl +++ b/design-consultation/SKILL.md.tmpl @@ -55,7 +55,7 @@ Look for office-hours output: ```bash setopt +o nomatch 2>/dev/null || true # zsh compat {{SLUG_EVAL}} -ls ~/.gstack/projects/$SLUG/*office-hours* 2>/dev/null | head -5 +ls "$PROJECT_DATA_DIR/"*office-hours* 2>/dev/null || ls ~/.gstack/projects/$SLUG/*office-hours* 2>/dev/null | head -5 ls .context/*office-hours* .context/attachments/*office-hours* 2>/dev/null | head -5 ``` diff --git a/design-html/SKILL.md b/design-html/SKILL.md index 100ed65cc..09201a101 100644 --- a/design-html/SKILL.md +++ b/design-html/SKILL.md @@ -44,6 +44,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) diff --git a/design-review/SKILL.md b/design-review/SKILL.md index b634d1879..5a37b1bba 100644 --- a/design-review/SKILL.md +++ b/design-review/SKILL.md @@ -43,6 +43,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) @@ -774,7 +779,7 @@ If `DESIGN_NOT_AVAILABLE`: skip mockup generation — the fix loop works without ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -REPORT_DIR=~/.gstack/projects/$SLUG/designs/design-audit-$(date +%Y%m%d) +REPORT_DIR="$PROJECT_DATA_DIR"/designs/design-audit-$(date +%Y%m%d) mkdir -p "$REPORT_DIR/screenshots" echo "REPORT_DIR: $REPORT_DIR" ``` @@ -1070,9 +1075,9 @@ Compare screenshots and observations across pages for: **Project-scoped:** ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR/designs" ``` -Write to: `~/.gstack/projects/{slug}/{user}-{branch}-design-audit-{datetime}.md` +Write to: `.gstack/designs/{user}-{branch}-design-audit-{datetime}.md` **Baseline:** Write `design-baseline.json` for regression mode: ```json @@ -1228,7 +1233,7 @@ Record baseline design score and AI slop score at end of Phase 6. ## Output Structure ``` -~/.gstack/projects/$SLUG/designs/design-audit-{YYYYMMDD}/ +"$PROJECT_DATA_DIR"/designs/design-audit-{YYYYMMDD}/ ├── design-audit-{domain}.md # Structured report ├── screenshots/ │ ├── first-impression.png # Phase 1 @@ -1454,9 +1459,9 @@ Write the report to `$REPORT_DIR` (already set up in the setup phase): **Also write a summary to the project index:** ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR" ``` -Write a one-line summary to `~/.gstack/projects/{slug}/{user}-{branch}-design-audit-{datetime}.md` with a pointer to the full report in `$REPORT_DIR`. +Write a one-line summary to `.gstack/{user}-{branch}-design-audit-{datetime}.md` with a pointer to the full report in `$REPORT_DIR`. **Per-finding additions** (beyond standard design audit report): - Fix Status: verified / best-effort / reverted / deferred diff --git a/design-review/SKILL.md.tmpl b/design-review/SKILL.md.tmpl index adca09913..c56f618df 100644 --- a/design-review/SKILL.md.tmpl +++ b/design-review/SKILL.md.tmpl @@ -90,7 +90,7 @@ If `DESIGN_NOT_AVAILABLE`: skip mockup generation — the fix loop works without ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -REPORT_DIR=~/.gstack/projects/$SLUG/designs/design-audit-$(date +%Y%m%d) +REPORT_DIR="$PROJECT_DATA_DIR"/designs/design-audit-$(date +%Y%m%d) mkdir -p "$REPORT_DIR/screenshots" echo "REPORT_DIR: $REPORT_DIR" ``` @@ -112,7 +112,7 @@ Record baseline design score and AI slop score at end of Phase 6. ## Output Structure ``` -~/.gstack/projects/$SLUG/designs/design-audit-{YYYYMMDD}/ +"$PROJECT_DATA_DIR"/designs/design-audit-{YYYYMMDD}/ ├── design-audit-{domain}.md # Structured report ├── screenshots/ │ ├── first-impression.png # Phase 1 @@ -260,7 +260,7 @@ Write the report to `$REPORT_DIR` (already set up in the setup phase): ```bash {{SLUG_SETUP}} ``` -Write a one-line summary to `~/.gstack/projects/{slug}/{user}-{branch}-design-audit-{datetime}.md` with a pointer to the full report in `$REPORT_DIR`. +Write a one-line summary to `.gstack/{user}-{branch}-design-audit-{datetime}.md` with a pointer to the full report in `$REPORT_DIR`. **Per-finding additions** (beyond standard design audit report): - Fix Status: verified / best-effort / reverted / deferred diff --git a/design-shotgun/SKILL.md b/design-shotgun/SKILL.md index 0f8f716e3..61428376f 100644 --- a/design-shotgun/SKILL.md +++ b/design-shotgun/SKILL.md @@ -40,6 +40,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) diff --git a/docs/skills.md b/docs/skills.md index e91a9da74..d07422307 100644 --- a/docs/skills.md +++ b/docs/skills.md @@ -93,7 +93,7 @@ Recommends A because you learn from real usage. CRM data comes naturally in week ### The design doc -Both modes end with a design doc written to `~/.gstack/projects/` — and that doc feeds directly into `/plan-ceo-review` and `/plan-eng-review`. The full lifecycle is now: `office-hours → plan → implement → review → QA → ship → retro`. +Both modes end with a design doc written to `.gstack/` — and that doc feeds directly into `/plan-ceo-review` and `/plan-eng-review`. The full lifecycle is now: `office-hours → plan → implement → review → QA → ship → retro`. After the design doc is approved, `/office-hours` reflects on what it noticed about how you think — not generic praise, but specific callbacks to things you said during the session. The observations appear in the design doc too, so you re-encounter them when you re-read later. @@ -147,7 +147,7 @@ It asks, **"what is the 10-star product hiding inside this request?"** - **HOLD SCOPE** — maximum rigor on the existing plan. No expansions surfaced. - **SCOPE REDUCTION** — find the minimum viable version. Cut everything else. -Visions and decisions are persisted to `~/.gstack/projects/` so they survive beyond the conversation. Exceptional visions can be promoted to `docs/designs/` in your repo for the team. +Visions and decisions are persisted to `.gstack/` so they survive beyond the conversation. Exceptional visions can be promoted to `docs/designs/` in your repo for the team. --- @@ -228,7 +228,7 @@ Eng Review is the only required gate (disable with `gstack-config set skip_eng_r ### Plan-to-QA flow -When `/plan-eng-review` finishes the test review section, it writes a test plan artifact to `~/.gstack/projects/`. When you later run `/qa`, it picks up that test plan automatically — your engineering review feeds directly into QA testing with no manual copy-paste. +When `/plan-eng-review` finishes the test review section, it writes a test plan artifact to `.gstack/`. When you later run `/qa`, it picks up that test plan automatically — your engineering review feeds directly into QA testing with no manual copy-paste. --- diff --git a/document-release/SKILL.md b/document-release/SKILL.md index e274cc28d..88797b6f4 100644 --- a/document-release/SKILL.md +++ b/document-release/SKILL.md @@ -40,6 +40,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) diff --git a/health/SKILL.md b/health/SKILL.md index 68ade8e20..a1f1be7ae 100644 --- a/health/SKILL.md +++ b/health/SKILL.md @@ -40,6 +40,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) @@ -637,7 +642,7 @@ DETAILS: Lint (3 warnings) ## Step 5: Persist to Health History ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR" ``` Append one JSONL line to `~/.gstack/projects/$SLUG/health-history.jsonl`: @@ -663,7 +668,7 @@ Read the last 10 entries from `~/.gstack/projects/$SLUG/health-history.jsonl` (i file exists and has prior entries). ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR" tail -10 ~/.gstack/projects/$SLUG/health-history.jsonl 2>/dev/null || echo "NO_HISTORY" ``` diff --git a/investigate/SKILL.md b/investigate/SKILL.md index 3f57ded9b..a68e8023e 100644 --- a/investigate/SKILL.md +++ b/investigate/SKILL.md @@ -55,6 +55,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) diff --git a/land-and-deploy/SKILL.md b/land-and-deploy/SKILL.md index 4a13ca100..dbce25c83 100644 --- a/land-and-deploy/SKILL.md +++ b/land-and-deploy/SKILL.md @@ -37,6 +37,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) @@ -634,11 +639,11 @@ and whether the deploy configuration has changed since then: ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -if [ ! -f ~/.gstack/projects/$SLUG/land-deploy-confirmed ]; then +if [ ! -f "$PROJECT_DATA_DIR/land-deploy-confirmed" ] && [ ! -f ~/.gstack/projects/$SLUG/land-deploy-confirmed ]; then echo "FIRST_RUN" else # Check if deploy config has changed since confirmation - SAVED_HASH=$(cat ~/.gstack/projects/$SLUG/land-deploy-confirmed 2>/dev/null) + SAVED_HASH=$(cat "$PROJECT_DATA_DIR/land-deploy-confirmed" 2>/dev/null || cat ~/.gstack/projects/$SLUG/land-deploy-confirmed 2>/dev/null) CURRENT_HASH=$(sed -n '/## Deploy Configuration/,/^## /p' CLAUDE.md 2>/dev/null | shasum -a 256 | cut -d' ' -f1) # Also hash workflow files that affect deploy behavior WORKFLOW_HASH=$(find .github/workflows -maxdepth 1 \( -name '*deploy*' -o -name '*cd*' \) 2>/dev/null | xargs cat 2>/dev/null | shasum -a 256 | cut -d' ' -f1) @@ -1469,7 +1474,7 @@ Log to the review dashboard: ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -mkdir -p ~/.gstack/projects/$SLUG +mkdir -p "$PROJECT_DATA_DIR" ``` Write a JSONL entry with timing data: diff --git a/land-and-deploy/SKILL.md.tmpl b/land-and-deploy/SKILL.md.tmpl index 9c01fc02b..fa574008d 100644 --- a/land-and-deploy/SKILL.md.tmpl +++ b/land-and-deploy/SKILL.md.tmpl @@ -107,11 +107,11 @@ and whether the deploy configuration has changed since then: ```bash {{SLUG_EVAL}} -if [ ! -f ~/.gstack/projects/$SLUG/land-deploy-confirmed ]; then +if [ ! -f "$PROJECT_DATA_DIR/land-deploy-confirmed" ] && [ ! -f ~/.gstack/projects/$SLUG/land-deploy-confirmed ]; then echo "FIRST_RUN" else # Check if deploy config has changed since confirmation - SAVED_HASH=$(cat ~/.gstack/projects/$SLUG/land-deploy-confirmed 2>/dev/null) + SAVED_HASH=$(cat "$PROJECT_DATA_DIR/land-deploy-confirmed" 2>/dev/null || cat ~/.gstack/projects/$SLUG/land-deploy-confirmed 2>/dev/null) CURRENT_HASH=$(sed -n '/## Deploy Configuration/,/^## /p' CLAUDE.md 2>/dev/null | shasum -a 256 | cut -d' ' -f1) # Also hash workflow files that affect deploy behavior WORKFLOW_HASH=$(find .github/workflows -maxdepth 1 \( -name '*deploy*' -o -name '*cd*' \) 2>/dev/null | xargs cat 2>/dev/null | shasum -a 256 | cut -d' ' -f1) @@ -876,7 +876,7 @@ Log to the review dashboard: ```bash {{SLUG_EVAL}} -mkdir -p ~/.gstack/projects/$SLUG +mkdir -p "$PROJECT_DATA_DIR" ``` Write a JSONL entry with timing data: diff --git a/learn/SKILL.md b/learn/SKILL.md index e8f6055c2..58519722f 100644 --- a/learn/SKILL.md +++ b/learn/SKILL.md @@ -40,6 +40,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) diff --git a/office-hours/SKILL.md b/office-hours/SKILL.md index 2fb28fad9..5b3cf6d57 100644 --- a/office-hours/SKILL.md +++ b/office-hours/SKILL.md @@ -47,6 +47,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) @@ -542,7 +547,7 @@ eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 4. **List existing design docs for this project:** ```bash setopt +o nomatch 2>/dev/null || true # zsh compat - ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null + ls -t "$PROJECT_DATA_DIR"/*-design-*.md 2>/dev/null ``` If design docs exist, list them: "Prior designs for this project: [titles + dates]" @@ -811,14 +816,14 @@ After the user states the problem (first question in Phase 2A or 2B), search exi Extract 3-5 significant keywords from the user's problem statement and grep across design docs: ```bash setopt +o nomatch 2>/dev/null || true # zsh compat -grep -li "\|\|" ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null +grep -li "\|\|" "$PROJECT_DATA_DIR"/*-design-*.md 2>/dev/null ``` If matches found, read the matching design docs and surface them: - "FYI: Related design found — '{title}' by {user} on {date} (branch: {branch}). Key overlap: {1-line summary of relevant section}." - Ask via AskUserQuestion: "Should we build on this prior design or start fresh?" -This enables cross-team discovery — multiple users exploring the same project will see each other's design docs in `~/.gstack/projects/`. +This enables cross-team discovery — multiple users exploring the same project will see each other's design docs in `.gstack/`. If no matches found, proceed silently. @@ -1208,7 +1213,7 @@ Count the signals. You'll use this count in Phase 6 to determine which tier of c Write the design document to the project directory. ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR" USER=$(whoami) DATETIME=$(date +%Y%m%d-%H%M%S) ``` @@ -1216,11 +1221,11 @@ DATETIME=$(date +%Y%m%d-%H%M%S) **Design lineage:** Before writing, check for existing design docs on this branch: ```bash setopt +o nomatch 2>/dev/null || true # zsh compat -PRIOR=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) +PRIOR=$(ls -t "$PROJECT_DATA_DIR"/*-$BRANCH-design-*.md 2>/dev/null | head -1) ``` If `$PRIOR` exists, the new doc gets a `Supersedes:` field referencing it. This creates a revision chain — you can trace how a design evolved across office hours sessions. -Write to `~/.gstack/projects/{slug}/{user}-{branch}-design-{datetime}.md`: +Write to `.gstack/{user}-{branch}-design-{datetime}.md`: ### Startup mode design doc template: @@ -1598,7 +1603,7 @@ After the plea, suggest the next step: - **`/plan-eng-review`** for well-scoped implementation planning — lock in architecture, tests, edge cases - **`/plan-design-review`** for visual/UX design review -The design doc at `~/.gstack/projects/` is automatically discoverable by downstream skills — they will read it during their pre-review system audit. +The design doc at `.gstack/` is automatically discoverable by downstream skills — they will read it during their pre-review system audit. --- diff --git a/office-hours/SKILL.md.tmpl b/office-hours/SKILL.md.tmpl index d461b9988..a822bb4bc 100644 --- a/office-hours/SKILL.md.tmpl +++ b/office-hours/SKILL.md.tmpl @@ -51,7 +51,7 @@ Understand the project and the area the user wants to change. 4. **List existing design docs for this project:** ```bash setopt +o nomatch 2>/dev/null || true # zsh compat - ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null + ls -t "$PROJECT_DATA_DIR"/*-design-*.md 2>/dev/null ``` If design docs exist, list them: "Prior designs for this project: [titles + dates]" @@ -284,14 +284,14 @@ After the user states the problem (first question in Phase 2A or 2B), search exi Extract 3-5 significant keywords from the user's problem statement and grep across design docs: ```bash setopt +o nomatch 2>/dev/null || true # zsh compat -grep -li "\|\|" ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null +grep -li "\|\|" "$PROJECT_DATA_DIR"/*-design-*.md 2>/dev/null ``` If matches found, read the matching design docs and surface them: - "FYI: Related design found — '{title}' by {user} on {date} (branch: {branch}). Key overlap: {1-line summary of relevant section}." - Ask via AskUserQuestion: "Should we build on this prior design or start fresh?" -This enables cross-team discovery — multiple users exploring the same project will see each other's design docs in `~/.gstack/projects/`. +This enables cross-team discovery — multiple users exploring the same project will see each other's design docs in `.gstack/`. If no matches found, proceed silently. @@ -431,11 +431,11 @@ DATETIME=$(date +%Y%m%d-%H%M%S) **Design lineage:** Before writing, check for existing design docs on this branch: ```bash setopt +o nomatch 2>/dev/null || true # zsh compat -PRIOR=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) +PRIOR=$(ls -t "$PROJECT_DATA_DIR"/*-$BRANCH-design-*.md 2>/dev/null | head -1) ``` If `$PRIOR` exists, the new doc gets a `Supersedes:` field referencing it. This creates a revision chain — you can trace how a design evolved across office hours sessions. -Write to `~/.gstack/projects/{slug}/{user}-{branch}-design-{datetime}.md`: +Write to `.gstack/{user}-{branch}-design-{datetime}.md`: ### Startup mode design doc template: @@ -753,7 +753,7 @@ After the plea, suggest the next step: - **`/plan-eng-review`** for well-scoped implementation planning — lock in architecture, tests, edge cases - **`/plan-design-review`** for visual/UX design review -The design doc at `~/.gstack/projects/` is automatically discoverable by downstream skills — they will read it during their pre-review system audit. +The design doc at `.gstack/` is automatically discoverable by downstream skills — they will read it during their pre-review system audit. --- diff --git a/plan-ceo-review/SKILL.md b/plan-ceo-review/SKILL.md index 2e692ed3c..feb1e4ecb 100644 --- a/plan-ceo-review/SKILL.md +++ b/plan-ceo-review/SKILL.md @@ -43,6 +43,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) @@ -600,10 +605,12 @@ Then read CLAUDE.md, TODOS.md, and any existing architecture docs. **Design doc check:** ```bash setopt +o nomatch 2>/dev/null || true # zsh compat -SLUG=$(~/.claude/skills/gstack/browse/bin/remote-slug 2>/dev/null || basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)") -BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-' || echo 'no-branch') -DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) -[ -z "$DESIGN" ] && DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null | head -1) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" +DESIGN=$(ls -t "$PROJECT_DATA_DIR"/designs/*-$BRANCH-design-*.md 2>/dev/null | head -1) +[ -z "$DESIGN" ] && DESIGN=$(ls -t "$PROJECT_DATA_DIR"/designs/*-design-*.md 2>/dev/null | head -1) +# Fallback: legacy global path +[ -z "$DESIGN" ] && DESIGN=$(ls -t "$PROJECT_DATA_DIR"/*-$BRANCH-design-*.md 2>/dev/null | head -1) +[ -z "$DESIGN" ] && DESIGN=$(ls -t "$PROJECT_DATA_DIR"/*-design-*.md 2>/dev/null | head -1) [ -n "$DESIGN" ] && echo "Design doc found: $DESIGN" || echo "No design doc found" ``` If a design doc exists (from `/office-hours`), read it. Use it as the source of truth for the problem statement, constraints, and chosen approach. If it has a `Supersedes:` field, note that this is a revised design. @@ -611,7 +618,7 @@ If a design doc exists (from `/office-hours`), read it. Use it as the source of **Handoff note check** (reuses $SLUG and $BRANCH from the design doc check above): ```bash setopt +o nomatch 2>/dev/null || true # zsh compat -HANDOFF=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null | head -1) +HANDOFF=$(ls -t "$PROJECT_DATA_DIR"/*-$BRANCH-ceo-handoff-*.md 2>/dev/null | head -1) [ -n "$HANDOFF" ] && echo "HANDOFF_FOUND: $HANDOFF" || echo "NO_HANDOFF" ``` If this block runs in a separate shell from the design doc check, recompute $SLUG and $BRANCH first using the same commands from that block. @@ -671,9 +678,11 @@ Execute every other section at full depth. When the loaded skill's instructions After /office-hours completes, re-run the design doc check: ```bash setopt +o nomatch 2>/dev/null || true # zsh compat -SLUG=$(~/.claude/skills/gstack/browse/bin/remote-slug 2>/dev/null || basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)") -BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-' || echo 'no-branch') -DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" +DESIGN=$(ls -t "$PROJECT_DATA_DIR"/designs/*-$BRANCH-design-*.md 2>/dev/null | head -1) +[ -z "$DESIGN" ] && DESIGN=$(ls -t "$PROJECT_DATA_DIR"/designs/*-design-*.md 2>/dev/null | head -1) +# Fallback: legacy global path +[ -z "$DESIGN" ] && DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) [ -z "$DESIGN" ] && DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null | head -1) [ -n "$DESIGN" ] && echo "Design doc found: $DESIGN" || echo "No design doc found" ``` @@ -870,17 +879,17 @@ Rules: After the opt-in/cherry-pick ceremony, write the plan to disk so the vision and decisions survive beyond this conversation. Only run this step for EXPANSION and SELECTIVE EXPANSION modes. ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG/ceo-plans +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR"/ceo-plans ``` Before writing, check for existing CEO plans in the ceo-plans/ directory. If any are >30 days old or their branch has been merged/deleted, offer to archive them: ```bash -mkdir -p ~/.gstack/projects/$SLUG/ceo-plans/archive -# For each stale plan: mv ~/.gstack/projects/$SLUG/ceo-plans/{old-plan}.md ~/.gstack/projects/$SLUG/ceo-plans/archive/ +mkdir -p "$PROJECT_DATA_DIR"/ceo-plans/archive +# For each stale plan: mv "$PROJECT_DATA_DIR"/ceo-plans/{old-plan}.md "$PROJECT_DATA_DIR"/ceo-plans/archive/ ``` -Write to `~/.gstack/projects/$SLUG/ceo-plans/{date}-{feature-slug}.md` using this format: +Write to `"$PROJECT_DATA_DIR"/ceo-plans/{date}-{feature-slug}.md` using this format: ```markdown --- @@ -1511,7 +1520,7 @@ the review is complete and the context is no longer needed. ```bash setopt +o nomatch 2>/dev/null || true # zsh compat eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -rm -f ~/.gstack/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null || true +rm -f "$PROJECT_DATA_DIR"/*-$BRANCH-ceo-handoff-*.md 2>/dev/null || true ``` ## Review Log @@ -1678,7 +1687,7 @@ At the end of the review, if the vision produced a compelling feature direction, "The vision from this review produced {N} accepted scope expansions. Want to promote it to a design doc in the repo?" - **A)** Promote to `docs/designs/{FEATURE}.md` (committed to repo, visible to the team) -- **B)** Keep in `~/.gstack/projects/` only (local, personal reference) +- **B)** Keep in `.gstack/` only (local, personal reference) - **C)** Skip If promoted, copy the CEO plan content to `docs/designs/{FEATURE}.md` (create the directory if needed) and update the `status` field in the original CEO plan from `ACTIVE` to `PROMOTED`. diff --git a/plan-ceo-review/SKILL.md.tmpl b/plan-ceo-review/SKILL.md.tmpl index 319c3c1a7..372983b15 100644 --- a/plan-ceo-review/SKILL.md.tmpl +++ b/plan-ceo-review/SKILL.md.tmpl @@ -106,10 +106,12 @@ Then read CLAUDE.md, TODOS.md, and any existing architecture docs. **Design doc check:** ```bash setopt +o nomatch 2>/dev/null || true # zsh compat -SLUG=$(~/.claude/skills/gstack/browse/bin/remote-slug 2>/dev/null || basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)") -BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-' || echo 'no-branch') -DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) -[ -z "$DESIGN" ] && DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null | head -1) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" +DESIGN=$(ls -t "$PROJECT_DATA_DIR"/designs/*-$BRANCH-design-*.md 2>/dev/null | head -1) +[ -z "$DESIGN" ] && DESIGN=$(ls -t "$PROJECT_DATA_DIR"/designs/*-design-*.md 2>/dev/null | head -1) +# Fallback: legacy global path +[ -z "$DESIGN" ] && DESIGN=$(ls -t "$PROJECT_DATA_DIR"/*-$BRANCH-design-*.md 2>/dev/null | head -1) +[ -z "$DESIGN" ] && DESIGN=$(ls -t "$PROJECT_DATA_DIR"/*-design-*.md 2>/dev/null | head -1) [ -n "$DESIGN" ] && echo "Design doc found: $DESIGN" || echo "No design doc found" ``` If a design doc exists (from `/office-hours`), read it. Use it as the source of truth for the problem statement, constraints, and chosen approach. If it has a `Supersedes:` field, note that this is a revised design. @@ -117,7 +119,7 @@ If a design doc exists (from `/office-hours`), read it. Use it as the source of **Handoff note check** (reuses $SLUG and $BRANCH from the design doc check above): ```bash setopt +o nomatch 2>/dev/null || true # zsh compat -HANDOFF=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null | head -1) +HANDOFF=$(ls -t "$PROJECT_DATA_DIR"/*-$BRANCH-ceo-handoff-*.md 2>/dev/null | head -1) [ -n "$HANDOFF" ] && echo "HANDOFF_FOUND: $HANDOFF" || echo "NO_HANDOFF" ``` If this block runs in a separate shell from the design doc check, recompute $SLUG and $BRANCH first using the same commands from that block. @@ -267,17 +269,17 @@ Rules: After the opt-in/cherry-pick ceremony, write the plan to disk so the vision and decisions survive beyond this conversation. Only run this step for EXPANSION and SELECTIVE EXPANSION modes. ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG/ceo-plans +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR"/ceo-plans ``` Before writing, check for existing CEO plans in the ceo-plans/ directory. If any are >30 days old or their branch has been merged/deleted, offer to archive them: ```bash -mkdir -p ~/.gstack/projects/$SLUG/ceo-plans/archive -# For each stale plan: mv ~/.gstack/projects/$SLUG/ceo-plans/{old-plan}.md ~/.gstack/projects/$SLUG/ceo-plans/archive/ +mkdir -p "$PROJECT_DATA_DIR"/ceo-plans/archive +# For each stale plan: mv "$PROJECT_DATA_DIR"/ceo-plans/{old-plan}.md "$PROJECT_DATA_DIR"/ceo-plans/archive/ ``` -Write to `~/.gstack/projects/$SLUG/ceo-plans/{date}-{feature-slug}.md` using this format: +Write to `"$PROJECT_DATA_DIR"/ceo-plans/{date}-{feature-slug}.md` using this format: ```markdown --- @@ -714,7 +716,7 @@ the review is complete and the context is no longer needed. ```bash setopt +o nomatch 2>/dev/null || true # zsh compat {{SLUG_EVAL}} -rm -f ~/.gstack/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null || true +rm -f "$PROJECT_DATA_DIR"/*-$BRANCH-ceo-handoff-*.md 2>/dev/null || true ``` ## Review Log @@ -767,7 +769,7 @@ At the end of the review, if the vision produced a compelling feature direction, "The vision from this review produced {N} accepted scope expansions. Want to promote it to a design doc in the repo?" - **A)** Promote to `docs/designs/{FEATURE}.md` (committed to repo, visible to the team) -- **B)** Keep in `~/.gstack/projects/` only (local, personal reference) +- **B)** Keep in `.gstack/` only (local, personal reference) - **C)** Skip If promoted, copy the CEO plan content to `docs/designs/{FEATURE}.md` (create the directory if needed) and update the `status` field in the original CEO plan from `ACTIVE` to `PROMOTED`. diff --git a/plan-design-review/SKILL.md b/plan-design-review/SKILL.md index 43c065a9e..fe7bb99a1 100644 --- a/plan-design-review/SKILL.md +++ b/plan-design-review/SKILL.md @@ -41,6 +41,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) diff --git a/plan-eng-review/SKILL.md b/plan-eng-review/SKILL.md index bf990f528..b7f6b2a8c 100644 --- a/plan-eng-review/SKILL.md +++ b/plan-eng-review/SKILL.md @@ -42,6 +42,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) @@ -528,8 +533,8 @@ When evaluating architecture, think "boring by default." When reviewing tests, t setopt +o nomatch 2>/dev/null || true # zsh compat SLUG=$(~/.claude/skills/gstack/browse/bin/remote-slug 2>/dev/null || basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)") BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-' || echo 'no-branch') -DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) -[ -z "$DESIGN" ] && DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null | head -1) +DESIGN=$(ls -t "$PROJECT_DATA_DIR/designs/"*-$BRANCH-design-*.md 2>/dev/null || ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) +[ -z "$DESIGN" ] && DESIGN=$(ls -t "$PROJECT_DATA_DIR/designs/"*-design-*.md 2>/dev/null || ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null | head -1) [ -n "$DESIGN" ] && echo "Design doc found: $DESIGN" || echo "No design doc found" ``` If a design doc exists, read it. Use it as the source of truth for the problem statement, constraints, and chosen approach. If it has a `Supersedes:` field, note that this is a revised design — check the prior version for context on what changed and why. @@ -581,9 +586,11 @@ Execute every other section at full depth. When the loaded skill's instructions After /office-hours completes, re-run the design doc check: ```bash setopt +o nomatch 2>/dev/null || true # zsh compat -SLUG=$(~/.claude/skills/gstack/browse/bin/remote-slug 2>/dev/null || basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)") -BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-' || echo 'no-branch') -DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" +DESIGN=$(ls -t "$PROJECT_DATA_DIR"/designs/*-$BRANCH-design-*.md 2>/dev/null | head -1) +[ -z "$DESIGN" ] && DESIGN=$(ls -t "$PROJECT_DATA_DIR"/designs/*-design-*.md 2>/dev/null | head -1) +# Fallback: legacy global path +[ -z "$DESIGN" ] && DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) [ -z "$DESIGN" ] && DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null | head -1) [ -n "$DESIGN" ] && echo "Design doc found: $DESIGN" || echo "No design doc found" ``` @@ -881,12 +888,12 @@ The plan should be complete enough that when implementation begins, every test i After producing the coverage diagram, write a test plan artifact to the project directory so `/qa` and `/qa-only` can consume it as primary test input: ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR/plans" USER=$(whoami) DATETIME=$(date +%Y%m%d-%H%M%S) ``` -Write to `~/.gstack/projects/{slug}/{user}-{branch}-eng-review-test-plan-{datetime}.md`: +Write to `.gstack/plans/{user}-{branch}-eng-review-test-plan-{datetime}.md`: ```markdown # Test Plan diff --git a/plan-eng-review/SKILL.md.tmpl b/plan-eng-review/SKILL.md.tmpl index fca7535ef..8fd07a091 100644 --- a/plan-eng-review/SKILL.md.tmpl +++ b/plan-eng-review/SKILL.md.tmpl @@ -71,8 +71,8 @@ When evaluating architecture, think "boring by default." When reviewing tests, t setopt +o nomatch 2>/dev/null || true # zsh compat SLUG=$(~/.claude/skills/gstack/browse/bin/remote-slug 2>/dev/null || basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)") BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-' || echo 'no-branch') -DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) -[ -z "$DESIGN" ] && DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null | head -1) +DESIGN=$(ls -t "$PROJECT_DATA_DIR/designs/"*-$BRANCH-design-*.md 2>/dev/null || ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) +[ -z "$DESIGN" ] && DESIGN=$(ls -t "$PROJECT_DATA_DIR/designs/"*-design-*.md 2>/dev/null || ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null | head -1) [ -n "$DESIGN" ] && echo "Design doc found: $DESIGN" || echo "No design doc found" ``` If a design doc exists, read it. Use it as the source of truth for the problem statement, constraints, and chosen approach. If it has a `Supersedes:` field, note that this is a revised design — check the prior version for context on what changed and why. diff --git a/qa-only/SKILL.md b/qa-only/SKILL.md index 996b2f364..cd5aa8766 100644 --- a/qa-only/SKILL.md +++ b/qa-only/SKILL.md @@ -38,6 +38,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) @@ -582,11 +587,11 @@ smarter on their codebase over time. Before falling back to git diff heuristics, check for richer test plan sources: -1. **Project-scoped test plans:** Check `~/.gstack/projects/` for recent `*-test-plan-*.md` files for this repo +1. **Project-scoped test plans:** Check `.gstack/plans/` for recent `*-test-plan-*.md` files for this repo ```bash setopt +o nomatch 2>/dev/null || true # zsh compat eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" - ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1 + ls -t "$PROJECT_DATA_DIR/plans/"*-test-plan-*.md 2>/dev/null || ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1 ``` 2. **Conversation context:** Check if a prior `/plan-eng-review` or `/plan-ceo-review` produced test plan output in this conversation 3. **Use whichever source is richer.** Fall back to git diff analysis only if neither is available. @@ -881,9 +886,9 @@ Write the report to both local and project-scoped locations: **Project-scoped:** Write test outcome artifact for cross-session context: ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR" ``` -Write to `~/.gstack/projects/{slug}/{user}-{branch}-test-outcome-{datetime}.md` +Write to `.gstack/{user}-{branch}-test-outcome-{datetime}.md` ### Output Structure diff --git a/qa-only/SKILL.md.tmpl b/qa-only/SKILL.md.tmpl index 1aea67e0b..00cb6c9d5 100644 --- a/qa-only/SKILL.md.tmpl +++ b/qa-only/SKILL.md.tmpl @@ -55,11 +55,11 @@ mkdir -p "$REPORT_DIR/screenshots" Before falling back to git diff heuristics, check for richer test plan sources: -1. **Project-scoped test plans:** Check `~/.gstack/projects/` for recent `*-test-plan-*.md` files for this repo +1. **Project-scoped test plans:** Check `.gstack/plans/` for recent `*-test-plan-*.md` files for this repo ```bash setopt +o nomatch 2>/dev/null || true # zsh compat {{SLUG_EVAL}} - ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1 + ls -t "$PROJECT_DATA_DIR/plans/"*-test-plan-*.md 2>/dev/null || ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1 ``` 2. **Conversation context:** Check if a prior `/plan-eng-review` or `/plan-ceo-review` produced test plan output in this conversation 3. **Use whichever source is richer.** Fall back to git diff analysis only if neither is available. @@ -80,7 +80,7 @@ Write the report to both local and project-scoped locations: ```bash {{SLUG_SETUP}} ``` -Write to `~/.gstack/projects/{slug}/{user}-{branch}-test-outcome-{datetime}.md` +Write to `.gstack/{user}-{branch}-test-outcome-{datetime}.md` ### Output Structure diff --git a/qa/SKILL.md b/qa/SKILL.md index 893d04112..bbd1de637 100644 --- a/qa/SKILL.md +++ b/qa/SKILL.md @@ -44,6 +44,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) @@ -812,11 +817,11 @@ smarter on their codebase over time. Before falling back to git diff heuristics, check for richer test plan sources: -1. **Project-scoped test plans:** Check `~/.gstack/projects/` for recent `*-test-plan-*.md` files for this repo +1. **Project-scoped test plans:** Check `.gstack/plans/` for recent `*-test-plan-*.md` files for this repo ```bash setopt +o nomatch 2>/dev/null || true # zsh compat eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" - ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1 + ls -t "$PROJECT_DATA_DIR/plans/"*-test-plan-*.md 2>/dev/null || ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1 ``` 2. **Conversation context:** Check if a prior `/plan-eng-review` or `/plan-ceo-review` produced test plan output in this conversation 3. **Use whichever source is richer.** Fall back to git diff analysis only if neither is available. @@ -1279,9 +1284,9 @@ Write the report to both local and project-scoped locations: **Project-scoped:** Write test outcome artifact for cross-session context: ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR" ``` -Write to `~/.gstack/projects/{slug}/{user}-{branch}-test-outcome-{datetime}.md` +Write to `.gstack/{user}-{branch}-test-outcome-{datetime}.md` **Per-issue additions** (beyond standard report template): - Fix Status: verified / best-effort / reverted / deferred diff --git a/qa/SKILL.md.tmpl b/qa/SKILL.md.tmpl index 697853953..5e2962173 100644 --- a/qa/SKILL.md.tmpl +++ b/qa/SKILL.md.tmpl @@ -96,11 +96,11 @@ mkdir -p .gstack/qa-reports/screenshots Before falling back to git diff heuristics, check for richer test plan sources: -1. **Project-scoped test plans:** Check `~/.gstack/projects/` for recent `*-test-plan-*.md` files for this repo +1. **Project-scoped test plans:** Check `.gstack/plans/` for recent `*-test-plan-*.md` files for this repo ```bash setopt +o nomatch 2>/dev/null || true # zsh compat {{SLUG_EVAL}} - ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1 + ls -t "$PROJECT_DATA_DIR/plans/"*-test-plan-*.md 2>/dev/null || ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1 ``` 2. **Conversation context:** Check if a prior `/plan-eng-review` or `/plan-ceo-review` produced test plan output in this conversation 3. **Use whichever source is richer.** Fall back to git diff analysis only if neither is available. @@ -289,7 +289,7 @@ Write the report to both local and project-scoped locations: ```bash {{SLUG_SETUP}} ``` -Write to `~/.gstack/projects/{slug}/{user}-{branch}-test-outcome-{datetime}.md` +Write to `.gstack/{user}-{branch}-test-outcome-{datetime}.md` **Per-issue additions** (beyond standard report template): - Fix Status: verified / best-effort / reverted / deferred diff --git a/retro/SKILL.md b/retro/SKILL.md index bd99a7624..2b8aa4510 100644 --- a/retro/SKILL.md +++ b/retro/SKILL.md @@ -38,6 +38,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) @@ -999,7 +1004,7 @@ Check review JSONL logs for plan completion data from /ship runs this period: ```bash setopt +o nomatch 2>/dev/null || true # zsh compat eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -cat ~/.gstack/projects/$SLUG/*-reviews.jsonl 2>/dev/null | grep '"skill":"ship"' | grep '"plan_items_total"' || echo "NO_PLAN_DATA" +cat "$PROJECT_DATA_DIR"/*-reviews.jsonl 2>/dev/null || cat ~/.gstack/projects/$SLUG/*-reviews.jsonl 2>/dev/null | grep '"skill":"ship"' | grep '"plan_items_total"' || echo "NO_PLAN_DATA" ``` If plan completion data exists within the retro time window: diff --git a/retro/SKILL.md.tmpl b/retro/SKILL.md.tmpl index d89cb7175..ce945bc52 100644 --- a/retro/SKILL.md.tmpl +++ b/retro/SKILL.md.tmpl @@ -464,7 +464,7 @@ Check review JSONL logs for plan completion data from /ship runs this period: ```bash setopt +o nomatch 2>/dev/null || true # zsh compat eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -cat ~/.gstack/projects/$SLUG/*-reviews.jsonl 2>/dev/null | grep '"skill":"ship"' | grep '"plan_items_total"' || echo "NO_PLAN_DATA" +cat "$PROJECT_DATA_DIR"/*-reviews.jsonl 2>/dev/null || cat ~/.gstack/projects/$SLUG/*-reviews.jsonl 2>/dev/null | grep '"skill":"ship"' | grep '"plan_items_total"' || echo "NO_PLAN_DATA" ``` If plan completion data exists within the retro time window: diff --git a/review/SKILL.md b/review/SKILL.md index eeb3c2ec1..67e4c521d 100644 --- a/review/SKILL.md +++ b/review/SKILL.md @@ -41,6 +41,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) diff --git a/review/greptile-triage.md b/review/greptile-triage.md index 3cb6e8d59..9402c9cf9 100644 --- a/review/greptile-triage.md +++ b/review/greptile-triage.md @@ -34,8 +34,8 @@ The `position != null` filter on line-level comments automatically skips outdate Derive the project-specific history path: ```bash -REMOTE_SLUG=$(browse/bin/remote-slug 2>/dev/null || ~/.claude/skills/gstack/browse/bin/remote-slug 2>/dev/null || basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)") -PROJECT_HISTORY="$HOME/.gstack/projects/$REMOTE_SLUG/greptile-history.md" +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" +PROJECT_HISTORY="$PROJECT_DATA_DIR/greptile-history.md" ``` Read `$PROJECT_HISTORY` if it exists (per-project suppressions). Each line records a previous triage outcome: @@ -183,13 +183,13 @@ When classifying comments, also assess whether Greptile's implied severity match Before writing, ensure both directories exist: ```bash -REMOTE_SLUG=$(browse/bin/remote-slug 2>/dev/null || ~/.claude/skills/gstack/browse/bin/remote-slug 2>/dev/null || basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)") -mkdir -p "$HOME/.gstack/projects/$REMOTE_SLUG" +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" +mkdir -p "$PROJECT_DATA_DIR" mkdir -p ~/.gstack ``` Append one line per triage outcome to **both** files (per-project for suppressions, global for retro): -- `~/.gstack/projects/$REMOTE_SLUG/greptile-history.md` (per-project) +- `$PROJECT_DATA_DIR/greptile-history.md` (per-project) - `~/.gstack/greptile-history.md` (global aggregate) Format: diff --git a/scripts/resolvers/design.ts b/scripts/resolvers/design.ts index 208b1db3b..be1b6592e 100644 --- a/scripts/resolvers/design.ts +++ b/scripts/resolvers/design.ts @@ -307,9 +307,9 @@ Compare screenshots and observations across pages for: **Project-scoped:** \`\`\`bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR/designs" \`\`\` -Write to: \`~/.gstack/projects/{slug}/{user}-{branch}-design-audit-{datetime}.md\` +Write to: \`.gstack/designs/{user}-{branch}-design-audit-{datetime}.md\` **Baseline:** Write \`design-baseline.json\` for regression mode: \`\`\`json diff --git a/scripts/resolvers/preamble.ts b/scripts/resolvers/preamble.ts index 49288500c..668e6fba4 100644 --- a/scripts/resolvers/preamble.ts +++ b/scripts/resolvers/preamble.ts @@ -44,6 +44,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(${ctx.paths.binDir}/gstack-repo-mode 2>/dev/null) || true REPO_MODE=\${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(${ctx.paths.binDir}/gstack-slug 2>/dev/null)" || true +if [ -n "\${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/\${SLUG:-}" ] && [ ! -f "\${PROJECT_DATA_DIR}/.migrated" ]; then + ${ctx.paths.binDir}/gstack-migrate-local 2>/dev/null && touch "\${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(${ctx.paths.binDir}/gstack-config get telemetry 2>/dev/null || true) diff --git a/scripts/resolvers/review.ts b/scripts/resolvers/review.ts index de01698a3..dc26cce81 100644 --- a/scripts/resolvers/review.ts +++ b/scripts/resolvers/review.ts @@ -241,9 +241,11 @@ ${invokeBlock} After /${first} completes, re-run the design doc check: \`\`\`bash setopt +o nomatch 2>/dev/null || true # zsh compat -SLUG=$(~/.claude/skills/gstack/browse/bin/remote-slug 2>/dev/null || basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)") -BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-' || echo 'no-branch') -DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" +DESIGN=$(ls -t "$PROJECT_DATA_DIR"/designs/*-$BRANCH-design-*.md 2>/dev/null | head -1) +[ -z "$DESIGN" ] && DESIGN=$(ls -t "$PROJECT_DATA_DIR"/designs/*-design-*.md 2>/dev/null | head -1) +# Fallback: legacy global path +[ -z "$DESIGN" ] && DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) [ -z "$DESIGN" ] && DESIGN=$(ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null | head -1) [ -n "$DESIGN" ] && echo "Design doc found: $DESIGN" || echo "No design doc found" \`\`\` diff --git a/scripts/resolvers/testing.ts b/scripts/resolvers/testing.ts index da1381c20..31adeefa4 100644 --- a/scripts/resolvers/testing.ts +++ b/scripts/resolvers/testing.ts @@ -401,12 +401,12 @@ The plan should be complete enough that when implementation begins, every test i After producing the coverage diagram, write a test plan artifact to the project directory so \`/qa\` and \`/qa-only\` can consume it as primary test input: \`\`\`bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR/plans" USER=$(whoami) DATETIME=$(date +%Y%m%d-%H%M%S) \`\`\` -Write to \`~/.gstack/projects/{slug}/{user}-{branch}-eng-review-test-plan-{datetime}.md\`: +Write to \`.gstack/plans/{user}-{branch}-eng-review-test-plan-{datetime}.md\`: \`\`\`markdown # Test Plan @@ -498,12 +498,12 @@ Using the coverage percentage from the diagram in substep 4 (the \`COVERAGE: X/Y After producing the coverage diagram, write a test plan artifact so \`/qa\` and \`/qa-only\` can consume it: \`\`\`bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR/plans" USER=$(whoami) DATETIME=$(date +%Y%m%d-%H%M%S) \`\`\` -Write to \`~/.gstack/projects/{slug}/{user}-{branch}-ship-test-plan-{datetime}.md\`: +Write to \`.gstack/plans/{user}-{branch}-ship-test-plan-{datetime}.md\`: \`\`\`markdown # Test Plan diff --git a/scripts/resolvers/utility.ts b/scripts/resolvers/utility.ts index e6167d02f..226ceeee7 100644 --- a/scripts/resolvers/utility.ts +++ b/scripts/resolvers/utility.ts @@ -5,7 +5,7 @@ export function generateSlugEval(ctx: TemplateContext): string { } export function generateSlugSetup(ctx: TemplateContext): string { - return `eval "$(${ctx.paths.binDir}/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG`; + return `eval "$(${ctx.paths.binDir}/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR"`; } export function generateBaseBranchDetect(_ctx: TemplateContext): string { diff --git a/setup-browser-cookies/SKILL.md b/setup-browser-cookies/SKILL.md index 91828dacb..8642ed200 100644 --- a/setup-browser-cookies/SKILL.md +++ b/setup-browser-cookies/SKILL.md @@ -35,6 +35,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) diff --git a/setup-deploy/SKILL.md b/setup-deploy/SKILL.md index a186aa339..38811a978 100644 --- a/setup-deploy/SKILL.md +++ b/setup-deploy/SKILL.md @@ -41,6 +41,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) diff --git a/ship/SKILL.md b/ship/SKILL.md index 925245824..ac731dbfa 100644 --- a/ship/SKILL.md +++ b/ship/SKILL.md @@ -42,6 +42,11 @@ echo "SKILL_PREFIX: $_SKILL_PREFIX" source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true REPO_MODE=${REPO_MODE:-unknown} echo "REPO_MODE: $REPO_MODE" +# Auto-migrate legacy ~/.gstack/projects/ data to project-local .gstack/ (once per project) +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" || true +if [ -n "${PROJECT_DATA_DIR:-}" ] && [ -d "$HOME/.gstack/projects/${SLUG:-}" ] && [ ! -f "${PROJECT_DATA_DIR}/.migrated" ]; then + ~/.claude/skills/gstack/bin/gstack-migrate-local 2>/dev/null && touch "${PROJECT_DATA_DIR}/.migrated" 2>/dev/null || true +fi _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) @@ -1246,12 +1251,12 @@ Using the coverage percentage from the diagram in substep 4 (the `COVERAGE: X/Y After producing the coverage diagram, write a test plan artifact so `/qa` and `/qa-only` can consume it: ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR/plans" USER=$(whoami) DATETIME=$(date +%Y%m%d-%H%M%S) ``` -Write to `~/.gstack/projects/{slug}/{user}-{branch}-ship-test-plan-{datetime}.md`: +Write to `.gstack/plans/{user}-{branch}-ship-test-plan-{datetime}.md`: ```markdown # Test Plan @@ -1643,13 +1648,6 @@ Present Codex output under a `CODEX (design):` header, merged with the checklist If no issues found: `Pre-Landing Review: No issues found.` -9. Persist the review result to the review log: -```bash -~/.claude/skills/gstack/bin/gstack-review-log '{"skill":"review","timestamp":"TIMESTAMP","status":"STATUS","issues_found":N,"critical":N,"informational":N,"commit":"'"$(git rev-parse --short HEAD)"'","via":"ship"}' -``` -Substitute TIMESTAMP (ISO 8601), STATUS ("clean" if no issues, "issues_found" otherwise), -and N values from the summary counts above. The `via:"ship"` distinguishes from standalone `/review` runs. - Save the review output — it goes into the PR body in Step 8. --- @@ -2075,12 +2073,7 @@ The PR/MR body should contain these sections: ``` ## Summary -..HEAD --oneline` to enumerate -every commit. Exclude the VERSION/CHANGELOG metadata commit (that's this PR's bookkeeping, -not a substantive change). Group the remaining commits into logical sections (e.g., -"**Performance**", "**Dead Code Removal**", "**Infrastructure**"). Every substantive commit -must appear in at least one section. If a commit's work isn't reflected in the summary, -you missed it.> + ## Test Coverage @@ -2179,13 +2172,13 @@ doc updates — the user runs `/ship` and documentation stays current without a Log coverage and plan completion data so `/retro` can track trends: ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR" ``` -Append to `~/.gstack/projects/$SLUG/$BRANCH-reviews.jsonl`: +Append to `$PROJECT_DATA_DIR/$BRANCH-reviews.jsonl`: ```bash -echo '{"skill":"ship","timestamp":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'","coverage_pct":COVERAGE_PCT,"plan_items_total":PLAN_TOTAL,"plan_items_done":PLAN_DONE,"verification_result":"VERIFY_RESULT","version":"VERSION","branch":"BRANCH"}' >> ~/.gstack/projects/$SLUG/$BRANCH-reviews.jsonl +echo '{"skill":"ship","timestamp":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'","coverage_pct":COVERAGE_PCT,"plan_items_total":PLAN_TOTAL,"plan_items_done":PLAN_DONE,"verification_result":"VERIFY_RESULT","version":"VERSION","branch":"BRANCH"}' >> "$PROJECT_DATA_DIR/$BRANCH-reviews.jsonl" ``` Substitute from earlier steps: diff --git a/ship/SKILL.md.tmpl b/ship/SKILL.md.tmpl index de2ee4b97..302bb237f 100644 --- a/ship/SKILL.md.tmpl +++ b/ship/SKILL.md.tmpl @@ -274,13 +274,6 @@ Review the diff for structural issues that tests don't catch. If no issues found: `Pre-Landing Review: No issues found.` -9. Persist the review result to the review log: -```bash -~/.claude/skills/gstack/bin/gstack-review-log '{"skill":"review","timestamp":"TIMESTAMP","status":"STATUS","issues_found":N,"critical":N,"informational":N,"commit":"'"$(git rev-parse --short HEAD)"'","via":"ship"}' -``` -Substitute TIMESTAMP (ISO 8601), STATUS ("clean" if no issues, "issues_found" otherwise), -and N values from the summary counts above. The `via:"ship"` distinguishes from standalone `/review` runs. - Save the review output — it goes into the PR body in Step 8. --- @@ -520,12 +513,7 @@ The PR/MR body should contain these sections: ``` ## Summary -..HEAD --oneline` to enumerate -every commit. Exclude the VERSION/CHANGELOG metadata commit (that's this PR's bookkeeping, -not a substantive change). Group the remaining commits into logical sections (e.g., -"**Performance**", "**Dead Code Removal**", "**Infrastructure**"). Every substantive commit -must appear in at least one section. If a commit's work isn't reflected in the summary, -you missed it.> + ## Test Coverage @@ -624,13 +612,13 @@ doc updates — the user runs `/ship` and documentation stays current without a Log coverage and plan completion data so `/retro` can track trends: ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$PROJECT_DATA_DIR" ``` -Append to `~/.gstack/projects/$SLUG/$BRANCH-reviews.jsonl`: +Append to `$PROJECT_DATA_DIR/$BRANCH-reviews.jsonl`: ```bash -echo '{"skill":"ship","timestamp":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'","coverage_pct":COVERAGE_PCT,"plan_items_total":PLAN_TOTAL,"plan_items_done":PLAN_DONE,"verification_result":"VERIFY_RESULT","version":"VERSION","branch":"BRANCH"}' >> ~/.gstack/projects/$SLUG/$BRANCH-reviews.jsonl +echo '{"skill":"ship","timestamp":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'","coverage_pct":COVERAGE_PCT,"plan_items_total":PLAN_TOTAL,"plan_items_done":PLAN_DONE,"verification_result":"VERIFY_RESULT","version":"VERSION","branch":"BRANCH"}' >> "$PROJECT_DATA_DIR/$BRANCH-reviews.jsonl" ``` Substitute from earlier steps: diff --git a/test/helpers/eval-store.ts b/test/helpers/eval-store.ts index a7d63178c..fa206b95e 100644 --- a/test/helpers/eval-store.ts +++ b/test/helpers/eval-store.ts @@ -22,7 +22,19 @@ const LEGACY_EVAL_DIR = path.join(os.homedir(), '.gstack-dev', 'evals'); */ export function getProjectEvalDir(): string { try { - // Try repo-local gstack-slug first, then global install + // Prefer project-local .gstack/evals/ via git root + const gitRoot = spawnSync('git', ['rev-parse', '--show-toplevel'], { + stdio: 'pipe', timeout: 3000, + }); + const root = gitRoot.stdout?.toString().trim(); + if (root) { + const dir = path.join(root, '.gstack', 'evals'); + fs.mkdirSync(dir, { recursive: true }); + return dir; + } + } catch { /* fall through */ } + try { + // Fallback: legacy slug-based path (backward-compatible read) const localSlug = spawnSync('bash', ['-c', '.claude/skills/gstack/bin/gstack-slug 2>/dev/null || ~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null'], { stdio: 'pipe', timeout: 3000, }); @@ -30,9 +42,8 @@ export function getProjectEvalDir(): string { if (output) { const slugMatch = output.match(/^SLUG=(.+)$/m); if (slugMatch && slugMatch[1]) { - const dir = path.join(os.homedir(), '.gstack', 'projects', slugMatch[1], 'evals'); - fs.mkdirSync(dir, { recursive: true }); - return dir; + const legacyDir = path.join(os.homedir(), '.gstack', 'projects', slugMatch[1], 'evals'); + if (fs.existsSync(legacyDir)) return legacyDir; } } } catch { /* fall through */ } diff --git a/test/skill-validation.test.ts b/test/skill-validation.test.ts index 26a0870d1..744bc47ab 100644 --- a/test/skill-validation.test.ts +++ b/test/skill-validation.test.ts @@ -282,26 +282,12 @@ describe('Update check preamble', () => { // --- Part 7: Cross-skill path consistency (A1) --- describe('Cross-skill path consistency', () => { - test('REMOTE_SLUG derivation pattern is identical across files that use it', () => { - const patterns = extractRemoteSlugPatterns(ROOT, ['qa', 'review']); - const allPatterns: string[] = []; - - for (const [, filePatterns] of patterns) { - allPatterns.push(...filePatterns); - } - - // Should find at least 2 occurrences (qa/SKILL.md + review/greptile-triage.md) - expect(allPatterns.length).toBeGreaterThanOrEqual(2); - - // All occurrences must be character-for-character identical - const unique = new Set(allPatterns); - if (unique.size > 1) { - const variants = Array.from(unique); - throw new Error( - `REMOTE_SLUG pattern differs across files:\n` + - variants.map((v, i) => ` ${i + 1}: ${v}`).join('\n') - ); - } + test('project data paths use $PROJECT_DATA_DIR from gstack-slug', () => { + // greptile-triage.md should use gstack-slug to set PROJECT_DATA_DIR + const triagePath = path.join(ROOT, 'review', 'greptile-triage.md'); + const content = fs.readFileSync(triagePath, 'utf-8'); + expect(content).toContain('$PROJECT_DATA_DIR/greptile-history.md'); + expect(content).toContain('gstack-slug'); }); test('all greptile-history write references specify both per-project and global paths', () => { @@ -317,15 +303,15 @@ describe('Cross-skill path consistency', () => { const content = fs.readFileSync(filePath, 'utf-8'); const hasBoth = (content.includes('per-project') && content.includes('global')) || - (content.includes('$REMOTE_SLUG/greptile-history') && content.includes('~/.gstack/greptile-history')); + (content.includes('$PROJECT_DATA_DIR/greptile-history') && content.includes('~/.gstack/greptile-history')); expect(hasBoth).toBe(true); } }); - test('greptile-triage.md contains both project and global history paths', () => { + test('greptile-triage.md contains both project-local and global history paths', () => { const content = fs.readFileSync(path.join(ROOT, 'review', 'greptile-triage.md'), 'utf-8'); - expect(content).toContain('$REMOTE_SLUG/greptile-history.md'); + expect(content).toContain('$PROJECT_DATA_DIR/greptile-history.md'); expect(content).toContain('~/.gstack/greptile-history.md'); }); @@ -947,9 +933,10 @@ describe('gstack-slug', () => { test('output is eval-compatible (KEY=VALUE format)', () => { const result = Bun.spawnSync([SLUG_BIN], { cwd: ROOT, stdout: 'pipe', stderr: 'pipe' }); const lines = result.stdout.toString().trim().split('\n'); - expect(lines.length).toBe(2); + expect(lines.length).toBe(3); expect(lines[0]).toMatch(/^SLUG=.+/); expect(lines[1]).toMatch(/^BRANCH=.+/); + expect(lines[2]).toMatch(/^PROJECT_DATA_DIR=.+/); }); test('output values contain only safe characters (no shell metacharacters)', () => {