# Changelog

All notable changes to `@iris-code/cli` are documented here.

## [1.20.2] - 2026-09-22

### Fixed
- **A bracket written as text inside a component no longer reads as code.** Markup that renders a code sample - `<span>]</span>` - put that bracket into the delimiter count, so the file was reported as a syntax error and every finding in it was discarded. Same defect as 1.20.1's apostrophe; both patches are replaced by the general rule that text between tags is content, never code.
- **A regular expression inside a `${...}` template no longer swallows the rest of the file.** The quote in an everyday CSV escape - `` `"${String(cell).replace(/"/g, '""')}"` `` - was read as the start of a string and ran on to the next quote, so everything after it stopped being analysed.
- **Your gate result may change on upgrade**, in the same direction as 1.20.1: files that were silently scoring 0 now report what is in them.

## [1.20.1] - 2026-09-22

### Fixed
- **An apostrophe in ordinary prose no longer blanks the rest of a React file.** JSX text is not quoted, so a sentence like `this project's limits` put a bare `'` into the analyser's scanner and was read as the start of a string. Everything after it, up to the next apostrophe or the end of the file, stopped being treated as code - which left brackets unbalanced, reported the file as a syntax error, and **discarded every finding in it**. A file in that state scored 0 and looked exactly like a file with nothing wrong. **Your gate result may change on upgrade**, in the direction of the truth: 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 not being measured before.

## [1.20.0] - 2026-09-21

### Changed
- **A Pro command whose licence cannot be verified now exits 3 instead of 0.** It used to skip and report success, so in CI a brief licence-server outage turned `iris gate` into a silent no-op: a build that should have been blocked passed with a green tick. Exit **3** is deliberately separate from 1 so a pipeline can distinguish "the gate failed" from "the gate never ran". A known-Free user is unaffected and still exits 0 - this only covers the case where a licence exists but could not be checked.

### Fixed
- **`--config` decides the rules, not just the summary.** `iris gate --config shared.json` and `iris check --config shared.json` applied only `minHealthScore` from the named file; every other threshold came from whatever `.irisconfig.json` sat in the scanned project, while the printed table showed the values you had asked for. A config named explicitly now wins, and a project without the flag still resolves its own as before. **Your CI result may change**, because the rules you asked for are the ones that now run.
- **A development sign-in no longer replaces your real credentials.** Both wrote to `~/.iris/credentials`, so a token issued by a local backend overwrote the production one and every command afterwards failed to validate. Development now uses its own file.
- **Repeated commands no longer re-check your licence every time.** A successful check is reused for ten minutes, so a script or CI job that runs several commands makes one request instead of one per command - which is what used to trip the licence endpoint's lockout on repeated failures. `iris auth status` and `iris auth login` always ask for real, and signing out clears the cached result.

## [1.19.0] - 2026-09-15

### Added
- **`iris init` sets up a project in one command.** It asks three short questions, recommends one of the shipped presets and says why, writes a commented `.irisconfig.json`, creates or refreshes the `AGENTS.md` rules block, and installs the pre-push hook for Pro licences. Every step reports what it did or why it skipped, running it again preserves an existing config, and `--yes` takes the Balanced default without prompting.
- **`iris fix --safe <file>` removes only what can be proved safe to remove.** 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. It previews by default; `--apply` confirms before writing and requires `--yes` when there is no terminal to ask at.
- **Husky and custom hook paths.** `iris hook git` follows Git's active `core.hooksPath`. In a Husky project the block goes in `.husky/pre-push`, preserving your script and leaving Husky's generated `.husky/_` files alone; any other `core.hooksPath` receives the block in that directory.

### Fixed
- **A rate-limited licence check no longer switches Pro off.** The lockout on the licence endpoint is per IP, not per key, so a shared network or a CI runner pool can trip it for someone who did nothing. That response was read as a definitive Free verdict and the cached entitlement was deleted with it, leaving no offline grace period to fall back on. It is now treated as the transient fault it is; a genuine rejection still revokes.
- **`presetId` now applies.** The CLI read `.irisconfig.json` but dropped its preset, so a project on `strict` was gated at the default 70 while the editor showed strict thresholds. Presets now set both the analysis thresholds and the gate score here, exactly as they already did in the editor: `legacy` 60, `balanced` 70, `typescript` 76, `strict` and `ai-assisted` 82, `security` 85. A value written explicitly in the config still wins over the preset. Run `iris check` before your next push if you would rather see the change than be blocked by it.

### Changed
- **Presets now carry gate limits**, so choosing one configures the whole gate rather than only the score. Gate limits remain Pro; a Free licence has them withheld whether a preset or a hand-edit supplied them.
- **CLI help now names `iris init`, `iris rules` and `iris fix`.** All three were usable and none was discoverable from `iris --help`.

## [1.18.0] - 2026-09-09

### Added
- **`iris gate` records Team audit evidence when a repository is bound to a Team project.** Set `teamProjectId` in `.irisconfig.json` and every gate run reports the health score, how many findings were open, how many were high severity, and the commit and branch it audited. No source, no file names, no findings text - the counts and the revision only, which is what the consent screen shown when you connect a project describes. A repository without `teamProjectId` reports nothing, exactly as before.
- **A gate run in CI now carries the policy version it enforced**, so a score that moved because the rules moved can be told apart from one that moved because the code did.

### Fixed
- **Canonical findings now include the cross-file duplicate pass.** They were built per file, before the pass that compares files against each other, so duplication findings were missing from the canonical set that the MCP tools and Team evidence read - while the CLI's own annotation output had them all along. The two now describe the same scan.

## [1.17.3] - 2026-08-31

### Fixed
- **`iris todos` now finds TODO and FIXME in block comments.** `/* TODO */` was missed everywhere, and in JSX a block comment is the only comment form available inside markup.
- **`iris security` no longer reports a localhost mention inside prose.** Documentation explaining how to connect to a local database was flagged on every mention; a real finding is a URL that is the value, not a sentence that mentions one.

### Changed
- **JSX markup no longer counts toward function length.** "Function too long" fired on about a third of `.tsx` files against 2% of `.ts` files, because half of a flagged component's measured length was markup rather than logic. A component with twenty lines of hooks and sixty lines of JSX was being reported as an eighty-line function.
- **`.tsx` scores rise by roughly 1 to 2 points on average** and "function too long" findings roughly halve, measured across two real projects. This is a correction, not an improvement in your code. If you run `iris gate` with `gateBaselineMode`, re-lock your baseline at this release so later runs compare against the corrected numbers.
- **`iris check` and `iris gate` still catch long components** - `file-too-long` and `too-many-functions` count every line including markup, so a 400-line component is still flagged as a long file. Only function length changed.
- Logic inside JSX expressions - inline handlers, `.filter().map()` chains - is measured as logic. Files with no JSX are byte-identical to before, verified across 422 real files.

## [1.17.2] - 2026-08-31

### Fixed
- **`iris mcp` now says it has started.** Run without `--root` it printed nothing at all on either stream, so a server that was up and waiting for its client was indistinguishable from one that had hung. It now writes a ready line to standard error naming the directory it will fall back to if your editor does not report a workspace.
- **Running it by hand explains itself.** If you start the server in a terminal rather than from an agent's configuration, it now tells you it is a Model Context Protocol server that speaks JSON-RPC over stdin, shows the configuration to paste, and says how to exit. Clients spawn the server with pipes rather than a terminal, so they never see this.
- Both messages go to standard error. Standard output remains the JSON-RPC channel and carries nothing else.
- `iris mcp --sdk` announces its root the same way.

## [1.17.1] - 2026-08-31

### Changed
- **`iris mcp` is now documented without `--root`.** The published setup pinned a folder in every example. Pinning stops Iris Code asking your editor which project is open, so one registration reused across projects answered all of them about the folder it named, `iris_fix_safe` included. The documented form is now `npx -y @iris-code/mcp`, which roots each session in whatever project that editor window has open.
- `--root <directory>` still pins the boundary when you want that, and is documented as a per-project setting. `--root .` pins nothing and is no longer suggested anywhere: it resolves to the launch directory, which Iris Code would have used regardless, while suppressing the question to your editor.
- Documentation only. Root resolution itself is unchanged.

## [1.17.0] - 2026-08-27

### Added
- **`iris mcp` - the agent handoff.** Runs Iris Code as a Model Context Protocol server over stdio, so Claude Code, Cursor, Windsurf, Copilot agent mode and Zed can ask for your real code-health numbers instead of inferring a standard. Install with `npx -y @iris-code/mcp`, or run `iris mcp` inside the project. `--sdk` serves the same tools through the official MCP TypeScript SDK.
- Six tools. Free: `iris_check` on a single file, `iris_config` for the project's resolved rules, and `iris_fix_safe` on a single file. Pro: folder and workspace scope, `iris_explain`, `iris_gate`, and `iris_check_dependency`. Pro tools stay listed and say they are Pro rather than hiding, so your assistant can tell you what an upgrade adds.
- **Findings carry every rule, its real severity and a stable id.** An agent gets the full picture - debug prints, TODOs and magic numbers included - rather than the error-severity list CI annotations are limited to. Oversized results drop the lowest severities first and disclose what was dropped, so a hardcoded secret is never truncated away to make room for magic numbers.
- **`iris_fix_safe` previews before it writes, and only writes what it showed you.** Applying requires a single-use token from a previous preview, so a change set nobody saw cannot be applied. Refusals are per finding with a reason: a secret is never auto-fixed, and a debug print sharing its line with real code is left alone because deleting the line would take the code with it. Every applied change is followed by re-analysis reporting the score change and how many findings were resolved.
- Long scans stay answerable: analysis runs under a deadline, dependency scans report progress and can be cancelled, and a cancelled scan returns partial results rather than nothing.
- Paths are confined to `--root`, checked after resolving symlinks. There is no shell-execution tool and no command passthrough. Secret values are never returned, only masked. Analysis stays on your machine; only dependency and advisory lookups reach the network, and those ask first.

### Changed
- `iris check` no longer lists debug prints among a file's findings. That list drives CI annotations, which are error-level and limited in number, so a low-severity rule in it both misreported severity and could push a real secret out of view. Debug prints still affect the health score exactly as before, and agents get the complete list through `iris mcp`.

## [1.16.0] - 2026-08-25

### Added
- **`iris rules`** writes this project's enforcement rules into the files your coding agents read: `AGENTS.md` by default, or `--target claude`, `--target cursor`, `--target copilot`. Free, and the only command that resolves no licence at all: it reads local config and writes a local file, so there is nothing to check and no network call to fail.
- `--target` may be repeated, `--all` writes all four files, and `--refresh` updates only the files this repository already has and creates none, which makes it the variant that is safe to run unattended. Each file is written independently, so one file that cannot be updated is reported and skipped while the rest still change; the exit code is 0 for a clean run and 2 if any file was refused.
- The content is derived from `.irisconfig.json`: thresholds, the naming convention and its per-path overrides, suppression policy, and the gate limits that block a push. Nothing is analysed and no AI model is involved.
- `--dry-run` prints the block instead of writing it. `--yes` skips the confirmation when the target file already exists, for scripted use. A file with a damaged `iris-rules` marker is refused with an explanation rather than duplicated.

## [1.15.0] - 2026-08-17

### Added
- **Java and Rust analysis.** `iris check`, `iris secrets`, `iris security`, `iris todos`, `iris gate`, and `iris report` all understand `.java`, `.properties`, and `.rs`, bringing the total to nine languages. Java reports unused imports and process execution; Rust reports `unwrap`/`expect` and explicit panics, both exempt inside `#[cfg(test)]` modules.
- **Maven, Gradle, and Cargo dependency audits.** `iris deps`, `iris cve`, and `iris sbom` read `pom.xml`, `build.gradle`, `build.gradle.kts`, Gradle version catalogues, `Cargo.toml`, and `Cargo.lock`. Cargo lockfiles give exact transitive versions; Maven and Gradle commit no resolved tree by default, so those scans cover declared dependencies only and say so.
- **Advisories with no published severity are shown as "unrated" rather than dropped.** They are counted and listed, and they never satisfy `--severity high` - an unrated advisory is one nobody has scored.

### Fixed
- Committed credentials in Java source and `.properties` files are now detected. Previously only credentials with a recognisable provider format were reported.
- Rust `static` declarations and struct literals are now checked for credentials.
- `iris sbom` no longer reports "no dependencies found" for Java and Rust projects, and `iris deps`/`iris cve`/`iris sbom` no longer refuse pure Rust and pure Gradle projects.
- Maven latest-version lookups no longer offer a 2003 date-based release as an upgrade, and no longer treat a `-jre` or `-android` classifier as a prerelease.
- A Cargo dependency whose name is not a valid crate name is reported as not checked rather than matched against an unrelated crate.
- A named constant is no longer reported as a magic number in Java or Rust.
- Large, heavily documented Java files analyse quickly again and scale in proportion to file size.

## [1.14.0] - 2026-08-13

### Added
- RubyGems and NuGet dependency audits: `iris deps`, `iris cve`, and `iris sbom` cover npm, Go, Python, RubyGems, and NuGet manifests. `Gemfile.lock` and `packages.lock.json` give exact versions; a version that cannot be resolved is reported as not checked rather than assumed clean.

### Fixed
- A native gem's platform suffix is no longer read as part of its version, so advisories already fixed in the installed version are not reported, and up-to-date native gems are not permanently listed as outdated.
- A gem RubyGems cannot resolve is reported as not checked rather than outdated with an upgrade target of "unknown".
- `iris sbom` components with no resolvable version carry a version-less `purl` plus an `iris:version-unresolved` property, instead of a `purl` whose version is the placeholder text - previously reachable for any range-only dependency. The old form was structurally valid, so scanners silently failed to match those components.
- When the licence server is unreachable, the CLI no longer prints "Running as Free" while honouring a cached Pro licence, and the GitHub Actions notice about skipped Pro steps is limited to runs where they are actually skipped.

## [1.13.0] - 2026-08-12

### Added
- **C# and .NET analysis** across `check`, `gate`, `secrets`, `security`, `todos`, and `report`. Covers `.cs` source and `.csproj` project files, the four C#-specific checks (`async void` outside an event handler, catch-all exception handlers, long LINQ chains, oversized types), suppressions, scoring, and the `languages.csharp` configuration block. A `.csproj` is read as a manifest: its `PackageReference` entries and any committed credential, never its build settings.
- `iris hook build install` detects a .NET project from a `.csproj` or `.sln` in the workspace root and generates a `dotnet build` target. The missing-manifest error now names every manifest the command accepts.

### Note
- .NET test projects are recognised, so fixture credentials and `localhost` URLs inside a `*.Tests` project or a `*Tests.cs` file are not reported by `iris secrets` or `iris security`.

## [1.12.0] - 2026-08-10

### Added
- **Ruby, Rails, and ERB analysis** across `check`, `gate`, `secrets`, `security`, `todos`, and `report`, including Ruby DSL filenames, native ERB lines, Rails-specific findings, suppressions, scoring, and per-language configuration.

## [1.11.0] - 2026-08-08

### Added
- **Slack notifications.** Set `IRIS_SLACK_WEBHOOK` and `iris check` / `iris gate` post locally; the webhook is environment-only so it cannot leak through a committed config, shell history or process arguments. `--slack-on always|failure` defaults to `failure`, and `--slack-dry-run` prints without sending. The payload carries file paths, scores, rule names and counts only. Only `https://hooks.slack.com` URLs are accepted, an invalid one fails before the scan runs, and a failed post never changes the exit code. The message uses a red/green status bar, unit-labelled per-file values (`complexity 24`, `332 lines`, `86/100`), and a **View CI run** button when GitHub Actions, GitLab or Bitbucket exposes a run URL.

- **`iris slack setup | test | manifest`.** Free. `setup` walks you through it: paste the webhook, it checks the URL, sends a real test message so you see it arrive, then prints the exact secret to add for GitHub Actions, GitLab or Bitbucket. `manifest` prints a one-click link that creates a pre-configured Slack app instead of clicking through six screens. `test` re-sends using the configured webhook.

### Changed
- **Setting `IRIS_SLACK_WEBHOOK` enables Slack notifications**: an existing `iris gate` or `iris check` step posts with no pipeline edit. Delivery controls remain available through `--slack-on` and `--slack-dry-run`; the credential itself is never accepted as an argument.
- `iris gate` now emits a `cli_gate_run` analytics event, matching `iris check`. Counts only, and only when signed in with analytics enabled.

## [1.10.1] - 2026-08-05

