fix(claude): expand slash commands when a message has attachments - #8271

Closed
lnieuwenhuis wants to merge 3 commits into
pingdotgg:mainfrom
lnieuwenhuis:fix/skill-expansion-with-attachments
Closed

fix(claude): expand slash commands when a message has attachments#8271
lnieuwenhuis wants to merge 3 commits into
pingdotgg:mainfrom
lnieuwenhuis:fix/skill-expansion-with-attachments

Conversation

@lnieuwenhuis

@lnieuwenhuislnieuwenhuis commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

With the Claude provider, sending /skill-name args expands the command normally, but the identical message with any image attached silently does not expand: the agent receives the literal string plus the image, with no <command-name>, no skill body, and no error anywhere in the UI.

Root cause

It is on our side, not in the claude CLI.

The CLI accepts slash commands alongside images, but only when the last content block of the streamed user message is text. Reading the CLI bundle (verified in both 2.1.234 and 2.1.246), the stream-json input handler does:

letlast=blocks.at(-1);if(last?.type==="text"){inputString=last.text;precedingInputBlocks=blocks.slice(0,-1);}else{precedingInputBlocks=blocks;}
...
if(inputString!==null&&inputString.startsWith("/"))processSlashCommand(inputString,precedingInputBlocks, ...)

precedingInputBlocks is then carried through the expansion, so the image is preserved in both the command message and the expanded skill content.

buildUserMessageEffect in the Claude adapter pushed the text block first and appended image blocks after it. With no attachment the single text block is last and expansion works; with any attachment the last block is an image, inputString stays null, and the turn falls through to the plain-prompt path. That is exactly the reporter’s isolation table: only the attachment variable matters.

Fix

Emit the image blocks first and the text block last. This also matches Anthropic’s recommended image-before-text ordering, so it is a single code path with no special case for command-looking text.

Other providers

Unchanged, and not affected by this defect. Codex passes prompt text and attachments as separate fields to the codex app-server. Cursor and Grok go over ACP session/prompt, and OpenCode over its own parts API; none of them route prompt text through the Claude CLI’s slash expander, so block ordering has no equivalent meaning there.

Verification

  • vp test run apps/server/src/provider/Layers/ClaudeAdapter.test.ts — 79 passed. The new regression test asserts the adapter produces the same trailing /flow-patterns hello text block with and without an image attachment; it fails on the pre-fix adapter and passes after.
  • vp test run apps/server/src/provider/Layers/ProviderService.test.ts — passed.
  • vp run --filter t3 typecheck — 0 errors.
  • vp lint and vp fmt --check on the two touched files — clean (one pre-existing unrelated warning elsewhere in the file).

Fixes#8013

Implemented with Claude Opus via the Claude Code harness.


Note

Low Risk
Narrow change to Claude user-message block ordering with targeted tests; fixes incorrect behavior without touching auth or other providers.

Overview
Fixes a silent failure where Claude slash commands (e.g. /flow-patterns hello) stopped expanding when an image was attached—the agent got literal text plus the image with no error.

buildUserMessageEffect now emits image blocks before the final text block. The Claude CLI only treats streamed input as a slash command when the last content block is text; putting text first meant any attachment made the last block an image and skipped expansion.

Skill-dispatch behavior is unchanged in intent: optional leading prose, images in the middle, then trailing command or plain text. Tests add readPromptMessages, expect image-then-text for image turns, and regress on command + attachment ordering.

Reviewed by Cursor Bugbot for commit a1b9f67. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix Claude slash command expansion when message has image attachments

  • In buildUserMessageEffect in ClaudeAdapter.ts, image attachment blocks are now emitted before the command or prompt text block, so the dispatched slash command is the final content block.
  • Previously, ordinary prompt text was emitted before attachments, causing a slash command to be followed by an image and not recognized for expansion.
  • Adds tests in ClaudeAdapter.test.ts covering command-only and command-plus-image turns, plus an async readPromptMessages helper to inspect multiple queued prompt messages.
  • Risk: any code or tests that assumed text blocks precede image blocks in Claude user messages will need updating; the new ordering places image blocks first.

Macroscope summarized a1b9f67.

The Claude CLI only treats a streamed user message as a slash-command
invocation when the final content block is text; earlier blocks ride along
as preceding input blocks and survive the expansion. The adapter appended
image blocks after the text, so any turn carrying an attachment lost the
command silently.
Send the text block last so an image never displaces it.
@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 473dab35-2f0b-4a6b-b81a-4679a86ee481

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Aug 26, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Aug 26, 2026
@macroscopeapp

macroscopeappBot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at a1b9f67

Macroscope's review found this PR approvable — This is a focused Claude adapter bug fix that places image blocks before the final text block so existing slash commands continue to expand when attachments are present. The production impact is limited to Claude message ordering, with targeted regression coverage and no broader API, infrastructure, or default-setting changes.

You can add or adjust custom eligibility rules. Learn more.

@macroscopeapp
macroscopeappBot dismissed their stale reviewSeptember 2, 2026 09:09

Dismissing prior approval to re-evaluate a1b9f67

@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 Claude Fable 5.1 responding on behalf of Theo

Closing in favor of #9122, which just merged. It is this same fix rebased onto main, with your commit and authorship kept. The rebase needed a one-line test fix because attachmentRelativePath became nullable after this PR was opened, and it picked up a test for non-image file attachments. Thanks for tracking down the root cause in the CLI.

@t3dotggt3dotgg closed this Sep 3, 2026
@lnieuwenhuis
lnieuwenhuis deleted the fix/skill-expansion-with-attachments branch September 3, 2026 09:35
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S10-29 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Attaching an image silently disables slash-command/skill expansion for the whole message (Claude provider)

2 participants

@lnieuwenhuis@t3dotgg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(claude): expand slash commands when a message has attachments - #8271

Closed
lnieuwenhuis wants to merge 3 commits into
pingdotgg:mainfrom
lnieuwenhuis:fix/skill-expansion-with-attachments
Closed

fix(claude): expand slash commands when a message has attachments#8271
lnieuwenhuis wants to merge 3 commits into
pingdotgg:mainfrom
lnieuwenhuis:fix/skill-expansion-with-attachments

Conversation

@lnieuwenhuis

@lnieuwenhuislnieuwenhuis commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

With the Claude provider, sending /skill-name args expands the command normally, but the identical message with any image attached silently does not expand: the agent receives the literal string plus the image, with no <command-name>, no skill body, and no error anywhere in the UI.

Root cause

It is on our side, not in the claude CLI.

The CLI accepts slash commands alongside images, but only when the last content block of the streamed user message is text. Reading the CLI bundle (verified in both 2.1.234 and 2.1.246), the stream-json input handler does:

letlast=blocks.at(-1);if(last?.type==="text"){inputString=last.text;precedingInputBlocks=blocks.slice(0,-1);}else{precedingInputBlocks=blocks;}
...
if(inputString!==null&&inputString.startsWith("/"))processSlashCommand(inputString,precedingInputBlocks, ...)

precedingInputBlocks is then carried through the expansion, so the image is preserved in both the command message and the expanded skill content.

buildUserMessageEffect in the Claude adapter pushed the text block first and appended image blocks after it. With no attachment the single text block is last and expansion works; with any attachment the last block is an image, inputString stays null, and the turn falls through to the plain-prompt path. That is exactly the reporter’s isolation table: only the attachment variable matters.

Fix

Emit the image blocks first and the text block last. This also matches Anthropic’s recommended image-before-text ordering, so it is a single code path with no special case for command-looking text.

Other providers

Unchanged, and not affected by this defect. Codex passes prompt text and attachments as separate fields to the codex app-server. Cursor and Grok go over ACP session/prompt, and OpenCode over its own parts API; none of them route prompt text through the Claude CLI’s slash expander, so block ordering has no equivalent meaning there.

Verification

  • vp test run apps/server/src/provider/Layers/ClaudeAdapter.test.ts — 79 passed. The new regression test asserts the adapter produces the same trailing /flow-patterns hello text block with and without an image attachment; it fails on the pre-fix adapter and passes after.
  • vp test run apps/server/src/provider/Layers/ProviderService.test.ts — passed.
  • vp run --filter t3 typecheck — 0 errors.
  • vp lint and vp fmt --check on the two touched files — clean (one pre-existing unrelated warning elsewhere in the file).

Fixes#8013

Implemented with Claude Opus via the Claude Code harness.


Note

Low Risk
Narrow change to Claude user-message block ordering with targeted tests; fixes incorrect behavior without touching auth or other providers.

Overview
Fixes a silent failure where Claude slash commands (e.g. /flow-patterns hello) stopped expanding when an image was attached—the agent got literal text plus the image with no error.

buildUserMessageEffect now emits image blocks before the final text block. The Claude CLI only treats streamed input as a slash command when the last content block is text; putting text first meant any attachment made the last block an image and skipped expansion.

Skill-dispatch behavior is unchanged in intent: optional leading prose, images in the middle, then trailing command or plain text. Tests add readPromptMessages, expect image-then-text for image turns, and regress on command + attachment ordering.

Reviewed by Cursor Bugbot for commit a1b9f67. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix Claude slash command expansion when message has image attachments

  • In buildUserMessageEffect in ClaudeAdapter.ts, image attachment blocks are now emitted before the command or prompt text block, so the dispatched slash command is the final content block.
  • Previously, ordinary prompt text was emitted before attachments, causing a slash command to be followed by an image and not recognized for expansion.
  • Adds tests in ClaudeAdapter.test.ts covering command-only and command-plus-image turns, plus an async readPromptMessages helper to inspect multiple queued prompt messages.
  • Risk: any code or tests that assumed text blocks precede image blocks in Claude user messages will need updating; the new ordering places image blocks first.

Macroscope summarized a1b9f67.

The Claude CLI only treats a streamed user message as a slash-command
invocation when the final content block is text; earlier blocks ride along
as preceding input blocks and survive the expansion. The adapter appended
image blocks after the text, so any turn carrying an attachment lost the
command silently.
Send the text block last so an image never displaces it.
@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 473dab35-2f0b-4a6b-b81a-4679a86ee481

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Aug 26, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Aug 26, 2026
@macroscopeapp

macroscopeappBot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at a1b9f67

Macroscope's review found this PR approvable — This is a focused Claude adapter bug fix that places image blocks before the final text block so existing slash commands continue to expand when attachments are present. The production impact is limited to Claude message ordering, with targeted regression coverage and no broader API, infrastructure, or default-setting changes.

You can add or adjust custom eligibility rules. Learn more.

@macroscopeapp
macroscopeappBot dismissed their stale reviewSeptember 2, 2026 09:09

Dismissing prior approval to re-evaluate a1b9f67

@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 Claude Fable 5.1 responding on behalf of Theo

Closing in favor of #9122, which just merged. It is this same fix rebased onto main, with your commit and authorship kept. The rebase needed a one-line test fix because attachmentRelativePath became nullable after this PR was opened, and it picked up a test for non-image file attachments. Thanks for tracking down the root cause in the CLI.

@t3dotggt3dotgg closed this Sep 3, 2026
@lnieuwenhuis
lnieuwenhuis deleted the fix/skill-expansion-with-attachments branch September 3, 2026 09:35
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S10-29 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Attaching an image silently disables slash-command/skill expansion for the whole message (Claude provider)

2 participants

@lnieuwenhuis@t3dotgg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(claude): expand slash commands when a message has attachments - #8271

