[BUG] /goal long prompt has no Show less control and hides task status/messages

Status Open
Maintainer reply None cached
Activity 5 comments · opened May 23, 2026

Preflight Checklist

  • [x] I searched existing issues for /goal, Show less, collapse behavior, and long-prompt UI behavior.
  • [x] This is a single bug report.
  • [ ] Exact Claude Code version is included below. It is not visible in the screenshots.

What's Wrong?

When /goal is used with a large but valid prompt, around the expected 4,000-character range, the goal prompt is rendered as a large expanded block in the conversation with no visible Show less / collapse control.

In the app UI, the long goal prompt can fill most of the viewport. New messages and status updates are pushed out of sight or appear hidden behind the prompt/input area, so the user cannot reliably tell whether Claude is still running, blocked, or completed.

Observed behavior:

  • Long /goal text remains expanded.
  • No Show less or collapse affordance is visible for the goal prompt.
  • The latest activity/status is not reliably visible without scrolling/guessing.
  • The input box remains visible at the bottom, but the active/completion state is obscured by the large prompt content.

What Should Happen?

Long /goal prompts should be bounded in the UI, for example by:

  • rendering collapsed by default after a reasonable number of lines,
  • showing Show more / Show less controls like other long content blocks, and/or
  • using a max-height scrollable container for the goal text.

The latest agent status/messages and completion state should remain visible or easy to reach while the goal is active.

Error Messages/Logs

No error message. This is a UI visibility/state-awareness issue.

Steps to Reproduce

  1. Open Claude Code in the app UI / Code tab.
  2. Submit a /goal prompt with a long multi-paragraph task description, still within the expected /goal input limit, approximately 4,000 characters.
  3. Let the task start running.
  4. Observe the goal prompt block in the conversation.
  5. The block stays expanded with no Show less control.
  6. Subsequent messages/status are pushed out of view or obscured.
  7. The user cannot reliably tell whether the task is still running, blocked, or completed without scrolling around.

Claude Model

Not sure / Multiple models

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

Unknown from screenshot. Please advise where to capture the exact app/CLI version if needed.

Platform

Claude Code app UI / Code tab

Operating System

macOS

Terminal/Shell

Other

Additional Information

This is related to long /goal prompts, but distinct from prompt-length/evaluator errors such as #58192 and #60966. In this case, the prompt is accepted and the task may be running; the problem is that the visible UI has no collapse affordance and hides the current conversation/status behind the prompt.

Refiled from the correct GitHub account after closing #61674.

View original on GitHub ↗

5 Comments

piercephilip981-glitch · 2 months ago

Seeing this exact problem in the VS Code native extension, and it is not specific to /goalany long pasted prompt triggers it. When I paste a multi-paragraph handoff/kickoff prompt into the chat, the user-message block expands to fill the whole panel with no collapse / Show less control, pushing Claude's responses and tool activity out of view. I cannot tell whether it is running, blocked, or done without scrolling past the giant bubble.

Repro (VS Code native extension, Code tab — not the terminal CLI):

  1. Paste a long multi-paragraph prompt (a few thousand characters) into the chat input and submit.
  2. The pasted user message renders as a full-height expanded block.
  3. New assistant messages / tool calls / status updates are pushed below the fold and obscured.
  4. There is no caret, Show less, or max-height scroll container on the user message.

Requested fix (same as the OP): collapse long user messages by default past N lines with Show more / Show less, or cap them in a max-height scrollable container, so the latest assistant activity stays visible. This should apply to all long user messages, not just /goal.

Platform: VS Code native extension (Code tab).

Zaclin-GIT · 2 months ago

Specifically for the VS Code extension

Here's a guide I had Claude generate to fix this issue for myself locally, which looks to have worked like a charm. Hope this helps anyone else struggling with this.

Patch: "Show less" collapse for long /goal & /loop slash-command prompts

What this fixes

In the Claude Code VS Code extension, regular user messages collapse with a
"Show more" / "Show less" control once they get tall. But slash-command
prompts
(e.g. /goal, /loop) rendered through a different code path that
skipped the collapsible wrapper, so a long prompt (~4,000 chars) filled the whole
chat window and pushed task status / latest messages out of view.

