garrytan / garrytan/gstack

Natural-language skill triggers are stored in triggers: but not folded into descriptions

Open
#2,118 3 comments 1 reaction 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.