Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,22 @@ Read the philosophy: https://garryslist.org/posts/boil-the-ocean
- Added `bin/gstack-slug` helper (5-line bash) with unit tests. Outputs `SLUG=` and `BRANCH=` lines, sanitizes `/` to `-`.
- New TODOs: smart review relevance detection (P3), `/merge` skill for review-gated PR merge (P2).

## fork: multi-stack portability — 2026-03-17

- **`/ship` works on Node.js, TypeScript, and Python projects now.** It used to hardcode `bin/test-lane` (Rails) + `npm run test`. Now it auto-detects your test runner: vitest, jest, pytest, make test, or the Rails test lane. If you have both a frontend and backend test suite, they run in parallel. No config needed.
- **`/ship` respects your version format.** Projects using standard 3-digit semver (`1.2.3`) are handled correctly — no more confusion from the internal 4-digit format. If you have no `VERSION` file at all, the version step is silently skipped.
- **Eval suites in `/ship` are now Rails-only.** The eval gate only activates when a `test/evals/` directory and Rails are both detected. TypeScript and Python projects skip it cleanly with a one-line message instead of failing.
- **`/retro` shows your local time, not Pacific.** Timestamps, histograms, session times, and streak dates are now displayed in your system's timezone. Works on macOS and Linux.
- **`/qa` no longer hard-fails on uncommitted changes.** Instead of refusing to start, it offers to stash your changes, run QA, and restore them automatically when done. The fix loop still stays atomic — the stash just handles the setup for you.
- **`/plan-ceo-review` system audit works on any stack.** The pre-review grep for TODOs/FIXMEs now covers `.py`, `.ts`, `.tsx`, `.go`, `.rs` alongside `.rb`. Recently-touched-files detection picks the right lock file anchor automatically (pnpm-lock, poetry.lock, go.sum, etc.).
- **Diff-aware QA and design review use the correct base branch.** Previously hardcoded to `main` — now uses the dynamically detected base branch, so stacked PRs and `master`-default repos work correctly.

### For contributors

- Added `{{PROJECT_DETECT}}` resolver to `gen-skill-docs.ts` — stack detection block (test runner, VERSION format, languages, eval suite flag) injected into `/ship`. Follows the `{{QA_METHODOLOGY}}` / `{{DESIGN_METHODOLOGY}}` pattern.
- Fixed unescaped `${}` shell interpolations in TypeScript template literals.
- `{{QA_METHODOLOGY}}` and `{{DESIGN_METHODOLOGY}}` diff-aware mode: replaced hardcoded `main` with `<base>` placeholder.

## 0.5.0 — 2026-03-16

- **Your site just got a design review.** `/plan-design-review` opens your site and reviews it like a senior product designer — typography, spacing, hierarchy, color, responsive, interactions, and AI slop detection. Get letter grades (A-F) per category, a dual headline "Design Score" + "AI Slop Score", and a structured first impression that doesn't pull punches.
Expand Down
28 changes: 26 additions & 2 deletions plan-ceo-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,32 @@ Run the following commands:
git log --oneline -30 # Recent history
git diff <base> --stat # What's already changed
git stash list # Any stashed work
grep -r "TODO\|FIXME\|HACK\|XXX" --include="*.rb" --include="*.js" -l
find . -name "*.rb" -newer Gemfile.lock | head -20 # Recently touched files
grep -r "TODO\|FIXME\|HACK\|XXX" \
--include="*.rb" --include="*.py" --include="*.ts" --include="*.tsx" \
--include="*.js" --include="*.jsx" --include="*.go" --include="*.rs" \
-l 2>/dev/null | head -20 # Files with deferred work
```

Detect recently touched source files based on the project's lock/manifest file:
```bash
# Find the most recent lock/manifest as the freshness anchor
_ANCHOR=""
[ -f Gemfile.lock ] && _ANCHOR="Gemfile.lock"
[ -z "$_ANCHOR" ] && [ -f pnpm-lock.yaml ] && _ANCHOR="pnpm-lock.yaml"
[ -z "$_ANCHOR" ] && [ -f package-lock.json ] && _ANCHOR="package-lock.json"
[ -z "$_ANCHOR" ] && [ -f poetry.lock ] && _ANCHOR="poetry.lock"
[ -z "$_ANCHOR" ] && [ -f pyproject.toml ] && _ANCHOR="pyproject.toml"
[ -z "$_ANCHOR" ] && [ -f go.sum ] && _ANCHOR="go.sum"
[ -z "$_ANCHOR" ] && [ -f package.json ] && _ANCHOR="package.json"