Reference: GitHub issue anthropics/claude-code#61675
([BUG] /goal long prompt has no Show less control and hides task status/messages).

The fix routes the slash-command branch through the extension's existing
collapsible component so these prompts collapse just like normal messages
(default ~60px, with Show more / Show less).

Applied successfully to versions 2.1.183 and 2.1.185.

---

File to edit

C:\Users\user\.vscode\extensions\anthropic.claude-code-<VERSION>-win32-x64\webview\index.js

This is a minified production bundle (~4.8 MB, effectively a few enormous
lines). Editing it is surgical. Two important caveats:

  1. Every extension update creates a new folder (...-<new version>-win32-x64)

with a fresh, unpatched bundle. You must re-apply this patch in the new folder.
Only the latest installed version actually runs.

  1. Minified identifiers can change between builds. In 2.1.183/2.1.185 the

relevant names were JQe (collapsible component), Rc (its React import),
wg (its CSS-module map), qi (chat CSS-module map), ey (content renderer),
and locals S / y / o / w in the message renderer. In a future build
these letters may differ. Section "If the literal strings don't match"
below explains how to re-derive them.

---

How it works (the mechanism)

  • There is a reusable collapsible component (minified JQe, internally an

ExpandableContent). It measures scrollHeight; if it exceeds maxHeight
it renders the content collapsed with a truncation gradient + a hover
"Show more" button, and a "Show less" button when expanded.

  • Regular text user messages already wrap their content in it:

createElement(JQe,{content:y,context:o,maxHeight:60}).

  • The slash-command branch instead did an early return that dumped the raw

text with no wrapper:
case"text":if(S.isSlashCommand)return createElement("div",{className:"userMessage slashCommandMessage"...},S.text);

Key subtlety: S.text is a transformed string. The normalizer (ny)
runs w8t(rawText) which extracts <command-name> + <command-args> from the
raw message. So you cannot just pass the message object y to JQe — its
internal renderer (ey) would re-render the raw XML-wrapped text. The fix
therefore feeds the already-transformed string S.text into JQe via a new
optional prop, instead of letting JQe render from the message object.

The patch generalizes JQe with two optional, backward-compatible props:

  • scChildren — a pre-rendered child (string or node) to show instead of the

default ey render.

  • scClass — extra class names applied to the inner content div (to keep the

slashCommandMessage monospace styling).

When both are absent, JQe behaves exactly as before (regular messages
unaffected).

---

The three edits (exact strings — fast path for 2.1.18x)

Edit 1 — generalize JQe's signature + re-measure when the new child changes

Find:

function JQe({content:e,context:t,maxHeight:i=250}){let n=Rc.useRef(null),[o,r]=Rc.useState(!1),[s,a]=Rc.useState(!1),[l,c]=Rc.useState(!1);Rc.useEffect(()=>{if(n.current){let u=n.current.scrollHeight;r(u>i)}},[i,e]);

Replace with:

function JQe({content:e,context:t,maxHeight:i=250,scChildren,scClass}){let n=Rc.useRef(null),[o,r]=Rc.useState(!1),[s,a]=Rc.useState(!1),[l,c]=Rc.useState(!1);Rc.useEffect(()=>{if(n.current){let u=n.current.scrollHeight;r(u>i)}},[i,e,scChildren]);

(Added ,scChildren,scClass to the destructured params, and ,scChildren to the effect deps.)

Edit 2 — render the optional child + extra class instead of always using ey

Find:

Rc.default.createElement("div",{ref:n,className:`${wg.content} ${!s&&o?wg.collapsed:""}`,style:!s&&o?{maxHeight:`${i}px`}:void 0},Rc.default.createElement(ey,{content:e,context:t,plainText:!0}),

Replace with:

Rc.default.createElement("div",{ref:n,className:`${wg.content} ${scClass||""} ${!s&&o?wg.collapsed:""}`,style:!s&&o?{maxHeight:`${i}px`}:void 0},scChildren!=null?scChildren:Rc.default.createElement(ey,{content:e,context:t,plainText:!0}),

(Inserted ${scClass||""} into the className, and scChildren!=null?scChildren: before the ey element.)

Edit 3 — route the slash-command branch through JQe

Find:

case"text":if(S.isSlashCommand)return je.default.createElement("div",{className:`${qi.userMessage} ${qi.slashCommandMessage}`,key:w},S.text);break

Replace with:

case"text":if(S.isSlashCommand)return je.default.createElement(JQe,{key:w,context:o,maxHeight:60,scClass:`${qi.userMessage} ${qi.slashCommandMessage}`,scChildren:S.text});break
The maxHeight:60 matches the value used for regular user messages, so slash commands collapse consistently. Bump it if you want slash prompts to show more before collapsing.

---

Verification checklist (all should be true)

node -e '
const fs=require("fs");const d=fs.readFileSync(process.argv[1],"utf8");
const checks=[
  ["JQe signature generalized", d.includes("maxHeight:i=250,scChildren,scClass})")],
  ["useEffect dep added",       d.includes("}},[i,e,scChildren]);")],
  ["content child generalized", d.includes("scChildren!=null?scChildren:Rc.default.createElement(ey,{content:e,context:t,plainText:!0})")],
  ["contentClassName applied",  d.includes("${wg.content} ${scClass||\"\"} ${!s&&o?wg.collapsed:\"\"}")],
  ["slash branch uses JQe",     d.includes("if(S.isSlashCommand)return je.default.createElement(JQe,{key:w,context:o,maxHeight:60,scClass:")],
  ["old raw slash render gone", !d.includes("${qi.slashCommandMessage}`,key:w},S.text)")],
  ["regular path unchanged",    d.includes("je.default.createElement(JQe,{content:y,context:o,maxHeight:60})")],
];
let ok=true;for(const[n,v]of checks){console.log((v?"PASS":"FAIL").padEnd(5),n);if(!v)ok=false;}
const c=(d.match(/createElement\(JQe,/g)||[]).length;console.log("JQe call sites:",c,"(expect 2)");
process.exit(ok&&c===2?0:1);
' "$DIR/index.js"

Apply / test

  • In VS Code: Ctrl+Shift+P → "Developer: Reload Window".
  • Run a long /goal or /loop prompt; it should render collapsed with

Show more / Show less.

---

If the literal strings don't match (identifiers changed in a new build)

Re-derive the names, then adapt the three edits to the new letters.

  1. Find the collapsible component (the JQe equivalent). Search the bundle

for the CSS-module marker:
``
expandableContainer:"expandableContainer_
`
The
function XXX({content:e,context:t,maxHeight:i=250}) immediately after it
is the component (note its name, its React import alias used as
XXX.useRef,
and the CSS-map variable used as
<map>.content / <map>.collapsed`).

  1. Confirm its render shape — it should contain "Show more" and

"Show less" literals and createElement(<contentRenderer>,{content:e,context:t,plainText:!0}).
That <contentRenderer> is the ey equivalent.

  1. Find the slash-command branch. Search for:

``
if(S.isSlashCommand)return
`
(The loop variable may not be
S/w/o — read the surrounding
case"text": to see the actual locals: the parsed part, the React key, and
the
context`.)

  1. Apply the same 3 transformations using the new identifiers:
  • add ,scChildren,scClass to the component's destructured props and

,scChildren to its measure-effect deps;

  • in its inner content <div>, inject ${scClass||""} into the className and

scChildren!=null?scChildren: before the content-renderer element;

  • replace the slash-command early return with a call to the collapsible

component passing scChildren:<the transformed text> and
scClass:<userMessage> <slashCommandMessage>`, plus key / context /
maxHeight:60`.

  1. Sanity: keep the transformed text (the S.text equivalent, i.e. the

w8t(...)-processed string). Do not pass the raw message object as the
child, or you'll render the <command-name>/<command-args> XML.

  1. node --check the file and reload the window.
kaanchan · 1 month ago

Reproducing this today on the VS Code extension (not just the referenced surface) — confirming this is still an issue and cross-surface, not CLI-specific.

Concrete example: a /goal prompt (~700+ words, multi-paragraph task spec with numbered scope items) was submitted. After Claude started working, the ENTIRE original /goal prompt text remained rendered at full height at the top of the visible viewport, with no Show less/collapse control anywhere on it. Below it, only a sliver of the actual conversation was visible — two collapsed "Thought for Ns" rows and a single in-progress Bash tool-call label ("Debug: why X cell not flagged"). The prompt text alone consumed roughly 80% of the visible vertical space, well after the turn had moved on to executing tool calls.

What made this worse than a typical long-message case: there was no way to reclaim the space at all short of scrolling past the entire original prompt every time — no click-to-collapse, no "open in separate editor tab" option, nothing. A large pasted prompt behaves like a fixed, immovable block rather than an expandable/collapsible one the way most chat UIs (and even a plain HTML <textarea>/expander) handle long input.

Suggested behavior (beyond the collapse/Show less already requested above): let a long submitted prompt behave like any other resizable input — clicking it should either (a) expand it inline to a scrollable max-height box the user can shrink back down, or (b) pop it out into a separate tab/panel for review, rather than permanently occupying primary screen real estate with the live conversation squeezed into whatever's left.

Related: #66578 (two-column layout / height caps so input + long messages don't cover Claude's replies) looks like the same underlying "long content has no space-reclaiming affordance" family of issue.

VP1967 · 1 month ago

Found a workaround which works for me, right after sending the /goal prompt send a simple prompt like "started?" and the following messages will push the /goal prompt upward clearing the history window.

kazuyakurashima · 19 days ago

Adding controlled measurements, because I think the "~4,000 characters" framing in this thread is a red herring. Character count is not the trigger. The trigger is whether the rendered height of the pasted block fits in the viewport.

I built a throwaway skill that ignores its argument entirely and emits one fixed marker block, so the only variable was the appearance of the input. One fresh conversation per run; I recorded only whether the marker block appeared on screen.

Environment: macOS (Darwin 25.5.0), VS Code extension 2.1.226 and 2.1.227 (both reproduce), external display, logical resolution 3008 × 1692, window maximized. The interactive terminal (2.1.220 / 2.1.227) did not reproduce, even at 9,621 characters.

1. Character and byte count are not the trigger

| chars | UTF-8 bytes | result |
| ---: | ---: | --- |
| 950 | 2,840 | visible |
| 1,400 | 4,188 | visible |
| 1,550 | 4,638 | visible |
| 2,500 | 7,488 | visible |

All single-paragraph, almost no newlines. Well past 4,000 characters and still fine.

2. Line count is the trigger — at a fixed 2,500 characters

| chars | bytes | lines | result |
| ---: | ---: | ---: | --- |
| 2,500 | 7,488 | 1 | visible |
| 2,500 | 7,360 | 60 | visible |
| 2,500 | 7,340 | 70 | visible (fit on one screen) |
| 2,500 | 7,320 | 80 | invisible |
| 2,500 | 7,158 | 160 | invisible |

Identical character count, near-identical byte count (a newline is 1 byte). The only difference is line count.

3. The real variable is viewport fit

Same payload, unchanged. I only halved the window height.

| lines | window height | fits on one screen | result |
| ---: | --- | --- | --- |
| 60 | normal | yes | visible |
| 60 | ~half | no | invisible |

The result flips with no change to the input at all. So line count was only a proxy for height. The condition is the pasted block's rendered height versus the viewport.

Additional observations

  • Generation and persistence are fine. In the invisible cases the response was still written to the session JSONL as a text block and reached end_turn. Example: 8,345-char argument, answer received 04:35:16, response generated 04:35:21 — never displayed.
  • Scrolling does not recover it. Scrolling to the very bottom does not reveal it.
  • Reopening the conversation does not recover it. It is absent from history replay too, so this is not merely a dropped live paint.
  • The only recovery I found is sending a new short message — which is exactly the workaround @VP1967 reported above. This measurement explains why it works: the new short turn shifts the tall block out of the layout position that suppresses the following render.

Why this distinction matters for a fix

If the trigger is character count, a length cap or a collapse-at-N-characters control fixes it. If the trigger is rendered height versus viewport, then the same input can be visible on one display and invisible on another, and a character-based collapse threshold will be wrong on some window sizes. A max-height scrollable container for the pasted block — as suggested in the original report — addresses the actual variable; a character threshold does not.

Full measurement record, including the derivation of a display-line threshold used as a workaround downstream:
https://github.com/kazuyakurashima/which-model/blob/main/tests/regression/vscode-render-threshold.md

Possibly related, though I have not confirmed a shared cause: #85573 and #84065 both describe text going unrendered around AskUserQuestion turns.