mirror of https://github.com/garrytan/gstack.git
Merge 49e6b7e48c into a3259400a3
This commit is contained in:
commit
b02258864f
|
|
@ -61,7 +61,7 @@ Invoke them by name (e.g., `/office-hours`).
|
||||||
| `/learn` | Manage what gstack learned across sessions. |
|
| `/learn` | Manage what gstack learned across sessions. |
|
||||||
| `/retro` | Weekly retro with per-person breakdowns and shipping streaks. |
|
| `/retro` | Weekly retro with per-person breakdowns and shipping streaks. |
|
||||||
| `/health` | Code quality dashboard (type checker, linter, tests, dead code). |
|
| `/health` | Code quality dashboard (type checker, linter, tests, dead code). |
|
||||||
| `/benchmark` | Performance regression detection (page load, Core Web Vitals). |
|
| `/web-performance-benchmark` | Performance regression detection (page load, Core Web Vitals). |
|
||||||
| `/benchmark-models` | Cross-model benchmark for skills (Claude, GPT, Gemini side-by-side). |
|
| `/benchmark-models` | Cross-model benchmark for skills (Claude, GPT, Gemini side-by-side). |
|
||||||
| `/cso` | OWASP Top 10 + STRIDE security audit. |
|
| `/cso` | OWASP Top 10 + STRIDE security audit. |
|
||||||
| `/setup-gbrain` | Set up gbrain for cross-machine session memory sync. |
|
| `/setup-gbrain` | Set up gbrain for cross-machine session memory sync. |
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ WebSocket — Claude's Bash tool already exists, so we use it.
|
||||||
Three escalating modes:
|
Three escalating modes:
|
||||||
|
|
||||||
- **Headless** (default). Daemon runs Chromium with no visible window. Fastest,
|
- **Headless** (default). Daemon runs Chromium with no visible window. Fastest,
|
||||||
cheapest, what skills like `/qa`, `/design-review`, `/benchmark` use by
|
cheapest, what skills like `/qa`, `/design-review`, `/web-performance-benchmark` use by
|
||||||
default.
|
default.
|
||||||
- **Headed via `$B connect`**. Same daemon, but Chromium is visible (rebranded
|
- **Headed via `$B connect`**. Same daemon, but Chromium is visible (rebranded
|
||||||
as "GStack Browser") with the Side Panel extension auto-loaded. You watch
|
as "GStack Browser") with the Side Panel extension auto-loaded. You watch
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ gstack/
|
||||||
├── plan-ceo-review/ # /plan-ceo-review skill
|
├── plan-ceo-review/ # /plan-ceo-review skill
|
||||||
├── plan-eng-review/ # /plan-eng-review skill
|
├── plan-eng-review/ # /plan-eng-review skill
|
||||||
├── autoplan/ # /autoplan skill (auto-review pipeline: CEO → design → eng)
|
├── autoplan/ # /autoplan skill (auto-review pipeline: CEO → design → eng)
|
||||||
├── benchmark/ # /benchmark skill (performance regression detection)
|
├── benchmark/ # /web-performance-benchmark skill (performance regression detection)
|
||||||
├── canary/ # /canary skill (post-deploy monitoring loop)
|
├── canary/ # /canary skill (post-deploy monitoring loop)
|
||||||
├── codex/ # /codex skill (multi-AI second opinion via OpenAI Codex CLI)
|
├── codex/ # /codex skill (multi-AI second opinion via OpenAI Codex CLI)
|
||||||
├── land-and-deploy/ # /land-and-deploy skill (merge → deploy → canary verify)
|
├── land-and-deploy/ # /land-and-deploy skill (merge → deploy → canary verify)
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ Fork it. Improve it. Make it yours. And if you want to hate on free open source
|
||||||
|
|
||||||
Open Claude Code and paste this. Claude does the rest.
|
Open Claude Code and paste this. Claude does the rest.
|
||||||
|
|
||||||
> Install gstack: run **`git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup`** then add a "gstack" section to CLAUDE.md that says to use the /browse skill from gstack for all web browsing, never use mcp\_\_claude-in-chrome\_\_\* tools, and lists the available skills: /office-hours, /plan-ceo-review, /plan-eng-review, /plan-design-review, /design-consultation, /design-shotgun, /design-html, /review, /ship, /land-and-deploy, /canary, /benchmark, /browse, /connect-chrome, /qa, /qa-only, /design-review, /setup-browser-cookies, /setup-deploy, /setup-gbrain, /retro, /investigate, /document-release, /document-generate, /codex, /cso, /autoplan, /plan-devex-review, /devex-review, /careful, /freeze, /guard, /unfreeze, /gstack-upgrade, /learn. Then ask the user if they also want to add gstack to the current project so teammates get it.
|
> Install gstack: run **`git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup`** then add a "gstack" section to CLAUDE.md that says to use the /browse skill from gstack for all web browsing, never use mcp\_\_claude-in-chrome\_\_\* tools, and lists the available skills: /office-hours, /plan-ceo-review, /plan-eng-review, /plan-design-review, /design-consultation, /design-shotgun, /design-html, /review, /ship, /land-and-deploy, /canary, /web-performance-benchmark, /browse, /connect-chrome, /qa, /qa-only, /design-review, /setup-browser-cookies, /setup-deploy, /setup-gbrain, /retro, /investigate, /document-release, /document-generate, /codex, /cso, /autoplan, /plan-devex-review, /devex-review, /careful, /freeze, /guard, /unfreeze, /gstack-upgrade, /learn. Then ask the user if they also want to add gstack to the current project so teammates get it.
|
||||||
|
|
||||||
### Step 2: Team mode — auto-update for shared repos (recommended)
|
### Step 2: Team mode — auto-update for shared repos (recommended)
|
||||||
|
|
||||||
|
|
@ -197,7 +197,7 @@ Each skill feeds into the next. `/office-hours` writes a design doc that `/plan-
|
||||||
| `/ship` | **Release Engineer** | Sync main, run tests, audit coverage, push, open PR. Bootstraps test frameworks if you don't have one. |
|
| `/ship` | **Release Engineer** | Sync main, run tests, audit coverage, push, open PR. Bootstraps test frameworks if you don't have one. |
|
||||||
| `/land-and-deploy` | **Release Engineer** | Merge the PR, wait for CI and deploy, verify production health. One command from "approved" to "verified in production." |
|
| `/land-and-deploy` | **Release Engineer** | Merge the PR, wait for CI and deploy, verify production health. One command from "approved" to "verified in production." |
|
||||||
| `/canary` | **SRE** | Post-deploy monitoring loop. Watches for console errors, performance regressions, and page failures. |
|
| `/canary` | **SRE** | Post-deploy monitoring loop. Watches for console errors, performance regressions, and page failures. |
|
||||||
| `/benchmark` | **Performance Engineer** | Baseline page load times, Core Web Vitals, and resource sizes. Compare before/after on every PR. |
|
| `/web-performance-benchmark` | **Performance Engineer** | Baseline page load times, Core Web Vitals, and resource sizes. Compare before/after on every PR. |
|
||||||
| `/document-release` | **Technical Writer** | Update all project docs to match what you just shipped. Catches stale READMEs automatically. Builds a Diataxis coverage map (reference / how-to / tutorial / explanation) so gaps are visible in the PR body. |
|
| `/document-release` | **Technical Writer** | Update all project docs to match what you just shipped. Catches stale READMEs automatically. Builds a Diataxis coverage map (reference / how-to / tutorial / explanation) so gaps are visible in the PR body. |
|
||||||
| `/document-generate` | **Documentation Author** | Generate missing docs from scratch using the Diataxis framework. Researches the codebase first, then writes reference / how-to / tutorial / explanation docs that actually match the code. Invokable standalone or chained from `/document-release` when the coverage map finds gaps. Learn more: [tutorial](docs/tutorial-document-generate.md) • [how-to](docs/howto-document-a-shipped-feature.md) • [why Diataxis](docs/explanation-diataxis-in-gstack.md). |
|
| `/document-generate` | **Documentation Author** | Generate missing docs from scratch using the Diataxis framework. Researches the codebase first, then writes reference / how-to / tutorial / explanation docs that actually match the code. Invokable standalone or chained from `/document-release` when the coverage map finds gaps. Learn more: [tutorial](docs/tutorial-document-generate.md) • [how-to](docs/howto-document-a-shipped-feature.md) • [why Diataxis](docs/explanation-diataxis-in-gstack.md). |
|
||||||
| `/retro` | **Eng Manager** | Team-aware weekly retro. Per-person breakdowns, shipping streaks, test health trends, growth opportunities. `/retro global` runs across all your projects and AI tools (Claude Code, Codex, Gemini). |
|
| `/retro` | **Eng Manager** | Team-aware weekly retro. Per-person breakdowns, shipping streaks, test health trends, growth opportunities. `/retro global` runs across all your projects and AI tools (Claude Code, Codex, Gemini). |
|
||||||
|
|
@ -480,7 +480,7 @@ On Windows without Developer Mode (MSYS2 / Git Bash), `setup` falls back to file
|
||||||
Use /browse from gstack for all web browsing. Never use mcp__claude-in-chrome__* tools.
|
Use /browse from gstack for all web browsing. Never use mcp__claude-in-chrome__* tools.
|
||||||
Available skills: /office-hours, /plan-ceo-review, /plan-eng-review, /plan-design-review,
|
Available skills: /office-hours, /plan-ceo-review, /plan-eng-review, /plan-design-review,
|
||||||
/design-consultation, /design-shotgun, /design-html, /review, /ship, /land-and-deploy,
|
/design-consultation, /design-shotgun, /design-html, /review, /ship, /land-and-deploy,
|
||||||
/canary, /benchmark, /browse, /open-gstack-browser, /qa, /qa-only, /design-review,
|
/canary, /web-performance-benchmark, /browse, /open-gstack-browser, /qa, /qa-only, /design-review,
|
||||||
/setup-browser-cookies, /setup-deploy, /setup-gbrain, /sync-gbrain, /retro, /investigate,
|
/setup-browser-cookies, /setup-deploy, /setup-gbrain, /sync-gbrain, /retro, /investigate,
|
||||||
/document-release, /document-generate, /codex, /cso, /autoplan, /pair-agent, /careful, /freeze,
|
/document-release, /document-generate, /codex, /cso, /autoplan, /pair-agent, /careful, /freeze,
|
||||||
/guard, /unfreeze, /gstack-upgrade, /learn.
|
/guard, /unfreeze, /gstack-upgrade, /learn.
|
||||||
|
|
|
||||||
2
SKILL.md
2
SKILL.md
|
|
@ -587,7 +587,7 @@ quality gates that produce better results than answering inline.
|
||||||
- User asks to make a PDF, document, publication → invoke `/make-pdf`
|
- User asks to make a PDF, document, publication → invoke `/make-pdf`
|
||||||
- User asks to launch a real browser for QA, "open the browser" → invoke `/open-gstack-browser`
|
- User asks to launch a real browser for QA, "open the browser" → invoke `/open-gstack-browser`
|
||||||
- User asks to import cookies for authenticated testing → invoke `/setup-browser-cookies`
|
- User asks to import cookies for authenticated testing → invoke `/setup-browser-cookies`
|
||||||
- User asks about page speed, performance regression, benchmarks → invoke `/benchmark`
|
- User asks about page speed, Core Web Vitals, or a web performance regression → invoke `/web-performance-benchmark`
|
||||||
- User asks what gstack has learned, "show learnings" → invoke `/learn`
|
- User asks what gstack has learned, "show learnings" → invoke `/learn`
|
||||||
- User asks to tune question sensitivity, "stop asking me that" → invoke `/plan-tune`
|
- User asks to tune question sensitivity, "stop asking me that" → invoke `/plan-tune`
|
||||||
- User asks for code quality dashboard, "health check" → invoke `/health`
|
- User asks for code quality dashboard, "health check" → invoke `/health`
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ quality gates that produce better results than answering inline.
|
||||||
- User asks to make a PDF, document, publication → invoke `/make-pdf`
|
- User asks to make a PDF, document, publication → invoke `/make-pdf`
|
||||||
- User asks to launch a real browser for QA, "open the browser" → invoke `/open-gstack-browser`
|
- User asks to launch a real browser for QA, "open the browser" → invoke `/open-gstack-browser`
|
||||||
- User asks to import cookies for authenticated testing → invoke `/setup-browser-cookies`
|
- User asks to import cookies for authenticated testing → invoke `/setup-browser-cookies`
|
||||||
- User asks about page speed, performance regression, benchmarks → invoke `/benchmark`
|
- User asks about page speed, Core Web Vitals, or a web performance regression → invoke `/web-performance-benchmark`
|
||||||
- User asks what gstack has learned, "show learnings" → invoke `/learn`
|
- User asks what gstack has learned, "show learnings" → invoke `/learn`
|
||||||
- User asks to tune question sensitivity, "stop asking me that" → invoke `/plan-tune`
|
- User asks to tune question sensitivity, "stop asking me that" → invoke `/plan-tune`
|
||||||
- User asks for code quality dashboard, "health check" → invoke `/health`
|
- User asks for code quality dashboard, "health check" → invoke `/health`
|
||||||
|
|
|
||||||
2
TODOS.md
2
TODOS.md
|
|
@ -2191,7 +2191,7 @@ Shipped in v0.6.5. TemplateContext in gen-skill-docs.ts bakes skill name into pr
|
||||||
### Deploy pipeline (v0.9.8.0)
|
### Deploy pipeline (v0.9.8.0)
|
||||||
- /land-and-deploy — merge PR, wait for CI/deploy, canary verification
|
- /land-and-deploy — merge PR, wait for CI/deploy, canary verification
|
||||||
- /canary — post-deploy monitoring loop with anomaly detection
|
- /canary — post-deploy monitoring loop with anomaly detection
|
||||||
- /benchmark — performance regression detection with Core Web Vitals
|
- /web-performance-benchmark — performance regression detection with Core Web Vitals
|
||||||
- /setup-deploy — one-time deploy platform configuration
|
- /setup-deploy — one-time deploy platform configuration
|
||||||
- /review Performance & Bundle Impact pass
|
- /review Performance & Bundle Impact pass
|
||||||
- E2E model pinning (Sonnet default, Opus for quality tests)
|
- E2E model pinning (Sonnet default, Opus for quality tests)
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ allowed-tools:
|
||||||
Runs the same prompt through Claude,
|
Runs the same prompt through Claude,
|
||||||
GPT (via Codex CLI), and Gemini side-by-side — compares latency, tokens, cost,
|
GPT (via Codex CLI), and Gemini side-by-side — compares latency, tokens, cost,
|
||||||
and optionally quality via LLM judge. Answers "which model is actually best
|
and optionally quality via LLM judge. Answers "which model is actually best
|
||||||
for this skill?" with data instead of vibes. Separate from /benchmark, which
|
for this skill?" with data instead of vibes. Separate from /web-performance-benchmark, which
|
||||||
measures web page performance. Use when: "benchmark models", "compare models",
|
measures web page performance. Use when: "benchmark models", "compare models",
|
||||||
"which model is best for X", "cross-model comparison", "model shootout".
|
"which model is best for X", "cross-model comparison", "model shootout".
|
||||||
|
|
||||||
|
|
@ -539,7 +539,7 @@ Skills that run plan reviews (`/plan-*-review`, `/codex review`) include the EXI
|
||||||
|
|
||||||
You are running the `/benchmark-models` workflow. Wraps the `gstack-model-benchmark` binary with an interactive flow that picks a prompt, confirms providers, previews auth, and runs the benchmark.
|
You are running the `/benchmark-models` workflow. Wraps the `gstack-model-benchmark` binary with an interactive flow that picks a prompt, confirms providers, previews auth, and runs the benchmark.
|
||||||
|
|
||||||
Different from `/benchmark` — that skill measures web page performance (Core Web Vitals, load times). This skill measures AI model performance on gstack skills or arbitrary prompts.
|
Different from `/web-performance-benchmark` — that skill measures web page performance (Core Web Vitals, load times). This skill measures AI model performance on gstack skills or arbitrary prompts.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ description: |
|
||||||
Cross-model benchmark for gstack skills. Runs the same prompt through Claude,
|
Cross-model benchmark for gstack skills. Runs the same prompt through Claude,
|
||||||
GPT (via Codex CLI), and Gemini side-by-side — compares latency, tokens, cost,
|
GPT (via Codex CLI), and Gemini side-by-side — compares latency, tokens, cost,
|
||||||
and optionally quality via LLM judge. Answers "which model is actually best
|
and optionally quality via LLM judge. Answers "which model is actually best
|
||||||
for this skill?" with data instead of vibes. Separate from /benchmark, which
|
for this skill?" with data instead of vibes. Separate from /web-performance-benchmark, which
|
||||||
measures web page performance. Use when: "benchmark models", "compare models",
|
measures web page performance. Use when: "benchmark models", "compare models",
|
||||||
"which model is best for X", "cross-model comparison", "model shootout". (gstack)
|
"which model is best for X", "cross-model comparison", "model shootout". (gstack)
|
||||||
voice-triggers:
|
voice-triggers:
|
||||||
|
|
@ -30,7 +30,7 @@ allowed-tools:
|
||||||
|
|
||||||
You are running the `/benchmark-models` workflow. Wraps the `gstack-model-benchmark` binary with an interactive flow that picks a prompt, confirms providers, previews auth, and runs the benchmark.
|
You are running the `/benchmark-models` workflow. Wraps the `gstack-model-benchmark` binary with an interactive flow that picks a prompt, confirms providers, previews auth, and runs the benchmark.
|
||||||
|
|
||||||
Different from `/benchmark` — that skill measures web page performance (Core Web Vitals, load times). This skill measures AI model performance on gstack skills or arbitrary prompts.
|
Different from `/web-performance-benchmark` — that skill measures web page performance (Core Web Vitals, load times). This skill measures AI model performance on gstack skills or arbitrary prompts.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
---
|
---
|
||||||
name: benchmark
|
name: web-performance-benchmark
|
||||||
preamble-tier: 1
|
preamble-tier: 1
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Performance regression detection using the browse daemon. (gstack)
|
description: Web page performance regression detection using the browse daemon. (gstack)
|
||||||
triggers:
|
triggers:
|
||||||
- performance benchmark
|
- web performance benchmark
|
||||||
- check page speed
|
- check page speed
|
||||||
- detect performance regression
|
- detect page performance regression
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
@ -23,8 +23,9 @@ allowed-tools:
|
||||||
Establishes
|
Establishes
|
||||||
baselines for page load times, Core Web Vitals, and resource sizes.
|
baselines for page load times, Core Web Vitals, and resource sizes.
|
||||||
Compares before/after on every PR. Tracks performance trends over time.
|
Compares before/after on every PR. Tracks performance trends over time.
|
||||||
Use when: "performance", "benchmark", "page speed", "lighthouse", "web vitals",
|
Use when: "web performance benchmark", "page speed", "lighthouse",
|
||||||
"bundle size", "load time".
|
"web vitals", "bundle size", "load time". Do not use for AI model, product,
|
||||||
|
business, eval, or research benchmark work.
|
||||||
|
|
||||||
Voice triggers (speech-to-text aliases): "speed test", "check performance".
|
Voice triggers (speech-to-text aliases): "speed test", "check performance".
|
||||||
|
|
||||||
|
|
@ -84,7 +85,7 @@ _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning
|
||||||
echo "QUESTION_TUNING: $_QUESTION_TUNING"
|
echo "QUESTION_TUNING: $_QUESTION_TUNING"
|
||||||
mkdir -p ~/.gstack/analytics
|
mkdir -p ~/.gstack/analytics
|
||||||
if [ "$_TEL" != "off" ]; then
|
if [ "$_TEL" != "off" ]; then
|
||||||
echo '{"skill":"benchmark","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
|
echo '{"skill":"web-performance-benchmark","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(_repo=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null | tr -cd 'a-zA-Z0-9._-'); echo "${_repo:-unknown}")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do
|
for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do
|
||||||
if [ -f "$_PF" ]; then
|
if [ -f "$_PF" ]; then
|
||||||
|
|
@ -106,7 +107,7 @@ if [ -f "$_LEARN_FILE" ]; then
|
||||||
else
|
else
|
||||||
echo "LEARNINGS: 0"
|
echo "LEARNINGS: 0"
|
||||||
fi
|
fi
|
||||||
~/.claude/skills/gstack/bin/gstack-timeline-log '{"skill":"benchmark","event":"started","branch":"'"$_BRANCH"'","session":"'"$_SESSION_ID"'"}' 2>/dev/null &
|
~/.claude/skills/gstack/bin/gstack-timeline-log '{"skill":"web-performance-benchmark","event":"started","branch":"'"$_BRANCH"'","session":"'"$_SESSION_ID"'"}' 2>/dev/null &
|
||||||
_HAS_ROUTING="no"
|
_HAS_ROUTING="no"
|
||||||
if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
|
if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
|
||||||
_HAS_ROUTING="yes"
|
_HAS_ROUTING="yes"
|
||||||
|
|
@ -571,22 +572,33 @@ If `NEEDS_SETUP`:
|
||||||
fi
|
fi
|
||||||
```
|
```
|
||||||
|
|
||||||
# /benchmark — Performance Regression Detection
|
# /web-performance-benchmark - Web Page Performance Regression Detection
|
||||||
|
|
||||||
You are a **Performance Engineer** who has optimized apps serving millions of requests. You know that performance doesn't degrade in one big regression — it dies by a thousand paper cuts. Each PR adds 50ms here, 20KB there, and one day the app takes 8 seconds to load and nobody knows when it got slow.
|
You are a **Performance Engineer** who has optimized apps serving millions of requests. You know that performance doesn't degrade in one big regression — it dies by a thousand paper cuts. Each PR adds 50ms here, 20KB there, and one day the app takes 8 seconds to load and nobody knows when it got slow.
|
||||||
|
|
||||||
Your job is to measure, baseline, compare, and alert. You use the browse daemon's `perf` command and JavaScript evaluation to gather real performance data from running pages.
|
Your job is to measure, baseline, compare, and alert. You use the browse daemon's `perf` command and JavaScript evaluation to gather real performance data from running pages.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
Use this skill for web page speed, Core Web Vitals, resource sizes, and performance
|
||||||
|
regressions. Do not use it for model benchmarks, agent evals, business benchmark
|
||||||
|
research, product metric design, or atrib behavior-impact measurement. For
|
||||||
|
cross-model gstack skill comparisons, use `/benchmark-models`.
|
||||||
|
|
||||||
## User-invocable
|
## User-invocable
|
||||||
When the user types `/benchmark`, run this skill.
|
Use `/web-performance-benchmark` for new work.
|
||||||
|
|
||||||
|
Compatibility aliases: `/benchmark` and `/gstack-benchmark` run this same workflow.
|
||||||
|
Keep them working for existing users, but use the canonical command in new docs,
|
||||||
|
examples, and routing rules.
|
||||||
|
|
||||||
## Arguments
|
## Arguments
|
||||||
- `/benchmark <url>` — full performance audit with baseline comparison
|
- `/web-performance-benchmark <url>` — full performance audit with baseline comparison
|
||||||
- `/benchmark <url> --baseline` — capture baseline (run before making changes)
|
- `/web-performance-benchmark <url> --baseline` — capture baseline (run before making changes)
|
||||||
- `/benchmark <url> --quick` — single-pass timing check (no baseline needed)
|
- `/web-performance-benchmark <url> --quick` — single-pass timing check (no baseline needed)
|
||||||
- `/benchmark <url> --pages /,/dashboard,/api/health` — specify pages
|
- `/web-performance-benchmark <url> --pages /,/dashboard,/api/health` — specify pages
|
||||||
- `/benchmark --diff` — benchmark only pages affected by current branch
|
- `/web-performance-benchmark --diff` — benchmark only pages affected by current branch
|
||||||
- `/benchmark --trend` — show performance trends from historical data
|
- `/web-performance-benchmark --trend` — show performance trends from historical data
|
||||||
|
|
||||||
## Instructions
|
## Instructions
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,21 @@
|
||||||
---
|
---
|
||||||
name: benchmark
|
name: web-performance-benchmark
|
||||||
preamble-tier: 1
|
preamble-tier: 1
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: |
|
description: |
|
||||||
Performance regression detection using the browse daemon. Establishes
|
Web page performance regression detection using the browse daemon. Establishes
|
||||||
baselines for page load times, Core Web Vitals, and resource sizes.
|
baselines for page load times, Core Web Vitals, and resource sizes.
|
||||||
Compares before/after on every PR. Tracks performance trends over time.
|
Compares before/after on every PR. Tracks performance trends over time.
|
||||||
Use when: "performance", "benchmark", "page speed", "lighthouse", "web vitals",
|
Use when: "web performance benchmark", "page speed", "lighthouse",
|
||||||
"bundle size", "load time". (gstack)
|
"web vitals", "bundle size", "load time". Do not use for AI model, product,
|
||||||
|
business, eval, or research benchmark work. (gstack)
|
||||||
voice-triggers:
|
voice-triggers:
|
||||||
- "speed test"
|
- "speed test"
|
||||||
- "check performance"
|
- "check performance"
|
||||||
triggers:
|
triggers:
|
||||||
- performance benchmark
|
- web performance benchmark
|
||||||
- check page speed
|
- check page speed
|
||||||
- detect performance regression
|
- detect page performance regression
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
@ -27,22 +28,33 @@ allowed-tools:
|
||||||
|
|
||||||
{{BROWSE_SETUP}}
|
{{BROWSE_SETUP}}
|
||||||
|
|
||||||
# /benchmark — Performance Regression Detection
|
# /web-performance-benchmark - Web Page Performance Regression Detection
|
||||||
|
|
||||||
You are a **Performance Engineer** who has optimized apps serving millions of requests. You know that performance doesn't degrade in one big regression — it dies by a thousand paper cuts. Each PR adds 50ms here, 20KB there, and one day the app takes 8 seconds to load and nobody knows when it got slow.
|
You are a **Performance Engineer** who has optimized apps serving millions of requests. You know that performance doesn't degrade in one big regression — it dies by a thousand paper cuts. Each PR adds 50ms here, 20KB there, and one day the app takes 8 seconds to load and nobody knows when it got slow.
|
||||||
|
|
||||||
Your job is to measure, baseline, compare, and alert. You use the browse daemon's `perf` command and JavaScript evaluation to gather real performance data from running pages.
|
Your job is to measure, baseline, compare, and alert. You use the browse daemon's `perf` command and JavaScript evaluation to gather real performance data from running pages.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
Use this skill for web page speed, Core Web Vitals, resource sizes, and performance
|
||||||
|
regressions. Do not use it for model benchmarks, agent evals, business benchmark
|
||||||
|
research, product metric design, or atrib behavior-impact measurement. For
|
||||||
|
cross-model gstack skill comparisons, use `/benchmark-models`.
|
||||||
|
|
||||||
## User-invocable
|
## User-invocable
|
||||||
When the user types `/benchmark`, run this skill.
|
Use `/web-performance-benchmark` for new work.
|
||||||
|
|
||||||
|
Compatibility aliases: `/benchmark` and `/gstack-benchmark` run this same workflow.
|
||||||
|
Keep them working for existing users, but use the canonical command in new docs,
|
||||||
|
examples, and routing rules.
|
||||||
|
|
||||||
## Arguments
|
## Arguments
|
||||||
- `/benchmark <url>` — full performance audit with baseline comparison
|
- `/web-performance-benchmark <url>` — full performance audit with baseline comparison
|
||||||
- `/benchmark <url> --baseline` — capture baseline (run before making changes)
|
- `/web-performance-benchmark <url> --baseline` — capture baseline (run before making changes)
|
||||||
- `/benchmark <url> --quick` — single-pass timing check (no baseline needed)
|
- `/web-performance-benchmark <url> --quick` — single-pass timing check (no baseline needed)
|
||||||
- `/benchmark <url> --pages /,/dashboard,/api/health` — specify pages
|
- `/web-performance-benchmark <url> --pages /,/dashboard,/api/health` — specify pages
|
||||||
- `/benchmark --diff` — benchmark only pages affected by current branch
|
- `/web-performance-benchmark --diff` — benchmark only pages affected by current branch
|
||||||
- `/benchmark --trend` — show performance trends from historical data
|
- `/web-performance-benchmark --trend` — show performance trends from historical data
|
||||||
|
|
||||||
## Instructions
|
## Instructions
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -193,7 +193,7 @@ Every gstack skill becomes a browser capability.
|
||||||
| `/qa` | Test every page, find bugs, fix them, verify fixes |
|
| `/qa` | Test every page, find bugs, fix them, verify fixes |
|
||||||
| `/design-review` | Screenshot → analyze → fix CSS → screenshot again |
|
| `/design-review` | Screenshot → analyze → fix CSS → screenshot again |
|
||||||
| `/investigate` | See the error in browser → trace to code → fix → verify |
|
| `/investigate` | See the error in browser → trace to code → fix → verify |
|
||||||
| `/benchmark` | Measure page performance → detect regressions → alert |
|
| `/web-performance-benchmark` | Measure page performance → detect regressions → alert |
|
||||||
| `/canary` | Monitor deployed site → screenshot periodically → alert on changes |
|
| `/canary` | Monitor deployed site → screenshot periodically → alert on changes |
|
||||||
| `/ship` | Run tests → review diff → create PR → verify deployment in browser |
|
| `/ship` | Run tests → review diff → create PR → verify deployment in browser |
|
||||||
| `/cso` | Audit page for XSS, open redirects, clickjacking in real browser |
|
| `/cso` | Audit page for XSS, open redirects, clickjacking in real browser |
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ Detailed guides for every gstack skill — philosophy, workflow, and examples.
|
||||||
| [`/ship`](#ship) | **Release Engineer** | Sync main, run tests, audit coverage, push, open PR. Bootstraps test frameworks if you don't have one. One command. |
|
| [`/ship`](#ship) | **Release Engineer** | Sync main, run tests, audit coverage, push, open PR. Bootstraps test frameworks if you don't have one. One command. |
|
||||||
| [`/land-and-deploy`](#land-and-deploy) | **Release Engineer** | Merge the PR, wait for CI and deploy, verify production health. One command from "approved" to "verified in production." |
|
| [`/land-and-deploy`](#land-and-deploy) | **Release Engineer** | Merge the PR, wait for CI and deploy, verify production health. One command from "approved" to "verified in production." |
|
||||||
| [`/canary`](#canary) | **SRE** | Post-deploy monitoring loop. Watches for console errors, performance regressions, and page failures using the browse daemon. |
|
| [`/canary`](#canary) | **SRE** | Post-deploy monitoring loop. Watches for console errors, performance regressions, and page failures using the browse daemon. |
|
||||||
| [`/benchmark`](#benchmark) | **Performance Engineer** | Baseline page load times, Core Web Vitals, and resource sizes. Compare before/after on every PR. Track trends over time. |
|
| [`/web-performance-benchmark`](#web-performance-benchmark) | **Performance Engineer** | Baseline page load times, Core Web Vitals, and resource sizes. Compare before/after on every PR. Track trends over time. |
|
||||||
| [`/cso`](#cso) | **Chief Security Officer** | OWASP Top 10 + STRIDE threat modeling security audit. Scans for injection, auth, crypto, and access control issues. |
|
| [`/cso`](#cso) | **Chief Security Officer** | OWASP Top 10 + STRIDE threat modeling security audit. Scans for injection, auth, crypto, and access control issues. |
|
||||||
| [`/document-release`](#document-release) | **Technical Writer** | Update all project docs to match what you just shipped. Catches stale READMEs automatically. |
|
| [`/document-release`](#document-release) | **Technical Writer** | Update all project docs to match what you just shipped. Catches stale READMEs automatically. |
|
||||||
| [`/document-generate`](#document-generate) | **Technical Writer** | Generate Diataxis docs (tutorial / how-to / reference / explanation) for a feature from code. |
|
| [`/document-generate`](#document-generate) | **Technical Writer** | Generate Diataxis docs (tutorial / how-to / reference / explanation) for a feature from code. |
|
||||||
|
|
@ -715,16 +715,16 @@ Claude: Monitoring 8 pages every 2 minutes...
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## `/benchmark`
|
## `/web-performance-benchmark`
|
||||||
|
|
||||||
This is my **performance engineer mode**.
|
This is my **performance engineer mode**.
|
||||||
|
|
||||||
`/benchmark` establishes performance baselines for your pages: load time, Core Web Vitals (LCP, CLS, INP), resource counts, and total transfer size. Run it before and after a PR to catch regressions.
|
`/web-performance-benchmark` establishes performance baselines for your pages: load time, Core Web Vitals (LCP, CLS, INP), resource counts, and total transfer size. Run it before and after a PR to catch regressions.
|
||||||
|
|
||||||
It uses the browse daemon for real Chromium measurements, not synthetic estimates. Multiple runs averaged. Results persist so you can track trends across PRs.
|
It uses the browse daemon for real Chromium measurements, not synthetic estimates. Multiple runs averaged. Results persist so you can track trends across PRs.
|
||||||
|
|
||||||
```
|
```
|
||||||
You: /benchmark https://myapp.com
|
You: /web-performance-benchmark https://myapp.com
|
||||||
|
|
||||||
Claude: Benchmarking 5 pages (3 runs each)...
|
Claude: Benchmarking 5 pages (3 runs each)...
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,10 @@ Conventions:
|
||||||
## Skills
|
## Skills
|
||||||
|
|
||||||
- [/autoplan](autoplan/SKILL.md): Auto-review pipeline — reads the full CEO, design, eng, and DX review skills from disk and runs them sequentially with auto-decisions using 6 decision principles.
|
- [/autoplan](autoplan/SKILL.md): Auto-review pipeline — reads the full CEO, design, eng, and DX review skills from disk and runs them sequentially with auto-decisions using 6 decision principles.
|
||||||
- [/benchmark](benchmark/SKILL.md): Performance regression detection using the browse daemon.
|
|
||||||
- [/benchmark-models](benchmark-models/SKILL.md): Cross-model benchmark for gstack skills.
|
- [/benchmark-models](benchmark-models/SKILL.md): Cross-model benchmark for gstack skills.
|
||||||
- [/browse](browse/SKILL.md): Fast headless browser for QA testing and site dogfooding.
|
- [/browse](browse/SKILL.md): Fast headless browser for QA testing and site dogfooding.
|
||||||
- [/canary](canary/SKILL.md): Post-deploy canary monitoring.
|
- [/canary](canary/SKILL.md): Post-deploy canary monitoring.
|
||||||
- [/careful](careful/SKILL.md): Safety guardrails for destructive commands.
|
- [/careful](careful/SKILL.md): Safety guardrails for destructive commands.
|
||||||
- [/claude](claude/SKILL.md): Claude Code CLI wrapper for non-Claude hosts - three modes.
|
|
||||||
- [/codex](codex/SKILL.md): OpenAI Codex CLI wrapper — three modes.
|
- [/codex](codex/SKILL.md): OpenAI Codex CLI wrapper — three modes.
|
||||||
- [/context-restore](context-restore/SKILL.md): Restore working context saved earlier by /context-save.
|
- [/context-restore](context-restore/SKILL.md): Restore working context saved earlier by /context-save.
|
||||||
- [/context-save](context-save/SKILL.md): Save working context.
|
- [/context-save](context-save/SKILL.md): Save working context.
|
||||||
|
|
@ -30,7 +28,7 @@ Conventions:
|
||||||
- [/document-generate](document-generate/SKILL.md): Generate missing documentation from scratch for a feature, module, or entire project.
|
- [/document-generate](document-generate/SKILL.md): Generate missing documentation from scratch for a feature, module, or entire project.
|
||||||
- [/document-release](document-release/SKILL.md): Post-ship documentation update.
|
- [/document-release](document-release/SKILL.md): Post-ship documentation update.
|
||||||
- [/freeze](freeze/SKILL.md): Restrict file edits to a specific directory for the session.
|
- [/freeze](freeze/SKILL.md): Restrict file edits to a specific directory for the session.
|
||||||
- [/gstack](gstack/SKILL.md): Router for the gstack skill suite.
|
- [/gstack](SKILL.md): Router for the gstack skill suite.
|
||||||
- [/gstack-upgrade](gstack-upgrade/SKILL.md): Upgrade gstack to the latest version.
|
- [/gstack-upgrade](gstack-upgrade/SKILL.md): Upgrade gstack to the latest version.
|
||||||
- [/guard](guard/SKILL.md): Full safety mode: destructive command warnings + directory-scoped edits.
|
- [/guard](guard/SKILL.md): Full safety mode: destructive command warnings + directory-scoped edits.
|
||||||
- [/health](health/SKILL.md): Code quality dashboard.
|
- [/health](health/SKILL.md): Code quality dashboard.
|
||||||
|
|
@ -65,6 +63,7 @@ Conventions:
|
||||||
- [/spec](spec/SKILL.md): Turn vague intent into a precise, executable spec in five phases.
|
- [/spec](spec/SKILL.md): Turn vague intent into a precise, executable spec in five phases.
|
||||||
- [/sync-gbrain](sync-gbrain/SKILL.md): Keep gbrain current with this repo's code and refresh agent search guidance in CLAUDE.md.
|
- [/sync-gbrain](sync-gbrain/SKILL.md): Keep gbrain current with this repo's code and refresh agent search guidance in CLAUDE.md.
|
||||||
- [/unfreeze](unfreeze/SKILL.md): Clear the freeze boundary set by /freeze, allowing edits to all directories again.
|
- [/unfreeze](unfreeze/SKILL.md): Clear the freeze boundary set by /freeze, allowing edits to all directories again.
|
||||||
|
- [/web-performance-benchmark](benchmark/SKILL.md): Web page performance regression detection using the browse daemon.
|
||||||
|
|
||||||
## Browse Commands
|
## Browse Commands
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1898,7 +1898,7 @@ If verdict is REVERTED: Tell the user "The merge was reverted. Your changes are
|
||||||
|
|
||||||
Then suggest relevant follow-ups:
|
Then suggest relevant follow-ups:
|
||||||
- If a production URL was verified: "Want extended monitoring? Run `/canary <url>` to watch the site for the next 10 minutes."
|
- If a production URL was verified: "Want extended monitoring? Run `/canary <url>` to watch the site for the next 10 minutes."
|
||||||
- If performance data was collected: "Want a deeper performance analysis? Run `/benchmark <url>`."
|
- If performance data was collected: "Want a deeper performance analysis? Run `/web-performance-benchmark <url>`."
|
||||||
- "Need to update docs? Run `/document-release` to sync README, CHANGELOG, and other docs with what you just shipped."
|
- "Need to update docs? Run `/document-release` to sync README, CHANGELOG, and other docs with what you just shipped."
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -988,7 +988,7 @@ If verdict is REVERTED: Tell the user "The merge was reverted. Your changes are
|
||||||
|
|
||||||
Then suggest relevant follow-ups:
|
Then suggest relevant follow-ups:
|
||||||
- If a production URL was verified: "Want extended monitoring? Run `/canary <url>` to watch the site for the next 10 minutes."
|
- If a production URL was verified: "Want extended monitoring? Run `/canary <url>` to watch the site for the next 10 minutes."
|
||||||
- If performance data was collected: "Want a deeper performance analysis? Run `/benchmark <url>`."
|
- If performance data was collected: "Want a deeper performance analysis? Run `/web-performance-benchmark <url>`."
|
||||||
- "Need to update docs? Run `/document-release` to sync README, CHANGELOG, and other docs with what you just shipped."
|
- "Need to update docs? Run `/document-release` to sync README, CHANGELOG, and other docs with what you just shipped."
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -992,7 +992,7 @@ Tell the user:
|
||||||
> You're all set! Here's what you can do with the connected Chrome:
|
> You're all set! Here's what you can do with the connected Chrome:
|
||||||
>
|
>
|
||||||
> **Watch Claude work in real time:**
|
> **Watch Claude work in real time:**
|
||||||
> - Run any gstack skill (`/qa`, `/design-review`, `/benchmark`) and watch
|
> - Run any gstack skill (`/qa`, `/design-review`, `/web-performance-benchmark`) and watch
|
||||||
> every action happen in the visible Chrome window + Side Panel feed
|
> every action happen in the visible Chrome window + Side Panel feed
|
||||||
> - No cookie import needed — the Playwright browser shares its own session
|
> - No cookie import needed — the Playwright browser shares its own session
|
||||||
>
|
>
|
||||||
|
|
@ -1010,7 +1010,7 @@ Tell the user:
|
||||||
> - `/qa` runs its full test suite in the visible browser — you see every page
|
> - `/qa` runs its full test suite in the visible browser — you see every page
|
||||||
> load, every click, every assertion
|
> load, every click, every assertion
|
||||||
> - `/design-review` takes screenshots in the real browser — same pixels you see
|
> - `/design-review` takes screenshots in the real browser — same pixels you see
|
||||||
> - `/benchmark` measures performance in the headed browser
|
> - `/web-performance-benchmark` measures performance in the headed browser
|
||||||
|
|
||||||
Then proceed with whatever the user asked to do. If they didn't specify a task,
|
Then proceed with whatever the user asked to do. If they didn't specify a task,
|
||||||
ask what they'd like to test or browse.
|
ask what they'd like to test or browse.
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,7 @@ Tell the user:
|
||||||
> You're all set! Here's what you can do with the connected Chrome:
|
> You're all set! Here's what you can do with the connected Chrome:
|
||||||
>
|
>
|
||||||
> **Watch Claude work in real time:**
|
> **Watch Claude work in real time:**
|
||||||
> - Run any gstack skill (`/qa`, `/design-review`, `/benchmark`) and watch
|
> - Run any gstack skill (`/qa`, `/design-review`, `/web-performance-benchmark`) and watch
|
||||||
> every action happen in the visible Chrome window + Side Panel feed
|
> every action happen in the visible Chrome window + Side Panel feed
|
||||||
> - No cookie import needed — the Playwright browser shares its own session
|
> - No cookie import needed — the Playwright browser shares its own session
|
||||||
>
|
>
|
||||||
|
|
@ -203,7 +203,7 @@ Tell the user:
|
||||||
> - `/qa` runs its full test suite in the visible browser — you see every page
|
> - `/qa` runs its full test suite in the visible browser — you see every page
|
||||||
> load, every click, every assertion
|
> load, every click, every assertion
|
||||||
> - `/design-review` takes screenshots in the real browser — same pixels you see
|
> - `/design-review` takes screenshots in the real browser — same pixels you see
|
||||||
> - `/benchmark` measures performance in the headed browser
|
> - `/web-performance-benchmark` measures performance in the headed browser
|
||||||
|
|
||||||
Then proceed with whatever the user asked to do. If they didn't specify a task,
|
Then proceed with whatever the user asked to do. If they didn't specify a task,
|
||||||
ask what they'd like to test or browse.
|
ask what they'd like to test or browse.
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ When asked for coding work, pick the dispatch tier:
|
||||||
|
|
||||||
**HEAVY:** needs a specific gstack methodology
|
**HEAVY:** needs a specific gstack methodology
|
||||||
→ sessions_spawn(runtime: "acp", prompt: "Load gstack. Run /qa https://...")
|
→ sessions_spawn(runtime: "acp", prompt: "Load gstack. Run /qa https://...")
|
||||||
Skills: /cso, /review, /qa, /ship, /investigate, /design-review, /benchmark, /gstack-upgrade
|
Skills: /cso, /review, /qa, /ship, /investigate, /design-review, /web-performance-benchmark, /gstack-upgrade
|
||||||
|
|
||||||
**FULL:** build a complete feature, multi-day scope, needs planning + review
|
**FULL:** build a complete feature, multi-day scope, needs planning + review
|
||||||
→ sessions_spawn(runtime: "acp", prompt: "<gstack-full content>\n\n<task>")
|
→ sessions_spawn(runtime: "acp", prompt: "<gstack-full content>\n\n<task>")
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ const OUTPUT = path.join(ROOT, 'gstack', 'llms.txt');
|
||||||
interface SkillEntry {
|
interface SkillEntry {
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
href: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -113,6 +114,11 @@ function oneLine(text: string): string {
|
||||||
return first.replace(/\s+/g, ' ').trim();
|
return first.replace(/\s+/g, ' ').trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function skillDirForOutput(output: string): string {
|
||||||
|
const dir = path.dirname(output);
|
||||||
|
return dir === '.' ? '.' : dir;
|
||||||
|
}
|
||||||
|
|
||||||
interface GenerateOptions {
|
interface GenerateOptions {
|
||||||
/** Override repo root (for tests). */
|
/** Override repo root (for tests). */
|
||||||
root?: string;
|
root?: string;
|
||||||
|
|
@ -135,6 +141,9 @@ export async function generateLlmsTxt(opts: GenerateOptions = {}): Promise<Gener
|
||||||
const templates = discoverTemplates(root);
|
const templates = discoverTemplates(root);
|
||||||
const skills: SkillEntry[] = [];
|
const skills: SkillEntry[] = [];
|
||||||
for (const t of templates) {
|
for (const t of templates) {
|
||||||
|
if (skillDirForOutput(t.output) === 'claude') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const filePath = path.join(root, t.tmpl);
|
const filePath = path.join(root, t.tmpl);
|
||||||
const entry = parseSkillFrontmatter(filePath);
|
const entry = parseSkillFrontmatter(filePath);
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
|
|
@ -144,7 +153,7 @@ export async function generateLlmsTxt(opts: GenerateOptions = {}): Promise<Gener
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
skills.push(entry);
|
skills.push({ ...entry, href: t.output });
|
||||||
}
|
}
|
||||||
skills.sort((a, b) => a.name.localeCompare(b.name));
|
skills.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
|
|
@ -167,7 +176,7 @@ export async function generateLlmsTxt(opts: GenerateOptions = {}): Promise<Gener
|
||||||
lines.push('');
|
lines.push('');
|
||||||
for (const skill of skills) {
|
for (const skill of skills) {
|
||||||
const summary = oneLine(skill.description);
|
const summary = oneLine(skill.description);
|
||||||
lines.push(`- [/${skill.name}](${skill.name}/SKILL.md): ${summary}`);
|
lines.push(`- [/${skill.name}](${skill.href}): ${summary}`);
|
||||||
}
|
}
|
||||||
lines.push('');
|
lines.push('');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,13 @@
|
||||||
"voice_line": "Voice triggers (speech-to-text aliases): \"auto plan\", \"automatic review\"."
|
"voice_line": "Voice triggers (speech-to-text aliases): \"auto plan\", \"automatic review\"."
|
||||||
},
|
},
|
||||||
"benchmark": {
|
"benchmark": {
|
||||||
"lead": "Performance regression detection using the browse daemon.",
|
"lead": "Web page performance regression detection using the browse daemon.",
|
||||||
"routing": "Establishes\nbaselines for page load times, Core Web Vitals, and resource sizes.\nCompares before/after on every PR. Tracks performance trends over time.\nUse when: \"performance\", \"benchmark\", \"page speed\", \"lighthouse\", \"web vitals\",\n\"bundle size\", \"load time\".",
|
"routing": "Establishes\nbaselines for page load times, Core Web Vitals, and resource sizes.\nCompares before/after on every PR. Tracks performance trends over time.\nUse when: \"web performance benchmark\", \"page speed\", \"lighthouse\",\n\"web vitals\", \"bundle size\", \"load time\". Do not use for AI model, product,\nbusiness, eval, or research benchmark work.",
|
||||||
"voice_line": "Voice triggers (speech-to-text aliases): \"speed test\", \"check performance\"."
|
"voice_line": "Voice triggers (speech-to-text aliases): \"speed test\", \"check performance\"."
|
||||||
},
|
},
|
||||||
"benchmark-models": {
|
"benchmark-models": {
|
||||||
"lead": "Cross-model benchmark for gstack skills.",
|
"lead": "Cross-model benchmark for gstack skills.",
|
||||||
"routing": "Runs the same prompt through Claude,\nGPT (via Codex CLI), and Gemini side-by-side — compares latency, tokens, cost,\nand optionally quality via LLM judge. Answers \"which model is actually best\nfor this skill?\" with data instead of vibes. Separate from /benchmark, which\nmeasures web page performance. Use when: \"benchmark models\", \"compare models\",\n\"which model is best for X\", \"cross-model comparison\", \"model shootout\".",
|
"routing": "Runs the same prompt through Claude,\nGPT (via Codex CLI), and Gemini side-by-side — compares latency, tokens, cost,\nand optionally quality via LLM judge. Answers \"which model is actually best\nfor this skill?\" with data instead of vibes. Separate from /web-performance-benchmark, which\nmeasures web page performance. Use when: \"benchmark models\", \"compare models\",\n\"which model is best for X\", \"cross-model comparison\", \"model shootout\".",
|
||||||
"voice_line": "Voice triggers (speech-to-text aliases): \"compare models\", \"model shootout\", \"which model is best\"."
|
"voice_line": "Voice triggers (speech-to-text aliases): \"compare models\", \"model shootout\", \"which model is best\"."
|
||||||
},
|
},
|
||||||
"browse": {
|
"browse": {
|
||||||
|
|
|
||||||
8
setup
8
setup
|
|
@ -1008,6 +1008,14 @@ if [ "$INSTALL_CLAUDE" -eq 1 ]; then
|
||||||
if [ -L "$_OGB_LINK" ] || [ ! -e "$_OGB_LINK" ]; then
|
if [ -L "$_OGB_LINK" ] || [ ! -e "$_OGB_LINK" ]; then
|
||||||
_link_or_copy "gstack/open-gstack-browser" "$_OGB_LINK"
|
_link_or_copy "gstack/open-gstack-browser" "$_OGB_LINK"
|
||||||
fi
|
fi
|
||||||
|
# Compatibility aliases for the former generic benchmark command. Keep
|
||||||
|
# both paths available while new docs use /web-performance-benchmark.
|
||||||
|
for _WEB_PERF_ALIAS in benchmark gstack-benchmark; do
|
||||||
|
_WEB_PERF_LINK="$INSTALL_SKILLS_DIR/$_WEB_PERF_ALIAS"
|
||||||
|
if [ -L "$_WEB_PERF_LINK" ] || [ ! -e "$_WEB_PERF_LINK" ]; then
|
||||||
|
_link_or_copy "$SOURCE_GSTACK_DIR/benchmark" "$_WEB_PERF_LINK"
|
||||||
|
fi
|
||||||
|
done
|
||||||
if [ "$LOCAL_INSTALL" -eq 1 ]; then
|
if [ "$LOCAL_INSTALL" -eq 1 ]; then
|
||||||
log "gstack ready (project-local)."
|
log "gstack ready (project-local)."
|
||||||
log " skills: $INSTALL_SKILLS_DIR"
|
log " skills: $INSTALL_SKILLS_DIR"
|
||||||
|
|
|
||||||
|
|
@ -467,6 +467,37 @@ describe('gen-skill-docs', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('benchmark skill is scoped to web performance', () => {
|
||||||
|
const generated = fs.readFileSync(path.join(ROOT, 'benchmark', 'SKILL.md'), 'utf-8');
|
||||||
|
const template = fs.readFileSync(path.join(ROOT, 'benchmark', 'SKILL.md.tmpl'), 'utf-8');
|
||||||
|
|
||||||
|
for (const content of [generated, template]) {
|
||||||
|
const frontmatter = content.match(/^---\n([\s\S]*?)\n---/)?.[1] ?? '';
|
||||||
|
expect(frontmatter).toContain('name: web-performance-benchmark');
|
||||||
|
expect(frontmatter).not.toContain('name: benchmark\n');
|
||||||
|
expect(frontmatter).toContain('web performance benchmark');
|
||||||
|
expect(content).toContain('Use `/web-performance-benchmark` for new work.');
|
||||||
|
expect(content).toContain('Compatibility aliases: `/benchmark` and `/gstack-benchmark` run this same workflow.');
|
||||||
|
expect(content).toContain('/web-performance-benchmark <url>');
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(template).toContain('cross-model gstack skill comparisons, use `/benchmark-models`.');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('setup retains benchmark command compatibility aliases', () => {
|
||||||
|
const setup = fs.readFileSync(path.join(ROOT, 'setup'), 'utf-8');
|
||||||
|
|
||||||
|
expect(setup).toContain('for _WEB_PERF_ALIAS in benchmark gstack-benchmark; do');
|
||||||
|
expect(setup).toContain('_link_or_copy "$SOURCE_GSTACK_DIR/benchmark" "$_WEB_PERF_LINK"');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('active browser design docs route to the canonical command', () => {
|
||||||
|
const design = fs.readFileSync(path.join(ROOT, 'docs', 'designs', 'GSTACK_BROWSER_V0.md'), 'utf-8');
|
||||||
|
|
||||||
|
expect(design).toContain('| `/web-performance-benchmark` | Measure page performance');
|
||||||
|
expect(design).not.toContain('| `/benchmark` | Measure page performance');
|
||||||
|
});
|
||||||
|
|
||||||
test('qa and qa-only templates use QA_METHODOLOGY placeholder', () => {
|
test('qa and qa-only templates use QA_METHODOLOGY placeholder', () => {
|
||||||
const qaTmpl = fs.readFileSync(path.join(ROOT, 'qa', 'SKILL.md.tmpl'), 'utf-8');
|
const qaTmpl = fs.readFileSync(path.join(ROOT, 'qa', 'SKILL.md.tmpl'), 'utf-8');
|
||||||
expect(qaTmpl).toContain('{{QA_METHODOLOGY}}');
|
expect(qaTmpl).toContain('{{QA_METHODOLOGY}}');
|
||||||
|
|
@ -1691,7 +1722,9 @@ describe('Codex generation (--host codex)', () => {
|
||||||
if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === 'node_modules') continue;
|
if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === 'node_modules') continue;
|
||||||
if (entry.name === 'codex') continue; // /codex is excluded from Codex output
|
if (entry.name === 'codex') continue; // /codex is excluded from Codex output
|
||||||
if (!fs.existsSync(path.join(ROOT, entry.name, 'SKILL.md.tmpl'))) continue;
|
if (!fs.existsSync(path.join(ROOT, entry.name, 'SKILL.md.tmpl'))) continue;
|
||||||
const codexName = entry.name.startsWith('gstack-') ? entry.name : `gstack-${entry.name}`;
|
const template = fs.readFileSync(path.join(ROOT, entry.name, 'SKILL.md.tmpl'), 'utf-8');
|
||||||
|
const frontmatterName = template.match(/^name:\s*(.+)$/m)?.[1]?.trim() ?? entry.name;
|
||||||
|
const codexName = frontmatterName.startsWith('gstack-') ? frontmatterName : `gstack-${frontmatterName}`;
|
||||||
if (isSymlinkLoop(codexName)) continue;
|
if (isSymlinkLoop(codexName)) continue;
|
||||||
skills.push({ dir: entry.name, codexName });
|
skills.push({ dir: entry.name, codexName });
|
||||||
}
|
}
|
||||||
|
|
@ -2010,7 +2043,9 @@ describe('Factory generation (--host factory)', () => {
|
||||||
if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === 'node_modules') continue;
|
if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === 'node_modules') continue;
|
||||||
if (entry.name === 'codex') continue;
|
if (entry.name === 'codex') continue;
|
||||||
if (!fs.existsSync(path.join(ROOT, entry.name, 'SKILL.md.tmpl'))) continue;
|
if (!fs.existsSync(path.join(ROOT, entry.name, 'SKILL.md.tmpl'))) continue;
|
||||||
const factoryName = entry.name.startsWith('gstack-') ? entry.name : `gstack-${entry.name}`;
|
const template = fs.readFileSync(path.join(ROOT, entry.name, 'SKILL.md.tmpl'), 'utf-8');
|
||||||
|
const frontmatterName = template.match(/^name:\s*(.+)$/m)?.[1]?.trim() ?? entry.name;
|
||||||
|
const factoryName = frontmatterName.startsWith('gstack-') ? frontmatterName : `gstack-${frontmatterName}`;
|
||||||
if (isSymlinkLoop(factoryName)) continue;
|
if (isSymlinkLoop(factoryName)) continue;
|
||||||
skills.push({ dir: entry.name, factoryName });
|
skills.push({ dir: entry.name, factoryName });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -367,6 +367,8 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
|
||||||
'journey-retro': ['*/SKILL.md.tmpl', 'SKILL.md.tmpl', 'scripts/gen-skill-docs.ts'],
|
'journey-retro': ['*/SKILL.md.tmpl', 'SKILL.md.tmpl', 'scripts/gen-skill-docs.ts'],
|
||||||
'journey-design-system': ['*/SKILL.md.tmpl', 'SKILL.md.tmpl', 'scripts/gen-skill-docs.ts'],
|
'journey-design-system': ['*/SKILL.md.tmpl', 'SKILL.md.tmpl', 'scripts/gen-skill-docs.ts'],
|
||||||
'journey-visual-qa': ['*/SKILL.md.tmpl', 'SKILL.md.tmpl', 'scripts/gen-skill-docs.ts'],
|
'journey-visual-qa': ['*/SKILL.md.tmpl', 'SKILL.md.tmpl', 'scripts/gen-skill-docs.ts'],
|
||||||
|
'journey-web-performance-benchmark': ['benchmark/**', 'scripts/gen-skill-docs.ts'],
|
||||||
|
'journey-web-performance-benchmark-legacy-alias': ['benchmark/**', 'scripts/gen-skill-docs.ts', 'setup'],
|
||||||
|
|
||||||
// Opus 4.7 behavior evals — keys match testName: values in the test file.
|
// Opus 4.7 behavior evals — keys match testName: values in the test file.
|
||||||
// Routing sub-tests use template literal `routing-${c.name}` testNames,
|
// Routing sub-tests use template literal `routing-${c.name}` testNames,
|
||||||
|
|
@ -735,6 +737,8 @@ export const E2E_TIERS: Record<string, 'gate' | 'periodic'> = {
|
||||||
'journey-retro': 'periodic',
|
'journey-retro': 'periodic',
|
||||||
'journey-design-system': 'periodic',
|
'journey-design-system': 'periodic',
|
||||||
'journey-visual-qa': 'periodic',
|
'journey-visual-qa': 'periodic',
|
||||||
|
'journey-web-performance-benchmark': 'periodic',
|
||||||
|
'journey-web-performance-benchmark-legacy-alias': 'periodic',
|
||||||
|
|
||||||
// Opus 4.7 overlay evals — periodic (non-deterministic LLM behavior + Opus cost)
|
// Opus 4.7 overlay evals — periodic (non-deterministic LLM behavior + Opus cost)
|
||||||
'fanout-arm-overlay-on': 'periodic',
|
'fanout-arm-overlay-on': 'periodic',
|
||||||
|
|
|
||||||
|
|
@ -25,17 +25,29 @@ describe('gen-llms-txt — shape', () => {
|
||||||
expect(generated.content).toContain('auto-generated');
|
expect(generated.content).toContain('auto-generated');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('every skill .tmpl in the repo appears in the index', () => {
|
test('every generated Claude skill appears in the index', () => {
|
||||||
const templates = discoverTemplates(ROOT);
|
const templates = discoverTemplates(ROOT);
|
||||||
|
const claudeGeneratedTemplates = templates.filter((t) => path.dirname(t.output) !== 'claude');
|
||||||
// Filter to those that successfully parsed (have name + description).
|
// Filter to those that successfully parsed (have name + description).
|
||||||
expect(generated.skills.length).toBeGreaterThan(0);
|
expect(generated.skills.length).toBeGreaterThan(0);
|
||||||
expect(generated.skills.length).toBeLessThanOrEqual(templates.length);
|
expect(generated.skills.length).toBeLessThanOrEqual(claudeGeneratedTemplates.length);
|
||||||
|
expect(generated.content).not.toContain('[/claude]');
|
||||||
|
|
||||||
for (const skill of generated.skills) {
|
for (const skill of generated.skills) {
|
||||||
expect(generated.content).toMatch(new RegExp(`/${skill.name}\\b`));
|
expect(generated.content).toMatch(new RegExp(`/${skill.name}\\b`));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('every skill link points at an existing generated file', () => {
|
||||||
|
const skillsSection = generated.content.split('## Skills')[1].split('## Browse Commands')[0];
|
||||||
|
const links = [...skillsSection.matchAll(/- \[\/[^\]]+\]\(([^)]+)\):/g)].map((m) => m[1]);
|
||||||
|
expect(links.length).toBe(generated.skills.length);
|
||||||
|
|
||||||
|
for (const href of links) {
|
||||||
|
expect(fs.existsSync(path.join(ROOT, href)), href).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test('every browse command in COMMAND_DESCRIPTIONS appears in the index', () => {
|
test('every browse command in COMMAND_DESCRIPTIONS appears in the index', () => {
|
||||||
expect(generated.browseCommands.length).toBeGreaterThan(0);
|
expect(generated.browseCommands.length).toBeGreaterThan(0);
|
||||||
for (const cmd of generated.browseCommands) {
|
for (const cmd of generated.browseCommands) {
|
||||||
|
|
|
||||||
|
|
@ -328,7 +328,7 @@ describeIfSelected('Benchmark skill E2E', ['benchmark-workflow'], () => {
|
||||||
|
|
||||||
testConcurrentIfSelected('benchmark-workflow', async () => {
|
testConcurrentIfSelected('benchmark-workflow', async () => {
|
||||||
const result = await runSkillTest({
|
const result = await runSkillTest({
|
||||||
prompt: `Read benchmark/SKILL.md for the /benchmark skill instructions.
|
prompt: `Read benchmark/SKILL.md for the /web-performance-benchmark skill instructions.
|
||||||
|
|
||||||
You are simulating a benchmark run. There is NO browse daemon available and NO production URL.
|
You are simulating a benchmark run. There is NO browse daemon available and NO production URL.
|
||||||
|
|
||||||
|
|
@ -353,8 +353,8 @@ Just create the files showing the correct schema and report format.`,
|
||||||
runId,
|
runId,
|
||||||
});
|
});
|
||||||
|
|
||||||
logCost('/benchmark', result);
|
logCost('/web-performance-benchmark', result);
|
||||||
recordE2E(evalCollector, '/benchmark workflow', 'Benchmark skill E2E', result);
|
recordE2E(evalCollector, '/web-performance-benchmark workflow', 'Web performance benchmark skill E2E', result);
|
||||||
expect(result.exitReason).toBe('success');
|
expect(result.exitReason).toBe('success');
|
||||||
|
|
||||||
expect(fs.existsSync(path.join(benchDir, '.gstack', 'benchmark-reports'))).toBe(true);
|
expect(fs.existsSync(path.join(benchDir, '.gstack', 'benchmark-reports'))).toBe(true);
|
||||||
|
|
|
||||||
|
|
@ -65,20 +65,26 @@ if (evalsEnabled && process.env.EVALS_TIER) {
|
||||||
* install that point to different worktrees or dangling targets. */
|
* install that point to different worktrees or dangling targets. */
|
||||||
function installSkills(tmpDir: string) {
|
function installSkills(tmpDir: string) {
|
||||||
const skillDirs = [
|
const skillDirs = [
|
||||||
'', // root gstack SKILL.md
|
{ source: '', command: 'gstack' },
|
||||||
'qa', 'qa-only', 'ship', 'review', 'plan-ceo-review', 'plan-eng-review',
|
{ source: 'qa' }, { source: 'qa-only' }, { source: 'ship' }, { source: 'review' },
|
||||||
'plan-design-review', 'design-review', 'design-consultation', 'retro',
|
{ source: 'plan-ceo-review' }, { source: 'plan-eng-review' },
|
||||||
'document-release', 'investigate', 'office-hours', 'browse', 'setup-browser-cookies',
|
{ source: 'plan-design-review' }, { source: 'design-review' },
|
||||||
'gstack-upgrade', 'humanizer',
|
{ source: 'design-consultation' }, { source: 'retro' },
|
||||||
|
{ source: 'document-release' }, { source: 'investigate' }, { source: 'office-hours' },
|
||||||
|
{ source: 'browse' },
|
||||||
|
{ source: 'benchmark', command: 'web-performance-benchmark' },
|
||||||
|
{ source: 'benchmark', command: 'benchmark' },
|
||||||
|
{ source: 'benchmark', command: 'gstack-benchmark' },
|
||||||
|
{ source: 'setup-browser-cookies' }, { source: 'gstack-upgrade' }, { source: 'humanizer' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const targetBase = path.join(tmpDir, '.claude', 'skills');
|
const targetBase = path.join(tmpDir, '.claude', 'skills');
|
||||||
|
|
||||||
for (const skill of skillDirs) {
|
for (const skill of skillDirs) {
|
||||||
const srcPath = path.join(ROOT, skill, 'SKILL.md');
|
const srcPath = path.join(ROOT, skill.source, 'SKILL.md');
|
||||||
if (!fs.existsSync(srcPath)) continue;
|
if (!fs.existsSync(srcPath)) continue;
|
||||||
|
|
||||||
const skillName = skill || 'gstack';
|
const skillName = skill.command || skill.source;
|
||||||
const destDir = path.join(targetBase, skillName);
|
const destDir = path.join(targetBase, skillName);
|
||||||
fs.mkdirSync(destDir, { recursive: true });
|
fs.mkdirSync(destDir, { recursive: true });
|
||||||
fs.copyFileSync(srcPath, path.join(destDir, 'SKILL.md'));
|
fs.copyFileSync(srcPath, path.join(destDir, 'SKILL.md'));
|
||||||
|
|
@ -107,6 +113,8 @@ Key routing rules:
|
||||||
- Design system, brand → invoke design-consultation
|
- Design system, brand → invoke design-consultation
|
||||||
- Visual audit, design polish → invoke design-review
|
- Visual audit, design polish → invoke design-review
|
||||||
- Architecture review → invoke plan-eng-review
|
- Architecture review → invoke plan-eng-review
|
||||||
|
- Web page speed, Core Web Vitals, or performance regression → invoke web-performance-benchmark
|
||||||
|
- /benchmark and /gstack-benchmark are compatibility aliases for web-performance-benchmark
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -181,6 +189,62 @@ describeE2E('Skill Routing E2E — Developer Journey', () => {
|
||||||
evalCollector?.finalize();
|
evalCollector?.finalize();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testIfSelected('journey-web-performance-benchmark', async () => {
|
||||||
|
const tmpDir = createRoutingWorkDir('web-performance-benchmark');
|
||||||
|
try {
|
||||||
|
const testName = 'journey-web-performance-benchmark';
|
||||||
|
const expectedSkill = 'web-performance-benchmark';
|
||||||
|
const result = await runSkillTest({
|
||||||
|
prompt: 'Our checkout page got slower after the latest release. Measure Core Web Vitals, compare the current page load against a baseline, and identify any web performance regression.',
|
||||||
|
workingDirectory: tmpDir,
|
||||||
|
maxTurns: 5,
|
||||||
|
allowedTools: ['Skill', 'Read', 'Bash', 'Glob', 'Grep'],
|
||||||
|
timeout: 60_000,
|
||||||
|
testName,
|
||||||
|
runId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const skillCalls = result.toolCalls.filter(tc => tc.tool === 'Skill');
|
||||||
|
const actualSkill = skillCalls[0]?.input?.skill;
|
||||||
|
|
||||||
|
logCost(`journey: ${testName}`, result);
|
||||||
|
recordRouting(testName, result, expectedSkill, actualSkill);
|
||||||
|
|
||||||
|
expect(skillCalls.length, `Expected Skill tool to be called but got 0 calls. Claude may have answered directly without invoking a skill. Tool calls: ${result.toolCalls.map(tc => tc.tool).join(', ')}`).toBeGreaterThan(0);
|
||||||
|
expect(actualSkill).toBe(expectedSkill);
|
||||||
|
} finally {
|
||||||
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
}, 150_000);
|
||||||
|
|
||||||
|
testIfSelected('journey-web-performance-benchmark-legacy-alias', async () => {
|
||||||
|
const tmpDir = createRoutingWorkDir('web-performance-benchmark-legacy-alias');
|
||||||
|
try {
|
||||||
|
const testName = 'journey-web-performance-benchmark-legacy-alias';
|
||||||
|
const expectedSkill = 'web-performance-benchmark';
|
||||||
|
const result = await runSkillTest({
|
||||||
|
prompt: 'Run /benchmark for the checkout page. Compare the current Core Web Vitals against the baseline and identify any performance regression.',
|
||||||
|
workingDirectory: tmpDir,
|
||||||
|
maxTurns: 5,
|
||||||
|
allowedTools: ['Skill', 'Read', 'Bash', 'Glob', 'Grep'],
|
||||||
|
timeout: 60_000,
|
||||||
|
testName,
|
||||||
|
runId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const skillCalls = result.toolCalls.filter(tc => tc.tool === 'Skill');
|
||||||
|
const actualSkill = skillCalls[0]?.input?.skill;
|
||||||
|
|
||||||
|
logCost(`journey: ${testName}`, result);
|
||||||
|
recordRouting(testName, result, expectedSkill, actualSkill);
|
||||||
|
|
||||||
|
expect(skillCalls.length, `Expected Skill tool to be called but got 0 calls. Claude may have answered directly without invoking a skill. Tool calls: ${result.toolCalls.map(tc => tc.tool).join(', ')}`).toBeGreaterThan(0);
|
||||||
|
expect(actualSkill).toBe(expectedSkill);
|
||||||
|
} finally {
|
||||||
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
}, 150_000);
|
||||||
|
|
||||||
testIfSelected('journey-ideation', async () => {
|
testIfSelected('journey-ideation', async () => {
|
||||||
const tmpDir = createRoutingWorkDir('ideation');
|
const tmpDir = createRoutingWorkDir('ideation');
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue