     1→---
     2→name: retro
     3→version: 2.0.0
     4→description: |
     5→  Weekly engineering retrospective. Analyzes commit history, work patterns,
     6→  and code quality metrics with persistent history and trend tracking.
     7→  Team-aware: breaks down per-person contributions with praise and growth areas.
     8→allowed-tools:
     9→  - Bash
    10→  - Read
    11→  - Write
    12→  - Glob
    13→  - AskUserQuestion
    14→---
    15→
    16→{{PREAMBLE}}
    17→
    18→## Detect default branch
    19→
    20→Before gathering data, detect the repo's default branch name:
    21→`gh repo view --json defaultBranchRef -q .defaultBranchRef.name`
    22→
    23→If this fails, fall back to `main`. Use the detected name wherever the instructions
    24→say `origin/<default>` below.
    25→
    26→---
    27→
    28→# /retro — Weekly Engineering Retrospective
    29→
    30→Generates a comprehensive engineering retrospective analyzing commit history, work patterns, and code quality metrics. Team-aware: identifies the user running the command, then analyzes every contributor with per-person praise and growth opportunities. Designed for a senior IC/CTO-level builder using Claude Code as a force multiplier.
    31→
    32→## User-invocable
    33→When the user types `/retro`, run this skill.
    34→
    35→## Arguments
    36→- `/retro` — default: last 7 days
    37→- `/retro 24h` — last 24 hours
    38→- `/retro 14d` — last 14 days
    39→- `/retro 30d` — last 30 days
    40→- `/retro compare` — compare current window vs prior same-length window
    41→- `/retro compare 14d` — compare with explicit window
    42→
    43→## Instructions
    44→
    45→Parse the argument to determine the time window. Default to 7 days if no argument given. Use `--since="N days ago"`, `--since="N hours ago"`, or `--since="N weeks ago"` (for `w` units) for git log queries. All times should be reported in **Pacific time** (use `TZ=America/Los_Angeles` when converting timestamps).
    46→
    47→**Argument validation:** If the argument doesn't match a number followed by `d`, `h`, or `w`, the word `compare`, or `compare` followed by a number and `d`/`h`/`w`, show this usage and stop:
    48→```
    49→Usage: /retro [window]
    50→  /retro              — last 7 days (default)
    51→  /retro 24h          — last 24 hours
    52→  /retro 14d          — last 14 days
    53→  /retro 30d          — last 30 days
    54→  /retro compare      — compare this period vs prior period
    55→  /retro compare 14d  — compare with explicit window
    56→```
    57→
    58→### Step 1: Gather Raw Data
    59→
    60→First, fetch origin and identify the current user:
    61→```bash
    62→git fetch origin <default> --quiet
    63→# Identify who is running the retro
    64→git config user.name
    65→git config user.email
    66→```
    67→
    68→The name returned by `git config user.name` is **"you"** — the person reading this retro. All other authors are teammates. Use this to orient the narrative: "your" commits vs teammate contributions.
    69→
    70→Run ALL of these git commands in parallel (they are independent):
    71→
    72→```bash
    73→# 1. All commits in window with timestamps, subject, hash, AUTHOR, files changed, insertions, deletions
    74→git log origin/<default> --since="<window>" --format="%H|%aN|%ae|%ai|%s" --shortstat
    75→
    76→# 2. Per-commit test vs total LOC breakdown with author
    77→#    Each commit block starts with COMMIT:<hash>|<author>, followed by numstat lines.
    78→#    Separate test files (matching test/|spec/|__tests__/) from production files.
    79→git log origin/<default> --since="<window>" --format="COMMIT:%H|%aN" --numstat
    80→
    81→# 3. Commit timestamps for session detection and hourly distribution (with author)
    82→#    Use TZ=America/Los_Angeles for Pacific time conversion
    83→TZ=America/Los_Angeles git log origin/<default> --since="<window>" --format="%at|%aN|%ai|%s" | sort -n
    84→
    85→# 4. Files most frequently changed (hotspot analysis)
    86→git log origin/<default> --since="<window>" --format="" --name-only | grep -v '^$' | sort | uniq -c | sort -rn
    87→
    88→# 5. PR numbers from commit messages (extract #NNN patterns)
    89→git log origin/<default> --since="<window>" --format="%s" | grep -oE '#[0-9]+' | sed 's/^#//' | sort -n | uniq | sed 's/^/#/'
    90→
    91→# 6. Per-author file hotspots (who touches what)
    92→git log origin/<default> --since="<window>" --format="AUTHOR:%aN" --name-only
    93→
    94→# 7. Per-author commit counts (quick summary)
    95→git shortlog origin/<default> --since="<window>" -sn --no-merges
    96→
    97→# 8. Greptile triage history (if available)
    98→cat ~/.gstack/greptile-history.md 2>/dev/null || true
    99→
   100→# 9. TODOS.md backlog (if available)
   101→cat TODOS.md 2>/dev/null || true
   102→
   103→# 10. Test file count
   104→find . -name '*.test.*' -o -name '*.spec.*' -o -name '*_test.*' -o -name '*_spec.*' 2>/dev/null | grep -v node_modules | wc -l
   105→
   106→# 11. Regression test commits in window
   107→git log origin/<default> --since="<window>" --oneline --grep="test(qa):" --grep="test(design):" --grep="test: coverage"
   108→
   109→# 12. Test files changed in window
   110→git log origin/<default> --since="<window>" --format="" --name-only | grep -E '\.(test|spec)\.' | sort -u | wc -l
   111→```
   112→
   113→### Step 2: Compute Metrics
   114→
   115→Calculate and present these metrics in a summary table:
   116→
   117→| Metric | Value |
   118→|--------|-------|
   119→| Commits to main | N |
   120→| Contributors | N |
   121→| PRs merged | N |
   122→| Total insertions | N |
   123→| Total deletions | N |
   124→| Net LOC added | N |
   125→| Test LOC (insertions) | N |
   126→| Test LOC ratio | N% |
   127→| Version range | vX.Y.Z.W → vX.Y.Z.W |
   128→| Active days | N |
   129→| Detected sessions | N |
   130→| Avg LOC/session-hour | N |
   131→| Greptile signal | N% (Y catches, Z FPs) |
   132→| Test Health | N total tests · M added this period · K regression tests |
   133→
   134→Then show a **per-author leaderboard** immediately below:
   135→
   136→```
   137→Contributor         Commits   +/-          Top area
   138→You (garry)              32   +2400/-300   browse/
   139→alice                    12   +800/-150    app/services/
   140→bob                       3   +120/-40     tests/
   141→```
   142→
   143→Sort by commits descending. The current user (from `git config user.name`) always appears first, labeled "You (name)".
   144→
   145→**Greptile signal (if history exists):** Read `~/.gstack/greptile-history.md` (fetched in Step 1, command 8). Filter entries within the retro time window by date. Count entries by type: `fix`, `fp`, `already-fixed`. Compute signal ratio: `(fix + already-fixed) / (fix + already-fixed + fp)`. If no entries exist in the window or the file doesn't exist, skip the Greptile metric row. Skip unparseable lines silently.
   146→
   147→**Backlog Health (if TODOS.md exists):** Read `TODOS.md` (fetched in Step 1, command 9). Compute:
   148→- Total open TODOs (exclude items in `## Completed` section)
   149→- P0/P1 count (critical/urgent items)
   150→- P2 count (important items)
   151→- Items completed this period (items in Completed section with dates within the retro window)
   152→- Items added this period (cross-reference git log for commits that modified TODOS.md within the window)
   153→
   154→Include in the metrics table:
   155→```
   156→| Backlog Health | N open (X P0/P1, Y P2) · Z completed this period |
   157→```
   158→
   159→If TODOS.md doesn't exist, skip the Backlog Health row.
   160→
   161→### Step 3: Commit Time Distribution
   162→
   163→Show hourly histogram in Pacific time using bar chart:
   164→
   165→```
   166→Hour  Commits  ████████████████
   167→ 00:    4      ████
   168→ 07:    5      █████
   169→ ...
   170→```
   171→
   172→Identify and call out:
   173→- Peak hours
   174→- Dead zones
   175→- Whether pattern is bimodal (morning/evening) or continuous
   176→- Late-night coding clusters (after 10pm)
   177→
   178→### Step 4: Work Session Detection
   179→
   180→Detect sessions using **45-minute gap** threshold between consecutive commits. For each session report:
   181→- Start/end time (Pacific)
   182→- Number of commits
   183→- Duration in minutes
   184→
   185→Classify sessions:
   186→- **Deep sessions** (50+ min)
   187→- **Medium sessions** (20-50 min)
   188→- **Micro sessions** (<20 min, typically single-commit fire-and-forget)
   189→
   190→Calculate:
   191→- Total active coding time (sum of session durations)
   192→- Average session length
   193→- LOC per hour of active time
   194→
   195→### Step 5: Commit Type Breakdown
   196→
   197→Categorize by conventional commit prefix (feat/fix/refactor/test/chore/docs). Show as percentage bar:
   198→
   199→```
   200→feat:     20  (40%)  ████████████████████
   201→fix:      27  (54%)  ███████████████████████████
   202→refactor:  2  ( 4%)  ██
   203→```
   204→
   205→Flag if fix ratio exceeds 50% — this signals a "ship fast, fix fast" pattern that may indicate review gaps.
   206→
   207→### Step 6: Hotspot Analysis
   208→
   209→Show top 10 most-changed files. Flag:
   210→- Files changed 5+ times (churn hotspots)
   211→- Test files vs production files in the hotspot list
   212→- VERSION/CHANGELOG frequency (version discipline indicator)
   213→
   214→### Step 7: PR Size Distribution
   215→
   216→From commit diffs, estimate PR sizes and bucket them:
   217→- **Small** (<100 LOC)
   218→- **Medium** (100-500 LOC)
   219→- **Large** (500-1500 LOC)
   220→- **XL** (1500+ LOC) — flag these with file counts
   221→
   222→### Step 8: Focus Score + Ship of the Week
   223→
   224→**Focus score:** Calculate the percentage of commits touching the single most-changed top-level directory (e.g., `app/services/`, `app/views/`). Higher score = deeper focused work. Lower score = scattered context-switching. Report as: "Focus score: 62% (app/services/)"
   225→
   226→**Ship of the week:** Auto-identify the single highest-LOC PR in the window. Highlight it:
   227→- PR number and title
   228→- LOC changed
   229→- Why it matters (infer from commit messages and files touched)
   230→
   231→### Step 9: Team Member Analysis
   232→
   233→For each contributor (including the current user), compute:
   234→
   235→1. **Commits and LOC** — total commits, insertions, deletions, net LOC
   236→2. **Areas of focus** — which directories/files they touched most (top 3)
   237→3. **Commit type mix** — their personal feat/fix/refactor/test breakdown
   238→4. **Session patterns** — when they code (their peak hours), session count
   239→5. **Test discipline** — their personal test LOC ratio
   240→6. **Biggest ship** — their single highest-impact commit or PR in the window
   241→
   242→**For the current user ("You"):** This section gets the deepest treatment. Include all the detail from the solo retro — session analysis, time patterns, focus score. Frame it in first person: "Your peak hours...", "Your biggest ship..."
   243→
   244→**For each teammate:** Write 2-3 sentences covering what they worked on and their pattern. Then:
   245→
   246→- **Praise** (1-2 specific things): Anchor in actual commits. Not "great work" — say exactly what was good. Examples: "Shipped the entire auth middleware rewrite in 3 focused sessions with 45% test coverage", "Every PR under 200 LOC — disciplined decomposition."
   247→- **Opportunity for growth** (1 specific thing): Frame as a leveling-up suggestion, not criticism. Anchor in actual data. Examples: "Test ratio was 12% this week — adding test coverage to the payment module before it gets more complex would pay off", "5 fix commits on the same file suggest the original PR could have used a review pass."
   248→
   249→**If only one contributor (solo repo):** Skip the team breakdown and proceed as before — the retro is personal.
   250→
   251→**If there are Co-Authored-By trailers:** Parse `Co-Authored-By:` lines in commit messages. Credit those authors for the commit alongside the primary author. Note AI co-authors (e.g., `noreply@anthropic.com`) but do not include them as team members — instead, track "AI-assisted commits" as a separate metric.
   252→
   253→### Step 10: Week-over-Week Trends (if window >= 14d)
   254→
   255→If the time window is 14 days or more, split into weekly buckets and show trends:
   256→- Commits per week (total and per-author)
   257→- LOC per week
   258→- Test ratio per week
   259→- Fix ratio per week
   260→- Session count per week
   261→
   262→### Step 11: Streak Tracking
   263→
   264→Count consecutive days with at least 1 commit to origin/<default>, going back from today. Track both team streak and personal streak:
   265→
   266→```bash
   267→# Team streak: all unique commit dates (Pacific time) — no hard cutoff
   268→TZ=America/Los_Angeles git log origin/<default> --format="%ad" --date=format:"%Y-%m-%d" | sort -u
   269→
   270→# Personal streak: only the current user's commits
   271→TZ=America/Los_Angeles git log origin/<default> --author="<user_name>" --format="%ad" --date=format:"%Y-%m-%d" | sort -u
   272→```
   273→
   274→Count backward from today — how many consecutive days have at least one commit? This queries the full history so streaks of any length are reported accurately. Display both:
   275→- "Team shipping streak: 47 consecutive days"
   276→- "Your shipping streak: 32 consecutive days"
   277→
   278→### Step 12: Load History & Compare
   279→
   280→Before saving the new snapshot, check for prior retro history:
   281→
   282→```bash
   283→ls -t .context/retros/*.json 2>/dev/null
   284→```
   285→
   286→**If prior retros exist:** Load the most recent one using the Read tool. Calculate deltas for key metrics and include a **Trends vs Last Retro** section:
   287→```
   288→                    Last        Now         Delta
   289→Test ratio:         22%    →    41%         ↑19pp
   290→Sessions:           10     →    14          ↑4
   291→LOC/hour:           200    →    350         ↑75%
   292→Fix ratio:          54%    →    30%         ↓24pp (improving)
   293→Commits:            32     →    47          ↑47%
   294→Deep sessions:      3      →    5           ↑2
   295→```
   296→
   297→**If no prior retros exist:** Skip the comparison section and append: "First retro recorded — run again next week to see trends."
   298→
   299→### Step 13: Save Retro History
   300→
   301→After computing all metrics (including streak) and loading any prior history for comparison, save a JSON snapshot:
   302→
   303→```bash
   304→mkdir -p .context/retros
   305→```
   306→
   307→Determine the next sequence number for today (substitute the actual date for `$(date +%Y-%m-%d)`):
   308→```bash
   309→# Count existing retros for today to get next sequence number
   310→today=$(TZ=America/Los_Angeles date +%Y-%m-%d)
   311→existing=$(ls .context/retros/${today}-*.json 2>/dev/null | wc -l | tr -d ' ')
   312→next=$((existing + 1))
   313→# Save as .context/retros/${today}-${next}.json
   314→```
   315→
   316→Use the Write tool to save the JSON file with this schema:
   317→```json
   318→{
   319→  "date": "2026-03-08",
   320→  "window": "7d",
   321→  "metrics": {
   322→    "commits": 47,
   323→    "contributors": 3,
   324→    "prs_merged": 12,
   325→    "insertions": 3200,
   326→    "deletions": 800,
   327→    "net_loc": 2400,
   328→    "test_loc": 1300,
   329→    "test_ratio": 0.41,
   330→    "active_days": 6,
   331→    "sessions": 14,
   332→    "deep_sessions": 5,
   333→    "avg_session_minutes": 42,
   334→    "loc_per_session_hour": 350,
   335→    "feat_pct": 0.40,
   336→    "fix_pct": 0.30,
   337→    "peak_hour": 22,
   338→    "ai_assisted_commits": 32
   339→  },
   340→  "authors": {
   341→    "Garry Tan": { "commits": 32, "insertions": 2400, "deletions": 300, "test_ratio": 0.41, "top_area": "browse/" },
   342→    "Alice": { "commits": 12, "insertions": 800, "deletions": 150, "test_ratio": 0.35, "top_area": "app/services/" }
   343→  },
   344→  "version_range": ["1.16.0.0", "1.16.1.0"],
   345→  "streak_days": 47,
   346→  "tweetable": "Week of Mar 1: 47 commits (3 contributors), 3.2k LOC, 38% tests, 12 PRs, peak: 10pm",
   347→  "greptile": {
   348→    "fixes": 3,
   349→    "fps": 1,
   350→    "already_fixed": 2,
   351→    "signal_pct": 83
   352→  }
   353→}
   354→```
   355→
   356→**Note:** Only include the `greptile` field if `~/.gstack/greptile-history.md` exists and has entries within the time window. Only include the `backlog` field if `TODOS.md` exists. Only include the `test_health` field if test files were found (command 10 returns > 0). If any has no data, omit the field entirely.
   357→
   358→Include test health data in the JSON when test files exist:
   359→```json
   360→  "test_health": {
   361→    "total_test_files": 47,
   362→    "tests_added_this_period": 5,
   363→    "regression_test_commits": 3,
   364→    "test_files_changed": 8
   365→  }
   366→```
   367→
   368→Include backlog data in the JSON when TODOS.md exists:
   369→```json
   370→  "backlog": {
   371→    "total_open": 28,
   372→    "p0_p1": 2,
   373→    "p2": 8,
   374→    "completed_this_period": 3,
   375→    "added_this_period": 1
   376→  }
   377→```
   378→
   379→### Step 14: Write the Narrative
   380→
   381→Structure the output as:
   382→
   383→---
   384→
   385→**Tweetable summary** (first line, before everything else):
   386→```
   387→Week of Mar 1: 47 commits (3 contributors), 3.2k LOC, 38% tests, 12 PRs, peak: 10pm | Streak: 47d
   388→```
   389→
   390→## Engineering Retro: [date range]
   391→
   392→### Summary Table
   393→(from Step 2)
   394→
   395→### Trends vs Last Retro
   396→(from Step 11, loaded before save — skip if first retro)
   397→
   398→### Time & Session Patterns
   399→(from Steps 3-4)
   400→
   401→Narrative interpreting what the team-wide patterns mean:
   402→- When the most productive hours are and what drives them
   403→- Whether sessions are getting longer or shorter over time
   404→- Estimated hours per day of active coding (team aggregate)
   405→- Notable patterns: do team members code at the same time or in shifts?
   406→
   407→### Shipping Velocity
   408→(from Steps 5-7)
   409→
   410→Narrative covering:
   411→- Commit type mix and what it reveals
   412→- PR size discipline (are PRs staying small?)
   413→- Fix-chain detection (sequences of fix commits on the same subsystem)
   414→- Version bump discipline
   415→
   416→### Code Quality Signals
   417→- Test LOC ratio trend
   418→- Hotspot analysis (are the same files churning?)
   419→- Any XL PRs that should have been split
   420→- Greptile signal ratio and trend (if history exists): "Greptile: X% signal (Y valid catches, Z false positives)"
   421→
   422→### Test Health
   423→- Total test files: N (from command 10)
   424→- Tests added this period: M (from command 12 — test files changed)
   425→- Regression test commits: list `test(qa):` and `test(design):` and `test: coverage` commits from command 11
   426→- If prior retro exists and has `test_health`: show delta "Test count: {last} → {now} (+{delta})"
   427→- If test ratio < 20%: flag as growth area — "100% test coverage is the goal. Tests make vibe coding safe."
   428→
   429→### Focus & Highlights
   430→(from Step 8)
   431→- Focus score with interpretation
   432→- Ship of the week callout
   433→
   434→### Your Week (personal deep-dive)
   435→(from Step 9, for the current user only)
   436→
   437→This is the section the user cares most about. Include:
   438→- Their personal commit count, LOC, test ratio
   439→- Their session patterns and peak hours
   440→- Their focus areas
   441→- Their biggest ship
   442→- **What you did well** (2-3 specific things anchored in commits)
   443→- **Where to level up** (1-2 specific, actionable suggestions)
   444→
   445→### Team Breakdown
   446→(from Step 9, for each teammate — skip if solo repo)
   447→
   448→For each teammate (sorted by commits descending), write a section:
   449→
   450→#### [Name]
   451→- **What they shipped**: 2-3 sentences on their contributions, areas of focus, and commit patterns
   452→- **Praise**: 1-2 specific things they did well, anchored in actual commits. Be genuine — what would you actually say in a 1:1? Examples:
   453→  - "Cleaned up the entire auth module in 3 small, reviewable PRs — textbook decomposition"
   454→  - "Added integration tests for every new endpoint, not just happy paths"
   455→  - "Fixed the N+1 query that was causing 2s load times on the dashboard"
   456→- **Opportunity for growth**: 1 specific, constructive suggestion. Frame as investment, not criticism. Examples:
   457→  - "Test coverage on the payment module is at 8% — worth investing in before the next feature lands on top of it"
   458→  - "3 of the 5 PRs were 800+ LOC — breaking these up would catch issues earlier and make review easier"
   459→  - "All commits land between 1-4am — sustainable pace matters for code quality long-term"
   460→
   461→**AI collaboration note:** If many commits have `Co-Authored-By` AI trailers (e.g., Claude, Copilot), note the AI-assisted commit percentage as a team metric. Frame it neutrally — "N% of commits were AI-assisted" — without judgment.
   462→
   463→### Top 3 Team Wins
   464→Identify the 3 highest-impact things shipped in the window across the whole team. For each:
   465→- What it was
   466→- Who shipped it
   467→- Why it matters (product/architecture impact)
   468→
   469→### 3 Things to Improve
   470→Specific, actionable, anchored in actual commits. Mix personal and team-level suggestions. Phrase as "to get even better, the team could..."
   471→
   472→### 3 Habits for Next Week
   473→Small, practical, realistic. Each must be something that takes <5 minutes to adopt. At least one should be team-oriented (e.g., "review each other's PRs same-day").
   474→
   475→### Week-over-Week Trends
   476→(if applicable, from Step 10)
   477→
   478→---
   479→
   480→## Compare Mode
   481→
   482→When the user runs `/retro compare` (or `/retro compare 14d`):
   483→
   484→1. Compute metrics for the current window (default 7d) using `--since="7 days ago"`
   485→2. Compute metrics for the immediately prior same-length window using both `--since` and `--until` to avoid overlap (e.g., `--since="14 days ago" --until="7 days ago"` for a 7d window)
   486→3. Show a side-by-side comparison table with deltas and arrows
   487→4. Write a brief narrative highlighting the biggest improvements and regressions
   488→5. Save only the current-window snapshot to `.context/retros/` (same as a normal retro run); do **not** persist the prior-window metrics.
   489→
   490→## Tone
   491→
   492→- Encouraging but candid, no coddling
   493→- Specific and concrete — always anchor in actual commits/code
   494→- Skip generic praise ("great job!") — say exactly what was good and why
   495→- Frame improvements as leveling up, not criticism
   496→- **Praise should feel like something you'd actually say in a 1:1** — specific, earned, genuine
   497→- **Growth suggestions should feel like investment advice** — "this is worth your time because..." not "you failed at..."
   498→- Never compare teammates against each other negatively. Each person's section stands on its own.
   499→- Keep total output around 3000-4500 words (slightly longer to accommodate team sections)
   500→- Use markdown tables and code blocks for data, prose for narrative
   501→- Output directly to the conversation — do NOT write to filesystem (except the `.context/retros/` JSON snapshot)
   502→
   503→## Important Rules
   504→
   505→- ALL narrative output goes directly to the user in the conversation. The ONLY file written is the `.context/retros/` JSON snapshot.
   506→- Use `origin/<default>` for all git queries (not local main which may be stale)
   507→- Convert all timestamps to Pacific time for display (use `TZ=America/Los_Angeles`)
   508→- If the window has zero commits, say so and suggest a different window
   509→- Round LOC/hour to nearest 50
   510→- Treat merge commits as PR boundaries
   511→- Do not read CLAUDE.md or other docs — this skill is self-contained
   512→- On first run (no prior retros), skip comparison sections gracefully
   513→


     1→---
     2→name: document-release
     3→version: 1.0.0
     4→description: |
     5→  Post-ship documentation update. Reads all project docs, cross-references the
     6→  diff, updates README/ARCHITECTURE/CONTRIBUTING/CLAUDE.md to match what shipped,
     7→  polishes CHANGELOG voice, cleans up TODOS, and optionally bumps VERSION.
     8→allowed-tools:
     9→  - Bash
    10→  - Read
    11→  - Write
    12→  - Edit
    13→  - Grep
    14→  - Glob
    15→  - AskUserQuestion
    16→---
    17→
    18→{{PREAMBLE}}
    19→
    20→{{BASE_BRANCH_DETECT}}
    21→
    22→# Document Release: Post-Ship Documentation Update
    23→
    24→You are running the `/document-release` workflow. This runs **after `/ship`** (code committed, PR
    25→exists or about to exist) but **before the PR merges**. Your job: ensure every documentation file
    26→in the project is accurate, up to date, and written in a friendly, user-forward voice.
    27→
    28→You are mostly automated. Make obvious factual updates directly. Stop and ask only for risky or
    29→subjective decisions.
    30→
    31→**Only stop for:**
    32→- Risky/questionable doc changes (narrative, philosophy, security, removals, large rewrites)
    33→- VERSION bump decision (if not already bumped)
    34→- New TODOS items to add
    35→- Cross-doc contradictions that are narrative (not factual)
    36→
    37→**Never stop for:**
    38→- Factual corrections clearly from the diff
    39→- Adding items to tables/lists
    40→- Updating paths, counts, version numbers
    41→- Fixing stale cross-references
    42→- CHANGELOG voice polish (minor wording adjustments)
    43→- Marking TODOS complete
    44→- Cross-doc factual inconsistencies (e.g., version number mismatch)
    45→
    46→**NEVER do:**
    47→- Overwrite, replace, or regenerate CHANGELOG entries — polish wording only, preserve all content
    48→- Bump VERSION without asking — always use AskUserQuestion for version changes
    49→- Use `Write` tool on CHANGELOG.md — always use `Edit` with exact `old_string` matches
    50→
    51→---
    52→
    53→## Step 1: Pre-flight & Diff Analysis
    54→
    55→1. Check the current branch. If on the base branch, **abort**: "You're on the base branch. Run from a feature branch."
    56→
    57→2. Gather context about what changed:
    58→
    59→```bash
    60→git diff <base>...HEAD --stat
    61→```
    62→
    63→```bash
    64→git log <base>..HEAD --oneline
    65→```
    66→
    67→```bash
    68→git diff <base>...HEAD --name-only
    69→```
    70→
    71→3. Discover all documentation files in the repo:
    72→
    73→```bash
    74→find . -maxdepth 2 -name "*.md" -not -path "./.git/*" -not -path "./node_modules/*" -not -path "./.gstack/*" -not -path "./.context/*" | sort
    75→```
    76→
    77→4. Classify the changes into categories relevant to documentation:
    78→   - **New features** — new files, new commands, new skills, new capabilities
    79→   - **Changed behavior** — modified services, updated APIs, config changes
    80→   - **Removed functionality** — deleted files, removed commands
    81→   - **Infrastructure** — build system, test infrastructure, CI
    82→
    83→5. Output a brief summary: "Analyzing N files changed across M commits. Found K documentation files to review."
    84→
    85→---
    86→
    87→## Step 2: Per-File Documentation Audit
    88→
    89→Read each documentation file and cross-reference it against the diff. Use these generic heuristics
    90→(adapt to whatever project you're in — these are not gstack-specific):
    91→
    92→**README.md:**
    93→- Does it describe all features and capabilities visible in the diff?
    94→- Are install/setup instructions consistent with the changes?
    95→- Are examples, demos, and usage descriptions still valid?
    96→- Are troubleshooting steps still accurate?
    97→
    98→**ARCHITECTURE.md:**
    99→- Do ASCII diagrams and component descriptions match the current code?
   100→- Are design decisions and "why" explanations still accurate?
   101→- Be conservative — only update things clearly contradicted by the diff. Architecture docs
   102→  describe things unlikely to change frequently.
   103→
   104→**CONTRIBUTING.md — New contributor smoke test:**
   105→- Walk through the setup instructions as if you are a brand new contributor.
   106→- Are the listed commands accurate? Would each step succeed?
   107→- Do test tier descriptions match the current test infrastructure?
   108→- Are workflow descriptions (dev setup, contributor mode, etc.) current?
   109→- Flag anything that would fail or confuse a first-time contributor.
   110→
   111→**CLAUDE.md / project instructions:**
   112→- Does the project structure section match the actual file tree?
   113→- Are listed commands and scripts accurate?
   114→- Do build/test instructions match what's in package.json (or equivalent)?
   115→
   116→**Any other .md files:**
   117→- Read the file, determine its purpose and audience.
   118→- Cross-reference against the diff to check if it contradicts anything the file says.
   119→
   120→For each file, classify needed updates as:
   121→
   122→- **Auto-update** — Factual corrections clearly warranted by the diff: adding an item to a
   123→  table, updating a file path, fixing a count, updating a project structure tree.
   124→- **Ask user** — Narrative changes, section removal, security model changes, large rewrites
   125→  (more than ~10 lines in one section), ambiguous relevance, adding entirely new sections.
   126→
   127→---
   128→
   129→## Step 3: Apply Auto-Updates
   130→
   131→Make all clear, factual updates directly using the Edit tool.
   132→
   133→For each file modified, output a one-line summary describing **what specifically changed** — not
   134→just "Updated README.md" but "README.md: added /new-skill to skills table, updated skill count
   135→from 9 to 10."
   136→
   137→**Never auto-update:**
   138→- README introduction or project positioning
   139→- ARCHITECTURE philosophy or design rationale
   140→- Security model descriptions
   141→- Do not remove entire sections from any document
   142→
   143→---
   144→
   145→## Step 4: Ask About Risky/Questionable Changes
   146→
   147→For each risky or questionable update identified in Step 2, use AskUserQuestion with:
   148→- Context: project name, branch, which doc file, what we're reviewing
   149→- The specific documentation decision
   150→- `RECOMMENDATION: Choose [X] because [one-line reason]`
   151→- Options including C) Skip — leave as-is
   152→
   153→Apply approved changes immediately after each answer.
   154→
   155→---
   156→
   157→## Step 5: CHANGELOG Voice Polish
   158→
   159→**CRITICAL — NEVER CLOBBER CHANGELOG ENTRIES.**
   160→
   161→This step polishes voice. It does NOT rewrite, replace, or regenerate CHANGELOG content.
   162→
   163→A real incident occurred where an agent replaced existing CHANGELOG entries when it should have
   164→preserved them. This skill must NEVER do that.
   165→
   166→**Rules:**
   167→1. Read the entire CHANGELOG.md first. Understand what is already there.
   168→2. Only modify wording within existing entries. Never delete, reorder, or replace entries.
   169→3. Never regenerate a CHANGELOG entry from scratch. The entry was written by `/ship` from the
   170→   actual diff and commit history. It is the source of truth. You are polishing prose, not
   171→   rewriting history.
   172→4. If an entry looks wrong or incomplete, use AskUserQuestion — do NOT silently fix it.
   173→5. Use Edit tool with exact `old_string` matches — never use Write to overwrite CHANGELOG.md.
   174→
   175→**If CHANGELOG was not modified in this branch:** skip this step.
   176→
   177→**If CHANGELOG was modified in this branch**, review the entry for voice:
   178→
   179→- **Sell test:** Would a user reading each bullet think "oh nice, I want to try that"? If not,
   180→  rewrite the wording (not the content).
   181→- Lead with what the user can now **do** — not implementation details.
   182→- "You can now..." not "Refactored the..."
   183→- Flag and rewrite any entry that reads like a commit message.
   184→- Internal/contributor changes belong in a separate "### For contributors" subsection.
   185→- Auto-fix minor voice adjustments. Use AskUserQuestion if a rewrite would alter meaning.
   186→
   187→---
   188→
   189→## Step 6: Cross-Doc Consistency & Discoverability Check
   190→
   191→After auditing each file individually, do a cross-doc consistency pass:
   192→
   193→1. Does the README's feature/capability list match what CLAUDE.md (or project instructions) describes?
   194→2. Does ARCHITECTURE's component list match CONTRIBUTING's project structure description?
   195→3. Does CHANGELOG's latest version match the VERSION file?
   196→4. **Discoverability:** Is every documentation file reachable from README.md or CLAUDE.md? If
   197→   ARCHITECTURE.md exists but neither README nor CLAUDE.md links to it, flag it. Every doc
   198→   should be discoverable from one of the two entry-point files.
   199→5. Flag any contradictions between documents. Auto-fix clear factual inconsistencies (e.g., a
   200→   version mismatch). Use AskUserQuestion for narrative contradictions.
   201→
   202→---
   203→
   204→## Step 7: TODOS.md Cleanup
   205→
   206→This is a second pass that complements `/ship`'s Step 5.5. Read `review/TODOS-format.md` (if
   207→available) for the canonical TODO item format.
   208→
   209→If TODOS.md does not exist, skip this step.
   210→
   211→1. **Completed items not yet marked:** Cross-reference the diff against open TODO items. If a
   212→   TODO is clearly completed by the changes in this branch, move it to the Completed section
   213→   with `**Completed:** vX.Y.Z.W (YYYY-MM-DD)`. Be conservative — only mark items with clear
   214→   evidence in the diff.
   215→
   216→2. **Items needing description updates:** If a TODO references files or components that were
   217→   significantly changed, its description may be stale. Use AskUserQuestion to confirm whether
   218→   the TODO should be updated, completed, or left as-is.
   219→
   220→3. **New deferred work:** Check the diff for `TODO`, `FIXME`, `HACK`, and `XXX` comments. For
   221→   each one that represents meaningful deferred work (not a trivial inline note), use
   222→   AskUserQuestion to ask whether it should be captured in TODOS.md.
   223→
   224→---
   225→
   226→## Step 8: VERSION Bump Question
   227→
   228→**CRITICAL — NEVER BUMP VERSION WITHOUT ASKING.**
   229→
   230→1. **If VERSION does not exist:** Skip silently.
   231→
   232→2. Check if VERSION was already modified on this branch:
   233→
   234→```bash
   235→git diff <base>...HEAD -- VERSION
   236→```
   237→
   238→3. **If VERSION was NOT bumped:** Use AskUserQuestion:
   239→   - RECOMMENDATION: Choose C (Skip) because docs-only changes rarely warrant a version bump
   240→   - A) Bump PATCH (X.Y.Z+1) — if doc changes ship alongside code changes
   241→   - B) Bump MINOR (X.Y+1.0) — if this is a significant standalone release
   242→   - C) Skip — no version bump needed
   243→
   244→4. **If VERSION was already bumped:** Do NOT skip silently. Instead, check whether the bump
   245→   still covers the full scope of changes on this branch:
   246→
   247→   a. Read the CHANGELOG entry for the current VERSION. What features does it describe?
   248→   b. Read the full diff (`git diff <base>...HEAD --stat` and `git diff <base>...HEAD --name-only`).
   249→      Are there significant changes (new features, new skills, new commands, major refactors)
   250→      that are NOT mentioned in the CHANGELOG entry for the current version?
   251→   c. **If the CHANGELOG entry covers everything:** Skip — output "VERSION: Already bumped to
   252→      vX.Y.Z, covers all changes."
   253→   d. **If there are significant uncovered changes:** Use AskUserQuestion explaining what the
   254→      current version covers vs what's new, and ask:
   255→      - RECOMMENDATION: Choose A because the new changes warrant their own version
   256→      - A) Bump to next patch (X.Y.Z+1) — give the new changes their own version
   257→      - B) Keep current version — add new changes to the existing CHANGELOG entry
   258→      - C) Skip — leave version as-is, handle later
   259→
   260→   The key insight: a VERSION bump set for "feature A" should not silently absorb "feature B"
   261→   if feature B is substantial enough to deserve its own version entry.
   262→
   263→---
   264→
   265→## Step 9: Commit & Output
   266→
   267→**Empty check first:** Run `git status` (never use `-uall`). If no documentation files were
   268→modified by any previous step, output "All documentation is up to date." and exit without
   269→committing.
   270→
   271→**Commit:**
   272→
   273→1. Stage modified documentation files by name (never `git add -A` or `git add .`).
   274→2. Create a single commit:
   275→
   276→```bash
   277→git commit -m "$(cat <<'EOF'
   278→docs: update project documentation for vX.Y.Z.W
   279→
   280→Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
   281→EOF
   282→)"
   283→```
   284→
   285→3. Push to the current branch:
   286→
   287→```bash
   288→git push
   289→```
   290→
   291→**PR body update (idempotent, race-safe):**
   292→
   293→1. Read the existing PR body into a PID-unique tempfile:
   294→
   295→```bash
   296→gh pr view --json body -q .body > /tmp/gstack-pr-body-$$.md
   297→```
   298→
   299→2. If the tempfile already contains a `## Documentation` section, replace that section with the
   300→   updated content. If it does not contain one, append a `## Documentation` section at the end.
   301→
   302→3. The Documentation section should include a **doc diff preview** — for each file modified,
   303→   describe what specifically changed (e.g., "README.md: added /document-release to skills
   304→   table, updated skill count from 9 to 10").
   305→
   306→4. Write the updated body back:
   307→
   308→```bash
   309→gh pr edit --body-file /tmp/gstack-pr-body-$$.md
   310→```
   311→
   312→5. Clean up the tempfile:
   313→
   314→```bash
   315→rm -f /tmp/gstack-pr-body-$$.md
   316→```
   317→
   318→6. If `gh pr view` fails (no PR exists): skip with message "No PR found — skipping body update."
   319→7. If `gh pr edit` fails: warn "Could not update PR body — documentation changes are in the
   320→   commit." and continue.
   321→
   322→**Structured doc health summary (final output):**
   323→
   324→Output a scannable summary showing every documentation file's status:
   325→
   326→```
   327→Documentation health:
   328→  README.md       [status] ([details])
   329→  ARCHITECTURE.md [status] ([details])
   330→  CONTRIBUTING.md [status] ([details])
   331→  CHANGELOG.md    [status] ([details])
   332→  TODOS.md        [status] ([details])
   333→  VERSION         [status] ([details])
   334→```
   335→
   336→Where status is one of:
   337→- Updated — with description of what changed
   338→- Current — no changes needed
   339→- Voice polished — wording adjusted
   340→- Not bumped — user chose to skip
   341→- Already bumped — version was set by /ship
   342→- Skipped — file does not exist
   343→
   344→---
   345→
   346→## Important Rules
   347→
   348→- **Read before editing.** Always read the full content of a file before modifying it.
   349→- **Never clobber CHANGELOG.** Polish wording only. Never delete, replace, or regenerate entries.
   350→- **Never bump VERSION silently.** Always ask. Even if already bumped, check whether it covers the full scope of changes.
   351→- **Be explicit about what changed.** Every edit gets a one-line summary.
   352→- **Generic heuristics, not project-specific.** The audit checks work on any repo.
   353→- **Discoverability matters.** Every doc file should be reachable from README or CLAUDE.md.
   354→- **Voice: friendly, user-forward, not obscure.** Write like you're explaining to a smart person
   355→  who hasn't seen the code.
   356→


     1→---
     2→name: qa
     3→version: 2.0.0
     4→description: |
     5→  Systematically QA test a web application and fix bugs found. Runs QA testing,
     6→  then iteratively fixes bugs in source code, committing each fix atomically and
     7→  re-verifying. Use when asked to "qa", "QA", "test this site", "find bugs",
     8→  "test and fix", or "fix what's broken". Three tiers: Quick (critical/high only),
     9→  Standard (+ medium), Exhaustive (+ cosmetic). Produces before/after health scores,
    10→  fix evidence, and a ship-readiness summary. For report-only mode, use /qa-only.
    11→allowed-tools:
    12→  - Bash
    13→  - Read
    14→  - Write
    15→  - Edit
    16→  - Glob
    17→  - Grep
    18→  - AskUserQuestion
    19→  - WebSearch
    20→---
    21→
    22→{{PREAMBLE}}
    23→
    24→{{BASE_BRANCH_DETECT}}
    25→
    26→# /qa: Test → Fix → Verify
    27→
    28→You are a QA engineer AND a bug-fix engineer. Test web applications like a real user — click everything, fill every form, check every state. When you find bugs, fix them in source code with atomic commits, then re-verify. Produce a structured report with before/after evidence.
    29→
    30→## Setup
    31→
    32→**Parse the user's request for these parameters:**
    33→
    34→| Parameter | Default | Override example |
    35→|-----------|---------|-----------------:|
    36→| Target URL | (auto-detect or required) | `https://myapp.com`, `http://localhost:3000` |
    37→| Tier | Standard | `--quick`, `--exhaustive` |
    38→| Mode | full | `--regression .gstack/qa-reports/baseline.json` |
    39→| Output dir | `.gstack/qa-reports/` | `Output to /tmp/qa` |
    40→| Scope | Full app (or diff-scoped) | `Focus on the billing page` |
    41→| Auth | None | `Sign in to user@example.com`, `Import cookies from cookies.json` |
    42→
    43→**Tiers determine which issues get fixed:**
    44→- **Quick:** Fix critical + high severity only
    45→- **Standard:** + medium severity (default)
    46→- **Exhaustive:** + low/cosmetic severity
    47→
    48→**If no URL is given and you're on a feature branch:** Automatically enter **diff-aware mode** (see Modes below). This is the most common case — the user just shipped code on a branch and wants to verify it works.
    49→
    50→**Require clean working tree before starting:**
    51→```bash
    52→if [ -n "$(git status --porcelain)" ]; then
    53→  echo "ERROR: Working tree is dirty. Commit or stash changes before running /qa."
    54→  exit 1
    55→fi
    56→```
    57→
    58→**Find the browse binary:**
    59→
    60→{{BROWSE_SETUP}}
    61→
    62→**Check test framework (bootstrap if needed):**
    63→
    64→{{TEST_BOOTSTRAP}}
    65→
    66→**Create output directories:**
    67→
    68→```bash
    69→mkdir -p .gstack/qa-reports/screenshots
    70→```
    71→
    72→---
    73→
    74→## Test Plan Context
    75→
    76→Before falling back to git diff heuristics, check for richer test plan sources:
    77→
    78→1. **Project-scoped test plans:** Check `~/.gstack/projects/` for recent `*-test-plan-*.md` files for this repo
    79→   ```bash
    80→   eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)
    81→   ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1
    82→   ```
    83→2. **Conversation context:** Check if a prior `/plan-eng-review` or `/plan-ceo-review` produced test plan output in this conversation
    84→3. **Use whichever source is richer.** Fall back to git diff analysis only if neither is available.
    85→
    86→---
    87→
    88→## Phases 1-6: QA Baseline
    89→
    90→{{QA_METHODOLOGY}}
    91→
    92→Record baseline health score at end of Phase 6.
    93→
    94→---
    95→
    96→## Output Structure
    97→
    98→```
    99→.gstack/qa-reports/
   100→├── qa-report-{domain}-{YYYY-MM-DD}.md    # Structured report
   101→├── screenshots/
   102→│   ├── initial.png                        # Landing page annotated screenshot
   103→│   ├── issue-001-step-1.png               # Per-issue evidence
   104→│   ├── issue-001-result.png
   105→│   ├── issue-001-before.png               # Before fix (if fixed)
   106→│   ├── issue-001-after.png                # After fix (if fixed)
   107→│   └── ...
   108→└── baseline.json                          # For regression mode
   109→```
   110→
   111→Report filenames use the domain and date: `qa-report-myapp-com-2026-03-12.md`
   112→
   113→---
   114→
   115→## Phase 7: Triage
   116→
   117→Sort all discovered issues by severity, then decide which to fix based on the selected tier:
   118→
   119→- **Quick:** Fix critical + high only. Mark medium/low as "deferred."
   120→- **Standard:** Fix critical + high + medium. Mark low as "deferred."
   121→- **Exhaustive:** Fix all, including cosmetic/low severity.
   122→
   123→Mark issues that cannot be fixed from source code (e.g., third-party widget bugs, infrastructure issues) as "deferred" regardless of tier.
   124→
   125→---
   126→
   127→## Phase 8: Fix Loop
   128→
   129→For each fixable issue, in severity order:
   130→
   131→### 8a. Locate source
   132→
   133→```bash
   134→# Grep for error messages, component names, route definitions
   135→# Glob for file patterns matching the affected page
   136→```
   137→
   138→- Find the source file(s) responsible for the bug
   139→- ONLY modify files directly related to the issue
   140→
   141→### 8b. Fix
   142→
   143→- Read the source code, understand the context
   144→- Make the **minimal fix** — smallest change that resolves the issue
   145→- Do NOT refactor surrounding code, add features, or "improve" unrelated things
   146→
   147→### 8c. Commit
   148→
   149→```bash
   150→git add <only-changed-files>
   151→git commit -m "fix(qa): ISSUE-NNN — short description"
   152→```
   153→
   154→- One commit per fix. Never bundle multiple fixes.
   155→- Message format: `fix(qa): ISSUE-NNN — short description`
   156→
   157→### 8d. Re-test
   158→
   159→- Navigate back to the affected page
   160→- Take **before/after screenshot pair**
   161→- Check console for errors
   162→- Use `snapshot -D` to verify the change had the expected effect
   163→
   164→```bash
   165→$B goto <affected-url>
   166→$B screenshot "$REPORT_DIR/screenshots/issue-NNN-after.png"
   167→$B console --errors
   168→$B snapshot -D
   169→```
   170→
   171→### 8e. Classify
   172→
   173→- **verified**: re-test confirms the fix works, no new errors introduced
   174→- **best-effort**: fix applied but couldn't fully verify (e.g., needs auth state, external service)
   175→- **reverted**: regression detected → `git revert HEAD` → mark issue as "deferred"
   176→
   177→### 8e.5. Regression Test
   178→
   179→Skip if: classification is not "verified", OR the fix is purely visual/CSS with no JS behavior, OR no test framework was detected AND user declined bootstrap.
   180→
   181→**1. Study the project's existing test patterns:**
   182→
   183→Read 2-3 test files closest to the fix (same directory, same code type). Match exactly:
   184→- File naming, imports, assertion style, describe/it nesting, setup/teardown patterns
   185→The regression test must look like it was written by the same developer.
   186→
   187→**2. Trace the bug's codepath, then write a regression test:**
   188→
   189→Before writing the test, trace the data flow through the code you just fixed:
   190→- What input/state triggered the bug? (the exact precondition)
   191→- What codepath did it follow? (which branches, which function calls)
   192→- Where did it break? (the exact line/condition that failed)
   193→- What other inputs could hit the same codepath? (edge cases around the fix)
   194→
   195→The test MUST:
   196→- Set up the precondition that triggered the bug (the exact state that made it break)
   197→- Perform the action that exposed the bug
   198→- Assert the correct behavior (NOT "it renders" or "it doesn't throw")
   199→- If you found adjacent edge cases while tracing, test those too (e.g., null input, empty array, boundary value)
   200→- Include full attribution comment:
   201→  ```
   202→  // Regression: ISSUE-NNN — {what broke}
   203→  // Found by /qa on {YYYY-MM-DD}
   204→  // Report: .gstack/qa-reports/qa-report-{domain}-{date}.md
   205→  ```
   206→
   207→Test type decision:
   208→- Console error / JS exception / logic bug → unit or integration test
   209→- Broken form / API failure / data flow bug → integration test with request/response
   210→- Visual bug with JS behavior (broken dropdown, animation) → component test
   211→- Pure CSS → skip (caught by QA reruns)
   212→
   213→Generate unit tests. Mock all external dependencies (DB, API, Redis, file system).
   214→
   215→Use auto-incrementing names to avoid collisions: check existing `{name}.regression-*.test.{ext}` files, take max number + 1.
   216→
   217→**3. Run only the new test file:**
   218→
   219→```bash
   220→{detected test command} {new-test-file}
   221→```
   222→
   223→**4. Evaluate:**
   224→- Passes → commit: `git commit -m "test(qa): regression test for ISSUE-NNN — {desc}"`
   225→- Fails → fix test once. Still failing → delete test, defer.
   226→- Taking >2 min exploration → skip and defer.
   227→
   228→**5. WTF-likelihood exclusion:** Test commits don't count toward the heuristic.
   229→
   230→### 8f. Self-Regulation (STOP AND EVALUATE)
   231→
   232→Every 5 fixes (or after any revert), compute the WTF-likelihood:
   233→
   234→```
   235→WTF-LIKELIHOOD:
   236→  Start at 0%
   237→  Each revert:                +15%
   238→  Each fix touching >3 files: +5%
   239→  After fix 15:               +1% per additional fix
   240→  All remaining Low severity: +10%
   241→  Touching unrelated files:   +20%
   242→```
   243→
   244→**If WTF > 20%:** STOP immediately. Show the user what you've done so far. Ask whether to continue.
   245→
   246→**Hard cap: 50 fixes.** After 50 fixes, stop regardless of remaining issues.
   247→
   248→---
   249→
   250→## Phase 9: Final QA
   251→
   252→After all fixes are applied:
   253→
   254→1. Re-run QA on all affected pages
   255→2. Compute final health score
   256→3. **If final score is WORSE than baseline:** WARN prominently — something regressed
   257→
   258→---
   259→
   260→## Phase 10: Report
   261→
   262→Write the report to both local and project-scoped locations:
   263→
   264→**Local:** `.gstack/qa-reports/qa-report-{domain}-{YYYY-MM-DD}.md`
   265→
   266→**Project-scoped:** Write test outcome artifact for cross-session context:
   267→```bash
   268→eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)
   269→mkdir -p ~/.gstack/projects/$SLUG
   270→```
   271→Write to `~/.gstack/projects/{slug}/{user}-{branch}-test-outcome-{datetime}.md`
   272→
   273→**Per-issue additions** (beyond standard report template):
   274→- Fix Status: verified / best-effort / reverted / deferred
   275→- Commit SHA (if fixed)
   276→- Files Changed (if fixed)
   277→- Before/After screenshots (if fixed)
   278→
   279→**Summary section:**
   280→- Total issues found
   281→- Fixes applied (verified: X, best-effort: Y, reverted: Z)
   282→- Deferred issues
   283→- Health score delta: baseline → final
   284→
   285→**PR Summary:** Include a one-line summary suitable for PR descriptions:
   286→> "QA found N issues, fixed M, health score X → Y."
   287→
   288→---
   289→
   290→## Phase 11: TODOS.md Update
   291→
   292→If the repo has a `TODOS.md`:
   293→
   294→1. **New deferred bugs** → add as TODOs with severity, category, and repro steps
   295→2. **Fixed bugs that were in TODOS.md** → annotate with "Fixed by /qa on {branch}, {date}"
   296→
   297→---
   298→
   299→## Additional Rules (qa-specific)
   300→
   301→11. **Clean working tree required.** Refuse to start if `git status --porcelain` is non-empty.
   302→12. **One commit per fix.** Never bundle multiple fixes into one commit.
   303→13. **Only modify tests when generating regression tests in Phase 8e.5.** Never modify CI configuration. Never modify existing tests — only create new test files.
   304→14. **Revert on regression.** If a fix makes things worse, `git revert HEAD` immediately.
   305→15. **Self-regulate.** Follow the WTF-likelihood heuristic. When in doubt, stop and ask.
   306→


     1→---
     2→name: review
     3→version: 1.0.0
     4→description: |
     5→  Pre-landing PR review. Analyzes diff against the base branch for SQL safety, LLM trust
     6→  boundary violations, conditional side effects, and other structural issues.
     7→allowed-tools:
     8→  - Bash
     9→  - Read
    10→  - Edit
    11→  - Write
    12→  - Grep
    13→  - Glob
    14→  - AskUserQuestion
    15→---
    16→
    17→{{PREAMBLE}}
    18→
    19→{{BASE_BRANCH_DETECT}}
    20→
    21→# Pre-Landing PR Review
    22→
    23→You are running the `/review` workflow. Analyze the current branch's diff against the base branch for structural issues that tests don't catch.
    24→
    25→---
    26→
    27→## Step 1: Check branch
    28→
    29→1. Run `git branch --show-current` to get the current branch.
    30→2. If on the base branch, output: **"Nothing to review — you're on the base branch or have no changes against it."** and stop.
    31→3. Run `git fetch origin <base> --quiet && git diff origin/<base> --stat` to check if there's a diff. If no diff, output the same message and stop.
    32→
    33→---
    34→
    35→## Step 2: Read the checklist
    36→
    37→Read `.claude/skills/review/checklist.md`.
    38→
    39→**If the file cannot be read, STOP and report the error.** Do not proceed without the checklist.
    40→
    41→---
    42→
    43→## Step 2.5: Check for Greptile review comments
    44→
    45→Read `.claude/skills/review/greptile-triage.md` and follow the fetch, filter, classify, and **escalation detection** steps.
    46→
    47→**If no PR exists, `gh` fails, API returns an error, or there are zero Greptile comments:** Skip this step silently. Greptile integration is additive — the review works without it.
    48→
    49→**If Greptile comments are found:** Store the classifications (VALID & ACTIONABLE, VALID BUT ALREADY FIXED, FALSE POSITIVE, SUPPRESSED) — you will need them in Step 5.
    50→
    51→---
    52→
    53→## Step 3: Get the diff
    54→
    55→Fetch the latest base branch to avoid false positives from stale local state:
    56→
    57→```bash
    58→git fetch origin <base> --quiet
    59→```
    60→
    61→Run `git diff origin/<base>` to get the full diff. This includes both committed and uncommitted changes against the latest base branch.
    62→
    63→---
    64→
    65→## Step 4: Two-pass review
    66→
    67→Apply the checklist against the diff in two passes:
    68→
    69→1. **Pass 1 (CRITICAL):** SQL & Data Safety, Race Conditions & Concurrency, LLM Output Trust Boundary, Enum & Value Completeness
    70→2. **Pass 2 (INFORMATIONAL):** Conditional Side Effects, Magic Numbers & String Coupling, Dead Code & Consistency, LLM Prompt Issues, Test Gaps, View/Frontend
    71→
    72→**Enum & Value Completeness requires reading code OUTSIDE the diff.** When the diff introduces a new enum value, status, tier, or type constant, use Grep to find all files that reference sibling values, then Read those files to check if the new value is handled. This is the one category where within-diff review is insufficient.
    73→
    74→Follow the output format specified in the checklist. Respect the suppressions — do NOT flag items listed in the "DO NOT flag" section.
    75→
    76→---
    77→
    78→## Step 4.5: Design Review (conditional)
    79→
    80→{{DESIGN_REVIEW_LITE}}
    81→
    82→Include any design findings alongside the findings from Step 4. They follow the same Fix-First flow in Step 5 — AUTO-FIX for mechanical CSS fixes, ASK for everything else.
    83→
    84→---
    85→
    86→## Step 5: Fix-First Review
    87→
    88→**Every finding gets action — not just critical ones.**
    89→
    90→Output a summary header: `Pre-Landing Review: N issues (X critical, Y informational)`
    91→
    92→### Step 5a: Classify each finding
    93→
    94→For each finding, classify as AUTO-FIX or ASK per the Fix-First Heuristic in
    95→checklist.md. Critical findings lean toward ASK; informational findings lean
    96→toward AUTO-FIX.
    97→
    98→### Step 5b: Auto-fix all AUTO-FIX items
    99→
   100→Apply each fix directly. For each one, output a one-line summary:
   101→`[AUTO-FIXED] [file:line] Problem → what you did`
   102→
   103→### Step 5c: Batch-ask about ASK items
   104→
   105→If there are ASK items remaining, present them in ONE AskUserQuestion:
   106→
   107→- List each item with a number, the severity label, the problem, and a recommended fix
   108→- For each item, provide options: A) Fix as recommended, B) Skip
   109→- Include an overall RECOMMENDATION
   110→
   111→Example format:
   112→```
   113→I auto-fixed 5 issues. 2 need your input:
   114→
   115→1. [CRITICAL] app/models/post.rb:42 — Race condition in status transition
   116→   Fix: Add `WHERE status = 'draft'` to the UPDATE
   117→   → A) Fix  B) Skip
   118→
   119→2. [INFORMATIONAL] app/services/generator.rb:88 — LLM output not type-checked before DB write
   120→   Fix: Add JSON schema validation
   121→   → A) Fix  B) Skip
   122→
   123→RECOMMENDATION: Fix both — #1 is a real race condition, #2 prevents silent data corruption.
   124→```
   125→
   126→If 3 or fewer ASK items, you may use individual AskUserQuestion calls instead of batching.
   127→
   128→### Step 5d: Apply user-approved fixes
   129→
   130→Apply fixes for items where the user chose "Fix." Output what was fixed.
   131→
   132→If no ASK items exist (everything was AUTO-FIX), skip the question entirely.
   133→
   134→### Greptile comment resolution
   135→
   136→After outputting your own findings, if Greptile comments were classified in Step 2.5:
   137→
   138→**Include a Greptile summary in your output header:** `+ N Greptile comments (X valid, Y fixed, Z FP)`
   139→
   140→Before replying to any comment, run the **Escalation Detection** algorithm from greptile-triage.md to determine whether to use Tier 1 (friendly) or Tier 2 (firm) reply templates.
   141→
   142→1. **VALID & ACTIONABLE comments:** These are included in your findings — they follow the Fix-First flow (auto-fixed if mechanical, batched into ASK if not) (A: Fix it now, B: Acknowledge, C: False positive). If the user chooses A (fix), reply using the **Fix reply template** from greptile-triage.md (include inline diff + explanation). If the user chooses C (false positive), reply using the **False Positive reply template** (include evidence + suggested re-rank), save to both per-project and global greptile-history.
   143→
   144→2. **FALSE POSITIVE comments:** Present each one via AskUserQuestion:
   145→   - Show the Greptile comment: file:line (or [top-level]) + body summary + permalink URL
   146→   - Explain concisely why it's a false positive
   147→   - Options:
   148→     - A) Reply to Greptile explaining why this is incorrect (recommended if clearly wrong)
   149→     - B) Fix it anyway (if low-effort and harmless)
   150→     - C) Ignore — don't reply, don't fix


     1→---
     2→name: qa
     3→version: 2.0.0
     4→description: |
     5→  Systematically QA test a web application and fix bugs found. Runs QA testing,
     6→  then iteratively fixes bugs in source code, committing each fix atomically and
     7→  re-verifying. Use when asked to "qa", "QA", "test this site", "find bugs",
     8→  "test and fix", or "fix what's broken". Three tiers: Quick (critical/high only),
     9→  Standard (+ medium), Exhaustive (+ cosmetic). Produces before/after health scores,
    10→  fix evidence, and a ship-readiness summary. For report-only mode, use /qa-only.
    11→allowed-tools:
    12→  - Bash
    13→  - Read
    14→  - Write
    15→  - Edit
    16→  - Glob
    17→  - Grep
    18→  - AskUserQuestion
    19→  - WebSearch
    20→---
    21→
    22→{{PREAMBLE}}
    23→
    24→{{BASE_BRANCH_DETECT}}
    25→
    26→# /qa: Test → Fix → Verify
    27→
    28→You are a QA engineer AND a bug-fix engineer. Test web applications like a real user — click everything, fill every form, check every state. When you find bugs, fix them in source code with atomic commits, then re-verify. Produce a structured report with before/after evidence.
    29→
    30→## Setup
    31→
    32→**Parse the user's request for these parameters:**
    33→
    34→| Parameter | Default | Override example |
    35→|-----------|---------|-----------------:|
    36→| Target URL | (auto-detect or required) | `https://myapp.com`, `http://localhost:3000` |
    37→| Tier | Standard | `--quick`, `--exhaustive` |
    38→| Mode | full | `--regression .gstack/qa-reports/baseline.json` |
    39→| Output dir | `.gstack/qa-reports/` | `Output to /tmp/qa` |
    40→| Scope | Full app (or diff-scoped) | `Focus on the billing page` |
    41→| Auth | None | `Sign in to user@example.com`, `Import cookies from cookies.json` |
    42→
    43→**Tiers determine which issues get fixed:**
    44→- **Quick:** Fix critical + high severity only
    45→- **Standard:** + medium severity (default)
    46→- **Exhaustive:** + low/cosmetic severity
    47→
    48→**If no URL is given and you're on a feature branch:** Automatically enter **diff-aware mode** (see Modes below). This is the most common case — the user just shipped code on a branch and wants to verify it works.
    49→
    50→**Require clean working tree before starting:**
    51→```bash
    52→if [ -n "$(git status --porcelain)" ]; then
    53→  echo "ERROR: Working tree is dirty. Commit or stash changes before running /qa."
    54→  exit 1
    55→fi
    56→```
    57→
    58→**Find the browse binary:**
    59→
    60→{{BROWSE_SETUP}}
    61→
    62→**Check test framework (bootstrap if needed):**
    63→
    64→{{TEST_BOOTSTRAP}}
    65→
    66→**Create output directories:**
    67→
    68→```bash
    69→mkdir -p .gstack/qa-reports/screenshots
    70→```
    71→
    72→---
    73→
    74→## Test Plan Context
    75→
    76→Before falling back to git diff heuristics, check for richer test plan sources:
    77→
    78→1. **Project-scoped test plans:** Check `~/.gstack/projects/` for recent `*-test-plan-*.md` files for this repo
    79→   ```bash
    80→   eval $(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)
    81→   ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1
    82→   ```
    83→2. **Conversation context:** Check if a prior `/plan-eng-review` or `/plan-ceo-review` produced test plan output in this conversation
    84→3. **Use whichever source is richer.** Fall back to git diff analysis only if neither is available.
    85→
    86→---
    87→
    88→## Phases 1-6: QA Baseline
    89→
    90→{{QA_METHODOLOGY}}
    91→
    92→Record baseline health score at end of Phase 6.
    93→
    94→---
    95→
    96→## Output Structure
    97→
    98→```
    99→.gstack/qa-reports/
   100→├── qa-report-{domain}-{YYYY-MM-DD}.md    # Structured report
   101→├── screenshots/
   102→│   ├── initial.png                        # Landing page annotated screenshot
   103→│   ├── issue-001-step-1.png               # Per-issue evidence
   104→│   ├── issue-001-result.png
   105→│   ├── issue-001-before.png               # Before fix (if fixed)
   106→│   ├── issue-001-after.png                # After fix (if fixed)
   107→│   └── ...
   108→└── baseline.json                          # For regression mode
   109→```
   110→
   111→Report filenames use the domain and date: `qa-report-myapp-com-2026-03-12.md`
   112→
   113→---
   114→
   115→## Phase 7: Triage
   116→
   117→Sort all discovered issues by severity, then decide which to fix based on the selected tier:
   118→
   119→- **Quick:** Fix critical + high only. Mark medium/low as "deferred."
   120→- **Standard:** Fix critical + high + medium. Mark low as "deferred."
   121→- **Exhaustive:** Fix all, including cosmetic/low severity.
   122→
   123→Mark issues that cannot be fixed from source code (e.g., third-party widget bugs, infrastructure issues) as "deferred" regardless of tier.
   124→
   125→---
   126→
   127→## Phase 8: Fix Loop
   128→
   129→For each fixable issue, in severity order:
   130→
   131→### 8a. Locate source
   132→
   133→```bash
   134→# Grep for error messages, component names, route definitions
   135→# Glob for file patterns matching the affected page
   136→```
   137→
   138→- Find the source file(s) responsible for the bug
   139→- ONLY modify files directly related to the issue
   140→
   141→### 8b. Fix
   142→
   143→- Read the source code, understand the context
   144→- Make the **minimal fix** — smallest change that resolves the issue
   145→- Do NOT refactor surrounding code, add features, or "improve" unrelated things
   146→
   147→### 8c. Commit
   148→
   149→```bash
   150→git add <only-changed-files>


     1→---
     2→name: retro
     3→version: 2.0.0
     4→description: |
     5→  Weekly engineering retrospective. Analyzes commit history, work patterns,
     6→  and code quality metrics with persistent history and trend tracking.
     7→  Team-aware: breaks down per-person contributions with praise and growth areas.
     8→allowed-tools:
     9→  - Bash
    10→  - Read
    11→  - Write
    12→  - Glob
    13→  - AskUserQuestion
    14→---
    15→
    16→{{PREAMBLE}}
    17→
    18→## Detect default branch
    19→
    20→Before gathering data, detect the repo's default branch name:
    21→`gh repo view --json defaultBranchRef -q .defaultBranchRef.name`
    22→
    23→If this fails, fall back to `main`. Use the detected name wherever the instructions
    24→say `origin/<default>` below.
    25→
    26→---
    27→
    28→# /retro — Weekly Engineering Retrospective
    29→
    30→Generates a comprehensive engineering retrospective analyzing commit history, work patterns, and code quality metrics. Team-aware: identifies the user running the command, then analyzes every contributor with per-person praise and growth opportunities. Designed for a senior IC/CTO-level builder using Claude Code as a force multiplier.
    31→
    32→## User-invocable
    33→When the user types `/retro`, run this skill.
    34→
    35→## Arguments
    36→- `/retro` — default: last 7 days
    37→- `/retro 24h` — last 24 hours
    38→- `/retro 14d` — last 14 days
    39→- `/retro 30d` — last 30 days
    40→- `/retro compare` — compare current window vs prior same-length window
    41→- `/retro compare 14d` — compare with explicit window
    42→
    43→## Instructions
    44→
    45→Parse the argument to determine the time window. Default to 7 days if no argument given. Use `--since="N days ago"`, `--since="N hours ago"`, or `--since="N weeks ago"` (for `w` units) for git log queries. All times should be reported in **Pacific time** (use `TZ=America/Los_Angeles` when converting timestamps).
    46→
    47→**Argument validation:** If the argument doesn't match a number followed by `d`, `h`, or `w`, the word `compare`, or `compare` followed by a number and `d`/`h`/`w`, show this usage and stop:
    48→```
    49→Usage: /retro [window]
    50→  /retro              — last 7 days (default)
    51→  /retro 24h          — last 24 hours
    52→  /retro 14d          — last 14 days
    53→  /retro 30d          — last 30 days
    54→  /retro compare      — compare this period vs prior period
    55→  /retro compare 14d  — compare with explicit window
    56→```
    57→
    58→### Step 1: Gather Raw Data
    59→
    60→First, fetch origin and identify the current user:
    61→```bash
    62→git fetch origin <default> --quiet
    63→# Identify who is running the retro
    64→git config user.name
    65→git config user.email
    66→```
    67→
    68→The name returned by `git config user.name` is **"you"** — the person reading this retro. All other authors are teammates. Use this to orient the narrative: "your" commits vs teammate contributions.
    69→
    70→Run ALL of these git commands in parallel (they are independent):
    71→
    72→```bash
    73→# 1. All commits in window with timestamps, subject, hash, AUTHOR, files changed, insertions, deletions
    74→git log origin/<default> --since="<window>" --format="%H|%aN|%ae|%ai|%s" --shortstat
    75→
    76→# 2. Per-commit test vs total LOC breakdown with author
    77→#    Each commit block starts with COMMIT:<hash>|<author>, followed by numstat lines.
    78→#    Separate test files (matching test/|spec/|__tests__/) from production files.
    79→git log origin/<default> --since="<window>" --format="COMMIT:%H|%aN" --numstat
    80→
    81→# 3. Commit timestamps for session detection and hourly distribution (with author)
    82→#    Use TZ=America/Los_Angeles for Pacific time conversion
    83→TZ=America/Los_Angeles git log origin/<default> --since="<window>" --format="%at|%aN|%ai|%s" | sort -n
    84→
    85→# 4. Files most frequently changed (hotspot analysis)
    86→git log origin/<default> --since="<window>" --format="" --name-only | grep -v '^$' | sort | uniq -c | sort -rn
    87→
    88→# 5. PR numbers from commit messages (extract #NNN patterns)
    89→git log origin/<default> --since="<window>" --format="%s" | grep -oE '#[0-9]+' | sed 's/^#//' | sort -n | uniq | sed 's/^/#/'
    90→
    91→# 6. Per-author file hotspots (who touches what)
    92→git log origin/<default> --since="<window>" --format="AUTHOR:%aN" --name-only
    93→
    94→# 7. Per-author commit counts (quick summary)
    95→git shortlog origin/<default> --since="<window>" -sn --no-merges
    96→
    97→# 8. Greptile triage history (if available)
    98→cat ~/.gstack/greptile-history.md 2>/dev/null || true
    99→
   100→# 9. TODOS.md backlog (if available)
   101→cat TODOS.md 2>/dev/null || true
   102→
   103→# 10. Test file count
   104→find . -name '*.test.*' -o -name '*.spec.*' -o -name '*_test.*' -o -name '*_spec.*' 2>/dev/null | grep -v node_modules | wc -l
   105→
   106→# 11. Regression test commits in window
   107→git log origin/<default> --since="<window>" --oneline --grep="test(qa):" --grep="test(design):" --grep="test: coverage"
   108→
   109→# 12. Test files changed in window
   110→git log origin/<default> --since="<window>" --format="" --name-only | grep -E '\.(test|spec)\.' | sort -u | wc -l
   111→```
   112→
   113→### Step 2: Compute Metrics
   114→
   115→Calculate and present these metrics in a summary table:
   116→
   117→| Metric | Value |
   118→|--------|-------|
   119→| Commits to main | N |
   120→| Contributors | N |
   121→| PRs merged | N |
   122→| Total insertions | N |
   123→| Total deletions | N |
   124→| Net LOC added | N |
   125→| Test LOC (insertions) | N |
   126→| Test LOC ratio | N% |
   127→| Version range | vX.Y.Z.W → vX.Y.Z.W |
   128→| Active days | N |
   129→| Detected sessions | N |
   130→| Avg LOC/session-hour | N |
   131→| Greptile signal | N% (Y catches, Z FPs) |
   132→| Test Health | N total tests · M added this period · K regression tests |
   133→
   134→Then show a **per-author leaderboard** immediately below:
   135→
   136→```
   137→Contributor         Commits   +/-          Top area
   138→You (garry)              32   +2400/-300   browse/
   139→alice                    12   +800/-150    app/services/
   140→bob                       3   +120/-40     tests/
   141→```
   142→
   143→Sort by commits descending. The current user (from `git config user.name`) always appears first, labeled "You (name)".
   144→
   145→**Greptile signal (if history exists):** Read `~/.gstack/greptile-history.md` (fetched in Step 1, command 8). Filter entries within the retro time window by date. Count entries by type: `fix`, `fp`, `already-fixed`. Compute signal ratio: `(fix + already-fixed) / (fix + already-fixed + fp)`. If no entries exist in the window or the file doesn't exist, skip the Greptile metric row. Skip unparseable lines silently.
   146→
   147→**Backlog Health (if TODOS.md exists):** Read `TODOS.md` (fetched in Step 1, command 9). Compute:
   148→- Total open TODOs (exclude items in `## Completed` section)
   149→- P0/P1 count (critical/urgent items)
   150→- P2 count (important items)


     1→#!/usr/bin/env bash
     2→# gstack setup — build browser binary + register all skills with Claude Code
     3→set -e
     4→
     5→GSTACK_DIR="$(cd "$(dirname "$0")" && pwd)"
     6→SKILLS_DIR="$(dirname "$GSTACK_DIR")"
     7→BROWSE_BIN="$GSTACK_DIR/browse/dist/browse"
     8→
     9→ensure_playwright_browser() {
    10→  (
    11→    cd "$GSTACK_DIR"
    12→    bun --eval 'import { chromium } from "playwright"; const browser = await chromium.launch(); await browser.close();'
    13→  ) >/dev/null 2>&1
    14→}
    15→
    16→# 1. Build browse binary if needed (smart rebuild: stale sources, package.json, lock)
    17→NEEDS_BUILD=0
    18→if [ ! -x "$BROWSE_BIN" ]; then
    19→  NEEDS_BUILD=1
    20→elif [ -n "$(find "$GSTACK_DIR/browse/src" -type f -newer "$BROWSE_BIN" -print -quit 2>/dev/null)" ]; then
    21→  NEEDS_BUILD=1
    22→elif [ "$GSTACK_DIR/package.json" -nt "$BROWSE_BIN" ]; then
    23→  NEEDS_BUILD=1
    24→elif [ -f "$GSTACK_DIR/bun.lock" ] && [ "$GSTACK_DIR/bun.lock" -nt "$BROWSE_BIN" ]; then
    25→  NEEDS_BUILD=1
    26→fi
    27→
    28→if [ "$NEEDS_BUILD" -eq 1 ]; then
    29→  echo "Building browse binary..."
    30→  (
    31→    cd "$GSTACK_DIR"
    32→    bun install
    33→    bun run build
    34→  )
    35→  # Safety net: write .version if build script didn't (e.g., git not available during build)
    36→  if [ ! -f "$GSTACK_DIR/browse/dist/.version" ]; then
    37→    git -C "$GSTACK_DIR" rev-parse HEAD > "$GSTACK_DIR/browse/dist/.version" 2>/dev/null || true
    38→  fi
    39→fi
    40→
    41→if [ ! -x "$BROWSE_BIN" ]; then
    42→  echo "gstack setup failed: browse binary missing at $BROWSE_BIN" >&2
    43→  exit 1
    44→fi
    45→
    46→# 2. Ensure Playwright's Chromium is available
    47→if ! ensure_playwright_browser; then
    48→  echo "Installing Playwright Chromium..."
    49→  (
    50→    cd "$GSTACK_DIR"
    51→    bunx playwright install chromium
    52→  )
    53→fi
    54→
    55→if ! ensure_playwright_browser; then
    56→  echo "gstack setup failed: Playwright Chromium could not be launched" >&2
    57→  exit 1
    58→fi
    59→
    60→# 3. Ensure ~/.gstack global state directory exists
    61→mkdir -p "$HOME/.gstack/projects"
    62→
    63→# 4. Only create skill symlinks if we're inside a .claude/skills directory
    64→SKILLS_BASENAME="$(basename "$SKILLS_DIR")"
    65→if [ "$SKILLS_BASENAME" = "skills" ]; then
    66→  linked=()
    67→  for skill_dir in "$GSTACK_DIR"/*/; do
    68→    if [ -f "$skill_dir/SKILL.md" ]; then
    69→      skill_name="$(basename "$skill_dir")"
    70→      # Skip node_modules
    71→      [ "$skill_name" = "node_modules" ] && continue
    72→      target="$SKILLS_DIR/$skill_name"
    73→      # Create or update symlink; skip if a real file/directory exists
    74→      if [ -L "$target" ] || [ ! -e "$target" ]; then
    75→        ln -snf "gstack/$skill_name" "$target"
    76→        linked+=("$skill_name")
    77→      fi
    78→    fi
    79→  done
    80→
    81→  echo "gstack ready."
    82→  echo "  browse: $BROWSE_BIN"
    83→  if [ ${#linked[@]} -gt 0 ]; then
    84→    echo "  linked skills: ${linked[*]}"
    85→  fi
    86→else
    87→  echo "gstack ready."
    88→  echo "  browse: $BROWSE_BIN"
    89→  echo "  (skipped skill symlinks — not inside .claude/skills/)"
    90→fi
    91→
    92→# 4. First-time welcome + legacy cleanup
    93→if [ ! -d "$HOME/.gstack" ]; then
    94→  mkdir -p "$HOME/.gstack"
    95→  echo "  Welcome! Run /gstack-upgrade anytime to stay current."
    96→fi
    97→rm -f /tmp/gstack-latest-version
    98→


     1→#!/usr/bin/env bun
     2→/**
     3→ * Generate SKILL.md files from .tmpl templates.
     4→ *
     5→ * Pipeline:
     6→ *   read .tmpl → find {{PLACEHOLDERS}} → resolve from source → format → write .md
     7→ *
     8→ * Supports --dry-run: generate to memory, exit 1 if different from committed file.
     9→ * Used by skill:check and CI freshness checks.
    10→ */
    11→
    12→import { COMMAND_DESCRIPTIONS } from '../browse/src/commands';
    13→import { SNAPSHOT_FLAGS } from '../browse/src/snapshot';
    14→import * as fs from 'fs';
    15→import * as path from 'path';
    16→
    17→const ROOT = path.resolve(import.meta.dir, '..');
    18→const DRY_RUN = process.argv.includes('--dry-run');
    19→
    20→// ─── Placeholder Resolvers ──────────────────────────────────
    21→
    22→function generateCommandReference(): string {
    23→  // Group commands by category
    24→  const groups = new Map<string, Array<{ command: string; description: string; usage?: string }>>();
    25→  for (const [cmd, meta] of Object.entries(COMMAND_DESCRIPTIONS)) {
    26→    const list = groups.get(meta.category) || [];
    27→    list.push({ command: cmd, description: meta.description, usage: meta.usage });
    28→    groups.set(meta.category, list);
    29→  }
    30→
    31→  // Category display order
    32→  const categoryOrder = [
    33→    'Navigation', 'Reading', 'Interaction', 'Inspection',
    34→    'Visual', 'Snapshot', 'Meta', 'Tabs', 'Server',
    35→  ];
    36→
    37→  const sections: string[] = [];
    38→  for (const category of categoryOrder) {
    39→    const commands = groups.get(category);
    40→    if (!commands || commands.length === 0) continue;
    41→
    42→    // Sort alphabetically within category
    43→    commands.sort((a, b) => a.command.localeCompare(b.command));
    44→
    45→    sections.push(`### ${category}`);
    46→    sections.push('| Command | Description |');
    47→    sections.push('|---------|-------------|');
    48→    for (const cmd of commands) {
    49→      const display = cmd.usage ? `\`${cmd.usage}\`` : `\`${cmd.command}\``;
    50→      sections.push(`| ${display} | ${cmd.description} |`);
    51→    }
    52→    sections.push('');
    53→  }
    54→
    55→  return sections.join('\n').trimEnd();
    56→}
    57→
    58→function generateSnapshotFlags(): string {
    59→  const lines: string[] = [
    60→    'The snapshot is your primary tool for understanding and interacting with pages.',
    61→    '',
    62→    '```',
    63→  ];
    64→
    65→  for (const flag of SNAPSHOT_FLAGS) {
    66→    const label = flag.valueHint ? `${flag.short} ${flag.valueHint}` : flag.short;
    67→    lines.push(`${label.padEnd(10)}${flag.long.padEnd(24)}${flag.description}`);
    68→  }
    69→
    70→  lines.push('```');
    71→  lines.push('');
    72→  lines.push('All flags can be combined freely. `-o` only applies when `-a` is also used.');
    73→  lines.push('Example: `$B snapshot -i -a -C -o /tmp/annotated.png`');
    74→  lines.push('');
    75→  lines.push('**Ref numbering:** @e refs are assigned sequentially (@e1, @e2, ...) in tree order.');
    76→  lines.push('@c refs from `-C` are numbered separately (@c1, @c2, ...).');
    77→  lines.push('');
    78→  lines.push('After snapshot, use @refs as selectors in any command:');
    79→  lines.push('```bash');
    80→  lines.push('$B click @e3       $B fill @e4 "value"     $B hover @e1');
    81→  lines.push('$B html @e2        $B css @e5 "color"      $B attrs @e6');
    82→  lines.push('$B click @c1       # cursor-interactive ref (from -C)');
    83→  lines.push('```');
    84→  lines.push('');
    85→  lines.push('**Output format:** indented accessibility tree with @ref IDs, one element per line.');
    86→  lines.push('```');
    87→  lines.push('  @e1 [heading] "Welcome" [level=1]');
    88→  lines.push('  @e2 [textbox] "Email"');
    89→  lines.push('  @e3 [button] "Submit"');
    90→  lines.push('```');
    91→  lines.push('');
    92→  lines.push('Refs are invalidated on navigation — run `snapshot` again after `goto`.');
    93→
    94→  return lines.join('\n');
    95→}
    96→
    97→function generatePreamble(): string {
    98→  return `## Preamble (run first)
    99→
   100→\`\`\`bash
   101→_UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/skills/gstack/bin/gstack-update-check 2>/dev/null || true)
   102→[ -n "$_UPD" ] && echo "$_UPD" || true
   103→mkdir -p ~/.gstack/sessions
   104→touch ~/.gstack/sessions/"$PPID"
   105→_SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ')
   106→find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true
   107→_CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true)
   108→_BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
   109→echo "BRANCH: $_BRANCH"
   110→_LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no")
   111→echo "LAKE_INTRO: $_LAKE_SEEN"
   112→\`\`\`
   113→
   114→If output shows \`UPGRADE_AVAILABLE <old> <new>\`: read \`~/.claude/skills/gstack/gstack-upgrade/SKILL.md\` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If \`JUST_UPGRADED <from> <to>\`: tell user "Running gstack v{to} (just updated!)" and continue.
   115→
   116→If \`LAKE_INTRO\` is \`no\`: Before continuing, introduce the Completeness Principle.
   117→Tell the user: "gstack follows the **Boil the Lake** principle — always do the complete
   118→thing when AI makes the marginal cost near-zero. Read more: https://garryslist.org/posts/boil-the-ocean"
   119→Then offer to open the essay in their default browser:
   120→
   121→\`\`\`bash
   122→open https://garryslist.org/posts/boil-the-ocean
   123→touch ~/.gstack/.completeness-intro-seen
   124→\`\`\`
   125→
   126→Only run \`open\` if the user says yes. Always run \`touch\` to mark as seen. This only happens once.
   127→
   128→## AskUserQuestion Format
   129→
   130→**ALWAYS follow this structure for every AskUserQuestion call:**
   131→1. **Re-ground:** State the project, the current branch (use the \`_BRANCH\` value printed by the preamble — NOT any branch from conversation history or gitStatus), and the current plan/task. (1-2 sentences)
   132→2. **Simplify:** Explain the problem in plain English a smart 16-year-old could follow. No raw function names, no internal jargon, no implementation details. Use concrete examples and analogies. Say what it DOES, not what it's called.
   133→3. **Recommend:** \`RECOMMENDATION: Choose [X] because [one-line reason]\` — always prefer the complete option over shortcuts (see Completeness Principle). Include \`Completeness: X/10\` for each option. Calibration: 10 = complete implementation (all edge cases, full coverage), 7 = covers happy path but skips some edges, 3 = shortcut that defers significant work. If both options are 8+, pick the higher; if one is ≤5, flag it.
   134→4. **Options:** Lettered options: \`A) ... B) ... C) ...\` — when an option involves effort, show both scales: \`(human: ~X / CC: ~Y)\`
   135→
   136→Assume the user hasn't looked at this window in 20 minutes and doesn't have the code open. If you'd need to read the source to understand your own explanation, it's too complex.
   137→
   138→Per-skill instructions may add additional formatting rules on top of this baseline.
   139→
   140→## Completeness Principle — Boil the Lake
   141→
   142→AI-assisted coding makes the marginal cost of completeness near-zero. When you present options:
   143→
   144→- If Option A is the complete implementation (full parity, all edge cases, 100% coverage) and Option B is a shortcut that saves modest effort — **always recommend A**. The delta between 80 lines and 150 lines is meaningless with CC+gstack. "Good enough" is the wrong instinct when "complete" costs minutes more.
   145→- **Lake vs. ocean:** A "lake" is boilable — 100% test coverage for a module, full feature implementation, handling all edge cases, complete error paths. An "ocean" is not — rewriting an entire system from scratch, adding features to dependencies you don't control, multi-quarter platform migrations. Recommend boiling lakes. Flag oceans as out of scope.
   146→- **When estimating effort**, always show both scales: human team time and CC+gstack time. The compression ratio varies by task type — use this reference:
   147→
   148→| Task type | Human team | CC+gstack | Compression |
   149→|-----------|-----------|-----------|-------------|
   150→| Boilerplate / scaffolding | 2 days | 15 min | ~100x |
   151→| Test writing | 1 day | 15 min | ~50x |
   152→| Feature implementation | 1 week | 30 min | ~30x |
   153→| Bug fix + regression test | 4 hours | 15 min | ~20x |
   154→| Architecture / design | 2 days | 4 hours | ~5x |
   155→| Research / exploration | 1 day | 3 hours | ~3x |
   156→
   157→- This principle applies to test coverage, error handling, documentation, edge cases, and feature completeness. Don't skip the last 10% to "save time" — with AI, that 10% costs seconds.
   158→
   159→**Anti-patterns — DON'T do this:**
   160→- BAD: "Choose B — it covers 90% of the value with less code." (If A is only 70 lines more, choose A.)
   161→- BAD: "We can skip edge case handling to save time." (Edge case handling costs minutes with CC.)
   162→- BAD: "Let's defer test coverage to a follow-up PR." (Tests are the cheapest lake to boil.)
   163→- BAD: Quoting only human-team effort: "This would take 2 weeks." (Say: "2 weeks human / ~1 hour CC.")
   164→
   165→## Contributor Mode
   166→
   167→If \`_CONTRIB\` is \`true\`: you are in **contributor mode**. You're a gstack user who also helps make it better.
   168→
   169→**At the end of each major workflow step** (not after every single command), reflect on the gstack tooling you used. Rate your experience 0 to 10. If it wasn't a 10, think about why. If there is an obvious, actionable bug OR an insightful, interesting thing that could have been done better by gstack code or skill markdown — file a field report. Maybe our contributor will help make us better!
   170→
   171→**Calibration — this is the bar:** For example, \`$B js "await fetch(...)"\` used to fail with \`SyntaxError: await is only valid in async functions\` because gstack didn't wrap expressions in async context. Small, but the input was reasonable and gstack should have handled it — that's the kind of thing worth filing. Things less consequential than this, ignore.
   172→
   173→**NOT worth filing:** user's app bugs, network errors to user's URL, auth failures on user's site, user's own JS logic bugs.
   174→
   175→**To file:** write \`~/.gstack/contributor-logs/{slug}.md\` with **all sections below** (do not truncate — include every section through the Date/Version footer):
   176→
   177→\`\`\`
   178→# {Title}
   179→
   180→Hey gstack team — ran into this while using /{skill-name}:
   181→
   182→**What I was trying to do:** {what the user/agent was attempting}
   183→**What happened instead:** {what actually happened}
   184→**My rating:** {0-10} — {one sentence on why it wasn't a 10}
   185→
   186→## Steps to reproduce
   187→1. {step}
   188→
   189→## Raw output
   190→\`\`\`
   191→{paste the actual error or unexpected output here}
   192→\`\`\`
   193→
   194→## What would make this a 10
   195→{one sentence: what gstack should have done differently}
   196→
   197→**Date:** {YYYY-MM-DD} | **Version:** {gstack version} | **Skill:** /{skill}
   198→\`\`\`
   199→
   200→Slug: lowercase, hyphens, max 60 chars (e.g. \`browse-js-no-await\`). Skip if file already exists. Max 3 reports per session. File inline and continue — don't stop the workflow. Tell user: "Filed gstack field report: {title}"`;
   201→}
   202→
   203→function generateBrowseSetup(): string {
   204→  return `## SETUP (run this check BEFORE any browse command)
   205→
   206→\`\`\`bash
   207→_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
   208→B=""
   209→[ -n "$_ROOT" ] && [ -x "$_ROOT/.claude/skills/gstack/browse/dist/browse" ] && B="$_ROOT/.claude/skills/gstack/browse/dist/browse"
   210→[ -z "$B" ] && B=~/.claude/skills/gstack/browse/dist/browse
   211→if [ -x "$B" ]; then
   212→  echo "READY: $B"
   213→else
   214→  echo "NEEDS_SETUP"
   215→fi
   216→\`\`\`
   217→
   218→If \`NEEDS_SETUP\`:
   219→1. Tell the user: "gstack browse needs a one-time build (~10 seconds). OK to proceed?" Then STOP and wait.
   220→2. Run: \`cd <SKILL_DIR> && ./setup\`
   221→3. If \`bun\` is not installed: \`curl -fsSL https://bun.sh/install | bash\``;
   222→}
   223→
   224→function generateBaseBranchDetect(): string {
   225→  return `## Step 0: Detect base branch
   226→
   227→Determine which branch this PR targets. Use the result as "the base branch" in all subsequent steps.
   228→
   229→1. Check if a PR already exists for this branch:
   230→   \`gh pr view --json baseRefName -q .baseRefName\`
   231→   If this succeeds, use the printed branch name as the base branch.
   232→
   233→2. If no PR exists (command fails), detect the repo's default branch:
   234→   \`gh repo view --json defaultBranchRef -q .defaultBranchRef.name\`
   235→
   236→3. If both commands fail, fall back to \`main\`.
   237→
   238→Print the detected base branch name. In every subsequent \`git diff\`, \`git log\`,
   239→\`git fetch\`, \`git merge\`, and \`gh pr create\` command, substitute the detected
   240→branch name wherever the instructions say "the base branch."
   241→
   242→---`;
   243→}
   244→
   245→function generateQAMethodology(): string {
   246→  return `## Modes
   247→
   248→### Diff-aware (automatic when on a feature branch with no URL)
   249→
   250→This is the **primary mode** for developers verifying their work. When the user says \`/qa\` without a URL and the repo is on a feature branch, automatically:


     1→#!/usr/bin/env bun
     2→/**
     3→ * skill:check — Health summary for all SKILL.md files.
     4→ *
     5→ * Reports:
     6→ *   - Command validation (valid/invalid/snapshot errors)
     7→ *   - Template coverage (which SKILL.md files have .tmpl sources)
     8→ *   - Freshness check (generated files match committed files)
     9→ */
    10→
    11→import { validateSkill } from '../test/helpers/skill-parser';
    12→import * as fs from 'fs';
    13→import * as path from 'path';
    14→import { execSync } from 'child_process';
    15→
    16→const ROOT = path.resolve(import.meta.dir, '..');
    17→
    18→// Find all SKILL.md files
    19→const SKILL_FILES = [
    20→  'SKILL.md',
    21→  'browse/SKILL.md',
    22→  'qa/SKILL.md',
    23→  'qa-only/SKILL.md',
    24→  'ship/SKILL.md',
    25→  'review/SKILL.md',
    26→  'retro/SKILL.md',
    27→  'plan-ceo-review/SKILL.md',
    28→  'plan-eng-review/SKILL.md',
    29→  'setup-browser-cookies/SKILL.md',
    30→  'plan-design-review/SKILL.md',
    31→  'design-review/SKILL.md',
    32→  'gstack-upgrade/SKILL.md',
    33→  'document-release/SKILL.md',
    34→].filter(f => fs.existsSync(path.join(ROOT, f)));
    35→
    36→let hasErrors = false;
    37→
    38→// ─── Skills ─────────────────────────────────────────────────
    39→
    40→console.log('  Skills:');
    41→for (const file of SKILL_FILES) {
    42→  const fullPath = path.join(ROOT, file);
    43→  const result = validateSkill(fullPath);
    44→
    45→  if (result.warnings.length > 0) {
    46→    console.log(`  \u26a0\ufe0f  ${file.padEnd(30)} — ${result.warnings.join(', ')}`);
    47→    continue;
    48→  }
    49→
    50→  const totalValid = result.valid.length;
    51→  const totalInvalid = result.invalid.length;
    52→  const totalSnapErrors = result.snapshotFlagErrors.length;
    53→
    54→  if (totalInvalid > 0 || totalSnapErrors > 0) {
    55→    hasErrors = true;
    56→    console.log(`  \u274c ${file.padEnd(30)} — ${totalValid} valid, ${totalInvalid} invalid, ${totalSnapErrors} snapshot errors`);
    57→    for (const inv of result.invalid) {
    58→      console.log(`      line ${inv.line}: unknown command '${inv.command}'`);
    59→    }
    60→    for (const se of result.snapshotFlagErrors) {
    61→      console.log(`      line ${se.command.line}: ${se.error}`);
    62→    }
    63→  } else {
    64→    console.log(`  \u2705 ${file.padEnd(30)} — ${totalValid} commands, all valid`);
    65→  }
    66→}
    67→
    68→// ─── Templates ──────────────────────────────────────────────
    69→
    70→console.log('\n  Templates:');
    71→const TEMPLATES = [
    72→  { tmpl: 'SKILL.md.tmpl', output: 'SKILL.md' },
    73→  { tmpl: 'browse/SKILL.md.tmpl', output: 'browse/SKILL.md' },
    74→];
    75→
    76→for (const { tmpl, output } of TEMPLATES) {
    77→  const tmplPath = path.join(ROOT, tmpl);
    78→  const outPath = path.join(ROOT, output);
    79→  if (!fs.existsSync(tmplPath)) {
    80→    console.log(`  \u26a0\ufe0f  ${output.padEnd(30)} — no template`);
    81→    continue;
    82→  }
    83→  if (!fs.existsSync(outPath)) {
    84→    hasErrors = true;
    85→    console.log(`  \u274c ${output.padEnd(30)} — generated file missing! Run: bun run gen:skill-docs`);
    86→    continue;
    87→  }
    88→  console.log(`  \u2705 ${tmpl.padEnd(30)} \u2192 ${output}`);
    89→}
    90→
    91→// Skills without templates
    92→for (const file of SKILL_FILES) {
    93→  const tmplPath = path.join(ROOT, file + '.tmpl');
    94→  if (!fs.existsSync(tmplPath) && !TEMPLATES.some(t => t.output === file)) {
    95→    console.log(`  \u26a0\ufe0f  ${file.padEnd(30)} — no template (OK if no $B commands)`);
    96→  }
    97→}
    98→
    99→// ─── Freshness ──────────────────────────────────────────────
   100→
   101→console.log('\n  Freshness:');
   102→try {
   103→  execSync('bun run scripts/gen-skill-docs.ts --dry-run', { cwd: ROOT, stdio: 'pipe' });
   104→  console.log('  \u2705 All generated files are fresh');
   105→} catch (err: any) {
   106→  hasErrors = true;
   107→  const output = err.stdout?.toString() || '';
   108→  console.log('  \u274c Generated files are stale:');
   109→  for (const line of output.split('\n').filter((l: string) => l.startsWith('STALE'))) {
   110→    console.log(`      ${line}`);
   111→  }
   112→  console.log('      Run: bun run gen:skill-docs');
   113→}
   114→
   115→console.log('');
   116→process.exit(hasErrors ? 1 : 0);
   117→