Natural-language skill triggers are stored in triggers: but not folded into descriptions
- Dominant language
- TypeScript
- Stars
- 133k
- Forks
- 19.9k
- Avg merge
- 18h 46m
- Merged PRs (30d)
- 26
Description
## What I observed
The README says gstack skills support natural/voice-friendly trigger phrases:
> gstack skills have voice-friendly trigger phrases. Say what you want naturally — \"run a security check\", \"test the website\", \"do an engineering review\" — and the right skill activates.
Source: `README.md`, Voice input section.
But most skill templates declare those phrases in a normal `triggers:` frontmatter field, while the generation pipeline only folds `voice-triggers:` into `description:`.
On current `main` (`11de390b`, v1.58.5.0), I counted:
- 56 `SKILL.md.tmpl` files with `triggers:`
- 20 `SKILL.md.tmpl` files with `voice-triggers:`
- 0 code paths that fold ordinary `triggers:` into `description:`
The implemented folding path is specific to `voice-triggers`:
- `scripts/gen-skill-docs.ts` has `extractVoiceTriggers()` / `processVoiceTriggers()`.
- `processVoiceTriggers()` appends `Voice triggers (speech-to-text aliases): ...` to the description.
- The function returns unchanged when there is no `voice-triggers:` field.
That means phrases under normal `triggers:` stay as metadata. For hosts that discover skills from name + description, those phrases are not part of the matching surface.
## Why this matters
Claude/OpenAI-style skill discovery appears to rely on `name` + `description`, not arbitrary extra frontmatter fields.
The repo already reflects that model in the Codex host config:
```ts
frontmatter: {
mode: 'allowlist',
keepFields: ['name', 'description'],
descriptionLimit: 1024,
descriptionLimitBehavior: 'error',
}
```
So for Codex/OpenAI output, `triggers:` is explicitly dropped. For Claude Code, even if the raw field remains in the file, the always-loaded catalog/triggering surface is still the description. The repo's own catalog trim also moves routing prose out of frontmatter after the lead sentence.
This creates a mismatch:
- README promises natural phrases activate the right skill.
- Templates contain those phrases, but mostly under `triggers:`.
- The generator only preserves `voice-triggers:` in `description:`.
- External host metadata uses the post-processed description, so ordinary triggers are invisible there too.
## Concrete example
`qa/SKILL.md.tmpl` has useful triggers like:
```yaml
triggers:
- qa test this
- find bugs on site
- test the site
voice-triggers:
- quality check
- test the app
- run QA
```
Only the `voice-triggers` values are appended to the description. The normal `triggers` values are not.
## Expected behavior
Either:
1. Fold selected `triggers:` phrases into the generated `description:` / Codex `openai.yaml` short description, similar to `voice-triggers`, or
2. Rename the intended model-visible field and migrate templates so trigger phrases that should affect skill selection live in `description:` / `voice-triggers:`, or
3. Document that `triggers:` is only for gstack's own router/browser-skill metadata and does not affect Claude/Codex skill discovery.
My guess is option 1 was intended, because `processVoiceTriggers()` already implements the exact mechanism, but only for `voice-triggers:`.
## Suggested test coverage
Add a generator test that fails if a skill template has `triggers:` but the generated host-visible description/metadata has none of the expected trigger intent. A minimal version could assert that a fixture with `triggers:` emits a `Use when asked to...` line in generated Codex/Claude descriptions, subject to the existing description length limit.
Contributor guide
Research direction
Start in scripts/gen-skill-docs.ts, especially extractVoiceTriggers() and processVoiceTriggers(), then compare the generated metadata for qa/SKILL.md.tmpl with its triggers: and voice-triggers: values. Check the Codex host configuration’s name/description filtering and existing description limits. Add or update generator coverage so the chosen behavior is observable in generated Claude/Codex metadata without exceeding the limit.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100