mirror of https://github.com/garrytan/gstack.git
fix: quote generated skill descriptions
This commit is contained in:
parent
a6fb31726c
commit
c99ea18a27
|
|
@ -31,3 +31,5 @@ jobs:
|
||||||
echo "Generated Factory SKILL.md files are stale. Run: bun run gen:skill-docs --host factory"
|
echo "Generated Factory SKILL.md files are stale. Run: bun run gen:skill-docs --host factory"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
- name: Validate generated skill frontmatter
|
||||||
|
run: bun run skill:frontmatter
|
||||||
|
|
|
||||||
2
SKILL.md
2
SKILL.md
|
|
@ -2,7 +2,7 @@
|
||||||
name: gstack
|
name: gstack
|
||||||
preamble-tier: 1
|
preamble-tier: 1
|
||||||
version: 1.1.0
|
version: 1.1.0
|
||||||
description: Fast headless browser for QA testing and site dogfooding. (gstack)
|
description: "Fast headless browser for QA testing and site dogfooding. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: autoplan
|
name: autoplan
|
||||||
preamble-tier: 3
|
preamble-tier: 3
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: 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. (gstack)
|
description: "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. (gstack)"
|
||||||
benefits-from: [office-hours]
|
benefits-from: [office-hours]
|
||||||
triggers:
|
triggers:
|
||||||
- run all reviews
|
- run all reviews
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: benchmark-models
|
name: benchmark-models
|
||||||
preamble-tier: 1
|
preamble-tier: 1
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Cross-model benchmark for gstack skills. (gstack)
|
description: "Cross-model benchmark for gstack skills. (gstack)"
|
||||||
triggers:
|
triggers:
|
||||||
- cross model benchmark
|
- cross model benchmark
|
||||||
- compare claude gpt gemini
|
- compare claude gpt gemini
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: benchmark
|
name: 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: "Performance regression detection using the browse daemon. (gstack)"
|
||||||
triggers:
|
triggers:
|
||||||
- performance benchmark
|
- performance benchmark
|
||||||
- check page speed
|
- check page speed
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: browse
|
name: browse
|
||||||
preamble-tier: 1
|
preamble-tier: 1
|
||||||
version: 1.1.0
|
version: 1.1.0
|
||||||
description: Fast headless browser for QA testing and site dogfooding. (gstack)
|
description: "Fast headless browser for QA testing and site dogfooding. (gstack)"
|
||||||
triggers:
|
triggers:
|
||||||
- browse a page
|
- browse a page
|
||||||
- headless browser
|
- headless browser
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: canary
|
name: canary
|
||||||
preamble-tier: 2
|
preamble-tier: 2
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Post-deploy canary monitoring. (gstack)
|
description: "Post-deploy canary monitoring. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
name: careful
|
name: careful
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
description: Safety guardrails for destructive commands. (gstack)
|
description: "Safety guardrails for destructive commands. (gstack)"
|
||||||
triggers:
|
triggers:
|
||||||
- be careful
|
- be careful
|
||||||
- warn before destructive
|
- warn before destructive
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: codex
|
name: codex
|
||||||
preamble-tier: 3
|
preamble-tier: 3
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: OpenAI Codex CLI wrapper — three modes. (gstack)
|
description: "OpenAI Codex CLI wrapper — three modes. (gstack)"
|
||||||
triggers:
|
triggers:
|
||||||
- codex review
|
- codex review
|
||||||
- second opinion
|
- second opinion
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: context-restore
|
name: context-restore
|
||||||
preamble-tier: 2
|
preamble-tier: 2
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Restore working context saved earlier by /context-save. (gstack)
|
description: "Restore working context saved earlier by /context-save. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: context-save
|
name: context-save
|
||||||
preamble-tier: 2
|
preamble-tier: 2
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Save working context. (gstack)
|
description: "Save working context. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: cso
|
name: cso
|
||||||
preamble-tier: 2
|
preamble-tier: 2
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
description: Chief Security Officer mode. (gstack)
|
description: "Chief Security Officer mode. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: design-consultation
|
name: design-consultation
|
||||||
preamble-tier: 3
|
preamble-tier: 3
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Design consultation: understands your product, researches the landscape, proposes a complete design system (aesthetic, typography, color, layout, spacing, motion), and generates font+color preview... (gstack)
|
description: "Design consultation: understands your product, researches the landscape, proposes a complete design system (aesthetic, typography, color, layout, spacing, motion), and generates font+color preview... (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: design-html
|
name: design-html
|
||||||
preamble-tier: 2
|
preamble-tier: 2
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Design finalization: generates production-quality Pretext-native HTML/CSS. (gstack)
|
description: "Design finalization: generates production-quality Pretext-native HTML/CSS. (gstack)"
|
||||||
triggers:
|
triggers:
|
||||||
- build the design
|
- build the design
|
||||||
- code the mockup
|
- code the mockup
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: design-review
|
name: design-review
|
||||||
preamble-tier: 4
|
preamble-tier: 4
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
description: Designer's eye QA: finds visual inconsistency, spacing issues, hierarchy problems, AI slop patterns, and slow interactions — then fixes them. (gstack)
|
description: "Designer's eye QA: finds visual inconsistency, spacing issues, hierarchy problems, AI slop patterns, and slow interactions — then fixes them. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: design-shotgun
|
name: design-shotgun
|
||||||
preamble-tier: 2
|
preamble-tier: 2
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Design shotgun: generate multiple AI design variants, open a comparison board, collect structured feedback, and iterate. (gstack)
|
description: "Design shotgun: generate multiple AI design variants, open a comparison board, collect structured feedback, and iterate. (gstack)"
|
||||||
triggers:
|
triggers:
|
||||||
- explore design variants
|
- explore design variants
|
||||||
- show me design options
|
- show me design options
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: devex-review
|
name: devex-review
|
||||||
preamble-tier: 3
|
preamble-tier: 3
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Live developer experience audit. (gstack)
|
description: "Live developer experience audit. (gstack)"
|
||||||
triggers:
|
triggers:
|
||||||
- live dx audit
|
- live dx audit
|
||||||
- test developer experience
|
- test developer experience
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: document-generate
|
name: document-generate
|
||||||
preamble-tier: 2
|
preamble-tier: 2
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Generate missing documentation from scratch for a feature, module, or entire project. (gstack)
|
description: "Generate missing documentation from scratch for a feature, module, or entire project. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: document-release
|
name: document-release
|
||||||
preamble-tier: 2
|
preamble-tier: 2
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Post-ship documentation update. (gstack)
|
description: "Post-ship documentation update. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
name: freeze
|
name: freeze
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
description: Restrict file edits to a specific directory for the session. (gstack)
|
description: "Restrict file edits to a specific directory for the session. (gstack)"
|
||||||
triggers:
|
triggers:
|
||||||
- freeze edits to directory
|
- freeze edits to directory
|
||||||
- lock editing scope
|
- lock editing scope
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
name: gstack-upgrade
|
name: gstack-upgrade
|
||||||
version: 1.1.0
|
version: 1.1.0
|
||||||
description: Upgrade gstack to the latest version.
|
description: "Upgrade gstack to the latest version."
|
||||||
triggers:
|
triggers:
|
||||||
- upgrade gstack
|
- upgrade gstack
|
||||||
- update gstack version
|
- update gstack version
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
name: guard
|
name: guard
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
description: Full safety mode: destructive command warnings + directory-scoped edits. (gstack)
|
description: "Full safety mode: destructive command warnings + directory-scoped edits. (gstack)"
|
||||||
triggers:
|
triggers:
|
||||||
- full safety mode
|
- full safety mode
|
||||||
- guard against mistakes
|
- guard against mistakes
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: health
|
name: health
|
||||||
preamble-tier: 2
|
preamble-tier: 2
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Code quality dashboard. (gstack)
|
description: "Code quality dashboard. (gstack)"
|
||||||
triggers:
|
triggers:
|
||||||
- code health check
|
- code health check
|
||||||
- quality dashboard
|
- quality dashboard
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: investigate
|
name: investigate
|
||||||
preamble-tier: 2
|
preamble-tier: 2
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Systematic debugging with root cause investigation. (gstack)
|
description: "Systematic debugging with root cause investigation. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: ios-clean
|
name: ios-clean
|
||||||
preamble-tier: 3
|
preamble-tier: 3
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Remove the DebugBridge SPM package and all #if DEBUG wiring from an iOS app. (gstack)
|
description: "Remove the DebugBridge SPM package and all #if DEBUG wiring from an iOS app. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: ios-design-review
|
name: ios-design-review
|
||||||
preamble-tier: 3
|
preamble-tier: 3
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Visual design audit for iOS apps on real hardware. (gstack)
|
description: "Visual design audit for iOS apps on real hardware. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: ios-fix
|
name: ios-fix
|
||||||
preamble-tier: 3
|
preamble-tier: 3
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Autonomous iOS bug fixer. (gstack)
|
description: "Autonomous iOS bug fixer. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: ios-qa
|
name: ios-qa
|
||||||
preamble-tier: 3
|
preamble-tier: 3
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Live-device iOS QA for SwiftUI apps. (gstack)
|
description: "Live-device iOS QA for SwiftUI apps. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: ios-sync
|
name: ios-sync
|
||||||
preamble-tier: 3
|
preamble-tier: 3
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Regenerate the iOS debug bridge against the latest upstream gstack templates. (gstack)
|
description: "Regenerate the iOS debug bridge against the latest upstream gstack templates. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: land-and-deploy
|
name: land-and-deploy
|
||||||
preamble-tier: 4
|
preamble-tier: 4
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Land and deploy workflow. (gstack)
|
description: "Land and deploy workflow. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
name: landing-report
|
name: landing-report
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
description: Read-only queue dashboard for workspace-aware ship. (gstack)
|
description: "Read-only queue dashboard for workspace-aware ship. (gstack)"
|
||||||
triggers:
|
triggers:
|
||||||
- landing report
|
- landing report
|
||||||
- version queue
|
- version queue
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: learn
|
name: learn
|
||||||
preamble-tier: 2
|
preamble-tier: 2
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Manage project learnings.
|
description: "Manage project learnings."
|
||||||
triggers:
|
triggers:
|
||||||
- show learnings
|
- show learnings
|
||||||
- what have we learned
|
- what have we learned
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: make-pdf
|
name: make-pdf
|
||||||
preamble-tier: 1
|
preamble-tier: 1
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Turn any markdown file into a publication-quality PDF. (gstack)
|
description: "Turn any markdown file into a publication-quality PDF. (gstack)"
|
||||||
triggers:
|
triggers:
|
||||||
- markdown to pdf
|
- markdown to pdf
|
||||||
- generate pdf
|
- generate pdf
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: office-hours
|
name: office-hours
|
||||||
preamble-tier: 3
|
preamble-tier: 3
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
description: YC Office Hours — two modes. (gstack)
|
description: "YC Office Hours — two modes. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
name: open-gstack-browser
|
name: open-gstack-browser
|
||||||
version: 0.2.0
|
version: 0.2.0
|
||||||
description: Launch GStack Browser — AI-controlled Chromium with the sidebar extension baked in.
|
description: "Launch GStack Browser — AI-controlled Chromium with the sidebar extension baked in."
|
||||||
triggers:
|
triggers:
|
||||||
- open gstack browser
|
- open gstack browser
|
||||||
- launch chromium
|
- launch chromium
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
"test:gemini": "EVALS=1 bun test test/gemini-e2e.test.ts",
|
"test:gemini": "EVALS=1 bun test test/gemini-e2e.test.ts",
|
||||||
"test:gemini:all": "EVALS=1 EVALS_ALL=1 bun test test/gemini-e2e.test.ts",
|
"test:gemini:all": "EVALS=1 EVALS_ALL=1 bun test test/gemini-e2e.test.ts",
|
||||||
"skill:check": "bun run scripts/skill-check.ts",
|
"skill:check": "bun run scripts/skill-check.ts",
|
||||||
|
"skill:frontmatter": "bun run scripts/skill-frontmatter-check.ts",
|
||||||
"dev:skill": "bun run scripts/dev-skill.ts",
|
"dev:skill": "bun run scripts/dev-skill.ts",
|
||||||
"start": "bun run browse/src/server.ts",
|
"start": "bun run browse/src/server.ts",
|
||||||
"eval:list": "bun run scripts/eval-list.ts",
|
"eval:list": "bun run scripts/eval-list.ts",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
name: pair-agent
|
name: pair-agent
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
description: Pair a remote AI agent with your browser. (gstack)
|
description: "Pair a remote AI agent with your browser. (gstack)"
|
||||||
triggers:
|
triggers:
|
||||||
- pair with agent
|
- pair with agent
|
||||||
- connect remote agent
|
- connect remote agent
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ name: plan-ceo-review
|
||||||
preamble-tier: 3
|
preamble-tier: 3
|
||||||
interactive: true
|
interactive: true
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: CEO/founder-mode plan review. (gstack)
|
description: "CEO/founder-mode plan review. (gstack)"
|
||||||
benefits-from: [office-hours]
|
benefits-from: [office-hours]
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ name: plan-design-review
|
||||||
preamble-tier: 3
|
preamble-tier: 3
|
||||||
interactive: true
|
interactive: true
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
description: Designer's eye plan review — interactive, like CEO and Eng review. (gstack)
|
description: "Designer's eye plan review — interactive, like CEO and Eng review. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Read
|
- Read
|
||||||
- Edit
|
- Edit
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ name: plan-devex-review
|
||||||
preamble-tier: 3
|
preamble-tier: 3
|
||||||
interactive: true
|
interactive: true
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
description: Interactive developer experience plan review. (gstack)
|
description: "Interactive developer experience plan review. (gstack)"
|
||||||
benefits-from: [office-hours]
|
benefits-from: [office-hours]
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ name: plan-eng-review
|
||||||
preamble-tier: 3
|
preamble-tier: 3
|
||||||
interactive: true
|
interactive: true
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Eng manager-mode plan review. (gstack)
|
description: "Eng manager-mode plan review. (gstack)"
|
||||||
benefits-from: [office-hours]
|
benefits-from: [office-hours]
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: plan-tune
|
name: plan-tune
|
||||||
preamble-tier: 2
|
preamble-tier: 2
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Self-tuning question sensitivity + developer psychographic for gstack (v1: observational). (gstack)
|
description: "Self-tuning question sensitivity + developer psychographic for gstack (v1: observational). (gstack)"
|
||||||
triggers:
|
triggers:
|
||||||
- tune questions
|
- tune questions
|
||||||
- stop asking me that
|
- stop asking me that
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: qa-only
|
name: qa-only
|
||||||
preamble-tier: 4
|
preamble-tier: 4
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Report-only QA testing. (gstack)
|
description: "Report-only QA testing. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: qa
|
name: qa
|
||||||
preamble-tier: 4
|
preamble-tier: 4
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
description: Systematically QA test a web application and fix bugs found. (gstack)
|
description: "Systematically QA test a web application and fix bugs found. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: retro
|
name: retro
|
||||||
preamble-tier: 2
|
preamble-tier: 2
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
description: Weekly engineering retrospective. (gstack)
|
description: "Weekly engineering retrospective. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: review
|
name: review
|
||||||
preamble-tier: 4
|
preamble-tier: 4
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Pre-landing PR review. (gstack)
|
description: "Pre-landing PR review. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
name: scrape
|
name: scrape
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Pull data from a web page. (gstack)
|
description: "Pull data from a web page. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -299,6 +299,10 @@ export function buildTrimmedDescription(parts: CatalogParts): string {
|
||||||
return `${lead}${suffix}`;
|
return `${lead}${suffix}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function yamlString(value: string): string {
|
||||||
|
return JSON.stringify(value);
|
||||||
|
}
|
||||||
|
|
||||||
/** Build the body section that holds the routing/voice prose. */
|
/** Build the body section that holds the routing/voice prose. */
|
||||||
export function buildWhenToInvokeSection(parts: CatalogParts): string {
|
export function buildWhenToInvokeSection(parts: CatalogParts): string {
|
||||||
const lines: string[] = ['## When to invoke this skill', ''];
|
const lines: string[] = ['## When to invoke this skill', ''];
|
||||||
|
|
@ -355,7 +359,7 @@ export function applyCatalogTrim(content: string, skillName: string): { content:
|
||||||
// Replace description in frontmatter — keep trailing newline so the next
|
// Replace description in frontmatter — keep trailing newline so the next
|
||||||
// YAML field doesn't collide on the same line as the description value.
|
// YAML field doesn't collide on the same line as the description value.
|
||||||
const newDesc = buildTrimmedDescription(parts);
|
const newDesc = buildTrimmedDescription(parts);
|
||||||
const newFrontmatter = frontmatter.replace(descMatch[0], `description: ${newDesc}\n`);
|
const newFrontmatter = frontmatter.replace(descMatch[0], `description: ${yamlString(newDesc)}\n`);
|
||||||
let newContent = '---\n' + newFrontmatter + content.slice(fmEnd);
|
let newContent = '---\n' + newFrontmatter + content.slice(fmEnd);
|
||||||
|
|
||||||
// Insert body section after frontmatter (after the closing ---\n and any
|
// Insert body section after frontmatter (after the closing ---\n and any
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
* - Freshness check (generated files match committed files)
|
* - Freshness check (generated files match committed files)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { validateSkill } from '../test/helpers/skill-parser';
|
import { validateSkill, validateSkillFrontmatter } from '../test/helpers/skill-parser';
|
||||||
import { discoverTemplates, discoverSkillFiles } from './discover-skills';
|
import { discoverTemplates, discoverSkillFiles } from './discover-skills';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
@ -35,6 +35,16 @@ let hasErrors = false;
|
||||||
console.log(' Skills:');
|
console.log(' Skills:');
|
||||||
for (const file of SKILL_FILES) {
|
for (const file of SKILL_FILES) {
|
||||||
const fullPath = path.join(ROOT, file);
|
const fullPath = path.join(ROOT, file);
|
||||||
|
const frontmatterErrors = validateSkillFrontmatter(fullPath);
|
||||||
|
if (frontmatterErrors.length > 0) {
|
||||||
|
hasErrors = true;
|
||||||
|
console.log(` ❌ ${file.padEnd(30)} — invalid frontmatter`);
|
||||||
|
for (const err of frontmatterErrors) {
|
||||||
|
console.log(` line ${err.line}: ${err.message}`);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const result = validateSkill(fullPath);
|
const result = validateSkill(fullPath);
|
||||||
|
|
||||||
if (result.warnings.length > 0) {
|
if (result.warnings.length > 0) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
#!/usr/bin/env bun
|
||||||
|
/**
|
||||||
|
* Validate YAML frontmatter for generated SKILL.md files.
|
||||||
|
*
|
||||||
|
* This is intentionally narrower than skill:check so CI can run it after
|
||||||
|
* regenerating only the host outputs relevant to a workflow.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { discoverSkillFiles } from './discover-skills';
|
||||||
|
import { validateSkillFrontmatter } from '../test/helpers/skill-parser';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
const ROOT = path.resolve(import.meta.dir, '..');
|
||||||
|
|
||||||
|
function walkSkillFiles(dir: string, acc: string[]): void {
|
||||||
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||||
|
if (entry.name === '.git' || entry.name === 'node_modules') continue;
|
||||||
|
const fullPath = path.join(dir, entry.name);
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
walkSkillFiles(fullPath, acc);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (entry.name === 'SKILL.md') {
|
||||||
|
acc.push(path.relative(ROOT, fullPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = new Set(discoverSkillFiles(ROOT));
|
||||||
|
for (const hostDir of ['.agents', '.factory']) {
|
||||||
|
const fullPath = path.join(ROOT, hostDir);
|
||||||
|
if (fs.existsSync(fullPath)) {
|
||||||
|
const hostFiles: string[] = [];
|
||||||
|
walkSkillFiles(fullPath, hostFiles);
|
||||||
|
for (const file of hostFiles) files.add(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const errors: string[] = [];
|
||||||
|
for (const file of [...files].sort()) {
|
||||||
|
const fullPath = path.join(ROOT, file);
|
||||||
|
for (const error of validateSkillFrontmatter(fullPath)) {
|
||||||
|
errors.push(`${file}:${error.line}: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors.length > 0) {
|
||||||
|
console.error('Invalid SKILL.md frontmatter:');
|
||||||
|
for (const error of errors) console.error(` ${error}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Validated frontmatter for ${files.size} SKILL.md files.`);
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: setup-browser-cookies
|
name: setup-browser-cookies
|
||||||
preamble-tier: 1
|
preamble-tier: 1
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Import cookies from your real Chromium browser into the headless browse session. (gstack)
|
description: "Import cookies from your real Chromium browser into the headless browse session. (gstack)"
|
||||||
triggers:
|
triggers:
|
||||||
- import browser cookies
|
- import browser cookies
|
||||||
- login to test site
|
- login to test site
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: setup-deploy
|
name: setup-deploy
|
||||||
preamble-tier: 2
|
preamble-tier: 2
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Configure deployment settings for /land-and-deploy.
|
description: "Configure deployment settings for /land-and-deploy."
|
||||||
triggers:
|
triggers:
|
||||||
- configure deploy
|
- configure deploy
|
||||||
- setup deployment
|
- setup deployment
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: setup-gbrain
|
name: setup-gbrain
|
||||||
preamble-tier: 2
|
preamble-tier: 2
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Set up gbrain for this coding agent: install the CLI, initialize a local PGLite or Supabase brain, register MCP, capture per-remote trust policy. (gstack)
|
description: "Set up gbrain for this coding agent: install the CLI, initialize a local PGLite or Supabase brain, register MCP, capture per-remote trust policy. (gstack)"
|
||||||
triggers:
|
triggers:
|
||||||
- setup gbrain
|
- setup gbrain
|
||||||
- install gbrain
|
- install gbrain
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: ship
|
name: ship
|
||||||
preamble-tier: 4
|
preamble-tier: 4
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Ship workflow: detect + merge base branch, run tests, review diff, bump VERSION, update CHANGELOG, commit, push, create PR. (gstack)
|
description: "Ship workflow: detect + merge base branch, run tests, review diff, bump VERSION, update CHANGELOG, commit, push, create PR. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
name: skillify
|
name: skillify
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Codify the most recent successful /scrape flow into a permanent browser-skill on disk. (gstack)
|
description: "Codify the most recent successful /scrape flow into a permanent browser-skill on disk. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
name: spec
|
name: spec
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
description: Turn vague intent into a precise, executable spec in five phases. (gstack)
|
description: "Turn vague intent into a precise, executable spec in five phases. (gstack)"
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- Bash
|
- Bash
|
||||||
- Read
|
- Read
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
name: sync-gbrain
|
name: sync-gbrain
|
||||||
preamble-tier: 2
|
preamble-tier: 2
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
description: Keep gbrain current with this repo's code and refresh agent search guidance in CLAUDE.md. Wraps the gstack-gbrain-sync orchestrator with state (gstack)
|
description: "Keep gbrain current with this repo's code and refresh agent search guidance in CLAUDE.md. Wraps the gstack-gbrain-sync orchestrator with state (gstack)"
|
||||||
triggers:
|
triggers:
|
||||||
- sync gbrain
|
- sync gbrain
|
||||||
- refresh gbrain
|
- refresh gbrain
|
||||||
|
|
|
||||||
|
|
@ -228,7 +228,7 @@ Original body content here.
|
||||||
expect(result).not.toBeNull();
|
expect(result).not.toBeNull();
|
||||||
const { content, parts } = result!;
|
const { content, parts } = result!;
|
||||||
// Frontmatter description is now ONE line ending with (gstack)
|
// Frontmatter description is now ONE line ending with (gstack)
|
||||||
expect(content).toMatch(/^description: Example skill:[^\n]*\(gstack\)\n/m);
|
expect(content).toMatch(/^description: "Example skill:[^\n]*\(gstack\)"\n/m);
|
||||||
// Body has the When to invoke section
|
// Body has the When to invoke section
|
||||||
expect(content).toContain('## When to invoke this skill');
|
expect(content).toContain('## When to invoke this skill');
|
||||||
expect(content).toContain('Use when asked to do an example task.');
|
expect(content).toContain('Use when asked to do an example task.');
|
||||||
|
|
@ -257,7 +257,7 @@ Original body content here.
|
||||||
expect(result).not.toBeNull();
|
expect(result).not.toBeNull();
|
||||||
expect(result!.content).not.toMatch(/\(gstack\)preamble-tier/);
|
expect(result!.content).not.toMatch(/\(gstack\)preamble-tier/);
|
||||||
expect(result!.content).not.toMatch(/\(gstack\)allowed-tools/);
|
expect(result!.content).not.toMatch(/\(gstack\)allowed-tools/);
|
||||||
expect(result!.content).toMatch(/\(gstack\)\n[a-z-]+:/);
|
expect(result!.content).toMatch(/\(gstack\)"\n[a-z-]+:/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('returns null on content without proper frontmatter', () => {
|
test('returns null on content without proper frontmatter', () => {
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,11 @@ export interface ValidationResult {
|
||||||
warnings: string[];
|
warnings: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FrontmatterError {
|
||||||
|
line: number;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract all $B invocations from bash code blocks in a SKILL.md file.
|
* Extract all $B invocations from bash code blocks in a SKILL.md file.
|
||||||
*/
|
*/
|
||||||
|
|
@ -138,6 +143,50 @@ export function validateSkill(skillPath: string): ValidationResult {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lightweight frontmatter validation for the YAML subset used by SKILL.md.
|
||||||
|
*
|
||||||
|
* This intentionally does not try to be a complete YAML parser. It catches the
|
||||||
|
* class of errors that make agents skip skills: missing delimiters and plain
|
||||||
|
* scalar values that contain ": " without being quoted or written as a block.
|
||||||
|
*/
|
||||||
|
export function validateSkillFrontmatter(skillPath: string): FrontmatterError[] {
|
||||||
|
const content = fs.readFileSync(skillPath, 'utf-8');
|
||||||
|
const errors: FrontmatterError[] = [];
|
||||||
|
|
||||||
|
if (!content.startsWith('---\n')) {
|
||||||
|
return [{ line: 1, message: 'missing opening frontmatter delimiter' }];
|
||||||
|
}
|
||||||
|
|
||||||
|
const fmEnd = content.indexOf('\n---', 4);
|
||||||
|
if (fmEnd === -1) {
|
||||||
|
return [{ line: 1, message: 'missing closing frontmatter delimiter' }];
|
||||||
|
}
|
||||||
|
|
||||||
|
const frontmatter = content.slice(4, fmEnd);
|
||||||
|
const lines = frontmatter.split('\n');
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const line = lines[i];
|
||||||
|
const match = line.match(/^\s*[A-Za-z0-9_-]+:\s+(.+?)\s*$/);
|
||||||
|
if (!match) continue;
|
||||||
|
|
||||||
|
const value = match[1].trim();
|
||||||
|
const isQuoted = value.startsWith('"') || value.startsWith("'");
|
||||||
|
const isBlockScalar = value === '|' || value === '>' || value.startsWith('|') || value.startsWith('>');
|
||||||
|
const isFlowValue = value.startsWith('[') || value.startsWith('{');
|
||||||
|
if (isQuoted || isBlockScalar || isFlowValue) continue;
|
||||||
|
|
||||||
|
if (/:\s/.test(value)) {
|
||||||
|
errors.push({
|
||||||
|
line: i + 2,
|
||||||
|
message: 'plain YAML scalar contains ": "; quote it or use a block scalar',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract all REMOTE_SLUG=$(...) assignment patterns from .md files in given subdirectories.
|
* Extract all REMOTE_SLUG=$(...) assignment patterns from .md files in given subdirectories.
|
||||||
* Returns a Map from filename → array of full assignment lines found.
|
* Returns a Map from filename → array of full assignment lines found.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, test, expect } from 'bun:test';
|
import { describe, test, expect } from 'bun:test';
|
||||||
import { validateSkill, extractRemoteSlugPatterns, extractWeightsFromTable } from './helpers/skill-parser';
|
import { validateSkill, validateSkillFrontmatter, extractRemoteSlugPatterns, extractWeightsFromTable } from './helpers/skill-parser';
|
||||||
import { ALL_COMMANDS, COMMAND_DESCRIPTIONS, READ_COMMANDS, WRITE_COMMANDS, META_COMMANDS } from '../browse/src/commands';
|
import { ALL_COMMANDS, COMMAND_DESCRIPTIONS, READ_COMMANDS, WRITE_COMMANDS, META_COMMANDS } from '../browse/src/commands';
|
||||||
import { SNAPSHOT_FLAGS } from '../browse/src/snapshot';
|
import { SNAPSHOT_FLAGS } from '../browse/src/snapshot';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
|
@ -8,6 +8,30 @@ import * as path from 'path';
|
||||||
const ROOT = path.resolve(import.meta.dir, '..');
|
const ROOT = path.resolve(import.meta.dir, '..');
|
||||||
|
|
||||||
describe('SKILL.md command validation', () => {
|
describe('SKILL.md command validation', () => {
|
||||||
|
test('all SKILL.md files have loadable YAML frontmatter', () => {
|
||||||
|
const skillFiles: string[] = [];
|
||||||
|
const walk = (dir: string) => {
|
||||||
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||||
|
if (entry.name === 'node_modules' || entry.name === '.git') continue;
|
||||||
|
const fullPath = path.join(dir, entry.name);
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
walk(fullPath);
|
||||||
|
} else if (entry.name === 'SKILL.md') {
|
||||||
|
skillFiles.push(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
walk(ROOT);
|
||||||
|
|
||||||
|
const errors = skillFiles.flatMap((skillPath) =>
|
||||||
|
validateSkillFrontmatter(skillPath).map((error) =>
|
||||||
|
`${path.relative(ROOT, skillPath)}:${error.line}: ${error.message}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(errors).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
test('all $B commands in SKILL.md are valid browse commands', () => {
|
test('all $B commands in SKILL.md are valid browse commands', () => {
|
||||||
const result = validateSkill(path.join(ROOT, 'SKILL.md'));
|
const result = validateSkill(path.join(ROOT, 'SKILL.md'));
|
||||||
expect(result.invalid).toHaveLength(0);
|
expect(result.invalid).toHaveLength(0);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
name: unfreeze
|
name: unfreeze
|
||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
description: Clear the freeze boundary set by /freeze, allowing edits to all directories again. (gstack)
|
description: "Clear the freeze boundary set by /freeze, allowing edits to all directories again. (gstack)"
|
||||||
triggers:
|
triggers:
|
||||||
- unfreeze edits
|
- unfreeze edits
|
||||||
- unlock all directories
|
- unlock all directories
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue