Skip to content

Composer offers skills and slash commands that cannot run #7671

Description

@Brechard

The composer pickers offer entries that are impossible to invoke, and picking one fails silently.

1. $ offers skills the agent cannot see

A skill whose frontmatter sets disable-model-invocation: true is deliberately hidden from the agent's skill tool, but discoverClaudeSkills only reads name and description, so the $ picker lists it anyway.

$ inserts the name into the prompt as plain text — a hint, not an invocation — so the agent still chooses. Unable to see the skill you named, it picks the closest one it can see.

Concretely: I picked $re-release-version from the $ menu. The agent could not see it, ran release-version instead, and cut a whole new release before I noticed.

2. $ offers skills the user switched off

skillOverrides in settings.json is not read, and discovery hardcodes enabled: true. Every skill set to "off" still shows up.

3. / offers commands where they will not expand

The / menu opens on any line starting with /, but the CLI only expands a slash command that opens the whole message. Chosen later in a message, the command reaches the agent as literal text.

Verified against the Agent SDK with tools disabled, so the reply could only come from expansion:

prompt result
/mycommand expanded
hello\n/mycommand not expanded
please run /mycommand I don't recognize /mycommand ... if it were a valid registered command, it would normally be expanded before I see it.

Expected

A picker should only offer entries that will actually run. User-invocation-only skills belong in /, which does start them; disabled skills belong in neither; and provider commands should only be offered where the CLI will expand them.

Environment

macOS, Claude provider, claude CLI 2.1.237.

Activity

  1. marcob896 commented on Aug 21, 2026

    @marcob896

    Confirming this with another concrete Claude vs Codex UX comparison.

    With Codex, the composer behaves consistently:

    • $ lists skills, and selecting one inserts the native $skill-name invocation.
    • / lists commands.

    With Claude, the same UI becomes misleading:

    • $ lists skills, but selecting a user-invocation-only skill inserts $skill-name as plain text. Claude then refuses to launch it because it was not directly invoked by the user.
    • / lists both Claude skills and commands, and selecting /skill-name works.

    Reproduced with setup-matt-pocock-skills: selecting it from $ produced $setup-matt-pocock-skills, after which Claude explicitly said the skill required direct /setup-matt-pocock-skills invocation.

    So the problem is not merely failed execution. The provider-neutral $ picker actively leads Claude users toward the invalid path while the valid path already exists under /. The behavior proposed in #7673, keeping user-invocation-only Claude skills out of $ and available under /, matches the expected UX.

  2. marcob896 commented on Aug 21, 2026

    @marcob896

    One more UX consequence: moving Claude user-invocation-only skills from $ to / is not semantically equivalent in T3.

    Provider slash-command autocomplete is intentionally available only when / opens the message. A skill mention using $, however, can be inserted naturally anywhere in the prompt, for example:

    ok, now $implement all the tickets
    

    Typing /implement in that same position does not autocomplete, and Claude slash expansion itself only works when the command opens the message. So #7673 is a useful safety fix, but it also removes inline skill composition for Claude users.

    The stronger provider-agnostic UX would be:

    • $skill-name always means a skill invocation in T3, in any supported composer position.
    • /command always means a command.
    • T3 preserves the skill invocation as structured composer/send metadata instead of flattening it immediately to plain $skill-name text.
    • Each provider adapter translates that canonical invocation into the provider-native mechanism and validates provider limitations.

    Today ComposerSkillNode.getTextContent() flattens the semantic skill node to $${skillName} before provider dispatch. That works natively for Codex but leaves Claude with plain prose rather than an explicit user invocation. Exposing Claude’s internal /skill convention directly in the shared UI leaks provider semantics and gives different composition capabilities depending on the selected provider.

  3. Brechard commented on Aug 28, 2026

    @Brechard
    ContributorAuthor

    Note

    🤖 Claude Opus 5 writing on behalf of Rodrigo

    #7673 now covers all three items in this issue, and @marcob896's follow-up changed the design for the better — thank you for it.

    On inline composition. You were right that gating skills behind a message-start / would remove it, and the branch did exactly that for a while. It no longer does. $name is the default insertion from either menu, so ok, now $implement all the tickets is what picking a skill mid-message gives you. /name is reserved for the one case a mention provably cannot reach: a user-invocation-only skill picked at the start of a message.

    I did not want to assert the $ half from reading code, so I measured it against the Agent SDK. A model-invocable skill named mid-line runs via the Skill tool 2/2. A disable-model-invocation skill named mid-message still runs roughly one time in three, because the tool accepts it by name even though it is hidden from the published command list — which is why those skills are the only ones routed to /, and only where / expands.

    That falls short of the canonical-invocation model you described. ComposerSkillNode.getTextContent() still flattens to $${skillName} before dispatch, and per-provider translation of a structured invocation is a larger change than this fix. I would rather that be its own issue than get folded in here; it is the right direction and it is not blocked by anything this PR does.

    On the three items above:

    1. disable-model-invocation is parsed during discovery and surfaces as userInvocationOnly. Those skills are out of $.
    2. skillOverrides is read from the merged settings chain — user, project, local, then managed policy, which wins. Skills set to "off" are out of both menus; "user-invocable-only" is treated the same as the frontmatter flag.
    3. Provider slash commands are only offered when the trigger opens the message. Built-ins like /model and /plan are applied locally on selection, so they stay available anywhere.

    Two further mismatches with the CLI turned up while verifying, both fixed on the branch: skills are identified by their directory, not frontmatter name (a skill in probe-alias/ declaring name: probe-alias-frontmatter is published as probe-alias, and only skillOverrides["probe-alias"] disables it), and name collisions resolve user over project, which is the opposite of what the code did and of what its own comment claimed. Both were measured against claude 2.1.250 rather than inferred.

  4. Brechard commented on Aug 28, 2026

    @Brechard
    ContributorAuthor

    Note

    🤖 Claude Opus 5 writing on behalf of Rodrigo

    @marcob896 — correcting myself on one thing: I said your canonical-invocation model "deserves its own issue". Wrong venue. CONTRIBUTING.md reserves issues for bug reports and sends proposals to Ideas discussions. That is where it belongs, and it is your proposal to file, not mine to relocate.

    I should also be straighter about what my fix does to the case you raised, because it is not neutral. Past the start of a message the / menu now narrows to skills a $ mention can actually reach, which excludes disable-model-invocation ones. So those skills are unreachable from the picker mid-message — previously they were offered and inserted $name, which I measured starting them about one time in three. I traded a flaky path for no path.

    I think that is the right trade for this issue specifically, since the reported harm was an agent silently running a different skill and cutting a release. A menu that does nothing is safer than a menu that does something else. But it is a capability reduction, it is exactly the inline-composition loss you flagged, and your model is the thing that would give it back properly rather than by picking a better sigil.

  5. D3OXY commented on Aug 28, 2026

    @D3OXY
    Contributor

    Related implementation: #8336 preserves selected $skill tokens as structured invocation metadata and dispatches them explicitly across Codex, Claude Code, Cursor, Grok, and OpenCode. It addresses the flattened/inert skill path and user-invocation-only behavior described here. This issue should stay open because #8336 does not cover every separate slash-command picker and expansion case.

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