Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,35 @@
# Changelog

## [0.9.1] — 08-12-2026

Config is not code, and reviewing it as though it were produced confident
nonsense. Reported from the field: every file under `.claude/` came back HIGH.

### Fixed

- **Coding-agent and editor config is no longer reviewed.** `.claude/`,
`.cursor/`, `.windsurf/`, `.aider/`, `.vscode/`, `.idea/`, `.zed/` and
`.fleet/` are dropped by the pre-filter. These files are imperative English
about credentials, shell commands and permissions — precisely the shape a
reviewer prompt primed for "exposed secrets, disabled auth" reads as an
emergency. `.claude/` is also instructions written *for* a model, which is a
poor thing to hand to one.
- **Ignore files and formatter config are dropped**: `.gitignore`,
`.dockerignore`, `.prettierignore` and the rest of the `.*ignore` family,
plus `.editorconfig`, `.gitattributes`, `.prettierrc*`, `.eslintrc*`,
`.npmrc`, `.nvmrc`, `.cursorrules`. Declarative lists with no program logic
in them. `eslint.config.js` and friends are still reviewed — those are real
JavaScript, and a bug in one is a bug.
- **Documentation is dropped by default** (`.md`, `.mdx`, `.rst`, `.txt`,
`.adoc`, `.org`, `.tex`). Re-enable with `--include-docs` or
`review.include_docs = true` for docs that carry API contracts. The switch
covers prose only — agent config stays out either way.

Everything dropped is *counted and reported* in the existing summary line
("312 hunks → 74 reviewable"), under the new `docs` and `tool config` reasons,
rather than silently disappearing. Test files are unaffected and still
reviewed: a test that asserts nothing is worth catching.

## [0.9.0] — 08-12-2026

diffmind was built as a gate: run it, get a verdict, pass or fail. This release
Expand Down
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Diffmind — a code review gate you can actually keep
# Diffmind — local-first AI code review, in your terminal

[![CI](https://github.com/thinkgrid-labs/diffmind/actions/workflows/ci.yml/badge.svg)](https://github.com/thinkgrid-labs/diffmind/actions/workflows/ci.yml)
[![Latest Release](https://img.shields.io/github/v/release/thinkgrid-labs/diffmind)](https://github.com/thinkgrid-labs/diffmind/releases/latest)
Expand Down Expand Up @@ -552,12 +552,16 @@ work to it.
1. **Parse** — the diff is turned into per-file hunks with correct before and
after line numbers.
2. **Filter** — lockfiles, `linguist-generated` paths, `@generated` banners,
minified bundles, assets, snapshots, your `ignore` globs and whitespace-only
hunks are dropped. This is free, usually removes most of a real branch, and
the counts are shown instead of hidden:
minified bundles, assets, snapshots, documentation, coding-agent and editor
config (`.claude/`, `.cursor/`, `.vscode/`, …), ignore files and formatter
settings, your `ignore` globs, and whitespace-only hunks are dropped. This is
free, usually removes most of a real branch, and the counts are shown instead
of hidden:
`312 hunks → 74 reviewable (238 filtered: lockfiles, generated, formatting)`.
Whitespace inside a string still counts as a real change, and indentation is
never dropped in Python or YAML.
never dropped in Python or YAML. Tests are *not* filtered — a test that
asserts nothing is worth catching. Docs can be reopened with
`--include-docs`.
3. **Fixed rules** — `DM001`, `DM002` and your regex rules. No model involved.
4. **Context** — built for each review from `.diffmind/graph.db`: the function
the hunk is in, the callers of every changed symbol, the definitions it
Expand Down Expand Up @@ -601,6 +605,7 @@ cache = true
temperature = 0.0 # 0 = greedy and reproducible
max_tokens = 1024
ignore = ["**/legacy/**", "*.generated.ts"] # on top of the built-in noise rules
include_docs = false # review .md/.rst/.txt too; off by default

[backend]
kind = "local" # or "ollama" / "openai-compatible"
Expand Down
2 changes: 1 addition & 1 deletion apps/tui-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "diffmind"
version = "0.9.0"
version = "0.9.1"
edition = "2024"
description = "Local-first AI code review agent — powered by on-device inference"

Expand Down
5 changes: 5 additions & 0 deletions apps/tui-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ pub struct Cli {
#[arg(long)]
pub seed: Option<u64>,

/// Review documentation (.md, .rst, .txt) too. Skipped by default —
/// a reviewer prompt finds vulnerabilities in prose.
#[arg(long)]
pub include_docs: bool,

/// Skip the on-disk result cache for this run
#[arg(long)]
pub no_cache: bool,
Expand Down
4 changes: 4 additions & 0 deletions apps/tui-cli/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ pub struct ReviewConfig {
/// Same syntax as a rule's `files`: `*.ts`, `**/legacy/**`, or an exact path.
#[serde(default)]
pub ignore: Option<Vec<String>>,
/// Review documentation (`.md`, `.rst`, `.txt`) as code. Off by default:
/// a model primed for vulnerabilities will find them in prose. Tool config
/// and ignore files are always skipped and have no switch.
pub include_docs: Option<bool>,
/// Refresh the code graph before each review. On by default — a stale graph
/// reports wrong line ranges, not merely missing ones.
pub auto_index: Option<bool>,
Expand Down
1 change: 1 addition & 0 deletions apps/tui-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,7 @@ fn apply_prefilter(
&PrefilterOptions {
generated_paths,
ignore_globs: settings.ignore_globs.clone(),
include_docs: settings.include_docs,
},
);

Expand Down
3 changes: 3 additions & 0 deletions apps/tui-cli/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ pub struct Settings {
pub debug: bool,
/// Extra globs dropped by the pre-filter, from `.diffmind/config.toml`.
pub ignore_globs: Vec<String>,
/// Send documentation to the model instead of dropping it as prose.
pub include_docs: bool,
/// Refresh the code graph before reviewing.
pub auto_index: bool,
}
Expand Down Expand Up @@ -137,6 +139,7 @@ pub fn resolve_settings(cli: &Cli, file: &FileConfig) -> Result<Settings> {
// *supposed* to differ, and replaying one would be a lie.
use_cache: !cli.no_cache && resolve(None, r.cache, true) && temperature == 0.0,
ignore_globs: r.ignore.clone().unwrap_or_default(),
include_docs: cli.include_docs || resolve(None, r.include_docs, false),
auto_index: !cli.no_index && resolve(None, r.auto_index, true),
use_baseline: !cli.no_baseline,
use_daemon: !cli.no_daemon,
Expand Down
143 changes: 143 additions & 0 deletions apps/tui-cli/tests/stdin_pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -483,3 +483,146 @@ fn the_exit_code_follows_the_fail_threshold_not_the_finding_count() {

let _ = std::fs::remove_dir_all(&dir);
}

/// Agent config, ignore files and prose must not cost an inference pass — and
/// must not reach the model at all.
///
/// Reported from the field on 0.9.0: every file under `.claude/` came back as a
/// HIGH finding. Those files are imperative English about credentials, shell
/// commands and permissions, which is exactly what a reviewer prompt primed for
/// "exposed secrets, disabled auth" is looking for. The content assertions
/// matter more than the path ones here: a path can vanish from the prompt while
/// the body is still being reviewed under the previous file's header.
#[test]
fn agent_config_ignore_files_and_docs_never_reach_the_model() {
let dir = tmpdir("toolconfig");
let stub = Stub::spawn(2);

let diff = "\
--- a/.claude/skills/deploy.md
+++ b/.claude/skills/deploy.md
@@ -1,2 +1,3 @@
# Deploy
+Always export AWS_SECRET_ACCESS_KEY before deploying.
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,2 @@
-.env
+.env.local
--- a/.prettierrc.json
+++ b/.prettierrc.json
@@ -1,1 +1,1 @@
-{ \"semi\": true }
+{ \"semi\": false }
--- a/README.md
+++ b/README.md
@@ -1,1 +1,2 @@
# Project
+Put your token in .env
--- a/src/two.rs
+++ b/src/two.rs
@@ -10,2 +10,2 @@
-let b = verify(token);
+let b = true;
";

let run = review_stdin(
&dir,
diff,
&[
"--format",
"json",
"--backend",
"openai-compatible",
"--backend-model",
"stub",
"--backend-url",
&stub.url(),
],
);

let prompts = stub.prompts();
assert_eq!(
prompts.len(),
1,
"only src/two.rs is reviewable.\nstderr: {}",
run.stderr
);

for leaked in [
"AWS_SECRET_ACCESS_KEY",
".claude",
".gitignore",
".env.local",
".prettierrc",
"semi",
"README.md",
"Put your token",
] {
assert!(
!prompts[0].contains(leaked),
"{leaked:?} reached the model:\n{}",
prompts[0]
);
}
assert!(prompts[0].contains("src/two.rs"));

let found = findings(&run.stdout);
let files: Vec<&str> = found.iter().filter_map(|f| f["file"].as_str()).collect();
assert_eq!(files, ["src/two.rs"], "only the code file is reported");

let _ = std::fs::remove_dir_all(&dir);
}

/// The opt-in exists for teams whose docs carry contracts. It reopens prose
/// only — agent config stays out regardless.
#[test]
fn include_docs_reopens_prose_but_not_agent_config() {
let dir = tmpdir("includedocs");
let stub = Stub::spawn(3);

let diff = "\
--- a/.claude/skills/deploy.md
+++ b/.claude/skills/deploy.md
@@ -1,2 +1,3 @@
# Deploy
+Always export AWS_SECRET_ACCESS_KEY before deploying.
--- a/docs/api.md
+++ b/docs/api.md
@@ -1,1 +1,2 @@
# API
+POST /v1/charge is idempotent.
";

let run = review_stdin(
&dir,
diff,
&[
"--include-docs",
"--format",
"json",
"--backend",
"openai-compatible",
"--backend-model",
"stub",
"--backend-url",
&stub.url(),
],
);

let prompts = stub.prompts();
assert_eq!(
prompts.len(),
1,
"docs are reviewed, agent config is not.\nstderr: {}",
run.stderr
);
assert!(prompts[0].contains("docs/api.md"));
assert!(
!prompts[0].contains("AWS_SECRET_ACCESS_KEY"),
"--include-docs must not reopen .claude/:\n{}",
prompts[0]
);

let _ = std::fs::remove_dir_all(&dir);
}
12 changes: 6 additions & 6 deletions npm/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@diffmind/cli",
"version": "0.9.0",
"version": "0.9.1",
"description": "Local-first AI code review for your git diffs \u2014 on-device inference, no cloud, no API keys",
"author": "Thinkgrid Labs <dennis@thinkgrid.dev>",
"license": "MIT",
Expand Down Expand Up @@ -39,10 +39,10 @@
"node": ">=18.0.0"
},
"optionalDependencies": {
"@diffmind/cli-darwin-arm64": "0.9.0",
"@diffmind/cli-darwin-x64": "0.9.0",
"@diffmind/cli-linux-arm64": "0.9.0",
"@diffmind/cli-linux-x64": "0.9.0",
"@diffmind/cli-win32-x64": "0.9.0"
"@diffmind/cli-darwin-arm64": "0.9.1",
"@diffmind/cli-darwin-x64": "0.9.1",
"@diffmind/cli-linux-arm64": "0.9.1",
"@diffmind/cli-linux-x64": "0.9.1",
"@diffmind/cli-win32-x64": "0.9.1"
}
}
2 changes: 1 addition & 1 deletion npm/platform/darwin-arm64/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@diffmind/cli-darwin-arm64",
"version": "0.9.0",
"version": "0.9.1",
"description": "diffmind prebuilt binary for darwin-arm64 (aarch64-apple-darwin)",
"author": "Thinkgrid Labs <dennis@thinkgrid.dev>",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion npm/platform/darwin-x64/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@diffmind/cli-darwin-x64",
"version": "0.9.0",
"version": "0.9.1",
"description": "diffmind prebuilt binary for darwin-x64 (x86_64-apple-darwin)",
"author": "Thinkgrid Labs <dennis@thinkgrid.dev>",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion npm/platform/linux-arm64/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@diffmind/cli-linux-arm64",
"version": "0.9.0",
"version": "0.9.1",
"description": "diffmind prebuilt binary for linux-arm64 (aarch64-unknown-linux-gnu)",
"author": "Thinkgrid Labs <dennis@thinkgrid.dev>",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion npm/platform/linux-x64/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@diffmind/cli-linux-x64",
"version": "0.9.0",
"version": "0.9.1",
"description": "diffmind prebuilt binary for linux-x64 (x86_64-unknown-linux-gnu)",
"author": "Thinkgrid Labs <dennis@thinkgrid.dev>",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion npm/platform/win32-x64/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@diffmind/cli-win32-x64",
"version": "0.9.0",
"version": "0.9.1",
"description": "diffmind prebuilt binary for win32-x64 (x86_64-pc-windows-msvc)",
"author": "Thinkgrid Labs <dennis@thinkgrid.dev>",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/core-engine/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "core-engine"
version = "0.9.0"
version = "0.9.1"
edition = "2024"
description = "Diffmind shared AI engine core"

Expand Down
Loading
Loading