Skip to content

feat: visible_only mode to limit diffs to open buffers#24

Merged
Cannon07 merged 3 commits into
Cannon07:mainfrom
kam-hak:feat/visible-only
Apr 7, 2026
Merged

feat: visible_only mode to limit diffs to open buffers#24
Cannon07 merged 3 commits into
Cannon07:mainfrom
kam-hak:feat/visible-only

Conversation

@kam-hak

@kam-hak kam-hak commented Apr 4, 2026

Copy link
Copy Markdown
Contributor

Problem

If you're focused on one file but Claude is making small edits elsewhere (wiring, imports, config), nvim mirrors every edit with a diff tab and neo-tree reveal. There's no way to get full-context review for the file you care about while letting the rest flow through the CLI.

Design

visible_only gives finer control: files open in nvim get the full diff preview, everything else falls through to the Claude CLI's normal review flow.

Changes

  • Add diff.visible_only config (default false, no behavior change)
  • When enabled, only files open in a visible nvim window get diff previews and neo-tree reveals
  • Add :ClaudePreviewToggleVisibleOnly for runtime toggling
  • Add hook_context() to batch config + file visibility into one RPC call

Note: permissionDecision removal (separable)

This PR also removes the permissionDecision output from the PreToolUse hook. This is separable from the visible_only feature if you'd prefer to handle it differently.

Currently the hook unconditionally returns "ask", which overrides Claude Code's own permission system — including bypass mode. A user who has explicitly configured bypass still gets prompted on every edit. A preview hook should be a UI layer that shows diffs, not an approval gate. Happy to split this out if you'd rather discuss it separately.

Test plan

  • Default config (visible_only = false) — all edits show diffs in nvim
  • Enable visible_only = true — only open files show diffs, others go through CLI
  • Toggle with :ClaudePreviewToggleVisibleOnly — takes effect on next edit
  • Claude's own permission settings (bypass/ask) work independently of this hook

@kam-hak

kam-hak commented Apr 4, 2026

Copy link
Copy Markdown
Contributor Author

Self-review caught two issues. Fixed and pushed:

  1. Same walk-up regression as feat: configurable neo-tree reveal with cross-CWD support #21 — dropped the nearest-existing-parent logic for created files, broke new-file reveal. Restored.
  2. The is_mac + :lower() branch I had was wrong — fs_realpath already returns the filesystem's canonical form, so case-insensitive volumes normalize without per-OS code. Worse, lowercasing would produce false positives on case-sensitive filesystems (Linux, case-sensitive APFS). Dropped it.

kam-hak added a commit to kam-hak/claude-preview.nvim that referenced this pull request Apr 4, 2026
Apply the four fixes from the PR review cycle:

- PR Cannon07#20: restore post-cleanup reveal inside is_open() guard so
  newly-created files appear in neo-tree after accept.