### Fixed
- **Corrected comments in generated configs.** The commented guide written into a new `.irisconfig.json` by `iris config init` called the tool "Iris" rather than "Iris Code" in two places, and had lost the `(Pro)` marker on `trendRegressionThreshold` and `gateBaselineMode` - so it suggested two fields that a Free licence has stripped with a warning. Existing config files are untouched.

## [1.10.0] - 2026-08-05

### Added
- **Vue and Svelte single-file components are analysed.** `iris check`, `secrets`, `security`, `todos`, `gate`, and `report` all read `.vue` and `.svelte` files, analysing their `<script>` blocks as JavaScript or TypeScript from the `lang` attribute. Findings keep the component's own line numbers; template and style markup is excluded from analysis and from duplicate matching.
- **Role-based file naming.** `fileNaming.overrides` in `.irisconfig.json` takes an ordered list of workspace-relative globs, so components can require `PascalCase` while hooks or composables require `camelCase`. The last matching rule wins and workspace `ignore` globs stay absolute.

### Fixed
- **Large files analyse dramatically faster.** Analysis time grew with the square of a file's line count, so a very long file - especially one with many blank lines, and worse with Windows line endings - could stall a CI run for minutes. A 200,000-line file went from over two minutes to a fraction of a second. Findings are unchanged.
- **An unusable file-naming glob no longer stops the scan.** A pattern too long to compile into a matcher aborted the run while `iris config validate` still reported the config as fine. The offending pattern is now named with its exact location, the rest of the rule keeps working, and `iris check` exits 2 as a config error.
- `no-exports` no longer fires on Vue and Svelte components, and SvelteKit route files (`+page.svelte` and siblings) are no longer reported as naming violations.

### Changed
- **`iris report` and `iris sbom` now require `--output` to stay inside the current directory,** matching `iris check`, `iris deps`, and `iris cve`.

## [1.9.0] - 2026-07-26

### Added
- **Quality Signals** in JSON reports: deterministic recurring-risk signals with raw counts, density-based attention levels, and no competing headline score.
- **Workspace-wide and per-language file naming conventions** with `snake_case`, `kebab-case`, `camelCase`, and `PascalCase`, framework-aware exemptions, ignore globs, baseline-aware new-file rollout, and the `gateMaxNamingViolations` gate.
- **AI-assisted preset** for teams reviewing AI-assisted code at volume.

### Fixed
- `enableSecuritySmells` is now applied from `.irisconfig.json` instead of being silently ignored.
- Config validation now has regression coverage for every supported top-level property family.
- `iris secrets --format json` and `iris deps --format json` now exit 1 when their JSON reports contain findings, instead of silently returning success to CI.

## [1.8.1] - 2026-07-22

### Fixed
- Suppression directives now work in files that use Windows-style CRLF line endings. Previously, `iris-ignore` and `iris-ignore-file` comments in those files could be missed, leaving findings active and preventing invalid directives from being reported as bare suppressions.

## [1.8.0] - 2026-07-20

### Fixed
- **Fewer false positives** from the shared analysis engine, so `iris check`, `iris gate`, and `iris secrets` report more accurately: `any` inside a comment or string, `print(...)` inside a Python docstring, CommonJS `module.exports` files being called export-less, arithmetic like `(a + b) * c` mistaken for a risky regular expression, and the `eval(` token inside a string literal are no longer flagged. Each fix is guarded by a labelled accuracy-benchmark fixture in `@iris/core`, and the benchmark now blocks any release that regresses a rule's precision or recall.

## [1.7.0] - 2026-07-19

### Added
- **Per-language config overrides** (Pro). `iris check` and `iris gate` respect a `languages` block in `.irisconfig.json`: per-file thresholds, `healthScoreWeights`, `severityOverrides`, and the per-file gate caps `gateMaxSmellsPerFile` / `gateMaxSuppressions` can differ for `typescript`, `javascript`, `go`, and `python` files. `iris gate` prints the limit that actually governed each failing file when it differs from the table threshold, plus a "Per-language overrides active" note; `iris config validate` reports per-language problems at their exact key path, warns on unknown language keys instead of failing (forward compatibility), and generated configs from `iris config init` include a commented-out example block.
- **Bounded pre-push hook timeout.** Hooks installed by `iris hook git install` stop a hung analysis after 120 seconds (`IRIS_HOOK_TIMEOUT` env var to override) instead of blocking `git push` indefinitely; a timeout blocks the push with a `--no-verify` bypass hint rather than silently skipping enforcement. Reinstall the hook to pick this up.

