Skip to content

[Bug]: Grok provider: $skill-name inserted by the composer picker is sent as literal text and never invokes the skill #13457

Open

Description

@SachinD6

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. Enable the Grok provider with a plugin-installed skill (on this machine the Grok CLI reports 97 skills, including poteto-mode from the pstack plugin).
  2. Open a Grok thread in the T3 web client.
  3. Type $ in the composer and pick the skill from the picker.
  4. Send the message.
  5. Compare with the same request typed as /poteto-mode at the start of the message.

Expected behavior

Selecting a skill from the $ picker should invoke that skill, matching the semantics the picker advertises.

Actual behavior

The picker inserts the literal text $skill-name into the message. For the Grok provider T3 never converts it to an invocation, and the Grok CLI does not expand $name in a plain prompt, so the model receives the raw $skill-name string as prose and the skill never loads. The /skill-name form works because the Grok harness expands it.

Impact

Major degradation or frequent failure

Version or commit

T3 0.0.42 (server runtime at ~/.t3/runtime/versions/0.0.42), Grok CLI 1.0.41

Environment

Linux (EC2), T3 web client over loopback, Grok provider, go-muse-spark-1-3 model

Logs or stack traces

The two observations below are from one thread on the same machine.

  1. A message beginning /poteto-mode ... reached the model with the skill expanded. The session transcript shows the skill block injected into the user message, and the skill ran.

  2. A message beginning $poteto-mode $poteto-mode ... reached the model as literal text. The session transcript shows $poteto-mode as plain prose with no skill block, and the reply treated it as prose.

A second check without T3 rules this out as a harness-side expansion gap rather than a T3-side one:

$ grok -m go-deepseek-v4-1-flash --always-approve --output-format json -p '$poteto-mode reply with the single word hi'

The model's own reasoning in the response: The "$poteto-mode" looks like a trigger phrase that isn't in my skill list. There's no such skill. The literal $poteto-mode string was delivered verbatim to the model.

Workaround

Type the skill as /skill-name at the start of the message. The composer's / menu also offers the skill, and that path invokes it correctly on Grok.


Supporting detail from the 0.0.42 runtime bundle, for whoever triages this.

  • The web client inserts the picker selection as `$${e.skill.name} ` into the composer.
  • src/provider/Drivers/CursorSkills.ts defines rewriteCursorSkillMentions, and src/provider/Layers/CursorAdapter.ts applies it so $name becomes /name for Cursor sessions. There is no equivalent rewrite for the Grok provider; src/provider/Drivers/GrokSkills.ts only performs discovery via grok inspect --json.
  • The Grok provider snapshot does expose these skills to the client (the cached snapshot at ~/.t3/caches/grok.json lists 97 skills including poteto-mode), so the $ picker offers them.

This is the same class of silent downgrade reported for Claude in #7795, which was closed and folded into the provider-agnostic skill model discussion (#6987). Filed as a bug because the concrete Grok behavior is a no-op invocation, not a feature gap.

Activity

  1. juliusmarminge commented on Sep 24, 2026

    @juliusmarminge
    Member

    Confirmed on current 0.0.42. Picking a Grok skill from the composer sends the literal $name text, and the Grok adapter never lowers it to the /name form the Grok CLI expands. The CLI check in the report matches that: grok delivers $poteto-mode as prose.

    The composer inserts $name for every provider, from both menus. Web does this in ChatComposer when a skill row is chosen, and mobile does the same in use-composer-command-menu. The slash-menu row is labeled /skill:name, but the inserted text is still $name . A message that actually starts with /skill-name is the path that runs, because the Grok harness expands a leading slash command. GrokAdapter.sendTurn also leaves that form untouched: when the text matches /^\/[^\s/]+/, it omits the runtime-instructions suffix so the command reaches the CLI on its own.

    Other providers already lower the same composer text:

    • Codex keeps $name. buildTurnStartParams only normalizes the currency prefix so the app-server sees its native skill token.
    • Claude splits the last $skill mention into a trailing text block that starts with /name (planClaudeSkillDispatch), which is the only place Claude Code expands a skill.
    • Cursor rewrites a discovered $name to /name inline (rewriteCursorSkillMentions in CursorAdapter).

    GrokAdapter.sendTurn trims input.input and puts that string in the session/prompt text block. There is no skill rewrite. GrokSkills only discovers the catalog via grok inspect --json, and that catalog is what fills the $ picker, so the skill is offered and then sent as prose. User docs say skill mentions work on any line (docs/user/composer.md), which is what Claude and Cursor implement and what this path misses for Grok.

    This is the same hole #9128 closed for Claude, kept at the adapter boundary so the harness runs the skill. The earlier cross-provider attempt in #8336 was closed in favor of that approach and did not land a Grok lowering. #7795 is the closed feature proposal; discussion #6987 is a separate "one plugin list" idea and does not track this failure.

    Workaround: start the message with /skill-name. Picking the skill row from $ or / both insert $skill-name, which is the broken path.

  2. added
    acceptedfeature request accepted
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Sep 24, 2026
  3. added a commit that references this issue on Oct 5, 2026
    b5926c2
  4. added 2 commits that reference this issue on Oct 5, 2026
    48fe81e
    f8d272c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions