anthropics / anthropics/claude-code
[BUG] /diff turn view: an image-only prompt does not start a turn, so its edits are folded into the previous turn or dropped
- Dominant language
- Python
- Stars
- 145k
- Forks
- 23.1k
- PR merge metrics
- PR metrics pending
Description
### Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
### What's Wrong?
In the turn tab of `/diff`, a prompt sent as an image alone (a pasted screenshot with no text) never starts a turn. Two visible results:
| Session | Turn tab shows |
|---|---|
| Prompt "first" → Claude edits `a.ts`; then a screenshot alone → Claude creates `b.ts` | One turn, `T1 "first"`, listing **both** `a.ts` and `b.ts` |
| Only prompt is a screenshot alone → Claude edits `a.ts` | **No turns at all** (the tab is empty although the git tab shows the edit) |
**Root cause** — `mods/diff/hooks/turns/turn-of/is-prompt-row/is-prompt-row.ts`:
```ts
export const isPromptRow = (message: SessionMessage) =>
message.role === 'user' &&
(message.toolResults === undefined || message.toolResults.length === 0) &&
message.text !== ''
```
`turnDiffsOf` starts a turn at every row `isPromptRow` accepts. The `text !== ''` clause was presumably meant to skip non-prompt user rows, but the `toolResults` check already does that, and it also rejects a real prompt with no text. `SessionMessage.text` is documented as "its text blocks joined; `''` when it has none", and that is what the engine records for an image-only prompt. I checked with a small function-hooks plugin that writes `$.session.messages()` at `turn.complete`, driving `claude` 2.1.274 in print mode (`--input-format stream-json`) with a user message whose only content block is an image:
```json
{ "role": "user", "text": "", "toolUses": [] }
```
No `toolResults`, empty `text` → not a turn start. The edits in that turn are then attributed to whichever earlier prompt did start a turn, or to nothing when there is none.
### What Should Happen?
A user row with no tool results is a prompt whatever its text. An image-only prompt should start its own turn (`Turn N`, with no quoted preview — `pane-state/turn-title-of` already draws an empty preview as no subtitle), and its edits should be listed under it.
### Error Messages/Logs
Two tests added beside the existing ones in `mods/diff/tests/turns/turn-diffs-of.test.ts`, on current `main` (68ac8bb):
```
(fail) turn-diffs-of > an image-only prompt (empty text, no tool results) starts its own turn
Expected: [[2, ""], [1, "first"]]
Received: [[1, "first"]]
(fail) turn-diffs-of > a session whose only prompt is image-only still lists its edits
Expected: length 1
Received: length 0
```
### Steps to Reproduce
1. In a git repo, fullscreen renderer, terminal wide enough for the `/diff` pane.
2. Type a prompt such as `add a comment to README.md` and let Claude edit the file.
3. Paste a screenshot into the prompt and send it with **no text** (for example, a screenshot of an error), so Claude makes another edit.
4. Open `/diff` and switch to the turn tab: there is one turn, `T1 "add a comment…"`, and it lists the second edit too.
5. Start a fresh session and send a screenshot alone as the first prompt; after Claude's edit, the turn tab lists nothing.
Or run the two tests above: `CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude plugin test mods/diff`.
### Claude Model
Not model-specific
### Is this a regression?
No, present since `mods/diff` was published (`isPromptRow` is unchanged since d9c456d)
### Last Working Version
N/A
### Claude Code Version
2.1.274 (Claude Code), `mods/diff` at `main` 68ac8bb
### Platform
Anthropic API
### Operating System
macOS
### Terminal/Shell
Ghostty / zsh
### Additional Information
**Proposed fix**, on a branch against current `main`, since PRs from outside accounts are not accepted here:
https://github.com/anthropics/claude-code/compare/main...farmer-data:claude-code-1:fix/diff-turn-view-image-only-prompt
One commit: drop the `text !== ''` clause from `isPromptRow` (docstring updated) and add the two tests above. `tsc -p mods/tsconfig.json` passes; `claude plugin test mods/diff` is 144/144 on 2.1.274.
Two notes:
- The mod says it reads turns "as the built-in turn view" does, so the built-in may skip empty-text rows the same way; if so this is the same fix in both places.
- With the fix an image-only turn reads as `Turn N` with no quote. A `"(image)"` placeholder would be friendlier, but `SessionMessage` carries no attachment information, so that would need the engine to say so — a follow-up, not part of this change.
AI tools used: Claude Code (analysis, the probe plugin, tests and the patch); I reviewed and ran everything.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with mods/diff/hooks/turns/turn-of/is-prompt-row/is-prompt-row.ts and the turnDiffsOf tests in mods/diff/tests/turns/turn-diffs-of.test.ts. Run the two image-only prompt tests, then the full `claude plugin test mods/diff` suite. Done means an empty-text prompt with no tool results starts its own turn and its edits are listed there.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- cli, testing
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 25/100