### Added (security)
- `iris secrets` (and every surface using the shared engine, including `--history`) now detects six more formats: Stripe live/restricted keys (`sk_live_`/`rk_live_`), GitHub fine-grained PATs (`github_pat_`), GitLab PATs (`glpat-`), npm access tokens (`npm_`), SendGrid API keys (`SG.`), and PEM private-key headers. Masked output as always.

### Fixed
- **Functions with destructured parameters are now measured for `function-too-long`** - previously the analyser ended them on their own first line, so React-style components were never checked against `functionLengthThreshold`. File scores can drop where long components were previously invisible to the rule.
- `iris report`, `iris sbom`, `iris config`, `iris auth`, `iris hook`, and `iris todos` no longer risk the Windows exit crash (libuv assertion) on argument errors and early exits. `iris config validate` was crashing this way on every run with a licence token present.
- CLI- and extension-installed pre-push hooks now generate identical scripts from one shared builder.
- `iris cve --allow-network` / `iris deps --allow-network` now reliably re-enable network lookups after `--revoke-network`. Previously, if a fresh scan cache existed the command answered from the cache without ever saving the re-grant, so the next run that actually needed the network still refused.

## [1.6.0] - 2026-07-15

### Added
- **`iris secrets --history [--depth N]`** (Pro). Walks the git commit history (default 50 commits, `--depth 0` for full history) and runs every file version introduced by each commit - committed `.env` files included - through the existing two-layer secrets detection. Findings are deduplicated per distinct secret, values are always masked, and each shows its first/last sighting, commit and file counts, and whether it is still in HEAD or was removed since. Content introduced by merge commits is included. Coverage limits are always disclosed - depth cap, size cap ("depth reduced"), and shallow clones each produce an explicit note (and a `shallow` field in JSON output) instead of implying full-history coverage. Exits 1 if any secret is found in the scanned range, with remediation guidance (rotate first; history removal needs `git filter-repo`). The working-tree `iris secrets` scan stays Free.

### Changed
- `iris check --staged` / `--changed` scope their git file discovery to the target path, so running against a subdirectory of a larger repository no longer picks up sibling changes; `--changed` also includes untracked files that are not excluded by `.gitignore`.

### Fixed
- Fixed a Windows crash on exit (libuv assertion, observable as exit code 127) that could follow argument errors in `iris check`, a missing path, or a Pro command run without a licence. The "requires Pro - skipped" path now reliably exits 0 again, so free users and unlicensed CI runs stay green as documented; clean `iris secrets` / `iris security` scans reliably exit 0.
- Git file discovery no longer fails in repositories with very large untracked sets (for example a fresh clone with no `.gitignore` and an unignored `node_modules`).
- A missing Git installation is now reported as "git is not installed or not on PATH" by `iris check --staged/--changed`, `iris secrets --history`, and `iris hook git`, instead of the misleading "not a git repository".

## [1.5.1] - 2026-07-13

### Fixed
- `.mjs` and `.cjs` files are now included by `iris secrets`, `iris security`, and `iris todos`, completing the v1.5.0 promise that Node ESM and CommonJS extension variants are analysed wherever `.js` is supported.

## [1.5.0] - 2026-07-13

### Added
- **Duplicate code detection.** `iris check` finds copy-pasted blocks via normalised token-window matching (identifier names, literals, whitespace, and comments ignored): within-file blocks in any check, cross-file blocks in directory scans. JSON file entries gain an additive `duplicateBlocks` array (`schemaVersion` unchanged). Suppressible with `// iris-ignore: duplicate-code -- <reason>`. Noise guards: import/require/re-export headers and uniform data literals (country lists, enum tables) never match, repeated blocks report as a minimal covering pair set (no pairwise explosion, contained sub-matches dropped), and markup-to-markup blocks (`.jsx`/`.tsx`, or `.js` carrying JSX) need 2× the token threshold.
- **`gateMaxDuplicateBlocks` gate rule** (Pro). `iris gate` shows a "Max duplicate blocks" row and fails when the scan-wide duplicate block total exceeds the cap (a block spanning two files counts once). Config knobs: `duplicateBlockMinTokens` (default 40), `enableDuplicateDetection` (default true), `healthScoreWeights.duplicateCode` (default 3).
- Files excluded from duplicate comparison (too large or minified-looking) are disclosed in `iris gate` output, never silently dropped.
- `.mjs` and `.cjs` files are now analysed everywhere `.js` is - directory checks previously skipped Node ESM/CommonJS extension variants.