Closed
lnieuwenhuis wants to merge 3 commits into
pingdotgg:mainfrom
lnieuwenhuis:fix/skill-expansion-with-attachments
Closed

fix(claude): expand slash commands when a message has attachments#8271
lnieuwenhuis wants to merge 3 commits into
pingdotgg:mainfrom
lnieuwenhuis:fix/skill-expansion-with-attachments

Conversation

@lnieuwenhuis

@lnieuwenhuislnieuwenhuis commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

With the Claude provider, sending /skill-name args expands the command normally, but the identical message with any image attached silently does not expand: the agent receives the literal string plus the image, with no <command-name>, no skill body, and no error anywhere in the UI.

Root cause

It is on our side, not in the claude CLI.

The CLI accepts slash commands alongside images, but only when the last content block of the streamed user message is text. Reading the CLI bundle (verified in both 2.1.234 and 2.1.246), the stream-json input handler does:

letlast=blocks.at(-1);if(last?.type==="text"){inputString=last.text;precedingInputBlocks=blocks.slice(0,-1);}else{precedingInputBlocks=blocks;}
...
if(inputString!==null&&inputString.startsWith("/"))processSlashCommand(inputString,precedingInputBlocks, ...)

precedingInputBlocks is then carried through the expansion, so the image is preserved in both the command message and the expanded skill content.

buildUserMessageEffect in the Claude adapter pushed the text block first and appended image blocks after it. With no attachment the single text block is last and expansion works; with any attachment the last block is an image, inputString stays null, and the turn falls through to the plain-prompt path. That is exactly the reporter’s isolation table: only the attachment variable matters.

Fix

Emit the image blocks first and the text block last. This also matches Anthropic’s recommended image-before-text ordering, so it is a single code path with no special case for command-looking text.

Other providers

Unchanged, and not affected by this defect. Codex passes prompt text and attachments as separate fields to the codex app-server. Cursor and Grok go over ACP session/prompt, and OpenCode over its own parts API; none of them route prompt text through the Claude CLI’s slash expander, so block ordering has no equivalent meaning there.

Verification

  • vp test run apps/server/src/provider/Layers/ClaudeAdapter.test.ts — 79 passed. The new regression test asserts the adapter produces the same trailing /flow-patterns hello text block with and without an image attachment; it fails on the pre-fix adapter and passes after.
  • vp test run apps/server/src/provider/Layers/ProviderService.test.ts — passed.
  • vp run --filter t3 typecheck — 0 errors.
  • vp lint and vp fmt --check on the two touched files — clean (one pre-existing unrelated warning elsewhere in the file).

Fixes#8013

Implemented with Claude Opus via the Claude Code harness.


Note

Low Risk
Narrow change to Claude user-message block ordering with targeted tests; fixes incorrect behavior without touching auth or other providers.

Overview
Fixes a silent failure where Claude slash commands (e.g. /flow-patterns hello) stopped expanding when an image was attached—the agent got literal text plus the image with no error.

buildUserMessageEffect now emits image blocks before the final text block. The Claude CLI only treats streamed input as a slash command when the last content block is text; putting text first meant any attachment made the last block an image and skipped expansion.

Skill-dispatch behavior is unchanged in intent: optional leading prose, images in the middle, then trailing command or plain text. Tests add readPromptMessages, expect image-then-text for image turns, and regress on command + attachment ordering.

Reviewed by Cursor Bugbot for commit a1b9f67. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix Claude slash command expansion when message has image attachments

  • In buildUserMessageEffect in ClaudeAdapter.ts, image attachment blocks are now emitted before the command or prompt text block, so the dispatched slash command is the final content block.
  • Previously, ordinary prompt text was emitted before attachments, causing a slash command to be followed by an image and not recognized for expansion.
  • Adds tests in ClaudeAdapter.test.ts covering command-only and command-plus-image turns, plus an async readPromptMessages helper to inspect multiple queued prompt messages.
  • Risk: any code or tests that assumed text blocks precede image blocks in Claude user messages will need updating; the new ordering places image blocks first.

Macroscope summarized a1b9f67.

The Claude CLI only treats a streamed user message as a slash-command
invocation when the final content block is text; earlier blocks ride along
as preceding input blocks and survive the expansion. The adapter appended
image blocks after the text, so any turn carrying an attachment lost the
command silently.
Send the text block last so an image never displaces it.
@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 473dab35-2f0b-4a6b-b81a-4679a86ee481

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Aug 26, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Aug 26, 2026
@macroscopeapp

macroscopeappBot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at a1b9f67

Macroscope's review found this PR approvable — This is a focused Claude adapter bug fix that places image blocks before the final text block so existing slash commands continue to expand when attachments are present. The production impact is limited to Claude message ordering, with targeted regression coverage and no broader API, infrastructure, or default-setting changes.

You can add or adjust custom eligibility rules. Learn more.

@macroscopeapp
macroscopeappBot dismissed their stale reviewSeptember 2, 2026 09:09

Dismissing prior approval to re-evaluate a1b9f67

@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 Claude Fable 5.1 responding on behalf of Theo

Closing in favor of #9122, which just merged. It is this same fix rebased onto main, with your commit and authorship kept. The rebase needed a one-line test fix because attachmentRelativePath became nullable after this PR was opened, and it picked up a test for non-image file attachments. Thanks for tracking down the root cause in the CLI.

@t3dotggt3dotgg closed this Sep 3, 2026
@lnieuwenhuis
lnieuwenhuis deleted the fix/skill-expansion-with-attachments branch September 3, 2026 09:35
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S10-29 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Attaching an image silently disables slash-command/skill expansion for the whole message (Claude provider)

2 participants

@lnieuwenhuis@t3dotgg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(claude): expand slash commands when a message has attachments - #8271

Closed
lnieuwenhuis wants to merge 3 commits into
pingdotgg:mainfrom
lnieuwenhuis:fix/skill-expansion-with-attachments
Closed

fix(claude): expand slash commands when a message has attachments#8271
lnieuwenhuis wants to merge 3 commits into
pingdotgg:mainfrom
lnieuwenhuis:fix/skill-expansion-with-attachments

Conversation

@lnieuwenhuis

@lnieuwenhuislnieuwenhuis commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

With the Claude provider, sending /skill-name args expands the command normally, but the identical message with any image attached silently does not expand: the agent receives the literal string plus the image, with no <command-name>, no skill body, and no error anywhere in the UI.

Root cause

It is on our side, not in the claude CLI.

The CLI accepts slash commands alongside images, but only when the last content block of the streamed user message is text. Reading the CLI bundle (verified in both 2.1.234 and 2.1.246), the stream-json input handler does:

letlast=blocks.at(-1);if(last?.type==="text"){inputString=last.text;precedingInputBlocks=blocks.slice(0,-1);}else{precedingInputBlocks=blocks;}
...
if(inputString!==null&&inputString.startsWith("/"))processSlashCommand(inputString,precedingInputBlocks, ...)

precedingInputBlocks is then carried through the expansion, so the image is preserved in both the command message and the expanded skill content.

buildUserMessageEffect in the Claude adapter pushed the text block first and appended image blocks after it. With no attachment the single text block is last and expansion works; with any attachment the last block is an image, inputString stays null, and the turn falls through to the plain-prompt path. That is exactly the reporter’s isolation table: only the attachment variable matters.

Fix

Emit the image blocks first and the text block last. This also matches Anthropic’s recommended image-before-text ordering, so it is a single code path with no special case for command-looking text.

Other providers

Unchanged, and not affected by this defect. Codex passes prompt text and attachments as separate fields to the codex app-server. Cursor and Grok go over ACP session/prompt, and OpenCode over its own parts API; none of them route prompt text through the Claude CLI’s slash expander, so block ordering has no equivalent meaning there.

Verification

  • vp test run apps/server/src/provider/Layers/ClaudeAdapter.test.ts — 79 passed. The new regression test asserts the adapter produces the same trailing /flow-patterns hello text block with and without an image attachment; it fails on the pre-fix adapter and passes after.
  • vp test run apps/server/src/provider/Layers/ProviderService.test.ts — passed.
  • vp run --filter t3 typecheck — 0 errors.
  • vp lint and vp fmt --check on the two touched files — clean (one pre-existing unrelated warning elsewhere in the file).

Fixes#8013

Implemented with Claude Opus via the Claude Code harness.


Note

Low Risk
Narrow change to Claude user-message block ordering with targeted tests; fixes incorrect behavior without touching auth or other providers.

Overview
Fixes a silent failure where Claude slash commands (e.g. /flow-patterns hello) stopped expanding when an image was attached—the agent got literal text plus the image with no error.

buildUserMessageEffect now emits image blocks before the final text block. The Claude CLI only treats streamed input as a slash command when the last content block is text; putting text first meant any attachment made the last block an image and skipped expansion.

Skill-dispatch behavior is unchanged in intent: optional leading prose, images in the middle, then trailing command or plain text. Tests add readPromptMessages, expect image-then-text for image turns, and regress on command + attachment ordering.

Reviewed by Cursor Bugbot for commit a1b9f67. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix Claude slash command expansion when message has image attachments

  • In buildUserMessageEffect in ClaudeAdapter.ts, image attachment blocks are now emitted before the command or prompt text block, so the dispatched slash command is the final content block.
  • Previously, ordinary prompt text was emitted before attachments, causing a slash command to be followed by an image and not recognized for expansion.
  • Adds tests in ClaudeAdapter.test.ts covering command-only and command-plus-image turns, plus an async readPromptMessages helper to inspect multiple queued prompt messages.
  • Risk: any code or tests that assumed text blocks precede image blocks in Claude user messages will need updating; the new ordering places image blocks first.

Macroscope summarized a1b9f67.

The Claude CLI only treats a streamed user message as a slash-command
invocation when the final content block is text; earlier blocks ride along
as preceding input blocks and survive the expansion. The adapter appended
image blocks after the text, so any turn carrying an attachment lost the
command silently.
Send the text block last so an image never displaces it.
@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 473dab35-2f0b-4a6b-b81a-4679a86ee481

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Aug 26, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Aug 26, 2026
@macroscopeapp

macroscopeappBot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at a1b9f67

Macroscope's review found this PR approvable — This is a focused Claude adapter bug fix that places image blocks before the final text block so existing slash commands continue to expand when attachments are present. The production impact is limited to Claude message ordering, with targeted regression coverage and no broader API, infrastructure, or default-setting changes.

You can add or adjust custom eligibility rules. Learn more.

@macroscopeapp
macroscopeappBot dismissed their stale reviewSeptember 2, 2026 09:09

Dismissing prior approval to re-evaluate a1b9f67

@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 Claude Fable 5.1 responding on behalf of Theo

Closing in favor of #9122, which just merged. It is this same fix rebased onto main, with your commit and authorship kept. The rebase needed a one-line test fix because attachmentRelativePath became nullable after this PR was opened, and it picked up a test for non-image file attachments. Thanks for tracking down the root cause in the CLI.

@t3dotggt3dotgg closed this Sep 3, 2026
@lnieuwenhuis
lnieuwenhuis deleted the fix/skill-expansion-with-attachments branch September 3, 2026 09:35
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S10-29 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Attaching an image silently disables slash-command/skill expansion for the whole message (Claude provider)

2 participants

@lnieuwenhuis@t3dotgg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(claude): expand slash commands when a message has attachments - #8271

Closed
lnieuwenhuis wants to merge 3 commits into
pingdotgg:mainfrom
lnieuwenhuis:fix/skill-expansion-with-attachments
Closed

fix(claude): expand slash commands when a message has attachments#8271
lnieuwenhuis wants to merge 3 commits into
pingdotgg:mainfrom
lnieuwenhuis:fix/skill-expansion-with-attachments

Conversation

@lnieuwenhuis

@lnieuwenhuislnieuwenhuis commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

With the Claude provider, sending /skill-name args expands the command normally, but the identical message with any image attached silently does not expand: the agent receives the literal string plus the image, with no <command-name>, no skill body, and no error anywhere in the UI.

Root cause

It is on our side, not in the claude CLI.

The CLI accepts slash commands alongside images, but only when the last content block of the streamed user message is text. Reading the CLI bundle (verified in both 2.1.234 and 2.1.246), the stream-json input handler does:

letlast=blocks.at(-1);if(last?.type==="text"){inputString=last.text;precedingInputBlocks=blocks.slice(0,-1);}else{precedingInputBlocks=blocks;}
...
if(inputString!==null&&inputString.startsWith("/"))processSlashCommand(inputString,precedingInputBlocks, ...)

precedingInputBlocks is then carried through the expansion, so the image is preserved in both the command message and the expanded skill content.

buildUserMessageEffect in the Claude adapter pushed the text block first and appended image blocks after it. With no attachment the single text block is last and expansion works; with any attachment the last block is an image, inputString stays null, and the turn falls through to the plain-prompt path. That is exactly the reporter’s isolation table: only the attachment variable matters.

Fix

Emit the image blocks first and the text block last. This also matches Anthropic’s recommended image-before-text ordering, so it is a single code path with no special case for command-looking text.

Other providers

Unchanged, and not affected by this defect. Codex passes prompt text and attachments as separate fields to the codex app-server. Cursor and Grok go over ACP session/prompt, and OpenCode over its own parts API; none of them route prompt text through the Claude CLI’s slash expander, so block ordering has no equivalent meaning there.

Verification

  • vp test run apps/server/src/provider/Layers/ClaudeAdapter.test.ts — 79 passed. The new regression test asserts the adapter produces the same trailing /flow-patterns hello text block with and without an image attachment; it fails on the pre-fix adapter and passes after.
  • vp test run apps/server/src/provider/Layers/ProviderService.test.ts — passed.
  • vp run --filter t3 typecheck — 0 errors.
  • vp lint and vp fmt --check on the two touched files — clean (one pre-existing unrelated warning elsewhere in the file).

Fixes#8013

Implemented with Claude Opus via the Claude Code harness.


Note

Low Risk
Narrow change to Claude user-message block ordering with targeted tests; fixes incorrect behavior without touching auth or other providers.

Overview
Fixes a silent failure where Claude slash commands (e.g. /flow-patterns hello) stopped expanding when an image was attached—the agent got literal text plus the image with no error.

buildUserMessageEffect now emits image blocks before the final text block. The Claude CLI only treats streamed input as a slash command when the last content block is text; putting text first meant any attachment made the last block an image and skipped expansion.

Skill-dispatch behavior is unchanged in intent: optional leading prose, images in the middle, then trailing command or plain text. Tests add readPromptMessages, expect image-then-text for image turns, and regress on command + attachment ordering.

Reviewed by Cursor Bugbot for commit a1b9f67. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix Claude slash command expansion when message has image attachments

  • In buildUserMessageEffect in ClaudeAdapter.ts, image attachment blocks are now emitted before the command or prompt text block, so the dispatched slash command is the final content block.
  • Previously, ordinary prompt text was emitted before attachments, causing a slash command to be followed by an image and not recognized for expansion.
  • Adds tests in ClaudeAdapter.test.ts covering command-only and command-plus-image turns, plus an async readPromptMessages helper to inspect multiple queued prompt messages.
  • Risk: any code or tests that assumed text blocks precede image blocks in Claude user messages will need updating; the new ordering places image blocks first.

Macroscope summarized a1b9f67.

The Claude CLI only treats a streamed user message as a slash-command
invocation when the final content block is text; earlier blocks ride along
as preceding input blocks and survive the expansion. The adapter appended
image blocks after the text, so any turn carrying an attachment lost the
command silently.
Send the text block last so an image never displaces it.
@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 473dab35-2f0b-4a6b-b81a-4679a86ee481

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Aug 26, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Aug 26, 2026
@macroscopeapp

macroscopeappBot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at a1b9f67

Macroscope's review found this PR approvable — This is a focused Claude adapter bug fix that places image blocks before the final text block so existing slash commands continue to expand when attachments are present. The production impact is limited to Claude message ordering, with targeted regression coverage and no broader API, infrastructure, or default-setting changes.

You can add or adjust custom eligibility rules. Learn more.

@macroscopeapp
macroscopeappBot dismissed their stale reviewSeptember 2, 2026 09:09

Dismissing prior approval to re-evaluate a1b9f67

@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 Claude Fable 5.1 responding on behalf of Theo

Closing in favor of #9122, which just merged. It is this same fix rebased onto main, with your commit and authorship kept. The rebase needed a one-line test fix because attachmentRelativePath became nullable after this PR was opened, and it picked up a test for non-image file attachments. Thanks for tracking down the root cause in the CLI.

@t3dotggt3dotgg closed this Sep 3, 2026
@lnieuwenhuis
lnieuwenhuis deleted the fix/skill-expansion-with-attachments branch September 3, 2026 09:35
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S10-29 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Attaching an image silently disables slash-command/skill expansion for the whole message (Claude provider)

2 participants

@lnieuwenhuis@t3dotgg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(claude): expand slash commands when a message has attachments - #8271

Closed
lnieuwenhuis wants to merge 3 commits into
pingdotgg:mainfrom
lnieuwenhuis:fix/skill-expansion-with-attachments
Closed

fix(claude): expand slash commands when a message has attachments#8271
lnieuwenhuis wants to merge 3 commits into
pingdotgg:mainfrom
lnieuwenhuis:fix/skill-expansion-with-attachments

Conversation

@lnieuwenhuis

@lnieuwenhuislnieuwenhuis commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

With the Claude provider, sending /skill-name args expands the command normally, but the identical message with any image attached silently does not expand: the agent receives the literal string plus the image, with no <command-name>, no skill body, and no error anywhere in the UI.

Root cause

It is on our side, not in the claude CLI.

The CLI accepts slash commands alongside images, but only when the last content block of the streamed user message is text. Reading the CLI bundle (verified in both 2.1.234 and 2.1.246), the stream-json input handler does:

letlast=blocks.at(-1);if(last?.type==="text"){inputString=last.text;precedingInputBlocks=blocks.slice(0,-1);}else{precedingInputBlocks=blocks;}
...
if(inputString!==null&&inputString.startsWith("/"))processSlashCommand(inputString,precedingInputBlocks, ...)

precedingInputBlocks is then carried through the expansion, so the image is preserved in both the command message and the expanded skill content.

buildUserMessageEffect in the Claude adapter pushed the text block first and appended image blocks after it. With no attachment the single text block is last and expansion works; with any attachment the last block is an image, inputString stays null, and the turn falls through to the plain-prompt path. That is exactly the reporter’s isolation table: only the attachment variable matters.

Fix

Emit the image blocks first and the text block last. This also matches Anthropic’s recommended image-before-text ordering, so it is a single code path with no special case for command-looking text.

Other providers

Unchanged, and not affected by this defect. Codex passes prompt text and attachments as separate fields to the codex app-server. Cursor and Grok go over ACP session/prompt, and OpenCode over its own parts API; none of them route prompt text through the Claude CLI’s slash expander, so block ordering has no equivalent meaning there.

Verification

  • vp test run apps/server/src/provider/Layers/ClaudeAdapter.test.ts — 79 passed. The new regression test asserts the adapter produces the same trailing /flow-patterns hello text block with and without an image attachment; it fails on the pre-fix adapter and passes after.
  • vp test run apps/server/src/provider/Layers/ProviderService.test.ts — passed.
  • vp run --filter t3 typecheck — 0 errors.
  • vp lint and vp fmt --check on the two touched files — clean (one pre-existing unrelated warning elsewhere in the file).

Fixes#8013

Implemented with Claude Opus via the Claude Code harness.


Note

Low Risk
Narrow change to Claude user-message block ordering with targeted tests; fixes incorrect behavior without touching auth or other providers.

Overview
Fixes a silent failure where Claude slash commands (e.g. /flow-patterns hello) stopped expanding when an image was attached—the agent got literal text plus the image with no error.

buildUserMessageEffect now emits image blocks before the final text block. The Claude CLI only treats streamed input as a slash command when the last content block is text; putting text first meant any attachment made the last block an image and skipped expansion.

Skill-dispatch behavior is unchanged in intent: optional leading prose, images in the middle, then trailing command or plain text. Tests add readPromptMessages, expect image-then-text for image turns, and regress on command + attachment ordering.

Reviewed by Cursor Bugbot for commit a1b9f67. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix Claude slash command expansion when message has image attachments

  • In buildUserMessageEffect in ClaudeAdapter.ts, image attachment blocks are now emitted before the command or prompt text block, so the dispatched slash command is the final content block.
  • Previously, ordinary prompt text was emitted before attachments, causing a slash command to be followed by an image and not recognized for expansion.
  • Adds tests in ClaudeAdapter.test.ts covering command-only and command-plus-image turns, plus an async readPromptMessages helper to inspect multiple queued prompt messages.
  • Risk: any code or tests that assumed text blocks precede image blocks in Claude user messages will need updating; the new ordering places image blocks first.

Macroscope summarized a1b9f67.

The Claude CLI only treats a streamed user message as a slash-command
invocation when the final content block is text; earlier blocks ride along
as preceding input blocks and survive the expansion. The adapter appended
image blocks after the text, so any turn carrying an attachment lost the
command silently.
Send the text block last so an image never displaces it.
@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 473dab35-2f0b-4a6b-b81a-4679a86ee481

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Aug 26, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Aug 26, 2026
@macroscopeapp

macroscopeappBot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at a1b9f67

Macroscope's review found this PR approvable — This is a focused Claude adapter bug fix that places image blocks before the final text block so existing slash commands continue to expand when attachments are present. The production impact is limited to Claude message ordering, with targeted regression coverage and no broader API, infrastructure, or default-setting changes.

You can add or adjust custom eligibility rules. Learn more.

@macroscopeapp
macroscopeappBot dismissed their stale reviewSeptember 2, 2026 09:09

Dismissing prior approval to re-evaluate a1b9f67

@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 Claude Fable 5.1 responding on behalf of Theo

Closing in favor of #9122, which just merged. It is this same fix rebased onto main, with your commit and authorship kept. The rebase needed a one-line test fix because attachmentRelativePath became nullable after this PR was opened, and it picked up a test for non-image file attachments. Thanks for tracking down the root cause in the CLI.

@t3dotggt3dotgg closed this Sep 3, 2026
@lnieuwenhuis
lnieuwenhuis deleted the fix/skill-expansion-with-attachments branch September 3, 2026 09:35
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S10-29 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Attaching an image silently disables slash-command/skill expansion for the whole message (Claude provider)

2 participants

@lnieuwenhuis@t3dotgg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(claude): expand slash commands when a message has attachments - #8271

Closed
lnieuwenhuis wants to merge 3 commits into
pingdotgg:mainfrom
lnieuwenhuis:fix/skill-expansion-with-attachments
Closed

fix(claude): expand slash commands when a message has attachments#8271
lnieuwenhuis wants to merge 3 commits into
pingdotgg:mainfrom
lnieuwenhuis:fix/skill-expansion-with-attachments

Conversation

@lnieuwenhuis

@lnieuwenhuislnieuwenhuis commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

With the Claude provider, sending /skill-name args expands the command normally, but the identical message with any image attached silently does not expand: the agent receives the literal string plus the image, with no <command-name>, no skill body, and no error anywhere in the UI.

Root cause

It is on our side, not in the claude CLI.

The CLI accepts slash commands alongside images, but only when the last content block of the streamed user message is text. Reading the CLI bundle (verified in both 2.1.234 and 2.1.246), the stream-json input handler does:

letlast=blocks.at(-1);if(last?.type==="text"){inputString=last.text;precedingInputBlocks=blocks.slice(0,-1);}else{precedingInputBlocks=blocks;}
...
if(inputString!==null&&inputString.startsWith("/"))processSlashCommand(inputString,precedingInputBlocks, ...)

precedingInputBlocks is then carried through the expansion, so the image is preserved in both the command message and the expanded skill content.

buildUserMessageEffect in the Claude adapter pushed the text block first and appended image blocks after it. With no attachment the single text block is last and expansion works; with any attachment the last block is an image, inputString stays null, and the turn falls through to the plain-prompt path. That is exactly the reporter’s isolation table: only the attachment variable matters.

Fix

Emit the image blocks first and the text block last. This also matches Anthropic’s recommended image-before-text ordering, so it is a single code path with no special case for command-looking text.

Other providers

Unchanged, and not affected by this defect. Codex passes prompt text and attachments as separate fields to the codex app-server. Cursor and Grok go over ACP session/prompt, and OpenCode over its own parts API; none of them route prompt text through the Claude CLI’s slash expander, so block ordering has no equivalent meaning there.

Verification

  • vp test run apps/server/src/provider/Layers/ClaudeAdapter.test.ts — 79 passed. The new regression test asserts the adapter produces the same trailing /flow-patterns hello text block with and without an image attachment; it fails on the pre-fix adapter and passes after.
  • vp test run apps/server/src/provider/Layers/ProviderService.test.ts — passed.
  • vp run --filter t3 typecheck — 0 errors.
  • vp lint and vp fmt --check on the two touched files — clean (one pre-existing unrelated warning elsewhere in the file).

Fixes#8013

Implemented with Claude Opus via the Claude Code harness.


Note

Low Risk
Narrow change to Claude user-message block ordering with targeted tests; fixes incorrect behavior without touching auth or other providers.

Overview
Fixes a silent failure where Claude slash commands (e.g. /flow-patterns hello) stopped expanding when an image was attached—the agent got literal text plus the image with no error.

buildUserMessageEffect now emits image blocks before the final text block. The Claude CLI only treats streamed input as a slash command when the last content block is text; putting text first meant any attachment made the last block an image and skipped expansion.

Skill-dispatch behavior is unchanged in intent: optional leading prose, images in the middle, then trailing command or plain text. Tests add readPromptMessages, expect image-then-text for image turns, and regress on command + attachment ordering.

Reviewed by Cursor Bugbot for commit a1b9f67. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix Claude slash command expansion when message has image attachments

  • In buildUserMessageEffect in ClaudeAdapter.ts, image attachment blocks are now emitted before the command or prompt text block, so the dispatched slash command is the final content block.
  • Previously, ordinary prompt text was emitted before attachments, causing a slash command to be followed by an image and not recognized for expansion.
  • Adds tests in ClaudeAdapter.test.ts covering command-only and command-plus-image turns, plus an async readPromptMessages helper to inspect multiple queued prompt messages.
  • Risk: any code or tests that assumed text blocks precede image blocks in Claude user messages will need updating; the new ordering places image blocks first.

Macroscope summarized a1b9f67.

The Claude CLI only treats a streamed user message as a slash-command
invocation when the final content block is text; earlier blocks ride along
as preceding input blocks and survive the expansion. The adapter appended
image blocks after the text, so any turn carrying an attachment lost the
command silently.
Send the text block last so an image never displaces it.
@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 473dab35-2f0b-4a6b-b81a-4679a86ee481

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Aug 26, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Aug 26, 2026
@macroscopeapp

macroscopeappBot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at a1b9f67

Macroscope's review found this PR approvable — This is a focused Claude adapter bug fix that places image blocks before the final text block so existing slash commands continue to expand when attachments are present. The production impact is limited to Claude message ordering, with targeted regression coverage and no broader API, infrastructure, or default-setting changes.

You can add or adjust custom eligibility rules. Learn more.

@macroscopeapp
macroscopeappBot dismissed their stale reviewSeptember 2, 2026 09:09

Dismissing prior approval to re-evaluate a1b9f67

@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 Claude Fable 5.1 responding on behalf of Theo

Closing in favor of #9122, which just merged. It is this same fix rebased onto main, with your commit and authorship kept. The rebase needed a one-line test fix because attachmentRelativePath became nullable after this PR was opened, and it picked up a test for non-image file attachments. Thanks for tracking down the root cause in the CLI.

@t3dotggt3dotgg closed this Sep 3, 2026
@lnieuwenhuis
lnieuwenhuis deleted the fix/skill-expansion-with-attachments branch September 3, 2026 09:35
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S10-29 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Attaching an image silently disables slash-command/skill expansion for the whole message (Claude provider)

2 participants

@lnieuwenhuis@t3dotgg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(claude): expand slash commands when a message has attachments - #8271

