Skip to content

[Bug]: Picking a skill from the / menu inserts $name, which cannot invoke a user-invocation-only skill #8295

Description

@mmenestret

Before submitting

Searched: #7671, #7673, #6095, #7795, #3594. Related, but none covers the /-menu insertion described here.

Area

apps/web

Steps to reproduce

  1. Claude provider, with a skill in ~/.claude/skills/ whose frontmatter sets disable-model-invocation: true (here, clarify).
  2. In an empty composer, type /clarify. The menu lists the skill as /skill:clarify.
  3. Accept the entry with Tab.
  4. Type the rest of the prompt — le ticket 10 — and send.

Expected behavior

The accepted entry yields /clarify le ticket 10. The CLI expands it as a slash command and the skill runs. This is the path #7671 and #7673 designate as the valid one for user-invocation-only skills.

Actual behavior

Accepting the entry replaces the / the user typed with a $. The message sent is $clarify le ticket 10.

The CLI authorizes a user-invocation-only skill only when it finds a literal slash in the turn's user text. With $, that test fails, so the agent falls back to the Skill tool and is refused:

Skill clarify cannot be used with Skill tool due to disable-model-invocation.
Ask the user to run /clarify themselves — it cannot be invoked via the Skill tool.
Do not replicate this skill's workflow by other means — it is reserved for
explicit user invocation.

The user typed a slash and picked an entry labelled /skill:clarify; the composer emitted the one form that cannot work. Nothing signals the substitution — the chip renders identically either way.

Where

apps/web/src/components/chat/ChatComposer.tsx, in the menu accept handler:

if (item.type === "skill") {
  const replacement = `$${item.skill.name} `;

This branch runs for both trigger kinds. Skills reached the / menu through the skills-in-slash-menu setting (on by default), which builds them with label: /skill:${skill.name} and type: "skill" — so they land on a handler written when $ was the only way to pick a skill.

The replacement should follow the trigger that opened the menu. Worth noting that the right insertion is provider-dependent: $name is native for Codex, whereas Claude needs /name, which is the asymmetry raised in the #7671 comments.

Relation to #7673

That PR keeps user-invocation-only skills out of $ and leaves them in /, "which does start them". Its base tree has no skills in the / menu, so the accept handler is outside its diff. Once it lands, / becomes the only offered path for these skills — and accepting from it will still emit $. The fix would close the $ door while the remaining door still leads back to it.

One nuance on the two paths

The message-start rule in #7671 is about expansion. Authorization is separate and less strict: the CLI bundle at 2.1.246 tests the turn's user messages against (?<!\S)/<name>(?=$|\s) — whitespace-bounded, not anchored at the message start. So ok, now /implement the tickets does authorize the Skill tool mid-message, even though no expansion happens. Inline composition for Claude is therefore not lost; it just runs through the skill tool rather than through expansion.

Impact

Major degradation or frequent failure.

Version or commit

t3 0.0.35-nightly.20260826.1194. Also checked the published tarball of 0.0.35-nightly.20260826.1195: ChatComposer.tsx is identical.

Environment

Ubuntu 24.04, Chrome, Claude provider, claude CLI 2.1.246.

Workaround

Type /clarify and dismiss the menu rather than accepting it — the literal text survives and expands normally. Or turn skills-in-slash-menu off, so / stops offering skills and there is nothing to accept.

Activity

  1. mmenestret commented on Aug 26, 2026

    @mmenestret
    Author
    Image The composer after accepting `/clarify` with Tab: the chip reads `Clarify`, but the text sent was `$clarify le ticket 10`, and the Skill call was refused.
  2. FabienDehopre commented on Aug 26, 2026

    @FabienDehopre

    I have the same issue with the /wayfinder skill from Matt Pocock. I installed it globally. It shows up when I type either /wayfinder or $wayfinder but the agent cannot load it.

    Image Image
  3. amihos commented on Aug 26, 2026

    @amihos

    Confirming on T3 Code desktop (Claude provider). Same disable-model-invocation skills from the Matt Pocock plugin (mattpocock-skills): /to-spec from the / menu substitutes $to-spec in the sent message, and the agent bounces with the identical Skill-tool refusal:
    Skill to-spec cannot be used with Skill tool due to disable-model-invocation ... reserved for explicit user invocation.

    Root cause matches the report exactly — the /-menu accept handler emits $name (ChatComposer.tsx), the player rejects the literal $ form, and the agent has no valid path to the user-requested skill.

    Additional data point (workaround, confirmed): the same skill does run when the picker is bypassed and the raw command reaches the model free-form (/to-spec with a trailing space, no menu accept). In that case the sent prompt keeps a leading /, the agent loads the SKILL.md and proceeds. So the break is specifically the menu-insert → $name path, not the skill gating itself — fixing the /-menu handler to emit /name (exactly as the CLI does for a real slash command) should restore both /- and $-picked skills, provided the rest of the provider expansion honors a leading slash.

    (This is the same mechanism that #6095/#7795 describe as provider/UX; flags to the maintainers that the free-form path is a reliable test case.)

  4. Brechard commented on Aug 27, 2026

    @Brechard
    Contributor

    Note

    🤖 Claude Opus 5 writing on behalf of Rodrigo

    Confirmed and fixed in #7673 (same PR that hides un-runnable entries from $).

    Cause. Both composers hard-coded the mention syntax when a skill was accepted, regardless of which menu was open:

    if (item.type === "skill") {
      const replacement = `$${item.skill.name} `;

    apps/web/src/components/chat/ChatComposer.tsx and apps/mobile/src/features/threads/ThreadComposer.tsx. So /clarify and /wayfinder were offered by the / menu, accepted as a chip that reads correctly, and submitted as $clarify — the one syntax a disable-model-invocation skill cannot be started with. The agent then refused the Skill call, which is what @mmenestret and @FabienDehopre both saw.

    Fix. The insertion follows the trigger the user typed — /name from the slash menu, $name from the mention menu — through one shared helper so the two clients cannot drift apart again. A regression test drives the real detectComposerTrigger and asserts both directions, rather than testing the formatter in isolation.

    One related gap the fix also closes: /name only starts a skill when it opens the message, so the slash menu now offers skills under the same message-start rule it already applied to provider slash commands. Further into a message, $name remains the way to reach a mentionable skill.

    A note for anyone verifying this. On a stock build the / menu only receives skills the provider snapshot found by probing the server's own directory, so a project-scoped skill may not appear in the menu at all — separate from this bug, and tracked in #7671 / #5622. Reproduce with a user-scope skill under ~/.claude/skills to keep the two apart.

  5. Brechard commented on Aug 28, 2026

    @Brechard
    Contributor

    Note

    🤖 Claude Opus 5 writing on behalf of Rodrigo

    Update on #7673: the fix for this changed shape since my comment above, and the new form is better for the cases in this thread.

    Picking a skill now inserts $name from either menu by default. /name is only used for a skill marked disable-model-invocation picked at the very start of a message — the one case a $ mention provably cannot reach.

    That is the opposite of where the branch started, and it is the right way round for the reports here. @FabienDehopre and @amihos: the Matt Pocock skills (/wayfinder, /to-spec) are model-invocable, so they were never the /-only case. Picking them gives you $wayfinder, which every provider forwards verbatim and which the agent starts through its own skill tool — measured running 2/2 when named mid-line, where /wayfinder would have arrived as inert text.

    @mmenestret's screenshot is the disable-model-invocation case, and that one gets /name when it opens the message.

    The menu row also renders the sigil it is about to insert, so the row can no longer promise a syntax the accept handler does not deliver — which is the specific thing that made this fail silently.

  6. Brechard commented on Aug 28, 2026

    @Brechard
    Contributor

    Note

    🤖 Claude Opus 5 writing on behalf of Rodrigo

    Correction to my comment above — I had @FabienDehopre's and @amihos's skills backwards, and it matters for what they should expect.

    I wrote that the Matt Pocock skills are model-invocable. @amihos quoted the CLI refusing to-spec with cannot be used with Skill tool due to disable-model-invocation, which says the opposite in the provider's own words. I should have read that line instead of generalising from /wayfinder showing up under $. I do not have the plugin installed, so I cannot speak to wayfinder's frontmatter specifically; @amihos groups it with the same disable-model-invocation set.

    The behaviour on the branch is unchanged and it is the right one for that case — I just described the wrong path through it:

    • $to-spec — gone from the $ menu entirely. $ cannot start it, so offering it there is the original defect.
    • /to-spec at the start of a message — the menu offers it and inserts /to-spec , which is exactly @amihos's confirmed-working free-form path, now reachable from the picker.
    • /to-spec further into a message — not offered at all. Neither syntax can start it from there, so the menu stays quiet rather than inserting something that silently does nothing.

    The $name-by-default rule I described applies to ordinary model-invocable skills. For disable-model-invocation skills, /name at message start is still the only path, and it is the one the picker now produces.

  7. 4dlt commented on Sep 1, 2026

    @4dlt

    Same issue still happening on latest release: Version 0.0.37

  8. RostyslavDzhohola commented on Sep 2, 2026

    @RostyslavDzhohola

    Still reproduces on 0.0.38-nightly.20260901.1250, matching @4dlt's report on 0.0.37.

    Root cause appears to be upstream of the composer, so I've filed it separately as #9161 rather
    than reopening here: T3 Code's Claude skill discovery never captures disable-model-invocation.
    Cached entries in ~/.t3/caches/claudeAgent.json carry only
    ['description', 'enabled', 'name', 'path', 'scope'].

    The rule described in the 2026-08-28 comment — /name only for "a skill marked
    disable-model-invocation picked at the very start of a message" — therefore tests a field that
    does not exist in T3 Code's data model, so it never fires and every pick still emits $name.
    The / route is otherwise wired correctly; ask-matt, to-spec, implement and the rest are all
    present in slashCommands.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions