[FEATURE] Opt-in frontmatter to let a custom slash command expand into a built-in command invocation
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
Custom slash commands are, mechanically, a text macro: the body of ~/.claude/commands/foo.md is injected verbatim as the text of a user message, wrapped in <command-message> / <command-name> / <command-args>, with $ARGUMENTS interpolated. That makes them excellent for reusable prompts and useless for reusable commands, because a slash command inside the body is not dispatched.
Measured on 2.1.252, first in headless print mode (claude -p "/cmd" --output-format json) by asking the model to echo the user message it received verbatim, then re-checked in an interactive session:
- A body whose second line was
/goal ZULU-4417 keep working until every task reaches completedcame back with that line intact, and no goal was set. - The same holds for an ordinary custom command, so this is not something special about built-ins: a command whose body contained a sibling custom command
/innerechoed the literal line/inner, and/inner's own body sentinel never appeared. - In an interactive session, a command whose body contained both
/goal <condition>and/innerdispatched itself (the model replied) while no◎ /goal activeindicator appeared and/inner's body sentinel was absent. The indicator is set by the harness rather than reported by the model, so it is not something the model could get wrong.
Both tested nested cases were inert, one built-in and one custom command; I have not tried to establish it for every command. Nothing warns that a leading /... in a command body is inert, so the failure is silent. The command "works", it just quietly did nothing but talk about itself.
The consequence is that there is no way inside the client to give a frequently-used built-in invocation a short name. Anything with a long argument, /goal above all, has to be retyped or pasted in full, every session. In my case that is a roughly 3000 to 4000 character goal condition kept in a text file and pasted by hand into every new session, which also runs into #85654 and #86803 (a slash command inside a collapsed [Pasted text #N] is submitted as a plain message rather than dispatched) and #84953 (the 4000-character cap, and no file reference).
Proposed Solution
Let a custom command declare, in frontmatter, that it expands into a command invocation rather than into prompt text. Something like:
---
description: arm my standard autonomy goal
expands-to: /goal
---
Every non-HIL task must reach completed. Proof: paste the command that re-verifies each claim. ...
Typing /mygoal would then dispatch /goal <body> exactly as if the user had typed it, and /mygoal <args> would append or substitute via the existing $ARGUMENTS mechanism.
Opt-in via frontmatter matters more than the exact spelling. Expanding by default would be a real hazard, because text arriving from plugins, marketplaces and @-file content could then silently dispatch /clear or /permissions. An explicit declaration keeps the current safe behaviour and puts the decision with whoever authored the file. Restricting expansion to user-invocable commands, refusing self-reference, and capping depth would keep the blast radius small.
Alternative Solutions
- Type it out or paste it every time. The status quo. For a multi-thousand-character goal this is a real per-session tax, and pasting collides with #85654 and #86803. The workaround that does work today is to type
/goalfirst and paste only the argument. /goal @file. Does not work. Measured on 2.1.252, it arms the literal string (Goal set: @payload.txt) and shows◎ /goal active, so it silently succeeds with a meaningless condition. Requested separately in #84953.- Have the agent arm the goal. Not available to me: it is gated off on my account. That is #85730, and it is a different feature anyway, since it puts the decision with the model, whereas this request keeps it with the user, who types a short command deliberately.
- Drive the terminal from outside (
tmux send-keys, AppleScript, and similar). Workable, but the wrong shape for a keystroke-level convenience: it requires the session to be hosted in a particular way and reaches into the user's terminal to do it. - A hook. Not possible in the plugin hooks API by design, since
$.prompt.submitexplicitly refuses text beginning with/.
Priority
Medium - Would be very helpful
Feature Category
Interactive mode (TUI)
Use Case Example
- I keep one carefully-worded goal condition, the stopping rules for autonomous work, in a file, and re-use it across sessions with small edits.
- Today: open the file, select all, copy, switch to the session, type
/goal, paste, submit. If I paste the whole/goal <body>blob instead, the paste collapses into a placeholder and #85654 silently submits it as an ordinary message, so the session runs with no goal and nothing says so. - With this feature:
~/.claude/commands/mygoal.mdholds the text withexpands-to: /goal, and arming it is typing/mygoal. - It also survives editing. Improving the goal means editing one file, not remembering which of the last N sessions had the good version pasted into it.
Additional Context
Verified on v2.1.252, macOS 26.4.1 (Darwin 25.4.0). The sentinel strings used in these probes deliberately do not appear in the instruction text, so the model could not have produced them from the question.
Closest prior art: #37307 ("Allow custom slash commands to trigger /clear or /compact"), closed by the inactivity bot rather than on the merits, and now locked. The motivation overlaps but the mechanism does not, which is why I have filed rather than commented. It proposes three context-management integrations: a frontmatter post-completion action, PreClear/PostClear hooks that run automation when /clear is invoked, and a Clear/ResetContext tool. All three are specialised to clearing or compacting context, and none turns a command body into arguments for an arbitrary built-in, so #37307's shape would not help with /goal.
Related: #84953 (the /goal character cap and file reference), #85654 and #86803 (collapsed-paste dispatch, bisected to 2.1.224), #85730 (agent-requestable goal arming). This request is the user-side complement to all of them: none lets a user give a long built-in invocation a short name.