# Changelog
All notable changes to cora-code are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.18.0] - 2026-10-09
### Security
- **Release checksums are signed with cosign keyless and verified by `install.sh` and `cora upgrade` (#591).** `release.yml` now signs `checksums-sha256.txt` (which covers every archive) via Sigstore/GitHub OIDC before creating the release and uploads `checksums-sha256.txt.sigstore.json`; a signing failure aborts the job so no unsigned release is published. `install.sh` and `cora upgrade` verify the bundle with `cosign verify-blob` (identity: this repo's `release.yml` on a `v*` tag, issuer `token.actions.githubusercontent.com`) when `cosign` is on `PATH`; an invalid signature aborts. Without `cosign`, or for releases without a bundle, they print a notice and fall back to checksum-only. `CORA_REQUIRE_SIGNATURE=1` makes a missing `cosign` or bundle fatal. See `docs/installation.md`.
### Changed
- **Documented what the hardcoded-secret check flags and its one known limit** (all-letter unquoted YAML values), and closed that item as not planned (#625, #635).
- **Scanners take file lines instead of diffs (#610).** New `engine::scan_input::ScanFile` (`path`, `language`, `(line number, text)` pairs) is the one input shape for `secrets_scanner::scan_secrets_in`, `security_scanner::scan_security_in`, `rules::run_rules_in` and `inline_suppress::apply_lines`. Diff chunks and whole files are thin adapters (`ScanFile::added_lines`, `ScanFile::post_image`, `ScanFile::from_entry`); `scan_secrets`/`scan_security`/`run_rules`/`inline_suppress::apply` keep their signatures. The `files_as_chunks` workaround and the MCP fake diff are gone. Findings are byte-identical for diffs and scans. Unit tests that open an index session now use a per-test temp database instead of the shared global one.
### Fixed
- **A `rules_engine:` section without `enabled: true` no longer disables the built-in rules.** `enabled` now defaults to `true` when the section is present, so `rules_engine: { max_findings: 0 }` only changes the cap; set `enabled: false` to turn the engine off (#638).
- **`cora.check_snippet` (MCP) now honors `ignore.rules`, inline `cora-ignore:` markers and the false-positive filters, and `rules_engine.max_findings` (#610).** The tool used to run the secrets and security scanners on a fake diff with a fixed cap of 10 per scanner and no filtering, so a snippet could report findings that `cora scan` suppresses. It now goes through the shared post-processing pipeline as a synthetic `snippet.<language>` file (so path-aware classification applies), loads the project config like the other MCP tools (and fails with the same "Failed to load config" error), and mentions how many findings the cap dropped. Output format is unchanged.
- **Secret-detection edge cases (#625, #628, #629, #630).** A string literal inside a constructor wrapper is now a literal: Java `new String("..")`, `Cow::Owned`, `Box::from`, `Secret::new`, Python `str("..")` (plain calls such as `get_password()` stay quiet) (#630). A literal default in the default position of an env read is flagged in code files: `os.environ.get("X", "lit")`, `os.getenv`, `process.env.X || "lit"` / `?? "lit"`, Rust `env::var("X").unwrap_or("lit".into())` / `unwrap_or_else(|_| "lit".to_string())`, Ruby `ENV.fetch`, PHP `getenv() ?:`; the env var name and name-only reads stay quiet (#628). Shell `${VAR:-literal}` / `${VAR:=literal}` is flagged, while `${VAR}`, `${VAR:-}` and `${VAR:?msg}` stay quiet (#629). The unquoted-YAML rule no longer applies to source code (`secret: Secret1Type` in `.ts`/`.py`), YAML flow-style `{ password: hunter2hunter2 }` is flagged, unspaced `password:=os.Getenv("X")` is no longer matched, and SQL `PASSWORD "..."` with double quotes is detected (#625). All-alphabetic unquoted YAML values (`password: correcthorsebattery`) remain undetected, tracked in #635. The LLM secret cross-check uses the same classification.
- **`rules_engine.max_findings` is applied after suppression and truncation is reported (#624).** The cap (default 5) used to be applied inside each scanner, before `ignore.rules` / `cora-ignore`, so suppressed findings consumed slots and pushed real findings out, and the rest were dropped silently. Scanners now run uncapped and the cap is enforced once in post-processing on deterministic findings only (LLM issues are never capped), highest severity first. `cora scan` and `cora review` print `⚠ N more deterministic findings not shown (...)` on stderr when findings are cut, and the `cora scan` summary mentions it; stdout formats are unchanged. `max_findings: 0` or `null` now means unlimited (previously `0` hid every finding).
## [0.17.2] - 2026-10-09
### Fixed
- **Env reads, call expressions and shell interpolation are no longer reported as hardcoded secrets.** `process.env.X`, `os.environ["X"]`, `os.getenv("X")`, `std::env::var("X")?`, `System.getenv("X")`, `get_password_from_vault()`, `String::new()` and bare identifiers/member accesses (`const password = userInput;`) are expressions, not literals, in source files (#616). `DB_PASSWORD=${VAR}`, `db.password=${DB_PASSWORD}`, `export X="$VAULT_X"` and `$(cmd)` are references (#617). Real values stay flagged: quoted or concatenated literals in code, and bare tokens in `.env`/`.properties`/`.ini`/`.toml`/shell/YAML files (`DB_PASSWORD=hunter2hunter2xx`). The LLM secret cross-check uses the same classification.
## [0.17.1] - 2026-10-09
### Fixed
- **Hardcoded secrets in Go short declarations, YAML/JSON keys and SQL `PASSWORD` literals are now detected.** Go `password := "..."` (with or without a trailing comment) and `var apiKey string = "..."` (#618); unquoted YAML `password: hunter2hunter2` and JSON quoted keys `"password": "..."` / `"apiKey": "..."` (#619); SQL `CREATE USER app WITH PASSWORD '<literal>'` and `IDENTIFIED BY '<literal>'` (#620). References stay quiet: `${VAR}`/`$VAR`, `!tag`, `*alias`, `&anchor`, paths, `null`/`required`-style bare words, and SQL placeholders (`PASSWORD '%s'`). The LLM secret cross-check uses the same pattern, so LLM findings on these shapes are no longer dropped.
### Changed
- **One shared post-processing module for `cora review` and `cora scan`.** The finding filter pipeline (LLM secret false-positive cross-check, Markdown code blocks, `ignore.rules`, inline `cora-ignore:`, context-line filter, scanner merge for scans) now lives in `engine::postprocess`, so a fix lands in one place. Internal refactor with no CLI or output change (#610).
- **`ReviewIssue::new` constructor and table-driven hardcoded-secret tests.** `ReviewIssue` is now built through `ReviewIssue::new(..)` plus `with_*` setters, so adding a field touches one place instead of every construction site. One end-to-end table covers the hardcoded-secret heuristics across languages; known false positives/negatives are pinned in a separate table. Internal change with no CLI or output change (#610).
## [0.17.0] - 2026-10-09
### Fixed
- **A `cora-ignore:` naming a scanner rule could hide an unrelated LLM finding on the same line.** A scanner finding is now merged into an LLM issue on the same line only when they share a topic word (the secret family — password, key, token, credential — counts as one topic); otherwise both are reported and the marker suppresses only the finding it names (#609).
### Fixed
- **Real secrets in typed declarations were missed.** `const password: string = "..."` (TypeScript), `password: str = "..."` (Python) and `let password: &str = "..."` (Rust) matched none of the secret patterns (they required the value right after `password =`/`password:`), and `cora review` also dropped the LLM's finding on them (and on single-quoted literals) as a false positive. The scanner, the built-in rule and the LLM cross-check now allow an optional type annotation, and the cross-check accepts single-quoted literals. The `bare identifier` check in the object-shorthand filter, which was always true, now rejects values containing a string literal (#607).
### Fixed
- **Real secrets were missed when the line had a trailing comment containing `:`.** The `hardcoded-secret` false-positive filter treated the first `:` anywhere (even inside a comment or string) as object shorthand, so `password = "..."; // note: fix later` was not flagged, and no scanner finding existed on lines carrying a `cora-ignore: <rule>` marker. The filter now judges only the code, ignoring trailing comments and quoted colons (#603).
- **Concurrent index opens could fail with `UNIQUE constraint failed: schema_version.version`.** Schema migrations are now serialised per process, which also removes a flaky test (#604).
### Added
- **Findings now carry a `rule_id`.** Deterministic scanner findings (rules, secrets, security, index) keep their rule id through the merge; it appears in pretty/compact output, as `rule_id` in JSON (omitted when absent, so older JSON still deserializes), and as `properties.coraRuleId` in SARIF (#597).
### Changed
- **`cora-ignore:` and `ignore.rules` match rule ids.** `cora-ignore: sec-hardcoded-secret` suppresses by id (exact, case-insensitive) as well as title; `ignore.rules` additionally matches ids exactly. A marker naming a scanner rule now also suppresses the LLM finding that displaced that scanner finding on the same line (#597).
## [0.16.1] - 2026-10-08
### Fixed
- **`cora scan` no longer reports "No issues found" when the LLM fails.** It now runs the deterministic secrets and security scanners on every scanned file, merges them with LLM findings, and applies `ignore.rules` and inline `cora-ignore:` markers (read from file contents), so a hardcoded secret is reported even on an LLM 502 (#595).
- **The LLM no longer flags `cora-ignore:` markers themselves** (e.g. "Security scanner findings suppressed instead of remediated"). The hardened system prompt now explains the marker is Cora's own suppression syntax (#596).
## [0.16.0] - 2026-10-08
### Added
- **Rule-scoped inline suppression with `cora-ignore:`.** A source comment such as `// cora-ignore: Hardcoded password or secret in variable` (also `#`, `--`, `/* */`, `<!-- -->`; comma-separated rules) suppresses findings with that exact title (case-insensitive) on the same line, or on the next line when the marker line holds only a comment. Applies to both static-scanner and LLM findings; other rules and other lines stay visible, and a bare `cora-ignore` without rules suppresses nothing. Coexists with `ignore.rules` / `ignore.files` (#554).
### Security
- **Project `.cora.yaml` can no longer redirect your API key.** `provider.base_url` from a discovered project config is ignored unless `CORA_TRUST_PROJECT_CONFIG=1`; `base_url` must be `https` (plain `http` only for loopback); LLM error bodies echoed to the terminal are length-capped (#563).
- **Hardened `cora upgrade` and `install.sh`.** Checksums are matched by exact filename; downloads go to a random 0700 temp dir; only the single `cora` binary entry is extracted (symlink/hardlink entries are rejected); every request has a timeout and a size cap; the version probe does not follow redirects and the tag is validated. `CORA_UPGRADE_SKIP_CHECKSUM` now also requires `CORA_UPGRADE_I_UNDERSTAND=1`. `install.sh` fails closed when the checksums file or entry is missing (opt out with `CORA_SKIP_CHECKSUM=1`) and falls back to `shasum -a 256` (#572).
- **`install.sh` requests now have timeouts and size caps.** Every `curl` call sets `--connect-timeout`, `--max-time` and `--max-filesize` (256 MiB archive, 1 MiB checksums/API JSON), matching `cora upgrade`, so a stalled or hostile server cannot hang or flood the installer (#580).
- **Hardened LLM response handling.** Review and scan system prompts tell the model to treat diff content as untrusted data; the diff fence is longer than any backtick run in the diff; the SSE stream errors on a line over 1 MiB or more than 16 MiB of content (#573).
- **Secrets in test and doc files are no longer invisible to the static security scanner.** Test and doc paths still skip the noisy general rules but now run high-confidence checks (AWS keys, private-key headers, GitHub/Slack/Stripe live tokens; values containing `EXAMPLE` are ignored) (#573).
### Fixed
- **`cora watch` reindexed every source file on each cycle.** Change detection now reindexes only files that changed, and `--filter` restricts what is indexed instead of only gating the trigger (#578, #584).
- **Secrets in test, fixture and example files were skipped entirely by the secrets scanner.** High-confidence secrets are now reported in those paths too (#579, #583).
- **`cora findings list --severity` never matched, and severity colouring never applied.** Both are now case-insensitive (#586, #588).
- **Unit tests wrote to the real data directory and could block on the global vector index lock.** Tests are isolated from the real data dir, and the lock wait is bounded (#587, #589).
- **`cora affected` never matched naming-convention tests.** The CLI took the file extension (`rs`) as the file stem. CLI and MCP now share one query, with escaped `LIKE` wildcards, batched queries and one deduplicated pattern list (`{stem}_test`, `test_{stem}`, `{stem}.test`, `{stem}.spec`, `tests/{stem}`, `__tests__/{stem}`). MCP dead-code now honors `analysis.entry_point_patterns` like the CLI (#575).
- **`cora --config <file> serve` ignored `--config`.** The global option is now passed through (#576).
- **MCP `brain_search` ignored the configured embedding backend.** It now resolves the backend from the project config (#576).
- **Review and indexing disagreed about which files to skip.** Review-time scanners now use the same patterns as the indexer (`ignore.files` + `index.skip_files`) (#576).
- **Review scanners could resolve a different project than indexing** when run from a subdirectory or workspace member. Every entry point now resolves the project root the same way, and review no longer creates an empty `cora.db` when no index exists (#574).
- **Files from different projects overwrote each other's fingerprints** (perpetual reindex). Files are keyed by `(project_id, path)` (schema v8). `callers`/`callees`/`trace` match names exactly instead of by substring (`run` no longer matches `rerun`), and index runs prune stale files and their edges (#565).
- **MCP server robustness.** Stdin is framed as bytes and decoded as UTF-8; garbage input yields a `-32700` parse error; notifications get no response and `notifications/cancelled` no longer stops the server; `tools/call` without a name returns `-32602`; `limit`/`depth`/`min_lines` are clamped; `cora.install` requires `confirm: true` (#564).
- **Chunked review printed "No issues found" while reporting issues** when chunks returned empty summaries (#562).
- **CI:** the CLA check works for fork PRs (#553); `rustls` pinned to 0.23.45 for RUSTSEC-2026-0285 and a deprecated `f32` import removed so clippy passes on rustc 1.99 (#561).
### Changed
- **Ignore patterns now use a single matcher** for the index walk, review scanners, `cora scan --include/--exclude` and `watch --filter`. Behavior changes you may notice in `ignore.files` and related options:
- `**/*.test.ts` no longer matches `footest.ts`, and `vite.config.*` no longer matches `vite.configx`.
- Patterns with a wildcard in the middle (`src/*.rs`, `*.gen.*`) now match; they were silently ignored before, so files you thought were excluded may now actually be excluded.
- Patterns without a `/` also match by basename, in `cora scan --include/--exclude` and `watch --filter` too.
- An invalid glob matches literally instead of being dropped (#577).
- **`cora index --watch` now runs the same watcher as `cora watch`.** It checks every 500 ms (was 2 s), skips hidden directories, and prints `Reindexed: ...` (#576).
- **Internal refactors with no CLI change:** one seam for opening the index (`IndexBridge`, #574), one index-session module (#576), and review split into a deterministic stage testable without an LLM (#577), LLM parse/repair/retry policy unified behind one `Transport` seam (#582), and review-history SQL owned by a single `review_store` module (#585).
## [0.15.0] - 2026-08-31
### Added
- **Opt-in `vecq` vector store for Brain Mode.** Set `brain.vector_store: vecq` in `.cora.yaml` to replace the usearch HNSW index with a vecq quantized scan (pure Rust, deterministic, ~5x smaller). Keyed persistence included: symbol ids survive reload, so a fresh process serves the index as-is and `cora index` no longer re-embeds unchanged projects (#542, #547).
- **`brain.vector_bits` quantization-width knob.** `residual` (default) | `4` | `5` | `6` — 4-bit base codes with second-pass residual rescoring, or plain Lloyd-Max widths. The default is residual: best recall@10 at 4-bit scan speed in a recall study on cora's own embeddings, ahead of plain 5-bit at 1k/5k/13k-symbol scales. Changing the width rebuilds the index once on the next `cora index` instead of silently serving the old width; unknown values fall back to `residual`.
### Fixed
- **Vector signal never fired in a fresh process.** `cora brain` and MCP `brain_search` only saw the vector index if the same process had run the embed — otherwise results silently degraded to FTS-only. The search path now lazy-loads the on-disk index once per process, with a dimension guard against backend switches (#545).
- **Stale embed fingerprints after a global vector-index rebuild.** The vector index is a single file shared by all projects; rebuilding it (width/dims change, legacy file, corruption) wiped every project's vectors while their fingerprints still said "embedded" — the incremental path would skip those symbols forever. A rebuild now clears fingerprints for all projects, and the usearch dims-mismatch path (which deleted the index without clearing) joins the same heal.
### Changed
- **vecq-core dependency 0.2.0 → 0.3.0.** Picks up the 4-bit+residual mode, plain 5/6-bit widths, runtime-detected AVX2 scoring, and file formats v1.3–v1.5 with the keyed-slot table. Pre-0.3.0 `.vecq` files carry no key table and rebuild once with a warning, then upgrade to the keyed format.
## [0.14.0] - 2026-08-28
### Fixed
- **Empty LLM responses from reasoning models.** Models like GLM can spend the entire `max_tokens` budget on chain-of-thought and return `content: ""` with `finish_reason: "length"`, which previously surfaced as a misleading `EOF while parsing` error. Cora now reads `finish_reason`/`reasoning_content`, automatically retries with a doubled budget (up to 32768), salvages JSON from reasoning text as a last resort, and reports an explicit "EMPTY response" error when nothing is recoverable (#536).
- **Dead-code false positives from cross-crate method calls.** Method calls inside Rust `impl` blocks were never walked for call edges, and call targets stored raw AST text (`self.export_full`) that could not join against symbol names — 557 false positives on a 5-crate workspace (#519).
- **Index root mismatch between CLI and MCP.** Running `cora index` inside a workspace member crate created a separate project row from the one MCP resolved, so `index_status` reported 0 symbols despite a populated DB. Root resolution now prefers a `[workspace]` Cargo.toml and never climbs past a `.git` boundary (#522).
- **`ignore.files` was not honored by the index.** Skip patterns only invalidated fingerprints; matched files were still indexed and surfaced in dead-code/review findings. They are now excluded from indexing entirely (#521).
### Changed
- **Default `max_tokens` raised from 4096 to 8192** to give reasoning models headroom above their chain-of-thought (#536).
### Changed
- **Default `max_tokens` raised from 4096 to 8192** to give reasoning models headroom above their chain-of-thought (#536).
- **Dead-code now skips public API surface by default** (`pub`/`export` items) — new `--include-pub` flag and MCP `include_pub_api` parameter opt back in (#520).
- **Review prompts include enclosing control-flow scope.** Hunks touching branching constructs get the enclosing function from the post-image (120-line cap), plus an always-on guardrail against unverified reachability claims (#523).
- **Incremental re-index reports honestly.** No-op runs print "Index up to date" with stored totals instead of "Indexed 0 symbols"; MCP `index_status` carries a root-mismatch hint (#522).
- **Relicensed from MIT to Apache-2.0.** All 18 CodeCoraDev repositories now
standardize on Apache-2.0 for patent grant protection and open-core model
compatibility. Added CLA (Individual + Corporate) for contributor copyright
and patent grants.
- **Added Contributor License Agreement (CLA).** Individual and Corporate CLA
documents added (`CLA_INDIVIDUAL.md`, `CLA_CORPORATE.md`). CLA includes
SIAC arbitration, patent retaliation, moral rights acknowledgment, and
no-compensation clause.
- **Updated CONTRIBUTING.md.** Added Contribution Terms section with
no-compensation notice, CLA requirement, and Apache-2.0 license reference.
- **Updated README license badge** from MIT to Apache-2.0.
## [0.13.0]
### Added
- **Runtime embedding backend selection.** Brain Mode now reads `brain.embedding` from `.cora.yaml` to select the embedding backend at runtime instead of compile time. Supported values: `auto` (best available — default), `hashing` (force 256d zero-dependency), `pretrained` (force 768d nomic). No recompilation needed to switch.
- **Incremental per-symbol embedding.** `embed_project()` now tracks an `embed_fingerprint` (hash of symbol name + signature) and skips re-embedding symbols that have not changed since the last index. On large projects, re-indexing after touching one file embeds only the changed symbols instead of all.
- **Schema migration v7.** Adds `embed_fingerprint TEXT` column to the `symbols` table for incremental embedding tracking. Auto-migrates on first run; existing indexes are upgraded transparently.
- **`Backend` enum + `resolve_backend()` in `embed` module.** Clean runtime dispatch with `OnceLock` caching, graceful fallback when a requested backend is not compiled, and `active_dims()` / `active_provider_name()` helpers.
- **`BrainConfig` + `BrainEmbeddingMode` in config schema.** New `brain` section in `.cora.yaml` with `embedding` field. Includes `Display`, `FromStr`, and `serde` impls for CLI and YAML ergonomics.
### Changed
- **`embed_code_dispatch()` now checks `ACTIVE_BACKEND` at runtime.** Previously selected via `#[cfg]` at compile time only. Falls back to compile-time default if `resolve_backend()` was never called (lazy resolution).
- **`cora index`, `cora brain`, `cora watch` all resolve embedding backend on startup.** Each command loads `.cora.yaml`, reads `brain.embedding`, and calls `resolve_backend()` before touching the vector index.
- **Embedding doc comments updated.** Module-level docs now describe runtime selection and the three-tier architecture (hashing → pretrained → ONNX future).
## [0.12.0]
### Fixed
- **FTS5 returning 0 results for camelCase queries (#451).** Added `file` column to FTS5 virtual table (schema v6), `split_camel_case()` identifier decomposition, and OR query expansion. Searches like `findUser` now correctly match via `find OR user`.
- **Dead-code false positives on framework entry points (#452).** Added `FRAMEWORK_ENTRY_PREFIXES` with SQL LIKE pattern matching for common framework handlers (`handle_*`, `on_*`, `route_*`, etc.) — these are no longer flagged as dead code.
- **Symbol-level suppression markers (#452).** Symbols containing `// cora: keep` in their body are excluded from dead-code detection.
- **Sticky skip files on config change (#453).** Added `index_config_hash` column to projects table — when `.cora.yaml` changes, previously skipped files are re-evaluated instead of permanently skipped.
### Added
- **`entry_point_patterns` config field** — New field in `AnalysisConfig` and `.cora.yaml` `analysis` section. Custom list of glob patterns for framework-specific entry points beyond built-in defaults.
- **Schema migration v6** — Auto-migration: adds `index_config_hash` to projects, drops and recreates FTS5 with `file` column, rebuilds search index.
- **`index_project_with_skip()`** — Indexing now respects `index_skip_files` from config, skipping non-code files during symbol extraction.
- **`split_camel_case()`** — Decomposes `camelCaseIdentifiers` into individual tokens for FTS5 search (`camelCase` → `camel OR case`).
- **Enhanced `sanitize_fts_query()`** — Handles quoted phrases, trims whitespace, and escapes special FTS5 characters.
- **31 new unit tests** — Tests for camelCase splitting, FTS5 query expansion, glob matching, suppression markers, framework prefix detection, schema migration v6, and config hash invalidation.
### Changed
- **`should_skip_file()` rewritten** — Simplified glob matching with early exit for non-glob patterns, explicit `**/name` branch handling.
- **Governance documentation** — Added CONTRIBUTING.md, CODE_OF_CONDUCT.md, SECURITY.md, PR template, issue templates (bug report + feature request), and PR checks workflow (branch naming, conventional commits, PR description validation).
## [0.11.1]
### Fixed
- **Index scanner false positives on entry-point files.** Added `index_skip_files` glob patterns to `RulesConfig`. Common bundler config files (`vite.config.ts`, `webpack.config.*`) and app entry points (`src/main.ts`, `src/index.tsx`) are now skipped by default — reducing noise from imports used by bundlers, not code.
- **`cora scan` now includes index findings.** Fixed bug where `cora scan` did not wire index scanners (unused imports, dead code) — the scan command now runs `scan_project_index()` to produce deterministic findings alongside LLM analysis.
- **Error fallback preserves index findings.** When LLM review fails, the fallback path now includes all index-based findings instead of silently dropping them.
### Added
- **`index_skip_files` config field** — New field in `RulesConfig` and `.cora.yaml` `rules_engine` section. Supports simple glob patterns (`*.config.ts`, `vite.config.*`, `**/main.ts`). Configurable per-project.
- **`should_skip_file()` helper** — Glob matching utility for index scanner file filtering.
- **8 new unit tests** — Tests for `should_skip_file()` covering exact match, wildcard suffix/prefix, `**/` patterns, and default skip list validation.
## [0.11.0]
### Highlights
- **Index-powered unused import detection.** `cora review` now flags unused imports using symbol graph analysis — not regex guessing. Detects imports that are never referenced in the file, across Rust, TypeScript, Go, and Python.
- **Dead code in review.** Changed files with dead functions/methods (zero callers) are now flagged automatically during review, not just via standalone `cora dead-code`.
- **Breaking change detection.** When a public symbol is removed or modified, review flags it with a list of affected callers — prevents silent breaking API changes.
- **HTTP route detection.** Route handlers (Axum, Actix, Express, Go net/http) are now tracked as first-class graph edges (`ROUTE`), enabling `cora query` to trace routes to handlers.
- **Brain enrichment (Tier 1).** Review pipeline now leverages symbol index for caller resolution, impact analysis, affected tests, and semantic search. Zero regression without index — falls back to regex-based resolution.
### Added
- **Unused import scanner** — `find_unused_imports()` in graph module, wired into review pipeline. Flags unused imports with file:line and imported symbol name.
- **Dead code scanner** — `find_dead_code_in_file()` in graph module. Detects unreachable symbols in changed files during review.
- **Breaking change scanner** — Detects removed public symbols and cross-references callers from index.
- **`EdgeKind::Route`** — New edge type for HTTP route → handler relationships.
- **Route extraction** — Axum `#[get("/path")]`, Actix `#[route("/path")]`, Express `app.get()`, Go `http.HandleFunc()`.
### Changed
- **Caller resolution** — Index-aware `resolve_callers()` uses graph query first, regex fallback only when no index.
- **Pre-commit hook** — Auto-runs `cora index --quiet` before review for persistent local index.
- **Context enrichment** — Impact analysis, affected tests, and brain search injected into LLM review prompt.
- **Ruby AST extraction** — Fixed `body_statement` wrapper bug in class/module method extraction.
### Technical
- **Resolved `test_extract_ruby`** — tree-sitter Ruby method extraction now correctly handles `body_statement` intermediate nodes.
- **CI 10/10 green** — All checks pass including format, clippy, test, build, security audit.
## [0.10.0] - 2026-07-29
### Highlights
- **Dead code detection.** `cora dead-code` finds functions/methods with no callers using call graph analysis. Available as both CLI command and MCP tool (`cora.dead_code`).
- **Graph query DSL.** `cora query "main -> *"` lets you traverse the code graph with simple patterns — no SQL needed. Available as both CLI and MCP (`cora.query`).
- **Auto-config agent installer.** `cora install` detects installed AI coding agents (Cline, Cursor, Windsurf, etc.) and configures Cora as their MCP server. One command setup.
- **Background reindex on serve.** `cora serve` now auto-reindexes the current project before starting the MCP server — always up-to-date symbols.
- **Tree-sitter is now a default feature.** The `edges` table (IMPORTS, IMPLEMENTS, INHERITS, CHILD_OF) now populates correctly in all builds, including release binaries.
### Added
- **`cora dead-code` CLI command** (#427). Detect dead functions/methods with `--include-tests`, `--min-lines`, and `--json` flags.
- **`cora.dead_code` MCP tool** (#428). Same dead code detection, accessible via Model Context Protocol.
- **`cora query` CLI command** (#435). Simple graph traversal DSL: `"symbol -> *"` (callees), `"* -> symbol"` (callers), `"SymbolName"` (symbol lookup).
- **`cora.query` MCP tool** (#435). Same query DSL via MCP.
- **`cora install` CLI command** (#431). Auto-detect 40+ AI coding agents and configure Cora MCP with one command. Supports `--list`, `--dry-run`, `--agents`, `--force`.
- **`cora.install` MCP tool** (#431). Same install detection via MCP.
- **`cora serve` with auto-reindex** (#434). `cora serve` runs incremental reindex on startup before launching MCP server.
- **Agent config module** (#432). Read/write support for JSON, JSONC, and YAML agent configuration files.
### Changed
- **Tree-sitter is now a default feature** (#429). `default = ["tree-sitter"]` in Cargo.toml. All builds (including release) now include AST-based extraction.
- **Release workflow** explicitly builds with `--features tree-sitter`.
- **CI workflow** explicitly builds/tests with `--features tree-sitter`.
- **MCP tool count** increased from 16 to 18 tools.
### Fixed
- **`edges` table was always empty** (#429). Root cause: tree-sitter feature was not enabled by default, so AST extraction (which produces IMPORTS, IMPLEMENTS, INHERITS, CHILD_OF edges) was never compiled into release binaries. Now fixed — 352+ edges populated on rebuild.
## [0.9.0] - 2026-07-28
### Highlights
- **Single source of truth.** Review findings, scan findings, and tech debt snapshots now persist to `cora.db` — one global database, no more scattered file snapshots.
- **Massive indexing speedup.** Rayon-parallel extraction + embedding, batch SQLite writes, PRAGMA tuning, and mtime:size fingerprinting deliver **52× faster incremental indexing** (414ms → 6ms) and **1.3× faster cold rebuild** (1,260ms → 936ms).
- **`cora findings` CLI.** Track, filter, dismiss, and reopen findings across all your reviews.
### Added
- **Persist review & scan findings to `cora.db`** (#397, #398). `cora review` and `cora scan` now save findings (severity, file, line, title, fingerprint) to the global database. Best-effort logging — never blocks the review pipeline on DB errors.
- **Auto-resolve stale findings** (#399). When a new review/scan completes, findings from prior reviews that no longer appear are automatically marked `resolved` with an `auto_resolved` event. Findings that reappear stay `open`.
- **`cora findings` CLI command** (#400). New subcommand with four actions:
- `cora findings list` — show open findings (use `--all`, `--severity`, `--file`, `--json` for filtering)
- `cora findings stats` — summary counts with resolution rate (`--json` supported)
- `cora findings dismiss <id>` — mark as won't-fix with optional `--reason`
- `cora findings reopen <id>` — reopen a dismissed/resolved finding
- **Migration v5 schema** (#396). New tables: `reviews`, `findings`, `finding_events`. Auto-migrates on first run.
- **`cora index --rebuild` flag.** Drop and re-index from scratch — useful for schema upgrades or corrupted indices.
- **Rayon parallel processing** (#409, #422). File extraction and embedding computation now run in parallel across CPU cores via Rayon.
- **Cache vector index in memory** (#407). `VECTOR_CACHE` (LazyLock) keeps the usearch HNSW index hot in memory — eliminates file I/O on every brain search.
- **Batch symbol lookup in RRF fusion** (#410). Brain search now batches DB lookups instead of per-result queries.
- **SQLite PRAGMA tuning** (#406). `journal_mode=WAL`, `synchronous=NORMAL`, `mmap_size=256MB`, `cache_size=-64MB` for faster writes.
- **Batch INSERT via multi-row VALUES** (#405). Symbol insertion now uses multi-row `INSERT ... VALUES (?,?,?),(?,?,?),...` instead of per-row inserts.
- **Batch transaction for `index_project`** (#404). All symbol/edge insertions wrapped in a single `BEGIN IMMEDIATE ... COMMIT`.
- **Disable FTS5 triggers during bulk indexing** (#411). Triggers re-enabled after commit — avoids redundant index updates mid-batch.
- **Mtime:size fingerprinting.** Replaces SHA256 content hashing for change detection. Trade-off: `--rebuild` available for full re-validation.
### Changed
- **`cora debt` reads from `cora.db` as primary source** (#403). File snapshots are now fallback only. DB is the single source of truth for tech debt reports.
- **`graph.db` renamed to `cora.db`** (#395). Auto-migrates existing `graph.db` on first run.
- `db_writer` module now exposes `open_db_for_read()`, `open_db_for_write()`, and `compute_fingerprint_pub()` for use by the findings CLI.
### Performance
Benchmarked on the cora-code repository (1,864 symbols, 115 Rust files, x86_64):
| Operation | Before (v0.8.3) | After (v0.9.0) | Speedup |
|-----------|-----------------|-----------------|---------|
| Cold index (full rebuild) | ~1,260ms | ~936ms | 1.3× |
| Incremental (no changes) | ~414ms | ~6ms | **52×** |
| Brain search (hybrid) | ~250ms | ~5ms | **40×** |
### Fixed
- **`cora brain` vector search filtered by project_id** (#382). Over-fetches from global usearch index, filters at DB layer — prevents cross-project noise.
- **Project root detection** (#380). Walks up from CWD to find `.cora.yaml` / `Cargo.toml` / `.git` instead of always using CWD.
## [0.8.3] - 2026-07-27
### Added
- **Svelte AST symbol indexing.** `cora index` with `--features tree-sitter` now extracts functions, arrow functions, and types from `<script>` blocks in `.svelte` files. Uses TypeScript/JavaScript grammar delegation — zero new dependencies. Supports `lang="ts"` and `lang="js"` attributes with correct line number offsets. Closes #384.
- **TypeScript arrow function extraction.** `export const handler = () => {}` and `const cb = function() {}` are now captured as symbols with call edges. Previously only ALL_CAPS constants were indexed from `lexical_declaration`/`variable_declaration` nodes.
### Fixed
- **Project root detection for scoped queries** (#380, #382). `resolve_project_root()` now walks up from CWD to find `.cora.yaml`, `Cargo.toml`, or `.git` instead of always using CWD. Fixes `cora brain` and `cora callers` returning results from wrong projects.
- **Vector search filtered by project_id** (#382). `brain_search()` now over-fetches from the global usearch index and filters by project_id at the DB layer, preventing cross-project noise in results.
- **Cross-project fallback for `cora callers`** (#381). When a symbol has no callers in the current project, falls back to searching across all projects in the global index.
- **Partial JSON recovery for scan results** (#383). `extract_partial_json_objects()` added as last-resort fallback when LLM returns truncated JSON arrays in scan output.
## [0.8.2] - 2026-07-25
### Added
- **8 additional tree-sitter languages.** Added AST extraction for C, C++, C#, Ruby, PHP, Scala, and JavaScript. Total tree-sitter supported languages: 12.
- **Dart symbol indexing.** `cora index` now extracts classes, mixins, enums, extensions, functions, getters, and typedefs from `.dart` files. Closes #373.
- **Svelte symbol indexing (regex).** Initial Svelte support via regex-based extraction — components (from filename), props, `$state`, `$derived`, functions. Closes #375. (Replaced by AST extraction in v0.8.3.)
## [0.8.1] - 2026-07-24
### Fixed
- crates.io publish (503 transient on v0.8.0)
## [0.8.0] - 2026-07-24
### Highlights
- **Brain Mode — hybrid code search.** `cora brain <query>` combines FTS5 keyword search, usearch vector similarity (HNSW), and graph BFS proximity into a single ranked result set via RRF fusion (k=60). Index-time embeddings use a zero-dependency static token method (256d) — no model download, no GPU.
- **tree-sitter AST extraction + call edges.** Schema v3 adds an `edges` table storing caller→callee relationships. When `cora index` runs with `--features tree-sitter`, it extracts function calls from AST nodes, enabling `cora trace` and `cora arch`.
- **`cora trace` and `cora arch` commands.** Trace symbol call chains (depth-limited) and display architecture overview (module breakdown, edge types, top connectors) from the indexed call graph.
- **Static token embedding engine.** Zero-dependency bag-of-tokens hashing (256d) for code symbol embeddings — suitable for near-duplicate detection and semantic search without external models.
- **Global index directory.** The symbol database migrated from `.cora/graph.db` (per-project) to `~/.codecora/cora-code/graph.db` (per-user), shared across all projects.
- **Renamed `cora-cli` → `cora-code`.** Binary is now `cora`, crate is `cora-code`.
### Added
- **Phase 3 — Brain Mode** (#362)
- `CodeVectorIndex` — persistent usearch HNSW vector index with fs2 file locking, key↔symbol mapping, and disk serialization
- `brain_search()` — hybrid search: FTS5 + usearch KNN (cosine, top-50) + graph BFS (depth-2 from FTS hits) → RRF k=60 fusion
- Schema v4: `embedding_tier`, `embedding_dims`, `embedding_model`, `last_embedded_at` columns on `projects` table
- Index-time embedding: all symbols embedded via static tokens during `cora index`
- CLI: `cora brain <query> [--json] [--limit N]`
- MCP tool: `cora.brain_search` — semantic code search for AI coding agents
- **Phase 2C — `cora trace` and `cora arch`** (#358)
- `cora trace <symbol>` — trace call chains from a symbol (depth-limited BFS on call edges)
- `cora arch` — architecture overview: module breakdown, edge types, top connectors
- **Phase 2 — tree-sitter AST extraction + Schema v3** (#356)
- tree-sitter AST node extraction for Rust, Python, JavaScript, TypeScript, Go, Java
- Schema v3: `edges` table (caller_id, callee_id, edge_type) storing call relationships
- Gated behind `--features tree-sitter` (default build does not include tree-sitter)
- **Phase 1 — Static token embedding engine** (#354)
- Bag-of-tokens hashing: 256d vectors from code text, zero external dependencies
- Pre-trained nomic-embed-code vocabulary included (768d, reserved for Phase 5)
- `tokenize_code()`, `embed_code()`, `cosine_similarity()` public API
- **Global index migration** (#355)
- Symbol database moved from `.cora/graph.db` to `~/.codecora/cora-code/graph.db`
- `CODECORA_HOME` env var override for custom data directory
- **Binary rename** (#338)
- Crate renamed `cora-cli` → `cora-code`
- Binary name: `cora`
### Changed
- **Docs website** — adopted `@codecora/theme` + VitePress base `/cora/docs/`, retired standalone LandingPage (#348)
- **Uteke memory integration** — removed from user-facing docs (implementation exists but undocumented until API stabilizes) (#367)
### Fixed
- **False positives suppressed in `sec-hardcoded-url` and `crypto/hardcoded-secret` rules** (#369, closes #357, #364)
- `post_match_filter()` added to `builtin.rs` — filters matches in XML/SVG `xmlns` attributes, Rust docstrings, config files, and bare identifiers
- Integrated into `security_scanner.rs` scan loop — all security scanner matches now pass through `post_match_filter`
- Docker hostname regex (`DOCKER_HOST_RE`) fixed — `\d+` now correctly matches port digits
- 31 targeted unit tests added for false positive suppression
- **CI clippy lints** — `map_or(false, ...) → idiomatic `is_some_and(...)` (#369)
### Stats
- 56 files changed, +46,948 / -2,050 lines since v0.7.0
- 11 PRs merged
## [0.7.0] - 2026-07-16
### Highlights
- **Deeper, token-economical cross-file review.** Reviews now resolve **who calls the changed code** (inbound / blast-radius), not just what the changed code calls — so breaking signature/type changes can be flagged. Bounded scanning + thin slices + a signature-only budget fallback keep token cost low.
- **Config is now validated at load time.** Out-of-range values (e.g. `temperature: 5`) and misspelled keys (`quailty_gate`) fail loudly instead of being silently ignored.
- **Markdown false positives suppressed.** Findings inside fenced code blocks (a `git push` in a fenced `bash` block flagged as SQL injection) are now dropped across all finding sources.
- **Performance, security, and correctness fixes** across the scan/review pipeline (10 perf bottlenecks, 2 CVE bumps, 8+ silent-corruption and best-practice bugs).
### Added
- **Inbound caller (blast-radius) resolution.** A new context-chain phase resolves call-sites of functions/types defined or modified in the diff, so breaking changes to their signatures surface their consumers. Gated by new `review.context_chain.include_callers` (default `true`); uses gitignore-aware walking and is bounded (≤400 files, ≤3 call-sites/symbol), injecting only the call line + 1 line of context. New `ContextPriority::CallerSite`.
- **Definition extraction** (`extract_definitions_from_diff`) for Rust/Python/JS-TS/Go/Java-Kotlin — detects functions/types *declared* in the diff, feeding caller resolution. Rust `mod foo;` and Java `import com.example.*` wildcards are now extracted correctly (#73, #72).
- **Signature-only budget fallback.** When the token budget can't fit a full function/type body, a thin signature slice (up to `{`) is injected instead of skipping the entry entirely (~3–5× more symbols under the same budget).
- **`Config::validate()`** (#94) — rejects out-of-range/unsupported values at load: `temperature` (0.0–2.0), `max_tokens`/`timeout` (≥1), `max_tokens_param`, `response_format`, `output.format`, `hook.mode`/`on_violation`/`min_severity`, and `provider.base_url` scheme. Multiple errors are aggregated into one message.
- **`Profile::validate()`** (#81) — focus `weight` must be 1–10, and `action`/`tone`/`detail_level` must be recognized values.
- **`deny_unknown_fields`** on all config sections (#80) — misspelled YAML keys are rejected at parse time.
### Changed
- **`CategoryAction` enum** (#57) — `quality_gate.categories.*.action` is now a case-insensitive enum (`block`/`warn`/`ignore`); a typo like `blok` fails loudly at config load instead of silently becoming blocking.
- **Disabled quality gate never fails** (#58) — `evaluate()` forces `Pass` when `enabled: false`.
- **`context_chain.max_context_tokens` default** raised 3000 → **5000**.
- **`issue_type`** serializes consistently as `issue_type` (#48); `type` retained as a deserialize alias.
- **`Severity::from_str_lossy`** uses `eq_ignore_ascii_case` (no allocation) (#10).
### Fixed
- **Markdown fenced-code-block false positives** (#329) — findings inside fenced code blocks (triple-backtick / triple-tilde) in `.md`/`.mdx`/`.markdown` files are dropped across all finding sources (security/secrets/rules scanners + LLM). Fence state is tracked across full hunk context, so it works even when only the block body was edited.
- **Cross-file resolver used the wrong ignore list** — `review.rs` passed `ignore.rules` (finding-type strings) instead of `ignore.files` (`target/**`, `node_modules/**`); the resolver could inject build-artifact code. Now uses `ignore.files`.
- **Test-file detection over-match** (#87) — `is_test_file` is path-segment aware; `latest`, `aspect`, `attestation` are no longer mistaken for test files.
- **Directory glob excludes over-permissive** (#66) — `src/` matches only at segment boundaries (`mysrc/` no longer caught).
- **Token estimation** (#68) — non-empty content returns ≥1 token (was 0 under integer division).
- **DB size** (#23) — `index_stats` queries `PRAGMA page_size` instead of assuming 4096 bytes.
- **Project-sync workflow** — a merged PR referencing issues via `Refs #N` (not `Closes #N`) no longer fails the `sync` check.
- **10 scan/review performance bottlenecks** (#335) — precompiled regex, batched DB queries, early cutoffs, file-content cache, single reused Tokio runtime, single-transaction prune, etc.
- **Security:** bumped `anyhow` 1.0.102 → 1.0.103 (RUSTSEC-2026-0190) and `crossbeam-epoch` 0.9.18 → 0.9.20 (RUSTSEC-2026-0204).
- Various silent-data-corruption bugs resolved (#333): severity sort, security-findings fallback, deterministic debt-snapshot hashing, debt-trend math, config precedence, `context_chain` merge, and hook-install composition.
## [0.6.2] - 2026-06-21
### Fixed — Token Usage Tracking
- **`tokens_used` is no longer always `None` in review and scan responses.**
- `parse_review_response` and `parse_scan_response` previously discarded the `usage` object returned by the LLM API, hardcoding `Ok((..., None))`. Token counts and cost estimates were silently dropped.
- `chat_completion` now returns `(content, Option<Usage>)` and the parse functions thread `usage` through as `TokenUsage`. All call sites updated.
- `ReviewResponse.tokens_used` and `ScanResponse.tokens_used` now report real values when the provider supplies them.
- **`cora review --stream` now collects token usage.**
- The streaming path (`chat_completion_stream`) previously only accumulated `delta.content` and ignored the `usage` field. It now sends `stream_options: { include_usage: true }` and parses `usage` from the final SSE chunk (top-level or nested in `choices[0].delta.usage`).
- Token counts are now reported correctly for both streaming and non-streaming review.
- **`cora scan` multi-batch token accumulation.**
- When scanning multiple batches, `total_tokens` was overwritten by each batch instead of accumulated. Only the last successful batch's tokens were reported.
- Token usage now accumulates across all batches (`input_tokens`, `output_tokens`, and `estimated_cost_usd` are summed).
### Changed — Code Quality
- **Extracted magic numbers into named constants.**
- `scan.rs`: the hardcoded batch size fallback `20` and token budget `60_000` are now `DEFAULT_MAX_FILES_PER_BATCH` and `DEFAULT_BATCH_TOKEN_BUDGET`.
- **`Usage` struct now accepts camelCase aliases.**
- Some providers (e.g. Azure OpenAI, certain third-party gateways) return `promptTokens` / `completionTokens` / `totalTokens` instead of snake_case. Both forms are now accepted via `#[serde(alias = ...)]`.
### Tests
- Added 4 regression tests for token usage threading: `parse_review_preserves_usage_when_provided`, `parse_review_returns_none_usage_when_not_provided`, `parse_scan_preserves_usage_when_provided`, `usage_to_token_usage_maps_fields_correctly`.
## [0.6.1] - 2026-06-17
### Fixed — Scan
- **`cora scan` no longer aborts on non-JSON LLM responses (#316)**
- Detect non-JSON responses early (provider error pages, rate-limit bodies, empty responses, prose wrappers) and surface the raw response prefix (first 512 bytes) in the error message so users can diagnose the cause.
- Per-batch parse failures are now **non-fatal by default**: the failing batch is skipped with a `warn`-level log and a stderr warning listing the affected files, and the scan continues with the remaining batches. Set `--no-continue-on-batch-error` to restore the old abort behavior.
- Added `--batch-files <N>` flag (default: 20) to control the maximum number of files per LLM batch — lower it to work around provider token limits or rate-limit errors on large scans.
- Truncated-JSON and general parse errors now include the raw response prefix for easier debugging without `--verbose`.
### Fixed — Review
- **`cora review` no longer exits 2 when severity filtering removes all blocking findings (#312)**
- Recompute `should_block` against the **filtered** issue list (after `--severity` filtering) so the exit code matches the SARIF/pretty output the user sees.
- Extracted exit-code logic into `compute_exit_code()` helper (pure function) with 8 unit tests covering gate pass/fail, CI mode, and hook `block` vs non-`block` modes.
- Applies to both the single-chunk and auto-chunked (`--auto-chunk`) review paths.
### Fixed — Install (macOS)
- **macOS installer now strips Gatekeeper quarantine attributes (#313)**
- Prebuilt macOS binaries (`aarch64-apple-darwin`) are not Apple-notarized. When downloaded directly, macOS attaches `com.apple.quarantine` / `com.apple.provenance` xattrs and kills the binary with `Killed: 9` on first launch.
- `install.sh` now runs `xattr -dr` for both attributes on the installed binary on macOS (best-effort, non-fatal).
- Added a prominent `<details>` block in the README install section explaining the symptom, the manual `xattr` workaround for users who download the binary directly, and the `cargo` / Homebrew alternatives.
### Changed — Docs
- **Install section now warns about multiple distribution channels (#314)**
- Recommends a single install method per platform and lists the supported channels (installer script, `cargo`, pre-built binaries).
- Adds a `which -a cora && cora --version` check snippet and guidance for removing stale copies when more than one `cora` is on `PATH` (e.g. `~/.local/bin` vs `~/.cargo/bin` vs npm global).
- Cross-links the original issue for background.
## [0.6.0] - 2026-06-14
### Added — Code Intelligence
- **`cora index`** — persistent SQLite symbol index with FTS5 (#264)
- Regex-based definition extraction for 13 languages
- Incremental reindex via SHA-256 file fingerprints
- `--stats`, `--prune`, `--rebuild`, `--watch` flags
- Database: `.cora/index.db`
- **`cora explore`** — search the symbol index (#265)
- FTS5 full-text search with bm25 ranking
- Filter by `--kind`, `--file`, `--language`
- JSON output mode
- **`cora callers` / `cora impact`** — call graph analysis (#266)
- Reverse call graph traversal (who calls this?)
- Forward impact analysis (what breaks if changed?)
- Depth-limited traversal
- **`cora affected`** — test file selection (#267)
- Find tests affected by source changes
- Call graph + naming convention strategies
- stdin support for `git diff --name-only | cora affected --stdin`
- **Language expansion** — 6 → 13 languages (#268)
- Ruby, PHP, Swift, Scala, Lua, Zig
- **`cora index --watch`** — auto-sync file watcher (#269)
- Poll-based incremental reindex (2s interval)
- No extra dependencies
### Added — MCP Server (14 tools)
- **Phase 1: Code Intelligence** (#284) — 5 new MCP tools:
`cora.search_symbols`, `cora.find_callers`, `cora.find_impact`, `cora.find_affected_tests`, `cora.index_status`
- **Phase 2: Review Pipeline** (#285) — 2 new MCP tools:
`cora.review_diff`, `cora.get_debt`
- **Phase 3: Context Enrichment** (#286) — 2 new MCP tools:
`cora.get_project_info`, `cora.get_memory`
### Added — Cross-Product Bundle
- **Cora + Uteke bundle installer** (#235)
- `install-bundle.sh` — single command installs both tools
- Cross-referencing documentation across all docs
### Fixed
- **Uteke recall flag** — `--format json` → `--json` (uteke v0.0.13+ API) (#259)
- **Uteke v0.1.0 empty results parser** — handle both bare `[]` and wrapped `{"results":[]}`
## [0.5.1] - 2026-06-13
### Added
- **`cora commit`** — review staged diff + generate commit message + commit (#262)
- HITL mode (default): interactive `[Y]es / [E]dit / [N]o` prompt
- YOLO mode (`--yolo`): auto-commit without prompts
- `--force`: commit even if quality gate fails
- `--no-review`: skip review, only generate commit message
- `--edit`: always open `$EDITOR`
- Conventional commit format (feat/fix/refactor/perf/docs/test/chore/style/build/ci)
- Auto-truncates subjects to 72 chars
- Quality gate integration (block on FAIL unless `--force`)
- Debt snapshot saved after commit
- `chat_completion_raw()` + `chat_completion_stream_raw()` in `engine/llm.rs`
- 22 unit tests
### Fixed
- **Uteke recall flag** — `--format json` → `--json` (uteke v0.0.13+ API change) (#259)
- **Uteke recall JSON parser** — handle both bare `[]` and wrapped `{"results":[]}` formats (uteke v0.1.0+)
- Extracted `parse_recall_json()` with 6 unit tests for format compatibility
## [0.5.0] - 2026-06-10
### Added
- **Quality Gate** — configurable threshold-based PASS/FAIL for CI enforcement (#205)
- Global thresholds: `max_critical`, `max_major`, `max_minor`, `max_security`
- Per-category overrides: `block`, `warn`, `ignore` actions
- Terminal-formatted gate output with status table
- Exit code 2 on gate failure
- 12 unit tests covering all gate scenarios
- **Static Security Scanner** — 11 regex patterns for common vulnerabilities (#234)
- Weak crypto (MD5/SHA1 for passwords), hardcoded secrets, SQL injection, eval(), command injection
- Hardcoded roles, debug mode, CORS wildcard, SSL verify disabled
- Auto-skips test files; only scans added lines
- Findings injected into LLM prompt as additional context
- **Language-Specific Analyzers** — tailored review guidance for 6 languages (#233)
- Dart/Flutter: widget lifecycle, state management, null safety
- Svelte/TypeScript: reactivity, stores, SSR, type safety
- Go: error handling, concurrency, goroutine leaks
- Rust: ownership, lifetimes, unsafe, idioms
- Python: type hints, async, security patterns
- **MCP Server** — expose rules and config to AI coding agents (#207)
- JSON-RPC 2.0 over stdio transport
- 5 tools: `list_rules`, `check_snippet`, `get_quality_gate`, `get_config`, `list_profiles`
- `cora mcp` subcommand
- Brace-depth stdin parsing (handles pretty-printed JSON)
- 17 unit tests
- **Auto-chunking** — large diffs split into reviewable chunks automatically (#188)
- `--no-auto-chunk` flag to disable
- `src/engine/chunker.rs` module (~310 lines)
- **Tech debt metrics** — cumulative review history and trend tracking (#206)
- `DebtSnapshot` per-review JSON snapshots with quality score (0-10)
- `cora debt` subcommand — terminal table, `--json`, `--trend` ASCII graph, `--since`, `--branch` filters
- Auto-save after every review (best-effort, never fails review)
- `debt:` config section in `.cora.yaml` (history_dir, retention_days)
- 32 unit tests
- **Uteke memory integration** — recall project patterns and learn from reviews (#232)
- `--memory` flag — recall context from Uteke before review
- `--learn` flag — recall + save findings after review
- `MemoryBackend` with auto-detect, graceful degradation when Uteke not installed
- 11 unit tests
- **Multi-platform CI docs** — Gitea/Forgejo, GitLab CI, Bitbucket Pipelines workflow examples (#225)
- **GitHub Marketplace action** — published as [`codecoradev/cora-review-action@v1`](https://github.com/marketplace/actions/cora-ai-code-review)
- **Improved review prompt** — better consistency, lower false-negative rate, explicit error handling focus area
- **Comprehensive docs/examples.md** — GitHub Actions section with setup guide, inputs reference, and provider table
### Changed
- **CI action moved to GitHub Marketplace** — workflow uses marketplace action instead of `.github/actions/cora-review/`
- **README links** — all documentation links now point to `codecora.dev` instead of relative file paths
- **CI workflows** — removed stale SvelteKit `website/` jobs, replaced with VitePress `docs/` build
- **`merge_into()` returns `Result`** — fail-fast on invalid profile config instead of silently continuing
- **Language context reuses parsed diff** — `build_language_context_from_chunks()` eliminates redundant `parse_diff()` call
- **13 stale issues closed** — migration epics, website tasks, v0.4 leftovers
- **15 stale branches deleted** — cleanup after merge
### Fixed
- **Profiles bugs** — path resolution with project root, fail-fast on invalid config, dedup merge by `id` (#238)
- **Code Scanning alert #79** — eliminated redundant `parse_diff()` call in language context injection
- **Download hardening** — 5x retry with exponential backoff, gzip validation, checksum verification for cora-code binary download in CI (#221)
- **curl hardening** — `--fail --show-error` + `set -e` guard prevents silent HTML downloads
- **Checksum enforcement** — hard fail on missing/invalid checksums (was warning-only)
- **Exact checksum match** — `awk` exact filename lookup replaces `grep` substring match
### Removed
- **SvelteKit `website/`** — 6,286 lines removed, replaced by VitePress `docs/`
- **`Website Lint` CI job** — removed from required status checks
- **Internal composite action** — `.github/actions/cora-review/` deleted, replaced by marketplace action
- **`cora-review-simple`** — unused duplicate action deleted
## [0.4.6] - 2026-06-07
### Changed
- **README redesigned** — 568 → 148 lines, professional layout with star badge, docs index table, links to docs/ for details (#162)
- **All docs updated for v0.4.5+** — changelog, getting-started, usage, roadmap, examples, installation
### Added
- **Deterministic secrets pre-scan** — 12 built-in patterns (AWS, GitHub, OpenAI, Anthropic, Groq, xAI, Slack, Stripe, Google, JWT, Private Key) run before AI review (#204)
- Masked output: `AKIA****CDEF` (first 4 + last 4 chars shown)
- Auto-skip test/spec/fixture/mock/example files
- Secrets findings injected into LLM context for consistent summary
- Fallback path blocks on critical findings even when LLM fails
- **Diff parser hardening** — hunk line count validation, broader binary detection (GIT binary patch, singular form), graceful truncated diff handling (#195 Phase 1)
- **`.agent.md` release checklist** — pre-release checklist to prevent docs drift between versions
### Fixed
- `cora config show --global` / `--project` documented in cli-reference.md (was missing)
- `cora auth login` path corrected from `config.toml` to `auth.toml` in cli-reference.md
- CI example in docs now includes CORA_BASE_URL and CORA_MODEL secrets
## [0.4.5] - 2026-06-07
### Changed
- **Config architecture redesign** — clear separation of concerns between config files (#209)
- `~/.cora/auth.toml` now stores **only the API key** (secret)
- `~/.cora/config.yaml` stores provider, model, base_url, and other settings (global)
- `.cora.yaml` (project) overrides global config per-project
- `CORA_API_KEY` env var reserved for CI use only
- **Provider info auto-migration** — if `auth.toml` still contains provider/model/base_url, automatically moved to `config.yaml` on first run
- **Deterministic rules** — `rules/` added to default exclude paths, preventing rules from matching their own source definitions (#185)
### Fixed
- **`cora config show`** — now displays the **effective resolved config** with source annotations like `[from: env CORA_PROVIDER]` instead of raw file values (#189)
- **`cora config show --global`** — new flag to show only `~/.cora/config.yaml` contents
- **`cora config show --project`** — new flag to show only `.cora.yaml` contents (mutually exclusive with `--global`)
- **`cora review` sends to wrong provider** — provider info from `auth.toml`/`config.yaml` was ignored at runtime, always defaulting to OpenAI. Now correctly reads from merged config (#209)
- **`save_provider_info` data loss** — parse failure on `config.yaml` no longer silently replaces the entire file with defaults (now returns error)
- **`cora auth login` interactive flow** — now auto-detects provider env vars (e.g. pick ZAI → detects `ZAI_API_KEY`), suggests model and base URL defaults from presets (enter to accept) (#203)
- **`cora auth login --provider zai`** — now auto-detects `ZAI_API_KEY` from environment, no need for `--api-key` flag (#184)
- **Env var override visibility** — `cora config show` now annotates which values come from env vars vs config files (#182)
- **Truncated JSON repair tests** — 12 new tests confirming `repair_truncated_json()` works correctly for all edge cases (#186)
### Added
- **`--global` / `--project` flags** on `cora config show` for scoped config inspection
- **Clap `conflicts_with`** on `--global`/`--project` — `cora config show --global --project` now rejected at CLI level
- **Interactive model/base URL prompts** — during `cora auth login`, shows preset defaults and allows override with enter-to-accept
## [0.4.4] - 2026-06-06
### Fixed
- **Spinner auto-hides in non-TTY** — `indicatif` progress spinners in `llm.rs` and `scanner.rs` now detect piped/redirected stderr and auto-hide, preventing ANSI pollution in captured output (#181)
- **Truncated JSON repair** — LLM responses cut off by max_tokens are now auto-repaired by closing unclosed strings/brackets before parse, preserving partial findings instead of failing completely (#186)
### Added
- **`--output-file <PATH>` flag** — write formatted review output to a file instead of stdout, guaranteeing capture in CI/batch pipelines (#181)
## [0.4.3] - 2026-06-06
### Fixed
- **Provider shortcut now resolves preset defaults** — bare `provider: zai` in `.cora.yaml` auto-fills `base_url` and `model` from the preset table (#183)
- **Env var override warnings** — `CORA_PROVIDER`, `CORA_MODEL`, `CORA_BASE_URL` now warn when they override config file settings (#182)
- **`config show` displays effective (resolved) config** — shows actual runtime values with `[from: env ...]` annotations when env vars override config (#189)
- **Auth file permissions auto-fix** — `~/.cora/auth.toml` permissions auto-corrected to 600 instead of just warning (#187)
- **Deterministic rules exclude own source files** — security rules no longer match against `rules/` and `tests/` directories, eliminating false positives (#185)
### Added
- **Non-interactive `cora auth login`** — `--provider`, `--api-key`, `--model`, `--base-url`, `--force` flags for scriptable setup (#184)
## [0.4.2] - 2026-06-06
### Fixed
- **Cora Review now works on fork PRs** — changed trigger from `pull_request` to `pull_request_target` so `GITHUB_TOKEN` has write access for PR comments on external contributor PRs. Explicitly checks out PR head SHA for correct diff (#178 context)
### Added
- **Top-level provider shortcuts in `.cora.yaml`** — `model:`, `base_url:`, and bare `provider:` string now accepted at top level without needing nested `provider:` section (#178, closes #176)
## [0.4.1] - 2026-06-06
### Fixed
- **Regex panic on optional hunk groups** — bare hunk headers like `@@ -1 +1 @@` (without `,count`) caused `caps[4]` index-out-of-bounds panic. Now uses `caps.get(N)` with safe fallback (#167)
- **Default max_diff_size raised to 5MB** — 50KB was too small for most real PRs (#167)
- **CI action resilience** — 3× retry on cora review failure, 600s timeout, graceful SARIF fallback when LLM API is unavailable (#174)
### Added
- **`cora init` now installs pre-commit hook** — automatically creates `.git/hooks/pre-commit` alongside `.cora.yaml`. Use `--no-hook` to skip. Falls back gracefully when not in a git repo (#176)
- **Tiered `cora auth login`** — interactive provider selection with numbered menu. Known providers (openai, anthropic, groq, ollama, zai) pre-fill base URL and model. Custom providers ask for base URL + model + key (#172)
- **Configurable CI action** — reads `.cora.yaml` from repo when present, falls back to 5MB limit when absent. Removes hardcoded `max_diff_size: 200000` (#172)
- **`on_violation` config + `--ci` mode** — hard gate for CI: `on_violation: disallow` makes cora exit non-zero on any finding. `--ci` flag enables strict non-interactive mode (#152)
- **`cora hook install/uninstall`** — explicit hook management commands (previously only via `cora init`)
### Changed
- **CI action reads `.cora.yaml`** — project config takes precedence over hardcoded fallback. `max_diff_size`, `hook.mode`, `llm.timeout` all respected in CI (#172)
## [0.4.0] - 2026-06-03
### Added
- **Deterministic rule engine** — pre-LLM regex-based rules that always report findings (no LLM dismissal). 12 built-in rules covering security (hardcoded URLs, secrets, TLS disabled, debug prints), SQL injection, TODO/FIXME, `panic!`/`unwrap` in new code, and large functions (#116)
- **Custom rules via `.cora.yaml`** — define project-specific regex rules with severity, category, exclude patterns, and glob file matching
- **Unified diff parser** — parse git diff into structured `FileChunk`/`DiffHunk`/`DiffLine` with language detection for 70+ extensions
- **File bundling engine** — smart grouping by directory and language family with configurable character/file limits. Bundle types: related, config, test, large, standalone. Token budget estimation (~4 chars/token). Defers full parallel review to v0.5 (#115)
- **Cross-file context chain** — deterministic symbol extraction (imports, function calls, type references) for 5 languages (Rust, Python, JS, Go, Java) with token-budgeted context injection into LLM prompt (#114)
- **`BundlingConfig`** — `strategy`, `max_chars_per_group`, `max_files_per_group`, `coalesce_by_directory`, `coalesce_by_language` in `.cora.yaml`
- **`ContextConfig`** — `enabled`, `max_context_tokens`, `follow_depth`, `max_symbols` in `.cora.yaml` review section
- **Default SARIF upload to GitHub Code Scanning ON** — opt-out with `upload-sarif: false` (#148)
- **SARIF tool branding** — `CodeCora` driver name (`codecoradev/cora-code`) in SARIF output (#148)
### Changed
- **Review pipeline** — rules engine runs before LLM call, context chain enriches LLM prompt with cross-file dependencies
- **LLM failure handling** — deterministic rule findings always visible even when LLM call fails
## [0.3.0] - 2026-06-03
### Added
- **Static analysis context injection** — optional clippy output injected into review prompt to reduce false positives on verified-intentional changes (#140)
- **`review.static_analysis.auto_clippy`** config — automatically run `cargo clippy` and filter output to changed files
- **`review.static_analysis.clippy_output_file`** config — read pre-computed clippy output from file
- **`cora config validate`** subcommand — validate `.cora.yaml` configuration file and report issues (#88)
- **`CoraError` enum via thiserror** — structured error types for engine layer with 17 variants (#86)
### Changed
- **Engine layer migrated from `anyhow` to `thiserror`** — structured error handling in engine, `anyhow` retained in CLI layer (#86)
- **All clippy pedantic warnings resolved** — 175 → 0 warnings across entire codebase (#84)
- **Repo URLs updated** to `codecoradev/cora-code` org (#137)
- **CI actions bumped** — `upload-artifact@v7`, Node 24 strict mode (`FORCE_JAVASCRIPT_ACTIONS_TO_NODE24`) (#142)
### Fixed
- **CI Cora Review fails on LLM API errors** — removed `|| true` suppression, added exit code + empty SARIF check (#142)
- **Match arm merge in `IssueType::from_str`** — clarified documentation (#141)
## [0.2.0] - 2026-06-02
### Added
- **`--progress` flag** — NDJSON progress events to stderr for structured CI/GUI consumers (Termul prerequisite) (#108)
- **`--max-diff-size` flag** — override `hook.max_diff_size` for large diffs from CLI (#112)
- **Output footer watermark** — Cora version stamp in terminal, SARIF, and JSON output when issues found (#106)
- **Security audit CI** — `cargo audit` via `rustsec/audit-check` for dependency CVE scanning (#85)
### Changed
- **Naive .gitignore parser → `ignore` crate** — ripgrep-grade correctness with nested .gitignore, global gitignore, and `.git/info/exclude` support (#80)
- **Blanket `#![allow(dead_code)]` removed** — targeted cleanup, 27 warnings → 0 (#79)
### Fixed
- **`REQUESTS_CA_BUNDLE` env var support** — custom CA certificates for corporate proxies, additive to built-in root certs (#74)
- **`tls_built_in_root_certs(false)` security fix** — custom CA bundle now added alongside system roots instead of replacing them (caught by Cora self-review)
- **`require_git(false)` on WalkBuilder** — gitignore rules applied even outside git repositories (#112)
- **CI `actions-rs/audit-check` → `rustsec/audit-check`** — replaced archived GitHub Action (#112)
- **Cora CI diff limit** — `CORA_CONFIG` env var with temp config for 200K char limit in CI action (#112)
## [0.1.8] - 2026-06-02
### Fixed
- **`unwrap()` → `expect()`** in ProgressStyle templates (llm.rs, scanner.rs) — clearer panic messages on template parse failure (#87)
- **Consolidated duplicate `impl Severity` blocks** into single implementation (#83)
- **`file_content_hash` returns `Option<String>`** instead of empty string on read failure — prevents infinite rescan loop on unreadable files (#77)
- **Permission errors logged in scanner** — file walk now logs permission errors at debug level instead of silently skipping (#76)
- **Auth file permission warning** — warns if `~/.cora/auth.toml` has overly permissive file permissions (Unix only) (#72)
- **SARIF upload size validation** — validates SARIF file size against GitHub's 10MB limit before upload (#82)
- **Float division for MB display** — SARIF size error now shows accurate fractional MB (was integer division truncating to 0) (#82)
- **Non-deterministic `DefaultHasher` → `sha2`** — scan cache now uses SHA-256 for deterministic hashing across Rust versions (#81)
### Added
- **`checksums-sha256.txt` in release artifacts** — release workflow generates SHA-256 checksums for all platform binaries (#109)
### Changed
- **Official CodeCora branding assets** — logo, favicon, and OG image updated from ajianaz/cora SaaS repo (#110)
- **Standalone `cora-review.yml` workflow** — CI action extracted from inline `ci.yml` job to dedicated workflow with concurrency control (#107)
- **Action v2 hardened** — all third-party actions pinned to commit SHA, checksum verification for binary downloads, env var indirection for inputs, `grep` pipefail fix, empty file guard, Node 24 strict mode compatibility (#107)
## [0.1.7] - 2026-06-01
### Added
- **Diff-hash caching** — review results cached by SHA-256 of diff + model + temperature in `~/.cache/cora/reviews/`. Cache TTL configurable via `llm.cache_ttl` (#100)
- **`--no-cache` flag** — bypass cache for fresh reviews (#100)
- **Configurable LLM parameters** — `llm.temperature` (default: 0), `llm.max_tokens` (default: 4096), `llm.timeout` (default: 120s), `llm.cache_ttl` (default: 1440 min) in `.cora.yaml` (#98 #101)
- **Git ref validation** — rejects refs containing shell metacharacters or path traversal sequences (#73)
### Fixed
- **Temperature default now 0** — eliminates non-deterministic LLM output. Same diff produces identical issues on every run (#98, #97)
- **HTTP timeout actually works** — per-request timeout via reqwest RequestBuilder (not client-level). Configurable timeout respected (#99)
- **Connection pooling** — shared reqwest::Client via LazyLock, reused across all requests (#99)
- **Cache key includes model + temperature** — config changes invalidate cache automatically (#100)
- **Silent config corruption** — malformed `.cora.yaml` now shows clear error with file path and hint (#78)
- **Composite action KeyError on API failure** — version resolution retries 3x with 5s delay, falls back to v0.1.6 with warning. Fixed in both `cora-review` and `cora-review-simple` actions (#102)
## [0.1.6] - 2026-06-01
### Added
- **Custom system prompts via config** — `review.system_prompt`, `review.system_prompt_file`, `scan.system_prompt`, `scan.system_prompt_file` fields in `.cora.yaml` (#94)
- **`response_format` config** — opt-in `json_object` response format for providers that support it, via `review.response_format: json_object` (#92)
- **File path injection into prompts** — valid diff file paths are injected into the review user prompt to reduce LLM hallucination (#93)
- **Post-parse file path filtering** — issues referencing non-existent files are filtered out after LLM response parsing (#93)
- **Enhanced default system prompts** — both review and scan prompts now include explicit anti-hallucination constraints, severity definitions, and format instructions (#95)
### Fixed
- **Path traversal in `system_prompt_file`** — arbitrary file read vulnerability. Now validates file path is within canonicalized project root (#92)
- **Symlink bypass in path traversal guard** — project root is now canonicalized to match resolved file paths
## [0.1.5] - 2026-06-01
### Fixed
- **Critical: JSON repair corrupts valid unicode escapes** — `is_valid_json_escape()` missing `'u'`, causing `\uXXXX` to be double-escaped. Now properly validates and handles incomplete `\u` sequences (#89)
- **Critical: TOML injection in `save_api_key()`** — API key written via `format!` string interpolation. Now uses `toml::Table` serialization (#69)
- **Retry prompt improvement** — retry on parse failure now includes stricter JSON format instructions (#90)
- **Temp file race condition** — SARIF upload now uses PID-suffixed temp path instead of fixed filename (#70)
- **Confusing unused `_cli_api_key` parameter** — removed from `load_config()` signature (#75)
### Security
- `save_api_key()` now uses `toml::Table::insert()` instead of string interpolation (prevents TOML injection)
- Temp SARIF file path includes process ID (prevents TOCTOU race)
## [0.1.4] - 2026-06-01
### Added
- LLM JSON repair engine (`repair_invalid_escapes`) — auto-fixes invalid escape sequences in LLM output (e.g. `\s`, `\d`) before JSON parse
- Retry mechanism in `review_diff` — if first LLM parse fails, automatically retries once
- Branding footer on "No issues found" PR comment — consistent with issues-found variant
### Fixed
- **Silent false-negative** — cora JSON parse failure previously posted "No issues found" without actual review (LLM invalid escapes)
- Hardcoded Infisical `identity-id` in `release.yml` and `deploy-website.yml` — migrated to `secrets.INFISICAL_IDENTITY_ID`
- Release workflow changelog extraction — `v` prefix mismatch (tag `v0.1.3` vs CHANGELOG `[0.1.3]`) now properly stripped
- `printf` double-escape in release workflow — `\\n` corrected to `\n`
- Stale `v0.1.2` binary download filenames in README
- Clippy `unnecessary_map_or` lint — `.map_or(false, |s| s.success())` replaced with `.is_ok_and(|s| s.success())`
### Changed
- All 3 workflows use `secrets.INFISICAL_IDENTITY_ID` (consistent with `ci.yml` pattern)
- Release workflow validates semver format before sed injection
- Branch cleanup — removed 14 stale branches
## [0.1.3] - 2026-06-01
### Added
- `cora config set --global` — write config to `~/.cora/config.yaml` instead of project `.cora.yaml`
- `cora config set base_url` — set base URL via CLI (previously only in YAML)
- Global config support (`~/.cora/config.yaml`) with priority chain: CLI flags → env vars → project → global → defaults
- Auto-migration from old `~/.cora/config.toml` to new YAML + `auth.toml` split
### Changed
- `cora config set` now writes YAML instead of TOML (compatible with config loader)
- API key storage moved from `~/.cora/config.toml` to `~/.cora/auth.toml` (0600 permissions)
- YAML serialization uses `skip_serializing_if` — no more `null` values in output
### Fixed
- **Severity comparison inverted** — `Critical` issues no longer silently pass `should_block` check (Ord ordering bug)
- Hook `mode: block` no longer exits with code 2 when "No issues found" (severity filter mismatch)
- Consistent severity logic across review, scan, and block mode paths
## [0.1.2] - 2025-05-29
### Added
- `cora init` — create `.cora.yaml` config file with provider/model selection
- `cora hook install|uninstall` — pre-commit hook management
- `cora config show|set` — configuration management
- CI composite action (`cora-review-simple`) for easy GitHub Actions integration
- Shell completions for bash, zsh, fish, and powershell
- `cora scan --incremental` with SHA256 content hash cache for fast incremental scanning
- `cora review --upload` for direct SARIF upload to GitHub Code Scanning
- `cora review --stream` for real-time review output
- `cora review --unpushed` for reviewing unpushed commits
- `cora review --base <branch>` for branch comparison
- `cora review --diff-file <path>` for reviewing external diff files
- `cora providers` command to list available LLM providers
- `cora auth login` for interactive API key storage
### Fixed
- SARIF schema compliance for GitHub Code Scanning upload
- Clippy `format_in_format_args` warnings
- Replaced deprecated `serde_yaml` with `serde_yaml_ng`
- Normalized release binary naming (`cora-{arch}-{target}-v{version}.tar.gz`)
### Changed
- Replaced deprecated dependencies
- Removed unused dependencies
- Bumped minimum Rust version to 1.85
## [0.1.1] - 2025-05-27
### Changed
- Replaced ASCII art banner with eye icon in README
- Updated README branding to cora-code
### Fixed
- CI `cargo publish` with `--allow-dirty` for Cargo.lock mismatch on tag checkout
## [0.1.0] - 2025-05-25
### Added
- **AI Code Review** — review staged changes, commit ranges, branch diffs, and full project scans
- **BYOK** — bring your own API key (OpenAI, Anthropic, Groq, Ollama, Google)
- **5 LLM Providers** — with auto-detection from installed API keys
- **Pre-commit Hooks** — `cora hook install` for automatic review on every commit
- **SARIF Output** — `--format sarif` for GitHub Code Scanning integration
- **4 Output Formats** — pretty (colored), compact, JSON, SARIF
- **Project Config** — `.cora.yaml` per-project configuration with provider, focus, rules, ignore, and hook settings
- **Environment Variables** — `CORA_API_KEY`, `CORA_MODEL`, `CORA_PROVIDER`, `CORA_BASE_URL`, `CORA_CONFIG`, `CORA_FORMAT`
- **Severity Levels** — `info`, `minor`, `major`, `critical` with configurable thresholds
- **Focus Areas** — `security`, `performance`, `bugs`, `best_practice`, `maintainability`
- **Ignore Rules** — file patterns and rule-level exclusions
- **Cross-platform** — Linux (x86_64, ARM64), macOS (Apple Silicon), Windows (x86_64)
- **MIT License** — fully open source
[Unreleased]: https://github.com/codecoradev/cora-code/compare/v0.18.0...develop
[0.18.0]: https://github.com/codecoradev/cora-code/compare/v0.17.2...v0.18.0
[0.17.2]: https://github.com/codecoradev/cora-code/compare/v0.17.1...v0.17.2
[0.17.1]: https://github.com/codecoradev/cora-code/compare/v0.17.0...v0.17.1
[0.17.0]: https://github.com/codecoradev/cora-code/compare/v0.16.1...v0.17.0
[0.16.1]: https://github.com/codecoradev/cora-code/compare/v0.16.0...v0.16.1
[0.16.0]: https://github.com/codecoradev/cora-code/compare/v0.15.0...v0.16.0
[0.15.0]: https://github.com/codecoradev/cora-code/compare/v0.14.0...v0.15.0
[0.14.0]: https://github.com/codecoradev/cora-code/compare/v0.13.0...v0.14.0
[0.13.0]: https://github.com/codecoradev/cora-code/compare/v0.12.0...v0.13.0
[0.12.0]: https://github.com/codecoradev/cora-code/compare/v0.11.1...v0.12.0
[0.11.1]: https://github.com/codecoradev/cora-code/compare/v0.11.0...v0.11.1
[0.11.0]: https://github.com/codecoradev/cora-code/compare/v0.9.0...v0.11.0
[0.9.0]: https://github.com/codecoradev/cora-code/compare/v0.8.3...v0.9.0
[0.8.3]: https://github.com/codecoradev/cora-code/compare/v0.8.2...v0.8.3
[0.8.2]: https://github.com/codecoradev/cora-code/compare/v0.8.1...v0.8.2
[0.8.1]: https://github.com/codecoradev/cora-code/compare/v0.8.0...v0.8.1
[0.8.0]: https://github.com/codecoradev/cora-code/compare/v0.7.0...v0.8.0
[0.7.0]: https://github.com/codecoradev/cora-code/compare/v0.6.2...v0.7.0
[0.6.2]: https://github.com/codecoradev/cora-code/compare/v0.6.1...v0.6.2
[0.6.1]: https://github.com/codecoradev/cora-code/compare/v0.6.0...v0.6.1
[0.6.0]: https://github.com/codecoradev/cora-code/compare/v0.5.0...v0.6.0
[0.5.0]: https://github.com/codecoradev/cora-code/compare/v0.4.6...v0.5.0
[0.4.6]: https://github.com/codecoradev/cora-code/compare/v0.4.5...v0.4.6
[0.4.5]: https://github.com/codecoradev/cora-code/compare/v0.4.4...v0.4.5
[0.4.4]: https://github.com/codecoradev/cora-code/compare/v0.4.3...v0.4.4
[0.4.3]: https://github.com/codecoradev/cora-code/compare/v0.4.2...v0.4.3
[0.4.2]: https://github.com/codecoradev/cora-code/compare/v0.4.1...v0.4.2
[0.4.1]: https://github.com/codecoradev/cora-code/compare/v0.4.0...v0.4.1
[0.4.0]: https://github.com/codecoradev/cora-code/compare/v0.3.0...v0.4.0
[0.3.0]: https://github.com/codecoradev/cora-code/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/codecoradev/cora-code/compare/v0.1.8...v0.2.0
[0.1.8]: https://github.com/codecoradev/cora-code/compare/v0.1.7...v0.1.8
[0.1.7]: https://github.com/codecoradev/cora-code/compare/v0.1.6...v0.1.7
[0.1.6]: https://github.com/codecoradev/cora-code/compare/v0.1.5...v0.1.6
[0.1.5]: https://github.com/codecoradev/cora-code/compare/v0.1.4...v0.1.5
[0.1.4]: https://github.com/codecoradev/cora-code/compare/v0.1.3...v0.1.4
[0.1.3]: https://github.com/codecoradev/cora-code/compare/v0.1.2...v0.1.3
[0.1.2]: https://github.com/codecoradev/cora-code/compare/v0.1.1...v0.1.2
[0.1.1]: https://github.com/codecoradev/cora-code/compare/v0.1.0...v0.1.1
[0.1.0]: https://github.com/codecoradev/cora-code/releases/tag/v0.1.0