Skip to content

feat: Antigravity + Cursor CLI support, cross-platform & secret-safety fixes (v0.2.0)#7

Merged
Fafoooo merged 5 commits into
mainfrom
feat/multi-tool-sync-and-fixes
Jul 2, 2026
Merged

feat: Antigravity + Cursor CLI support, cross-platform & secret-safety fixes (v0.2.0)#7
Fafoooo merged 5 commits into
mainfrom
feat/multi-tool-sync-and-fixes

Conversation

@Fafoooo

@Fafoooo Fafoooo commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Summary

Prepares the v0.2.0 release. This branch lands on top of the two merged PRs (#5 opencode/Copilot/Kilo adapters, #6 community-health files) and adds two more tools, fixes the cross-platform bugs the PR #5 review surfaced, and hardens secret handling.

New tool support

  • Google Antigravity — a three-root, VS Code-fork adapter:
    • VS Code User dir (settings.json, keybindings.json, snippets/) → per-OS (~/Library/Application Support · ~/.config · %APPDATA%)
    • ~/.antigravity/extensions/extensions.json (extension list)
    • the shared ~/.gemini agent config (GEMINI.md, settings.json, antigravity/mcp_config.json, skills/)
    • Secret safety: capture is allowlist-only and the denylist hard-excludes the Google OAuth tokens that live in ~/.gemini (oauth_creds.json, google_accounts.json, *.pb, history/). A test asserts the OAuth token reaches no captured file.
  • Cursor CLI (cursor-agent) — it shares ~/.cursor with the IDE, so rather than a duplicate adapter (which would double-capture mcp.json/skills), the existing Cursor adapter is widened to also capture cli-config.json + global commands/ + rules/, honor CURSOR_CONFIG_DIR, and exclude runtime worktrees/.

Cross-platform fixes (verified against official docs)

  • Windows: opencode + Kilo config now resolve to ~/.config/<tool> (XDG on every OS) instead of %APPDATA%\<tool>. The old mapping made detect() report the tool absent and restore write where the CLI never reads — the exact mac→Windows breakage this repo is meant to prevent.
  • Copilot: freeze the real user config (settings.json, mcp-config.json, lsp-config.json, copilot-instructions.md, instructions/, agents/, hooks/) and hard-deny config.json, which per GitHub's config-dir reference is auto-managed auth/plugin state — restoring a stale one would clobber the target's sign-in.
  • Kilo: also capture tui.json(c) (terminal-UI settings) so they round-trip.

Security hardening (from a review of the secret-handling paths)

  • Fixed (HIGH): the shared looksBinary heuristic base64'd any file containing a NUL byte straight into the repo, skipping the sanitizer entirely — even with includeSecrets=false. A UTF-16 config (PowerShell's default Out-File/Set-Content encoding on Windows) trips this, so a secret-bearing mcp_config.json/settings.json could have shipped unredacted. New decodeForCapture() decodes UTF-16 to text so it's sanitized across all four capture sites (antigravity, opencode/Copilot/Kilo, cursor, claude), with a fail-safe that drops any "binary" file containing secret-shaped bytes. Regression-tested with a UTF-16LE config.
  • Verified airtight: Antigravity's ~/.gemini OAuth files (oauth_creds.json, google_accounts.json, *.pb) are excluded by the allowlist (never opened) and the denylist; the review confirmed no directory-walk or symlink path can reach them, and a test asserts the OAuth token reaches no captured file.
  • Documented follow-ups (LOW, pre-existing, defense-in-depth only): captured symlink target strings are stored unsanitized (path/username metadata, not secret content); hook/agent scripts get pattern-based (not structural) redaction; denylist matching is case-sensitive. None is a live leak given the allowlist.

Test plan

  • npm run typecheck — clean
  • npm run test202 passed (22 files; +27 new)
  • npm run build + npm pack --dry-run — clean (v0.2.0, 5 files)
  • npm audit --omit=dev — 0 vulnerabilities (shipped deps)
  • CLI smoke: --help, setup/status/push/pull/auth/secrets --help, --version
  • New tests: Antigravity capture/restore + OAuth-never-captured; Copilot config-in/auth-out; Kilo TUI; Windows-path regression (faked win32); per-OS Antigravity User-dir; UTF-16 config sanitized-not-shipped-raw
  • Manual end-to-end push/pull on a real machine per OS (recommended before tagging the release)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added Google Antigravity as a first-class supported tool across init/config validation, setup, backup, restore, and status.
    • Expanded tool capture coverage for Cursor (CLI rules/config), Copilot (portable settings/hooks), and Kilo (TUI settings).
  • Bug Fixes
    • Improved file capture decoding for text vs binary (including UTF-16 scenarios) and safer binary handling when secrets are disabled.
    • Enhanced secret redaction for env/environment/headers-style containers.
  • Documentation
    • Updated README and package metadata to reflect the broader supported toolset.
  • Tests
    • Added/expanded unit tests for Antigravity and decoding/redaction edge cases.

Fafoooo and others added 2 commits July 2, 2026 00:19
…r bugs

