Releases: serialexperimentslainnnn/claude-code-for-jetbrains
Release list
v5.5.0
[5.5.0] — 2026-08-19
This release needs IntelliJ Platform 2025.3.1 (build 253.29346.138) or newer. On 2026.2 it is the fix:
5.1.1 could not open a chat there at all. On 2025.1, 2025.2 or the first 2025.3, stay on 5.1.1 — it keeps
working — or update the IDE.
Added
- A tab per agent, with its own transcript. A second row under the chats lists everything the open chat
started — agents, the agents they started, background tasks — all of it at once rather than behind a menu,
and scrollable the same way the chats are. Opening one swaps what the conversation area shows; closing it
hides a view and destroys nothing, and the card that started the agent opens it again. - The chat tabs are all one width, so the row reads as a strip rather than as an accordion of long and
short titles, and nothing reflows when you select a tab. A long name ellipsises with the whole of it in the
tooltip. Selecting a chat centres it, which is what makes ordinary use need no scrolling at all. - A Chat settings menu on the composer — the wrench beside the prompt box — holding the settings worth
changing without leaving the chat, in ten collapsible groups: model, effort, permission mode, the chat
toggles, the security lock's rules — 28 of them, behind the nine groups they belong to, so the list is
navigable rather than a wall — setting sources, allowed / disallowed / always-allowed tools,
and the two MCP switches. Model, effort and mode act on the chat you are in — they are the same controls as
the pills beside them, so the two can never disagree — and anything that can only take effect the next time
a chat starts says so over its group instead of quietly doing nothing. Everything else is one row away,
behind Open Plugin Settings. - "Always allow" can be granted in advance from that menu, instead of only by answering a permission card
for that tool first. What it cannot do is widen the deterministic lock: a credential file, a dangerous
command, the system temp folder and anything outside your project still stop and ask, for an always-allowed
tool exactly as for any other. - The composer's two rows of buttons collect what does not fit behind a ⋮, instead of running off the edge
of a narrow tool window where nothing could reach them. The send button is never collected and never
shrinks to make room. - Attach ▸ Files… and Directory… browse your project inside the menu, as a tree that unfolds in place,
rather than opening a separate file dialog over the IDE. Pick as many as you like and press Done; marking
a folder marks everything under it and tells you how many that is before you commit to it. It offers what
the IDE considers yours — your.gitignoreand the project's excluded folders are honoured, sobuild/
andnode_modules/are simply not there — and it says so out loud when a folder is too large to offer
whole, rather than quietly attaching part of it. - The chat's own buttons are in the chat. New chat, Stop, Commands, Git, Close all diffs and Log out sit
in a row above the prompt box, where they are used, instead of in the tool window's title bar — which now
holds only the IDE's own controls. Stop is greyed unless a turn is running, and Close all diffs unless
there is a diff open. - Install, sign-in and loading are screens over the transcript, and nothing else. Whichever one a tab
owes you covers the conversation and leaves the chat tabs and the prompt box alone, so you can switch
chats while one starts and type into a chat whose binary is still coming up — what you type is queued and
sent the moment it is ready. A start that takes a moment draws no screen at all. - Workloads: everything running across every open chat as one diagram, replacing the three lists that
were three views of the same tree. Finished work ages out of it on a window you choose in Settings — five
minutes through four hours, or All — while anything still running is always shown. - Background tasks keep their tab and their output after they end, tailed live while they run and
rebuilt after a restart. Until now both vanished at exactly the moment the output was worth reading. - Git, without the plugin ever running
git. A Git button in the chat's own button row opens the
repository view, which holds a conversation of its own about the repository — so none of this plumbing
lands in the chat you are working in, and none of it makes you leave it either. ⚙ also offers
Initialize Git Repository, Commit Changes with Claude and
Revert This File with Claude — each one asks Claude to do it, so the command is in front of you in an
approval card before it runs and you can answer back ("squash those two", "not that file") instead of
getting one shot at a button. On a project that is not a repository yet, opening it offers to create one.- Its turns are always approved by hand — whatever permission mode you are in, and whatever you have
marked "Always allow". The plugin started the turn, so it does not inherit permissions you granted for
your own work. - Each entry is hidden where it means nothing: no Initialize where there is already a repository, no
Commit with nothing to commit, no Revert unless the file in the editor has actually changed.
- Its turns are always approved by hand — whatever permission mode you are in, and whatever you have
- ⚙ ▸ Git Operations — branches and new branch, pull, fetch, push, merge, rebase, stash, unstash and the
commit dialog, which are the IDE's own actions: the same dialogs, the same shortcuts, the same
enablement, one menu away. - Git context in the ⚙ menu: the checked-out branch in the menu label itself, your recent commits, and
the history of the file you have open — all of it handing off to the IDE's own Git Log. It only ever
reads, and on an IDE without the Git plugin, or outside a working copy, the entries are simply absent. - A Git view in the session dashboard, so the same repository picture and the same actions are one click
from the conversation: the branch, what is still uncommitted file by file, and the history as one graph
with branch lanes — a commit list and a separate branch map asked you to hold two pictures of the same
history at once. Every line and every fork in it comes from real parents and real refs; nothing is
inferred, and a line that continues past the oldest commit shown says so rather than ending in mid-air. It
reads every branch, remote branch and tag, not only the one you have checked out, because a fork you can
only see one side of cannot be drawn. Colour carries nothing on its own: a branch is a text tag on its row
and a merge says the word. - GitHub and GitLab answer for the branch you are on: the pull or merge requests open from it, and its
most recent CI run. Read-only, and opt-in — it does nothing until you paste an access token under Settings ▸
Claude Code ▸ Git forge, and until then there is no card and no prompt to configure one. The token is stored in
your OS keychain and kept per server, so a company GitLab and gitlab.com are two separate credentials
and one can never be sent to the other. Clearing the field revokes it. - ⚙ ▸ Review This Session's Changes… — everything the agent has touched this session, as native diff
tabs, against one base. When the original side cannot be rebuilt exactly the file still opens and that
pane says why — new file, binary, too large or restricted, or changed on disk since — because a fabricated
original in a review tool is worse than none. - A Plan view in the dashboard, holding this session's plan in full, with a button that appears only
once there is one. It is re-read as soon as you approve a plan, so a revision does not leave the old one
on screen. - The transcript keeps a bounded number of rows in memory, dropping the oldest and saying so in a row at
the top. Nothing is lost: the whole conversation is on disk, and "Open Previous Session…" reads it
back in full. - A chat names itself. At the end of the first turn Claude is asked to title the conversation, and the
title is kept with the conversation — so it survives a restart and is never asked for twice. Until it
arrives the tab shows the first thing you typed, one line, rather than "Chat 3" for the rest of its life. A
name you set yourself always wins, whenever you set it. The same name appears in the live tab, in the tabs
restored at startup and in the list behind "Open Previous Session…". - The agent is told what it is running inside — that the transcript is a real interface and not a
terminal, that its edits become a diff you review and its paths become links you click, and that a
deterministic guard may refuse a call outright. It is fixed text: nothing about your machine, your
environment or your project, and nothing that softens a rule.
Changed
- Settings moved into the IDE's password safe (the OS keychain), one encrypted document shared by every
project. They used to sit in a plain-text file inside the project, committable, including the environment
block where an API key ends up. Existing settings are adopted on first run — and because they are now
shared rather than per project, the first project you open after upgrading is the one whose settings
become everyone's. If you kept deliberately different settings in two projects, note them down first. - The chat is noticeably lighter. The tab bar and the dashboard used to redraw everything on every update
— several times a turn, including updates that changed nothing you could see — and the dashboard did it
even while it was closed. Both now redraw only when what they draw has actually changed, so a tab bar no
longer rebuilds itself under your pointer. - The conversation uses the whole width of the tool window. It was capped at a fixed column, so the wider
you made the panel the more of it was margin. Diffs, tables and command output are what ...
v5.1.1
[5.1.1] — 2026-08-10
Fixed
- The plan limits stopped refreshing whenever the panel was not on screen. The poll was gated on
isShowing, so a collapsed tool window or a chat tab that was not the selected one asked for nothing at
all — and a quota window is not the plugin's state to begin with: other sessions, other devices and
claude.ai spend the same windows, and a reset is a wall-clock event that owes nothing to this IDE. The
figure on screen was therefore whatever the last probe happened to catch, and it only moved again when
something else triggered one — a turn, or opening the dashboard. "It only updates when I talk to the agent"
is precisely what a visibility-gated poll looks like from outside. The gate is gone and the period is 30 s;
what it was saving is one control request per half minute against a process that is already running, and
the event-driven refreshes (turn edges,rate_limit_event, dashboard open, session ready) are unchanged.
Added
- The chat's plan-limit row now says how long each window has left —
Reset time: 4h 18mon its own line
directly under that window's bar, with the full sentence in the tooltip. A percentage alone does not say
whether it is urgent: 90% with eight minutes to go and 90% with six hours to go are different situations,
and only the dashboard was answering that. Under the bar rather than beside it because the row is already
three items wide per window, and a fourth made the countdown the first thing to be squeezed out — the one
case where it matters most. The countdown is computed by one function inapp-core
(CC.resetIn/resetInShort) that the dashboard card now shares, and a window with no reset time renders
no element rather than an empty slot that would read as "resets now".
Changed
- Every
get_usagepoll now logs the reply it got,rate_limitsverbatim (truncated), at INFO. The
derived per-window lines cannot answer the question that keeps coming up — is the number on screen stale,
or is the server still saying that? — because a window the reply omits leaves no line at all, and one
carried forward from the previous poll is indistinguishable from a fresh one. It immediately earned its
place: a live capture showed two of three consecutive polls coming back in the header-seeded shape
(five_hour/seven_dayonly, nolimits[],resets_atrounded to.000Z), which is the degraded reply
5.1.0's merge exists for, and confirmed the binary does not cache the endpoint. RateLimitInfo.resetsAtIso()puts the epoch-seconds → ISO-8601 conversion on the model, so a window that
reaches a surface from the event stream and one that arrives in theget_usagereport are
interchangeable to everything that renders them. It was a private copy in the dashboard's builder, and the
composer needed the same thing.
Verifying this release. Both the .asc files and the tag are signed by the project's CI
signing key (docs/ci-signing-key.asc), which is itself certified by the maintainer's hardware
key — so the chain terminates in a key that has never been on a computer.
What the signatures do NOT assert is that a human pressed a button: the release is cut
automatically from main. That claim rests on the two gates around it — main accepts only
reviewed pull requests, and publication requires an approval on a protected environment.
gpg --import docs/ci-signing-key.asc
gpg --verify claude-code-native-*.zip.asc # these bytes came from this workflow
git verify-tag <tag> # this workflow cut this release from mainv5.1.0
[5.1.0] — 2026-08-10
Added
-
An "Other models" group in the model picker, holding previous generations (Opus 4.8 → 4.0, Sonnet 4.6 →
4.0, Sonnet 3.7 and 3.5, Haiku 3.5). Collapsed by default so the four current models keep the menu they had,
and expanded automatically when the selected model lives inside it.The list is curated in the plugin, which deserves stating plainly because this repository removed a
hardcoded model label in 4.3.3. There is no runtime source for it: the binary's selectable catalog — the
initializereply, and the identical answer to thelist_modelscontrol request — contains only the current
generation, andModelInfocarries nodeprecated/legacyflag. The binary still accepts these ids, it
just will not list them. The distinction that makes a curated list defensible here: these are historical
ids, which never change and never disappear, so the list can only gain entries. What went stale in 4.3.3 was
a label describing the current tier. Nothing here names a current model, and a test enforces that.Choosing a model the account cannot run is handled rather than left to fail:
set_modelis now sent as a
correlated control request, and a refusal restores the previous model and says so in the transcript
instead of leaving the tab pointed at a model every later turn would fail on. -
Per-model plan limits — Fable among them — are reported.
get_usagereturns them in
rate_limits.model_scoped, an array alongside the keyed windows rather than another key inside them
(sdk.d.ts:{ display_name, utilization, resets_at }[], and its own example names'Fable').
parseUsageReportwalked only the keyed windows, so every per-model figure the server sent was dropped on
the floor — which is why the CLI's/usageshowed a Fable row the plugin never did.Reading that array is necessary and not sufficient, which is what the first attempt got wrong: the
binary does not relaymodel_scoped, it synthesises it, and only behind its own remote config. Its
projection (IUt(limits, jJe())in 2.1.223) reads thetengu_usage_overage_included_modelsgate, returns
an empty list the moment that gate is empty, and the key is spliced intorate_limitsonly when the
projection yielded something — so in a--printsession it simply never arrived, which is why the plugin
loggedfive_hourandseven_dayand nothing else while the same account's interactive/usagelisted
Fable. The plugin therefore also walks the rawrate_limits.limits[]array the projection reads from,
which does ride through untouched — the binary's own/usageformatter assumes as much, callingIUton
this very payload — taking theweekly_scopedentries that name a model, with the binary's filter and
without its allowlist. Dropping the allowlist is deliberate: it selects which models get overage billing,
not which limits a user is subject to, and a limit that meters you is worth showing whether or not you can
pay past it.resets_atis epoch seconds there as often as a string, so it is normalised rather than
deserialized — a numeric one would have failed to decode and dropped the whole window in silence.And a usage refresh is now merged into the last one instead of replacing it, because the same fetch has
a second fallback that omits windows:loadPlanRateLimitsgives/api/oauth/usage5 s, and on a timeout, a
429 or a fieldless body it substitutesseedUtilization()— an object rebuilt from the rate-limit response
headers, which structurally carries onlyfive_hourandseven_day. It is flaggedstatus:"seeded"and
then accepted identically to a full reply, so a poll that simply failed was indistinguishable from one
saying the per-model window no longer exists — and the Fable bar blinked out and back every few polls.
Merged by window key over the whole set, sinceseven_day_opus/seven_day_sonnetare missing from a seeded
object for the same reason and would flicker the same way; a carried-forward window keeps the last figure
actually reported for it and the next real refresh overwrites it. The extra-credit balance is deliberately
not carried:nullthere already means "this plan has none" as often as "this reply did not say".They are keyed
model_scoped:<display_name>because the quota-crossing record is kept per window and has to
stay stable across refreshes, and titled from the server's owndisplay_name— the only source for it,
since nothing in the plugin can name a window the server invents. An entry whose name collides with a keyed
window is dropped rather than duplicated, one missing a name or a figure is skipped, and they sort after the
known windows so the row order the user already reads does not shuffle when Anthropic adds a model.
Removed
-
The
nimbus_quillusage window is no longer shown. The claude.ai usage endpoint emits it and the CLI
relays it untouched; it appears in no version of the binary and in no SDK type, so nothing here can say what
it meters — it rendered as "Nimbus quill 0.0%", a row that asks a question and answers none. Hidden by
name, deliberately not by a general "hide unknown windows" rule, which would silently swallow the next
real limit; the moment it means something, deleting one line brings it back with its label, bar and ordering
intact.It kept appearing anyway, because the filter sat on one of the two paths that feed a window to the UI:
theget_usagereport was filtered, therate_limit_eventstream was not, and that is the door it was
arriving through. The rule is now applied on both (isHiddenUsageWindow), and on the event path the window
is dropped whole rather than merely hidden — it must not become the session'srateLimiteither, which
drives the single-number quota bar.
Fixed
-
A quota notification announcing 100% when almost nothing had been used.
get_usagereports each
window as a percentage on a 0–100 scale —sdk.d.tssays so on every window, and a live reply from
claude2.1.222 carries8and67.ClaudeSessionheld a private copy of an "the wire sends both
0–100 and 0–1, accept either" heuristic that multiplied any value<= 1.0by a hundred. So a window at a
genuine 1% was reported as 100%, crossed the 85% threshold, and raised an IDE notification telling
the user their plan was spent — at the moment they had spent almost none of it, which is to say right
after a window resets. The heuristic is undecidable at exactly 1.0 by construction: it cannot tell a full
window from a barely-touched one.The rule now lives once, on the model (
UsageWindow.utilizationPercent()), with no scale guessing: the
value is already a percentage. Two of the three copies had been removed in 5.0.1 when the dashboard
stopped rounding; this was the third, and the only one wired to notifications, which is why the bars got
quieter while the notifications kept shouting. The event path (RateLimitInfo.utilization, genuinely a
0..1 fraction) is unchanged and was never affected.
Changed
-
The plan limits are their own row under the status line, one labelled bar per window, instead of dots
at the end of the readout. Inline, they sat behindRunning… / Context 65% / 65.3k out / 0 reasoningon a
wrapping row — so the windows nearest their cap, the ones the row exists for, were the ones most likely to
wrap out of sight in a narrow tool window. The row is arepeat(auto-fit, minmax(150px, 1fr))grid: it
spends the full width at any size and drops to fewer columns as the panel narrows, with no media query and
no fixed layout to outgrow. The bar is clamped to 100%; the number is not, because a window reported past
its cap is exactly the figure worth reading. -
Quota notifications title themselves through
UsageWindow.title(key)rather than from the key, so a
per-model window announces "Fable quota at 85%" instead of the syntheticmodel_scoped:Fable. The record
that decides whether a threshold has already been announced stays keyed by the key, which is what makes it
survive a refresh. -
The
get_usagepath now logs each window's raw utilization and the percentage derived from it, at INFO.
When the false 100% was reported there was nothing inidea.logto check it against, because only the
event path carried a trace — and that one isdebug, so it is off by default. A number the user can see
should leave behind the value that produced it.
Verifying this release. Both the .asc files and the tag are signed by the project's CI
signing key (docs/ci-signing-key.asc), which is itself certified by the maintainer's hardware
key — so the chain terminates in a key that has never been on a computer.
What the signatures do NOT assert is that a human pressed a button: the release is cut
automatically from main. That claim rests on the two gates around it — main accepts only
reviewed pull requests, and publication requires an approval on a protected environment.
gpg --import docs/ci-signing-key.asc
gpg --verify claude-code-native-*.zip.asc # these bytes came from this workflow
git verify-tag <tag> # this workflow cut this release from mainv5.0.1
[5.0.1] — 2026-08-10
Fixed
-
The subscription login did not survive a restart. The credential was stored correctly — in the IDE's
PasswordSafe, which resolves to the OS store — KWallet or GNOME Keyring through the Secret Service on
Linux, the Keychain on macOS, the Credential Manager on Windows — and it was still there after the reboot,
confirmed by reading the entry back out of the OS store directly. What expired was the access
token inside it: the OAuth flow issues one good for hours (~10 h, measured), so any restart the next day
found a perfectly persisted credential that no longer authenticated anything.hasUsableToken()answered
false, and false meant "signed out", so the sign-in card came back every morning.The blob beside it always carried a refresh token valid for weeks and the plugin never spent it, by
design: only the binary can, and it does so by rewriting~/.claude/.credentials.json— the exact file the
vault exists to remove. The way out is that the binary has a non-interactive login for precisely this:
givenCLAUDE_CODE_OAUTH_REFRESH_TOKENandCLAUDE_CODE_OAUTH_SCOPES,claude auth logintakes a
dedicated branch, mints a fresh credential and exits — no browser, no TTY, no user. So renewal is now the
binary's job, exactly as it always was, and the plugin's job stays what it was: take custody of the result
and delete the plaintext copy. No OAuth client here, no token endpoint called from the IDE, no file written
back — the invariantNoFileDeletionContractTestand the vault's KDoc both state is untouched.Reported on Linux and Windows, and it is one bug rather than two: the binary's default credential store
is itsplaintextprovider (~/.claude/.credentials.json) on every platform, so the vault takes custody
the same way everywhere and the token expires the same way everywhere. The fix carries no platform-specific
code — the only Windows-specific care is that the renewal environment stripsCLAUDE_CODE_OAUTH_TOKEN
case-insensitively, since environment names are case-insensitive there.Scope: the subscription (OAuth) credential only. An Anthropic API key is a different identity in a
different slot —providerApiKey:anthropicin the same PasswordSafe, notCLAUDE_CREDENTIALS_JSON— and it
has no expiry and no refresh token, so there was nothing to lose across a restart and there is nothing to
renew now.CredentialsVault.renew()reads theclaudeAiOauthblob and nothing else, andenvOverlay
withdraws entirely when an API key is present, so an API-key session is untouched by any of this.An expired-but-renewable credential now counts as an identity (
CredentialsVault.canRenew), the renewal
runs off the EDT at launch (ClaudeSession.renewVaultedCredential, before the launch env is built, and
never while a sign-in is in flight), the refresh token rotates at every renewal so ordinary use extends it
indefinitely, and a failed renewal arms a five-minute cooldown so the three-second boot watcher cannot turn
a flaky network into a process spawn per poll. Sign-in is now needed only after a genuinely idle period, or
when Anthropic invalidates the grant.
Verifying this release. Both the .asc files and the tag are signed by the project's CI
signing key (docs/ci-signing-key.asc), which is itself certified by the maintainer's hardware
key — so the chain terminates in a key that has never been on a computer.
What the signatures do NOT assert is that a human pressed a button: the release is cut
automatically from main. That claim rests on the two gates around it — main accepts only
reviewed pull requests, and publication requires an approval on a protected environment.
gpg --import docs/ci-signing-key.asc
gpg --verify claude-code-native-*.zip.asc # these bytes came from this workflow
git verify-tag <tag> # this workflow cut this release from mainv4.4.1 — /login fix, restored-transcript rendering, review prompt
v4.4.1 — 2026-07-29
🔑 Fixed: /login never actually opened the terminal. Signing in from the chat always ended on "run this command yourself in a terminal" — the plugin was calling IDE terminal APIs that no longer exist in current IDEs (they were removed after 2025.2), and because each lookup failed quietly, nothing showed up in the log to explain it. /login now opens a real terminal tab and runs the sign-in there, on every supported IDE version.
If the terminal can't be opened at all — say you've disabled the bundled Terminal plugin — the plugin now falls back to signing you in natively, with no terminal involved, instead of giving up. The manual command is only ever shown as a genuine last resort.
🔁 Fixed: reopening a past chat lost the command code blocks. Restored conversations rendered Bash and other command calls in the old plain-text style — you couldn't copy the command, and its output wasn't a code block either. Reloading a session now produces exactly the same cards a live turn does.
⭐ Asked once, and only once. After 25 successful turns, a single IDE notification asks whether you'd leave a review on the Marketplace — and then never asks again, whatever you click. No dialogs, no "remind me later", nothing that interrupts your work. Failed or interrupted turns don't count, so you'll only ever see it if the plugin has actually been working for you.
📝 Listing rewritten, with a few stale claims corrected along the way — it advertised a 2025.2 minimum when the plugin actually supports 2025.1+, and documented a couple of shortcuts that had since changed.
Verified Compatible on IC-251, IC-252 and IU-262 — including the exact build the /login regression was reported on.
See CHANGELOG.md for the full technical detail.
v4.4.0 — per-rule security toggles
v4.4.0 — 2026-07-28
🔓 Security toggles — Settings ▸ Claude Code ▸ Security. The five rules behind the plugin's deterministic sensitive-data lock (credentials, dangerous commands, and the three foreign-territory checks — another user's home, network/UNC mounts, foreign WSL drives) can now be switched off individually, if you specifically need to. They're all ON by default, so nothing changes unless you go looking. Turning one off is never a silent allow: the lock still watches for it, it just shows you a permission card instead of blocking automatically — every time, for every caller, MCP servers and Skills included. Every block or prompt from the lock now also tells you exactly where to change it.
🛠 Fixed: several of Claude's own native tools were treated like a blocked MCP server. As the CLI grew its own orchestration surface — background tasks, cron jobs, worktrees, and more — the plugin's allowlist of "trusted, first-party" tools hadn't kept up, so calls from tools like TaskCreate, CronList, or EnterWorktree were silently hard-denied instead of asking, exactly as if they were a blocked third-party MCP call. The allowlist is now current with the CLI's real tool set.
See CHANGELOG.md for the full technical detail.
v4.3.3 — versioned model picker, Opus pinned as default
v4.3.3 — 2026-07-27
🧬 The model picker shows the version now — and picks Opus for you. The list of models is read straight from the binary (it always was), but it used to label each one without its version — "Opus (1M context)", "Sonnet" — so you couldn't tell Opus 4.8 from Opus 5. Every model now shows its version ("Opus 5 with 1M context", "Sonnet 5", "Haiku 4.5"), in both the composer and Settings. The vague "Default" entry — which was just Opus listed a second time, with no version — is gone, and a fresh install defaults to the concrete Opus tier and stays there. A stale hardcoded "Opus 4.8" label that lingered on the pill is gone too.
Maintenance: protocol baseline refreshed to the latest claude 2.1.220 / SDK 0.3.220 (no protocol changes). Verified Compatible on IC-251, IC-252, IU-253, IU-261 and IU-262.
See CHANGELOG.md for the full technical detail.
v4.3.2 — command code block, syntax highlighting, sensitive-guard fixes
v4.3.2 — 2026-07-23
⌨️ The command you ran, right on the card — no need to expand it. A Bash/PowerShell/MCP-exec call now shows the exact command as its own copyable code block, right under the header, visible whether the card is collapsed or open. The title no longer crams the whole command into the tool name — it just tells you which tool ran — and a command call gets its own distinct look (a left accent) so it stands out at a glance.
🎨 Syntax highlighting for diffs, and for Read/Write/Edit output. File content and coloured diffs in a tool card are now syntax-highlighted from the file's extension, on top of the existing added/removed line colouring.
🛡 Fixed two false-triggers in the sensitive-data lock. An everyday Edit/Write touching a line with a // comment could get silently denied outright — the lock's UNC-network-path check mistook the comment's leading // for a Windows network share, and a foreign-territory hit denies regardless of how trusted the tool is. And a Bash command that assigned a shell variable containing $/${...} (e.g. k=${OTHER}/x) could crash the permission check entirely, leaving that call stuck with no response ever sent back. Both are fixed; neither required weakening what the lock actually protects.
Fix: the plugin now starts on a WSL project under /mnt/c. WSL2 exposes the Windows C: drive over the 9p protocol, which the network-drive detector treated as a remote mount — so the deterministic security layer, which refuses to launch an agent rooted on a network share, was blocking perfectly normal WSL projects on C:. /mnt/c is now correctly recognised as the local Windows disk (every other /mnt/* drive stays foreign, as intended).
See CHANGELOG.md for the full technical detail.
v4.3.1 — Deterministic sensitive-data protection
v4.3.1 — 2026-07-14
🛡 Deterministic sensitive-data protection
A new permission-layer control evaluates every tool call before it can be auto-approved. It is deterministic and enforced outside the model: the classification and verdict are the plugin's, independent of anything the model or a prompt injection can say.
What it covers. Calls that touch credential or key material — SSH/GPG keys, cloud and cluster credentials, database and shell-history secrets, browser and password-manager stores, crypto wallets, and the access tokens of well-known AI agents and code hosts. Patterns match by structure, so the same rule covers Linux, macOS, Windows (C:\Users\…\.ssh) and WSL (/mnt/c/Users/…\.ssh). Credential-dumping and exfiltration commands (secret exports, reverse shells, offensive tooling) are covered too, evaluated after resolving symlinks and .. on disk and after normalising common shell obfuscation (broken quotes, $IFS, a path hidden in a variable, a base64 payload piped to a shell).
How it decides. The agent's own tools require an explicit permission card whenever a call is flagged — including in acceptEdits and bypassPermissions. MCP servers and Skills are denied access to that material rather than prompted. Access that reaches another user's home, a network or UNC mount, or a foreign WSL drive is denied for every caller. The blacklist is configurable additively (you can widen it, not narrow it), and a session will not start when the project is located on a remote or network-mounted drive.
Scope. Detecting a path concealed inside an arbitrary shell string is best-effort and can be widened over time; the enforcement of a match, however, is absolute and cannot be overridden by the model. See SECURITY.md for the full model.
🔗 Jump to code, straight from the conversation
Claude names a file, you click it, you are there. The conversation stops being a wall of text you have to translate back into your project.
On tool cards. A file tool now names its file the way you think about it — Read(src/main/kotlin/permission/PermissionBroker.kt), relative to the project, not a bare PermissionBroker.kt that tells you nothing about which one. And it is a link: it opens the file in the editor at the right line and selects it in the Project view, so you can see where it lives.
In Claude's own words. Paths (src/Foo.kt, a/b.py:42, ~/.claude), directories (build/ — revealed and expanded in the Project view, or opened in your file manager when they live outside the project) and symbols (PermissionBroker → straight to its declaration) all become links. Even the way developers actually cite a file works: app.css:190, a bare name and a line, resolves through the IDE's file index — and through a bounded on-disk scan for excluded folders like build/, which no index knows about. Archives reveal in the tree instead of opening a useless binary buffer.
And it never lies to you. The transcript can only guess what is a path or a symbol — so nothing is linked on a guess: the IDE confirms every candidate first, and links only what it can resolve unambiguously. Two files named app.css? No link at all, rather than a jump to an arbitrary one. A path that does not exist stays plain text. A link is never dead, and never takes you somewhere you did not ask for. Symbols resolve through Go to Symbol, so this works in every JetBrains IDE, not just the Java/Kotlin ones — and a link can only ever point inside your project or your own home, never at /etc/passwd, never at another user's files, not even through a symlink.
Also in this release
💾 The IDE sees Claude's writes immediately. The virtual file system was only refreshed at the end of a turn, so until Claude went idle the editor showed stale contents — and a link to a file Claude had just written opened nothing at all, because the IDE did not know that file existed yet. Every successful write now refreshes at once: by exact path for Edit/Write, and by re-scanning the project tree after a Bash command or a file-mutating MCP tool, which can change anything. Newly created files are picked up too (refreshing a file the VFS has never heard of is a no-op, so its parent directory is re-scanned as well).
💬 Fixed: a chat tab could come up unusable — the composer refused to take focus. A new tab (and sometimes the tabs restored at startup) gave you a chat you could not click into; the only cure was closing and reopening the tool window. And even when keystrokes did arrive, a fresh tab showed no caret. Both are gone: the tab now tells the platform where its keyboard focus lives and lets the IDE hand it over, and the web view is told it has the focus once the chat actually exists — which is when there is a caret to paint.
v4.2.0 — Protocol upgrade (2.1.204) + Background tasks card
Protocol upgrade to claude 2.1.204 / SDK 0.3.204, plus a new dashboard card.
🗂 Background tasks in the session dashboard
The plugin now understands the binary's background_tasks_changed signal and shows a Background tasks card (with Stop) listing everything running in the background.
It's a level signal — the binary re-sends the full live set on every membership change — so unlike the edge-derived Subagents list it can never wedge a stale "running" indicator on a missed start/stop bookend. It's deliberately kept uncorrelated with the subagent stream (the SDK leaves their relative ordering unspecified) and resets whenever the CLI process restarts.
🔁 Retry progress for /btw
Progress for long-running side questions (control_request_progress) is now recognised: an API retry is surfaced as a "Retrying (attempt n/m)…" notice instead of being silently dropped.
🧩 Protocol surface
- Models the new
system/background_tasks_changedandsystem/control_request_progressmessages. - Triages the new
list_models,get_planandget_workspace_diffcontrol requests — thin-client dialogs the plugin knowingly never sends (its model catalog comes from theinitializereply). ./gradlew checkDriftgreen at the new baseline.- Backward-compatible with older binaries.
Compatibility: JetBrains IDEs build 252 (2025.2) → latest EAP/RC (verified Compatible IC-252 → IU-262).
Install: Settings → Plugins → ⚙ → Install Plugin from Disk… → claude-code-native-4.2.0.zip.
🔐 Verification
sha256 4e5819b2f252d27a917123c856bd1918d20e5bb9d68cbc51bb395bd58e608ca0 claude-code-native-4.2.0.zip
GPG-signed (detached, same key as the commits) — claude-code-native-4.2.0.zip.asc and …sha256.asc.
Signing key: 6CD3 0675 6132 C6FD DEE8 8A74 CD0C 12D8 3C04 435A — Lain.
gpg --verify claude-code-native-4.2.0.zip.asc claude-code-native-4.2.0.zip
sha256sum -c claude-code-native-4.2.0.zip.sha256