# MCP server: give coding agents your project rules and findings
Source: https://docs.iriscode.co/agents/mcp
Use iris mcp to give supported coding agents the resolved project configuration, deterministic findings, gate result, dependency risk, explanations, preview-first safe fixes, and refactor plans with verification.
MCP connects an AI coding agent to tools it can call while working. The Iris Code MCP server lets the agent scan your project, check a proposed package and verify a refactor without asking you to run each command. Your project and team permissions control whether the agent can access repository tools.
## Running the server
```bash npm theme={null}
npx -y @iris-code/mcp
```
```bash Installed CLI theme={null}
iris mcp
```
```bash Bundled binary theme={null}
iris-lsp --mcp
```
All three run the same server. The `npx` form needs no install and is the one to use with agents that
are themselves npm-installed. `iris-lsp --mcp` suits editors that already bundle the Iris Code
language-server binary, and needs no Node.js.
The server writes only protocol messages to standard output, so do not wrap it in a script that
prints anything there.
## Which folder it analyses
`--root` is the boundary: nothing outside it is read, and nothing outside it is written. Iris Code
resolves it in this order.
1. **`--root`, if you pass it.** Your own explicit choice, never overridden.
2. **The workspace your editor reports.** Clients that support MCP's `roots` capability tell the
server which project is open, so no flag is needed. Iris Code asks for this at startup.
3. **The directory the client launched Iris Code in.** The fallback when neither of the above
applies.
Most clients land on the right folder through step 2, which is why the commands above pass no flag.
Leaving it off is the right default if you work in more than one project: register the server once,
and each editor window roots itself in the project it has open.
Pass `--root` when you want the boundary pinned regardless of client behaviour, such as a scripted or shared setup where the launch directory is not dependable. Pin it per project, though.
A `--root` registered once for every workspace points every workspace at that one folder: an agent working in project B is then answered about project A, and `iris_fix_safe` writes into A. Iris Code cannot detect that for you, because a pinned root is a legitimate instruction.
`--root .` is not a pin. It resolves to the directory the client launched the server in, which is what
Iris Code would have used anyway, and passing it suppresses step 2. Omit the flag instead.
Where several workspace roots are open, Iris Code analyses the first and names the others it is not
analysing. It does not merge them: separate roots are usually separate projects with their own
config and their own gate, so one blended score would describe neither.
Iris Code writes a ready line to standard error as soon as it starts, followed by the resolved folder
and where it came from, so your client log shows what it is actually looking at. Nothing but JSON-RPC
goes to standard output.
Started in a terminal rather than from a client, the server has nobody to talk to and will simply
wait. It says so, and prints the configuration to paste, rather than sitting there silently.
Iris Code refuses to start if the folder turns out to be a whole filesystem, your home directory, or
a directory containing home directories. Granting an agent read and write access across any of those
is not something a project setup needs, and it is the kind of mistake that is otherwise invisible.
## Client setup
With the Iris Code extension installed, VS Code discovers the server through its own MCP server
picker. Choose **Iris Code** there; no JSON entry is needed, and VS Code supplies the workspace
boundary for each session.
For every other client, register the command as an MCP server:
```json theme={null}
{
"command": "npx",
"args": ["-y", "@iris-code/mcp"]
}
```
Claude Code, Codex, Cursor, Windsurf and Copilot agent mode all accept a command of this shape; the
file it goes in differs per client. Zed can point at `iris-lsp --mcp` when the binary is installed.
Naming no folder is what lets one registration cover every project you open, and it works when your
client either reports MCP roots or starts the server in the workspace. Verified on real clients:
| Client | How it resolves | Registration |
| - | - | - |
| Claude Code | reports MCP roots | user or project level |
| Cursor | reports MCP roots | user or project level |
| Codex | starts the server in the workspace | user or project level |
| JetBrains AI Assistant | neither - see below | per project, via the plugin |
| Windsurf, Copilot agent mode, Zed | not yet confirmed | check the startup line |
After connecting, the startup line on standard error states the folder and where it came from, so you
can confirm your client rather than assume. `from your editor, over MCP roots` and `from the working
directory` both resolve correctly when that directory is the project.
### JetBrains IDEs
The Iris Code JetBrains plugin bundles `iris-lsp`, so it can provide the same server without Node.js. With a project open, choose **Tools | Iris Code | Set Up MCP Server**. The action copies a complete stdio configuration with the bundled executable's absolute path and `--root` set to the open project.
Paste it into the MCP settings for the AI assistant you use in that IDE. The plugin does not change an assistant's configuration itself.
This one pins the folder, and that is not a precaution - it is required. JetBrains AI Assistant reports no MCP roots, starts stdio servers from the IDE installation directory rather than the open project, and exposes no environment variable naming the project.
A bare command there resolves to the IDE's own install folder, which is not your code. The plugin action fills in the pin so you never type a path, but it is per project: paste it into that project's settings rather than a global one, and generate it again for the next project.
The bare command resolves the current release each run, so agents pick up fixes without anyone
editing a configuration file. Write `@latest` if you would rather that were explicit:
```bash theme={null}
npx -y @iris-code/mcp@latest
```
In controlled environments, where a client must not pick up a change you have not reviewed, append
an exact version from [the npm page](https://www.npmjs.com/package/@iris-code/mcp) instead.
An absolute path to a local `dist/cli.js` also works, but it never updates, so prefer the `npx` form
outside development.
## Tools
| Tool | Tier | What it returns |
| - | - | - |
| `iris_check` | Free | Findings for a path, with rule ids, lines and severities |
| `iris_config` | Free | The project's resolved thresholds, weights and gate limits |
| `iris_fix_safe` | Free | Preview, then apply, only the fixes Iris Code can prove safe |
| `iris_refactor_plan` | Free | A refactor plan for a file: proved mechanical fixes, then suggestions ordered by risk, with a session `baseline_id`. See [agent integration](/refactor/agents) |
| `iris_verify_refactor` | Free | Whether a refactor improved, did not improve or regressed against the session baseline or git HEAD, with a correction |
| `iris_check_package` | Free | Whether one package is safe to add, and the version to use instead. See the [package guard](/agents/package-guard) |
| `iris_explain` | Pro | Findings plus the rule detail behind them |
| `iris_gate` | Pro | Whether the configured gate would pass, and which files fail which rule |
| `iris_check_dependency` | Pro | OSV advisories with severity, confidence and fix version |
`iris_check`, `iris_fix_safe`, `iris_refactor_plan` and `iris_verify_refactor` are Free on a single
file. Folder and workspace scope are Pro, as they are in every other Iris Code surface. When
`iris_check` fails a file for size or complexity, the result says a refactor plan is available.
Pro tools stay in the tool list and describe themselves as Pro rather than being hidden, so your
assistant can tell you what an upgrade would add. Entitlement is checked when a tool runs, not when
it is listed, so a cached tool list cannot be replayed for Pro work.
## Findings
`iris_check` returns every rule that fired, each anchored to a line and carrying its severity
(`critical`, `high`, `medium` or `low`) and a stable id.
This is a wider list than `iris check --format github` produces in CI. That surface writes one
error-level annotation per finding into a budget of roughly ten visible per step, so it carries
error-severity findings only. An agent has neither limit and can act on the rest, so it receives
them.
Large results are bounded. When a scan exceeds the response cap, the lowest severities are dropped
first and the response reports `totalFindingCount`, `returnedFindingCount`,
`findingCountsBySeverity` and `droppedFindingCountsBySeverity`. A critical finding is never dropped
to make room for a low one, and an agent can see that something was withheld rather than presenting
a partial answer as complete.
## Explanations
`iris_explain` returns the finding report plus a context bundle for each finding, so an agent can act
on the cause rather than the label.
For a threshold rule, the bundle carries the measured value, the configured limit, and the overage:
a 302-line file against a 300-line limit reports `actual: 302`, `limit: 300`, `overBy: 2`. The limit
quoted is the one that actually applied to that file, so a per-language `languages` block or a nested
`.irisconfig.json` in a monorepo package is reflected rather than the workspace default.
Every bundle also carries the file's imports and exports, and the other findings in the same file, so
a fix can account for what the file is for and what else is wrong with it.
Rules without a configured number - a hardcoded secret, a security smell - carry the finding's own
message and that same file context. Iris Code does not invent remediation prose for them.
## Safe fixes
`iris_fix_safe` previews by default. The preview lists the exact changes it would make and returns a
`preview_token`.
Writing requires `apply: true` and that token. It is single-use, expires after ten minutes, and
authorises exactly the change set the preview showed, so Iris Code only ever applies edits that have
already been put in front of you. A confirmation flag alone would not give you that, because the
model sets the flag.
For unused JavaScript imports, removing the last binding keeps the module load as `import './a'`. Whole TypeScript value imports are left for review because the load depends on compiler settings; `import type` can be removed. See [fixes and suggestions](/refactor/reference).
Eligible fixes can span several lines: a debug statement on its own, an unused variable inside a function that is assigned a literal, a whole unused import or an unused name in an import, and two compatible imports merged into one. The [`iris fix` reference](/cli/commands#iris-fix) lists the languages and refusals for each.
The same fixes are offered as a Quick Fix in VS Code and by `iris fix --safe`, from one shared planner, so the surfaces agree. `console.error` is never removed, because it reports real errors.
Refusals are per finding, with a reason, and one refusal does not block the rest. Iris Code declines
anything it cannot prove:
* A hardcoded secret is never auto-fixed. Rotating a leaked credential and moving it to an
environment variable is not a mechanical edit.
* A debug print sharing its line with real code is left alone, because deleting the line would take
the code with it.
* Anything needing a name or a judgement, such as extracting a magic number or shortening a long
function, stays the agent's work.
Every applied change is followed by re-analysis, reported as a score change and a count of findings
resolved.
## Long scans
Code analysis runs under a fifteen-minute deadline in a separate process, so a large workspace cannot
hang the server.
Dependency scans allow one active scan per workspace and return `SCAN_IN_PROGRESS` for a second
request. If your client sends a progress token, the server reports phase and package counts as it
goes, and warns at two minutes. Cancelling returns the partial results collected so far, marked as
partial, rather than discarding them.
## What the server will not do
Analysis is local. Your code is not uploaded, and there is no shell-execution tool and no generic
"run any Iris Code command" passthrough, which would route around each tool's own policy.
Every requested path must resolve inside `--root`. That is checked after resolving symlinks, so a
link pointing outside the workspace is refused rather than followed.
Secret values are never returned, only the masked form every other Iris Code surface shows.
`iris_check_dependency` and `iris_check_package` are the tools that reach the network, querying public package registries and OSV.dev. Both require `allow_network: true` on each call unless you have already allowed network lookups from the CLI, disclose what they are about to do, and fall back to a local cache.
The agent's flag is never saved as your consent, and it cannot override lookups you have turned off with `iris deps --revoke-network`.
## Licensing
The server resolves your licence from the same credentials the CLI uses, so signing in once with
`iris auth login` covers the CLI, your editor and MCP. The offline grace period applies here too.
A Free licence calling a Pro tool gets an explicit refusal naming the feature and where to upgrade,
which your assistant can relay. It is never a silent empty result.
## Organisation and repository restrictions
The MCP server refuses repository tools with `HANDOFF_FORBIDDEN` when an organisation, project or committed `"agentHandoff": false` setting forbids the handoff. Tools stay listed and enforcement occurs on every call. The result names the restriction and has no upgrade link. `iris_check_package` keeps answering.
See [Team governance](/teams/governance) for policy scope, session refresh, signed-out caching and the limits of these controls.
[Refactor agent integration](/refactor/agents) describes deterministic plans, session baselines and opt-in finish verification.
# Package Guard: check a package before it is installed
Source: https://docs.iriscode.co/agents/package-guard
Stop a coding agent installing a known-vulnerable, malicious, yanked or brand-new package version, and get the version to use instead. Works as a hook for Claude Code, Cursor, Codex, GitHub Copilot, Gemini CLI and Windsurf, an MCP tool, and the iris install command.
A package is code written by someone else that your project uses. A coding agent can install one before you have reviewed its name or version. The free package guard checks the requested version before installation and can stop packages with known risks.
## What it checks
Each package gets one verdict:
| Verdict | Meaning | What happens |
| - | - | - |
| `ok` | No known vulnerability, a stable release, not deprecated | The install goes ahead |
| `warn` | A prerelease, a deprecated version, a release under three days old, or an advisory below your block severity | The install goes ahead, and the agent is told why |
| `block` | A known vulnerability at or above your block severity, a yanked version, or a release younger than your minimum age | The install is stopped |
| `malicious` | The package or version is in the OpenSSF malicious-packages feed | The install is stopped, whatever your block severity |
| `unchecked` | The guard could not check it: offline, a registry error, a git or local dependency, or a version range it cannot read | Your policy decides; the default is to warn |
A package the guard could not check is never reported as safe. The verdict says `unchecked` and
names the reason.
The registry and OSV.dev are separate services, so one being down does not hide the other. If the registry does not answer but you asked for an exact version, that version is still checked against OSV.dev, and a known serious vulnerability still blocks.
Only the checks that need the registry, such as release age, are reported as not run. For Maven, when Maven Central's search API is slow, versions are read from the repository's own `maven-metadata.xml`, which has no publish dates, so release age is not checked for that lookup.
When a version is blocked, the guard names a replacement: the newest stable release with no advisory at or above your block severity, preferring one in the same major version as the one requested, since that is the upgrade least likely to break code already written against it.
When no safe version exists it says so, and when every published version is a prerelease or deprecated it says that too.
Advisories come from [OSV.dev](https://osv.dev), which includes the OpenSSF malicious-packages
feed. Versions, publish times and deprecation notices come from the registry itself: npm, PyPI,
crates.io, the Go module proxy, RubyGems, NuGet and Maven Central.
## Installing for coding agents
The strongest layer is a hook the agent runs before every shell command. It works whether or not the
agent follows any instructions, because it runs outside the agent.
```bash CLI theme={null}
iris hook agent install
```
```text VS Code theme={null}
Sidebar → Hooks → Package guard → Install now
Command palette → Iris Code: Check Packages Before Agents Install Them
```
```text JetBrains theme={null}
Iris Code panel → Hooks → Package guard → Install now
Find Action → Iris Code: Check Packages Before Agents Install Them
```
The **Hooks** section of the sidebar shows whether the hook is installed, on every plan, beside the
git and build hooks. It reads the hook files themselves, so it stays right however the hook was
installed. A hook with its settings entry but no launcher, or the reverse, checks nothing and is
shown as incomplete, with a **Repair** button.
With no `--target`, the command installs for the agents your project already shows signs of using (a `CLAUDE.md`, `GEMINI.md` or `.github/copilot-instructions.md` file, or a `.claude/`, `.cursor/`, `.codex/`, `.gemini/`, `.windsurf/` or `.devin/` folder), or Claude Code when there are none. Pass `--target` with one agent, a comma-separated list such as `--target claude,codex`, or `all`.
In VS Code and JetBrains IDEs the picker is multi-select, with the agents the project already uses ticked.
| Agent | `--target` | Hook file | Hook |
| - | - | - | - |
| Claude Code | `claude` | `.claude/settings.json` | `PreToolUse` on `Bash` |
| Cursor | `cursor` | `.cursor/hooks.json` | `beforeShellExecution` |
| Codex | `codex` | `.codex/hooks.json` | `PreToolUse` on `Bash` |
| GitHub Copilot | `copilot` | `.github/hooks/iris-package-guard.json` | `preToolUse` |
| Gemini CLI | `gemini` | `.gemini/settings.json` | `BeforeTool` on `run_shell_command` |
| Windsurf | `windsurf` | `.devin/hooks.json` and `.windsurf/hooks.json` | `pre_run_command` |
Each also gets a small launcher in a `hooks` folder beside its hook file, such as
`.claude/hooks/iris-package-guard.cjs`. Existing settings are kept: Iris Code adds its own entry and
touches nothing else, and running the install again changes nothing. A hook file that is not valid
JSON is refused rather than rewritten.
Some agents need one step of their own before a project hook runs:
* **Codex** runs hooks only when they are switched on. Add `hooks = true` under `[features]` in
`~/.codex/config.toml`, then approve the project hook once with `/hooks`.
* **GitHub Copilot**: one file serves Copilot CLI, the Copilot cloud agent and VS Code agent mode. The
cloud agent reads hooks from the default branch, so there it applies once the file is merged.
* **Gemini CLI** asks once before it runs a new or changed project hook.
* **Windsurf** became Devin Desktop and reads `.devin/hooks.json`, falling back to
`.windsurf/hooks.json` in older builds. Both are written, and the hook never runs twice.
Agents without a hook that runs before a shell command, such as Zed and JetBrains AI Assistant, can
still ask before installing through the [`iris_check_package`](#asking-from-an-agent) MCP tool.
`iris init` offers the hook as one of its questions, and defaults to yes when it finds signs of a
coding agent in the project.
### Committing it
Commit the settings files and the launchers so your team gets the guard too. They hold no paths or
secrets.
The launcher runs the guard installed on each person's machine at `~/.iris/bin/guard.js`, which the
VS Code extension, the JetBrains plugin and `iris hook agent install` keep up to date. On a machine
without Iris Code the launcher lets every command through, so a teammate who has not installed it is
never blocked by it. The launcher needs Node.js on the `PATH`.
The hook files are read from the project root, so install the hook there and start the agent there. Claude Code, for example, reads `.claude/settings.json` from the root of the git repository, or from the folder it was started in when there is no repository.
Started in a subfolder of a project that is not a git repository, it does not see the hook, and installs it runs are not checked.
### What the agent sees
For a command that installs nothing, which is almost every command an agent runs, the hook exits in
the time it takes Node.js to start, with no network request.
For a blocked install, the agent is stopped before anything is installed and given the reason and
the replacement:
```text theme={null}
Iris Code package guard blocked this command before anything was installed.
- lodash@4.17.20: Known vulnerability CVE-2020-28500 (medium, fixed in 4.17.21): Regular Expression Denial of Service (ReDoS) in lodash. Known vulnerability CVE-2021-23337 (high, fixed in 4.17.21): Command Injection in lodash. Known vulnerability CVE-2025-13465 (medium, fixed in 4.18.0): lodash vulnerable to Prototype Pollution via array path bypass in `_.unset` and `_.omit`. Install 4.18.1 instead: `npm install lodash@4.18.1`
Run this instead: `npm install lodash@4.18.1`. Do not retry the blocked version.
```
That is real output for `npm install lodash@4.17.20`. The high-severity advisory blocks it at the
default policy; the two medium ones are listed so the agent has the whole picture.
A warning lets the install run and adds a note to the agent's context, for example that it installed
a prerelease and which stable release exists.
The hook never approves a command on the user's behalf. It blocks, warns, stays silent, or hands the
decision to the person (see [Installing a whole project](#installing-a-whole-project)), so the
permission prompts you have configured for your agent still apply.
### Which commands it recognises
`npm install`, `pnpm add`, `yarn add`, `bun add`, `pip install` (including `python -m pip`), `uv add`, `uv pip install`, `poetry add`, `cargo add`, `go get`, `go install`, `gem install`, `bundle add` and `dotnet add package`, including chained commands (`cd web && npm i x`) and command substitution.
It also recognises installs that name no package and install a whole project: `npm install`, `npm ci`, `pnpm install`, `yarn`, `bun install`, `pip install -r `, `uv sync`, `poetry install`, `bundle install`, `dotnet restore`, `go mod download` and `cargo fetch`. Those are covered in [Installing a whole project](#installing-a-whole-project).
An install from a git URL, a tarball or a local path cannot be looked up in a registry, so it is
reported as `unchecked` rather than ignored.
## Checking a package yourself
npm, pnpm and the other package managers have no hook that runs before `install ` and is told
which package is being added. So a command you type yourself cannot be intercepted, and the guard does
not pretend otherwise. For your own installs, use `iris install`:
```bash theme={null}
iris install lodash@4.17.20
iris install requests --check # check only, install nothing
```
`iris install` checks every package you name, prints the verdicts, and then runs your project's own
package manager, found from its lockfile, with the same packages. A blocked package stops the whole
run before anything is installed. `--force` installs anyway and prints what was overridden.
| Exit code | Meaning |
| - | - |
| 0 | Installed, or clean with `--check` |
| 1 | Blocked |
| 2 | Usage error |
If the package manager itself fails, its exit code is passed through.
Each verdict lists the five most serious advisories and counts the rest by severity; `--verbose` lists
them all with links, and `--format json` always includes every one. `iris remove` is the counterpart:
it removes packages with the same package manager, and checks nothing. `iris add` and `iris i` are
accepted as other names for `iris install`.
## Installing a whole project
A common attack on developers is a fake job interview or "take-home task": clone this repository and
run `npm install`. The repository needs nothing unusual to do harm. Either its lockfile pulls in a
malicious package, or its own `package.json` has a `preinstall` or `postinstall` script, which runs
the moment the install starts. A per-package check never sees either, because the command names no
package.
`iris install` with no package names checks the whole project before installing it:
```bash theme={null}
git clone https://example.com/take-home-task.git
cd take-home-task
iris install
```
It reads every package the lockfile would install, including dependencies of dependencies, and checks
them all against OSV in one batch request, then looks up the direct dependencies and anything OSV
flagged in full. A known-malicious package anywhere in the tree stops the install before anything
runs.
Known vulnerabilities do not stop a whole-project install. A package already pinned in a lockfile with a published CVE is the normal state of most real projects, and blocking it would stop `npm install` almost everywhere. They are listed as warnings instead, and [`iris cve`](/cli/commands#iris-cve) is where they are dealt with.
A package you name, as in `iris install lodash@4.17.20`, is still blocked for a known vulnerability.
Before running the install, it shows the repository's own install scripts word for word and asks whether to run them, install with them turned off (`--ignore-scripts`), or cancel. The default is to turn them off. With no terminal to ask in (a CI job, or `--format json`), they are turned off unless you pass `--yes`.
Dependencies that run install scripts of their own are named as well.
```bash theme={null}
iris install --check # check the whole project, install nothing
iris install --ignore-scripts # install without running the project's scripts
iris install -r requirements/dev.txt
```
The agent hook uses the same check when an agent runs a bare install. A malicious package blocks the command. When nothing is blocked but the repository runs its own install scripts, Claude Code, Cursor and GitHub Copilot show the person a confirmation naming the scripts.
Codex, Gemini CLI and Windsurf have no way to ask, so the agent is told to show the scripts to the user before going on.
A lockfile is needed to check the full tree. Without one, only the declared dependencies are checked,
at the newest version their ranges allow, and the output says so. For Go, the list comes from
`go.mod`, which already names every module the build uses. Install scripts are an npm, pnpm, Yarn and
Bun concept; the other package managers have no equivalent to show.
## Asking from an agent
Agents connected to the [Iris Code MCP server](/agents/mcp) can ask before they install, with the
`iris_check_package` tool. It takes an `ecosystem`, a `name` and an optional `version` (exact, a
range or a tag), returns the verdict with a one-paragraph summary written for the agent, and a
`suggestedCommand` when a safer version exists. It is the same check the hook runs.
When the hook is installed, [project rules](/agents/project-rules) generated by `iris rules` gain a
short section telling agents to check dependencies before adding them and not to retry a blocked
version.
## In the editor
When a manifest or lockfile changes on disk, VS Code and the JetBrains IDEs check the packages that
were added or changed and show one notice if any would be blocked. This happens seconds after an
install someone typed, so the notice says the package was "just added". Each package version is
reported once: per workspace in VS Code, per session in JetBrains IDEs.
Every project folder in the workspace counts, not just the top one: a `go.mod` in `services/gateway`
or a `requirements.txt` in `analytics` raises the notice the same way. Installed packages and build
output (`node_modules`, `vendor`, `target`, virtual environments) are never read.
A Python requirement is a pinned version only when it uses `==`. `requests>=2.19.0` is checked as a
range, against the newest release it allows, because that is what pip installs.
The notice needs network lookups. If this machine has not answered that question yet, the first manifest change asks it, naming the packages that changed and what is sent: package names and versions only, never your code. Answer yes and those packages are checked straight away, with a result either way.
Close the dialog and it asks again next session. In VS Code, **Don't ask again** stops it. The answer is shared with `iris install` and the agent hook.
## Before a push
This requires **Iris Code Pro**.
With `packageGuard.prePush` set to `true`, the [git hook](/enforcement/git-hook) also checks the
packages added or changed in your lockfiles since the upstream branch, and blocks the push on a
blocked or malicious one. It reads `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, `poetry.lock`,
`uv.lock`, `Pipfile.lock`, `Gemfile.lock`, `packages.lock.json`, `Cargo.lock` and `go.sum`.
It uses the same check as the agent hook, so a package the hook blocked cannot get in by being added
by hand and pushed. A push that changes more than 300 packages at once checks the first 300 and says
how many it did not check.
A git hook installed before Iris Code 1.33.0 does not run this step. Run `iris hook git install`
again to update it.
## Configuration
The policy lives in `.irisconfig.json` under `packageGuard`, so it travels with the repository.
Every field is optional.
```jsonc theme={null}
{
"packageGuard": {
"onUnchecked": "warn",
"blockSeverity": "high",
"minimumReleaseAgeDays": 7,
"allow": [
{ "name": "left-pad@1.3.0", "reason": "Pinned by the legacy build, tracked in SEC-41" }
],
"prePush": false
}
}
```
| Field | Default | Effect |
| - | - | - |
| `onUnchecked` | `"warn"` | `"block"` stops packages the guard could not check. The default is to warn, so an offline laptop does not stop an agent working |
| `blockSeverity` | `"high"` | Advisories at or above this severity block; lower ones warn. An advisory with no published severity warns |
| `minimumReleaseAgeDays` | unset | Blocks versions published more recently than this. When unset, a version under three days old warns |
| `allow` | `[]` | Reviewed exceptions: `name` or `name@version`, each with a required `reason` |
| `prePush` | `false` | Pro: the pre-push lockfile check above |
A freshly published version of a popular package is the usual window for a hijacked publish, which
is why very new releases warn by default and `minimumReleaseAgeDays` can make them block.
An `allow` entry without a `reason` is ignored and reported as an error, the same rule as an
[`iris-ignore` suppression](/enforcement/suppressions). An allowed package still appears in the
guard's output, naming the entry and its reason, so a bypass is never silent.
The Free presets do not restrict this block: it is a safety policy, available on every plan.
The [Config Studio](/configuration/config-studio) sets `onUnchecked`, `blockSeverity`,
`minimumReleaseAgeDays` and `prePush`. The `allow` list is edited in the file itself, because its
entries belong to one repository, and syncing from the studio keeps it.
## Network and privacy
Checking a package needs the registry and OSV.dev. Only package names and versions are sent, never
your code, file paths or project name. Network lookups are asked about once, by `iris install`,
`iris hook agent install` or the editor command, and can be turned off with
`iris install --revoke-network`.
Registry data is cached for 24 hours and advisory data for 6 hours, per user under `~/.iris/cache/` and never inside the project, so a repository cannot ship an answer with its code. Advisories are
cached for less time than elsewhere in Iris Code because a package newly reported as malicious should
not keep passing for a day.
The hook itself never prompts. If network lookups have not been allowed on a machine, its verdicts
are `unchecked` with that reason, and `onUnchecked` decides.
When you are not signed in, the guard also sends an anonymous daily count of its checks: the
ecosystem, the verdict, the surface and, for a hook, the coding agent. It never sends a package, a
path or anything about the project, and it sends nothing before network lookups are allowed. Set
`DO_NOT_TRACK=1` or `IRIS_TELEMETRY=off` to turn it off. See
[Network requests](/trust/security-and-support#network-requests).
## What this does not do
* It does not intercept a package-manager command you type yourself. Use `iris install`, or rely on the
editor notice and the pre-push check.
* It does not guess at typosquats. It reports a package as malicious when OSV's malicious-packages
feed does, and does not suggest a "did you mean" of its own.
* It does not check licences, and it does not judge what an install script does. It shows the
repository's own scripts before they run and names the dependencies that have them.
* A named install checks the packages the command names. A whole-project install checks the full
lockfile, and so does the pre-push step.
* A whole-project install stops only on a malicious package, not on a known vulnerability; see
[Installing a whole project](#installing-a-whole-project).
* Maven Central and several other registries do not report deprecation, so for those the deprecation
check does not run. A clean result says which checks it could not run.
* Java projects get the editor notice (`pom.xml`, `build.gradle`, `build.gradle.kts` and
`gradle/libs.versions.toml`) and the `iris_check_package` tool, but not `iris install` or the pre-push
step: Maven and Gradle do not commit a lockfile by default, so there is no resolved list to compare.
## Free and Pro
The agent hooks, `iris install`, `iris_check_package` and the editor notice are free. The pre-push
lockfile check needs Iris Code Pro.
## Uninstalling
```bash theme={null}
iris hook agent uninstall
```
Or run **Iris Code: Remove the Package Guard Hook** in either editor. Only Iris Code's own entry and
launcher are removed; other hooks in the same files are left alone, and a settings file that held
nothing else is deleted.
# Project rules: generate agent instructions from your Iris Code config
Source: https://docs.iriscode.co/agents/project-rules
Use iris rules to write the project's configured limits, naming conventions, and gate rules into the instruction files supported coding agents already read.
Project rules are written instructions that tell a coding agent how your project should be built. Iris Code can generate these instructions from your configuration, so the agent sees the same standards that your scans and gates use. These instructions guide the agent; a gate provides the separate check that can stop a change.
## Generating the file
```bash CLI theme={null}
iris rules
```
```text Editor theme={null}
Command palette → Iris Code: Write Project Rules for AI Agents
```
Both write the same file. In the editor you get a picker for the target and a confirmation before
anything is written; in the terminal, pass `--yes` to skip the prompt in a script.
## Targets
`AGENTS.md` is the default because most agents read it.
| Flag | File | Read by |
| - | - | - |
| *(default)* | `AGENTS.md` | Most coding agents |
| `--target claude` | `CLAUDE.md` | Claude Code |
| `--target cursor` | `.cursor/rules/iris.mdc` | Cursor |
| `--target copilot` | `.github/copilot-instructions.md` | GitHub Copilot |
The Cursor file carries the frontmatter Cursor expects (`alwaysApply: true`), so the rules apply to
every request rather than only when a glob matches.
### Writing more than one
Teams routinely run two agents. Claude Code reads only `CLAUDE.md`, while `AGENTS.md` is the file
Codex, Cursor and Copilot read, so a project using both needs both files.
```bash theme={null}
iris rules --target agents --target claude # the two most common
iris rules --all # all four
iris rules --refresh # update the ones this repo already has
```
In the editor the picker is multi-select: tick every file your team's agents read and they are all
written from one confirmation.
Each file gets the full rules rather than a pointer to another file. A `@AGENTS.md` import is Claude-specific syntax that reads as noise to every other tool, and a symlink does not survive a Windows checkout or a zip export.
Every file owns its own block independently, so there is nothing to keep in sync by hand: run the command again and all of them update.
`--refresh` is the one to use in a script. It updates the files that already exist and creates
nothing, so it cannot add an agent file to a repository whose conventions you do not own. If nothing
is there yet, it says so rather than creating a default.
If one file cannot be updated, the others still are. A damaged file is reported with its reason and
left exactly as it was.
## What the file says
Imperatives with your numbers in them, grouped into sections: size and shape, things not to write,
file naming, suppressions, and what blocks a push. A project with a 250-line file limit and a
kebab-case convention produces rules that say exactly that.
When size or complexity limits are set, a section on refactoring an over-limit file describes the
[refactor playbook](/refactor/agents) loop: run `iris refactor` or `iris_refactor_plan`, apply proved
fixes first, verify after each move, and stop after the configured `maxCorrections` to show you the
verdict and diff. It works without MCP.
Only what is configured appears. Turn off magic-number detection and that rule is absent rather than stated and contradicted. When the [package guard](/agents/package-guard) hook is installed, the file also tells agents to check a dependency before adding it and not to retry a blocked version. Set no naming policy and there is no naming section.
Set gate limits and they appear under **What blocks a push**, because that is the part an agent most needs to know before it finishes.
Gate limits and per-language blocks are included even on a Free licence. The file describes the
config your project committed, which is what runs in your CI and on your teammates' machines, so
understating it would produce rules that contradict your own gate.
## Regenerating
The block sits between markers:
```markdown theme={null}
...generated rules...
```
Everything outside them is yours and is never touched. Run the command again after changing
`.irisconfig.json` and only the block updates. Run it when nothing has changed and it reports the
file is already up to date rather than rewriting it, so it produces no diff.
If a marker has been deleted, the command refuses and says so instead of guessing which lines it
owns. Restore the marker or delete the block, then run it again.
Nothing regenerates automatically. Iris Code will not rewrite a committed file as a side effect of a
config change.
## Committing it
Commit the file. The rules are for whoever, or whatever, writes code in the project next, and a rules
file that only exists on your machine helps only your agent.
## What this does not do
* It does not check the agent's work. That is what `iris check`, the [git hook](/enforcement/git-hook) and the [MCP server](/agents/mcp) are for, and the generated file points the agent at them.
* It does not send your code or your config anywhere.
* It does not format anything. Iris Code analyses and diagnoses; your editor's formatter owns document edits.
# Iris Code Changelog: VS Code, JetBrains, and CLI Release Notes
Source: https://docs.iriscode.co/changelog
Full release notes for the Iris Code VS Code extension, the JetBrains plugin, and the @iris-code/cli npm package. Updates ship automatically through the VS Code Marketplace, Open VSX, and the JetBrains Marketplace.
These are the release notes for the Iris Code VS Code extension, the JetBrains plugin, and the `@iris-code/cli` npm package. Updates ship automatically through the VS Code Marketplace, Open VSX, and the JetBrains Marketplace - you do not need to reinstall to receive new versions.
***
## v1.36.1 - 2026-10-07
### Fixed
* **Fixed** safe fixes so removing an unused import never stops its module loading. JavaScript keeps the load as `import './module'`, a TypeScript value import whose names are all unused is left for you because whether it loads depends on `verbatimModuleSyntax`, and `import type` is still removed.
* **Fixed** refactor verification so an `.irisconfig.json` edited, added or removed after the plan reports `regressed`, because a loosened limit could hide a new finding. The plan's `refactor` settings are kept until you request a new plan.
* **Fixed** MCP refactor plans and verification so files in a folder where the agent handoff is off are left out, with only a count reported.
* **Fixed** verification so unsaved editor buffers are compared by content, including files other than the planned one.
## v1.36.0 - 2026-10-07
**feature** - The [refactor playbook](/refactor/overview) gives a long file a concrete plan and checks the result
### The refactor playbook
**Refactor plan** on the File, Folder and Workspace tabs opens the Refactor Playbook panel in VS Code and JetBrains IDEs. **Get plan** reads the sources locally, detects the conventions the repository already follows, and returns mechanical fixes Iris Code can prove safe plus ranked structural suggestions. **Verify refactor** compares the result with the snapshot taken at planning time. Iris Code does not call a model. Planning and verifying one file is free; folder and workspace plans require **Iris Code Pro**.
* **Added** **Apply mechanical fixes**, which makes the safe fixes as one undoable editor change. Structural suggestions carry their evidence, risk and destination and are never applied automatically.
* **Added** **Open agent instructions**, which shows the plan as readable instructions and exact JSON for a coding agent.
* **Added** verification that reports improved, not improved or regressed, and catches lines saved only by packing code or deleting comments, broken imports, new import cycles, removed exports that are still imported and new findings. **Copy correction for your agent** turns a regression into the next step.
* **Added** the `iris_refactor_plan` and `iris_verify_refactor` MCP tools, free for one file and Pro for a folder or workspace.
* **Added** `iris refactor `, which prints a plan, and `iris refactor --verify`, which compares the file with git HEAD.
* **Added** optional finish verification for Claude Code, Codex, GitHub Copilot, Gemini CLI and Cursor with `iris hook agent install --verify-on-finish`.
* **Added** the [`refactor` block](/refactor/configuration) in `.irisconfig.json`. Top-level settings are free; per-language settings require Pro.
Suggestion coverage differs by language. The [language coverage table](/refactor/language-coverage) lists which suggestions are available for each one.
### Changed
* **Changed** safe fixes to handle debug statements, literal-only variables and imports that span several lines, to merge compatible imports from the same module, and to remove an unused name from an import that is otherwise used.
* **Changed** safe fixes to leave more changes for you to make by hand when removing them could change behaviour, including class attributes, script-wide globals and Go package variables, imports in Vue and Svelte components, and debug statements whose arguments do work.
* **Changed** `console.error` so it is no longer removed by a safe fix, because it reports real errors.
### Fixed
* **Fixed** agent policy in edge cases, including a repository whose `.irisconfig.json` cannot be read and brief file-access errors on Windows.
* **Fixed** `iris_check` failing on an oversized file outside the project root.
***
## v1.35.0 - 2026-10-05
**feature** - Teams decide whether coding agents can use Iris Code, with [agent policy](/teams/governance)
* **Added** agent policy for Teams. Owners and admins can turn off the Iris Code agent handoff on members' machines, in the team's projects or in one project. Every repository tool in the MCP server then refuses and names who turned it off. The package guard keeps working.
* **Added** the `agentHandoff` setting. `"agentHandoff": false` in `.irisconfig.json` turns the handoff off for everyone who opens the repository, on every plan.
* **Added** policy refresh while an agent runs. The VS Code extension refreshes the policy at startup, when the window regains focus and every ten minutes, and every MCP tool call reads it. Offline and signed-out editors keep a remembered restriction for up to seven days.
* **Added** the policy on the licence card in VS Code and JetBrains IDEs, and policy changes in the Workspace history.
* **Added** hook receipts for Team projects. A push that passes the pre-push check records a receipt.
* **Added** `iris hook verify` (Pro), which warns in CI when a pushed commit has no hook receipt. It never fails the pipeline.
***
## v1.34.0 - 2026-09-30
**feature** - Repeat [workspace and folder scans](/features/workspace-analysis#scanning-again) reuse the analysis of files that have not changed
* **Added** incremental scans in VS Code and JetBrains IDEs. A repeat workspace or folder scan reuses the analysis of every file whose contents and settings are unchanged, and works out the totals, unused packages and duplicate findings again from the current files. The result matches a full scan.
* **Added** a notification after each scan saying how many files were analysed and how many were reused.
* **Added** **Rescan everything**, in the Workspace tab, the folder view and the command palette, which analyses every file again.
* **Added** the email of the account you are signed in with, on the licence card at the bottom of the sidebar.
The CLI, git hooks, CI, the MCP server and cloud scans still analyse every file on each run.
***
## v1.33.0 - 2026-09-29
**feature** - The [package guard](/agents/package-guard) checks a package before it is installed, by you or by a coding agent
### The package guard
The package guard works out which version an install would actually add, then blocks a known-vulnerable, malicious, yanked or, if you choose, too-new version and names the version to use instead. A package it could not check is reported as not checked, never as safe. It is free on every plan.
* **Added** hooks for Claude Code, Cursor, Codex, GitHub Copilot, Gemini CLI and Windsurf. Run **Iris Code: Check Packages Before Agents Install Them**, or `iris hook agent install`, and every package the agent tries to install is checked first.
* **Added** whole-project checks. When an agent runs `npm install`, `pip install -r requirements.txt`, `bundle install` or another install that names no package, every package the lockfile would install is checked, and a malicious package blocks the install. The repository's own install scripts are shown and asked about.
* **Added** `iris install` and `iris remove`, which check packages and then run the project's own package manager. With no package names, `iris install` checks the whole project first.
* **Added** the `iris_check_package` MCP tool, which answers whether one package is safe to add.
* **Added** a notice in VS Code and JetBrains IDEs when a manifest or lockfile gains a package that would be blocked, in any project folder of a monorepo.
* **Added** `packageGuard` in `.irisconfig.json`: `onUnchecked`, `blockSeverity`, `minimumReleaseAgeDays`, reviewed `allow` exceptions and, on Pro, `prePush` to check packages added in pushed lockfiles. The Config Studio sets the policy.
* **Added** a package guard row in the sidebar's Hooks section, which now appears on every plan.
When you are not signed in, the guard sends an anonymous daily count of its checks: the ecosystem, the verdict, the surface and, for a hook, which agent. It never sends a package, path or project, and only after you have allowed dependency lookups. `DO_NOT_TRACK=1` or `IRIS_TELEMETRY=off` turns it off.
### Changed
* **Changed** the dependency cache location to `~/.iris/cache/`, per user, instead of `.iris-cache/` in the project. Iris Code no longer edits your `.gitignore` for it, and an existing `.iris-cache/` folder can be deleted.
* **Changed** the Dependencies table, `iris deps` and `iris cve` to include nested Go, Python and Ruby projects, such as a monorepo's `services/gateway/go.mod`.
### Fixed
* **Fixed** a Python range being reported as the installed version. Only `==` pins a version now; without a lockfile, a ranged requirement is shown as not checked.
* **Fixed** Config Studio sync removing `ignoreFiles`, `ignoreFunctions`, `testConvention` and the package guard's `allow` list from your config.
* **Fixed** the Config Studio missing controls for naming rules, severity overrides, scoring weights and inline findings, and writing a cleared gate limit as `0`.
* **Fixed** `iris cve` letting a malicious-package report pass at the default `--severity high`.
## v1.32.0 - 2026-09-27
**feature** - [Change Review](/features/change-review) shows which files import each file you changed
### Import impact
* **Added** import impact beside each changed file in Change Review. Expand the count to open the files that import it. Pro also shows the number of transitive dependents, capped at 500+, and a summary of other files the whole change set reaches.
* **Added** import impact to `iris_check` for each checked file: direct importers on Free, plus the transitive count on Pro. `iris check --changed` and `--staged` include it in JSON and as one line per file in pretty output. Impact informs a review and does not change gate decisions or exit codes.
* **Added** automatic import mapping. A review that opens without an import graph builds one, in VS Code and JetBrains IDEs, and asks first in a very large workspace. An unmapped file is never shown as having zero importers.
* **Added** imports through a package. In Go, a C# `using`, a Java wildcard import or a Python namespace package, an import names the package rather than the file; those importers are counted and labelled "through its package".
### Fixed
* **Fixed** the focused [Code Map](/features/code-map) Graph and `iris graph --focus` drawing a file imported through its package as if nothing imported it.
* **Fixed** a pause of about 5 seconds on the first Iris Code action after a JetBrains IDE starts. The language server was rewritten on every start, so Windows scanned it as a new program each session; it is now extracted once and reused.
* **Fixed** Change Review hiding each finding's message on narrow panels.
* **Fixed** Change Review showing Windows paths with backslashes.
* **Fixed** `iris_check` reporting impact from imports as they were when the session started. It now re-reads files changed on disk.
## v1.31.0 - 2026-09-25
**feature** - Code Map shows how your files import each other, and exports as a picture, Mermaid or a table
### The import graph
Switch the [Code Map](/features/code-map) from **Tree** to **Graph** to see how files import each other. Each line is an import; selecting it lists the lines it came from and opens them in the editor, and cycles are marked. The workspace scan that scores the tree also maps the imports, so there is nothing separate to build.
* **Added** the Graph for TypeScript, JavaScript, Vue, Svelte, Go, Python, Java, C# and Rust. A Go or Java package, or a C# namespace, is drawn as its folder, because that is what the import names.
* **Added** a list of every import Iris Code could not resolve, each with its reason, rather than a guess.
* **Added** the same Graph to the account dashboard's Code Map for each cloud audit.
* Free shows the focused file with its direct imports and importers; Pro adds Folder and Workspace scope.
An import graph only shows imports. Files in the same Go or Java package, C# namespace or Rust module can use each other without one, so those uses are not drawn, and the canvas says so.
### Export
* **Added** export for the Tree and the Graph: PNG, SVG, Mermaid, a Markdown table, CSV and JSON. An export is exactly what is on screen. Mermaid and Markdown can also be copied to the clipboard; GitHub draws Mermaid when it is pasted into a README or pull request.
* Export is free, and a Free export contains only what a Free user can see.
### CLI 1.22.0
* **Added** [`iris graph`](/cli/commands#iris-graph): the same graph as text or JSON. `--focus ` is free; the whole graph needs Pro. Unresolved imports never change the exit code.
### JetBrains
* **Fixed** the progress bars not appearing during a workspace or folder scan.
## v1.29.2 - 2026-09-22
**fix** - text written between tags is content, and is no longer counted as code
### Finishing what 1.29.1 started
1.29.1 stopped an apostrophe in a React component being read as the start of a string. That fixed one character. This release fixes the category: **text between tags is a string literal, delimited by tags instead of quotes**, and is never counted as code.
* **Fixed** a bracket written as text being counted in the bracket balance. Markup that renders a code sample - a page showing a JSON snippet, written as `]` - put that bracket into the count, so the file was reported as a syntax error and **every finding in it was discarded**.
* **Fixed** a regular expression inside a `${...}` template swallowing the rest of the file. The scanner knew about strings, backticks and braces inside a template but not regular expressions, so the quote in an everyday CSV escape - `` `"${String(cell).replace(/"/g, '""')}"` `` - opened a string and ran on to the next quote.
* **Changed** a code sample rendered as markup so it is no longer counted as a declaration. A component displaying `export async function cancelOrder(...)` as text on the page had that counted as a real function in the file.
* **Changed**, same as 1.29.1 and in the same direction: **your gate result may change on upgrade**, because files that were silently scoring 0 now report what is in them.
### How it was checked
The fix was measured rather than argued: 1,258 real files from four codebases were analysed before and after, and diffed file by file. Files reported as syntax errors went from **two to none**, and exactly **three** other files changed, each one explained - the two that had been scoring 0, and the component that had been counting a function out of a code sample.
The two gaps [v1.29.1](#v1-29-1-2026-09-22) recorded as known and unfixed are both closed by this, because prose stops being code once the analyser knows where the markup region is.
## v1.29.1 - 2026-09-22
**fix** - an apostrophe in ordinary prose was hiding whole files from the analyser
### A file that scored 0 looked exactly like a clean file
JSX children are not quoted, so a sentence written as ordinary text inside a React component put a bare `'` into the analyser's scanner every time it contained a possessive or a contraction: `this project's limits`, `each rule's threshold`, `agents don't read them`. That apostrophe was read as the start of a string, and everything after it - up to the next apostrophe or the end of the file - stopped being treated as code.
The consequence was not a wrong finding. It was **no findings at all**: the file's brackets no longer balanced, so it was reported as a syntax error, scored 0, and every real finding in it was discarded. Two of Iris Code's own screens had been in that state.
* **Fixed** a quote directly preceded by a letter or digit opening a string. No valid JavaScript or TypeScript string literal is ever written that way, so this costs nothing and is decided by a single character. Backticks are exempt, because a backtick after a name is a tagged template.
* **Fixed** an unterminated quote being honoured to the end of the file. A quote that never closes was not a string opener, and the scan now resumes after it instead of blanking everything below.
* **Changed**, and worth expecting: **your gate result may change on upgrade.** Files that were silently scoring 0 now report what is actually in them, so a project can move from pass to fail without a line of its code changing. Those files were never being measured; the new number is the honest one.
The same fix reaches the [Playground](https://www.iriscode.co/playground) and the Config Studio, which run the real engine in your browser.
### Known, and deliberately not fixed here
A bracket written as literal text in JSX - `]`, which happens when markup renders a code sample - is still counted as a delimiter and can still report a false syntax error. It has the same root cause: this scanner is lexical and has no concept of a JSX text region. Closing it properly is its own change.
## v1.29.0 - 2026-09-21
**change** - a gate that cannot verify your licence now fails the build instead of passing it
### Two changes that affect CI results
Both can turn a passing pipeline red the first time you run them. In each case the old behaviour reported a pass it had not established, so the new failure is the accurate answer rather than a regression in Iris Code.
* **Changed** a Pro command whose licence cannot be verified to exit **3** instead of `0`. It used to skip and report success, so a brief licence-server outage turned `iris gate` into a silent no-op and let a build through that should have been blocked. Exit 3 is separate from 1 so a pipeline can distinguish "the gate failed" from "the gate never ran". See [CI exit codes](/cli/ci).
* **Unchanged** for users without a Pro licence. That case is *known*, not unverified, so those commands still skip and exit `0` and your build stays green. Only a licence that exists but could not be checked fails closed.
* **Fixed** `--config` so it decides the rules rather than only `minHealthScore` and the printed summary. `iris gate --config shared.json` applied one field from the file you named; every other threshold came from whatever `.irisconfig.json` sat in the scanned project, while the table displayed the values you had asked for. A config named explicitly now wins, and a project without the flag still resolves its own as before. `iris check` and `iris report` had the same fault.
### The dependency table
* **Fixed** the table not scanning when you open it. It showed "No scan yet" and waited for a click even on a project it had scanned before, because a cached scan older than 24 hours was read as no cache at all.
* **Changed** the first open to still show the Scan button, so the network-access prompt follows a click rather than appearing because you opened a panel. Every open after that scans by itself.
* **Fixed** clearing the cache reading as "nothing here" while the rescan it had just started was already running.
### JetBrains
* **Fixed** the Trends and Git History Secrets tables opening blank. The panel delivered a table's first state before the page was listening, and Trends sends its state exactly once, so it was lost for good.
* **Fixed** the sixth and later tables opening blank when several were open at once. Each table held a connection for as long as its tab existed and the embedded browser allows only six, so which table failed depended on the order you opened them.
### Licences and the CLI
* **Fixed** a sign-in through a development build replacing your real credentials. Both wrote to the same file, so a token issued by a local backend overwrote the production one and every command afterwards failed to validate.
* **Fixed** every command re-checking your licence over the network. A successful check is now reused for ten minutes, so a script or CI job that runs several commands makes one request instead of one per command. `iris auth status` and `iris auth login` always check for real, and signing out clears it.
### Accuracy
* **Added** `deep-nesting` to the measured corpus, taking it to [38 rules and 174 rule-language pairs](/trust/accuracy-benchmark). The rule now counts control blocks rather than indentation, so a widely-indented file is no longer reported as deeply nested and a minified one no longer escapes.
* **Fixed** file naming not being checked on a file whose contents fail to parse. The parse-error path reported "no naming problem" rather than "never looked", so those files silently passed the rule.
***
## v1.28.0 - 2026-09-16
**change** - one name per concept, across the editors, the CLI and these docs
### Every command that runs an analysis is called Scan
The Command Palette offered two verbs for a single act. `Scan Workspace` sat beside `Analyse Current File`, and the Folder tab told you to choose `Iris: Analyse This Folder`, a name no registered command had. The JetBrains plugin carried its own copies of these titles and had drifted from VS Code.
* **Changed** `Analyse Workspace` to `Scan Workspace`, `Analyse This Folder` to `Scan This Folder`, `Analyse Current Folder` to `Scan Current Folder`, `Analyse Current File` to `Scan Current File`, and `Analyse This File` to `Scan This File`.
* **Changed** `Restart First Review` to `Restart First Scan`. It restarts the first file scan; a review is the changed-files feature.
* **Unchanged** command ids. Any keybinding you have already set still works.
* **Fixed** six places in the in-app documentation that named commands prefixed `Iris:` rather than `Iris Code:`, including `Install Git Hook`, `Install Build Hook`, `Add GitHub Actions Workflow`, `Add CI Pipeline Snippet` and `Open Dependents Table`. Following any of those instructions found nothing.
### One name per concept everywhere else
* **Changed** the Issues tab to the Findings tab. A finding is what Iris Code reports; "issue" also meant a real defect and a support report.
* **Changed** "Project Review" to a workspace scan. The behaviour is identical: your first full workspace scan is still free, still saved locally, still needs no account.
* **Changed** cloud audits to cloud scans, and the Teams entity that owns seats and billing from a "workspace" to a **team**. Workspace now means the project folder open in your editor and nothing else, because that is what VS Code uses the word for.
* **Added** a [terminology reference](/reference/terminology) listing every term and the ones it replaced, and inline definitions of finding, blocker, warning, gate and threshold on the [home page](/).
### Corrections to these docs
* **Fixed** Gate Preview and Quick Start stating five presets while the configuration pages stated six. The `ai-assisted` preset, minimum score 82, was missing from both.
* **Fixed** the CLI overview omitting `iris init`, `iris fix`, `iris mcp`, `iris rules` and `iris auth` while linking to a page describing all seventeen commands.
* **Fixed** the Languages table on the home page listing six of ten languages, silently omitting Ruby, C#, Java and Rust.
* **Fixed** Gate Preview claiming it would block commits. It blocks pushes.
***
## v1.27.0 - 2026-09-15
**feature** - one-command setup, provably safe fixes, and presets that enforce what they display
### `iris init` sets a project up in one command
It asks three short questions, recommends one of the shipped presets and explains why, writes a commented `.irisconfig.json`, creates or refreshes the `AGENTS.md` rules block, and installs the pre-push hook where the licence allows it. Every step says what it did or why it skipped it. Re-running preserves an existing config, and `--yes` takes the Balanced default without prompting.
### `iris fix --safe` removes only what it can prove is safe
Three cases qualify: a line holding nothing but a debug statement, a literal-only unused variable, and a standalone unused Java import. Everything else refuses with a reason, per finding rather than per run, and a hardcoded secret is never auto-fixed.
* **Added** the same fixes as a Quick Fix in VS Code. The lightbulb on an eligible finding offers to remove it, sharing one planner with the CLI and the MCP server so all three agree on what is safe.
* Previews by default. `--apply` asks before writing, and requires `--yes` when there is nobody to ask.
### A preset now enforces what it displays
* **Fixed** `presetId` being honoured in the editor and silently dropped by the CLI, the git hook and CI. A project on `strict` saw strict thresholds while its hook and CI enforced the shipped defaults: one repository judged by two standards, with nothing on screen saying so. Expect your gate to start enforcing the threshold it has been displaying. `legacy` gates at 60, `balanced` at 70, `typescript` at 76, `strict` and `ai-assisted` at 82, `security` at 85.
* **Changed** presets to carry gate limits, so choosing one configures the whole gate rather than only the score. Gate limits remain a Pro feature.
* **Changed** `strict`, `security` and `ai-assisted` to check filenames across the whole project rather than only newly added files. Set `"fileNamingScope": "new-files"` to keep the old behaviour.
### Smaller additions
* **Added** stale-rules detection. Saving `.irisconfig.json` compares the Iris Code block in files it has already written and suggests `iris rules --refresh` when it no longer describes the config. It never edits a committed file.
* **Added** native MCP server discovery in VS Code, so no hand-written JSON entry is needed.
* **Fixed** Husky and custom hook paths. Iris Code follows Git's active `core.hooksPath`, so the block lands in `.husky/pre-push` with your script preserved.
* **Fixed** a rate-limited licence check switching Pro off. The endpoint locks out per IP rather than per key, so a shared network could trip it; that response was read as a definitive "you are on Free". A rate limit is now treated as transient and nothing is revoked.
***
## v1.26.0 - 2026-09-08
**feature** - cloud audits and team evidence
### Audits that run without your editor open
Connect a GitHub repository once and Iris Code audits it on the events you choose: every change that lands on your default branch, every pull request update, or both. It keeps the score, findings and dependency report against the commit that produced them.
Each repository decides its own triggers, because a merge costs one audit and a pull request costs one per update pushed to it. Merges are on by default; pull requests are opt-in. See [Cloud scanning](/cloud/overview).
### Team evidence, from the editor and from CI
* **Added** `teamProjectId` in `.irisconfig.json`, binding a repository to a team project. Every completed workspace scan and every `iris gate` run then records privacy-minimal evidence against it. The value is opaque and is not a repository URL.
* **Added** the option to the JSON schema, so editors complete and validate it.
* Team evidence records execution outcomes and aggregate counts. It never contains source, file paths, repository URLs, snippets, or finding text.
### Fixed
* **Fixed** a dependency scan being discarded in order to save its cache. Creating the cache directory happened outside the write's error handling, so a read-only checkout aborted the whole scan after every registry and advisory lookup had already completed.
***
## v1.25.2 - 2026-08-31
**fix** - accuracy corrections across JavaScript and TypeScript analysis
### Files that could not be read are no longer scored as though they were bad
Iris Code reported some valid JavaScript and TypeScript files as unparseable, scored them zero, and failed the gate on them. Two causes: brackets inside regular expressions were counted as code, and a URL in JSX text was read as the start of a comment, which swallowed the rest of the line. Both were reported on the same day from two unrelated projects. Ships with `@iris-code/cli` 1.17.3.
* **Fixed** false parse errors on regular expressions containing brackets or braces, such as `/\[([^\]]*)\]/`, and on `.jsx`/`.tsx` files with a URL in visible markup. Twelve files in one project moved from a score of 0 to between 92 and 100.
* **Fixed** a related error where a self-closing JSX tag with an attribute, such as ``, caused the rest of the component to be misread.
* **Changed** how an unparseable file affects your results. It is now excluded from the workspace average and from the list of files below the minimum score, and is reported separately as a file that could not be analysed. A syntax error your compiler already catches no longer blocks the gate twice.
* **Added** `gateMaxParseErrors` for teams that want unparseable files to fail the gate. Unset by default. See [.irisconfig.json](/configuration/irisconfig).
### React components are measured by their logic
"Function too long" counted JSX markup as function length, so it fired on about a third of `.tsx` files against 2% of `.ts` files. A component with twenty lines of hooks and sixty lines of markup was reported as an eighty-line function.
* **Changed** function length in `.jsx` and `.tsx` to exclude JSX markup. Logic inside markup, such as an inline handler or a `.filter().map()` chain, still counts. Vue and Svelte have always worked this way, so this brings React into line with them.
* **Unchanged** file length and functions-per-file, which still count every line. A 400-line component is still reported as a long file.
* `.tsx` files score about 1 to 2 points higher as a result, measured across two projects. This corrects a measurement rather than reflecting a change in your code. If you use `gateBaselineMode` or follow the trend chart, re-lock your baseline after updating.
* Files with no JSX are measured exactly as before, verified across 422 files.
### Smaller corrections
* **Fixed** storage keys being reported as hardcoded secrets. `const KEY = "da-os-canvas"` carried the same penalty as a live provider token. A value that a secret-named variable holds, such as `CLIENT_SECRET`, still reports.
* **Fixed** TODO and FIXME going undetected in block comments. This affected every language variant, and mattered most in React, where a block comment is the only comment form available inside markup.
* **Fixed** a localhost mention inside prose being reported as a hardcoded endpoint. Documentation explaining how to connect to a local database was flagged on every mention.
* **Fixed** sample code quoted inside a string being detected as a real function.
***
## v1.25.1 - 2026-08-31
**fix** - MCP server setup
### The documented MCP setup no longer names a folder
Every published example pinned `--root /path/to/project`, including the configuration block most people copy. That flag stops Iris Code asking your editor which project is open, so one pinned entry reused across projects answered all of them about the folder it named, safe fixes included.
* **Changed** the documented setup to `npx -y @iris-code/mcp` with no folder, which is what makes a single registration correct for every project you open.
* **Changed** `--root` to be documented as a per-project pin rather than a default. `--root .` pins nothing: it resolves to the directory the server was started in, which is already the fallback.
* **Fixed** the server printing nothing at startup, which made a server that was waiting for its client indistinguishable from one that had failed. It now reports the folder it is analysing and where that came from. Run in a terminal by hand, it explains that it is a Model Context Protocol server and shows the configuration to paste.
* Root resolution itself is unchanged. See [MCP server](/agents/mcp).
***
## v1.25.0 - 2026-08-28
**feature** - MCP server for AI coding agents
### Your agent can ask what your project actually enforces
An agent asked to clean up a file has no way to know what your project counts as clean, so it guesses, and its guess is not the standard your pre-push hook uses. Iris Code now runs as a Model Context Protocol server, so Claude Code, Codex, Cursor, Windsurf, Copilot agent mode, Zed and JetBrains AI Assistant can ask for your real thresholds instead. Delivered through `@iris-code/cli` 1.17.0, so it needs no editor. See [MCP server](/agents/mcp).
* **Added** six tools. Free: findings for a single file, the project's resolved configuration, and safe fixes on a single file. Pro: folder and workspace scope, rule detail, gate status, and dependency risk. Pro tools stay listed and identify themselves as Pro rather than hiding.
* **Added** preview-first safe fixes. Applying requires a token from a previous preview, so a change set you have not seen cannot be written. Refusals are per finding with a reason: a hardcoded secret is never auto-fixed, and a debug print sharing its line with real code is left alone.
* **Added** findings carrying every rule with its real severity and a stable id, rather than the error-level list CI annotations are limited to. Oversized results drop the lowest severities first and report what was withheld.
* **Added** **Tools | Iris Code | Set Up MCP Server** in JetBrains IDEs, which copies a ready-to-paste configuration using the bundled binary and needs no Node.js installation.
* Analysis stays on your machine. Only dependency and advisory lookups reach the network, and those ask first.
### Changed
* **Changed** the finding list used by CI annotations to exclude debug prints. That list produces one error-level annotation per entry with about ten visible per step, so a low-severity rule in it both misreported severity and could push a hardcoded-secret annotation out of view. Health scores are unchanged, and agents receive the complete list through MCP.
***
## v1.24.0 - 2026-08-25
**feature** - project rules for AI agents
### Your standards, in the file your agent reads
An agent writing code in your repository does not know your file-length limit, your naming convention, or the score your pre-push hook blocks below. It finds out when something fails, which is after the work is done. `iris rules` writes those rules into the file the agent already reads, generated from `.irisconfig.json`. Nothing is analysed, nothing is sent anywhere, and no model is involved. Ships alongside `@iris-code/cli` 1.16.0. See [Project rules](/agents/project-rules).
* **Added** `iris rules` and the editor command **Iris Code: Write Project Rules for AI Agents**, reachable from the command palette, the File tab, the welcome page, and in JetBrains from Find Action. Free.
* **Added** support for writing several files in one run. Claude Code reads only `CLAUDE.md`, while `AGENTS.md` is the file Codex, Cursor and Copilot read, so a team running two agents needs both. Each file receives the full rules rather than a pointer to another file, and the editor picker accepts more than one.
* **Added** `--all` to write every target, `--refresh` to update only the files a repository already has and create none, and `--dry-run` to print the result instead of writing it. Each file is written independently, so one file that cannot be updated is reported with its reason while the rest still change.
* **Added** markers around the generated block, so regenerating updates only Iris Code's own section and never text you wrote. Running it again with an unchanged config reports the file is already up to date rather than producing a diff, and a file with a damaged marker is refused with an explanation rather than duplicated.
* **Changed** the rules to describe only what your project contains. A Java project is not told to avoid `any`, and the debug-output rule names `System.out.println` rather than listing every language's equivalent.
* **Changed** how a single-language project is described: where your config raises that language's limits, the rules state the effective numbers rather than the project defaults with a footnote. A rule that quotes a limit the gate does not enforce is worse than no rule.
* **Fixed** per-language overrides for Java and Rust being rejected by the config schema. Both languages have been analysed since 1.23.0, but the schema declared six language keys against the analyser's eight, so a valid `languages.java` block was reported as invalid in every editor with schema support.
***
## v1.23.1 - 2026-08-23
**fix** - workspace scans stay inside the workspace
### A scan covers the folder you selected
A workspace score is only useful if it describes the project it names. A symlinked file pointing outside the scanned folder was followed, so a file living elsewhere on the machine could be analysed, scored, and counted as part of the project.
* **Fixed** workspace scans following symlinked files out of the selected folder. Partial scans compare resolved paths, and full scans skip links.
Version 1.23.2 followed the same day with a fix to the release process itself and no change to analysis.
***
## v1.23.0 - 2026-08-17
**feature** - Java and Rust analysis, with Maven, Gradle, and Cargo dependency audits
### Java and Rust join the same engine
Java and Rust are analysed everywhere Iris Code runs - the editor, the CLI, hooks, and CI - which brings the total to nine languages. Both arrive with the full rule set rather than a subset: functions and methods, imports, nesting, complexity, magic numbers, debug output, TODOs, secrets, security smells, duplicate detection, suppressions, file naming, scoring, and gates. Ships alongside `@iris-code/cli` 1.15.0. See [Language support](/features/language-support).
* **Added** Java analysis for `.java` source and `.properties` configuration. A `.properties` file is read as configuration rather than code: it is scanned for committed credentials, and its ports and log levels are not reported as unexplained numbers.
* **Added** Rust analysis, covering functions, modules, imports, complexity, secrets, suppressions, naming, scoring, gates, and duplicate detection.
* **Added** three language-specific checks: process execution in Java through `Runtime.exec` or `ProcessBuilder`, and in Rust both `unwrap` or `expect` calls and explicit panics.
* **Added** Java to the catch-all exception rule that C# already used, under one shared rule id. The same defect has the same fix in both languages, so one severity setting and one `iris-ignore` comment cover a codebase that uses both.
* **Added** test-aware exemptions for Rust. Rust puts its tests in the file under test, so the exemption is computed per `#[cfg(test)]` item rather than per file. A file-level exemption would spare the production half of the same file.
* **Added** Java unused-import reporting. The usage search reads Javadoc, so an import referenced only from a `{@link}` is left alone, because removing it would break `javadoc`.
* **Added** Maven and Gradle dependency and CVE audits, covering `pom.xml`, `build.gradle`, `build.gradle.kts`, and version catalogues, including `${property}` placeholders and `dependencyManagement`. Neither tool commits a resolved dependency tree by default, so these scans cover declared dependencies and say so. See [Dependents Table](/features/dependents-table).
* **Added** Cargo dependency and CVE audits. `Cargo.lock` gives exact versions and the full transitive tree, with git and path dependencies excluded, because a public registry knows nothing about them.
* **Added** Java and Rust to the published accuracy benchmark, on 17 and 16 rules respectively. See [Accuracy benchmark](/trust/accuracy-benchmark).
* **Changed** advisories published without a severity to appear rather than be dropped. Most RustSec advisories carry no score, so they were hidden entirely. They now show as unrated and are counted, and they never satisfy a `--severity high` gate, because an unrated advisory is one nobody has scored rather than one known to be minor.
* **Fixed** committed credentials in Java source and `.properties` files going undetected. Java has no `const`, `let`, or `var`, so the name-based check matched nothing and only credentials with a recognisable provider format were ever reported. A database password has no such format.
* **Fixed** constants that name a credential field being reported as the credential itself, such as `PASSWORD_FIELD = "password"`.
* **Fixed** fixture credentials in Gradle test source sets being reported as production secrets. Gradle projects declare test source sets freely, and only `src/test` was recognised.
* **Fixed** a named constant being reported as a magic number in Java and Rust. Naming the number is the fix the rule asks for, so reporting it described the fix as the defect.
* **Fixed** a naming convention asking you to rename `application.properties`. Spring resolves that filename exactly, so the rename would have broken the application, and both the Quick Fix and the Pro batch rename offered to make it in one click.
* **Fixed** `iris sbom` reporting no dependencies for Java and Rust projects, and `iris deps`, `iris cve`, and `iris sbom` refusing pure Rust and pure Gradle projects while naming both manifest families in the error.
* **Fixed** Maven latest-version lookups in both directions. An upgrade is no longer offered to a date-based release from 2003, which sorts above every modern version, and a `-jre` or `-android` classifier is no longer read as a prerelease, which had left some packages with no known latest version at all.
* **Fixed** a Cargo dependency whose name is not a valid crate name being matched against an unrelated crate that occupies the same index path. It is reported as not checked.
* **Fixed** the Dependencies table flickering when a scan finishes. It rebuilt every row in order to re-sort them, which also closed any advisory panel you had expanded, and repeated on every filter click and search keystroke.
* **Fixed** large, heavily documented Java files analysing slowly. Analysis now scales in proportion to file size.
***
## v1.22.0 - 2026-08-13
**feature** - RubyGems and NuGet dependency audits
### Ruby and .NET dependencies are audited like every other ecosystem
The Dependents Table, `iris deps`, `iris cve`, and `iris sbom` now read Ruby and .NET manifests alongside npm, Go, and Python. `Gemfile.lock` gives the full resolved tree, so transitive gems are covered; NuGet without `packages.lock.json` sees the packages a project declares. Where a version cannot be resolved, the package is reported as **not checked** rather than assumed clean. See [Dependents Table](/features/dependents-table).
* **Added** RubyGems support: `Gemfile.lock` for exact transitive versions, with `Gemfile` and `.gemspec` as fallbacks. A gem pinned to an exact version is checked; a range is reported as not checked, because an advisory verdict against a range is either a false clean or a false alarm.
* **Added** NuGet support: `.csproj` `PackageReference` entries aggregated across every project in a solution, plus Central Package Management (`Directory.Packages.props`), `packages.lock.json`, and legacy `packages.config`. Two projects pinning different versions of one package are reported as a conflict rather than silently resolved to one of them.
* **Added** prerelease filtering for NuGet, so a `9.0.0-preview` is never offered as an upgrade from a stable release.
* **Added** Ruby to the published accuracy benchmark on every shared rule, not only its three Ruby-specific ones. See [Accuracy benchmark](/trust/accuracy-benchmark).
* **Changed** the dependency-lookup disclosure to name RubyGems.org and NuGet.org, the two new registries contacted. Only package names and versions are sent, as before.
* **Fixed** native gems being reported as vulnerable when already patched. A gem built for your platform is locked as `1.19.4-x64-mingw-ucrt`, and that platform suffix was read as part of the version, sorting it below the plain release - so advisories already fixed in the installed version still matched, and up-to-date gems sat permanently in "outdated". This affected most Ruby projects, since Rails pulls in several native gems.
* **Fixed** a gem RubyGems cannot resolve - yanked, renamed, private, or mistyped - appearing as outdated with an upgrade target of "unknown" instead of being reported as not checked.
* **Fixed** SBOM components with no resolvable version carrying a package URL whose version was placeholder text. The form was structurally valid, so a vulnerability scanner reading the SBOM failed to match those components without reporting a problem. They now carry a version-less package URL and state why.
* **Fixed** "Update all outdated" offering a command that covered only part of what it listed in a project using more than one package manager.
* **Fixed** the CLI reporting "Running as Free" when the licence server was unreachable but a cached Pro licence was still valid, and emitting the CI notice about skipped Pro steps on runs where nothing was skipped.
***
## v1.21.0 - 2026-08-12
**feature** - C# and .NET analysis
### C# joins the same engine
C# is analysed everywhere Iris Code runs - the editor, the CLI, hooks, and CI - with the same scoring, secrets detection, security smells, duplicate detection and suppressions as every other language. A `.csproj` is read as a manifest rather than as code: it contributes the packages your project depends on, and it is scanned for secrets, but its language version and warning level are build settings. See [Language support](/features/language-support).
* **Added** C# analysis for `.cs` and `.csproj`, covering methods and constructors, nesting and complexity, `using` namespaces and `PackageReference` entries, security idioms, secrets in fields, properties, locals and project XML, test conventions, suppressions, file naming, scoring, and gates.
* **Added** four C#-specific checks: `async void` outside event handlers, where a thrown exception cannot be observed by the caller; catch-all exception handlers, with `when (...)` filters exempt; LINQ chains of five or more operators in one statement; and types carrying too many responsibilities.
* **Added** .NET build hooks. A project with a `.csproj` or `.sln` gets an `iris-check` Makefile target wired as a prerequisite of `build` or `all`, defaulting to `dotnet build`.
* **Added** a `csharp` key to the `languages` block in `.irisconfig.json`, and severity overrides plus `iris-ignore` support for the four new rule ids.
* **Fixed** a verbatim Windows path breaking the file it appeared in. `@"C:\logs\"` was read as an unterminated string, so every brace after it counted at the wrong depth and the file was reported as a parse error with no findings at all. Raw strings failed the same way in the other direction. Both are now understood, along with the interpolated and doubled-quote forms.
* **Fixed** .NET test projects being treated as production code. C# has no `_test` filename separator and its test directory is `.Tests`, so fixture credentials and localhost URLs in tests were reported as real findings.
* **Fixed** project files reporting their language version and warning level as unnamed numbers, and C# attributes, `const`/`readonly` declarations and enum members now count as naming their own values.
* **Fixed** constructors with a `: base(...)` or `: this(...)` initialiser being skipped entirely, so no length, parameter, or nesting check applied to them, and a positional `record` being counted as both a type and a method.
* **Fixed** the C# debug-output Code Lens appearing in JetBrains only, and the File tab, Problems entries and inline diagnostic using the JavaScript wording for a C# file.
* **Fixed** the Iris Code panel opening blank in IntelliJ 2026.2 and other current JetBrains IDEs. The IDE moved its embedded browser out of the platform into a bundled component that Iris Code did not ask for, so the panel and the table tabs had nothing to draw with. Where an IDE cannot provide an embedded browser at all - some platforms and architectures do not ship one - the panel now offers to open the same interface in your own browser, and everything you click there still acts on the IDE. Analysis, diagnostics and enforcement were never affected.
* **Fixed** a type written entirely on one line - `class C { public void M() { } }` - reporting no methods, which exempted its members from the length, parameter-count and nesting checks.
* **Fixed** the Problems list showing raw rule ids as group headings for the C# checks and, since 1.20.0, the Ruby ones.
C# reports no unused members or unused packages, and no NuGet advisories. Dependency injection, reflection, partial classes and source generators reference code in ways static analysis cannot see, so an unused verdict is not one that can be stood behind. Dependency and CVE scanning covers npm, Go, and Python manifests only.
***
## v1.20.0 - 2026-08-10
**feature** - Ruby, Rails, and ERB analysis
### Ruby joins the same engine
Ruby is analysed everywhere Iris Code runs - the editor, the CLI, hooks, and CI - with the same scoring, secrets detection, security smells, duplicate detection and suppressions as every other language. Ships alongside `@iris-code/cli` 1.12.0. See [Language support](/features/language-support).
* **Added** Ruby analysis covering `.rb` and the Ruby DSL extensions, plus the extensionless files Ruby projects actually use: `Gemfile`, `Rakefile`, `Vagrantfile`, `Capfile`, the Fastlane set, and `.irbrc`. A file's language is not always decidable from its extension, so these are matched by name.
* **Added** ERB template support. Ruby inside `<% %>` and `<%= %>` tags is analysed while the surrounding HTML is excluded from scoring and duplicate matching, and findings report the line number in the template file itself.
* **Added** three Ruby-specific checks: `method_missing` defined without `respond_to_missing?`, god classes, and Rails parameters reaching a model without `require` and `permit`.
* **Added** Ruby build hooks. A project with a `Gemfile` gets an `iris-check` Makefile target wired as a prerequisite of `build` or `all`.
* **Changed** file renaming to warn on Ruby as it already did on Python. A rename does not rewrite `require_relative` paths or the constant names Rails derives from a filename.
* **Fixed** a Ruby guard clause such as `return 0 if value.nil?` being read as a missing `end`, which scored the file 0 and skipped its analysis.
* **Fixed** analysis speed on large files containing many findings. Several checks repeated whole-file work per finding, so cost grew with the square of the file size; a large Rails controller that took over a minute now completes in a fraction of a second. Findings are unchanged.
Ruby reports no unused imports or unused gems. Constants resolve at runtime through `send`, `const_get` and Rails autoloading, so an unused verdict is not one that can be stood behind. Dependency and CVE scanning covers npm, Go, and Python manifests only.
***
## v1.19.0 - 2026-08-08
**feature** - Slack notifications and a responsive Config Studio
### Gate results where the team sees them
Iris Code can send `iris check` and `iris gate` results directly from your own CI to Slack. The message never passes through Iris Code servers, and notification delivery never changes the command's exit code. Ships alongside `@iris-code/cli` 1.11.0. See [Slack notifications](/enforcement/slack-notifications).
* **Added** Slack notifications controlled by the `IRIS_SLACK_WEBHOOK` CI secret. Messages contain file paths, scores, rule names and counts, but never source, finding text or secret values.
* **Added** the free `iris slack setup`, `iris slack test` and `iris slack manifest` commands for guided setup, delivery testing and creation of a pre-configured Slack app.
* **Added** `--slack-on always` to report passing runs and `--slack-dry-run` to inspect the payload without posting.
* **Changed** the Config Studio on narrow screens. **Preview config** opens the live `.irisconfig.json` preview in a centred, scrollable modal, and account navigation opens from an animated menu instead of occupying the bottom of each page.
* **Fixed** file-naming violations appearing twice in the VS Code Problems panel.
***
## v1.18.0 - 2026-08-05
**feature** - Vue and Svelte analysis, plus file naming that follows folder roles
### Components are code too
Iris Code reads `.vue` and `.svelte` components: their `