Skip to content

fix: preserve $ in file paths for ClaudeCodeAdd and openFile#291

Closed
ThomasK33 wants to merge 2 commits into
mainfrom
fix/285-dollar-path-expand
Closed

fix: preserve $ in file paths for ClaudeCodeAdd and openFile#291
ThomasK33 wants to merge 2 commits into
mainfrom
fix/285-dollar-path-expand

Conversation

@ThomasK33

Copy link
Copy Markdown
Member

Summary

Fixes #285. Adding a file whose path contains a $ (e.g. TanStack Router's src/routes/$post/index.tsx) via :ClaudeCodeAdd failed with File or directory does not exist, and the same path failed openFile ("File not found") when Claude tried to open it.

Root cause: both call sites resolved the path with vim.fn.expand(), which performs shell-style environment-variable substitution. $post was read as the undefined env var post and replaced with the empty string, so src/routes/$post/index.tsx became src/routes//index.tsx — which does not exist, failing the filereadable() / isdirectory() check even though the file is present on disk.

Fix: use the existing claudecode.utils.expand_tilde() helper instead, at both sites. It expands only a leading ~ / ~/ and leaves $, globs and everything else untouched.

  • lua/claudecode/init.lua:ClaudeCodeAdd command
  • lua/claudecode/tools/open_file.luaopenFile MCP tool (invoked by Claude itself)

The other vim.fn.expand() usages were reviewed and left as-is: terminal.lua uses the special %:p token, and cwd.lua / lockfile.lua expand configuration-supplied directories where $VAR / ~ expansion is the intended behaviour.

Behavioural note

For these two entry points, a typed argument such as $HOME/foo or a glob like src/*.lua is no longer expanded. This is deliberate: such inputs are rare for these paths (which usually arrive already-resolved from buffers/explorers), and preserving real $-named files is the more common, higher-impact case. ~ expansion and absolute/relative paths are unchanged (verified — expand_tilde is identical to vim.fn.expand for those).

Test plan

  • New regression tests assert a $-containing path passes the readability gate in tests/unit/claudecode_add_command_spec.lua and tests/unit/tools/open_file_spec.lua (existing tilde/relative/absolute cases updated to the new helper; the old mock fictionally absolutized relative paths, which real expand() never did).
  • mise run all — 660 tests pass, luacheck clean (0 warnings/0 errors across 109 files), formatting stable.
  • Self-verifying headless repro scripts/repro_issue_285.lua (exits 1 before the fix, 0 after) drives the real :ClaudeCodeAdd command and openFile handler against real on-disk files.
  • Interactive fixture fixtures/issue-285/ (vv issue-285:Repro285) verified live in a Neovim TUI.

🤖 Generated with Claude Code

vim.fn.expand() performs shell-style environment-variable substitution,
so a real path segment like `$post` (e.g. TanStack Router's
`src/routes/$post/`) was read as the undefined env var `post` and
replaced with the empty string -- turning `src/routes/$post/index.tsx`
into `src/routes//index.tsx`. The subsequent filereadable()/isdirectory()
check then failed and the existing file was reported as missing.

Replace vim.fn.expand() with claudecode.utils.expand_tilde() at both
affected sites. The helper expands only a leading `~`/`~/` and leaves
`$`, globs and everything else untouched:

  - lua/claudecode/init.lua (:ClaudeCodeAdd command)
  - lua/claudecode/tools/open_file.lua (openFile MCP tool, invoked by Claude)

Add regression tests asserting a `$`-containing path survives the
readability gate, plus a self-verifying headless repro
(scripts/repro_issue_285.lua) and an interactive fixture
(fixtures/issue-285/).

Fixes #285

Change-Id: I88afde99ea40bae1e394ddd9070898839857db08
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@claude review

@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: 72b40e6df9

ℹ️ 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 lua/claudecode/init.lua Outdated
@ThomasK33

Copy link
Copy Markdown
Member Author

Fix verification (recorded)

Before/after run of the self-verifying repro (scripts/repro_issue_285.lua) against real on-disk files — same files, only this PR's vim.fn.expandutils.expand_tilde change differs:

  • BEFORE (the two source files reverted to the pre-fix commit HEAD~1): BUG #285 REPRODUCED, exit code 1
  • AFTER (this PR restored): FIXED, exit code 0

issue285_fix_proof.gif

Same recording as a video (webm)
issue285_fix_proof.webm

🤖 Generated with Claude Code

The previous commit routed ALL :ClaudeCodeAdd path args through expand_tilde,
which also dropped vim.fn.expand's current/alternate-file token expansion --
breaking the documented `:ClaudeCodeAdd %` "add current buffer" keymap (README).

Expand Vim's %/#/<...> file tokens via vim.fn.expand and route only plain
filesystem paths through expand_tilde, so both real `$` paths and the `%`
workflow work. This is strictly better than the pre-PR behaviour: `$` is fixed
while %/#/<...> behave exactly as before.

Add a `%` regression test and a `%` scenario to the repro script.

Addresses codex review feedback on #291.

Change-Id: Idbdf3e1bfcd1eeb334acebd66314d386b5d914f0
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review — addressed the P1: :ClaudeCodeAdd % (and #/<cfile>) token expansion is preserved in 95323ac, with a regression test added.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: 95323ace31

ℹ️ 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".

@ThomasK33

Copy link
Copy Markdown
Member Author

Closing in favour of #286, which is the original reporter's PR (@geoff-tw). To preserve their contribution, I rebased this work as a follow-up commit stacked on top of theirs in #286 (token-aware %/#/<cfile> handling + repro + fixture + % regression test). The proof video is re-posted there. No work is lost — #286 contains everything from this PR plus their original $ fix and utils_spec test.

@ThomasK33 ThomasK33 closed this Jun 22, 2026
@ThomasK33 ThomasK33 deleted the fix/285-dollar-path-expand branch June 22, 2026 14:51
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.

[BUG] ClaudeCodeAdd fails when adding a file in a directory with a $

1 participant