Prepares the v0.2.0 release. Builds on the just-merged opencode/GitHub Copilot
CLI/Kilo Code adapters (PR #5) and community-health files (PR #6).

New tool support
- Antigravity (Google): a three-root IDE adapter — the VS Code-style User dir
  (settings/keybindings/snippets), the ~/.antigravity extension list, and the
  shared ~/.gemini agent config (GEMINI.md, settings.json, MCP servers, skills).
  Capture is allowlist-based and the denylist hard-excludes the Google OAuth
  tokens that live in ~/.gemini (oauth_creds.json, google_accounts.json, *.pb,
  history) so no credential can reach the repo by construction.
- Cursor CLI (cursor-agent): it SHARES ~/.cursor with the IDE, so instead of a
  duplicate adapter the existing Cursor adapter is widened to also capture
  cli-config.json + global commands/ + rules/, honor CURSOR_CONFIG_DIR, and
  exclude the CLI's runtime worktrees.

Cross-platform fixes (from the PR #5 review, verified against official docs)
- Windows: opencode + Kilo config now resolves to ~/.config/<tool> (XDG on every
  OS) instead of %APPDATA%\<tool>. The old mapping made detect() report the tool
  absent and restore write where the CLI never reads.
- Copilot: freeze the real user config (settings.json, mcp-config.json,
  lsp-config.json, copilot-instructions.md, instructions/, agents/, hooks/) and
  hard-deny config.json — it is auto-managed auth/plugin state, and restoring a
  stale one would clobber the target machine's sign-in.
- Kilo: also capture tui.json(c) (terminal-UI settings) so they round-trip.

Tests: +25 (200 total). Adds Antigravity capture/restore + secret-safety (the
OAuth token reaches no captured file), Copilot config-in / auth-out, Kilo TUI,
a Windows-path regression (faked win32), and per-OS Antigravity User-dir
resolution (macOS Library / Linux .config / Windows APPDATA).

Antigravity is wired through the full stack: types + TOOL_IDS, adapter registry,
os.ts (toolHomeDir/installCommandFor/cliBinaryName), platform/install
dependencies, config + manifest schemas, sanitizer denylist, secret-file map,
and every command (backup, status, restore incl. multi-root frozenRootsForTool,
init, setup, secrets).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACwssULEcxKCbB28zZRZ63
…dening)

A security review found a HIGH-severity gap: the shared "contains a NUL byte =>
binary" heuristic base64'd a file straight into the repo, skipping the sanitizer
entirely — even with includeSecrets=false. A UTF-16-encoded JSON config
(PowerShell's default Out-File/Set-Content encoding on Windows) packs a 0x00
after every ASCII char, so a secret-bearing settings.json / mcp_config.json would
have been committed unredacted.

- Add src/utils/capture-bytes.ts: decodeForCapture() detects UTF-16 (by BOM or a
  NUL-interleave heuristic) and decodes it to text so the normal sanitize +
  template path always runs; genuinely-binary content is returned as binary plus
  a lossy UTF-8 view for a fail-safe scan.
- Route all four capture sites through it (antigravity, shared/configDir for
  opencode/Copilot/Kilo, cursor, claude). In the binary branch, when not opted
  into carrying secrets, DROP any file whose bytes contain secret-shaped content
  (with a warning) rather than storing it raw.
- Test: a UTF-16LE (no-BOM) opencode.json carrying an MCP API key is captured as
  sanitized TEXT with the secret redacted, and the token reaches no file.

202 tests pass. The review's remaining LOW items (unsanitized symlink-target
strings, heuristic-only redaction of hook scripts, case-sensitive denylist) are
pre-existing, defense-in-depth-only, and left as documented follow-ups — the
allowlist remains the primary guarantee for the OAuth files.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACwssULEcxKCbB28zZRZ63
Copilot AI review requested due to automatic review settings July 1, 2026 22:36
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 961d9793-188c-41f5-a4a4-00098aa33c4a

📥 Commits

Reviewing files that changed from the base of the PR and between 9c560d7 and 9572891.

📒 Files selected for processing (15)
  • src/adapters/antigravity/index.ts
  • src/adapters/antigravity/paths.ts
  • src/adapters/claude/capture.ts
  • src/adapters/cursor/index.ts
  • src/adapters/shared/configDir.ts
  • src/commands/backup.ts
  • src/commands/restore.ts
  • src/core/sanitizer/index.ts
  • src/core/sanitizer/patterns.ts
  • src/utils/capture-bytes.ts
  • test/unit/antigravity-adapter.test.ts
  • test/unit/capture-bytes.test.ts
  • test/unit/configdir-adapters.test.ts
  • test/unit/cursor-cli-and-antigravity-wiring.test.ts
  • test/unit/sanitizer.test.ts
 ____________________________________________________________________________________________________________________________________________________________________
< Use blackboards to coordinate workflow. Use blackboards to coordinate disparate facts and agents, while maintaining independence and isolation among participants. >
 --------------------------------------------------------------------------------------------------------------------------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
📝 Walkthrough

Walkthrough

This PR adds a new Antigravity adapter (capture, restore, detect, install-check) with multi-root filesystem handling, wires the tool through registry, CLI commands, schemas, types, and platform/install logic, updates denylists and frozen-path allowlists for several adapters, and introduces a shared UTF-16-aware byte decoder replacing NUL-byte binary detection across capture pipelines.

Changes

Antigravity Tool Integration

Layer / File(s) Summary
Shared byte decoding utility
src/utils/capture-bytes.ts
New decodeForCapture classifies bytes as UTF-16/UTF-8 text or binary using BOM/NUL-bias heuristics.
Adopt decoder across capture pipelines
src/adapters/claude/capture.ts, src/adapters/cursor/index.ts, src/adapters/shared/configDir.ts, test/unit/configdir-adapters.test.ts
Existing capture flows replace the looksBinary heuristic with decodeForCapture, adding a secret-shaped-bytes safety scan for binary content.
ToolId and schema contracts
src/types.ts, src/core/config/schema.ts, src/core/manifest/schema.ts
ToolId, TOOL_IDS, and Zod schemas now accept "antigravity".
Platform, install, and denylist wiring
src/platform/os.ts, src/platform/install.ts, src/core/sanitizer/denylist.ts, src/core/secrets/index.ts
Adds antigravity tool-home resolution, non-installable dependency spec, ANTIGRAVITY_DENY list, and secret-file registry entry; also expands Cursor/Copilot denylists.
Antigravity paths module
src/adapters/antigravity/paths.ts
Defines the three Antigravity roots, repo prefixes, frozen-path allowlists, and derived user/gemini directory helpers.
Antigravity adapter core
src/adapters/antigravity/index.ts
Implements capture, restore (with dry-run planning), detect, and CLI-install-check logic with secret-safe handling.
CLI and registry wiring
src/adapters/registry.ts, src/commands/backup.ts, src/commands/restore.ts, src/commands/status.ts, src/commands/secrets.ts, src/commands/init.ts, src/commands/setup.ts
Integrates the antigravity adapter into detection, capture, restore, labels, and validation across CLI commands.
Expanded frozen-path coverage for other adapters
src/adapters/copilot/paths.ts, src/adapters/cursor/paths.ts, src/adapters/kilo/paths.ts
Adds additional portable config files/dirs to capture allowlists and removes config.json from Copilot's list.
Tests and docs/version bump
test/unit/antigravity-adapter.test.ts, test/unit/cursor-cli-and-antigravity-wiring.test.ts, test/unit/new-tool-platform.test.ts, README.md, package.json
Adds test coverage for new capture/restore paths and platform wiring, updates docs and version/keywords.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI as arbella backup CLI
  participant Adapter as antigravityAdapter
  participant FS as Filesystem Roots
  participant Sanitizer as Sanitizer
  participant Repo as Backup Repo

  CLI->>Adapter: capture(ctx)
  Adapter->>FS: check antigravity/user/gemini roots present
  FS-->>Adapter: root existence + frozen files
  Adapter->>Adapter: walkFrozen (denylist filter, symlinks, files)
  Adapter->>Sanitizer: sanitizeText / detect secret-shaped bytes
  Sanitizer-->>Adapter: redacted content + SecretRefs
  Adapter->>Adapter: template content with vars
  Adapter-->>Repo: CapturedFile entries (text/base64)
  Adapter-->>CLI: CaptureResult (files, warnings, secrets)
Loading
sequenceDiagram
  participant CLI as arbella pull CLI
  participant Adapter as antigravityAdapter
  participant Plan as planActions
  participant FS as Destination Roots

  CLI->>Adapter: restore(ctx, data)
  Adapter->>Plan: planActions(ctx, data) [dry-run]
  Plan->>FS: check existing destinations (sourceOfTruth=local)
  FS-->>Plan: existence status
  Plan-->>Adapter: RestoreAction[] (write/symlink/skip)
  Adapter->>FS: writeRestoredFile / writeRestoredSymlink
  FS-->>Adapter: write result or warning
  Adapter-->>CLI: restore complete
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: Antigravity and Cursor CLI support plus cross-platform and secret-safety fixes for v0.2.0.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/multi-tool-sync-and-fixes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9c560d7339

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/commands/backup.ts
Comment thread src/commands/restore.ts

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Prepares the v0.2.0 release by expanding adapter coverage (Antigravity + Cursor CLI), tightening cross-platform config-dir resolution (notably Windows), and hardening capture sanitization to prevent UTF‑16 text from bypassing secret redaction.

Changes:

  • Add Google Antigravity adapter (multi-root capture/restore) and widen Cursor adapter to include cursor-agent CLI config while excluding runtime worktrees.
  • Fix cross-platform home resolution for config-dir tools (XDG ~/.config on all OSes) and tighten Copilot capture to exclude config.json auth/internal state.
  • Replace NUL-byte “binary” heuristics with decodeForCapture() to correctly decode UTF‑16 as text for sanitization, plus a fail-safe secret scan for true binary blobs.

Reviewed changes

Copilot reviewed 29 out of 29 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/unit/new-tool-platform.test.ts Extends platform/path/denylist regression coverage (Antigravity tool id + Windows XDG config-dir behavior + Copilot hard-deny).
test/unit/cursor-cli-and-antigravity-wiring.test.ts Adds wiring tests for Cursor CLI frozen paths/denylist and Antigravity OS/home/install wiring.
test/unit/configdir-adapters.test.ts Adds capture tests for Copilot “user config in, auth out”, Kilo TUI config capture, and UTF‑16 sanitization regression.
test/unit/antigravity-adapter.test.ts Adds end-to-end unit tests for Antigravity multi-root capture/restore and OAuth “never captured” guarantees.
src/utils/capture-bytes.ts Introduces decodeForCapture() to classify/decode bytes (UTF‑16 vs binary) for safe sanitization.
src/types.ts Adds antigravity to ToolId and canonical TOOL_IDS order.
src/platform/os.ts Unifies XDG config-dir resolution across OSes; adds CURSOR_CONFIG_DIR support; wires Antigravity home/install/binary name.
src/platform/install.ts Registers Antigravity as a dependency with “none” install kind and download guidance.
src/core/secrets/index.ts Adds Antigravity entry for secrets guidance (no portable credential file).
src/core/sanitizer/denylist.ts Expands denylists (Cursor worktrees, Copilot auth/internal state, Antigravity OAuth/state exclusions).
src/core/manifest/schema.ts Extends manifest tool-id schema to include antigravity.
src/core/config/schema.ts Extends config tool-id schema to include antigravity.
src/commands/status.ts Wires Antigravity adapter into status command capture map.
src/commands/setup.ts Updates setup description to include Antigravity.
src/commands/secrets.ts Adds Antigravity label handling in secrets command output.
src/commands/restore.ts Wires Antigravity restore planning (multi-root) and adds re-auth reminder text.
src/commands/init.ts Adds Antigravity display name and CLI parsing support for the new ToolId.
src/commands/backup.ts Wires Antigravity into backup capture selection and labeling.
src/adapters/shared/configDir.ts Replaces NUL-byte binary heuristic with decodeForCapture() + binary fail-safe scan.
src/adapters/registry.ts Registers the Antigravity adapter in the adapter registry.
src/adapters/kilo/paths.ts Captures Kilo tui.json(c) alongside main config for round-trip portability.
src/adapters/cursor/paths.ts Widens Cursor frozen paths to include Cursor CLI config + global commands/rules.
src/adapters/cursor/index.ts Uses decodeForCapture() + binary fail-safe scan in Cursor capture logic.
src/adapters/copilot/paths.ts Switches Copilot frozen paths to user-authored config only (excluding config.json).
src/adapters/claude/capture.ts Uses decodeForCapture() + binary fail-safe scan in Claude capture logic.
src/adapters/antigravity/paths.ts Defines Antigravity’s three-root path model (home/User/.gemini) and allowlisted frozen paths.
src/adapters/antigravity/index.ts Implements Antigravity adapter (capture/restore/detect/install planActions).
README.md Updates supported tools and behavior descriptions (Cursor CLI + Antigravity + secret safety).
package.json Bumps version to 0.2.0 and updates package description/keywords for expanded tool support.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/utils/capture-bytes.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (4)
src/core/config/schema.ts (1)

16-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider deriving the Zod enum from TOOL_IDS to prevent drift.

toolIdSchema duplicates the tool-id literal list already defined in TOOL_IDS (src/types.ts). Nothing enforces these stay in sync — a future addition to one and not the other would only surface as a runtime validation mismatch, not a type error.

♻️ Possible refactor (derive from TOOL_IDS)
-const toolIdSchema = z.enum([
-  "claude",
-  "codex",
-  "cursor",
-  "opencode",
-  "copilot",
-  "kilo",
-  "antigravity",
-]);
+// TOOL_IDS is `readonly ToolId[]`; the cast is safe because TOOL_IDS is guaranteed
+// non-empty and exhaustive over ToolId.
+const toolIdSchema = z.enum(TOOL_IDS as unknown as [ToolId, ...ToolId[]]);

Same duplication exists in src/core/manifest/schema.ts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/config/schema.ts` around lines 16 - 24, The tool-id Zod enum is
duplicated in toolIdSchema instead of being derived from TOOL_IDS, which can
drift from the shared source of truth. Update toolIdSchema in the schema
definitions to build from TOOL_IDS from src/types.ts so the validation list
stays aligned with the typed constants, and apply the same refactor to the
matching schema in src/core/manifest/schema.ts.
src/adapters/antigravity/index.ts (3)

410-454: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

installCli's runInstall(cmd) branch appears to be dead code.

The docstring states installCommandFor returns null on every OS for antigravity, so the await runInstall(cmd) path (Line 453) is currently unreachable, and it's also not wrapped in try/catch — inconsistent with the "never throws" guarantee stated for this function if a future OS mapping ever returns a non-null command.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/adapters/antigravity/index.ts` around lines 410 - 454, The installCli
function contains an unreachable runInstall(cmd) branch because
installCommandFor("antigravity", os) is documented to return null on every OS,
so simplify the control flow around installCli and its cmd check to remove the
dead path. If you keep the fallback for future OS mappings, make the
runInstall(cmd) call explicitly guarded and wrapped so installCli still never
throws, and keep the behavior aligned with the existing detect, isCliInstalled,
and installCommandFor symbols.

254-297: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

capture()'s root-presence check doesn't mirror detect()'s stricter Gemini rule.

detect() explicitly documents that Plain ~/.gemini alone is NOT enough — that can be a Gemini-CLI-only install (see its own comment at Line 423). But capture()'s present filter (Line 264-266) treats the "gemini" root as present whenever ~/.gemini exists as a directory, regardless of whether home/user are present or whether the antigravity/ subtree exists inside it. If a user has only ever run the legacy/standalone Gemini CLI (no Antigravity), ~/.gemini will exist, and capture() will still walk GEMINI_FROZEN_PATHS from it — mislabeling that user's unrelated Gemini CLI config (GEMINI.md, settings.json) as Antigravity backup data under antigravity/gemini/* in the repo.

This isn't a secret leak (same allowlist/denylist still applies) but is a real correctness/labeling gap relative to the documented detection intent.

♻️ Align gemini-root presence with detect()'s stricter check
   const roots = captureRoots(ctx);
   const present: Array<{ root: Root; abs: string; frozen: readonly string[] }> = [];
   for (const r of roots) {
     if ((await ctx.fs.statKind(r.abs)) === "dir") present.push(r);
   }
+
+  // A bare ~/.gemini (Gemini-CLI-only install) alone should not count as
+  // "Antigravity present" — mirror detect()'s stricter check.
+  const hasAntigravitySignal =
+    present.some((r) => r.root === "home" || r.root === "user") ||
+    (await ctx.fs.statKind(path.join(geminiDir(ctx.toolHome), "antigravity"))) === "dir";
+  const finalPresent = present.filter((r) => r.root !== "gemini" || hasAntigravitySignal);

(and use finalPresent in place of present below)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/adapters/antigravity/index.ts` around lines 254 - 297, Align capture()
with detect() by tightening the gemini root presence check so plain ~/.gemini
alone does not count as Antigravity data; reuse the stricter logic already
documented in detect() when building the present roots in capture(). Update the
root filtering in capture() to only include the gemini root when the
Antigravity-specific subtree or equivalent supporting roots are present, and
then use the resulting finalPresent list for the walkFrozen loop and the
empty-result warning path.

126-134: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid the direct node:fs call in readMode

FsService has statKind, but no mode-aware stat API, so this helper has to bypass ctx.fs to preserve executable bits. Add a mode-preserving helper there and use it here so fixture/virtual filesystems stay injectable and tests can cover this path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/adapters/antigravity/index.ts` around lines 126 - 134, The readMode
helper is bypassing ctx.fs by importing node:fs directly, which makes the
filesystem path non-injectable and harder to test. Add a mode-preserving stat
helper to FsService (alongside statKind) that can return the file mode while
still working with fixture/virtual filesystems, then update readMode in
Antigravity’s index flow to call that FsService helper instead of node:fs. Keep
the executable-bit preservation behavior intact while routing all filesystem
access through the injected service.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/adapters/antigravity/paths.ts`:
- Around line 64-72: Update antigravityUserDir in paths.ts so it resolves the
User data directory under the Antigravity IDE application root instead of
Antigravity, since current installs place VS Code-style data there. Keep the
existing userHome/appUserConfigRoot logic, but change the final joined segment
in antigravityUserDir to match the Antigravity IDE install layout so
settings.json, keybindings.json, and snippets are found.

In `@src/adapters/kilo/paths.ts`:
- Around line 13-14: Fix the doc comment in the paths module by replacing the
nonsensical word “arbella” with the intended phrase “this adapter” in the
runtime-state boundary description. Update the comment near the path helper
symbols in src/adapters/kilo/paths.ts so it reads naturally and accurately
describes that this adapter never touches the ~/.kilocode/cli tree.

In `@src/utils/capture-bytes.ts`:
- Around line 77-84: The UTF-16 no-BOM heuristic in capture-bytes.ts is too
permissive and can misclassify tiny binary payloads as text. Update the logic
around the pairs/nulOdd/nulEven checks in the captureBytes flow to require a
minimum dominant-NUL count before either UTF-16 path is accepted, then keep the
existing DENSITY and RATIO checks as secondary gates. Use the existing
decodeUtf16be and utf16le branches, but add the new minimum-signal guard so
small inputs like single-byte or very short binaries do not decode as text.

In `@test/unit/configdir-adapters.test.ts`:
- Line 217: The test fixture in configdir-adapters.test.ts uses a contiguous
GitHub-token-shaped literal for COPILOT_TOKEN, which triggers secret scanners.
Update the test to construct the same value at runtime inside the relevant test
setup instead of storing it as one literal, and keep the existing constant name
and any assertions in the configdir-adapters.test.ts suite unchanged.
- Around line 346-350: The secret-leak assertion in the u16 capture test only
decodes binary payloads as UTF-8, so it can miss UTF-16LE secrets. Update the
check in the u16Capture.files loop to inspect binary captures using UTF-16
decoding as well as the existing text/base64 path, and keep the assertion in the
same test case that references U16_SECRET.

---

Nitpick comments:
In `@src/adapters/antigravity/index.ts`:
- Around line 410-454: The installCli function contains an unreachable
runInstall(cmd) branch because installCommandFor("antigravity", os) is
documented to return null on every OS, so simplify the control flow around
installCli and its cmd check to remove the dead path. If you keep the fallback
for future OS mappings, make the runInstall(cmd) call explicitly guarded and
wrapped so installCli still never throws, and keep the behavior aligned with the
existing detect, isCliInstalled, and installCommandFor symbols.
- Around line 254-297: Align capture() with detect() by tightening the gemini
root presence check so plain ~/.gemini alone does not count as Antigravity data;
reuse the stricter logic already documented in detect() when building the
present roots in capture(). Update the root filtering in capture() to only
include the gemini root when the Antigravity-specific subtree or equivalent
supporting roots are present, and then use the resulting finalPresent list for
the walkFrozen loop and the empty-result warning path.
- Around line 126-134: The readMode helper is bypassing ctx.fs by importing
node:fs directly, which makes the filesystem path non-injectable and harder to
test. Add a mode-preserving stat helper to FsService (alongside statKind) that
can return the file mode while still working with fixture/virtual filesystems,
then update readMode in Antigravity’s index flow to call that FsService helper
instead of node:fs. Keep the executable-bit preservation behavior intact while
routing all filesystem access through the injected service.

In `@src/core/config/schema.ts`:
- Around line 16-24: The tool-id Zod enum is duplicated in toolIdSchema instead
of being derived from TOOL_IDS, which can drift from the shared source of truth.
Update toolIdSchema in the schema definitions to build from TOOL_IDS from
src/types.ts so the validation list stays aligned with the typed constants, and
apply the same refactor to the matching schema in src/core/manifest/schema.ts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 20acbb77-8118-4f15-83e3-65ae8ef81c92

📥 Commits

Reviewing files that changed from the base of the PR and between 4090f24 and 9c560d7.

📒 Files selected for processing (29)
  • README.md
  • package.json
  • src/adapters/antigravity/index.ts
  • src/adapters/antigravity/paths.ts
  • src/adapters/claude/capture.ts
  • src/adapters/copilot/paths.ts
  • src/adapters/cursor/index.ts
  • src/adapters/cursor/paths.ts
  • src/adapters/kilo/paths.ts
  • src/adapters/registry.ts
  • src/adapters/shared/configDir.ts
  • src/commands/backup.ts
  • src/commands/init.ts
  • src/commands/restore.ts
  • src/commands/secrets.ts
  • src/commands/setup.ts
  • src/commands/status.ts
  • src/core/config/schema.ts
  • src/core/manifest/schema.ts
  • src/core/sanitizer/denylist.ts
  • src/core/secrets/index.ts
  • src/platform/install.ts
  • src/platform/os.ts
  • src/types.ts
  • src/utils/capture-bytes.ts
  • test/unit/antigravity-adapter.test.ts
  • test/unit/configdir-adapters.test.ts
  • test/unit/cursor-cli-and-antigravity-wiring.test.ts
  • test/unit/new-tool-platform.test.ts

Comment thread src/adapters/antigravity/paths.ts
Comment thread src/adapters/kilo/paths.ts
Comment thread src/utils/capture-bytes.ts Outdated
Comment thread test/unit/configdir-adapters.test.ts Outdated
Comment thread test/unit/configdir-adapters.test.ts
Fafoooo and others added 2 commits July 2, 2026 00:59
Codex (both P2, real):
- backup: toolRepoDataRoots() now owns antigravity/user + antigravity/gemini, so
  replaceToolFiles() mirrors ALL of Antigravity's roots (locally deleted files
  can no longer resurrect from the repo) and the generated .gitignore scopes its
  rules over them too.
- restore: safetySourcesForTool() snapshots Antigravity's restore targets before
  a repo-wins pull — the User dir (both name variants) and, surgically, only the
  ~/.gemini frozen paths (the dir is shared with the Gemini CLI and holds a
  browser profile + OAuth files that must not be bulk-copied).

Copilot:
- capture-bytes: valid UTF-16 is always even-length; odd-length buffers (a lone
  0x00, a truncated BOM file) no longer take a lossy UTF-16 decode and are stored
  byte-exact as binary. Follow-up hardening from the same test round: NUL-free
  content is stored as text only when strictly valid UTF-8 (lenient decode
  mangled Latin-1 to U+FFFD on round-trip).

CodeRabbit:
- antigravity: handle the post-2.0 dir split ("Antigravity IDE" +
  ~/.antigravity-ide vs classic "Antigravity" + ~/.antigravity; Google AI forum,
  May 2026 — verified live: macOS 2.0.6 still uses the classic names). Capture,
  restore, and detect now probe both candidates and prefer the existing one
  (IDE-variant first), with the classic name as the fresh-machine default.
- capture-bytes: require >= 2 dominant-parity NULs before the no-BOM UTF-16
  heuristic so tiny blobs like [0x00, 0x01] stay byte-exact binary.
- tests: build token-shaped fixtures at runtime (join) so secret scanners do not
  flag them; leak assertions on base64 payloads now check the UTF-16LE decoding
  too, not just UTF-8.
- kilo doc "which arbella never touches": kept — "arbella" is the product name,
  the sentence is intentional (skipped with reason on the thread).

Tests: +3 (219 total), including a behavioral capture+restore round-trip against
the "Antigravity IDE" layout and byte-classifier regressions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACwssULEcxKCbB28zZRZ63
…SON pass

Found by a live end-to-end test (real `arbella init`/`push`/`pull` against a
sandboxed HOME with planted fake secrets across all 7 tools): an opencode MCP
env var named just `KEY` — no api/token/secret stem, opaque value — reached the
repo unredacted. Codex's TOML pass already treats env/headers tables as secret
containers (every leaf below is redacted, whatever its name); the JSON pass had
no such rule, so JSON-config tools (opencode, cursor, antigravity, copilot,
claude, kilo) relied on key-name matching alone — which arbitrary env-var names
defeat.

- patterns.ts: shared SECRET_CONTAINER_KEYS ("env", "environment", "headers") +
  isSecretContainerKey(), and *_key/-key suffixes in isSecretKey so vendor names
  like OPENAI_KEY / DEEPL-KEY are caught anywhere. Deliberately NOT bare "key":
  VS Code-style keybindings.json entries are {"key": "cmd+k"} and must survive.
- sanitizer/index.ts: cloneRedact threads an inSecretContainer flag — once the
  walk descends under an env/environment/headers map, EVERY leaf in that subtree
  is redacted, mirroring the TOML pass.

E2E verification (real binary, file:// remote, fresh restore home): 9/9 planted
secrets now excluded or redacted (was 8/9); {{TOOL_HOME}}/{{HOME}} templating,
per-OS multi-root restore placement, safety-backup, and reauth reminders all
verified live. +5 unit tests (224 total), including keybindings survival and
dotted-settings-key non-container regression guards.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACwssULEcxKCbB28zZRZ63
Copilot AI review requested due to automatic review settings July 1, 2026 23:13

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 33 out of 33 changed files in this pull request and generated 4 comments.

Comment thread src/adapters/shared/configDir.ts
Comment thread src/adapters/cursor/index.ts
Comment thread src/adapters/antigravity/index.ts
Comment thread src/adapters/claude/capture.ts Outdated
The binary-branch fail-safe scanned only the lossy UTF-8 view, so a credential
embedded as NUL-interleaved UTF-16 bytes inside a binary-classified file
(s\0k\0-\0...) was invisible and the file would be base64-stored raw. This
matters most for the odd-length route the previous fix added: a truncated
UTF-16 config lands in the binary branch while still carrying a full secret.

- capture-bytes: new binaryScanViews() — the UTF-8 view plus UTF-16LE and
  UTF-16BE views at BOTH byte alignments (a leading header byte shifts the
  interleave). Views are for scanning only; stored payloads stay byte-exact.
- All four capture sites (shared/configDir, cursor, antigravity, claude) loop
  the views: any hit drops the file with a warning + SecretRef instead of
  storing it.

Tests: +6 (230 total) — view-visibility units (LE even/odd offset, BE, UTF-8)
and a behavioral opencode capture where a balanced-NUL-parity blob with an
embedded UTF-16LE token is skipped while an equal-shape benign blob is still
captured as base64. Verified live end-to-end: a planted UTF-16-in-binary secret
is skipped on `arbella push` and a UTF-8+UTF-16 deep scan of the pushed repo
finds zero leaks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACwssULEcxKCbB28zZRZ63
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/Fafoooo/arbella/issues/comments/4860580405","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- review_stack_entry_start -->\n\n[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/Fafoooo/arbella/pull/7?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)\n\n<!-- review_stack_entry_end -->\n<!-- walkthrough_start -->\n\n<details>\n<summary>📝 Walkthrough</summary>\n\n## Walkthrough\n\nThis PR adds Antigravity support across capture, restore, detection, CLI wiring, and platform metadata, while also hardening byte classification and secret redaction. It expands frozen-path and denylist coverage for existing tools and updates docs, tests, and package metadata.\n\n### Changes\n\n**Antigravity Tool Integration**\n\n|Layer / File(s)|Summary|\n|---|---|\n|**Shared byte decoding** <br> `src/utils/capture-bytes.ts`, `src/adapters/claude/capture.ts`, `src/adapters/cursor/index.ts`, `src/adapters/shared/configDir.ts`, `test/unit/capture-bytes.test.ts`, `test/unit/configdir-adapters.test.ts`|Adds UTF-16-aware byte decoding and binary scan views, then switches existing capture paths to use them.|\n|**Sanitizer and path contracts** <br> `src/core/sanitizer/index.ts`, `src/core/sanitizer/patterns.ts`, `src/core/sanitizer/denylist.ts`, `src/core/secrets/index.ts`, `src/adapters/antigravity/paths.ts`, `src/platform/os.ts`, `src/platform/install.ts`|Expands secret-container redaction, updates denylists and secret-file metadata, and adds Antigravity path/install lookups.|\n|**Tool identity and registry wiring** <br> `src/types.ts`, `src/core/config/schema.ts`, `src/core/manifest/schema.ts`, `src/adapters/registry.ts`, `test/unit/new-tool-platform.test.ts`|Adds `antigravity` to tool IDs, schemas, registry membership, and platform expectations.|\n|**Antigravity adapter core** <br> `src/adapters/antigravity/index.ts`|Implements Antigravity root mapping, capture/restore behavior, dry-run planning, detection, and CLI install checks.|\n|**CLI command wiring** <br> `src/commands/backup.ts`, `src/commands/restore.ts`, `src/commands/status.ts`, `src/commands/init.ts`, `src/commands/secrets.ts`, `src/commands/setup.ts`|Connects Antigravity to backup, restore, status, init, secrets, and setup command flows.|\n|**Existing adapter coverage updates** <br> `src/adapters/copilot/paths.ts`, `src/adapters/cursor/paths.ts`, `src/adapters/kilo/paths.ts`|Expands frozen-path allowlists and updates capture documentation for Copilot, Cursor, and Kilo.|\n|**Tests, README, and package metadata** <br> `README.md`, `package.json`, `test/unit/antigravity-adapter.test.ts`, `test/unit/cursor-cli-and-antigravity-wiring.test.ts`, `test/unit/sanitizer.test.ts`|Adds adapter, wiring, sanitizer, platform, and byte-decoding tests; updates the README wording; and bumps package metadata.|\n\n**Estimated code review effort:** 4 (Complex) | ~60 minutes\n\n### Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n  participant BackupCLI as arbella backup\n  participant Adapter as antigravityAdapter\n  participant Bytes as capture-bytes\n  participant Sanitizer as sanitizer\n  participant Repo as backup repo\n\n  BackupCLI->>Adapter: capture(ctx)\n  Adapter->>Bytes: decodeForCapture(bytes)\n  Bytes-->>Adapter: text or binary\n  Adapter->>Sanitizer: sanitizeText / secret-shaped scan\n  Sanitizer-->>Adapter: redactions + SecretRefs\n  Adapter->>Repo: write captured files / symlinks\n  Adapter-->>BackupCLI: CaptureResult\n```\n\n```mermaid\nsequenceDiagram\n  participant RestoreCLI as arbella pull\n  participant Adapter as antigravityAdapter\n  participant Plan as planActions\n  participant FS as destination roots\n\n  RestoreCLI->>Adapter: restore(ctx, data)\n  Adapter->>Plan: planActions(ctx, data)\n  Plan->>FS: inspect existing destinations\n  FS-->>Plan: overwrite / preserve state\n  Plan-->>Adapter: RestoreAction[]\n  Adapter->>FS: writeRestoredFile / writeRestoredSymlink\n  Adapter-->>RestoreCLI: restore complete\n```\n\n</details>\n\n<!-- walkthrough_end -->\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 5</summary>\n\n<details>\n<summary>✅ Passed checks (5 passed)</summary>\n\n|         Check name         | Status   | Explanation                                                                                                                                  |\n| :------------------------: | :------- | :------------------------------------------------------------------------------------------------------------------------------------------- |\n|      Description Check     | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled.                                                                                  |\n|         Title check        | ✅ Passed | The title accurately summarizes the main changes: Antigravity and Cursor CLI support plus cross-platform and secret-safety fixes for v0.2.0. |\n|     Docstring Coverage     | ✅ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.                                   |\n|     Linked Issues check    | ✅ Passed | Check skipped because no linked issues were found for this pull request.                                                                     |\n| Out of Scope Changes check | ✅ Passed | Check skipped because no linked issues were found for this pull request.                                                                     |\n\n</details>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing Touches</summary>\n\n<details>\n<summary>📝 Generate docstrings</summary>\n\n- [ ] <!-- {\"checkboxId\": \"7962f53c-55bc-4827-bfbf-6a18da830691\"} --> Create stacked PR\n- [ ] <!-- {\"checkboxId\": \"3e1879ae-f29b-4d0d-8e06-d12b7ba33d98\"} --> Commit on current branch\n\n</details>\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"6ba7b810-9dad-11d1-80b4-00c04fd430c8\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Commit unit tests in branch `feat/multi-tool-sync-and-fixes`\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Fafoooo/arbella&utm_content=7)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n\n<sub>Comment `@coderabbitai help` to get the list of available commands.</sub>\n\n<!-- tips_end -->"},"request":{"retryCount":3,"signal":{},"retries":3,"retryAfter":16}}}

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
src/adapters/antigravity/paths.ts (1)

84-87: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor duplication between default and candidate list.

antigravityUserDir's classic default "Antigravity" duplicates ANTIGRAVITY_APP_DIR_NAMES[1]. Deriving it from the constant (e.g. ANTIGRAVITY_APP_DIR_NAMES[ANTIGRAVITY_APP_DIR_NAMES.length - 1]) would prevent silent drift if the list is ever reordered/edited.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/adapters/antigravity/paths.ts` around lines 84 - 87, The
antigravityUserDir helper currently hardcodes the classic app directory name,
which duplicates ANTIGRAVITY_APP_DIR_NAMES[1] and can drift if the list changes.
Update antigravityUserDir in paths.ts to derive the default directory segment
from ANTIGRAVITY_APP_DIR_NAMES instead of repeating the string, using the
existing constant near the function so the fallback stays aligned with the
candidate list.
src/utils/capture-bytes.ts (1)

32-49: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Unbounded full-buffer scan for every binary file.

decodeForCapture's own classification sniff is capped at SNIFF_LIMIT for performance, but binaryScanViews converts the entire buffer into 5 separate lossy string views, with no size bound. This runs on every file classified as binary (images, sqlite DBs, other blobs per the module's own doc comment) unless includeSecrets is set, so large binary artifacts pay a 5x full-buffer conversion cost on every capture.

Consider capping the scan to a reasonable prefix/sample size (or skipping the scan and simply excluding oversized binaries) to avoid unbounded CPU/memory use on large files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/capture-bytes.ts` around lines 32 - 49, The binary secret-scan path
in binaryScanViews currently converts the entire Buffer into multiple lossy
string views, which creates unbounded work for large binary captures. Update
binaryScanViews (and any caller like decodeForCapture that relies on it) to scan
only a bounded prefix or sample size consistent with SNIFF_LIMIT, or skip secret
scanning for oversized binaries, so binary files do not incur full-buffer 5x
conversion cost.
src/core/sanitizer/patterns.ts (1)

246-250: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

"_key"/"-key" suffix risks false positives on legitimate non-secret fields.

Fields like public_key, primary_key, partition_key, sort_key, or foreign_key are common in SSH/GPG/DB/ORM configs and are not secrets (a public key is, by definition, shareable), but they'll now match this suffix and get their values redacted.

Consider excluding a short list of known non-secret "_key" terms (e.g. public_key, primary_key, partition_key, sort_key, foreign_key) to reduce unnecessary over-redaction of legitimate config values.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/sanitizer/patterns.ts` around lines 246 - 250, The "_key"/"-key"
suffixes in the sanitizer pattern list are over-matching legitimate non-secret
fields and causing unnecessary redaction. Update the matching logic in
patterns.ts around the key suffix entries to exclude known safe identifiers such
as public_key, primary_key, partition_key, sort_key, and foreign_key, so the
sanitizer still catches vendor-style secret names without redacting common
database/crypto config fields. Keep the change localized to the pattern
definitions used by the sanitizer so existing secret-detection behavior remains
intact.
test/unit/sanitizer.test.ts (1)

293-304: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Test doesn't exercise the scenario its comment describes.

The comment describes guarding against "terminal.integrated.env.osx" (a dotted key containing "env") being misread as a secret container, but the actual payload (editor.fontSize, workbench.colorTheme) has no such key and no nested object at all. As written, this test would pass even if dotted-key container matching were broken — it isn't a regression guard for the described case.

♻️ Proposed fix to actually exercise the dotted-key scenario
   it("does not misread dotted VS Code settings keys as containers", () => {
     // "terminal.integrated.env.osx" is ONE key (not an env container); its
     // object value is a real env map though — but only once a key literally
     // named env/environment/headers introduces it.
     const settings = JSON.stringify(
-      { "editor.fontSize": 14, "workbench.colorTheme": "Dark" },
+      { "terminal.integrated.env.osx": { MY_VAR: "not-a-secret-value" } },
       null,
       2,
     );
     const res = sanitizer.sanitizeFile(settings, "cursor", "settings.json");
     expect(res.changed).toBe(false);
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/unit/sanitizer.test.ts` around lines 293 - 304, The sanitizer test is
not covering the dotted-key regression it describes, so update the payload in
the settings.json case to include a literal dotted key like
sanitizer.sanitizeFile with "terminal.integrated.env.osx" and a non-secret
object value that should not be treated as an env container. Keep the assertion
on res.changed and verify the test still uses the existing sanitizeFile path so
it specifically guards against misreading dotted VS Code settings keys as
containers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/unit/configdir-adapters.test.ts`:
- Line 369: The BIN_SECRET fixture in configdir-adapters.test.ts is a contiguous
secret-shaped literal that can trigger scanners. Update the BIN_SECRET constant
to be assembled at runtime in the same style as the other fixed fixtures in this
file, so the test still uses the same value but no longer contains a raw
Anthropic-like token prefix.

---

Nitpick comments:
In `@src/adapters/antigravity/paths.ts`:
- Around line 84-87: The antigravityUserDir helper currently hardcodes the
classic app directory name, which duplicates ANTIGRAVITY_APP_DIR_NAMES[1] and
can drift if the list changes. Update antigravityUserDir in paths.ts to derive
the default directory segment from ANTIGRAVITY_APP_DIR_NAMES instead of
repeating the string, using the existing constant near the function so the
fallback stays aligned with the candidate list.

In `@src/core/sanitizer/patterns.ts`:
- Around line 246-250: The "_key"/"-key" suffixes in the sanitizer pattern list
are over-matching legitimate non-secret fields and causing unnecessary
redaction. Update the matching logic in patterns.ts around the key suffix
entries to exclude known safe identifiers such as public_key, primary_key,
partition_key, sort_key, and foreign_key, so the sanitizer still catches
vendor-style secret names without redacting common database/crypto config
fields. Keep the change localized to the pattern definitions used by the
sanitizer so existing secret-detection behavior remains intact.

In `@src/utils/capture-bytes.ts`:
- Around line 32-49: The binary secret-scan path in binaryScanViews currently
converts the entire Buffer into multiple lossy string views, which creates
unbounded work for large binary captures. Update binaryScanViews (and any caller
like decodeForCapture that relies on it) to scan only a bounded prefix or sample
size consistent with SNIFF_LIMIT, or skip secret scanning for oversized
binaries, so binary files do not incur full-buffer 5x conversion cost.

In `@test/unit/sanitizer.test.ts`:
- Around line 293-304: The sanitizer test is not covering the dotted-key
regression it describes, so update the payload in the settings.json case to
include a literal dotted key like sanitizer.sanitizeFile with
"terminal.integrated.env.osx" and a non-secret object value that should not be
treated as an env container. Keep the assertion on res.changed and verify the
test still uses the existing sanitizeFile path so it specifically guards against
misreading dotted VS Code settings keys as containers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 961d9793-188c-41f5-a4a4-00098aa33c4a

📥 Commits

Reviewing files that changed from the base of the PR and between 9c560d7 and 9572891.

📒 Files selected for processing (15)
  • src/adapters/antigravity/index.ts
  • src/adapters/antigravity/paths.ts
  • src/adapters/claude/capture.ts
  • src/adapters/cursor/index.ts
  • src/adapters/shared/configDir.ts
  • src/commands/backup.ts
  • src/commands/restore.ts
  • src/core/sanitizer/index.ts
  • src/core/sanitizer/patterns.ts
  • src/utils/capture-bytes.ts
  • test/unit/antigravity-adapter.test.ts
  • test/unit/capture-bytes.test.ts
  • test/unit/configdir-adapters.test.ts
  • test/unit/cursor-cli-and-antigravity-wiring.test.ts
  • test/unit/sanitizer.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/adapters/shared/configDir.ts
  • src/adapters/claude/capture.ts
  • src/adapters/antigravity/index.ts
  • test/unit/antigravity-adapter.test.ts
  • src/adapters/cursor/index.ts

Comment thread test/unit/configdir-adapters.test.ts
@Fafoooo Fafoooo merged commit fdb465e into main Jul 2, 2026
7 checks passed
@Fafoooo Fafoooo deleted the feat/multi-tool-sync-and-fixes branch July 2, 2026 12:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants