# Changelog All notable changes to this project will be documented in this file. ## v0.12.0 (2026-10-13) - **Static musl release binaries (#21 P10).** Releases now include `x86_64-unknown-linux-musl` and `aarch64-unknown-linux-musl` archives (7 archives plus `SHA256SUMS`), so the binary runs on Alpine and on glibc older than 2.34. - **Binary files are reported, not dumped; the header no longer leaks absolute paths (#21 P11).** A file with NUL bytes in its first 8 KiB (UTF-16/32 text with a BOM excepted) is listed under `## Skipped` as `binary` instead of a `` block in the body (a repo without `.git` produced hundreds of those). The header says `from the `my proj` directory` rather than the absolute path passed with `-d`. - **`--stats[=N]` per-file token breakdown; real token count after a run (#21 P7).** `--stats` lists the largest files by tokens (20 by default; `--stats=0` lists all) and, with `--max-tokens`, which files fit, which is cut first and what is omitted; it writes no document. The `Estimated tokens` line after a run is now the tokenizer's count of the written document (it was bytes/4, off by −11% to −75% on real code); output piped with `-o -` keeps the `~NK` estimate. - **Auto-diff honors `--max-tokens` like the standard renderer (#20 B14).** The auto-diff path used a bytes/4 cut over the finished document (mid-file, ignoring `--encoding`, omissions unreported). Files are now budgeted one by one with the real tokenizer: a file that would cross the budget is clamped at a `--truncate` boundary (and says so) or omitted, and the notice counts the omitted files. The budget search is shared (`markdown::fit_chunk_to_budget`) with the standard path. - **`--structure` lists imports and exports (#22 B15).** The help promised them but only counts were printed (and the internal lists held placeholders). Each file now gets `**Imports:**` / `**Exports:**` lists (Rust `use`/`pub use`, Python, JS/TS `import`/`export`, Go, Java, C/C++ `#include`), capped at 20 entries. Top-level declarations only. - **Rust `--signatures` group methods under their `impl`/`trait` (#22 B16).** Methods used to be listed flat after `impl A`, leaving several `pub fn new` ambiguous. Each impl/trait is now one entry: `impl A { pub fn new() -> A … }`. Library: members are part of the impl/trait signature's `full_signature` and no longer separate `Signature`s. - **Python `--signatures`: no nested functions, overloads keep their return types (#22 B16).** Functions and classes defined inside a function are no longer listed; `@overload def f(...) -> T: ...` used to lose the decorator and `-> T` (the one-line body defeated the slicer) and now keeps both, with decorator lines no longer carrying stray indentation. - **Go `--signatures` keep return types (#22 B16).** Methods lost their results (`func (c *Command) Context()` for `… context.Context`) because the grammar's `result` field was looked up under the wrong node kind; type aliases printed `type X type`. Now `func (c *Command) Context() context.Context` and `type X flag.ParseErrorsWhitelist`. - **Installer picks the right libc and verifies what it installed (#21 P10, B23).** `install.sh` uses the static `musl` archive on Alpine and glibc older than 2.34 (`CONTEXT_BUILDER_LIBC=musl|gnu` overrides) and stops with instructions when a release has no musl build (v0.12.0 is the first that has one) instead of installing a binary that cannot start. It runs `$INSTALL_DIR/context-builder --version` instead of whatever is first on `PATH`, and warns when the install is shadowed or not on `PATH`. README says `| sh`, matching the script. - **Malformed config is an error; unknown keys warn (#21 B19).** A `context-builder.toml` that cannot be parsed (or has a wrong value type) now stops the run with `invalid config '': …` instead of silently using defaults. Unknown keys such as `filters` are reported with a suggestion (`did you mean 'filter'?`) and ignored. Library: new `config::try_load_config`; `load_config_from_path` keeps its old lenient behavior. - **`keep_last = N` snapshot retention (#20).** With timestamped output, only the newest N generated snapshots are kept. - **Docs (#21):** bare invocation, `-o -` in help, environment variables, hidden files/symlinks, diff format. - **`--config ` and `--no-config` (#21).** Use another config file, or ignore the project one, for a single run. - **`--token-count` honors `--max-tokens` (#21 B21).** It reports the capped size, the budget, and how many files would be omitted. - **Python `--signatures` headings use `#` (#22 B16).** Section headings inside a `python` block were `// Functions`. `--structure`'s help no longer promises imports/exports, which it never emitted. - **`--structure` code-line counts (#22 B15).** TypeScript, JavaScript, Python, Go, Java and C/C++ no longer report `(0 code)`; blank lines and whole-line comments are excluded, as for Rust. - **Snapshots (#20 B10, B9).** Timestamped outputs written within the same second get `-1`, `-2`, … suffixes instead of prompting/cancelling or overwriting the previous snapshot. The auto-diff renderer prints `Modified:` as `YYYY-MM-DD HH:MM:SS UTC` (it printed a Debug `SystemTime { … }`) when `--file-metadata` is on. - **Built-in ignores can be overridden (#16).** `--ignore '!build'` (also `!dist`, `!vendor`, …) keeps a directory the defaults would hide, so tracked sources such as `src/build/mod.rs` can be included. - **Go files under `doc/` rank as source (#19).** cobra's real `doc` package no longer sorts with documentation; Markdown in `doc/` is still docs. - **Install docs no longer suggest `curl | sh` from `master` (#37).** The README and `install.sh` header now show download, review, run from a release tag; `install.sh` honors `CONTEXT_BUILDER_VERSION` to pin the release (and `curl -f`, and reports the version of the binary it just installed); a no-script, checksum-verified path is documented. `SKILL.md` and the README pin `cargo install … --version 0.11.0 --locked`. **Release checklist:** bump these pinned versions with each release. - **The report says its file tree is alphabetical (#42).** File bodies follow the relevance ranking (root README and manifests first), so a one-line note under `## File Tree Structure` (and in `--preview`) now explains why the two orders differ. - **Size warnings match what the run writes (#41).** The `large file(s) detected … Total context size` block is no longer printed when `--max-tokens` caps the output, and leaves out files that `--signatures` replaces with an outline (`--structure` only appends a summary, so those files still count) (it still appears for full runs and `--preview`). The >128K advice omits `--filter …` when a filter was already given. - **`--init` honors `-d` and no longer drops docs (#17 B11).** The config is written to, and file types detected from, the `-d` directory. The generated `ignore` no longer lists `docs`, and `output_folder` is `.context-builder/out` instead of `docs`. - **`--init` no longer suggests asset, secret or oversized types in `filter` (#39).** The generated `filter` used to list every common extension, including `png` or `pem`; an explicit filter of such a type opts those files back in, which undid the v0.11.0 default skips. Detection now counts only files a default run would include. - **Diagnostics (#21: B13, B17, B18).** The tree-sitter notice no longer prints on every default run: only for `--signatures`/`--structure`, or `--truncate smart` with `--max-tokens`, and it suggests `cargo install context-builder --features tree-sitter-all`. Errors print once as `Error: ` (not Debug), and output-path errors name the path. In builds without tree-sitter, truncation notices say `mode: byte`. - **A closed stderr or stdout pipe no longer aborts the run (#40).** `context-builder … 2>&1 | head -3` used to panic with exit code 101 and could leave the output file missing. Progress and warning lines now ignore write errors, and `-o - | head` ends with exit code 0. ## v0.11.0 (2026-10-10) — "Quiet Defaults" v0.11.0 trims what a default run puts in the document and removes the surprises around it. **Breaking changes** (details below): - Lockfiles, binary/media assets, files over 256 KiB, and likely secrets are skipped by default (opt back in with: `--include-lockfiles` for lockfiles; a matching explicit `--filter`, e.g. `--filter svg`, for an asset; `--max-file-size` for the size cap, `0` disables it; `--include-secrets` for name-only secrets such as `id_rsa`; `--hidden` for dotfiles, with hidden secret files like `.env` needing both `--hidden` and `--include-secrets`). - Per-file `Size` / `Modified` lines are off by default (`--file-metadata` opts in). - The >100-file confirmation prompt is gone; the overwrite prompt only appears when both stdin and stderr are terminals. - New file ranking: category 0 is root-only, files are grouped by directory. - **Library API:** `Prompter` no longer has `confirm_processing`, `file_utils::confirm_processing` is removed, `markdown::generate_markdown` takes a `skipped: &[SkippedFile]` argument and returns `io::Result`, and `cli::Args` / `config::Config` gained fields (`include_lockfiles`, `file_metadata`, `max_file_size`, `hidden`, `include_secrets`). The auto-diff cache fingerprint changed, so the first run after upgrading resets the diff baseline once. ### Fixes and file inclusion - **`--ignore` accepts comma-separated values (B12)** - `-i docs,assets` ignores both names, matching `--filter`. Repeated `-i` flags still append. - `--help` and the README document that `--ignore` takes gitignore-style globs and paths (`*.lock`, `crates/core`). The help example no longer suggests `--ignore lock`, which matched nothing useful. - The >128K-token warning suggests `--filter` extensions detected in that run instead of a hardcoded `--filter rs,toml`, and its `--ignore docs,assets` example is a command the flag actually honors. - **File inclusion** - `.gitignore` and `.ignore` files inside the `-d` tree apply even when that tree has no checkout. Ignore files above `-d` apply only when a `.git` directory or file exists at the walk root or an ancestor (a real repository, including worktrees). Otherwise parent ignore files are not read - Directories containing a `CACHEDIR.TAG` file are skipped. `target` is a default ignore only at the walk root (`/target`); a nested source directory such as `src/target/` is kept. Nested Cargo `target/` directories are still skipped via `CACHEDIR.TAG` - When the walk matches no files, one stderr warning is printed (`No files matched; check .gitignore, --ignore, and --filter`) and the process still exits 0 - A previous context-builder report is not pulled back in. A file is skipped when its header is `# Directory Structure Report` followed by a `Content hash:` line; only a small prefix is read - The output auto-ignore is anchored to the resolved output path relative to the project, so a nested `docs/output.md` is kept when the default output name is `output.md` ### Prompt removal - **Removed the >100-file confirmation prompt** ([#23](https://github.com/igorls/context-builder/issues/23) (b)) - Processing no longer stops to ask before a large file set, on a terminal or in a script. File count is a poor proxy for cost (about 100 files takes milliseconds). - Large outputs still emit the existing non-blocking warning on stderr when the bytes/4 estimate exceeds 128K tokens. Nothing is asked, and the warning is not written to stdout (including `-o -`). - `-y` / `--yes` remains accepted so existing scripts keep working. It still skips the overwrite prompt on a TTY and does not change processing. ### Breaking: file metadata - **Per-file `Size` / `Modified` metadata is now off by default.** Each file header no longer includes `- Size:` and `- Modified:` lines unless you opt in. Those lines cost tokens, and the modification time changes the document on checkout, copy, or `touch` even when the file bytes (and therefore the content hash) did not. - Opt in with `--file-metadata`, or set `file_metadata = true` in `context-builder.toml`. An explicit `--file-metadata` overrides `file_metadata = false` in config. When the flag is omitted, the config key applies. - The content hash still fingerprints file bytes, not mtime. With metadata off, two runs that differ only in mtime produce identical output and an identical hash. - The auto-diff cache compares those content hashes, so an mtime-only change is not reported as an edit and does not rewrite the per-file sections. Toggling `file_metadata` does not reset the diff baseline (it is a rendering option, like `line_numbers`). ### Breaking: asset, size, and secret defaults **Breaking default change.** A full-tree run no longer inlines common binary/media assets, files larger than 256 KiB, or likely-secret files. Previously those files were copied into the markdown (hidden dotfiles were already omitted). Outputs are smaller and no longer dump `id_rsa`, `*.pem`, or `.env` (the last only once `--hidden` is on). Assets are restored by an explicit matching `--filter`, secrets by `--include-secrets` (dotfile secrets also need `--hidden`), and large files by `--max-file-size`; see the escape hatches below. - **Assets are skipped by default.** Images (including SVG), fonts, audio, video, archives, PDFs and office documents, design files (`.ai`, `.psd`), compiled objects, wasm, model weights, source maps (`*.map`), and minified bundles (`*.min.js`, `*.min.mjs`, `*.min.cjs`, `*.min.css`). The list is `ASSET_EXTENSIONS` in `src/content_filter.rs`. - **`--max-file-size `** (default `256K`). Accepts `256K`, `1M`, `262144` (bytes), and `KB`/`KiB` spellings; `K`/`M`/`G` are powers of 1024. Files strictly larger than the limit are skipped. **`--max-file-size 0`** disables the limit. - **Likely secrets are skipped** with a stderr warning that names the path and a category, never the contents: `id_rsa` / `id_dsa` / `id_ecdsa` / `id_ed25519` (and `*_sk`), `*.pem`, `*.key`, `*.p12`, `*.pfx`, `*.ppk`, `credentials*.json`, `.env` and `.env.*` except `.env.example` and `.env.sample`, and `.npmrc` / `.pypirc` when they contain a token. A value that is wholly an environment reference (`${VAR}`, `$VAR`, `%VAR%`) — including `//registry.npmjs.org/:_authToken=${NPM_TOKEN}` — is a placeholder, not a token. `id_rsa.pub` is kept. - **Skipped files are reported.** The markdown gains a short `## Skipped` section (`path` — `asset` | `too large` | `secret`). Stderr gets one summary line (`Skipped N files (…)`). - **Escape hatches.** An explicit `--filter` / config `filter` of an excluded extension includes it (`--filter svg`, `--filter pem`, `--filter js` for `*.min.js`). The size limit still applies to those files. `--include-secrets` includes name-only secrets (`id_rsa`, `credentials.json`, `.env`); `--filter json` does not. Dotfile secrets also need `--hidden`. - **`--hidden`** opts into hidden files and directories (`.github/workflows/ci.yml`, `.gitignore`, `.cargo/config.toml`). It does not follow symlinks, does not override gitignore or `--ignore`, does not descend into `.git` / `.hg` / `.svn` / `.bzr`, and does **not** include secrets. - **Config keys** (CLI wins): `max_file_size` (string or integer bytes), `hidden`, `include_secrets`. For the boolean keys, omitting the flag leaves a config `true` in place, same as `--line-numbers`. An explicit `--max-file-size 256K` overrides a different config value. ### Breaking: lockfiles and ranking Category numbers are unchanged. - **Lockfiles are skipped by default.** Basenames in `LOCKFILES` (`Cargo.lock`, `package-lock.json`, `uv.lock`, `pnpm-lock.yaml`, …) are left out of the tree and the document. Opt in with `--include-lockfiles` or `include_lockfiles = true` in `context-builder.toml`. The CLI flag wins when both are set. A type filter that matches a lockfile (`-f toml` for `Cargo.lock`, `-f lock`) still skips them until the opt-in is set. When any are skipped, stderr reports the count: `Skipped N lockfiles (use --include-lockfiles to include)`. - **Category 0 is root-only.** Root manifests, the root README (first), and other root priority docs (`AGENTS.md`, `CONTRIBUTING.md`, …) stay category 0. Nested `README`, `package.json`, and other manifests rank with their directory and sort at the top of that directory's files. - **Changelogs are documentation (category 3) at every depth,** including the repository root: `CHANGELOG.md`, `CHANGELOG`, `HISTORY.md`, `HISTORY`, `CHANGES.md`, `NEWS.md`. - **Within a category, files are grouped by directory,** then README/manifest, then the existing entry-point priority (`main`, `lib`, `mod`, `index`, `app`, `__init__`), then path. The order is deterministic across operating systems. ## v0.10.0 (2026-08-31) — "Honest Signatures" > Delivered per `docs/research/v0.10-plan.md` (derived from the post-v0.9.0 review): every documented flag now does what it says, divergent language maps are unified, and packaging/CI gaps are closed. - **`--truncate` is now wired into the `--max-tokens` budget path (review §1.1)** - A file that crosses the remaining budget is **truncated in place** (with a visible `_⚠️ File content truncated: N of M bytes (mode: …)_` marker) instead of being omitted wholesale; only files after the crossing one are omitted - `smart` mode cuts at an AST boundary via tree-sitter (char-safe per B19); `byte` mode cuts at a UTF-8 character boundary — the two modes now genuinely differ - The truncated chunk is verified with the real tokenizer to fit the remaining allowance (probing the fixed section cost first, then shrinking proportionally); if even a minimal section cannot fit, the file is omitted exactly as before - Serial and parallel paths truncate identically — byte-for-byte determinism between builds is preserved (verified) - **TypeScript class members are now extracted (review §1.3)** - `--signatures` on a TS/TSX file now lists `method_definition` (including `async`/`static`/abstract), `public_field_definition` fields, and preserves access modifiers — previously only the bare class header appeared - `--visibility` is honored for TypeScript: class members resolve `public`/`private`/`protected` (public by default), and top-level declarations are Public when `export`ed, Private otherwise (review §1.2, TS half) - **Honest `--visibility` for the remaining languages (review §1.2)** - Rust, Go, Java, and TypeScript honor the filter; C, C++, Python, and JavaScript now emit a one-time `warn!` (per language, per process) stating the filter is not yet applied, instead of silently returning unfiltered signatures - Full visibility classification for those four languages is queued for v0.11 - **Single extension→language map (review §2)** - New `src/languages.rs` — one `language_for_extension()` used by the file-body fence, the signature block, and the auto-diff path, replacing three divergent `match` maps - Fixes the observable drift: `.mjs` bodies/signatures/auto-diff now consistently say `javascript` (previously the body fence said the non-highlightable `mjs`); `.jsx`/`.sh` keep their deliberate distinct tags (`jsx`, `bash`) everywhere — previously the auto-diff path emitted raw extensions - **Documentation sync (review §5)** - `SKILL.md` no longer documents a nonexistent `simple` truncate mode (values are `smart`/`byte`) - `README.md` describes `--max-tokens` truncation semantics accurately and states which languages honor `--visibility` - `AGENTS.md` updated: std file locking (fs2 removed in v0.9.0), `src/tree_sitter/` in the structure tree, all nine `tree-sitter-*` features in the feature table, and the full release process (binary release + winget) - **CI, packaging & repo hygiene (review §4)** - The MSRV job now builds with the pinned declared MSRV (`1.89`, `cargo check --all-features`) instead of `stable` — previously it never verified the MSRV at all - `aarch64-unknown-linux-gnu` added to the release matrix (native `ubuntu-24.04-arm` runner) — the one-liner installer previously 404'd on Raspberry Pi / Graviton / ARM WSL because `install.sh` downloads an archive that was never built - `[package.metadata.binstall]` added so `cargo binstall context-builder` resolves the pre-built release binaries - `.github/dependabot.yml` added (cargo + github-actions, weekly; majors stay ungrouped for MSRV-conscious review) - `test.md` (16 MB generated output) untracked; `Cargo.lock` no longer listed in `.gitignore` while being tracked (binary crate keeps its lockfile) - **Dependency diet (review §3)** - Removed `walkdir` (never imported), `once_cell` (→ `std::sync::LazyLock`), and `num_cpus` (→ `std::thread::available_parallelism()`); `tempfile` no longer duplicated in `[dev-dependencies]` - `similar` 2.7 → 3.2 and `tiktoken-rs` 0.9 → 0.12 (adds gpt-5 / o-series model mappings); both verified through the full suite - `tree-sitter` stays at 0.26: v0.27 requires Rust 1.90, above the declared MSRV 1.89 — deferred to v0.11 together with the MSRV bump ## v0.9.0 (2026-06-04) — "Trustworthy Output" > Delivered per `docs/research/next-release-roadmap.md`: accurate token counting, honest tree-sitter signatures, pipe-friendly output, and a reliable auto-diff cache. - **Accurate token counting — selectable tokenizer (F1)** - New `--encoding {o200k_base|cl100k_base}` flag (and `encoding` config option), defaulting to **`o200k_base`** (GPT-4o / o-series). The previous hardcoded `cl100k_base` under-counts every modern OpenAI model. The selected encoding flows through both `--token-count` and `--max-tokens` budgeting - `--token-count` now reports counts using the chosen encoding - **CLI validation — invalid values are now rejected (F7)** - `--truncate`, `--visibility`, and `--encoding` are constrained to their allowed sets via clap `value_parser`, so invalid values produce a clear `error: ... [possible values: ...]` instead of being silently coerced (fixes B3 `--visibility` and B4 `--truncate` silent-acceptance) and `--help` now lists the valid values - **CLI precedence — explicit flags override config even at their default value** - `--encoding`, `--truncate`, and `--visibility` are now honored when passed explicitly even if the value equals the clap default — e.g. `--encoding o200k_base` overriding a config that sets `cl100k_base`. Resolution uses clap's `ValueSource` to tell an explicitly-passed flag from an omitted one, instead of treating "value == default" as "not provided", which previously left the default value un-selectable from the CLI whenever the config set a non-default - **Pipe-friendly output — stream to stdout (F2)** - `-o -` streams the generated document to **stdout** instead of a file, enabling `context-builder -f rs -o - | llm`. All progress/status chatter is suppressed (or already on stderr) in this mode so the pipe stays clean, and `-` is never folded into an output folder or timestamped name. Works for the auto-diff path too (the composed diff document is written to stdout) - In pipe mode the large-project (>100 files) confirmation prompt is skipped as well — it `print!`s to stdout and would otherwise prepend prompt text to the piped document (and block on stdin); pipe mode is non-interactive, so it proceeds as if `--yes` were given - **Trustworthy token budget — `--max-tokens` uses the real tokenizer (F4, B1, B2)** - `--max-tokens` now counts the real tokenizer (per `--encoding`) on each file's rendered output, replacing the crude `buf.len()/4` (parallel) and `metadata().len()/4` (serial) byte heuristics. Both code paths now estimate identically, restoring byte-for-byte determinism between parallel and non-parallel builds - Fixed the budget bypass where the first file was always emitted in full regardless of `--max-tokens` (the `tokens_used > 0` guard). The budget now applies to every file, including the first (B1) - The document header + file tree are debited from the budget, so `--max-tokens` accounts for the whole document, not just file bodies - Tokenization is skipped entirely when no budget is set — no overhead on normal runs. In that case the non-parallel build also streams each file straight to the output instead of rendering it into a buffer first (buffering exists only to tokenize the chunk for the budget), keeping peak memory bounded on large files - The deterministic content hash now folds in every output-affecting option (`line_numbers`, `max_tokens`, `encoding`, tree-sitter flags, `encoding_strategy`). Previously, toggling e.g. `--line-numbers` produced a different document under the **same** hash, silently breaking LLM prompt caching. The hash still fingerprints raw file content (not the rendered output, which embeds volatile mtimes), so it stays stable across checkouts (B2) - **Robustness & accuracy — low-severity mop-up (B5, B9, B17, B19, B20)** - Auto-diff cache is now written atomically (temp file + rename) instead of truncate-then-write, so a crash mid-write can't leave a corrupt, baseline-dropping cache (B20). The `fs2` dependency was dropped — `std::fs::File` provides advisory locking natively since Rust 1.89 (MSRV is now 1.89) - `find_smart_truncation_point` clamps its result to a UTF-8 char boundary, preventing a panic when the byte-budget fallback lands mid-character (B19) - `--token-count` renders each file through the **same path as the document**, so the preview matches the produced output (including encoding transcoding and tree-sitter signature/structure enrichment) instead of diverging via a separate raw-byte read (B9) - C++ struct inheritance, `enum` underlying types, and `using`/`typedef` alias targets — plus C `typedef` aliased types — are preserved in extracted signatures instead of being reduced to a bare name (B17) - An unrecognized `encoding_strategy` in config now warns instead of silently falling back to `detect` (B5) - **Reliable auto-diff cache invalidation (B6, B7, B8)** - Unified the two duplicated config-hash functions (`cache.rs::hash_config` and `state.rs::compute_config_hash`) into one shared `config::config_fingerprint`, removing the "must stay in sync" drift hazard (B6) - The auto-diff cache fingerprint now keys on **only the file-selection inputs** (`filter` / `ignore`) — the options that decide which files form the comparable baseline. The diff compares each file's **raw captured content** (`ProjectState` stores the bytes), which no rendering option changes, so `--signatures` / `--structure` / `--truncate` / `--visibility` / `--max-tokens` / `--line-numbers` / `--encoding` / `encoding_strategy` are all **excluded**: toggling one against an existing baseline no longer silently discards the diff (which would hide real content changes on that run). The resolved CLI `filter`/`ignore` are folded in so the key reflects real behavior even when they come from flags rather than the config file (B7). _(This supersedes an earlier B7 approach that included the rendering options; per PR review, those caused exactly the spurious baseline resets described above.)_ - `--diff-only` now warns when used without `auto_diff` instead of silently emitting full file contents (B8) - **Tree-Sitter correctness — honest `--signatures` / `--visibility`** - Fixed Java `--visibility` filter being completely non-functional — `get_visibility` returned `Visibility::All` unconditionally, so `--visibility public` dropped *every* Java symbol and `--visibility private` leaked *all* of them. It now inspects the declaration's `modifiers` node (B12) - Fixed C/C++ functions returning a pointer or reference being silently dropped — `find_function_name` now descends through `pointer_declarator` / `reference_declarator` / `parenthesized_declarator` to locate the `function_declarator` (B13) - Fixed C++ qualified return types (e.g. `std::string`) being misread as the function name — the name is now resolved strictly inside `function_declarator`, with no sibling-identifier fallback (B14) - Fixed Rust bodiless trait methods (`function_signature_item`, e.g. `fn draw(&self);`) being dropped from both signature extraction and structure counts (B15) - Fixed Python class base lists being double-parenthesized (`class User((Base))`) — `argument_list` already includes the surrounding parentheses (B16) - Fixed Rust restricted visibility (`pub(crate)` / `pub(super)` / `pub(in ...)`) being reported as fully public; restricted forms are no longer matched by `--visibility public` (B18) - Fixed a public trait's required methods being dropped under `--visibility public` — a `function_signature_item` carries no visibility modifier (Rust forbids one on trait items), so it now inherits the enclosing trait's visibility instead of defaulting to private; a public trait's required methods are part of its public API and are kept, while a private trait's are still filtered out - Fixed the Java analog: a `public interface`'s methods are implicitly public but carry no `modifiers` node, so they were classified package-private and dropped under `--visibility public`. Java members with no access modifier are now resolved by context — implicitly public as interface/annotation members, package-private in a class/enum — while an explicit `private`/`protected` (incl. Java 9+ private interface methods) is still respected - Added regression tests covering each of the above - **Maintenance** - Modernized two `sort_by` comparisons to `sort_by_key(Reverse(..))` to satisfy a newer clippy under `-D warnings` (surfaced after the toolchain advanced during the release gap) - Added `docs/research/next-release-roadmap.md` — the full prioritized v0.9.0 plan (verified bug backlog, competitive refresh, feature/dependency/DX roadmap) ## v0.8.3 - **Bug Fixes** (identified by Gemini Deep Think v6 — clean benchmark prompt, zero historical bias) - Fixed `output_folder` auto-ignore silently excluding all user content in the folder — now only ignores `*.md` context files within it - Fixed `diff_context_lines` config being ignored in auto-diff mode — the value was never passed through `compare_with` to `diff_file_contents` - Fixed double file I/O in `process_file` — `seek(0)` was followed by `fs::read_to_string` which opens a new file descriptor, wasting the seek. Now reuses the already-open handle - **Security Hardening** - `install.sh` now defaults to `~/.local/bin` (user-local, no sudo required) instead of `/usr/local/bin` - Supports `CONTEXT_BUILDER_INSTALL_DIR` env var for custom install paths - SKILL.md rewritten: `cargo install` promoted as primary method (fully verified via crates.io), `-y` flag guidance softened to require explicit path scoping ## v0.8.2 - **Documentation** - Updated SKILL.md for v0.8.1+ with Security & Path Scoping section - Documented Tree-Sitter CLI flags (`--signatures`, `--structure`, `--visibility`, `--truncate`) - Added AST signatures and API surface review recipes - **Test Coverage** - Extended unit test coverage across `config.rs`, `file_utils.rs`, `state.rs`, `markdown.rs`, and `lib.rs` - Added tests for file relevance categories, lock files, various source extensions, encoding handling, auto-diff workflows, and config hash consistency ## v0.8.1 - **Bug Fixes** (identified by Gemini Deep Think v6 code review — 11 confirmed bugs, 0 false positives) - Fixed cache hash desync — `cache.rs` was missing 4 fields (`signatures`, `structure`, `truncate`, `visibility`), causing stale cache hits when toggling tree-sitter flags - Fixed JavaScript arrow function body leak — `statement_block` is a child of `arrow_function`, not `variable_declarator`, causing full function bodies to leak into signature output - Fixed TypeScript arrow function handling — same root cause as JavaScript - Fixed Python decorator erasure — intercepting `decorated_definition` nodes now preserves `@decorator` lines in signatures - Fixed Python `is_method` for decorated methods — iterative 4-level parent walk replaces fragile 2-level check - Fixed Rust tuple struct erasure — added `ordered_field_declaration_list` to body kinds - Fixed C/C++ header file prototype extraction — added `declaration` node matching for `.h` files - Fixed C++ class inheritance dropped — applied byte-slicing to preserve `template<>` and `: public Base` - Fixed JS/TS exported arrow functions invisible — added `lexical_declaration` to export signature extraction - Added `.jsx` extension support for JavaScript - **Dependency Updates** - Updated `tree-sitter` core: 0.24 → 0.26 - Updated `tree-sitter-rust`: 0.23 → 0.24 - Updated `tree-sitter-javascript`: 0.23 → 0.25 - Updated `tree-sitter-python`: 0.23 → 0.25 - Updated `tree-sitter-go`: 0.23 → 0.25 - Updated `tree-sitter-c`: 0.23 → 0.24 ## v0.8.0 - **Tree-Sitter AST Integration** (feature-gated) - New `--signatures` flag: Replaces full file content with extracted function/class signatures — dramatically reduces token usage (~4K vs ~15K tokens per file) - New `--structure` flag: Appends a structural summary to each file (e.g., "6 functions, 2 structs, 1 impl block") - New `--truncate smart` mode: Prefers AST-boundary truncation when content needs truncating - Supports 8 languages: Rust, JavaScript, TypeScript, Python, Go, Java, C, C++ - Install with: `cargo install context-builder --features tree-sitter-all` - Individual language features available (e.g., `--features tree-sitter-rust`) - **Dependency Updates** - Updated `tree-sitter` core: 0.22 → 0.24 - Updated all grammar crates: 0.21 → 0.23 - Migrated from deprecated `language()` functions to `LANGUAGE` constants API - **Bug Fixes** - Fixed config hash mismatch — cache now includes `auto_diff` and `diff_context_lines` fields, preventing stale cache hits when toggling these options - Fixed silent config parse failure — `context-builder.toml` with invalid TOML syntax now prints a warning instead of silently falling back to defaults - Fixed smart truncation unconditionally cutting 50% of file content — now only activates with explicit token budget - Fixed Windows path separators in determinism test causing CI failure - **CI & Quality** - Added Coveralls code coverage integration via `cargo-tarpaulin` - All 188+ tests passing across Ubuntu, macOS, and Windows ## v0.7.1 - **Bug Fixes** (identified by Gemini Deep Think multi-round code review) - Fixed content hash using absolute OS paths — now normalized to relative unix-style for cross-platform determinism - Fixed hash collision risk — added null byte delimiter between path and content in content hash - Fixed `strip_prefix('+')` leaving extra space in diff_only mode, corrupting indentation - Fixed auto_diff path bypassing `--max-tokens` budget entirely - Fixed `src/tests/` files misclassified as source code instead of tests - Fixed `sorted_paths` missing cwd fallback, silently dropping files when cwd ≠ base_path - **Auto-Ignore Common Directories** - 19 heavy directories (node_modules, dist, build, __pycache__, .venv, vendor, etc.) are now excluded by default - Prevents million-line outputs when processing projects without a `.git` directory - **Context Window Warnings** - Shows estimated token count after every run - Warns when output exceeds 128K tokens with actionable CLI suggestions ## v0.7.0 - **Deterministic Output** - Replaced volatile timestamp (`Processed at: `) with a content hash (`Content hash: `) in the Markdown header - Identical project states now produce byte-for-byte identical output files, enabling LLM prompt caching - **Context Budgeting (`--max-tokens N`)** - New CLI argument `--max-tokens` and `context-builder.toml` config option to cap the output token budget - Files are processed until the budget is exhausted, with a `` marker appended - Prevents API errors from excessively large contexts and reduces costs - **Relevance-Based File Ordering** - Files are now sorted by relevance category: config files (0) → source code (1) → tests (2) → docs/other (3) - Within each category, files remain alphabetically sorted - Helps LLMs prioritize core logic and configuration over supporting files ## v0.6.1 - **Bug Fixes** (identified by Gemini Deep Think code review) - Fixed TOCTOU race in cache writes: `File::create` was truncating before acquiring lock, risking data loss for concurrent readers - Fixed indentation destruction in `diff_only` mode: `trim_start()` was stripping all leading whitespace from added files, corrupting Python/YAML - Fixed UTF-8 boundary corruption: 8KB sniff buffer could split multi-byte characters, misclassifying valid UTF-8 files as binary - Fixed CLI flags silently overwritten: config file values were unconditionally overriding CLI arguments post-resolution - Removed duplicate file seek block (copy-paste error) ## v0.6.0 - **Smart Defaults** - Auto-exclude output files: the tool now automatically excludes its own generated output file, output folder, and `.context-builder/` cache directory from context collection without requiring manual `--ignore` flags - Timestamped output glob patterns (e.g., `docs/context_*.md`) are auto-excluded when `timestamped_output` is enabled - Large-file detection: warns about files exceeding 100 KB with a sorted top-5 list and total context size summary - Improved project name detection: canonicalizes relative paths (like `.`) to resolve the actual directory name instead of showing "unknown" - **Testing & Stability** - Added `#[serial]` annotations to integration tests that mutate CWD, fixing intermittent test failures in parallel execution - All 146 tests pass consistently with `--test-threads=1` - **Dependencies** - Updated `criterion` to 0.8.2 - Updated `tiktoken-rs` to 0.9.1 - Updated `toml` to 1.0.1 ## v0.5.2 - Enhanced `--init` command to detect major file types in the current directory and suggest appropriate filters instead of using generic defaults - Fixed file type detection to respect .gitignore patterns and common ignore directories (target, node_modules, etc.) ## v0.5.1 - Added `--init` command to create a new `context-builder.toml` configuration file in the current directory with sensible defaults ## v0.5.0 - **BREAKING CHANGES** - Cache file locations changed to project-specific paths to prevent collisions - **Critical Bug Fixes** - **Fixed inverted ignore logic**: Corrected critical bug where ignore patterns were being treated as include patterns, causing files/directories meant to be ignored to be explicitly included instead - **Fixed cache read panics**: Improved error handling for corrupted cache files to prevent application crashes - **Fixed potential panics in path manipulation**: Added safe handling for edge case filenames without extensions or stems - **Major Improvements** - **Deterministic Output**: Files are now sorted consistently, ensuring identical output for the same input across multiple runs - **Robust Caching Architecture**: Complete rewrite of caching system with: - Project-specific cache keys based on absolute path hash to prevent collisions - JSON-based structured caching replacing fragile markdown parsing - File locking with `fs2` crate for thread-safe concurrent access - Configuration changes now properly invalidate cache - **Enhanced Auto-Diff System**: - Structured state representation before markdown generation - Eliminated fragile text parsing with `extract_file_contents` and `strip_line_number` functions - Cache structured data (JSON) instead of markdown for reliability - **Thread Safety**: Removed all `unsafe` blocks and explicit configuration passing replaces environment variables - **Performance Optimizations** - **Custom Ignores**: Now uses `ignore::overrides::OverrideBuilder` with glob pattern support for better performance - **Parallel Processing**: Improved error handling to collect all errors and continue processing other files - **Directory Traversal**: Let `ignore` crate optimize directory traversal instead of custom logic - **Bug Fixes** - Fixed non-deterministic output order that caused inconsistent LLM context generation - Removed incorrect triple-backtick filtering in diff logic that was corrupting file content - Fixed cache corruption issues in concurrent access scenarios - Improved error recovery for partial failures and corrupted cache - Fixed inconsistent file tree visualization between auto-diff and standard modes - **Testing & Quality** - Added comprehensive integration test suite with tests covering: - Determinism verification - Auto-diff workflows - Cache collision prevention - Configuration change detection - Error recovery scenarios - Fixed test race conditions by running tests serially in CI (`--test-threads=1`) - Added `pretty_assertions` for better test output - Fixed all clippy warnings and enforced `-D warnings` in CI - **Dependencies** - Added `fs2` for file locking - Added `serde_json` for structured cache format - Added `serial_test` for test serialization - Added `pretty_assertions` for enhanced test output - Added `encoding_rs` for enhanced encoding detection and transcoding - **Migration** - Automatic detection and cleanup of old markdown-based cache files (`last_canonical.md`, etc.) - First run after upgrade will clear old cache format to prevent conflicts - CLI interface remains fully backward compatible - **Code Quality & Maintenance** - Fixed all clippy warnings including type complexity, collapsible if statements, and redundant closures - Updated CI workflow to prevent race conditions in tests - Improved binary file detection with better encoding strategy handling - Enhanced error handling for edge cases and file system operations ## v0.4.0 - Added - Token count mode (`--token-count`) now provides accurate token counts using the `tiktoken-rs` library. - Configuration file support (`context-builder.toml`) for project-specific settings. - Timestamped output versions. - `auto_diff` feature to automatically generate a diff from the latest output. - `diff_only` mode (`--diff-only` / `diff_only = true`) to output only the change summary and modified file diffs (no full file bodies) for lower token usage. - Removed - Deprecated, unpublished `standalone_snapshot` option (replaced by `diff_only`). ## v0.3.0 - Changed - Parallel processing is now enabled by default via the `parallel` feature (uses `rayon`) for significant speedups on large projects. - To build/run sequentially, disable default features: - CLI/build: `cargo build --no-default-features` or `cargo run --no-default-features` - As a dependency: `default-features = false` - Updated Rust edition to 2024. - Benchmarks - Benchmarks run silent by default by setting `CB_SILENT=1` at startup to avoid skewing timings with console I/O. - Override with `CB_SILENT=0` if you want to see output during benches. ## v0.2.0 - Added line numbers support - Improved file tree visualization - Enhanced error handling - Better CLI argument validation ## v0.1.0 - Initial release - Basic directory processing - File filtering and ignoring - Markdown output generation