Repository navigation
[Bug]: Picking a skill from the / menu inserts $name, which cannot invoke a user-invocation-only skill #8295
Description
Activity
Confirming on T3 Code desktop (Claude provider). Same
disable-model-invocationskills from the Matt Pocock plugin (mattpocock-skills):/to-specfrom the/menu substitutes$to-specin 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-specwith 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 →$namepath, 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.)
Reacted by Fabien Dehopré, schuma7, Svilen Petrov and Dave JNote
🤖 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.tsxandapps/mobile/src/features/threads/ThreadComposer.tsx. So/clarifyand/wayfinderwere offered by the/menu, accepted as a chip that reads correctly, and submitted as$clarify— the one syntax adisable-model-invocationskill 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 —
/namefrom the slash menu,$namefrom the mention menu — through one shared helper so the two clients cannot drift apart again. A regression test drives the realdetectComposerTriggerand asserts both directions, rather than testing the formatter in isolation.One related gap the fix also closes:
/nameonly 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,$nameremains 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/skillsto keep the two apart.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
$namefrom either menu by default./nameis only used for a skill markeddisable-model-invocationpicked 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/wayfinderwould have arrived as inert text.@mmenestret's screenshot is the
disable-model-invocationcase, and that one gets/namewhen 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.
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-specwithcannot 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/wayfindershowing up under$. I do not have the plugin installed, so I cannot speak towayfinder's frontmatter specifically; @amihos groups it with the samedisable-model-invocationset.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-specat 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-specfurther 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. Fordisable-model-invocationskills,/nameat message start is still the only path, and it is the one the picker now produces.Same issue still happening on latest release: Version 0.0.37
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 capturesdisable-model-invocation.
Cached entries in~/.t3/caches/claudeAgent.jsoncarry only
['description', 'enabled', 'name', 'path', 'scope'].The rule described in the 2026-08-28 comment —
/nameonly for "a skill marked
disable-model-invocationpicked 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,implementand the rest are all
present inslashCommands.Reacted by Ohad Cohen


Before submitting
Searched: #7671, #7673, #6095, #7795, #3594. Related, but none covers the
/-menu insertion described here.Area
apps/web
Steps to reproduce
~/.claude/skills/whose frontmatter setsdisable-model-invocation: true(here,clarify)./clarify. The menu lists the skill as/skill:clarify.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: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:This branch runs for both trigger kinds. Skills reached the
/menu through theskills-in-slash-menusetting (on by default), which builds them withlabel: /skill:${skill.name}andtype: "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:
$nameis 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. Sook, now /implement the ticketsdoes 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
t30.0.35-nightly.20260826.1194. Also checked the published tarball of 0.0.35-nightly.20260826.1195:ChatComposer.tsxis identical.Environment
Ubuntu 24.04, Chrome, Claude provider,
claudeCLI 2.1.246.Workaround
Type
/clarifyand dismiss the menu rather than accepting it — the literal text survives and expands normally. Or turnskills-in-slash-menuoff, so/stops offering skills and there is nothing to accept.