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.
- Agents validate before they commit (#674). A review of the staged diff, by an agent (
cora.review_finalize) or bycora review --staged, now leaves a marker bound to that exact change set.cora hook verifyreports it (exit 0 validated, 10 validated but blocking, 11 not validated) andcora hook claude-guardis a Claude CodePreToolUsehook that denies agit committhat 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 bycora hook install) accepts a validated diff and otherwise falls back tocora review --staged --ci --severity major; a tooling error is reported as "not a pass". The/cora-reviewskill tells agents to review before every commit. Seedocs/usage.md.
- Custom rules are validated and the docs match the schema (#684, #678).
cora config validateand 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 unknownlanguagesvalue, naming the rule.languagesaccepts language names ("rust") as well as extensions ("rs");languagesandexcludeare optional.cora config validate --dry-run [--path DIR]runs each custom rule and prints how many lines it matched.docs/configuration.mdnow shows the real schema (rules_engine.custom); the 0.19.0 entry no longer sayscustom_rules. cora-ignoreno 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, socora-ignore: sec-hardcoded-secret -- env var namesuppresses the finding and keeps the reason in the source.- The
Cora Reviewworkflow now fails with an error naming each empty secret (CORA_API_KEY,CORA_BASE_URL,CORA_MODEL) instead of running without them. - The
Cora Reviewjob 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
-
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_preparereturns the hardened prompts,valid_files, deterministic findings and the response schema;cora.review_finalizemerges the agent's findings (validated byjudge::admit), appliesignore.rules, inline markers and the quality gate, and returns the verdict.cora.scan_prepare(paged by batch, path restricted to the project) andcora.scan_finalizedo the same for a scan. Stateless: finalize recomputes the deterministic half from the same diff/path.cora installalso installs a/cora-reviewskill for Claude Code (~/.claude/skills/cora-review/SKILL.mdwhenever~/.claudeexists; a customized copy is never overwritten or removed without--force). For Claude Code the MCP server itself is registered withclaude mcp add cora -- cora mcp. Seedocs/usage.mdfor the security model. -
cora reviewandcora scanrun 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 = noneon the invocation, even with zero results).--no-llmforces 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 commitand MCPcora.review_diffstill need a key.
-
--helpno longer prints secrets from the environment. clap shows[env: NAME=value]for options bound to an env var, socora --helpwithCORA_API_KEYorGITHUB_TOKENset printed the key (into logs, CI output, screenshots).--api-keyand--tokennow 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_filesnow 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_prepareno longer follow symlinks, and enclosing-scope reads stay inside the project (a hostile repo'sid -> ~/.ssh/id_rsaor a+++ b/../../xheader could put outside files into a model prompt). -
cora install --removekeeps a customized skill unless--force; agent-mode tools reportdropped_by_validation, accept any severity case, refuse ascan_finalizewhosefiles_fingerprintno longer matches, and computeshould_blockand the quality gate on all findings (the caller'smin_severityonly trims the list).admitalso fixes lines for deleted files and the old path of a renamed file.--no-llmandreview_finalizeskip building the LLM prompt (no clippy run). -
Bumped
rustls0.23.45 → 0.23.46 andquinn-proto0.11.16 → 0.11.19 (RUSTSEC-2026-0338/0339/0340/0341/0342 and RUSTSEC-2026-0346/0347/0348/0349), plus the yankedchacha200.10.1 → 0.10.2. Lockfile-only patch bumps; this was failing theSecurity Audit (CVE)job on every PR. -
A judge's findings are untrusted input, bounded by one gate (#644).
judge::admitclearsrule_id(only deterministic scanners may carry one; it feeds suppression and the finding cap), truncatestitle/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 reviewandcora scanboth apply it; deterministic findings are merged afterwards, so a judge can add findings but never remove one.cora scannow 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_severityand deterministic rules are applied on every run, including cache hits. Existing cache entries are simply not hit again.
-
Help points to agent mode.
cora --help,cora review/scan --help,cora mcp --helpandcora install --helpnow mention--no-llm, the agent-as-reviewer MCP tools, the/cora-reviewskill andclaude mcp add cora -- cora mcp. -
Rules are test-code aware (#663). Whole test files (
*_test.*,*.test.ts,spec/,fixtures/, ... not justtests/) and Rust#[cfg(test)]modules /#[test]functions (also a diff hunk insidemod tests) are exempt frombug-unwrap,bug-expect,bug-println,qual-clone,qual-error-ignore,sec-hardcoded-urlandsec-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,evaland 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-printlnis also skipped inmain.rs,src/bin/,examples/andbenches/. New settingrules_engine.include_tests: trueturns the exemptions off. Onuteke-core(Rust) the findings drop from 2400 to 235 and the 6 Critical ones disappear; onuteke-cloudthe 3 Major URL findings are gone. New moduleengine::test_context. -
rules_engine.max_findingsnow 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. Setmax_findings: 0(ornull) for no cap. -
Review is now prepare / judge / finalize with the LLM as the seam (#649).
review::prepareturns a diff into ajudge::Packet(judge-visible text, assembled context, system prompt override,valid_files, diff chunks, deterministic findings); ajudge::Judgereturns findings (HttpJudge= today's LLM call,NoJudge= deterministic only);review::review_withmerges, guards file paths, post-processes and decidesshould_block. Output ofcora reviewis unchanged. The review pipeline now has an end-to-end test with a fake judge (no network), and thellmmodule no longer prints: spinners moved toprogress::Spinnerandllm::run_review_prompts/llm::scan_filestake an events observer (the oldllm::review_diff/llm::review_diff_streamare replaced byrun_review_prompts). -
One
finalizemodule 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 ofcora review(single and chunked) andcora scanis unchanged. Fixed along the way: the MCPcora.review_difftool now honors itsmin_severityparameter and evaluates the quality gate on the reported findings;cora commitnow records its review in the review history (command = "commit").
-
crypto/hardcoded-secret/sec-hardcoded-secretno longer flagname: valueentries 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_PWand"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-concatandsec-sql-concatno 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_preparelisted every deterministic finding butcora.review_finalizeshowed onlyrules_engine.max_findings(default 5) of them with no explanation; found by running a real agent on a real project.review_preparenow reportsdeterministic_findings_totaland the cap, andreview_finalize/scan_finalize/cora.review_diffreportnot_shown(with a note). -
The MCP
cora.review_difftool 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 throughblock_in_place. -
cora scannow runs the rules engine (#645). Built-in rules (bug-todo,sec-sql-concat, ...) and customrules_engine.customrules were only run bycora review; scan silently skipped them. Expect more findings fromcora scanafter upgrading (e.g.bug-todoon every TODO), still under therules_engine.max_findingscap (default 5);rules_engine.enabled: falseturns them off. One set of deterministic families (deterministic::run_filesfor whole files,deterministic::runfor diffs) now serves both, and the MCPcora.check_snippettool picks up rules too. Scan also has a judge seam (judge::ScanJudge, withHttpScanJudgeandNoJudge) so the batch loop is tested without a network, andDEFAULT_BATCH_TOKEN_BUDGETis renamedDEFAULT_BATCH_CHAR_BUDGET(it was always a character count).docs/cli-reference.mdnow states which review flags scan does not have.
0.18.0 - 2026-10-09
- Release checksums are signed with cosign keyless and verified by
install.shandcora upgrade(#591).release.ymlnow signschecksums-sha256.txt(which covers every archive) via Sigstore/GitHub OIDC before creating the release and uploadschecksums-sha256.txt.sigstore.json; a signing failure aborts the job so no unsigned release is published.install.shandcora upgradeverify the bundle withcosign verify-blob(identity: this repo'srelease.ymlon av*tag, issuertoken.actions.githubusercontent.com) whencosignis onPATH; an invalid signature aborts. Withoutcosign, or for releases without a bundle, they print a notice and fall back to checksum-only.CORA_REQUIRE_SIGNATURE=1makes a missingcosignor bundle fatal. Seedocs/installation.md.
-
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 forsecrets_scanner::scan_secrets_in,security_scanner::scan_security_in,rules::run_rules_inandinline_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::applykeep their signatures. Thefiles_as_chunksworkaround 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.
-
A
rules_engine:section withoutenabled: trueno longer disables the built-in rules.enablednow defaults totruewhen the section is present, sorules_engine: { max_findings: 0 }only changes the cap; setenabled: falseto turn the engine off (#638). -
cora.check_snippet(MCP) now honorsignore.rules, inlinecora-ignore:markers and the false-positive filters, andrules_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 thatcora scansuppresses. It now goes through the shared post-processing pipeline as a syntheticsnippet.<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, Pythonstr("..")(plain calls such asget_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", Rustenv::var("X").unwrap_or("lit".into())/unwrap_or_else(|_| "lit".to_string()), RubyENV.fetch, PHPgetenv() ?:; 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: Secret1Typein.ts/.py), YAML flow-style{ password: hunter2hunter2 }is flagged, unspacedpassword:=os.Getenv("X")is no longer matched, and SQLPASSWORD "..."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_findingsis applied after suppression and truncation is reported (#624). The cap (default 5) used to be applied inside each scanner, beforeignore.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 scanandcora reviewprint⚠ N more deterministic findings not shown (...)on stderr when findings are cut, and thecora scansummary mentions it; stdout formats are unchanged.max_findings: 0ornullnow means unlimited (previously0hid every finding).
0.17.2 - 2026-10-09
- 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
- Hardcoded secrets in Go short declarations, YAML/JSON keys and SQL
PASSWORDliterals are now detected. Gopassword := "..."(with or without a trailing comment) andvar apiKey string = "..."(#618); unquoted YAMLpassword: hunter2hunter2and JSON quoted keys"password": "..."/"apiKey": "..."(#619); SQLCREATE USER app WITH PASSWORD '<literal>'andIDENTIFIED 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.
-
One shared post-processing module for
cora reviewandcora scan. The finding filter pipeline (LLM secret false-positive cross-check, Markdown code blocks,ignore.rules, inlinecora-ignore:, context-line filter, scanner merge for scans) now lives inengine::postprocess, so a fix lands in one place. Internal refactor with no CLI or output change (#610). -
ReviewIssue::newconstructor and table-driven hardcoded-secret tests.ReviewIssueis now built throughReviewIssue::new(..)pluswith_*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
- 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).
- Real secrets in typed declarations were missed.
const password: string = "..."(TypeScript),password: str = "..."(Python) andlet password: &str = "..."(Rust) matched none of the secret patterns (they required the value right afterpassword =/password:), andcora reviewalso 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. Thebare identifiercheck in the object-shorthand filter, which was always true, now rejects values containing a string literal (#607).
- Real secrets were missed when the line had a trailing comment containing
:. Thehardcoded-secretfalse-positive filter treated the first:anywhere (even inside a comment or string) as object shorthand, sopassword = "..."; // note: fix laterwas not flagged, and no scanner finding existed on lines carrying acora-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).
- 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, asrule_idin JSON (omitted when absent, so older JSON still deserializes), and asproperties.coraRuleIdin SARIF (#597).
cora-ignore:andignore.rulesmatch rule ids.cora-ignore: sec-hardcoded-secretsuppresses by id (exact, case-insensitive) as well as title;ignore.rulesadditionally 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
cora scanno 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 appliesignore.rulesand inlinecora-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
- 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 barecora-ignorewithout rules suppresses nothing. Coexists withignore.rules/ignore.files(#554).
- Project
.cora.yamlcan no longer redirect your API key.provider.base_urlfrom a discovered project config is ignored unlessCORA_TRUST_PROJECT_CONFIG=1;base_urlmust behttps(plainhttponly for loopback); LLM error bodies echoed to the terminal are length-capped (#563). - Hardened
cora upgradeandinstall.sh. Checksums are matched by exact filename; downloads go to a random 0700 temp dir; only the singlecorabinary 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_CHECKSUMnow also requiresCORA_UPGRADE_I_UNDERSTAND=1.install.shfails closed when the checksums file or entry is missing (opt out withCORA_SKIP_CHECKSUM=1) and falls back toshasum -a 256(#572). install.shrequests now have timeouts and size caps. Everycurlcall sets--connect-timeout,--max-timeand--max-filesize(256 MiB archive, 1 MiB checksums/API JSON), matchingcora 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
EXAMPLEare ignored) (#573).
cora watchreindexed every source file on each cycle. Change detection now reindexes only files that changed, and--filterrestricts 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 --severitynever 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 affectednever matched naming-convention tests. The CLI took the file extension (rs) as the file stem. CLI and MCP now share one query, with escapedLIKEwildcards, batched queries and one deduplicated pattern list ({stem}_test,test_{stem},{stem}.test,{stem}.spec,tests/{stem},__tests__/{stem}). MCP dead-code now honorsanalysis.entry_point_patternslike the CLI (#575).cora --config <file> serveignored--config. The global option is now passed through (#576).- MCP
brain_searchignored 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.dbwhen 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/tracematch names exactly instead of by substring (runno longer matchesrerun), 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
-32700parse error; notifications get no response andnotifications/cancelledno longer stops the server;tools/callwithout a name returns-32602;limit/depth/min_linesare clamped;cora.installrequiresconfirm: 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);
rustlspinned to 0.23.45 for RUSTSEC-2026-0285 and a deprecatedf32import removed so clippy passes on rustc 1.99 (#561).
- Ignore patterns now use a single matcher for the index walk, review scanners,
cora scan --include/--excludeandwatch --filter. Behavior changes you may notice inignore.filesand related options:**/*.test.tsno longer matchesfootest.ts, andvite.config.*no longer matchesvite.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, incora scan --include/--excludeandwatch --filtertoo. - An invalid glob matches literally instead of being dropped (#577).
cora index --watchnow runs the same watcher ascora watch. It checks every 500 ms (was 2 s), skips hidden directories, and printsReindexed: ...(#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 oneTransportseam (#582), and review-history SQL owned by a singlereview_storemodule (#585).
0.15.0 - 2026-08-31
- Opt-in
vecqvector store for Brain Mode. Setbrain.vector_store: vecqin.cora.yamlto 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 andcora indexno longer re-embeds unchanged projects (#542, #547). brain.vector_bitsquantization-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 nextcora indexinstead of silently serving the old width; unknown values fall back toresidual.
- Vector signal never fired in a fresh process.
cora brainand MCPbrain_searchonly 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.
- 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
.vecqfiles carry no key table and rebuild once with a warning, then upgrade to the keyed format.
0.14.0 - 2026-08-28
-
Empty LLM responses from reasoning models. Models like GLM can spend the entire
max_tokensbudget on chain-of-thought and returncontent: ""withfinish_reason: "length", which previously surfaced as a misleadingEOF while parsingerror. Cora now readsfinish_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
implblocks 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 indexinside a workspace member crate created a separate project row from the one MCP resolved, soindex_statusreported 0 symbols despite a populated DB. Root resolution now prefers a[workspace]Cargo.toml and never climbs past a.gitboundary (#522). -
ignore.fileswas 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).
- Default
max_tokensraised from 4096 to 8192 to give reasoning models headroom above their chain-of-thought (#536).
- Default
max_tokensraised 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/exportitems) — new--include-pubflag and MCPinclude_pub_apiparameter 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_statuscarries 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.
- Runtime embedding backend selection. Brain Mode now reads
brain.embeddingfrom.cora.yamlto 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 anembed_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 TEXTcolumn to thesymbolstable for incremental embedding tracking. Auto-migrates on first run; existing indexes are upgraded transparently. Backendenum +resolve_backend()inembedmodule. Clean runtime dispatch withOnceLockcaching, graceful fallback when a requested backend is not compiled, andactive_dims()/active_provider_name()helpers.BrainConfig+BrainEmbeddingModein config schema. Newbrainsection in.cora.yamlwithembeddingfield. IncludesDisplay,FromStr, andserdeimpls for CLI and YAML ergonomics.
embed_code_dispatch()now checksACTIVE_BACKENDat runtime. Previously selected via#[cfg]at compile time only. Falls back to compile-time default ifresolve_backend()was never called (lazy resolution).cora index,cora brain,cora watchall resolve embedding backend on startup. Each command loads.cora.yaml, readsbrain.embedding, and callsresolve_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).
- FTS5 returning 0 results for camelCase queries (#451). Added
filecolumn to FTS5 virtual table (schema v6),split_camel_case()identifier decomposition, and OR query expansion. Searches likefindUsernow correctly match viafind OR user. - Dead-code false positives on framework entry points (#452). Added
FRAMEWORK_ENTRY_PREFIXESwith 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: keepin their body are excluded from dead-code detection. - Sticky skip files on config change (#453). Added
index_config_hashcolumn to projects table — when.cora.yamlchanges, previously skipped files are re-evaluated instead of permanently skipped.
entry_point_patternsconfig field — New field inAnalysisConfigand.cora.yamlanalysissection. Custom list of glob patterns for framework-specific entry points beyond built-in defaults.- Schema migration v6 — Auto-migration: adds
index_config_hashto projects, drops and recreates FTS5 withfilecolumn, rebuilds search index. index_project_with_skip()— Indexing now respectsindex_skip_filesfrom config, skipping non-code files during symbol extraction.split_camel_case()— DecomposescamelCaseIdentifiersinto 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.
should_skip_file()rewritten — Simplified glob matching with early exit for non-glob patterns, explicit**/namebranch 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).
- Index scanner false positives on entry-point files. Added
index_skip_filesglob patterns toRulesConfig. 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 scannow includes index findings. Fixed bug wherecora scandid not wire index scanners (unused imports, dead code) — the scan command now runsscan_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.
index_skip_filesconfig field — New field inRulesConfigand.cora.yamlrules_enginesection. 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.
- Index-powered unused import detection.
cora reviewnow 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), enablingcora queryto 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.
- 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")], Expressapp.get(), Gohttp.HandleFunc().
- Caller resolution — Index-aware
resolve_callers()uses graph query first, regex fallback only when no index. - Pre-commit hook — Auto-runs
cora index --quietbefore review for persistent local index. - Context enrichment — Impact analysis, affected tests, and brain search injected into LLM review prompt.
- Ruby AST extraction — Fixed
body_statementwrapper bug in class/module method extraction.
- Resolved
test_extract_ruby— tree-sitter Ruby method extraction now correctly handlesbody_statementintermediate nodes. - CI 10/10 green — All checks pass including format, clippy, test, build, security audit.
- Dead code detection.
cora dead-codefinds 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 installdetects installed AI coding agents (Cline, Cursor, Windsurf, etc.) and configures Cora as their MCP server. One command setup. - Background reindex on serve.
cora servenow auto-reindexes the current project before starting the MCP server — always up-to-date symbols. - Tree-sitter is now a default feature. The
edgestable (IMPORTS, IMPLEMENTS, INHERITS, CHILD_OF) now populates correctly in all builds, including release binaries.
cora dead-codeCLI command (#427). Detect dead functions/methods with--include-tests,--min-lines, and--jsonflags.cora.dead_codeMCP tool (#428). Same dead code detection, accessible via Model Context Protocol.cora queryCLI command (#435). Simple graph traversal DSL:"symbol -> *"(callees),"* -> symbol"(callers),"SymbolName"(symbol lookup).cora.queryMCP tool (#435). Same query DSL via MCP.cora installCLI command (#431). Auto-detect 40+ AI coding agents and configure Cora MCP with one command. Supports--list,--dry-run,--agents,--force.cora.installMCP tool (#431). Same install detection via MCP.cora servewith auto-reindex (#434).cora serveruns incremental reindex on startup before launching MCP server.- Agent config module (#432). Read/write support for JSON, JSONC, and YAML agent configuration files.
- 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.
edgestable 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
- 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 findingsCLI. Track, filter, dismiss, and reopen findings across all your reviews.
- Persist review & scan findings to
cora.db(#397, #398).cora reviewandcora scannow 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
resolvedwith anauto_resolvedevent. Findings that reappear stayopen. cora findingsCLI command (#400). New subcommand with four actions:cora findings list— show open findings (use--all,--severity,--file,--jsonfor filtering)cora findings stats— summary counts with resolution rate (--jsonsupported)cora findings dismiss <id>— mark as won't-fix with optional--reasoncora 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 --rebuildflag. 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=-64MBfor 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 singleBEGIN 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:
--rebuildavailable for full re-validation.
cora debtreads fromcora.dbas primary source (#403). File snapshots are now fallback only. DB is the single source of truth for tech debt reports.graph.dbrenamed tocora.db(#395). Auto-migrates existinggraph.dbon first run.db_writermodule now exposesopen_db_for_read(),open_db_for_write(), andcompute_fingerprint_pub()for use by the findings CLI.
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× |
cora brainvector 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/.gitinstead of always using CWD.
0.8.3 - 2026-07-27
- Svelte AST symbol indexing.
cora indexwith--features tree-sitternow extracts functions, arrow functions, and types from<script>blocks in.sveltefiles. Uses TypeScript/JavaScript grammar delegation — zero new dependencies. Supportslang="ts"andlang="js"attributes with correct line number offsets. Closes #384. - TypeScript arrow function extraction.
export const handler = () => {}andconst cb = function() {}are now captured as symbols with call edges. Previously only ALL_CAPS constants were indexed fromlexical_declaration/variable_declarationnodes.
- Project root detection for scoped queries (#380, #382).
resolve_project_root()now walks up from CWD to find.cora.yaml,Cargo.toml, or.gitinstead of always using CWD. Fixescora brainandcora callersreturning 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
- 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 indexnow extracts classes, mixins, enums, extensions, functions, getters, and typedefs from.dartfiles. 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
- crates.io publish (503 transient on v0.8.0)
0.8.0 - 2026-07-24
- 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
edgestable storing caller→callee relationships. Whencora indexruns with--features tree-sitter, it extracts function calls from AST nodes, enablingcora traceandcora arch. cora traceandcora archcommands. 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 nowcora, crate iscora-code.
- Phase 3 — Brain Mode (#362)
CodeVectorIndex— persistent usearch HNSW vector index with fs2 file locking, key↔symbol mapping, and disk serializationbrain_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_atcolumns onprojectstable - 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 traceandcora 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:
edgestable (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.dbto~/.codecora/cora-code/graph.db CODECORA_HOMEenv var override for custom data directory
- Symbol database moved from
- Binary rename (#338)
- Crate renamed
cora-cli→cora-code - Binary name:
cora
- Crate renamed
- 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)
- False positives suppressed in
sec-hardcoded-urlandcrypto/hardcoded-secretrules (#369, closes #357, #364)post_match_filter()added tobuiltin.rs— filters matches in XML/SVGxmlnsattributes, Rust docstrings, config files, and bare identifiers- Integrated into
security_scanner.rsscan loop — all security scanner matches now pass throughpost_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, ...) → idiomaticis_some_and(...)` (#369)
- 56 files changed, +46,948 / -2,050 lines since v0.7.0
- 11 PRs merged
0.7.0 - 2026-07-16
- 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 pushin a fencedbashblock 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).
- 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(defaulttrue); uses gitignore-aware walking and is bounded (≤400 files, ≤3 call-sites/symbol), injecting only the call line + 1 line of context. NewContextPriority::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. Rustmod foo;and Javaimport 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, andprovider.base_urlscheme. Multiple errors are aggregated into one message.Profile::validate()(#81) — focusweightmust be 1–10, andaction/tone/detail_levelmust be recognized values.deny_unknown_fieldson all config sections (#80) — misspelled YAML keys are rejected at parse time.
CategoryActionenum (#57) —quality_gate.categories.*.actionis now a case-insensitive enum (block/warn/ignore); a typo likeblokfails loudly at config load instead of silently becoming blocking.- Disabled quality gate never fails (#58) —
evaluate()forcesPasswhenenabled: false. context_chain.max_context_tokensdefault raised 3000 → 5000.issue_typeserializes consistently asissue_type(#48);typeretained as a deserialize alias.Severity::from_str_lossyuseseq_ignore_ascii_case(no allocation) (#10).
- Markdown fenced-code-block false positives (#329) — findings inside fenced code blocks (triple-backtick / triple-tilde) in
.md/.mdx/.markdownfiles 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.rspassedignore.rules(finding-type strings) instead ofignore.files(target/**,node_modules/**); the resolver could inject build-artifact code. Now usesignore.files. - Test-file detection over-match (#87) —
is_test_fileis path-segment aware;latest,aspect,attestationare 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_statsqueriesPRAGMA page_sizeinstead of assuming 4096 bytes. - Project-sync workflow — a merged PR referencing issues via
Refs #N(notCloses #N) no longer fails thesynccheck. - 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
anyhow1.0.102 → 1.0.103 (RUSTSEC-2026-0190) andcrossbeam-epoch0.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_chainmerge, and hook-install composition.
0.6.2 - 2026-06-21
-
tokens_usedis no longer alwaysNonein review and scan responses.parse_review_responseandparse_scan_responsepreviously discarded theusageobject returned by the LLM API, hardcodingOk((..., None)). Token counts and cost estimates were silently dropped.chat_completionnow returns(content, Option<Usage>)and the parse functions threadusagethrough asTokenUsage. All call sites updated.ReviewResponse.tokens_usedandScanResponse.tokens_usednow report real values when the provider supplies them.
-
cora review --streamnow collects token usage.- The streaming path (
chat_completion_stream) previously only accumulateddelta.contentand ignored theusagefield. It now sendsstream_options: { include_usage: true }and parsesusagefrom the final SSE chunk (top-level or nested inchoices[0].delta.usage). - Token counts are now reported correctly for both streaming and non-streaming review.
- The streaming path (
-
cora scanmulti-batch token accumulation.- When scanning multiple batches,
total_tokenswas 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, andestimated_cost_usdare summed).
- When scanning multiple batches,
- Extracted magic numbers into named constants.
scan.rs: the hardcoded batch size fallback20and token budget60_000are nowDEFAULT_MAX_FILES_PER_BATCHandDEFAULT_BATCH_TOKEN_BUDGET.
Usagestruct now accepts camelCase aliases.- Some providers (e.g. Azure OpenAI, certain third-party gateways) return
promptTokens/completionTokens/totalTokensinstead of snake_case. Both forms are now accepted via#[serde(alias = ...)].
- Some providers (e.g. Azure OpenAI, certain third-party gateways) return
- 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
cora scanno 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-errorto 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.
cora reviewno longer exits 2 when severity filtering removes all blocking findings (#312)- Recompute
should_blockagainst the filtered issue list (after--severityfiltering) 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 hookblockvs non-blockmodes. - Applies to both the single-chunk and auto-chunked (
--auto-chunk) review paths.
- Recompute
- macOS installer now strips Gatekeeper quarantine attributes (#313)
- Prebuilt macOS binaries (
aarch64-apple-darwin) are not Apple-notarized. When downloaded directly, macOS attachescom.apple.quarantine/com.apple.provenancexattrs and kills the binary withKilled: 9on first launch. install.shnow runsxattr -drfor 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 manualxattrworkaround for users who download the binary directly, and thecargo/ Homebrew alternatives.
- Prebuilt macOS binaries (
- 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 --versioncheck snippet and guidance for removing stale copies when more than onecorais onPATH(e.g.~/.local/binvs~/.cargo/binvs npm global). - Cross-links the original issue for background.
- Recommends a single install method per platform and lists the supported channels (installer script,
0.6.0 - 2026-06-14
-
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,--watchflags- 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
-
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
- Cora + Uteke bundle installer (#235)
install-bundle.sh— single command installs both tools- Cross-referencing documentation across all docs
- 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":[]}
cora commit— review staged diff + generate commit message + commit (#262)- HITL mode (default): interactive
[Y]es / [E]dit / [N]oprompt - 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()inengine/llm.rs- 22 unit tests
- HITL mode (default): interactive
- 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
- 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,ignoreactions - Terminal-formatted gate output with status table
- Exit code 2 on gate failure
- 12 unit tests covering all gate scenarios
- Global thresholds:
- 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 mcpsubcommand- Brace-depth stdin parsing (handles pretty-printed JSON)
- 17 unit tests
- Auto-chunking — large diffs split into reviewable chunks automatically (#188)
--no-auto-chunkflag to disablesrc/engine/chunker.rsmodule (~310 lines)
- Tech debt metrics — cumulative review history and trend tracking (#206)
DebtSnapshotper-review JSON snapshots with quality score (0-10)cora debtsubcommand — terminal table,--json,--trendASCII graph,--since,--branchfilters- 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)
--memoryflag — recall context from Uteke before review--learnflag — recall + save findings after reviewMemoryBackendwith 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
- 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.devinstead of relative file paths - CI workflows — removed stale SvelteKit
website/jobs, replaced with VitePressdocs/build merge_into()returnsResult— fail-fast on invalid profile config instead of silently continuing- Language context reuses parsed diff —
build_language_context_from_chunks()eliminates redundantparse_diff()call - 13 stale issues closed — migration epics, website tasks, v0.4 leftovers
- 15 stale branches deleted — cleanup after merge
- 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 -eguard prevents silent HTML downloads - Checksum enforcement — hard fail on missing/invalid checksums (was warning-only)
- Exact checksum match —
awkexact filename lookup replacesgrepsubstring match
- SvelteKit
website/— 6,286 lines removed, replaced by VitePressdocs/ Website LintCI 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
- 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
- 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
- Masked output:
- Diff parser hardening — hunk line count validation, broader binary detection (GIT binary patch, singular form), graceful truncated diff handling (#195 Phase 1)
.agent.mdrelease checklist — pre-release checklist to prevent docs drift between versions
cora config show --global/--projectdocumented in cli-reference.md (was missing)cora auth loginpath corrected fromconfig.tomltoauth.tomlin cli-reference.md- CI example in docs now includes CORA_BASE_URL and CORA_MODEL secrets
0.4.5 - 2026-06-07
- Config architecture redesign — clear separation of concerns between config files (#209)
~/.cora/auth.tomlnow stores only the API key (secret)~/.cora/config.yamlstores provider, model, base_url, and other settings (global).cora.yaml(project) overrides global config per-projectCORA_API_KEYenv var reserved for CI use only
- Provider info auto-migration — if
auth.tomlstill contains provider/model/base_url, automatically moved toconfig.yamlon first run - Deterministic rules —
rules/added to default exclude paths, preventing rules from matching their own source definitions (#185)
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.yamlcontentscora config show --project— new flag to show only.cora.yamlcontents (mutually exclusive with--global)cora reviewsends to wrong provider — provider info fromauth.toml/config.yamlwas ignored at runtime, always defaulting to OpenAI. Now correctly reads from merged config (#209)save_provider_infodata loss — parse failure onconfig.yamlno longer silently replaces the entire file with defaults (now returns error)cora auth logininteractive flow — now auto-detects provider env vars (e.g. pick ZAI → detectsZAI_API_KEY), suggests model and base URL defaults from presets (enter to accept) (#203)cora auth login --provider zai— now auto-detectsZAI_API_KEYfrom environment, no need for--api-keyflag (#184)- Env var override visibility —
cora config shownow 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)
--global/--projectflags oncora config showfor scoped config inspection- Clap
conflicts_withon--global/--project—cora config show --global --projectnow 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
- Spinner auto-hides in non-TTY —
indicatifprogress spinners inllm.rsandscanner.rsnow 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)
--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
- Provider shortcut now resolves preset defaults — bare
provider: zaiin.cora.yamlauto-fillsbase_urlandmodelfrom the preset table (#183) - Env var override warnings —
CORA_PROVIDER,CORA_MODEL,CORA_BASE_URLnow warn when they override config file settings (#182) config showdisplays effective (resolved) config — shows actual runtime values with[from: env ...]annotations when env vars override config (#189)- Auth file permissions auto-fix —
~/.cora/auth.tomlpermissions auto-corrected to 600 instead of just warning (#187) - Deterministic rules exclude own source files — security rules no longer match against
rules/andtests/directories, eliminating false positives (#185)
- Non-interactive
cora auth login—--provider,--api-key,--model,--base-url,--forceflags for scriptable setup (#184)
0.4.2 - 2026-06-06
- Cora Review now works on fork PRs — changed trigger from
pull_requesttopull_request_targetsoGITHUB_TOKENhas write access for PR comments on external contributor PRs. Explicitly checks out PR head SHA for correct diff (#178 context)
- Top-level provider shortcuts in
.cora.yaml—model:,base_url:, and bareprovider:string now accepted at top level without needing nestedprovider:section (#178, closes #176)
0.4.1 - 2026-06-06
- Regex panic on optional hunk groups — bare hunk headers like
@@ -1 +1 @@(without,count) causedcaps[4]index-out-of-bounds panic. Now usescaps.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)
cora initnow installs pre-commit hook — automatically creates.git/hooks/pre-commitalongside.cora.yaml. Use--no-hookto 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.yamlfrom repo when present, falls back to 5MB limit when absent. Removes hardcodedmax_diff_size: 200000(#172) on_violationconfig +--cimode — hard gate for CI:on_violation: disallowmakes cora exit non-zero on any finding.--ciflag enables strict non-interactive mode (#152)cora hook install/uninstall— explicit hook management commands (previously only viacora init)
- CI action reads
.cora.yaml— project config takes precedence over hardcoded fallback.max_diff_size,hook.mode,llm.timeoutall respected in CI (#172)
0.4.0 - 2026-06-03
- 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!/unwrapin 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/DiffLinewith 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_languagein.cora.yamlContextConfig—enabled,max_context_tokens,follow_depth,max_symbolsin.cora.yamlreview section- Default SARIF upload to GitHub Code Scanning ON — opt-out with
upload-sarif: false(#148) - SARIF tool branding —
CodeCoradriver name (codecoradev/cora-code) in SARIF output (#148)
- 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
- Static analysis context injection — optional clippy output injected into review prompt to reduce false positives on verified-intentional changes (#140)
review.static_analysis.auto_clippyconfig — automatically runcargo clippyand filter output to changed filesreview.static_analysis.clippy_output_fileconfig — read pre-computed clippy output from filecora config validatesubcommand — validate.cora.yamlconfiguration file and report issues (#88)CoraErrorenum via thiserror — structured error types for engine layer with 17 variants (#86)
- Engine layer migrated from
anyhowtothiserror— structured error handling in engine,anyhowretained in CLI layer (#86) - All clippy pedantic warnings resolved — 175 → 0 warnings across entire codebase (#84)
- Repo URLs updated to
codecoradev/cora-codeorg (#137) - CI actions bumped —
upload-artifact@v7, Node 24 strict mode (FORCE_JAVASCRIPT_ACTIONS_TO_NODE24) (#142)
- CI Cora Review fails on LLM API errors — removed
|| truesuppression, added exit code + empty SARIF check (#142) - Match arm merge in
IssueType::from_str— clarified documentation (#141)
0.2.0 - 2026-06-02
--progressflag — NDJSON progress events to stderr for structured CI/GUI consumers (Termul prerequisite) (#108)--max-diff-sizeflag — overridehook.max_diff_sizefor 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 auditviarustsec/audit-checkfor dependency CVE scanning (#85)
- Naive .gitignore parser →
ignorecrate — ripgrep-grade correctness with nested .gitignore, global gitignore, and.git/info/excludesupport (#80) - Blanket
#![allow(dead_code)]removed — targeted cleanup, 27 warnings → 0 (#79)
REQUESTS_CA_BUNDLEenv 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_CONFIGenv var with temp config for 200K char limit in CI action (#112)
0.1.8 - 2026-06-02
unwrap()→expect()in ProgressStyle templates (llm.rs, scanner.rs) — clearer panic messages on template parse failure (#87)- Consolidated duplicate
impl Severityblocks into single implementation (#83) file_content_hashreturnsOption<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.tomlhas 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)
checksums-sha256.txtin release artifacts — release workflow generates SHA-256 checksums for all platform binaries (#109)
- Official CodeCora branding assets — logo, favicon, and OG image updated from ajianaz/cora SaaS repo (#110)
- Standalone
cora-review.ymlworkflow — CI action extracted from inlineci.ymljob 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,
greppipefail fix, empty file guard, Node 24 strict mode compatibility (#107)
0.1.7 - 2026-06-01
- Diff-hash caching — review results cached by SHA-256 of diff + model + temperature in
~/.cache/cora/reviews/. Cache TTL configurable viallm.cache_ttl(#100) --no-cacheflag — 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)
- 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.yamlnow 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-reviewandcora-review-simpleactions (#102)
0.1.6 - 2026-06-01
- Custom system prompts via config —
review.system_prompt,review.system_prompt_file,scan.system_prompt,scan.system_prompt_filefields in.cora.yaml(#94) response_formatconfig — opt-injson_objectresponse format for providers that support it, viareview.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)
- 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
- Critical: JSON repair corrupts valid unicode escapes —
is_valid_json_escape()missing'u', causing\uXXXXto be double-escaped. Now properly validates and handles incomplete\usequences (#89) - Critical: TOML injection in
save_api_key()— API key written viaformat!string interpolation. Now usestoml::Tableserialization (#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_keyparameter — removed fromload_config()signature (#75)
save_api_key()now usestoml::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
- 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
- Silent false-negative — cora JSON parse failure previously posted "No issues found" without actual review (LLM invalid escapes)
- Hardcoded Infisical
identity-idinrelease.ymlanddeploy-website.yml— migrated tosecrets.INFISICAL_IDENTITY_ID - Release workflow changelog extraction —
vprefix mismatch (tagv0.1.3vs CHANGELOG[0.1.3]) now properly stripped printfdouble-escape in release workflow —\\ncorrected to\n- Stale
v0.1.2binary download filenames in README - Clippy
unnecessary_map_orlint —.map_or(false, |s| s.success())replaced with.is_ok_and(|s| s.success())
- All 3 workflows use
secrets.INFISICAL_IDENTITY_ID(consistent withci.ymlpattern) - Release workflow validates semver format before sed injection
- Branch cleanup — removed 14 stale branches
0.1.3 - 2026-06-01
cora config set --global— write config to~/.cora/config.yamlinstead of project.cora.yamlcora 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.tomlto new YAML +auth.tomlsplit
cora config setnow writes YAML instead of TOML (compatible with config loader)- API key storage moved from
~/.cora/config.tomlto~/.cora/auth.toml(0600 permissions) - YAML serialization uses
skip_serializing_if— no morenullvalues in output
- Severity comparison inverted —
Criticalissues no longer silently passshould_blockcheck (Ord ordering bug) - Hook
mode: blockno 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
cora init— create.cora.yamlconfig file with provider/model selectioncora hook install|uninstall— pre-commit hook managementcora 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 --incrementalwith SHA256 content hash cache for fast incremental scanningcora review --uploadfor direct SARIF upload to GitHub Code Scanningcora review --streamfor real-time review outputcora review --unpushedfor reviewing unpushed commitscora review --base <branch>for branch comparisoncora review --diff-file <path>for reviewing external diff filescora providerscommand to list available LLM providerscora auth loginfor interactive API key storage
- SARIF schema compliance for GitHub Code Scanning upload
- Clippy
format_in_format_argswarnings - Replaced deprecated
serde_yamlwithserde_yaml_ng - Normalized release binary naming (
cora-{arch}-{target}-v{version}.tar.gz)
- Replaced deprecated dependencies
- Removed unused dependencies
- Bumped minimum Rust version to 1.85
0.1.1 - 2025-05-27
- Replaced ASCII art banner with eye icon in README
- Updated README branding to cora-code
- CI
cargo publishwith--allow-dirtyfor Cargo.lock mismatch on tag checkout
0.1.0 - 2025-05-25
- 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 installfor automatic review on every commit - SARIF Output —
--format sariffor GitHub Code Scanning integration - 4 Output Formats — pretty (colored), compact, JSON, SARIF
- Project Config —
.cora.yamlper-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,criticalwith 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