Skip to content

Latest commit

 

History

History
1115 lines (790 loc) · 94.6 KB

CHANGELOG.md

File metadata and controls

1115 lines (790 loc) · 94.6 KB

Changelog

All notable changes to cora-code are documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Added

  • Agents validate before they commit (#674). A review of the staged diff, by an agent (cora.review_finalize) or by cora review --staged, now leaves a marker bound to that exact change set. cora hook verify reports it (exit 0 validated, 10 validated but blocking, 11 not validated) and cora hook claude-guard is a Claude Code PreToolUse hook that denies a git commit that is unreviewed, blocking, uses --no-verify, or is not a plain commit of the staged change. The pre-commit hook (now v2, upgraded in place by cora hook install) accepts a validated diff and otherwise falls back to cora review --staged --ci --severity major; a tooling error is reported as "not a pass". The /cora-review skill tells agents to review before every commit. See docs/usage.md.

Fixed

  • Custom rules are validated and the docs match the schema (#684, #678). cora config validate and config load now reject an invalid regex, an empty or duplicate id, an id that shadows a built-in rule, an empty message and an unknown languages value, naming the rule. languages accepts language names ("rust") as well as extensions ("rs"); languages and exclude are optional. cora config validate --dry-run [--path DIR] runs each custom rule and prints how many lines it matched. docs/configuration.md now shows the real schema (rules_engine.custom); the 0.19.0 entry no longer says custom_rules.
  • cora-ignore no longer fails silently (#679). A marker on a line with findings that names none of them (a typo, an unknown rule, or explanation text that became part of the rule name) now prints a warning on stderr naming the marker and the rule ids on that line. -- ends the rule list, so cora-ignore: sec-hardcoded-secret -- env var name suppresses the finding and keeps the reason in the source.
  • The Cora Review workflow now fails with an error naming each empty secret (CORA_API_KEY, CORA_BASE_URL, CORA_MODEL) instead of running without them.
  • The Cora Review job no longer gets cancelled on larger pull requests: its time limit is 30 minutes (the review action allows up to three LLM attempts of 10 minutes each).

0.19.0 - 2026-10-16

Added

  • The calling agent can be the reviewer, with no LLM configured in cora (#647). Four MCP tools split a review into prepare / judge / finalize with the agent as the judge: cora.review_prepare returns the hardened prompts, valid_files, deterministic findings and the response schema; cora.review_finalize merges the agent's findings (validated by judge::admit), applies ignore.rules, inline markers and the quality gate, and returns the verdict. cora.scan_prepare (paged by batch, path restricted to the project) and cora.scan_finalize do the same for a scan. Stateless: finalize recomputes the deterministic half from the same diff/path. cora install also installs a /cora-review skill for Claude Code (~/.claude/skills/cora-review/SKILL.md whenever ~/.claude exists; a customized copy is never overwritten or removed without --force). For Claude Code the MCP server itself is registered with claude mcp add cora -- cora mcp. See docs/usage.md for the security model.

  • cora review and cora scan run without an API key (#646). With no key they run the deterministic checks only, print a one-line notice on stderr, and label the result "Deterministic checks only (no LLM judge)" (SARIF: cora.judge = none on the invocation, even with zero results). --no-llm forces it and silences the notice. A missing key is no longer an error for these two commands; any other config error still is. The judge is chosen once at the entry point (judge::select_judge, JudgeChoice). cora commit and MCP cora.review_diff still need a key.

Security

  • --help no longer prints secrets from the environment. clap shows [env: NAME=value] for options bound to an env var, so cora --help with CORA_API_KEY or GITHUB_TOKEN set printed the key (into logs, CI output, screenshots). --api-key and --token now hide their env value.

  • The SARIF "no judge" marker can no longer be forged or missed. A model-written summary containing the marker sentence is stripped, and the judge-failure fallback (deterministic findings only) now carries the marker, so cora.judge = "none" is set exactly when no model judged.

  • A hostile diff can no longer hide findings (audit follow-up). Inside a hunk whose line counts are not used up, --- / +++ lines are content, not file headers (a removed SQL/Lua comment -- x, or an added line ++ b/tests/y.py, used to drop the rest of the hunk or re-file it under another path, so a secret was reported as nothing). valid_files now comes from the same parser, and file names are escaped in the review prompt.

  • A judge can no longer downgrade or absorb a scanner finding (audit follow-up). When a judge issue sits on the same line and topic as a scanner finding, the surviving issue takes the stronger severity and the scanner's rule_id (an "info" judge issue used to replace a CRITICAL scanner finding).

  • cora scan / cora.scan_prepare no longer follow symlinks, and enclosing-scope reads stay inside the project (a hostile repo's id -> ~/.ssh/id_rsa or a +++ b/../../x header could put outside files into a model prompt).

  • cora install --remove keeps a customized skill unless --force; agent-mode tools report dropped_by_validation, accept any severity case, refuse a scan_finalize whose files_fingerprint no longer matches, and compute should_block and the quality gate on all findings (the caller's min_severity only trims the list). admit also fixes lines for deleted files and the old path of a renamed file. --no-llm and review_finalize skip building the LLM prompt (no clippy run).

  • Bumped rustls 0.23.45 → 0.23.46 and quinn-proto 0.11.16 → 0.11.19 (RUSTSEC-2026-0338/0339/0340/0341/0342 and RUSTSEC-2026-0346/0347/0348/0349), plus the yanked chacha20 0.10.1 → 0.10.2. Lockfile-only patch bumps; this was failing the Security Audit (CVE) job on every PR.

  • A judge's findings are untrusted input, bounded by one gate (#644). judge::admit clears rule_id (only deterministic scanners may carry one; it feeds suppression and the finding cap), truncates title/body/suggested_fix, caps the list at 200 findings (most severe kept), drops findings for files outside the diff or scan batch, and turns a line the diff/file does not contain into a file-level finding. cora review and cora scan both apply it; deterministic findings are merged afterwards, so a judge can add findings but never remove one. cora scan now wraps each batch in an untrusted-data fence like review does.

  • The review cache can no longer serve a stale or cross-mode result (#644). The cache moved behind the LLM adapter and stores the model's raw answer keyed by the exact prompts (so focus, rules, profile, system prompt, sanitization, context and memory all invalidate it) plus model/provider/base URL/temperature. ignore.rules, hook.min_severity and deterministic rules are applied on every run, including cache hits. Existing cache entries are simply not hit again.

Changed

  • Help points to agent mode. cora --help, cora review/scan --help, cora mcp --help and cora install --help now mention --no-llm, the agent-as-reviewer MCP tools, the /cora-review skill and claude mcp add cora -- cora mcp.

  • Rules are test-code aware (#663). Whole test files (*_test.*, *.test.ts, spec/, fixtures/, ... not just tests/) and Rust #[cfg(test)] modules / #[test] functions (also a diff hunk inside mod tests) are exempt from bug-unwrap, bug-expect, bug-println, qual-clone, qual-error-ignore, sec-hardcoded-url and sec-hardcoded-secret; the secret scanners only run their high-confidence patterns there, so a real AWS key or token in a test is still reported (injection, eval and other security patterns still apply inside test blocks, and a test region is only trusted when our brace matching agrees with the code's own indentation: /* { */ comments are ignored, and a region that never closes, or that contains an item starting at or left of its own indentation, is dropped instead of exempting what follows). An attribute that carries its item on the same line (#[cfg(test)] fn helper() {}) no longer exempts the NEXT line, and an attribute inside a block comment is not an attribute. bug-println is also skipped in main.rs, src/bin/, examples/ and benches/. New setting rules_engine.include_tests: true turns the exemptions off. On uteke-core (Rust) the findings drop from 2400 to 235 and the 6 Critical ones disappear; on uteke-cloud the 3 Major URL findings are gone. New module engine::test_context.

  • rules_engine.max_findings now defaults to 50, and Critical/Major findings are never hidden. The old default of 5 could hide serious findings (a module with 7 critical findings showed 5) and made agents and users misread a capped list as the full picture. The cap now limits only the noise: it keeps every Critical and Major finding and fills the remaining slots with the most severe Minor/Info ones, still reporting how many were not shown. On a real Rust codebase the unfiltered list was up to 2400 findings, 88% bug-unwrap, so unlimited is not the default. Set max_findings: 0 (or null) for no cap.

  • Review is now prepare / judge / finalize with the LLM as the seam (#649). review::prepare turns a diff into a judge::Packet (judge-visible text, assembled context, system prompt override, valid_files, diff chunks, deterministic findings); a judge::Judge returns findings (HttpJudge = today's LLM call, NoJudge = deterministic only); review::review_with merges, guards file paths, post-processes and decides should_block. Output of cora review is unchanged. The review pipeline now has an end-to-end test with a fake judge (no network), and the llm module no longer prints: spinners moved to progress::Spinner and llm::run_review_prompts / llm::scan_files take an events observer (the old llm::review_diff / llm::review_diff_stream are replaced by run_review_prompts).

  • One finalize module owns the result tail of review, scan, commit and MCP (#648). engine::finalize (shape, shape_issues, blocks, exit_code, save_debt, save_history) replaces six hand-copied versions of severity filtering, should_block, quality gate, exit code, debt snapshot and review history. Behavior of cora review (single and chunked) and cora scan is unchanged. Fixed along the way: the MCP cora.review_diff tool now honors its min_severity parameter and evaluates the quality gate on the reported findings; cora commit now records its review in the review history (command = "commit").

Fixed

  • crypto/hardcoded-secret / sec-hardcoded-secret no longer flag name: value entries whose value is not a literal (#661). In source files, webhook_secret: env::var("X"), refresh_token: prev?.refresh_token, token: Option<&'static str>, "password": ACCOUNT_PW and "clientSecret": j["clientSecret"] are references or types, and format placeholders (api_key = "{}", "%s") are not values. Quoted literals (token: "ghp_...") and = assignments are judged as before. On two real codebases the Critical/Major findings dropped from 38 to 20 (all remaining ones are test fixtures, SQL and test URLs, tracked in #662/#663).

  • injection/sql-concat and sec-sql-concat no longer flag prose or identifier interpolation (#662). A SQL keyword in a message (format!("Insert failed: {e}"), format!("Deleted tag '{}'")) is not SQL: the line needs SELECT..FROM, INSERT INTO, UPDATE..SET or DELETE FROM. Real SQL is reported only when a value is spliced in (= '{x}', WHERE id = {id}, LIKE '%{t}%', IN ({ids}), VALUES (...), "..." + user, query += ...); FROM {table}, SET {cols} and ?{n} parameter indexes cannot be bound as parameters and are not value injection. On two real codebases all five SQL false positives (Critical) are gone.

  • Agent-mode review no longer hides capped findings without saying so. cora.review_prepare listed every deterministic finding but cora.review_finalize showed only rules_engine.max_findings (default 5) of them with no explanation; found by running a real agent on a real project. review_prepare now reports deterministic_findings_total and the cap, and review_finalize / scan_finalize / cora.review_diff report not_shown (with a note).

  • The MCP cora.review_diff tool no longer crashes the server (#647). Every call panicked with "Cannot start a runtime from within a runtime" because the handler blocked on a second runtime from a tokio runtime thread. Handlers now go through block_in_place.

  • cora scan now runs the rules engine (#645). Built-in rules (bug-todo, sec-sql-concat, ...) and custom rules_engine.custom rules were only run by cora review; scan silently skipped them. Expect more findings from cora scan after upgrading (e.g. bug-todo on every TODO), still under the rules_engine.max_findings cap (default 5); rules_engine.enabled: false turns them off. One set of deterministic families (deterministic::run_files for whole files, deterministic::run for diffs) now serves both, and the MCP cora.check_snippet tool picks up rules too. Scan also has a judge seam (judge::ScanJudge, with HttpScanJudge and NoJudge) so the batch loop is tested without a network, and DEFAULT_BATCH_TOKEN_BUDGET is renamed DEFAULT_BATCH_CHAR_BUDGET (it was always a character count). docs/cli-reference.md now states which review flags scan does not have.

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.

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).

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).

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.

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
  • 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