Closed
lnieuwenhuis wants to merge 3 commits into
pingdotgg:mainfrom
lnieuwenhuis:fix/skill-expansion-with-attachments
Closed

fix(claude): expand slash commands when a message has attachments#8271
lnieuwenhuis wants to merge 3 commits into
pingdotgg:mainfrom
lnieuwenhuis:fix/skill-expansion-with-attachments

Conversation

@lnieuwenhuis

@lnieuwenhuislnieuwenhuis commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

With the Claude provider, sending /skill-name args expands the command normally, but the identical message with any image attached silently does not expand: the agent receives the literal string plus the image, with no <command-name>, no skill body, and no error anywhere in the UI.

Root cause

It is on our side, not in the claude CLI.

The CLI accepts slash commands alongside images, but only when the last content block of the streamed user message is text. Reading the CLI bundle (verified in both 2.1.234 and 2.1.246), the stream-json input handler does:

letlast=blocks.at(-1);if(last?.type==="text"){inputString=last.text;precedingInputBlocks=blocks.slice(0,-1);}else{precedingInputBlocks=blocks;}
...
if(inputString!==null&&inputString.startsWith("/"))processSlashCommand(inputString,precedingInputBlocks, ...)

precedingInputBlocks is then carried through the expansion, so the image is preserved in both the command message and the expanded skill content.

buildUserMessageEffect in the Claude adapter pushed the text block first and appended image blocks after it. With no attachment the single text block is last and expansion works; with any attachment the last block is an image, inputString stays null, and the turn falls through to the plain-prompt path. That is exactly the reporter’s isolation table: only the attachment variable matters.

Fix

Emit the image blocks first and the text block last. This also matches Anthropic’s recommended image-before-text ordering, so it is a single code path with no special case for command-looking text.

Other providers

Unchanged, and not affected by this defect. Codex passes prompt text and attachments as separate fields to the codex app-server. Cursor and Grok go over ACP session/prompt, and OpenCode over its own parts API; none of them route prompt text through the Claude CLI’s slash expander, so block ordering has no equivalent meaning there.

Verification

  • vp test run apps/server/src/provider/Layers/ClaudeAdapter.test.ts — 79 passed. The new regression test asserts the adapter produces the same trailing /flow-patterns hello text block with and without an image attachment; it fails on the pre-fix adapter and passes after.
  • vp test run apps/server/src/provider/Layers/ProviderService.test.ts — passed.
  • vp run --filter t3 typecheck — 0 errors.
  • vp lint and vp fmt --check on the two touched files — clean (one pre-existing unrelated warning elsewhere in the file).

Fixes#8013

Implemented with Claude Opus via the Claude Code harness.


Note

Low Risk
Narrow change to Claude user-message block ordering with targeted tests; fixes incorrect behavior without touching auth or other providers.

Overview
Fixes a silent failure where Claude slash commands (e.g. /flow-patterns hello) stopped expanding when an image was attached—the agent got literal text plus the image with no error.

buildUserMessageEffect now emits image blocks before the final text block. The Claude CLI only treats streamed input as a slash command when the last content block is text; putting text first meant any attachment made the last block an image and skipped expansion.

Skill-dispatch behavior is unchanged in intent: optional leading prose, images in the middle, then trailing command or plain text. Tests add readPromptMessages, expect image-then-text for image turns, and regress on command + attachment ordering.

Reviewed by Cursor Bugbot for commit a1b9f67. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix Claude slash command expansion when message has image attachments

  • In buildUserMessageEffect in ClaudeAdapter.ts, image attachment blocks are now emitted before the command or prompt text block, so the dispatched slash command is the final content block.
  • Previously, ordinary prompt text was emitted before attachments, causing a slash command to be followed by an image and not recognized for expansion.
  • Adds tests in ClaudeAdapter.test.ts covering command-only and command-plus-image turns, plus an async readPromptMessages helper to inspect multiple queued prompt messages.
  • Risk: any code or tests that assumed text blocks precede image blocks in Claude user messages will need updating; the new ordering places image blocks first.

Macroscope summarized a1b9f67.

The Claude CLI only treats a streamed user message as a slash-command
invocation when the final content block is text; earlier blocks ride along
as preceding input blocks and survive the expansion. The adapter appended
image blocks after the text, so any turn carrying an attachment lost the
command silently.
Send the text block last so an image never displaces it.
@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 473dab35-2f0b-4a6b-b81a-4679a86ee481

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Aug 26, 2026
macroscopeapp[bot]
macroscopeappBot previously approved these changes Aug 26, 2026
@macroscopeapp

macroscopeappBot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at a1b9f67

Macroscope's review found this PR approvable — This is a focused Claude adapter bug fix that places image blocks before the final text block so existing slash commands continue to expand when attachments are present. The production impact is limited to Claude message ordering, with targeted regression coverage and no broader API, infrastructure, or default-setting changes.

You can add or adjust custom eligibility rules. Learn more.

@macroscopeapp
macroscopeappBot dismissed their stale reviewSeptember 2, 2026 09:09

Dismissing prior approval to re-evaluate a1b9f67

@t3dotgg

Copy link
Copy Markdown
Member

Note

🤖 Claude Fable 5.1 responding on behalf of Theo

Closing in favor of #9122, which just merged. It is this same fix rebased onto main, with your commit and authorship kept. The rebase needed a one-line test fix because attachmentRelativePath became nullable after this PR was opened, and it picked up a test for non-image file attachments. Thanks for tracking down the root cause in the CLI.

@t3dotggt3dotgg closed this Sep 3, 2026
@lnieuwenhuis
lnieuwenhuis deleted the fix/skill-expansion-with-attachments branch September 3, 2026 09:35
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S10-29 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Attaching an image silently disables slash-command/skill expansion for the whole message (Claude provider)

2 participants

@lnieuwenhuis@t3dotgg