- PR Cannon07#21: replace jq '// true' with explicit conditional so
  reveal=false config is actually honored (// treats false like null).
- PR Cannon07#21/Cannon07#24: restore walk-up-to-parent logic for created files so
  neo-tree can expand paths that don't exist on disk yet.
- PR Cannon07#23: replace bufnr(path) with literal canonical-path comparison;
  bufnr does regex matching and mis-matches paths with metacharacters.
- PR Cannon07#24: drop the is_mac + :lower() hack; fs_realpath already returns
  the filesystem canonical form, and lowercasing produces false
  positives on case-sensitive filesystems.
@kam-hak
kam-hak force-pushed the feat/visible-only branch from b2b5b88 to cc66ecf Compare April 5, 2026 16:06
@kam-hak

kam-hak commented Apr 5, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto main. Two commits:

  1. feat: visible_only mode
  2. refactor: drop permissionDecision output. My perspective is that an nvim plugin should not override CC's permissions. Currently, this triggers even if nvim is not open if there is a stale socket (which is common at least for me). If intentional, perhaps we add a respect CC's permission flag, and happy to split this as a separate issue and design discussion.

@kam-hak

kam-hak commented Apr 5, 2026

Copy link
Copy Markdown
Contributor Author

looking into these failing tests

@kam-hak

kam-hak commented Apr 5, 2026

Copy link
Copy Markdown
Contributor Author

Also opened #26 / #27 for a separate test-infrastructure bug.

The e2e stale-socket tests weren't actually exercising stale-socket discovery (they bypassed the scan logic via the NVIM_LISTEN_ADDRESS fast path, and had dead wrapper code). Independent of this PR, but relevant for test coverage around these hooks. Happy to rebase this PR on top of #27 if you merge that first.

@kam-hak

kam-hak commented Apr 5, 2026

Copy link
Copy Markdown
Contributor Author

Note on interaction with #27: if #27 merges first, this PR has a one-line merge conflict in test_stale_socket.sh (both PRs touch the same assertion in the test_no_nvim_graceful test). Resolution should be simple. Drop this PR's change to that file specifically, since #27 restructured the test entirely and its new version doesn't assert on the printf output. test_edit.sh changes here are independent and apply cleanly either way. Happy to do this if 27 is merged!

@kam-hak
kam-hak force-pushed the feat/visible-only branch from cc66ecf to 8c547f8 Compare April 5, 2026 19:35
@Cannon07

Cannon07 commented Apr 5, 2026

Copy link
Copy Markdown
Owner

Great work on the visible_only feature — tested it and it works well. The runtime toggle is a nice touch too.

Regarding the permissionDecision removal — I'd prefer to keep it as the default since it's a core part of the review workflow for most users. However, the use case you described (bypass mode users getting prompted on every edit) is valid.

Would you be open to making it configurable instead of removing it? Something like:

diff = {
override_permission = true, -- default, preserves current behavior
}

When true (default), the hook returns "ask" as it does in main. When false, the hook produces no permissionDecision output and defers to Claude Code's own permission settings.

This way existing users get no behavior change, and power users who want bypass mode can opt in.
Let me know your thoughts.

@kam-hak

kam-hak commented Apr 6, 2026

Copy link
Copy Markdown
Contributor Author

Regarding the permissionDecision removal — I'd prefer to keep it as the default since it's a core part of the review workflow for most users. However, the use case you described (bypass mode users getting prompted on every edit) is valid.

Would you be open to making it configurable instead of removing it? Something like:

diff = { override_permission = true, -- default, preserves current behavior }

When true (default), the hook returns "ask" as it does in main. When false, the hook produces no permissionDecision output and defers to Claude Code's own permission settings.

This way existing users get no behavior change, and power users who want bypass mode can opt in. Let me know your thoughts.

Sure! Can put it behind a flag. Just so the intended behavior is clear, the flag might be "defer_claude_permissions = true" if turned on.

This would never override but defer to claude, avoiding having two different permission toggles which interact: one in this nvim addon, which injects 'ask' directly to claude (overriding CC's permission setting), and a second toggle in claude's session between vanilla/accept edits/plan/bypass window, which at least for me, gets overridden by this nvim.

If that sounds good can make the update.

@Cannon07

Cannon07 commented Apr 6, 2026

Copy link
Copy Markdown
Owner

That works! defer_claude_permissions with a default of false is a clean name — makes it clear what you're opting into.

kam-hak added 3 commits April 6, 2026 20:21
When diff.visible_only = true, only files already open in a visible
nvim window get a diff preview. Files not in any visible window are
silently skipped — no diff tab, no neo-tree indicator, no reveal.

- Add diff.visible_only config (default false, no behavior change)
- Add :ClaudePreviewToggleVisibleOnly for runtime toggling
- Extend hook_context() to report file visibility alongside existing
  neo_tree_reveal and reveal_root, in a single RPC call
- Gate the existing nvim UI calls behind SHOULD_SHOW (note: most of
  the shell diff is re-indentation from wrapping existing logic in
  the new guard)
…n model

The hook was unconditionally emitting permissionDecision='ask',
forcing Claude Code to prompt on every edit regardless of the user's
bypass/allowlist/ask configuration. That overrides a system the user
has already set up deliberately.

Remove the printf. Claude Code's native permission settings now
decide whether to prompt. This hook becomes a pure Neovim UI layer —
it sends the diff preview and updates neo-tree, and lets Claude Code
handle whether to ask the user.
When true, the hook produces no permissionDecision output and defers
to Claude Code's own permission settings (bypass, ask, allowlist).
Default is false, preserving existing behavior where the hook always
returns "ask" to prompt the user on every edit.
@kam-hak
kam-hak force-pushed the feat/visible-only branch from 8c547f8 to e9f21dc Compare April 7, 2026 00:36
@kam-hak

kam-hak commented Apr 7, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto main (now includes #27). Added defer_claude_permissions flag in a third commit:

  1. feat: visible_only mode (unchanged)
  2. refactor: drop permissionDecision output (unchanged)
  3. feat: add defer_claude_permissions config flag (new)

defer_claude_permissions defaults to false, preserving the current "ask" behavior. When true, the hook produces no permissionDecision output and defers to Claude Code's own settings. Updated test assertions to match.

@Cannon07 Cannon07 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Great addition! Both features are well implemented.

LGTM. Merging!

@Cannon07
Cannon07 merged commit bdf581a into Cannon07:main Apr 7, 2026
2 checks passed
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