M CHANGELOG.md => CHANGELOG.md +23 -0
@@ 1,5 1,28 @@
# Changelog
+## [0.13.9.0] - 2026-03-29 — Composable Skills
+
+Skills can now load other skills inline. Write `{{INVOKE_SKILL:office-hours}}` in a template and the generator emits the right "read file, skip preamble, follow instructions" prose automatically. Handles host-aware paths and customizable skip lists.
+
+### Added
+
+- **`{{INVOKE_SKILL:skill-name}}` resolver.** Composable skill loading as a first-class resolver. Emits host-aware prose that tells Claude or Codex to read another skill's SKILL.md and follow it inline, skipping preamble sections. Supports optional `skip=` parameter for additional sections to skip.
+- **Parameterized resolver support.** The placeholder regex now handles `{{NAME:arg1:arg2}}`, enabling resolvers that take arguments at generation time. Fully backward compatible with existing `{{NAME}}` patterns.
+- **`{{CHANGELOG_WORKFLOW}}` resolver.** Changelog generation logic extracted from /ship into a reusable resolver. Includes voice guidance ("lead with what the user can now do") inline.
+- **Frontmatter `name:` for skill registration.** Setup script and gen-skill-docs now read `name:` from SKILL.md frontmatter for symlink naming. Enables directory names that differ from invocation names (e.g., `run-tests/` directory registered as `/test`).
+- **Proactive skill routing.** Skills now ask once to add routing rules to your project's CLAUDE.md. This makes Claude invoke the right skill automatically instead of answering directly. Your choice is remembered in `~/.gstack/config.yaml`.
+- **Annotated config file.** `~/.gstack/config.yaml` now gets a documented header on first creation explaining every setting. Edit it anytime.
+
+### Changed
+
+- **BENEFITS_FROM now delegates to INVOKE_SKILL.** Eliminated duplicated skip-list logic. The prerequisite offer wrapper stays in BENEFITS_FROM, but the actual "read and follow" instructions come from INVOKE_SKILL.
+- **/plan-ceo-review mid-session fallback uses INVOKE_SKILL.** The "user can't articulate the problem, offer /office-hours" path now uses the composable resolver instead of inline prose.
+- **Stronger routing language.** office-hours, investigate, and ship descriptions now say "Proactively invoke" instead of "Proactively suggest" for more reliable automatic skill invocation.
+
+### Fixed
+
+- **Config grep anchored to line start.** Commented header lines no longer shadow real config values.
+
## [0.13.8.0] - 2026-03-29 — Security Audit Round 2
Browse output is now wrapped in trust boundary markers so agents can tell page content from tool output. Markers are escape-proof. The Chrome extension validates message senders. CDP binds to localhost only. Bun installs use checksum verification.
M CLAUDE.md => CLAUDE.md +17 -0
@@ 258,6 258,23 @@ not what was already on main.
3. Does an existing entry on this branch already cover earlier work? (If yes, replace
it with one unified entry for the final version.)
+**Merging main does NOT mean adopting main's version.** When you merge origin/main into
+a feature branch, main may bring new CHANGELOG entries and a higher VERSION. Your branch
+still needs its OWN version bump on top. If main is at v0.13.8.0 and your branch adds
+features, bump to v0.13.9.0 with a new entry. Never jam your changes into an entry that
+already landed on main. Your entry goes on top because your branch lands next.
+
+**After merging main, always check:**
+- Does CHANGELOG have your branch's own entry separate from main's entries?
+- Is VERSION higher than main's VERSION?
+- Is your entry the topmost entry in CHANGELOG (above main's latest)?
+If any answer is no, fix it before continuing.
+
+**After any CHANGELOG edit that moves, adds, or removes entries,** immediately run
+`grep "^## \[" CHANGELOG.md` and verify the full version sequence is contiguous
+with no gaps or duplicates before committing. If a version is missing, the edit
+broke something. Fix it before moving on.
+
CHANGELOG.md is **for users**, not contributors. Write it like product release notes:
- Lead with what the user can now **do** that they couldn't before. Sell the feature.
M SKILL.md => SKILL.md +82 -22
@@ 68,6 68,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 149,6 157,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
**Tone:** direct, concrete, sharp, never corporate, never academic. Sound like a builder, not a consultant. Name the file, the function, the command. No filler, no throat-clearing.
@@ 271,28 322,37 @@ Then write a `## GSTACK REVIEW REPORT` section to the end of the plan file:
file you are allowed to edit in plan mode. The plan file review report is part of the
plan's living status.
-If `PROACTIVE` is `false`: do NOT proactively suggest other gstack skills during this session.
-Only run skills the user explicitly invokes. This preference persists across sessions via
-`gstack-config`.
-
-If `PROACTIVE` is `true` (default): suggest adjacent gstack skills when relevant to the
-user's workflow stage:
-- Brainstorming → /office-hours
-- Strategy → /plan-ceo-review
-- Architecture → /plan-eng-review
-- Design → /plan-design-review or /design-consultation
-- Auto-review → /autoplan
-- Debugging → /investigate
-- QA → /qa
-- Code review → /review
-- Visual audit → /design-review
-- Shipping → /ship
-- Docs → /document-release
-- Retro → /retro
-- Second opinion → /codex
-- Prod safety → /careful or /guard
-- Scoped edits → /freeze or /unfreeze
-- Upgrades → /gstack-upgrade
+If `PROACTIVE` is `false`: do NOT proactively invoke or suggest other gstack skills during
+this session. Only run skills the user explicitly invokes. This preference persists across
+sessions via `gstack-config`.
+
+If `PROACTIVE` is `true` (default): **invoke the Skill tool** when the user's request
+matches a skill's purpose. Do NOT answer directly when a skill exists for the task.
+Use the Skill tool to invoke it. The skill has specialized workflows, checklists, and
+quality gates that produce better results than answering inline.
+
+**Routing rules — when you see these patterns, INVOKE the skill via the Skill tool:**
+- User describes a new idea, asks "is this worth building", wants to brainstorm → invoke `/office-hours`
+- User asks about strategy, scope, ambition, "think bigger" → invoke `/plan-ceo-review`
+- User asks to review architecture, lock in the plan → invoke `/plan-eng-review`
+- User asks about design system, brand, visual identity → invoke `/design-consultation`
+- User asks to review design of a plan → invoke `/plan-design-review`
+- User wants all reviews done automatically → invoke `/autoplan`
+- User reports a bug, error, broken behavior, asks "why is this broken" → invoke `/investigate`
+- User asks to test the site, find bugs, QA → invoke `/qa`
+- User asks to review code, check the diff, pre-landing review → invoke `/review`
+- User asks about visual polish, design audit of a live site → invoke `/design-review`
+- User asks to ship, deploy, push, create a PR → invoke `/ship`
+- User asks to update docs after shipping → invoke `/document-release`
+- User asks for a weekly retro, what did we ship → invoke `/retro`
+- User asks for a second opinion, codex review → invoke `/codex`
+- User asks for safety mode, careful mode → invoke `/careful` or `/guard`
+- User asks to restrict edits to a directory → invoke `/freeze` or `/unfreeze`
+- User asks to upgrade gstack → invoke `/gstack-upgrade`
+
+**Do NOT answer the user's question directly when a matching skill exists.** The skill
+provides a structured, multi-step workflow that is always better than an ad-hoc answer.
+Invoke the skill first. If no skill matches, answer directly as usual.
If the user opts out of suggestions, run `gstack-config set proactive false`.
If they opt back in, run `gstack-config set proactive true`.
M SKILL.md.tmpl => SKILL.md.tmpl +31 -22
@@ 16,28 16,37 @@ allowed-tools:
{{PREAMBLE}}
-If `PROACTIVE` is `false`: do NOT proactively suggest other gstack skills during this session.
-Only run skills the user explicitly invokes. This preference persists across sessions via
-`gstack-config`.
-
-If `PROACTIVE` is `true` (default): suggest adjacent gstack skills when relevant to the
-user's workflow stage:
-- Brainstorming → /office-hours
-- Strategy → /plan-ceo-review
-- Architecture → /plan-eng-review
-- Design → /plan-design-review or /design-consultation
-- Auto-review → /autoplan
-- Debugging → /investigate
-- QA → /qa
-- Code review → /review
-- Visual audit → /design-review
-- Shipping → /ship
-- Docs → /document-release
-- Retro → /retro
-- Second opinion → /codex
-- Prod safety → /careful or /guard
-- Scoped edits → /freeze or /unfreeze
-- Upgrades → /gstack-upgrade
+If `PROACTIVE` is `false`: do NOT proactively invoke or suggest other gstack skills during
+this session. Only run skills the user explicitly invokes. This preference persists across
+sessions via `gstack-config`.
+
+If `PROACTIVE` is `true` (default): **invoke the Skill tool** when the user's request
+matches a skill's purpose. Do NOT answer directly when a skill exists for the task.
+Use the Skill tool to invoke it. The skill has specialized workflows, checklists, and
+quality gates that produce better results than answering inline.
+
+**Routing rules — when you see these patterns, INVOKE the skill via the Skill tool:**
+- User describes a new idea, asks "is this worth building", wants to brainstorm → invoke `/office-hours`
+- User asks about strategy, scope, ambition, "think bigger" → invoke `/plan-ceo-review`
+- User asks to review architecture, lock in the plan → invoke `/plan-eng-review`
+- User asks about design system, brand, visual identity → invoke `/design-consultation`
+- User asks to review design of a plan → invoke `/plan-design-review`
+- User wants all reviews done automatically → invoke `/autoplan`
+- User reports a bug, error, broken behavior, asks "why is this broken" → invoke `/investigate`
+- User asks to test the site, find bugs, QA → invoke `/qa`
+- User asks to review code, check the diff, pre-landing review → invoke `/review`
+- User asks about visual polish, design audit of a live site → invoke `/design-review`
+- User asks to ship, deploy, push, create a PR → invoke `/ship`
+- User asks to update docs after shipping → invoke `/document-release`
+- User asks for a weekly retro, what did we ship → invoke `/retro`
+- User asks for a second opinion, codex review → invoke `/codex`
+- User asks for safety mode, careful mode → invoke `/careful` or `/guard`
+- User asks to restrict edits to a directory → invoke `/freeze` or `/unfreeze`
+- User asks to upgrade gstack → invoke `/gstack-upgrade`
+
+**Do NOT answer the user's question directly when a matching skill exists.** The skill
+provides a structured, multi-step workflow that is always better than an ad-hoc answer.
+Invoke the skill first. If no skill matches, answer directly as usual.
If the user opts out of suggestions, run `gstack-config set proactive false`.
If they opt back in, run `gstack-config set proactive true`.
M VERSION => VERSION +1 -1
@@ 1,1 1,1 @@
-0.13.8.0
+0.13.9.0
M autoplan/SKILL.md => autoplan/SKILL.md +61 -5
@@ 77,6 77,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 158,6 166,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
@@ 426,10 477,11 @@ If they choose A:
Say: "Running /office-hours inline. Once the design doc is ready, I'll pick up
the review right where we left off."
-Read the office-hours skill file from disk using the Read tool:
-`~/.claude/skills/gstack/office-hours/SKILL.md`
+Read the `/office-hours` skill file at `~/.claude/skills/gstack/office-hours/SKILL.md` using the Read tool.
+
+**If unreadable:** Skip with "Could not load /office-hours — skipping." and continue.
-Follow it inline, **skipping these sections** (already handled by the parent skill):
+Follow its instructions from top to bottom, **skipping these sections** (already handled by the parent skill):
- Preamble (run first)
- AskUserQuestion Format
- Completeness Principle — Boil the Lake
@@ 437,9 489,13 @@ Follow it inline, **skipping these sections** (already handled by the parent ski
- Contributor Mode
- Completion Status Protocol
- Telemetry (run last)
+- Step 0: Detect platform and base branch
+- Review Readiness Dashboard
+- Plan File Review Report
+- Prerequisite Skill Offer
+- Plan Status Footer
-If the Read fails (file not found), say:
-"Could not load /office-hours — proceeding with standard review."
+Execute every other section at full depth. When the loaded skill's instructions are complete, continue with the next step below.
After /office-hours completes, re-run the design doc check:
```bash
M benchmark/SKILL.md => benchmark/SKILL.md +51 -0
@@ 70,6 70,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 151,6 159,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
**Tone:** direct, concrete, sharp, never corporate, never academic. Sound like a builder, not a consultant. Name the file, the function, the command. No filler, no throat-clearing.
M bin/gstack-config => bin/gstack-config +39 -3
@@ 13,6 13,38 @@ set -euo pipefail
STATE_DIR="${GSTACK_STATE_DIR:-$HOME/.gstack}"
CONFIG_FILE="$STATE_DIR/config.yaml"
+# Annotated header for new config files. Written once on first `set`.
+CONFIG_HEADER='# gstack configuration — edit freely, changes take effect on next skill run.
+# Docs: https://github.com/garrytan/gstack
+#
+# ─── Behavior ────────────────────────────────────────────────────────
+# proactive: true # Auto-invoke skills when your request matches one.
+# # Set to false to only run skills you type explicitly.
+#
+# routing_declined: false # Set to true to skip the CLAUDE.md routing injection
+# # prompt. Set back to false to be asked again.
+#
+# ─── Telemetry ───────────────────────────────────────────────────────
+# telemetry: anonymous # off | anonymous | community
+# # off — no data sent, no local analytics
+# # anonymous — counter only, no device ID
+# # community — usage data + stable device ID
+#
+# ─── Updates ─────────────────────────────────────────────────────────
+# auto_upgrade: false # true = silently upgrade on session start
+# update_check: true # false = suppress version check notifications
+#
+# ─── Skill naming ────────────────────────────────────────────────────
+# skill_prefix: false # true = namespace skills as /gstack-qa, /gstack-ship
+# # false = short names /qa, /ship
+#
+# ─── Advanced ────────────────────────────────────────────────────────
+# codex_reviews: enabled # disabled = skip Codex adversarial reviews in /ship
+# gstack_contributor: false # true = file field reports when gstack misbehaves
+# skip_eng_review: false # true = skip eng review gate in /ship (not recommended)
+#
+'
+
case "${1:-}" in
get)
KEY="${2:?Usage: gstack-config get <key>}"
@@ 21,7 53,7 @@ case "${1:-}" in
echo "Error: key must contain only alphanumeric characters and underscores" >&2
exit 1
fi
- grep -F "${KEY}:" "$CONFIG_FILE" 2>/dev/null | tail -1 | awk '{print $2}' | tr -d '[:space:]' || true
+ grep -E "^${KEY}:" "$CONFIG_FILE" 2>/dev/null | tail -1 | awk '{print $2}' | tr -d '[:space:]' || true
;;
set)
KEY="${2:?Usage: gstack-config set <key> <value>}"
@@ 32,12 64,16 @@ case "${1:-}" in
exit 1
fi
mkdir -p "$STATE_DIR"
+ # Write annotated header on first creation
+ if [ ! -f "$CONFIG_FILE" ]; then
+ printf '%s' "$CONFIG_HEADER" > "$CONFIG_FILE"
+ fi
# Escape sed special chars in value and drop embedded newlines
ESC_VALUE="$(printf '%s' "$VALUE" | head -1 | sed 's/[&/\]/\\&/g')"
- if grep -qF "${KEY}:" "$CONFIG_FILE" 2>/dev/null; then
+ if grep -qE "^${KEY}:" "$CONFIG_FILE" 2>/dev/null; then
# Portable in-place edit (BSD sed uses -i '', GNU sed uses -i without arg)
_tmpfile="$(mktemp "${CONFIG_FILE}.XXXXXX")"
- sed "s/^${KEY}:.*/${KEY}: ${ESC_VALUE}/" "$CONFIG_FILE" > "$_tmpfile" && mv "$_tmpfile" "$CONFIG_FILE"
+ sed "/^${KEY}:/s/.*/${KEY}: ${ESC_VALUE}/" "$CONFIG_FILE" > "$_tmpfile" && mv "$_tmpfile" "$CONFIG_FILE"
else
echo "${KEY}: ${VALUE}" >> "$CONFIG_FILE"
fi
M browse/SKILL.md => browse/SKILL.md +51 -0
@@ 70,6 70,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 151,6 159,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
**Tone:** direct, concrete, sharp, never corporate, never academic. Sound like a builder, not a consultant. Name the file, the function, the command. No filler, no throat-clearing.
M browse/test/gstack-config.test.ts => browse/test/gstack-config.test.ts +58 -0
@@ 135,4 135,62 @@ describe('gstack-config', () => {
const { stdout } = run(['get', 'test_special']);
expect(stdout).toBe('a/b&c\\d');
});
+
+ // ─── annotated header ──────────────────────────────────────
+ test('first set writes annotated header with docs', () => {
+ run(['set', 'telemetry', 'off']);
+ const content = readFileSync(join(stateDir, 'config.yaml'), 'utf-8');
+ expect(content).toContain('# gstack configuration');
+ expect(content).toContain('edit freely');
+ expect(content).toContain('proactive:');
+ expect(content).toContain('telemetry:');
+ expect(content).toContain('auto_upgrade:');
+ expect(content).toContain('skill_prefix:');
+ expect(content).toContain('routing_declined:');
+ expect(content).toContain('codex_reviews:');
+ expect(content).toContain('skip_eng_review:');
+ });
+
+ test('header written only once, not duplicated on second set', () => {
+ run(['set', 'foo', 'bar']);
+ run(['set', 'baz', 'qux']);
+ const content = readFileSync(join(stateDir, 'config.yaml'), 'utf-8');
+ const headerCount = (content.match(/# gstack configuration/g) || []).length;
+ expect(headerCount).toBe(1);
+ });
+
+ test('header does not break get on commented-out keys', () => {
+ run(['set', 'telemetry', 'community']);
+ // Header contains "# telemetry: anonymous" as a comment example.
+ // get should return the real value, not the comment.
+ const { stdout } = run(['get', 'telemetry']);
+ expect(stdout).toBe('community');
+ });
+
+ test('existing config file is not overwritten with header', () => {
+ writeFileSync(join(stateDir, 'config.yaml'), 'existing: value\n');
+ run(['set', 'new_key', 'new_value']);
+ const content = readFileSync(join(stateDir, 'config.yaml'), 'utf-8');
+ expect(content).toContain('existing: value');
+ expect(content).not.toContain('# gstack configuration');
+ });
+
+ // ─── routing_declined ──────────────────────────────────────
+ test('routing_declined defaults to empty (not set)', () => {
+ const { stdout } = run(['get', 'routing_declined']);
+ expect(stdout).toBe('');
+ });
+
+ test('routing_declined can be set and read', () => {
+ run(['set', 'routing_declined', 'true']);
+ const { stdout } = run(['get', 'routing_declined']);
+ expect(stdout).toBe('true');
+ });
+
+ test('routing_declined can be reset to false', () => {
+ run(['set', 'routing_declined', 'true']);
+ run(['set', 'routing_declined', 'false']);
+ const { stdout } = run(['get', 'routing_declined']);
+ expect(stdout).toBe('false');
+ });
});
M canary/SKILL.md => canary/SKILL.md +51 -0
@@ 70,6 70,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 151,6 159,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
M codex/SKILL.md => codex/SKILL.md +51 -0
@@ 71,6 71,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 152,6 160,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
M connect-chrome/SKILL.md => connect-chrome/SKILL.md +51 -0
@@ 68,6 68,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 149,6 157,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
M cso/SKILL.md => cso/SKILL.md +51 -0
@@ 74,6 74,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 155,6 163,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
M design-consultation/SKILL.md => design-consultation/SKILL.md +51 -0
@@ 75,6 75,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 156,6 164,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
M design-review/SKILL.md => design-review/SKILL.md +51 -0
@@ 75,6 75,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 156,6 164,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
M design-shotgun/SKILL.md => design-shotgun/SKILL.md +51 -0
@@ 72,6 72,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 153,6 161,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
M document-release/SKILL.md => document-release/SKILL.md +51 -0
@@ 72,6 72,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 153,6 161,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
M investigate/SKILL.md => investigate/SKILL.md +54 -2
@@ 7,8 7,9 @@ description: |
analyze, hypothesize, implement. Iron Law: no fixes without root cause.
Use when asked to "debug this", "fix this bug", "why is this broken",
"investigate this error", or "root cause analysis".
- Proactively suggest when the user reports errors, unexpected behavior, or
- is troubleshooting why something stopped working. (gstack)
+ Proactively invoke this skill (do NOT debug directly) when the user reports
+ errors, 500 errors, stack traces, unexpected behavior, "it was working
+ yesterday", or is troubleshooting why something stopped working. (gstack)
allowed-tools:
- Bash
- Read
@@ 86,6 87,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 167,6 176,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
M investigate/SKILL.md.tmpl => investigate/SKILL.md.tmpl +3 -2
@@ 7,8 7,9 @@ description: |
analyze, hypothesize, implement. Iron Law: no fixes without root cause.
Use when asked to "debug this", "fix this bug", "why is this broken",
"investigate this error", or "root cause analysis".
- Proactively suggest when the user reports errors, unexpected behavior, or
- is troubleshooting why something stopped working. (gstack)
+ Proactively invoke this skill (do NOT debug directly) when the user reports
+ errors, 500 errors, stack traces, unexpected behavior, "it was working
+ yesterday", or is troubleshooting why something stopped working. (gstack)
allowed-tools:
- Bash
- Read
M land-and-deploy/SKILL.md => land-and-deploy/SKILL.md +51 -0
@@ 69,6 69,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 150,6 158,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
M learn/SKILL.md => learn/SKILL.md +51 -0
@@ 72,6 72,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 153,6 161,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
M office-hours/SKILL.md => office-hours/SKILL.md +55 -2
@@ 9,8 9,10 @@ description: |
hackathons, learning, and open source. Saves a design doc.
Use when asked to "brainstorm this", "I have an idea", "help me think through
this", "office hours", or "is this worth building".
- Proactively suggest when the user describes a new product idea or is exploring
- whether something is worth building — before any code is written.
+ Proactively invoke this skill (do NOT answer directly) when the user describes
+ a new product idea, asks whether something is worth building, wants to think
+ through design decisions for something that doesn't exist yet, or is exploring
+ a concept before any code is written.
Use before /plan-ceo-review or /plan-eng-review. (gstack)
allowed-tools:
- Bash
@@ 77,6 79,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 158,6 168,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
M office-hours/SKILL.md.tmpl => office-hours/SKILL.md.tmpl +4 -2
@@ 9,8 9,10 @@ description: |
hackathons, learning, and open source. Saves a design doc.
Use when asked to "brainstorm this", "I have an idea", "help me think through
this", "office hours", or "is this worth building".
- Proactively suggest when the user describes a new product idea or is exploring
- whether something is worth building — before any code is written.
+ Proactively invoke this skill (do NOT answer directly) when the user describes
+ a new product idea, asks whether something is worth building, wants to think
+ through design decisions for something that doesn't exist yet, or is exploring
+ a concept before any code is written.
Use before /plan-ceo-review or /plan-eng-review. (gstack)
allowed-tools:
- Bash
M package.json => package.json +1 -1
@@ 1,6 1,6 @@
{
"name": "gstack",
- "version": "0.13.8.0",
+ "version": "0.13.9.0",
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
"license": "MIT",
"type": "module",
M plan-ceo-review/SKILL.md => plan-ceo-review/SKILL.md +81 -10
@@ 75,6 75,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 156,6 164,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
@@ 529,10 580,11 @@ If they choose A:
Say: "Running /office-hours inline. Once the design doc is ready, I'll pick up
the review right where we left off."
-Read the office-hours skill file from disk using the Read tool:
-`~/.claude/skills/gstack/office-hours/SKILL.md`
+Read the `/office-hours` skill file at `~/.claude/skills/gstack/office-hours/SKILL.md` using the Read tool.
+
+**If unreadable:** Skip with "Could not load /office-hours — skipping." and continue.
-Follow it inline, **skipping these sections** (already handled by the parent skill):
+Follow its instructions from top to bottom, **skipping these sections** (already handled by the parent skill):
- Preamble (run first)
- AskUserQuestion Format
- Completeness Principle — Boil the Lake
@@ 540,9 592,13 @@ Follow it inline, **skipping these sections** (already handled by the parent ski
- Contributor Mode
- Completion Status Protocol
- Telemetry (run last)
+- Step 0: Detect platform and base branch
+- Review Readiness Dashboard
+- Plan File Review Report
+- Prerequisite Skill Offer
+- Plan Status Footer
-If the Read fails (file not found), say:
-"Could not load /office-hours — proceeding with standard review."
+Execute every other section at full depth. When the loaded skill's instructions are complete, continue with the next step below.
After /office-hours completes, re-run the design doc check:
```bash
@@ 568,12 624,27 @@ sure," or is clearly exploring rather than reviewing — offer `/office-hours`:
Options: A) Yes, run /office-hours now. B) No, keep going.
If they keep going, proceed normally — no guilt, no re-asking.
-If they choose A: Read the office-hours skill file from disk:
-`~/.claude/skills/gstack/office-hours/SKILL.md`
+If they choose A:
+
+Read the `/office-hours` skill file at `~/.claude/skills/gstack/office-hours/SKILL.md` using the Read tool.
+
+**If unreadable:** Skip with "Could not load /office-hours — skipping." and continue.
+
+Follow its instructions from top to bottom, **skipping these sections** (already handled by the parent skill):
+- Preamble (run first)
+- AskUserQuestion Format
+- Completeness Principle — Boil the Lake
+- Search Before Building
+- Contributor Mode
+- Completion Status Protocol
+- Telemetry (run last)
+- Step 0: Detect platform and base branch
+- Review Readiness Dashboard
+- Plan File Review Report
+- Prerequisite Skill Offer
+- Plan Status Footer
-Follow it inline, skipping these sections (already handled by parent skill):
-Preamble, AskUserQuestion Format, Completeness Principle, Search Before Building,
-Contributor Mode, Completion Status Protocol, Telemetry.
+Execute every other section at full depth. When the loaded skill's instructions are complete, continue with the next step below.
Note current Step 0A progress so you don't re-ask questions already answered.
After completion, re-run the design doc check and resume the review.
M plan-ceo-review/SKILL.md.tmpl => plan-ceo-review/SKILL.md.tmpl +2 -5
@@ 143,12 143,9 @@ sure," or is clearly exploring rather than reviewing — offer `/office-hours`:
Options: A) Yes, run /office-hours now. B) No, keep going.
If they keep going, proceed normally — no guilt, no re-asking.
-If they choose A: Read the office-hours skill file from disk:
-`~/.claude/skills/gstack/office-hours/SKILL.md`
+If they choose A:
-Follow it inline, skipping these sections (already handled by parent skill):
-Preamble, AskUserQuestion Format, Completeness Principle, Search Before Building,
-Contributor Mode, Completion Status Protocol, Telemetry.
+{{INVOKE_SKILL:office-hours}}
Note current Step 0A progress so you don't re-ask questions already answered.
After completion, re-run the design doc check and resume the review.
M plan-design-review/SKILL.md => plan-design-review/SKILL.md +51 -0
@@ 73,6 73,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 154,6 162,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
M plan-eng-review/SKILL.md => plan-eng-review/SKILL.md +61 -5
@@ 74,6 74,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 155,6 163,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
@@ 439,10 490,11 @@ If they choose A:
Say: "Running /office-hours inline. Once the design doc is ready, I'll pick up
the review right where we left off."
-Read the office-hours skill file from disk using the Read tool:
-`~/.claude/skills/gstack/office-hours/SKILL.md`
+Read the `/office-hours` skill file at `~/.claude/skills/gstack/office-hours/SKILL.md` using the Read tool.
+
+**If unreadable:** Skip with "Could not load /office-hours — skipping." and continue.
-Follow it inline, **skipping these sections** (already handled by the parent skill):
+Follow its instructions from top to bottom, **skipping these sections** (already handled by the parent skill):
- Preamble (run first)
- AskUserQuestion Format
- Completeness Principle — Boil the Lake
@@ 450,9 502,13 @@ Follow it inline, **skipping these sections** (already handled by the parent ski
- Contributor Mode
- Completion Status Protocol
- Telemetry (run last)
+- Step 0: Detect platform and base branch
+- Review Readiness Dashboard
+- Plan File Review Report
+- Prerequisite Skill Offer
+- Plan Status Footer
-If the Read fails (file not found), say:
-"Could not load /office-hours — proceeding with standard review."
+Execute every other section at full depth. When the loaded skill's instructions are complete, continue with the next step below.
After /office-hours completes, re-run the design doc check:
```bash
M qa-only/SKILL.md => qa-only/SKILL.md +51 -0
@@ 70,6 70,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 151,6 159,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
M qa/SKILL.md => qa/SKILL.md +51 -0
@@ 76,6 76,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 157,6 165,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
M retro/SKILL.md => retro/SKILL.md +51 -0
@@ 70,6 70,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 151,6 159,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
M review/SKILL.md => review/SKILL.md +51 -0
@@ 73,6 73,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 154,6 162,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
M scripts/gen-skill-docs.ts => scripts/gen-skill-docs.ts +23 -12
@@ 83,11 83,15 @@ const OPENAI_LITMUS_CHECKS = [
// ─── External Host Helpers ───────────────────────────────────
// Re-export local copy for use in this file (matches codex-helpers.ts)
-function externalSkillName(skillDir: string): string {
+// Accepts optional frontmatter name to support directory/invocation name divergence
+function externalSkillName(skillDir: string, frontmatterName?: string): string {
+ // Root skill (skillDir === '' or '.') always maps to 'gstack' regardless of frontmatter
if (skillDir === '.' || skillDir === '') return 'gstack';
+ // Use frontmatter name when it differs from directory name (e.g., run-tests/ with name: test)
+ const baseName = frontmatterName && frontmatterName !== skillDir ? frontmatterName : skillDir;
// Don't double-prefix: gstack-upgrade → gstack-upgrade (not gstack-gstack-upgrade)
- if (skillDir.startsWith('gstack-')) return skillDir;
- return `gstack-${skillDir}`;
+ if (baseName.startsWith('gstack-')) return baseName;
+ return `gstack-${baseName}`;
}
function extractNameAndDescription(content: string): { name: string; description: string } {
@@ 255,11 259,12 @@ function processExternalHost(
skillDir: string,
extractedDescription: string,
ctx: TemplateContext,
+ frontmatterName?: string,
): { content: string; outputPath: string; outputDir: string; symlinkLoop: boolean } {
const config = EXTERNAL_HOST_CONFIG[host];
if (!config) throw new Error(`No external host config for: ${host}`);
- const name = externalSkillName(skillDir === '.' ? '' : skillDir);
+ const name = externalSkillName(skillDir === '.' ? '' : skillDir, frontmatterName);
const outputDir = path.join(ROOT, config.hostSubdir, 'skills', name);
fs.mkdirSync(outputDir, { recursive: true });
const outputPath = path.join(outputDir, 'SKILL.md');
@@ 324,10 329,13 @@ function processTemplate(tmplPath: string, host: Host = 'claude'): { outputPath:
// Determine skill directory relative to ROOT
const skillDir = path.relative(ROOT, path.dirname(tmplPath));
- // Extract skill name from frontmatter for TemplateContext
+ // Extract skill name from frontmatter early — needed for both TemplateContext and external host output paths.
+ // When frontmatter name: differs from directory name (e.g., run-tests/ with name: test),
+ // the frontmatter name is used for external skill naming and setup script symlinks.
const { name: extractedName, description: extractedDescription } = extractNameAndDescription(tmplContent);
const skillName = extractedName || path.basename(path.dirname(tmplPath));
+
// Extract benefits-from list from frontmatter (inline YAML: benefits-from: [a, b])
const benefitsMatch = tmplContent.match(/^benefits-from:\s*\[([^\]]*)\]/m);
const benefitsFrom = benefitsMatch
@@ 340,15 348,18 @@ function processTemplate(tmplPath: string, host: Host = 'claude'): { outputPath:
const ctx: TemplateContext = { skillName, tmplPath, benefitsFrom, host, paths: HOST_PATHS[host], preambleTier };
- // Replace placeholders
- let content = tmplContent.replace(/\{\{(\w+)\}\}/g, (match, name) => {
- const resolver = RESOLVERS[name];
- if (!resolver) throw new Error(`Unknown placeholder {{${name}}} in ${relTmplPath}`);
- return resolver(ctx);
+ // Replace placeholders (supports parameterized: {{NAME:arg1:arg2}})
+ let content = tmplContent.replace(/\{\{(\w+(?::[^}]+)?)\}\}/g, (match, fullKey) => {
+ const parts = fullKey.split(':');
+ const resolverName = parts[0];
+ const args = parts.slice(1);
+ const resolver = RESOLVERS[resolverName];
+ if (!resolver) throw new Error(`Unknown placeholder {{${resolverName}}} in ${relTmplPath}`);
+ return args.length > 0 ? resolver(ctx, args) : resolver(ctx);
});
// Check for any remaining unresolved placeholders
- const remaining = content.match(/\{\{(\w+)\}\}/g);
+ const remaining = content.match(/\{\{(\w+(?::[^}]+)?)\}\}/g);
if (remaining) {
throw new Error(`Unresolved placeholders in ${relTmplPath}: ${remaining.join(', ')}`);
}
@@ 359,7 370,7 @@ function processTemplate(tmplPath: string, host: Host = 'claude'): { outputPath:
if (host === 'claude') {
content = transformFrontmatter(content, host);
} else {
- const result = processExternalHost(content, tmplContent, host, skillDir, extractedDescription, ctx);
+ const result = processExternalHost(content, tmplContent, host, skillDir, extractedDescription, ctx, extractedName || undefined);
content = result.content;
outputPath = result.outputPath;
symlinkLoop = result.symlinkLoop;
A scripts/resolvers/composition.ts => scripts/resolvers/composition.ts +48 -0
@@ 0,0 1,48 @@
+import type { TemplateContext } from './types';
+
+/**
+ * {{INVOKE_SKILL:skill-name}} — emits prose instructing Claude to read
+ * another skill's SKILL.md and follow it, skipping preamble sections.
+ *
+ * Supports optional skip= parameter for additional sections to skip:
+ * {{INVOKE_SKILL:plan-ceo-review:skip=Outside Voice,Design Outside Voices}}
+ */
+export function generateInvokeSkill(ctx: TemplateContext, args?: string[]): string {
+ const skillName = args?.[0];
+ if (!skillName || skillName === '') {
+ throw new Error('{{INVOKE_SKILL}} requires a skill name, e.g. {{INVOKE_SKILL:plan-ceo-review}}');
+ }
+
+ // Parse optional skip= parameter from args[1+]
+ const extraSkips = (args?.slice(1) || [])
+ .filter(a => a.startsWith('skip='))
+ .flatMap(a => a.slice(5).split(','))
+ .map(s => s.trim())
+ .filter(Boolean);
+
+ const DEFAULT_SKIPS = [
+ 'Preamble (run first)',
+ 'AskUserQuestion Format',
+ 'Completeness Principle — Boil the Lake',
+ 'Search Before Building',
+ 'Contributor Mode',
+ 'Completion Status Protocol',
+ 'Telemetry (run last)',
+ 'Step 0: Detect platform and base branch',
+ 'Review Readiness Dashboard',
+ 'Plan File Review Report',
+ 'Prerequisite Skill Offer',
+ 'Plan Status Footer',
+ ];
+
+ const allSkips = [...DEFAULT_SKIPS, ...extraSkips];
+
+ return `Read the \`/${skillName}\` skill file at \`${ctx.paths.skillRoot}/${skillName}/SKILL.md\` using the Read tool.
+
+**If unreadable:** Skip with "Could not load /${skillName} — skipping." and continue.
+
+Follow its instructions from top to bottom, **skipping these sections** (already handled by the parent skill):
+${allSkips.map(s => `- ${s}`).join('\n')}
+
+Execute every other section at full depth. When the loaded skill's instructions are complete, continue with the next step below.`;
+}
M scripts/resolvers/index.ts => scripts/resolvers/index.ts +6 -3
@@ 3,7 3,7 @@
* Each resolver takes a TemplateContext and returns the replacement string.
*/
-import type { TemplateContext } from './types';
+import type { TemplateContext, ResolverFn } from './types';
// Domain modules
import { generatePreamble } from './preamble';
@@ 12,11 12,12 @@ import { generateCommandReference, generateSnapshotFlags, generateBrowseSetup }
import { generateDesignMethodology, generateDesignHardRules, generateDesignOutsideVoices, generateDesignReviewLite, generateDesignSketch, generateDesignSetup, generateDesignMockup, generateDesignShotgunLoop } from './design';
import { generateTestBootstrap, generateTestCoverageAuditPlan, generateTestCoverageAuditShip, generateTestCoverageAuditReview } from './testing';
import { generateReviewDashboard, generatePlanFileReviewReport, generateSpecReviewLoop, generateBenefitsFrom, generateCodexSecondOpinion, generateAdversarialStep, generateCodexPlanReview, generatePlanCompletionAuditShip, generatePlanCompletionAuditReview, generatePlanVerificationExec } from './review';
-import { generateSlugEval, generateSlugSetup, generateBaseBranchDetect, generateDeployBootstrap, generateQAMethodology, generateCoAuthorTrailer } from './utility';
+import { generateSlugEval, generateSlugSetup, generateBaseBranchDetect, generateDeployBootstrap, generateQAMethodology, generateCoAuthorTrailer, generateChangelogWorkflow } from './utility';
import { generateLearningsSearch, generateLearningsLog } from './learnings';
import { generateConfidenceCalibration } from './confidence';
+import { generateInvokeSkill } from './composition';
-export const RESOLVERS: Record<string, (ctx: TemplateContext) => string> = {
+export const RESOLVERS: Record<string, ResolverFn> = {
SLUG_EVAL: generateSlugEval,
SLUG_SETUP: generateSlugSetup,
COMMAND_REFERENCE: generateCommandReference,
@@ 53,4 54,6 @@ export const RESOLVERS: Record<string, (ctx: TemplateContext) => string> = {
LEARNINGS_SEARCH: generateLearningsSearch,
LEARNINGS_LOG: generateLearningsLog,
CONFIDENCE_CALIBRATION: generateConfidenceCalibration,
+ INVOKE_SKILL: generateInvokeSkill,
+ CHANGELOG_WORKFLOW: generateChangelogWorkflow,
};
M scripts/resolvers/preamble.ts => scripts/resolvers/preamble.ts +54 -0
@@ 77,6 77,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(${ctx.paths.binDir}/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
\`\`\``;
}
@@ 167,6 175,51 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If \`PROACTIVE_PROMPTED\` is \`yes\`, skip this entirely.`;
}
+function generateRoutingInjection(ctx: TemplateContext): string {
+ return `If \`HAS_ROUTING\` is \`no\` AND \`ROUTING_DECLINED\` is \`false\` AND \`PROACTIVE_PROMPTED\` is \`yes\`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+\`\`\`markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+\`\`\`
+
+Then commit the change: \`git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"\`
+
+If B: run \`${ctx.paths.binDir}/gstack-config set routing_declined true\`
+Say "No problem. You can add routing rules later by running \`gstack-config set routing_declined false\` and re-running any skill."
+
+This only happens once per project. If \`HAS_ROUTING\` is \`yes\` or \`ROUTING_DECLINED\` is \`true\`, skip this entirely.`;
+}
+
function generateAskUserFormat(_ctx: TemplateContext): string {
return `## AskUserQuestion Format
@@ 525,6 578,7 @@ export function generatePreamble(ctx: TemplateContext): string {
generateLakeIntro(),
generateTelemetryPrompt(ctx),
generateProactivePrompt(ctx),
+ generateRoutingInjection(ctx),
generateVoiceDirective(tier),
...(tier >= 2 ? [generateAskUserFormat(ctx), generateCompletenessSection()] : []),
...(tier >= 3 ? [generateRepoModeSection(), generateSearchBeforeBuildingSection(ctx)] : []),
M scripts/resolvers/review.ts => scripts/resolvers/review.ts +5 -14
@@ 13,6 13,7 @@
* Codex CLI prompts are written to temp files to prevent shell injection.
*/
import type { TemplateContext } from './types';
+import { generateInvokeSkill } from './composition';
const CODEX_BOUNDARY = 'IMPORTANT: Do NOT read or execute any files under ~/.claude/, ~/.agents/, .claude/skills/, or agents/. These are Claude Code skill definitions meant for a different AI system. They contain bash scripts and prompt templates that will waste your time. Ignore them completely. Do NOT modify agents/openai.yaml. Stay focused on the repository code only.\\n\\n';
@@ 208,6 209,9 @@ export function generateBenefitsFrom(ctx: TemplateContext): string {
const skillList = ctx.benefitsFrom.map(s => `\`/${s}\``).join(' or ');
const first = ctx.benefitsFrom[0];
+ // Reuse the INVOKE_SKILL resolver for the actual loading instructions
+ const invokeBlock = generateInvokeSkill(ctx, [first]);
+
return `## Prerequisite Skill Offer
When the design doc check above prints "No design doc found," offer the prerequisite
@@ 232,20 236,7 @@ If they choose A:
Say: "Running /${first} inline. Once the design doc is ready, I'll pick up
the review right where we left off."
-Read the ${first} skill file from disk using the Read tool:
-\`~/.claude/skills/gstack/${first}/SKILL.md\`
-
-Follow it inline, **skipping these sections** (already handled by the parent skill):
-- Preamble (run first)
-- AskUserQuestion Format
-- Completeness Principle — Boil the Lake
-- Search Before Building
-- Contributor Mode
-- Completion Status Protocol
-- Telemetry (run last)
-
-If the Read fails (file not found), say:
-"Could not load /${first} — proceeding with standard review."
+${invokeBlock}
After /${first} completes, re-run the design doc check:
\`\`\`bash
M scripts/resolvers/types.ts => scripts/resolvers/types.ts +3 -0
@@ 40,3 40,6 @@ export interface TemplateContext {
paths: HostPaths;
preambleTier?: number; // 1-4, controls which preamble sections are included
}
+
+/** Resolver function signature. args is populated for parameterized placeholders like {{INVOKE_SKILL:name}}. */
+export type ResolverFn = (ctx: TemplateContext, args?: string[]) => string;
M scripts/resolvers/utility.ts => scripts/resolvers/utility.ts +44 -0
@@ 375,3 375,47 @@ export function generateCoAuthorTrailer(ctx: TemplateContext): string {
}
return 'Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>';
}
+
+export function generateChangelogWorkflow(_ctx: TemplateContext): string {
+ return `## CHANGELOG (auto-generate)
+
+1. Read \`CHANGELOG.md\` header to know the format.
+
+2. **First, enumerate every commit on the branch:**
+ \`\`\`bash
+ git log <base>..HEAD --oneline
+ \`\`\`
+ Copy the full list. Count the commits. You will use this as a checklist.
+
+3. **Read the full diff** to understand what each commit actually changed:
+ \`\`\`bash
+ git diff <base>...HEAD
+ \`\`\`
+
+4. **Group commits by theme** before writing anything. Common themes:
+ - New features / capabilities
+ - Performance improvements
+ - Bug fixes
+ - Dead code removal / cleanup
+ - Infrastructure / tooling / tests
+ - Refactoring
+
+5. **Write the CHANGELOG entry** covering ALL groups:
+ - If existing CHANGELOG entries on the branch already cover some commits, replace them with one unified entry for the new version
+ - Categorize changes into applicable sections:
+ - \`### Added\` — new features
+ - \`### Changed\` — changes to existing functionality
+ - \`### Fixed\` — bug fixes
+ - \`### Removed\` — removed features
+ - Write concise, descriptive bullet points
+ - Insert after the file header (line 5), dated today
+ - Format: \`## [X.Y.Z.W] - YYYY-MM-DD\`
+ - **Voice:** Lead with what the user can now **do** that they couldn't before. Use plain language, not implementation details. Never mention TODOS.md, internal tracking, or contributor-facing details.
+
+6. **Cross-check:** Compare your CHANGELOG entry against the commit list from step 2.
+ Every commit must map to at least one bullet point. If any commit is unrepresented,
+ add it now. If the branch has N commits spanning K themes, the CHANGELOG must
+ reflect all K themes.
+
+**Do NOT ask the user to describe changes.** Infer from the diff and commit history.`;
+}
M setup => setup +6 -3
@@ 272,9 272,12 @@ link_claude_skill_dirs() {
local linked=()
for skill_dir in "$gstack_dir"/*/; do
if [ -f "$skill_dir/SKILL.md" ]; then
- skill_name="$(basename "$skill_dir")"
+ dir_name="$(basename "$skill_dir")"
# Skip node_modules
- [ "$skill_name" = "node_modules" ] && continue
+ [ "$dir_name" = "node_modules" ] && continue
+ # Use frontmatter name: if present (e.g., run-tests/ with name: test → symlink as "test")
+ skill_name=$(grep -m1 '^name:' "$skill_dir/SKILL.md" 2>/dev/null | sed 's/^name:[[:space:]]*//' | tr -d '[:space:]')
+ [ -z "$skill_name" ] && skill_name="$dir_name"
# Apply gstack- prefix unless --no-prefix or already prefixed
if [ "$SKILL_PREFIX" -eq 1 ]; then
case "$skill_name" in
@@ 287,7 290,7 @@ link_claude_skill_dirs() {
target="$skills_dir/$link_name"
# Create or update symlink; skip if a real file/directory exists
if [ -L "$target" ] || [ ! -e "$target" ]; then
- ln -snf "gstack/$skill_name" "$target"
+ ln -snf "gstack/$dir_name" "$target"
linked+=("$link_name")
fi
fi
M setup-browser-cookies/SKILL.md => setup-browser-cookies/SKILL.md +51 -0
@@ 67,6 67,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 148,6 156,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
**Tone:** direct, concrete, sharp, never corporate, never academic. Sound like a builder, not a consultant. Name the file, the function, the command. No filler, no throat-clearing.
M setup-deploy/SKILL.md => setup-deploy/SKILL.md +51 -0
@@ 73,6 73,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 154,6 162,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
M ship/SKILL.md => ship/SKILL.md +56 -3
@@ 5,8 5,9 @@ version: 1.0.0
description: |
Ship workflow: detect + merge base branch, run tests, review diff, bump VERSION,
update CHANGELOG, commit, push, create PR. Use when asked to "ship", "deploy",
- "push to main", "create a PR", or "merge and push".
- Proactively suggest when the user says code is ready or asks about deploying. (gstack)
+ "push to main", "create a PR", "merge and push", or "get it deployed".
+ Proactively invoke this skill (do NOT push/PR directly) when the user says code
+ is ready, asks about deploying, wants to push code up, or asks to create a PR. (gstack)
allowed-tools:
- Bash
- Read
@@ 73,6 74,14 @@ if [ -f "$_LEARN_FILE" ]; then
else
echo "LEARNINGS: 0"
fi
+# Check if CLAUDE.md has routing rules
+_HAS_ROUTING="no"
+if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
+ _HAS_ROUTING="yes"
+fi
+_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
+echo "HAS_ROUTING: $_HAS_ROUTING"
+echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
```
If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not
@@ 154,6 163,49 @@ touch ~/.gstack/.proactive-prompted
This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely.
+If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`:
+Check if a CLAUDE.md file exists in the project root. If it does not exist, create it.
+
+Use AskUserQuestion:
+
+> gstack works best when your project's CLAUDE.md includes skill routing rules.
+> This tells Claude to use specialized workflows (like /ship, /investigate, /qa)
+> instead of answering directly. It's a one-time addition, about 15 lines.
+
+Options:
+- A) Add routing rules to CLAUDE.md (recommended)
+- B) No thanks, I'll invoke skills manually
+
+If A: Append this section to the end of CLAUDE.md:
+
+```markdown
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+```
+
+Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
+
+If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true`
+Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill."
+
+This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely.
+
## Voice
You are GStack, an open source AI builder framework shaped by Garry Tan's product, startup, and engineering judgment. Encode how he thinks, not his biography.
@@ 1721,7 1773,7 @@ already knows. A good test: would this insight save time in a future session? If
---
-## Step 5: CHANGELOG (auto-generate)
+## CHANGELOG (auto-generate)
1. Read `CHANGELOG.md` header to know the format.
@@ 1754,6 1806,7 @@ already knows. A good test: would this insight save time in a future session? If
- Write concise, descriptive bullet points
- Insert after the file header (line 5), dated today
- Format: `## [X.Y.Z.W] - YYYY-MM-DD`
+ - **Voice:** Lead with what the user can now **do** that they couldn't before. Use plain language, not implementation details. Never mention TODOS.md, internal tracking, or contributor-facing details.
6. **Cross-check:** Compare your CHANGELOG entry against the commit list from step 2.
Every commit must map to at least one bullet point. If any commit is unrepresented,
M ship/SKILL.md.tmpl => ship/SKILL.md.tmpl +4 -42
@@ 5,8 5,9 @@ version: 1.0.0
description: |
Ship workflow: detect + merge base branch, run tests, review diff, bump VERSION,
update CHANGELOG, commit, push, create PR. Use when asked to "ship", "deploy",
- "push to main", "create a PR", or "merge and push".
- Proactively suggest when the user says code is ready or asks about deploying. (gstack)
+ "push to main", "create a PR", "merge and push", or "get it deployed".
+ Proactively invoke this skill (do NOT push/PR directly) when the user says code
+ is ready, asks about deploying, wants to push code up, or asks to create a PR. (gstack)
allowed-tools:
- Bash
- Read
@@ 345,46 346,7 @@ For each classified comment:
---
-## Step 5: CHANGELOG (auto-generate)
-
-1. Read `CHANGELOG.md` header to know the format.
-
-2. **First, enumerate every commit on the branch:**
- ```bash
- git log <base>..HEAD --oneline
- ```
- Copy the full list. Count the commits. You will use this as a checklist.
-
-3. **Read the full diff** to understand what each commit actually changed:
- ```bash
- git diff <base>...HEAD
- ```
-
-4. **Group commits by theme** before writing anything. Common themes:
- - New features / capabilities
- - Performance improvements
- - Bug fixes
- - Dead code removal / cleanup
- - Infrastructure / tooling / tests
- - Refactoring
-
-5. **Write the CHANGELOG entry** covering ALL groups:
- - If existing CHANGELOG entries on the branch already cover some commits, replace them with one unified entry for the new version
- - Categorize changes into applicable sections:
- - `### Added` — new features
- - `### Changed` — changes to existing functionality
- - `### Fixed` — bug fixes
- - `### Removed` — removed features
- - Write concise, descriptive bullet points
- - Insert after the file header (line 5), dated today
- - Format: `## [X.Y.Z.W] - YYYY-MM-DD`
-
-6. **Cross-check:** Compare your CHANGELOG entry against the commit list from step 2.
- Every commit must map to at least one bullet point. If any commit is unrepresented,
- add it now. If the branch has N commits spanning K themes, the CHANGELOG must
- reflect all K themes.
-
-**Do NOT ask the user to describe changes.** Infer from the diff and commit history.
+{{CHANGELOG_WORKFLOW}}
---
M test/gen-skill-docs.test.ts => test/gen-skill-docs.test.ts +135 -2
@@ 1153,6 1153,138 @@ describe('BENEFITS_FROM resolver', () => {
expect(ceoContent).toContain('office-hours/SKILL.md');
expect(engContent).toContain('office-hours/SKILL.md');
});
+
+ test('BENEFITS_FROM delegates to INVOKE_SKILL pattern', () => {
+ // Should contain the INVOKE_SKILL-style loading prose (not the old manual skip list)
+ expect(engContent).toContain('Follow its instructions from top to bottom');
+ expect(engContent).toContain('skipping these sections');
+ expect(ceoContent).toContain('Follow its instructions from top to bottom');
+ });
+});
+
+// --- {{INVOKE_SKILL}} resolver tests ---
+
+describe('INVOKE_SKILL resolver', () => {
+ const ceoContent = fs.readFileSync(path.join(ROOT, 'plan-ceo-review', 'SKILL.md'), 'utf-8');
+
+ test('plan-ceo-review uses INVOKE_SKILL for mid-session office-hours fallback', () => {
+ // The mid-session detection path should use INVOKE_SKILL-generated prose
+ expect(ceoContent).toContain('office-hours/SKILL.md');
+ expect(ceoContent).toContain('Follow its instructions from top to bottom');
+ });
+
+ test('INVOKE_SKILL output includes default skip list', () => {
+ expect(ceoContent).toContain('Preamble (run first)');
+ expect(ceoContent).toContain('Telemetry (run last)');
+ expect(ceoContent).toContain('AskUserQuestion Format');
+ });
+
+ test('INVOKE_SKILL output includes error handling', () => {
+ expect(ceoContent).toContain('If unreadable');
+ expect(ceoContent).toContain('Could not load');
+ });
+
+ test('template uses {{INVOKE_SKILL:office-hours}} placeholder', () => {
+ const tmpl = fs.readFileSync(path.join(ROOT, 'plan-ceo-review', 'SKILL.md.tmpl'), 'utf-8');
+ expect(tmpl).toContain('{{INVOKE_SKILL:office-hours}}');
+ });
+});
+
+// --- {{CHANGELOG_WORKFLOW}} resolver tests ---
+
+describe('CHANGELOG_WORKFLOW resolver', () => {
+ const shipContent = fs.readFileSync(path.join(ROOT, 'ship', 'SKILL.md'), 'utf-8');
+
+ test('ship SKILL.md contains changelog workflow', () => {
+ expect(shipContent).toContain('CHANGELOG (auto-generate)');
+ expect(shipContent).toContain('git log <base>..HEAD --oneline');
+ });
+
+ test('changelog workflow includes cross-check step', () => {
+ expect(shipContent).toContain('Cross-check');
+ expect(shipContent).toContain('Every commit must map to at least one bullet point');
+ });
+
+ test('changelog workflow includes voice guidance', () => {
+ expect(shipContent).toContain('Lead with what the user can now **do**');
+ });
+
+ test('template uses {{CHANGELOG_WORKFLOW}} placeholder', () => {
+ const tmpl = fs.readFileSync(path.join(ROOT, 'ship', 'SKILL.md.tmpl'), 'utf-8');
+ expect(tmpl).toContain('{{CHANGELOG_WORKFLOW}}');
+ // Should NOT contain the old inline changelog content
+ expect(tmpl).not.toContain('Group commits by theme');
+ });
+
+ test('changelog workflow includes keep-changelog format', () => {
+ expect(shipContent).toContain('### Added');
+ expect(shipContent).toContain('### Fixed');
+ });
+});
+
+// --- Parameterized resolver infrastructure tests ---
+
+describe('parameterized resolver support', () => {
+ test('gen-skill-docs regex handles colon-separated args', () => {
+ // Verify the template containing {{INVOKE_SKILL:office-hours}} was processed
+ // without leaving unresolved placeholders
+ const ceoContent = fs.readFileSync(path.join(ROOT, 'plan-ceo-review', 'SKILL.md'), 'utf-8');
+ expect(ceoContent).not.toMatch(/\{\{INVOKE_SKILL:[^}]+\}\}/);
+ });
+
+ test('templates with parameterized resolvers pass unresolved check', () => {
+ // All generated SKILL.md files should have no unresolved {{...}} placeholders
+ const skillDirs = fs.readdirSync(ROOT).filter(d =>
+ fs.existsSync(path.join(ROOT, d, 'SKILL.md'))
+ );
+ for (const dir of skillDirs) {
+ const content = fs.readFileSync(path.join(ROOT, dir, 'SKILL.md'), 'utf-8');
+ const unresolved = content.match(/\{\{[A-Z_]+(?::[^}]*)?\}\}/g);
+ if (unresolved) {
+ throw new Error(`${dir}/SKILL.md has unresolved placeholders: ${unresolved.join(', ')}`);
+ }
+ }
+ });
+});
+
+// --- Preamble routing injection tests ---
+
+describe('preamble routing injection', () => {
+ const shipContent = fs.readFileSync(path.join(ROOT, 'ship', 'SKILL.md'), 'utf-8');
+
+ test('preamble bash checks for routing section in CLAUDE.md', () => {
+ expect(shipContent).toContain('grep -q "## Skill routing" CLAUDE.md');
+ expect(shipContent).toContain('HAS_ROUTING');
+ });
+
+ test('preamble bash reads routing_declined config', () => {
+ expect(shipContent).toContain('routing_declined');
+ expect(shipContent).toContain('ROUTING_DECLINED');
+ });
+
+ test('preamble includes routing injection AskUserQuestion', () => {
+ expect(shipContent).toContain('Add routing rules to CLAUDE.md');
+ expect(shipContent).toContain("I'll invoke skills manually");
+ });
+
+ test('routing injection respects prior decline', () => {
+ expect(shipContent).toContain('ROUTING_DECLINED');
+ expect(shipContent).toMatch(/routing_declined.*true/);
+ });
+
+ test('routing injection only fires when all conditions met', () => {
+ // Must be: HAS_ROUTING=no AND ROUTING_DECLINED=false AND PROACTIVE_PROMPTED=yes
+ expect(shipContent).toContain('HAS_ROUTING');
+ expect(shipContent).toContain('ROUTING_DECLINED');
+ expect(shipContent).toContain('PROACTIVE_PROMPTED');
+ });
+
+ test('routing section content includes key routing rules', () => {
+ expect(shipContent).toContain('invoke office-hours');
+ expect(shipContent).toContain('invoke investigate');
+ expect(shipContent).toContain('invoke ship');
+ expect(shipContent).toContain('invoke qa');
+ });
});
// --- {{DESIGN_OUTSIDE_VOICES}} resolver tests ---
@@ 1793,11 1925,12 @@ describe('setup script validation', () => {
});
test('link_claude_skill_dirs creates relative symlinks', () => {
- // Claude links should be relative: ln -snf "gstack/skill_name"
+ // Claude links should be relative: ln -snf "gstack/$dir_name"
+ // Uses dir_name (not skill_name) because symlink target must point to the physical directory
const fnStart = setupContent.indexOf('link_claude_skill_dirs()');
const fnEnd = setupContent.indexOf('}', setupContent.indexOf('linked[@]}', fnStart));
const fnBody = setupContent.slice(fnStart, fnEnd);
- expect(fnBody).toContain('ln -snf "gstack/$skill_name"');
+ expect(fnBody).toContain('ln -snf "gstack/$dir_name"');
});
test('setup supports --host auto|claude|codex|kiro', () => {
M test/skill-routing-e2e.test.ts => test/skill-routing-e2e.test.ts +24 -5
@@ 93,11 93,30 @@ function installSkills(tmpDir: string) {
}
}
- // Copy CLAUDE.md so Claude has project context for skill routing.
- const claudeMdSrc = path.join(ROOT, 'CLAUDE.md');
- if (fs.existsSync(claudeMdSrc)) {
- fs.copyFileSync(claudeMdSrc, path.join(tmpDir, 'CLAUDE.md'));
- }
+ // Write a CLAUDE.md with explicit routing instructions.
+ // The skill descriptions in system-reminder aren't strong enough to override
+ // Claude's default behavior of answering directly. A CLAUDE.md instruction
+ // puts routing rules in project context which Claude weighs more heavily.
+ fs.writeFileSync(path.join(tmpDir, 'CLAUDE.md'), `# Project Instructions
+
+## Skill routing
+
+When the user's request matches an available skill, ALWAYS invoke it using the Skill
+tool as your FIRST action. Do NOT answer directly, do NOT use other tools first.
+The skill has specialized workflows that produce better results than ad-hoc answers.
+
+Key routing rules:
+- Product ideas, "is this worth building", brainstorming → invoke office-hours
+- Bugs, errors, "why is this broken", 500 errors → invoke investigate
+- Ship, deploy, push, create PR → invoke ship
+- QA, test the site, find bugs → invoke qa
+- Code review, check my diff → invoke review
+- Update docs after shipping → invoke document-release
+- Weekly retro → invoke retro
+- Design system, brand → invoke design-consultation
+- Visual audit, design polish → invoke design-review
+- Architecture review → invoke plan-eng-review
+`);
}
/** Init a git repo with config */
M test/skill-validation.test.ts => test/skill-validation.test.ts +2 -2
@@ 1409,13 1409,13 @@ describe('Skill trigger phrases', () => {
];
for (const skill of SKILLS_REQUIRING_PROACTIVE) {
- test(`${skill}/SKILL.md has "Proactively suggest" phrase`, () => {
+ test(`${skill}/SKILL.md has proactive routing phrase`, () => {
const skillPath = path.join(ROOT, skill, 'SKILL.md');
if (!fs.existsSync(skillPath)) return;
const content = fs.readFileSync(skillPath, 'utf-8');
const frontmatterEnd = content.indexOf('---', 4);
const frontmatter = content.slice(0, frontmatterEnd);
- expect(frontmatter).toMatch(/Proactively suggest/i);
+ expect(frontmatter).toMatch(/Proactively (suggest|invoke)/i);
});
}
});