if [ -n "$_ANCHOR" ]; then
find . \( -name "*.rb" -o -name "*.py" -o -name "*.ts" -o -name "*.tsx" \
-o -name "*.js" -o -name "*.jsx" -o -name "*.go" -o -name "*.rs" \) \
-newer "$_ANCHOR" \
-not -path "*/node_modules/*" -not -path "*/.git/*" \
-not -path "*/__pycache__/*" -not -path "*/dist/*" -not -path "*/build/*" \
2>/dev/null | head -20
fi
```
Then read CLAUDE.md, TODOS.md, and any existing architecture docs. When reading TODOS.md, specifically:
* Note any TODOs this plan touches, blocks, or unlocks
Expand Down
28 changes: 26 additions & 2 deletions plan-ceo-review/SKILL.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,32 @@ Run the following commands:
git log --oneline -30 # Recent history
git diff <base> --stat # What's already changed
git stash list # Any stashed work
grep -r "TODO\|FIXME\|HACK\|XXX" --include="*.rb" --include="*.js" -l
find . -name "*.rb" -newer Gemfile.lock | head -20 # Recently touched files
grep -r "TODO\|FIXME\|HACK\|XXX" \
--include="*.rb" --include="*.py" --include="*.ts" --include="*.tsx" \
--include="*.js" --include="*.jsx" --include="*.go" --include="*.rs" \
-l 2>/dev/null | head -20 # Files with deferred work
```

Detect recently touched source files based on the project's lock/manifest file:
```bash
# Find the most recent lock/manifest as the freshness anchor
_ANCHOR=""
[ -f Gemfile.lock ] && _ANCHOR="Gemfile.lock"
[ -z "$_ANCHOR" ] && [ -f pnpm-lock.yaml ] && _ANCHOR="pnpm-lock.yaml"
[ -z "$_ANCHOR" ] && [ -f package-lock.json ] && _ANCHOR="package-lock.json"
[ -z "$_ANCHOR" ] && [ -f poetry.lock ] && _ANCHOR="poetry.lock"
[ -z "$_ANCHOR" ] && [ -f pyproject.toml ] && _ANCHOR="pyproject.toml"
[ -z "$_ANCHOR" ] && [ -f go.sum ] && _ANCHOR="go.sum"
[ -z "$_ANCHOR" ] && [ -f package.json ] && _ANCHOR="package.json"

if [ -n "$_ANCHOR" ]; then
find . \( -name "*.rb" -o -name "*.py" -o -name "*.ts" -o -name "*.tsx" \
-o -name "*.js" -o -name "*.jsx" -o -name "*.go" -o -name "*.rs" \) \
-newer "$_ANCHOR" \
-not -path "*/node_modules/*" -not -path "*/.git/*" \
-not -path "*/__pycache__/*" -not -path "*/dist/*" -not -path "*/build/*" \
2>/dev/null | head -20
fi
```
Then read CLAUDE.md, TODOS.md, and any existing architecture docs. When reading TODOS.md, specifically:
* Note any TODOs this plan touches, blocks, or unlocks
Expand Down
2 changes: 1 addition & 1 deletion plan-design-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ Comprehensive review: 10-15 pages, every interaction flow, exhaustive checklist.

### Diff-aware (automatic when on a feature branch with no URL)
When on a feature branch, scope to pages affected by the branch changes:
1. Analyze the branch diff: `git diff main...HEAD --name-only`
1. Analyze the branch diff: `git diff <base>...HEAD --name-only` (use base branch from preamble)
2. Map changed files to affected pages/routes
3. Detect running app on common local ports (3000, 4000, 8080)
4. Audit only affected pages, compare design quality before/after
Expand Down
2 changes: 1 addition & 1 deletion qa-design-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ Comprehensive review: 10-15 pages, every interaction flow, exhaustive checklist.

### Diff-aware (automatic when on a feature branch with no URL)
When on a feature branch, scope to pages affected by the branch changes:
1. Analyze the branch diff: `git diff main...HEAD --name-only`
1. Analyze the branch diff: `git diff <base>...HEAD --name-only` (use base branch from preamble)
2. Map changed files to affected pages/routes
3. Detect running app on common local ports (3000, 4000, 8080)
4. Audit only affected pages, compare design quality before/after
Expand Down
6 changes: 3 additions & 3 deletions qa-only/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,10 @@ Before falling back to git diff heuristics, check for richer test plan sources:

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. **Analyze the branch diff** to understand what changed:
1. **Analyze the branch diff** to understand what changed (use the base branch detected in the preamble, defaulting to `main`):
```bash
git diff main...HEAD --name-only
git log main..HEAD --oneline
git diff <base>...HEAD --name-only
git log <base>..HEAD --oneline
```

2. **Identify affected pages/routes** from the changed files:
Expand Down
25 changes: 16 additions & 9 deletions qa/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,14 +168,21 @@ You are a QA engineer AND a bug-fix engineer. Test web applications like a real

**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.

**Require clean working tree before starting:**
**Check working tree state:**
```bash
if [ -n "$(git status --porcelain)" ]; then
echo "ERROR: Working tree is dirty. Commit or stash changes before running /qa."
exit 1
fi
_DIRTY=$(git status --porcelain 2>/dev/null)
[ -n "$_DIRTY" ] && echo "DIRTY_WORKING_TREE:" && echo "$_DIRTY" | head -10 || echo "CLEAN"
```

If `DIRTY_WORKING_TREE` is printed, use AskUserQuestion:
- Re-ground: project, branch, the uncommitted changes shown above
- Explain: there are unsaved changes that would mix with QA's own commits if not handled first
- `RECOMMENDATION: Choose A — stashing is safe and reversible`
- A) Stash now (`git stash`) and proceed — stash will be restored automatically after Phase 10
- B) Cancel — I'll commit or stash manually and re-run `/qa`

If the user chooses A: run `git stash` and note that `git stash pop` will be run automatically after Phase 10 completes. If B: stop.

**Find the browse binary:**

## SETUP (run this check BEFORE any browse command)
Expand Down Expand Up @@ -382,10 +389,10 @@ Before falling back to git diff heuristics, check for richer test plan sources:

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. **Analyze the branch diff** to understand what changed:
1. **Analyze the branch diff** to understand what changed (use the base branch detected in the preamble, defaulting to `main`):
```bash
git diff main...HEAD --name-only
git log main..HEAD --oneline
git diff <base>...HEAD --name-only
git log <base>..HEAD --oneline
```

2. **Identify affected pages/routes** from the changed files:
Expand Down Expand Up @@ -860,7 +867,7 @@ If the repo has a `TODOS.md`:

## Additional Rules (qa-specific)

11. **Clean working tree required.** Refuse to start if `git status --porcelain` is non-empty.
11. **Clean working tree required.** If `git status --porcelain` is non-empty, offer to stash (AskUserQuestion A/B). Never start the fix loop with uncommitted changes present — QA's own commits must be atomic and isolated. If stash was used, run `git stash pop` after Phase 10.
12. **One commit per fix.** Never bundle multiple fixes into one commit.
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.
14. **Revert on regression.** If a fix makes things worse, `git revert HEAD` immediately.
Expand Down
19 changes: 13 additions & 6 deletions qa/SKILL.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,21 @@ You are a QA engineer AND a bug-fix engineer. Test web applications like a real

**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.

**Require clean working tree before starting:**
**Check working tree state:**
```bash
if [ -n "$(git status --porcelain)" ]; then
echo "ERROR: Working tree is dirty. Commit or stash changes before running /qa."
exit 1
fi
_DIRTY=$(git status --porcelain 2>/dev/null)
[ -n "$_DIRTY" ] && echo "DIRTY_WORKING_TREE:" && echo "$_DIRTY" | head -10 || echo "CLEAN"
```

If `DIRTY_WORKING_TREE` is printed, use AskUserQuestion:
- Re-ground: project, branch, the uncommitted changes shown above
- Explain: there are unsaved changes that would mix with QA's own commits if not handled first
- `RECOMMENDATION: Choose A — stashing is safe and reversible`
- A) Stash now (`git stash`) and proceed — stash will be restored automatically after Phase 10
- B) Cancel — I'll commit or stash manually and re-run `/qa`

If the user chooses A: run `git stash` and note that `git stash pop` will be run automatically after Phase 10 completes. If B: stop.

**Find the browse binary:**

{{BROWSE_SETUP}}
Expand Down Expand Up @@ -298,7 +305,7 @@ If the repo has a `TODOS.md`:

## Additional Rules (qa-specific)

11. **Clean working tree required.** Refuse to start if `git status --porcelain` is non-empty.
11. **Clean working tree required.** If `git status --porcelain` is non-empty, offer to stash (AskUserQuestion A/B). Never start the fix loop with uncommitted changes present — QA's own commits must be atomic and isolated. If stash was used, run `git stash pop` after Phase 10.
12. **One commit per fix.** Never bundle multiple fixes into one commit.
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.
14. **Revert on regression.** If a fix makes things worse, `git revert HEAD` immediately.
Expand Down
32 changes: 21 additions & 11 deletions retro/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,9 @@ When the user types `/retro`, run this skill.

## Instructions

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).
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.

