[BUG] Transcript "N skills available" (and sibling) notices describe the agent's context but read as the user's inventory
Filed by Claude Code (Opus 4.8) on @evnchn's behalf, with his review.
TL;DR:
A family of transcript status notices describes changes to the agent's context but is worded like changes to the user's inventory, so users read them as "I just gained things to use."
The clearest offender: after the model's skill set grows mid-session, the transcript prints 3 skills available — which a user reads as "I now have 3 more skills/menu items I can pick". It actually means "the model's context just gained 3 skills it can call via the Skill tool." There is nothing to click and nothing changed in the user's / menu. (Original report: a user saw 3 skills available, believed they had three new user-facing skills, and went looking for where to click them.)
It is not one string. The same dim notice component renders both "things the user did" (⧉ Selected 12 lines from … in VS Code, queued prompts) and "things that happened to the agent's context" (3 skills available, 3 agent types available, Recalled 5 memories, Skills restored (loop), Loaded 2 skills from …) — with no word or marker distinguishing the audience. The user-action ones read naturally as second-person; the agent-context ones are bare counts ending in available / recalled / restored / loaded, which is exactly what reads as the reader's own inventory.
The ask: mark the audience on the agent-context notices — e.g. +3 skills now available to the agent / Agent context: +3 skills instead of 3 skills available. One-word fix, no behavior change.
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet (12 issue-search queries + 2 web searches; closest priors are related but distinct — see fold below)
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
What's Wrong?
When the set of skills/subagent-types available to the model changes during a session, Claude Code prints a transcript notice such as:
3 skills available
The wording is ambiguous about who the thing is "available" to. Read by a human, "available" implies available to me, the reader — i.e. "I now have 3 more skills in my menu that I can invoke." The actual meaning is the opposite direction: the model's context just gained 3 skills it can call via the Skill tool. Nothing in the user's / command list changed, and the notice is read-only telemetry, not an interactive control — so a user who tries to act on it finds nothing to click.
This is a family of notices, all rendered through the same dim system-notice component, none of which marks its audience:
| Transcript text (user sees) | Actual meaning (agent-context change) |
|---|---|
| 3 skills available | model's Skill-tool set gained 3 skills |
| 3 agent types available | model gained 3 dispatchable subagent types |
| Recalled 5 memories | model pulled 5 memory files into context |
| Skills restored (loop) | skills re-injected into context after a compaction |
| Loaded 2 skills from <path> | dir-scoped skills injected into model context |
The tell that this is a wording defect and not inherent: the notices that are about the user — ⧉ Selected 12 lines from src/app.ts in VS Code, the echo of a queued prompt — use naturally second-person phrasing and are never misread. The confusing ones swapped that for <count> + available/recalled/restored.
What Should Happen?
Agent-context delta notices should make the audience explicit, so a user cannot read them as their own inventory. For example:
3 skills available→Agent gained 3 skills/+3 skills now available to the agent/Agent context: +3 skills3 agent types available→Agent gained 3 subagent typesRecalled 5 memories→Agent recalled 5 memories
No behavior change is requested — only the human-facing label. The interactive surface for the user's skills (/skills, and the / command menu) already exists and is unaffected.
Error Messages/Logs
(not an error — this is the user-visible transcript string, verbatim)
3 skills available
3 agent types available
Recalled 5 memories
Skills restored (loop)
Steps to Reproduce
It was first observed organically (a user mid-session saw 3 skills available and went looking for where to click it). The notice renders only on a delta — a non-initial skill listing; the first listing each session is silent (it is the block injected into the model's system prompt). So the deterministic trigger is: the set of skills available to the model grows after the initial listing.
The most direct lever I can point at from the render logic:
- Start a session whose available-skill set is some set S (the silent initial listing).
- Mid-session, increase that set — e.g. add a skill under
~/.claude/skills/or enter/open a project that contributes its own.claude/skills/, so the next turn's recomputed skill set is a superset of S. - On the next turn, the transcript prints
N skills available, where N is the count of newly-added skills. - Note there is no associated control — the line is informational only, despite reading like newly-available user functionality.
I have not pinned the exact minimal recipe (the organic sighting + the delta-only render path are what I'm confident of). I'm happy to nail down and attach a copy-paste fixture with a throwaway .claude/skills/ dir if that would help triage.
Claude Model
Opus
Is this a regression?
I don't know
Claude Code Version
2.1.196 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Other (render-layer issue — terminal/shell appears not material to the wording; observed in the Claude Code transcript UI)
Additional Information
<details>
<summary><b>Verbatim render source (bundled JS, v2.1.196) — the shared component + the ambiguous vs. unambiguous cases</b></summary>
Read out of the shipped bundle at ~/.local/share/claude/versions/2.1.196 by printable-string extraction; variable names (WC, Tn, As.jsxs) are minified. WC is the dim system-notice/aside component; Tn(n, "skill") is the pluralizer. These are quoted to show the strings are constructed identically regardless of audience — proves the wording, not intent.
The ambiguous one (skill_listing) — renders only on a delta, silent on first listing:
case "skill_listing": {
if (n.isInitial) return null; // first listing → silent (it's the model's context block)
... i = <v bold>{n.skillCount}</v>
a = Tn(n.skillCount, "skill") // "skill" / "skills"
l = <WC>{i}{" "}{a}{" available"}</WC> // → "3 skills available"
return l
}
Its twin (agent_listing_delta):
case "agent_listing_delta": {
if (n.isInitial || n.addedTypes.length === 0) return null;
a = <v bold>{n.addedTypes.length}</v>
l = Tn(n.addedTypes.length, "type")
c = <WC>{a}{" agent "}{l}{" available"}</WC> // → "3 agent types available"
return c
}
Same component, other agent-context deltas:
case "relevant_memories": ... <v dimColor>{"Recalled "}{count}{" "}{"memory"/"memories"}</v>
case "invoked_skills": ... <WC>{"Skills restored ("}{names}{")"}</WC>
case "dynamic_skill": ... <WC>{"Loaded "}{count+" skills"}{" from "}{displayPath}</WC>
case "nested_memory": ... <WC>{"Loaded "}<v bold>{displayPath}</v></WC>
**Contrast — the notices that ARE about the user, rendered through the same WC, but naturally second-person and never misread:**
case "selected_lines_in_ide": ... <WC>⧉ {"Selected "}{n}{" lines from "}{path}{" in "}{ideName}</WC>
case "selected_lines_in_diff": ... <WC>⧉ {"Selected "}{n}{" lines from diff view"}</WC>
// queued_command renders the user's own queued prompt back to them
The defect is precisely the lexical gap: user-action notices say "Selected…" (you did it); agent-context notices say "{count} … available / restored / recalled" (ambiguous direction).
</details>
<details>
<summary><b>Prior-art search — why this is a fresh report (and the close-but-distinct neighbours)</b></summary>
Searched before filing: 12 GitHub issue-search queries (open + closed) over lexical variants of "skills available", "agent types available", "Skills restored", Recalled memories, and audience/wording/confusing/ambiguous/misleading combinations, plus 2 web searches. No issue reports the audience-ambiguity wording nit.
Closest neighbours — related surface, different complaint:
- #46561 (closed, not-planned) — quotes the exact
⎿ Skills restored (loop)string and argues the "Skills restored" replay is misleading. Useful precedent that this phrasing already confuses people, but its reported harm is a re-firing/loop/cron contract, not a human misreading "available/restored" as their own inventory. - #71301 (open) — about a transcript surface mis-presenting agent-context internals; it even names the "Available agent types for the Agent tool" section. But the complaint there is wrong token counts, not ambiguous wording.
- #62482 (closed, not-planned) — user's custom skills missing from slash-command autocomplete: a discoverability bug, not a wording one.
</details>
<details>
<summary><b>Why it matters / suggested scope</b></summary>
- It's a small, self-contained UX fix (label text only), but it sits on a high-traffic surface — sessions that trigger these deltas (loading dir-scoped skills, recovering from a compaction, etc.) can emit one of these lines.
- It likely reads worse on touch surfaces (mobile / remote control): dim text that says "available" next to a number invites a tap even more than in a terminal.
- The fix needs no behavior or API change and is independent of the (separate) token-accounting and discoverability issues above.
</details>
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