[Desktop 2.2553.1.0] Slash picker opens only at position 0, even after an accepted /skill

Status Open
Reported on v2.1.265
Maintainer reply None cached
Activity 0 comments · opened Sep 20, 2026

The bug, in two rules. In the Claude Desktop Code tab composer there are exactly two behaviours:

  1. / typed as the first character of an empty box opens the command and skill list.
  2. With anything at all before the / the list never opens. Anything means anything: one space,

a full stop, a word, or even an already accepted command chip earlier in the same message.

Rule 2 is the bug. A name typed under it is sent as plain text and does not run. Both rules were
checked first-hand on Claude Desktop 2.2553.1.0; see "First-hand check".

Scope. Similar reports exist for the terminal CLI, but they do not agree: #91094 states the
terminal was unaffected at the time of that report. The terminal was not tested here, so this issue
is written about the desktop composer, with the terminal reports listed but not claimed.

What follows. The linked reports, verbatim changelog lines read from the repository, one measured
check, a proposed trigger rule, a runnable reference implementation, and its test cases. Everything
that is not the first-hand check is attributed to an issue number or a changelog line.

What is broken

  • Reports describe a slash-command and skill picker that opens when / is the first character, but not after existing text (#89398, #90441, #91337, #91442).
  • Reports cover terminal and desktop interfaces. They do not establish one shared regression: #91094 explicitly says the terminal CLI was unaffected.
  • #77868 reports mid-sentence commands highlighted but not dispatched. #91417 reports that mid-input completion misses plugin-namespaced skills.
  • #90441 reports that @ still triggers mid-prompt while / does not.

Everything above is a summary of other people's reports. The one measurement in this issue is the First-hand check below.

How to reproduce

Use an installed command or skill. Record the version and interface for each run. These steps are reconstructed from the reports.

  1. In an empty composer, type /, then part of a known name. Check whether the picker opens and filters.
  2. Clear the input. Type hello world , then / and the same name prefix.
  3. Compare with step 1. The failure is that step 2 shows no picker. Reported in the issues listed below, and confirmed first-hand in the check that follows.
  4. Repeat after a newline and with a plugin-namespaced skill, if one is installed.
  5. Type a complete known name mid-message. Record its highlighting and submit result separately. #77868 reports highlighting without dispatch; #89398 reports that a command still runs despite the missing picker. The issue text read for this report does not give enough detail to equate those submit cases.
  6. Compare / and @ after text. #90441 reports that only @ opens its picker there.
  7. Repeat step 2, then press Tab before typing anything else. The 2.1.265 entry says mid-prompt matches show in a list that Tab opens outside fullscreen. Record the result. See the first-hand check below for one measurement of this step.

First-hand check, the only measurement in this report

Environment: Windows 11 Pro 26200, Claude Desktop 2.2553.1.0 (Microsoft Store build), the Code tab
composer, Claude Code CLI 2.1.275. Checked on 2026-09-21.

Typed test /for with the caret at the end, where forge-god is an installed skill. Result: no
picker appeared at any point, and the token received no command highlight.
Pressing Tab did not open
a list. Tab moved keyboard focus to the send control at the right of the composer. Repeated at two
window sizes with the same result. Control: typing /for as the first characters of an empty
composer does open the picker and filters it.

Follow-up in the same environment: with the picker already open, which happens only when / is the
first character, Tab accepts the highlighted entry. So in this build Tab is the accept key for an
already open list, not a key that opens one. The 2.1.265 wording, "Tab opens it outside fullscreen",
does not describe this composer's Tab binding.

The clearest single case. Type /test-helpers in an empty box. The list opens, the name is
accepted, and the composer shows it as a recognised command. Then type a space and /forge in the
same message. No list appears, and /forge stays plain text. So the composer can hold one
recognised command and still refuse to open the list for the next one in the same input.

The terminal CLI was not tested here.

What this issue asks for: restore the mid-message picker, without ever silently running a command
name that was only mentioned inside a sentence.

When it started

| Evidence read from the issues | What it establishes |
|---|---|
| #44447 and #44488, 2026-04-07; #56002, 2026-05-04 | Earlier reports exist. All three are closed; closure does not establish a fix. |
| #56002 cites v2.1.0 and "anywhere in input"; #90102 names v2.1.0 and v2.1.136 | Both version claims are correct. Their changelog lines are quoted verbatim in the table below. |
| #91337 | Reporter names 2.1.246 as last good and 2.1.247 as the regression. |
| #91094 | Reporter names desktop build 1.40609.0 and says the terminal CLI was unaffected. |
| #89398 through #95268 in the related list | Those reports were opened between 2026-08-25 and 2026-09-18. |

The public changelog was read directly from the repository on 2026-09-21, from
https://raw.githubusercontent.com/anthropics/claude-code/main/CHANGELOG.md. These lines are quoted
word for word, each under its own version heading in that file:

| Version | Changelog line, verbatim |
|---|---|
| 2.1.0 | "Added slash command autocomplete support when / appears anywhere in input, not just at the beginning" |
| 2.1.20 | "Fixed ghost text flickering when typing slash commands mid-input" |
| 2.1.110 | "Fixed skills with disable-model-invocation: true failing when invoked via /<skill> mid-message" |
| 2.1.136 | "Fixed mid-input slash command autocomplete not working after an initial slash command" |
| 2.1.246 | "Fixed prompts beginning with /-- (e.g. Lean doc comments) being rejected as an unknown slash command instead of being sent to Claude" |
| 2.1.265 | "Improved slash commands typed mid-prompt: matches now show in a list (Tab opens it outside fullscreen) instead of a single suggestion, and a plugin skill is now found by its bare name" |

Two things follow from those lines alone. Mid-input completion was an intentional feature, shipped
in 2.1.0 and maintained through 2.1.265. And no changelog line anywhere states that it was removed or
made position-dependent.
The boundary reported in #91337 (2.1.246 last good, 2.1.247 first broken) is not
corroborated by any composer-related entry under 2.1.247. There is still no established single start
date across interfaces.

Likely mechanism (one theory tested, the rest unproven)

  • tested and ruled out for the desktop app: the 2.1.265 entry says mid-prompt matches "now show in a list (Tab opens it outside fullscreen) instead of a single suggestion". Tab was tried in the first-hand check above and opened nothing; it moved focus to the send control, and with an already open picker it accepts the highlighted entry instead. Whatever surface that entry describes, it is not the desktop Code tab composer at 2.2553.1.0. Still untested in the terminal CLI, and worth one check there.
  • unproven: the picker may be checking the start of the whole input rather than the slash token being edited. This would fit the position-dependent reports.
  • unproven: completion and submit handling may share a position check. Separate checks could also produce these symptoms. The reports do not establish the code layout.
  • unproven, and weakened by the changelog: the /-- fix is often named as the cause, but its entry sits under 2.1.246, which #91337 itself names as the last good version.

A start-of-input check alone cannot protect /--, since that string also starts with /. The proposed rule below handles it explicitly, without claiming how the product currently does so.

Trigger vs dispatch

| Decision | Proposed responsibility |
|---|---|
| Trigger | Offer names for the slash token at the caret, including after text. |
| Dispatch | Run only an exact registered name in the first token of the message. |
| Highlight | Mark a name as executable only when the submit rule will run it. |

Share token extraction and name lookup, not one yes/no answer for all three decisions.

Opening the picker must not imply execution. This proposal restores completion, not mid-message execution or skill attachment. An inline completion must be labelled as text before submit. Otherwise the picker fix would leave the confusion reported in #77868.

Proposed rule

This is a proposed contract, not a description of existing internals.

1. Find the token and use one name list

  • Here, whitespace means space, tab, LF or CR. A token is a run of other characters.
  • A slash candidate must start a token. Allow position 0 or whitespace before /. Do not add punctuation boundaries such as (, quotes or =.
  • For completion, find the token containing the caret. The caret must be after its initial / and no later than the token's end. Do not require the caret to be immediately after /.
  • Use the full text after / in that token as the name, including text after the caret. Use the same registered names, aliases and plugin-namespaced skills at every position. Do not impose a new ASCII-only name pattern.
  • Reserve any token starting with /-- as literal text. This check comes before name lookup in both completion and submit handling. Flag a conflicting registered name in tests rather than silently executing it.

2. Open and complete, without submitting

  • On direct typing in a candidate, open for an empty name or a name that is a prefix of at least one registered name. Filter by the whole current name, even when editing inside the token.
  • Close on no match, /--, removal of the initial slash, accepting an item, Escape, or the caret leaving the token (including typing its ending whitespace).
  • Paste or caret movement alone must not open the picker. An explicit picker action may open a valid candidate. Moving within an already active token updates its results.
  • Accepting a picker item replaces only that token with the selected /name. Keep all surrounding text. It does not submit, execute, attach a skill, or move the token to the start.
  • If Enter accepts a picker item, consume that event. A separate send action is required to submit.

3. Decide what to run on submit

  1. Locate the first token, ignoring leading whitespace for this decision only. Do not alter the original message.
  2. If it starts with /--, send the entire original input as text, without a slash-command error.
  3. Otherwise, run only if that first token is / followed by an exact registered name. Pass the remaining input to that command's normal argument handler.
  4. For an empty name, unknown name or non-command first token, send the entire original input as text. Do not execute a prefix match or scan later tokens for commands.

Leading whitespace and unknown-name fallback are explicit policy choices here. They are not established existing behavior. They need compatibility tests, not assumptions.

4. Explain the result before send

  • Only the first exact command token gets an executable-command highlight. Mid-message names must not receive that highlight.
  • When the current inline token has a nonempty registered-name prefix or was completed by the picker, show: "Text only. To run this command, put it first." Keep that indication for completed inline names until the input changes their status.
  • For an unknown leading slash name, show: "No matching command. This will be sent as text." Do not warn for a bare / or the reserved /-- form.
  • Compute these states from the same token and registry checks as submission, including after edits and paste.

Reference implementation

This is a standalone implementation of the rule above, written and run while preparing this report.
It is not a patch against the product, since the composer source is not public. It is here so the
rule is unambiguous and so its behaviour can be checked in seconds.

Pure functions, no DOM, no dependencies. Save as slash-trigger.mjs and run the test file with
node --test --test-reporter=tap slash-trigger.test.mjs.

Result when this report was written: 100 tests, 100 pass, 0 fail, on Node 26 (Windows). The test
file covers every row of the table below plus the event cases, using the fixture registry
help, compact, plugin:skill, 9task, -task, équipe. It is about 14 KB and can be added as
a comment on request.

// slash-trigger.mjs
// Reference implementation of the proposed composer slash-trigger rule (SPEC.md).
// Pure functions only: no DOM, no framework, no dependencies, no global state.
//
// Shared vocabulary:
//   whitespace = space, tab, LF or CR
//   token      = a maximal run of non-whitespace characters
//   candidate  = a token that starts with "/"

const WS = /[ \t\n\r]/;
const WS_ONLY = /^[ \t\n\r]*$/;

/** All tokens of the text, in order, as { start, end } half-open ranges. */
function tokens(text) {
  const out = [];
  let i = 0;
  while (i < text.length) {
    while (i < text.length && WS.test(text[i])) i++;
    if (i >= text.length) break;
    const start = i;
    while (i < text.length && !WS.test(text[i])) i++;
    out.push({ start, end: i });
  }
  return out;
}

/** Does this candidate token name match the registry (empty name = every name)? */
function matchesFor(name, registry) {
  if (name.startsWith("--")) return [];
  return registry.filter((n) => name === "" || n.startsWith(name));
}

/**
 * The slash token containing the caret, or null.
 * The token must start with "/" at index 0 or right after whitespace (a token
 * boundary already guarantees that). The caret must be after that "/" and no
 * later than the token's end. The name is all text after the "/" in the token,
 * including text after the caret.
 */
export function findSlashToken(text, caret) {
  if (typeof text !== "string" || !Number.isInteger(caret)) return null;
  if (caret < 0 || caret > text.length) return null;
  for (const t of tokens(text)) {
    if (t.start + 1 <= caret && caret <= t.end) {
      if (text[t.start] !== "/") return null;
      const name = text.slice(t.start + 1, t.end);
      return { start: t.start, end: t.end, name, isReserved: name.startsWith("--") };
    }
  }
  return null;
}

/**
 * Picker state for the token at the caret.
 * open only for a candidate whose name is empty or a prefix of a registered
 * name, and never for a reserved "/--" token. textOnly is true when the open
 * token is not the first token of the message.
 */
export function pickerState(text, caret, registry = []) {
  const closed = { open: false, matches: [], textOnly: false };
  const tok = findSlashToken(text, caret);
  if (!tok) return closed;
  if (tok.isReserved) return closed;
  const matches = matchesFor(tok.name, registry);
  if (matches.length === 0) return closed;
  const before = text.slice(0, tok.start);
  return { open: true, matches, textOnly: !WS_ONLY.test(before) };
}

const UNKNOWN_NOTICE = "No matching command. This will be sent as text.";

/**
 * What an explicit send of this text would do. The original text is never
 * changed. Leading whitespace is ignored only for locating the first token.
 */
export function submitPlan(text, registry = []) {
  const all = tokens(text);
  const text_ = (notice) => ({ action: "text", command: null, args: null, notice });
  if (all.length === 0) return text_(null);
  const first = all[0];
  const tokenText = text.slice(first.start, first.end);
  if (tokenText[0] !== "/") return text_(null);
  const name = tokenText.slice(1);
  if (name.startsWith("--")) return text_(null); // reserved, no notice
  if (name === "") return text_(null); // bare "/", no notice
  if (registry.includes(name)) {
    const rest = text.slice(first.end).replace(/^[ \t\n\r]+/, "");
    return { action: "run", command: name, args: rest === "" ? null : rest, notice: null };
  }
  return text_(UNKNOWN_NOTICE);
}

/**
 * Highlight plan for the whole message.
 * "executable" for the first token only when submitPlan would run it;
 * "text-only" for any later token that pickerState would treat as a command
 * name. A later token is never executable.
 */
export function highlightPlan(text, registry = []) {
  const out = [];
  const all = tokens(text);
  const plan = submitPlan(text, registry);
  all.forEach((tok, i) => {
    if (text[tok.start] !== "/") return;
    const name = text.slice(tok.start + 1, tok.end);
    if (name.startsWith("--")) return;
    if (i === 0) {
      if (plan.action === "run") out.push({ start: tok.start, end: tok.end, kind: "executable" });
      return;
    }
    if (matchesFor(name, registry).length > 0) {
      out.push({ start: tok.start, end: tok.end, kind: "text-only" });
    }
  });
  return out;
}

Test cases

Read this first. Both result columns describe the behaviour this issue ASKS FOR, after a fix.
They are not today's behaviour. Today, every row whose input has text before the / shows NO picker
at all in the desktop composer: that is the bug being reported. The rows where / is the first
character already behave as shown.

These are proposed expectations, not product test results. Run the same cases in terminal and desktop
composers. Use a test registry containing help, compact, plugin:skill, 9task, -task and équipe; he and unknowncmd are not registered. These are fixtures, not claims about installed commands.

How to read each row. Column 1 is typed character by character, with the caret left at the end, and
\n, \t and \r mean real whitespace. Column 2 answers one question: at that moment, should the
command list be on screen? Column 3 says what an explicit send should do, which is a separate action
from accepting an item in the list. "Sends as text" means the typed input goes to the model unchanged,
with nothing run and no slash-command error.

Rows whose input starts with / already behave this way today. Rows with text before the / do
not: today the list never appears there. Those rows are the fix being requested.

| What is typed | Today: does the list appear? | Wanted: should the list appear? | Wanted: what pressing send should do |
|---|---|---|---|
| /test-helpers /forge | No | Yes, filtered, marked text only | Sends as text |
| / | Yes, the whole list | Yes, listing every name | Sends as text |
| /he | Yes, and it filters as you type | Yes, filtered | Sends as text, with an unknown-name notice |
| /help | Yes, and it filters as you type | Yes | Runs help |
| /help topic | Yes on the /, rest not checked | No | Runs help with the rest as arguments |
| /unknowncmd | Yes on the /, rest not checked | No | Sends as text, with an unknown-name notice |
| /-- | Yes on the /, rest not checked | No | Sends as text |
| /-- doc comment | Yes on the /, rest not checked | No | Sends as text |
| /--suffix | Yes on the /, rest not checked | No | Sends as text |
| hello / | No | Yes | Sends as text |
| hello /he | No | Yes, filtered, marked text only | Sends as text |
| hello /help | No | Yes, marked text only | Sends as text, and no runnable highlight |
| hello /help now | No | No | Sends as text |
| hello /-- | No | No | Sends as text |
| line one\n/help | No | Yes, marked text only | Sends as text |
| word\t/help | No | Yes, marked text only | Sends as text |
| /help | No | Yes | Runs help |
| \t\r\n/help | No | Yes | Runs help |
| /-- | No | No | Sends as text, leading space kept |
| /help | Yes on the /, rest not checked | No | Runs help |
| hello/help, http://x, src/utils | No | No for each | Sends as text, each one |
| a/b, 1/2, and/or, C:\foo/bar | No | No for each | Sends as text, each one |
| (/help, "/help, =/help | No | No for each | Sends as text, each one |
| /b/c, /help/file, //comment | Yes on the /, rest not checked | No for each | Sends as text, each one |
| 3 / | No | Yes | Sends as text |
| 3 / 4 | No | No | Sends as text |
| /plugin:skill | Yes, and it filters as you type | Yes | Runs plugin:skill |
| use /plugin:skill | No | Yes, marked text only | Sends as text |
| use /plugin:skill now | No | No | Sends as text |
| /9task, /-task, /équipe | Yes on the /, rest not checked | Yes for each | Runs that exact name |
| /help /compact | Yes for /help, No for /compact | Yes, marked text only on /compact | Runs help only, /compact is an argument |
| prose /help /compact | No | Yes, marked text only | Sends as text, runs neither |

How the "Today" column was filled. Claude Desktop 2.2553.1.0, checked on 2026-09-21. Two
behaviours were observed and they cover every row: / as the first character of an empty box opens
the list, and anything before the / means the list never opens. Rows that start with / are marked
for the moment the / is typed; whether the list then stays open while the rest is typed was not
checked, and those cells say so. The send column was not measured today, apart from the fact that a
command name typed after other text does not run.

Also test events, not just final strings:

  • Type /, then --. The initially open picker closes; sending stays literal. Repeat mid-message. Repeat with a test registration named --: the literal guard must still win and the registry audit must flag it.
  • With an open picker on hello /help, move the caret to hello /he|lp (| marks the caret). It remains valid. Accept help: the result is exactly hello /help, with no duplicated suffix and no submit.
  • Complete hello /he before a following note. Only /he changes. The surrounding text stays intact and the inline text-only indication remains.
  • Press Escape, move outside the token, remove /, and paste a block containing /help on a later line. None may reopen the picker without a new typing event or explicit picker action. None executes an inline command on send.
  • Start with /help, then insert prose before it. Remove the executable highlight and show text-only status. Move the token back to the start and recompute both states.
  • Check the registry, including aliases and plugin namespaces, against the shared matcher. No independent name pattern may exclude a registered name except the explicit /-- reservation.
  • Keep the existing @ behavior unchanged. Compare @ after text before and after the change; do not route it through slash handling.

Risks of this rule

| Risk | Required check |
|---|---|
| Leading whitespace makes a command executable | Compare the proposed leading-space, tab and newline cases with existing submit behavior before release. |
| Unknown leading names go to the model as text | This may change typo handling. Verify the pre-send notice and the no-prefix-execution test. |
| A bare slash after a space opens during arithmetic | 3 / opens briefly; typing the next space closes it. Test the transition, not just 3 / 4. |
| An absolute path can resemble a registered command | /help is ambiguous. This proposal treats an exact leading name as a command; mid-message mentions stay text. |
| Completion is mistaken for skill attachment | Keep the inline text-only indication visible. Do not claim this adds attachment semantics. |
| Prefix matching or reserved names hide valid entries | Audit the registry, especially plugin names and /-- conflicts. |
| Terminal and desktop behavior diverges | Run the same trigger, submit and highlight tests on both. Do not assume shared code. |

Related issues

States below were read from the GitHub API on 2026-09-21. Numbers refer to anthropics/claude-code.

  • Closed: #44447, terminal mid-input menu; #44488, anywhere-in-prompt request, duplicate; #56002, start-only picker despite the verified 2.1.0 changelog entry.
  • Open: #77868, mid-sentence highlight without dispatch; #89398, picker requires first character but command still runs on submit.
  • Open: #89720, mid-prompt autocomplete; #89771 and #90727, desktop mid-input reports; #90441, position 0 versus @.
  • Open: #90102, claimed earlier support and fix; #91094, desktop 1.40609.0 with terminal unaffected; #91337, reported 2.1.246 to 2.1.247 boundary.
  • Open: #91163, request for chat:openCommandPicker; #91417, plugin-namespaced completion; #91442, desktop input already has text; #95268, terminal report whose title says #44447 closed without a fix.

What I could not establish

  • The underlying code, a shared terminal/desktop implementation, or a causal link to /-- handling.
  • Official intent. No reply from an Anthropic maintainer appears in any of the issues listed here.
  • Whether the 2.1.265 mid-prompt change reaches the terminal CLI. Tab was checked only on the desktop app, where it opens nothing.
  • Whether closing an issue fixed it. Closure text and comment authors were not reviewed for every issue listed.
  • The precise submit context in #89398, or where non-dispatched text in #77868 goes. Titles alone do not establish those details.
  • The existing leading-whitespace policy, unknown-command behavior, full command-name grammar, or @ boundary rules.

No product was run for this review. The test cases specify the proposed behavior; they are not evidence that it has shipped.

How to help this get fixed

  • If you see the same behaviour, add a thumbs-up reaction to this issue instead of a "+1" comment. It keeps the signal in one place.
  • If you can check a row marked "not checked" in the table above, post that row and what you saw, with your build number.
  • Suggested triage labels: bug, regression, has repro, platform:windows, area:desktop, area:skills.
  • Other reports of the same behaviour, so they can be linked or closed as duplicates of one thread: #89771, #91094, #91337, #90441, #89398, #91442, #90727, #89720.

View original on GitHub ↗