**Timezone:** detect the system's local timezone once at the start of Step 1, then use it for all timestamp conversions. All displayed times should use the local timezone (not hardcoded Pacific).

**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:
```
Expand All @@ -161,14 +163,22 @@ Usage: /retro [window]

### Step 1: Gather Raw Data

First, fetch origin and identify the current user:
First, detect the local timezone, fetch origin, and identify the current user:
```bash
# Detect local timezone (macOS + Linux compatible)
_TZ=$(readlink /etc/localtime 2>/dev/null | sed 's|.*/zoneinfo/||' || \
cat /etc/timezone 2>/dev/null || \
timedatectl show --property=Timezone --value 2>/dev/null || \
echo "UTC")
echo "LOCAL_TZ: $_TZ"
git fetch origin <default> --quiet
# Identify who is running the retro
git config user.name
git config user.email
```

Use the printed `LOCAL_TZ` value for all timestamp conversions in subsequent steps.

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.

Run ALL of these git commands in parallel (they are independent):
Expand All @@ -183,8 +193,8 @@ git log origin/<default> --since="<window>" --format="%H|%aN|%ae|%ai|%s" --short
git log origin/<default> --since="<window>" --format="COMMIT:%H|%aN" --numstat

# 3. Commit timestamps for session detection and hourly distribution (with author)
# Use TZ=America/Los_Angeles for Pacific time conversion
TZ=America/Los_Angeles git log origin/<default> --since="<window>" --format="%at|%aN|%ai|%s" | sort -n
# Use LOCAL_TZ detected above for time conversion
TZ=<LOCAL_TZ> git log origin/<default> --since="<window>" --format="%at|%aN|%ai|%s" | sort -n

# 4. Files most frequently changed (hotspot analysis)
git log origin/<default> --since="<window>" --format="" --name-only | grep -v '^$' | sort | uniq -c | sort -rn
Expand Down Expand Up @@ -264,7 +274,7 @@ If TODOS.md doesn't exist, skip the Backlog Health row.

### Step 3: Commit Time Distribution

Show hourly histogram in Pacific time using bar chart:
Show hourly histogram in local time using bar chart:

```
Hour Commits ████████████████
Expand All @@ -282,7 +292,7 @@ Identify and call out:
### Step 4: Work Session Detection

Detect sessions using **45-minute gap** threshold between consecutive commits. For each session report:
- Start/end time (Pacific)
- Start/end time (local timezone)
- Number of commits
- Duration in minutes

Expand Down Expand Up @@ -368,11 +378,11 @@ If the time window is 14 days or more, split into weekly buckets and show trends
Count consecutive days with at least 1 commit to origin/<default>, going back from today. Track both team streak and personal streak:

```bash
# Team streak: all unique commit dates (Pacific time) — no hard cutoff
TZ=America/Los_Angeles git log origin/<default> --format="%ad" --date=format:"%Y-%m-%d" | sort -u
# Team streak: all unique commit dates (local timezone) — no hard cutoff
TZ=<LOCAL_TZ> git log origin/<default> --format="%ad" --date=format:"%Y-%m-%d" | sort -u

# Personal streak: only the current user's commits
TZ=America/Los_Angeles git log origin/<default> --author="<user_name>" --format="%ad" --date=format:"%Y-%m-%d" | sort -u
TZ=<LOCAL_TZ> git log origin/<default> --author="<user_name>" --format="%ad" --date=format:"%Y-%m-%d" | sort -u
```

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:
Expand Down Expand Up @@ -411,7 +421,7 @@ mkdir -p .context/retros
Determine the next sequence number for today (substitute the actual date for `$(date +%Y-%m-%d)`):
```bash
# Count existing retros for today to get next sequence number
today=$(TZ=America/Los_Angeles date +%Y-%m-%d)
today=$(TZ=<LOCAL_TZ> date +%Y-%m-%d)
existing=$(ls .context/retros/${today}-*.json 2>/dev/null | wc -l | tr -d ' ')
next=$((existing + 1))
# Save as .context/retros/${today}-${next}.json
Expand Down Expand Up @@ -608,7 +618,7 @@ When the user runs `/retro compare` (or `/retro compare 14d`):

- ALL narrative output goes directly to the user in the conversation. The ONLY file written is the `.context/retros/` JSON snapshot.
- Use `origin/<default>` for all git queries (not local main which may be stale)
- Convert all timestamps to Pacific time for display (use `TZ=America/Los_Angeles`)
- Convert all timestamps to local time for display (use `TZ=<LOCAL_TZ>` detected in Step 1)
- If the window has zero commits, say so and suggest a different window
- Round LOC/hour to nearest 50
- Treat merge commits as PR boundaries
Expand Down
Loading