* fix(proxy): skip vendor trees in repo intel
BM25 was indexing .pixi/envs and other dependency
dirs, then injecting those file:line guesses every
turn. Exclude known vendor trees and drop the path
block when no local scout is configured.
Closes#899
Co-authored-by: Julius Brussee <JuliusBrussee@users.noreply.github.com>
* fix(proxy): detect dep trees by content, not name
Name denylist missed conda envs, undotted pixi, and
virtualenvs under any name, while dropping real
first-party build/ and vendor/ dirs.
- listFiles prefers `git ls-files --exclude-standard`,
so the project's own .gitignore defines vendored
- walk fallback identifies environments by contents
(conda-meta, pyvenv.cfg, site-packages child),
catching the interpreter stdlib they carry
- ambiguous build/dist/target/vendor need a sibling
manifest before exclusion
- symlink and junction dirs are never followed
Refs #899
Co-authored-by: Julius Brussee <JuliusBrussee@users.noreply.github.com>
* fix(proxy): treat the indexed repo as untrusted input
Second review found the git listing both unsafe and
incomplete.
- gitCommand pins core.fsmonitor and other exec
vectors off and strips GIT_*; fsmonitor runs during
an index read, so `ls-files` in a hostile clone ran
attacker code at session start
- git-listed paths now run the same content filter as
the walk, so an un-ignored conda prefix or committed
vendor tree no longer rides in on git's say-so
- an empty-but-successful listing means the cwd is
ignored by an enclosing repo; fall back to the walk
instead of reporting a complete map
- Direct matches the file name, not the whole path:
a term like "src" marked every file beneath it
- ListingBasis discloses which path ran, hashed in
Refs #899
Co-authored-by: Julius Brussee <JuliusBrussee@users.noreply.github.com>
* fix(proxy): log declined evidence, document invariants
A declined turn stays silent to the model but now
records repository_evidence_declined_no_direct_match,
so weak evidence is distinguishable from a broken or
disabled mechanism.
Adds a positive control to the suppression test and
records the gating, filtering, and git-hardening
invariants in proxy/CLAUDE.md.
Refs #899
Co-authored-by: Julius Brussee <JuliusBrussee@users.noreply.github.com>
* fix(installer): make prune path flavour explicit, not host-derived
pruneOrphanedManagedHooks guards against pruning a hook whose path is
absolute on the other platform. That guard is POSIX-only by construction:
win32's absolute set is a superset of posix's, so a Windows host reads
C:\... natively and prunes it. The test asserting the guard hard-coded a
Windows path and so failed on the windows CI runner.
Select the path flavour from an explicit platform arg (default
process.platform, same as rewriteLegacyManagedHookCommands) so the POSIX
side stays reachable from a Windows runner. Host behaviour unchanged.
Co-authored-by: Julius Brussee <JuliusBrussee@users.noreply.github.com>
* test(cli): escape stub preload path in NODE_OPTIONS
Node parses NODE_OPTIONS itself and treats a backslash inside a quoted
value as an escape, so the raw Windows path reached --require with every
separator eaten:
Cannot find module 'C:UsersRUNNER~1AppDataLocalTempcave-pi-enableXbinstub-dispatch.cjs'
Every stub-backed process died in the preload, failing all six pi enable
and wrap round trips. The quotes must stay for profile names with spaces,
so double the separators instead. POSIX is unaffected — stubEnv returns
before this on non-Windows.
Never caught before because npm test failed earlier in the same job, so
test:windows has not run since these tests joined the gate.
Co-authored-by: Julius Brussee <JuliusBrussee@users.noreply.github.com>
* fix(proxy): harden every untrusted git call, not just repo intelligence
`repointel` hardened its own `git ls-files` and left three other call sites
raw. `nativehook.currentRepositoryState` runs `git status` on EVERY hook
event, and `status` refreshes the index, so it triggers `core.fsmonitor`
harder than `ls-files` does — verified: the sentinel hook fires. Same for
`store/detect_outcomes` and the two `execFileSync` sites in the CLI.
- `proxy/internal/gitsafe` and `packages/cli/src/git-safe.ts` now own the
`-c` override list; all five call sites route through them.
- `verify_repo.py` fails the build on a NEW raw `git -C <dir>` call site,
which is the regression class this bug actually belongs to.
- `Bundle.DirectOnly()` strips metadata-only items BEFORE the evidence
object is written. Filtering only at render time left the BM25-proximity
guesses stored behind the `ccr://` handle, one retrieval from the model,
which is what the block refuses to render.
- `gitListBudget` caps git at half the caller's remaining warm budget. 2s
of a 3s window left the fallback walk — the MORE expensive path — unable
to finish, so the map failed closed in exactly the case the fallback
exists for.
- `NormalizeTerms` floors terms at 3 characters, matching the hook that
produces them: direct evidence is a substring test on a file's own name,
so `go`/`py`/`js` marked every file of that language a direct hit.
Regression tests verified to fail without their fix.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(build): stop wedge generation racing itself on Windows
mcp, shrink, and browse each run gen-wedge-installer from their own
prepack/pretest, and every run writes all four targets, not just its own.
`pnpm -r test` runs those packages in parallel, so several processes
copy onto the same file at once and Windows fails the loser:
EBUSY: resource busy or locked, copyfile
packages/shared/binary-installer/installer.mjs
-> mcp/bin/binary-installer.generated.mjs
The bytes are identical every time and the outputs are committed, so the
race was over writes that change nothing. Skip when the destination
already matches, and land a real change through a temp file + rename so
no concurrent reader sees a partial file. Verified with a 12-way
concurrent storm, and against corrupted outputs to prove the write path
still restores them.
Co-authored-by: Julius Brussee <JuliusBrussee@users.noreply.github.com>
* fix(proxy): keep system git config readable while hardening
GIT_CONFIG_NOSYSTEM defends against nothing here — /etc/gitconfig is
root-owned, the threat is the repository's own .git/config, and the `-c`
overrides beat every config level anyway. What it does cost is org-wide
`safe.directory` allowances: without them `git status` fails "dubious
ownership" on shared and CI machines, repository state comes back empty,
and reuse silently stops. Dropped from both wrappers, with a test so it
does not come back.
Also names the fsmonitor tradeoff at the hot call site: disabling it taxes
the people who configured it because their repo is huge, and status has a
100ms budget. That degrades safely; moving the budget is a latency
decision for every user and needs a measurement this fix does not have.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(build): pin wedge generation against the concurrency it now survives
The EBUSY fix has no committed regression proof, and the failure only
appears when several packages run the script at once — which is exactly
what `pnpm -r test` does and what a single-process check never reproduces.
Eight concurrent runs must all succeed and leave the shared outputs
byte-identical; a drifted output must still be repaired, so skip-if-
identical cannot regress into skip-always.
Runs in root `npm test`, which is the gate the Windows job failed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Julius Brussee <JuliusBrussee@users.noreply.github.com>
Co-authored-by: Julius Brussee <juliusbrussee@getlockedin.nl>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Improve Windows support across installer, CLI hooks, binary lookup, hidden child processes, JetBrains detection, and Chrome discovery. Add real Windows ACL checks for SQLite parent directories instead of skipping the security guard.
Also expand BSL-1.1 coverage/documentation for new engine-linked runtime modules (`cacheengine`, `rewriter`, `browse`) and add repo/CI checks so license boundaries and cross-platform test paths stay enforced.
Single-commit port of the Caveman 2 tree onto the original repo history
(candidate caveman-2@88878a4 applied over main@3098342). No unrelated-history
merge; original history untouched.
Preserved v1 surfaces:
- skills/caveman/SKILL.md byte-for-byte, plus plugin mirror and caveman.skill zip
- legacy CI (Node 18/20/22 installer + Python 3.11 suites) alongside engine-ci
- cli/install.js compatibility shim forwarding to bin/install.js
- .codex/config.toml ships both hooks and codex_hooks for older Codex (#617)
- README Trendshift + Skills.sh badges and local star-history asset
- v1.10.0-pinned curl installs; benchmarks/tmp-starcharts ignore rules
Fixed for public same-repo release:
- SECURITY.md vulnerability reporting points at this repository
- release-binaries.yml publishes to this repo via job-scoped GITHUB_TOKEN
(cross-repo PAT removed); binary-release environment still gates signing
- packages/cli/PUBLISHING.md rewritten for packages/ layout and same-repo flow
- profile-contract and provider-catalog lanes skip lane-scope gates on
initial import so the launch PR can gate on their real validation steps
- ANNOUNCEMENT.md network claim corrected: compression is local; requests
still go to the provider you configured
Caveman 2 stays opt-in: bin-v1.0.0 binaries and @caveman-ai/cli publish
separately after this lands and soaks.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BLvb5QniwYjmcawyssQNw9