## [1.4.0] - 2026-07-09

### Added
- **Inline suppressions.** `// iris-ignore: <ruleId> -- <reason>` (or `# ...` in Python) suppresses matching findings on the next line; `iris-ignore-file` covers the whole file. The reason is required - bare directives stay inert and are reported as findings. Suppressed counts always appear in `iris check` and `iris gate` output; the new `--show-suppressed` flag lists each suppressed finding with its reason, and JSON reports carry a per-file `suppressed` array.
- **`gateMaxSuppressions` gate rule** (Pro) - per-file cap on suppressions - and the `ignoreSuppressions: true` config override that treats all iris-ignore comments as inert. The `strict` and `security` presets ship with default caps (5 and 3 per file) and a raised bare-ignore penalty.
- **Confidence labels on advisories.** `iris deps` and `iris cve` rows now show a confidence label (`fix-available`, `direct`, `transitive`, `manual-review`) and the fixed-in version where one exists.

### Changed
- **Vulnerability lookups now use the OSV.dev database** instead of the GitHub Advisory API. No token or rate-limit setup is needed; the `GITHUB_TOKEN` env var and the GitHub PAT prompt in `iris auth login` are gone. Advisories without a verifiable id or severity are skipped and the skip count disclosed; dependencies whose lookup failed are marked "not checked" rather than assumed clean. Existing advisory caches are invalidated automatically.

### Fixed
- Debug prints quoted inside comments are no longer flagged as live debug prints in TS/JS, Go, and Python.
- `iris deps` and `iris cve` argument errors no longer risk a Windows crash on exit (same libuv fix class as 1.3.2).

## [1.3.2] - 2026-07-05

### Fixed
- Fixed a Windows crash on exit (libuv assertion) that could follow an unknown command, an invalid `config`/`auth` subcommand, or `--help <command>` when run with a licence token present. Exit codes are unaffected.
- The unknown-command error now suggests checking `iris --version` and updating, since a stale global install is the most common cause.

## [1.3.1] - 2026-07-05

### Changed
- Brand consistency: CLI help text and printed output now say "Iris Code" instead of bare "Iris" throughout. No behaviour change.

## [1.3.0] - 2026-07-05

### Added
- `iris sbom [path]` (Pro) - exports a CycloneDX 1.5 SBOM of every dependency across npm, Go, Python, RubyGems, and NuGet manifests. Fully offline; components carry name, version, purl, and direct/dev scope. If a cached `iris deps` scan exists, known vulnerabilities are folded in and cross-referenced by purl. `--output` and `--stdout` supported.
- `iris cve [path]` (Pro) - same scan and cache as `iris deps`, but exits non-zero only when a CVE at or above `--severity` (`low`/`medium`/`high`/`critical`, default `high`) is found. Built for CI: gate on serious findings without going red over low-severity noise. `--format pretty|json` and `--output` supported.
- Lockfile-aware versions - dependency scans resolve the exact installed version from `package-lock.json` (v1-v3), `pnpm-lock.yaml`, `yarn.lock` (classic and Berry), `poetry.lock`, `Pipfile.lock`, `Gemfile.lock`, or `packages.lock.json` instead of the manifest's version range.
- Monorepo support - npm/yarn `workspaces` and `pnpm-workspace.yaml` members are discovered and their dependencies merged into the scan, deduplicated. Internal `workspace:`/`file:`/`link:` deps are excluded from lookups.
- Network consent - the first dependency/advisory lookup asks permission, explains what is sent (package names and versions only), and persists the answer in `~/.iris/preferences.json`. `--revoke-network` disables lookups; `--allow-network` re-enables. Cached scan results now print their age.

