跳到正文
Hacker News· ob4cl·· 6 天前AI 评分37

ConfigLens:一款用于排查 AI 编程工具为何忽略配置的 Rust CLI

ConfigLens: Figure out why your AI tool is ignore your settings

AI 导读

ConfigLens 是一款本地只读的 Rust CLI,可追踪 Claude Code 和 OpenCode 的配置键,展示实际生效的值及其来源文件、行号和原因,包括被覆盖的配置项。

正文 · 原文

ConfigLens

Website & interactive demo · Releases · Documentation

Find out why your AI coding tool is ignoring your configuration.

ConfigLens is a small, local, read-only Rust CLI that traces a supported configuration key for Claude Code and OpenCode across supported configuration sources, and shows the observed winner — with the file, line and reason for each contribution, including the ones that lost.

It never executes your configuration, never runs a client binary, opens no socket and reads no credential. Values are redacted unless an adapter explicitly allowlists them.

Pre-release, 0.1.0-beta.3. A working prototype, not a finished product: both adapters are fixture-verified rather than runtime-conformant, and unreadable layers are reported as gaps instead of guessed. Status says exactly what that means.

Install

Prebuilt binaries are attached to every release. Three targets ship today:

Platform Release archive
Windows (x86-64) configlens-<tag>-x86_64-pc-windows-msvc.zip
macOS (Apple Silicon) configlens-<tag>-aarch64-apple-darwin.tar.gz
Linux (x86-64, glibc) configlens-<tag>-x86_64-unknown-linux-gnu.tar.gz

Each release also carries a SHA256SUMS file and a CycloneDX SBOM per target. The binaries are unsigned and there is no installer or package manager; replace <tag> below with the release you want (currently v0.1.0-beta.3).

Verify the download

Linux and macOS:

tag=v0.1.0-beta.3
target=x86_64-unknown-linux-gnu              # or aarch64-apple-darwin
base=https://github.com/ob4cl/configlens/releases/download/$tag
curl -LO $base/configlens-$tag-$target.tar.gz
curl -LO $base/SHA256SUMS
sha256sum -c --ignore-missing SHA256SUMS      # macOS: use shasum -a 256 -c
tar xzf configlens-$tag-$target.tar.gz
./configlens-$tag-$target/configlens --version

Windows (PowerShell):

$tag  = 'v0.1.0-beta.3'
$repo = 'ob4cl/configlens'
# With the GitHub CLI:
gh release download $tag --repo $repo --pattern '*windows-msvc.zip'
gh release download $tag --repo $repo --pattern 'SHA256SUMS'
# Without it: download both files from the release page in a browser.

$zip      = Get-ChildItem *windows-msvc.zip | Select-Object -First 1
$expected = (Get-Content .\SHA256SUMS | Select-String $zip.Name).Line.Split()[0]
$actual   = (Get-FileHash -Algorithm SHA256 $zip.FullName).Hash.ToLower()
if ($actual -ne $expected) { throw 'SHA256 mismatch - do not run this file' }
Expand-Archive $zip.FullName -DestinationPath unpacked
& .\unpacked\configlens-*\configlens.exe --version

Expect configlens 0.1.0-beta.3. On macOS the archive is not notarized, so Gatekeeper warns on first run: right-click → Open, or xattr -d com.apple.quarantine <binary>.

Install from source

Any platform with Rust 1.85 or newer:

cargo install --locked --git https://github.com/ob4cl/configlens

Or from a checkout: cargo build --locked --release, which leaves the binary at target/release/configlens.

Not available yet: macOS Intel, Linux arm64, crates.io, Homebrew, Scoop/winget. There is no mandatory curl-to-shell installer, and nothing here contacts the network at run time.

Your first command

configlens explain /model --client claude --client-version 2.1.296

That prints the observed winner for model with the file, line and reason for every contribution. A real terminal example shows the full output and how to read it.

The question this answers

You set a model, a permission, an MCP flag. The tool ignores it. You now have five plausible files, an environment variable, a session flag and a managed policy that might all be involved — and the tool just says "using the other one". ConfigLens answers the four questions that follow:

  • Which value won? The observed winner among everything it could read.
  • Where did each value come from? File, line and column for every contribution, in precedence order, including the definitions that were replaced or skipped.
  • Which file should I edit? The winning source is named, so you edit the right one.
  • Can I paste this into an issue? Reports redact protected values, and --paths share reduces identifying details; configlens support-report --preview builds a share-mode report for exactly this. Review the output and the privacy limitations below before sharing; a redacted report is not a guarantee of anonymity.

Quick start (from source)

Until a package is published, build from this checkout. No client needs to be installed, and no network access is needed at run time.

cargo build --locked --release
./target/release/configlens --help

Prefer to install the binary into your PATH from the checkout:

cargo install --locked --path .

Run the isolated demo

demo/demo.sh reads no real configuration. It creates a throwaway home and project under a temporary directory, writes only obviously-fake values, clears the environment, and runs the CLI against that sandbox. Nothing outside the temporary directory is written, and no network or client subprocess is used.

cargo build --locked --release
./demo/demo.sh

A real terminal example

Captured from the isolated demo above (synthetic values only). The long coverage line and the two trailing trust_unknown diagnostics are elided — the tool prints every gap and every diagnostic.

$ configlens explain /model --client claude --client-version 2.1.296
/model = "local-model" (observed)
Observed winner: $PROJECT/.claude/settings.local.json:1:12  "local-model"  observed_winner
Losers:
  "project-model"  $PROJECT/.claude/settings.json:2:12  replaced_by_later_layer
Rule: claude.scalar.layer-override
Coverage: partial — cc.managed.hkcu — registry_not_read (expected_unobservable); … (3 more gaps)
Support: fixture_verified; confidence: evidence_class:fixture_verified
Conclusion: "local-model" wins among the observed applicable sources; effective value withheld.
Next inspection:
  - supply the managed settings file with --input-context to include it
  - remote/MDM layers are not observable here; re-run where the managed source is readable

Read it as: the project file set model to "project-model", a later layer replaced it with "local-model", and "local-model" is what ConfigLens observed to win. It deliberately does not call that the runtime value, because a higher layer it cannot read might still change it — see bounded semantics.

Commands

configlens <command> [options]
Command Example What it does
clients configlens clients List implemented adapters with their tested versions, the layers that are always incomplete, and the clients explicitly unsupported.
scan configlens scan --client claude Inventory candidate sources with applicability and readability, plus coverage gaps. No recursive crawl.
explain configlens explain /model --client claude --client-version 2.1.296 Explain one RFC 6901 JSON Pointer: the ordered trace, the observed winner (or an unresolved result) and coverage gaps.
doctor configlens doctor --client opencode --format json --strict Parse / schema / applicability / coverage diagnostics. Never certifies a configuration as safe.
snapshot configlens snapshot --client claude --output before.json Write the redacted report JSON to an explicit path. Create-new by default; --overwrite to replace.
diff configlens diff before.json after.json Compare two reports from the same client and schema; protected values compare as unknown_redacted.
schema configlens schema --kind report Print the bundled report JSON Schema. No network.
support-report configlens support-report --preview Build a share-mode report for pasting into an issue. Always share mode (regardless of --paths). --preview prints it to stdout and writes nothing; --output <PATH> writes it (0600 on Unix). There is no upload.
completions configlens completions --shell bash Print a shell completion script for bash, zsh or fish, generated from the live command tree. Nothing is written to disk.

Run configlens --help (and configlens <command> --help) for the current, authoritative list — the table above covers the core commands.

Options

--client <opencode|claude> is required for resolution. Shared options: --cwd <PATH> (default .), --client-version <VERSION> (a claimed version — ConfigLens never reads a client binary), --format text|json, --strict, --no-env, --input-context <PATH>, --color auto|always|never, --paths local|share, --allow-read-root <PATH> (repeatable), --project-root <PATH>. In addition, a global --quiet (accepted on every subcommand, as configlens <cmd> --quiet) suppresses informational stderr notes only — the snapshot wrote N bytes line and the support-report write/--preview notes. Errors are always printed to stderr, stdout stays machine-readable (JSON), and exit codes are unchanged. snapshot adds --output/--overwrite; doctor adds --fail-on warning|error; support-report takes --preview or --output <PATH>; supplying neither is a usage error (exit 2), and if both are given --preview wins and nothing is written; completions takes --shell bash|zsh|fish.

Exit codes

Code Meaning
0 Report produced. Non-fatal findings are allowed.
1 doctor findings at or above --fail-on (default error).
2 Usage error or incompatible input/report.
3 Requested key/client semantics unsupported, or --strict found an incomplete context.
4 A required file was inaccessible, malformed or over a resource limit.

When several apply, the priority is 2 → 4 → 3 → 1 → 0.

What "wins" actually means

Three terms carry the weight in every report:

  • observed winner — the highest-precedence definition among the sources ConfigLens could read.
  • fixture_verified — the rule was checked against this project's fixtures and upstream documentation, not against a running client.
  • coverage gap — a source that exists but could not be read, so the effective value is withheld.

ConfigLens is a static analyser of the inputs it can read. It does not inspect a running session and it never assumes an unreadable layer is absent. That has three visible consequences:

  • Observed, not effective. When a higher-priority layer exists but is unobservable (a server-managed policy, an MDM/registry setting, OpenCode remote defaults) — or the project trust state is unknown — the result is marked partial, the winner is labelled an observed winner, and the effective value is withheld alongside the list of coverage gaps.
  • Inventory, not guessing. An unknown key is preserved as opaque inventory and never given an invented merge rule. An unknown or untested client version is reported provisionally; --strict then exits 3.
  • Provenance, not authorization. For Claude permission lists, ConfigLens shows how list entries combine and prints an explicit caveat that this is not an authorization decision.

So exit code 0 means "a report was produced", not "this is the value your tool will use".

Compatibility

Two bounded adapters ship today. Both are fixture_verified at most — verified against the project's own fixtures and the current upstream documentation, not against the upstream loaders, so no rule claims upstream_conformance_verified. Support states run inventory_only < documented < fixture_verified < upstream_conformance_verified.

Client Adapter revision Tested / documented version Max support Always-incomplete layers
OpenCode 0.1.0 1.18.35 tested / 1.19.0 documented fixture_verified remote defaults, .opencode directories, macOS managed preferences
Claude Code 0.1.0 2.1.296 fixture_verified MDM policy store, server-managed settings, HKCU registry, managed-file-absent

Per-key detail, evidence URLs, retrieval dates and rule→test IDs live in the adapters' registries: compatibility/opencode/registry.md and compatibility/claude/registry.md.

OpenCode's @opencode/cli 2.x line is a separate beta with a different documented merge model (directory walk to the filesystem root; every .opencode config overriding every direct config). It is not implemented; a claimed version outside the supported sets is reported unsupported rather than resolved on a guess. See the adapter registry.

These clients are not implemented and are labelled unsupported — no compatibility claim is made from shared file names: Codex and Gemini (0.2 backlog), Aider (backlog), and Cursor, Copilot CLI, Cline, Roo and Hermes (not investigated). Run configlens clients for the current matrix.

Privacy and caveats

  • Offline and read-only. No network, no subprocess and no client-binary execution; a test scans src/ to keep it that way. $schema references are never fetched.
  • Redact unless allowlisted. Only the redaction projection produces reportable values; secrets appear as {"redacted": true, "reason": "..."}.
  • Two path modes. --paths local (default) shows config locations and key names so you can diagnose; --paths share pseudonymizes paths, replaces unknown key names with opaque IDs, and strips URL userinfo, query and fragment.
  • Snapshots. Written create-new, atomically, with 0600 owner-only permissions on Unix. On Windows there is no 0600 equivalent: the snapshot is written as a same-directory temporary file and then renamed, so it inherits the ACL of the destination folder — ConfigLens cannot tighten it. The report states this (redaction.snapshot_acl = "not_implemented") rather than claiming a protection that does not exist.
  • Known 0.1 gap / Windows guidance. Windows snapshot ACL restriction is not implemented. A restrictive DACL would need Win32 security APIs through unsafe FFI or a new dependency, both ruled out by this crate's unsafe_code = "forbid" and its minimal-dependency policy, so it is out of scope for 0.1. Save snapshots in a private, user-controlled directory (for example %USERPROFILE%, not a shared or world-readable folder) so the inherited ACL is the one you mean.
  • Reports still describe your project structure. Never paste raw credential files into an issue.

Status

0.1.0-beta.3, pre-release. The honest summary:

  • Two adapters ship — Claude Code and OpenCode — both at most fixture_verified. No rule claims upstream conformance, because no upstream loader is executed in the tests.
  • Some layers are always reported as coverage gaps rather than guessed; they are listed per client in Compatibility.
  • Windows snapshot ACL restriction is not implemented, and the report says so rather than implying a protection that does not exist.
  • The engine is quadratic in key count; real configurations have tens of keys, where it is sub-millisecond. The measured scaling is in docs/implementation-status.md.
  • What is implemented, verified and not verified is tracked item by item in docs/implementation-status.md, with the commands actually run in docs/verification-log.md.

Requirements

Rust, edition 2021. rust-version = "1.85" is declared in Cargo.toml and verified: cargo +1.85.0 check --locked --all-targets --all-features exits 0 (§8.1 of docs/verification-log.md), and the CI msrv job runs the same check. Local development used 1.99.0. See the verification log for the exact commands and results.

Contributing

Good first contributions are synthetic fixtures, clearer diagnostics and version-specific rule evidence. Every adapter change needs a primary source, a declared compatibility scope and positive/negative tests — and never a real secret or proprietary instruction. The development checks are:

cargo fmt --all -- --check
cargo clippy --locked --all-targets --all-features -- -D warnings
cargo test --locked --no-fail-fast
python -m unittest discover -s scripts -p "test_*.py"

Run these locally before opening a pull request. See CONTRIBUTING.md for the regression workflow and docs/maintaining.md for the maintainer-side process. Report security issues as described in SECURITY.md.

Documentation

License

Licensed under either of

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

来源:Hacker News · github.com