### Fixed
- Advisory results are filtered to the installed version of each package - packages on patched versions are no longer flagged for advisories that only affect other versions. Stale cached advisory entries are invalidated automatically.
- `iris deps`, `iris gate`, and `iris cve` no longer risk a libuv assertion crash at process exit on Windows, which could garble the exit code CI depends on.

## [1.2.0] - 2026-07-02

### Added
- `iris security [path]` (Free) - scans JS/TS/Go/Python for 9 security smell categories (eval/exec usage, SQL string concatenation, insecure RNG, ReDoS-prone regex, hardcoded localhost URLs, disabled TLS verification, debug flags, weak hashing, open redirects). Supports `--format pretty|json` and `--output`; exits 1 if any smell is found.
- `gateMaxSecuritySmells` gate rule - `iris gate` now fails when the total number of security smells across the workspace exceeds this `.irisconfig.json` value.

### Fixed
- `healthScoreWeights`, `inlineDiagnostics.securitySmells`, and `severityOverrides["security-smell"]` are now accepted in `.irisconfig.json` - all three were silently dropped with an "unknown key" warning since security smells shipped in 1.1.x.

## [1.1.1] - 2026-06-17

### Fixed
- Multi-line Python block headers (wrapped `def`/`class`/`if`/`with` signatures, e.g.
  Black-formatted) are no longer misreported as a syntax error. Affected files were
  scoring 0 and skipping analysis; they now analyse normally.
- The suspicious-name secret heuristic is skipped in test files (`test_*.py`,
  `*_test.go`, `*.test.ts`, `tests/`, `__tests__/`, `conftest.py`, …) so fixture
  credentials are not flagged. Real provider token formats are still detected everywhere.

## [1.1.0] - 2026-06-16

### Added
- `--format github` on `iris check` and `iris gate` - emits GitHub Actions annotation
  commands so failing rules/findings render as inline annotations on the pull request
  diff, and writes a pass/fail summary table to `$GITHUB_STEP_SUMMARY`.
- `schemaVersion` field (currently `1`) on all `--format json` output so consumers can
  depend on the payload shape.
- `iris-alpine-amd64` musl binary published alongside the Linux and Windows binaries
  (the `npm install -g @iris-code/cli` route was already musl-safe).

### Changed
- Pro-gated commands (`gate`, directory `check`, `deps`, `todos`, hook install) now
  **exit 0 instead of 1** when no valid licence is present, so CI pipelines stay green;
  inside a GitHub runner they also emit a non-blocking warning annotation.

## [1.0.4] - 2026-06-04

### Added
- SECURITY.md documenting all network endpoints, files written, and environment variables read
- CHANGELOG.md

### Fixed
- Added `bugs` field to package.json pointing to GitHub Issues

## [1.0.3] - 2026-06-03

### Fixed
- README links updated to point to iriscode.co docs and download page
- Dropped macOS self-contained binary target (codesigning requires a macOS runner)

## [1.0.2] - 2026-06-02

### Fixed
- npm publish pipeline stabilised (registry URL, auth token, provenance flags)

## [1.0.1] - 2026-06-02

### Added
- Initial public release
- `iris check` - health score for a single file (Free) or full directory (Pro)
- `iris check --staged` / `--changed` - check only git-staged or changed files (Pro)
- `iris secrets` - scan for hardcoded API keys, tokens, and passwords (Free)
- `iris deps` - dependency versions and CVE lookups via GitHub Advisory API (Pro)
- `iris todos` - list all TODO / FIXME / HACK comments (Pro)
- `iris gate` - run enforcement gate with per-rule pass/fail output (Pro)
- `iris report` - generate a standalone dark-themed HTML report (Free)
- `iris hook git install|uninstall|status` - manage pre-push git hook
- `iris hook build install|uninstall|status` - manage pre-build hook (Node, Go, Python)
- `iris config init` - interactive preset selection, writes `.irisconfig.json`
- `iris config validate` - validate an existing `.irisconfig.json`
- `iris auth login|logout|status` - browser and token-based authentication
- Credentials stored in `~/.iris/credentials` (mode 0600)
- `IRIS_LICENCE_TOKEN` and `GITHUB_TOKEN` env var support
- `--format json` and `--output` flags across check, secrets, gate, and report
- Exit codes: 0 pass, 1 findings/fail, 2 invalid args
