Skip to content

fix(cli): use fs.symlinkSync for paths with spaces, show version in status#833

Merged
evgeniko merged 7 commits intomainfrom
fix/symlink-spaces-in-path
Mar 9, 2026
Merged

fix(cli): use fs.symlinkSync for paths with spaces, show version in status#833
evgeniko merged 7 commits intomainfrom
fix/symlink-spaces-in-path

Conversation

@evgeniko
Copy link
Copy Markdown
Contributor

@evgeniko evgeniko commented Mar 6, 2026

Summary

Replace shell ln -fs with fs.symlinkSync to fix symlink creation in paths with spaces, and extract version info into a shared module so ntt status prints the CLI version.

  • Replace shell ln -fs $(pwd)/... with fs.symlinkSync in createWorkTree to fix symlink creation failing when the working directory contains spaces
  • Extract nttVersion() into a shared version.ts module and print CLI version at the top of ntt status output
  • Guard against malformed ~/.ntt-cli/version files (fewer than 4 lines)
  • Use nttVersion() null check instead of string comparison in index.ts
  • Add end-to-end test that exercises createWorkTree in a temp repo with spaces in the path

Resolves #537
Resolves #512

Test plan

  • Regression tests pass (bun test src/__tests__/bugfix-regression.test.ts)
  • End-to-end test creates a git repo in a "dir with spaces" temp directory and verifies symlink creation
  • Manual: run ntt status and verify version is printed
  • Manual: run CLI from a path containing spaces and verify worktree symlink is created correctly

Summary by CodeRabbit

  • New Features

    • Version information now displays formatted details including commit and remote.
    • Status command displays NTT version on startup.
  • Bug Fixes

    • Resolved symlink creation issues when repository paths contain spaces.
  • Tests

    • Added regression test coverage for symlink creation with space-containing paths.

…tatus

Replace shell `ln -fs` with Node.js fs.symlinkSync to handle paths
containing spaces. Extract nttVersion() into shared version.ts and
print CLI version in `ntt status` output.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 6, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0494204e-5462-4659-ae3a-cd977870177d

📥 Commits

Reviewing files that changed from the base of the PR and between 0940774 and ead475a.

📒 Files selected for processing (1)
  • cli/src/index.ts

📝 Walkthrough

Walkthrough

Replaces shell-based symlink creation with a path-safe fs-based approach; adds nttVersion() and formatNttVersion() utilities to read/format local CLI version and prints that formatted version when running ntt status. Adds a test covering symlink behavior when repository paths contain spaces.

Changes

Cohort / File(s) Summary
Version Utilities
cli/src/version.ts
Adds nttVersion() to read/parse ~/.ntt-cli/version and formatNttVersion() to produce a printable NTT version string with conditional path/remote formatting.
CLI Index
cli/src/index.ts
Replaces in-file version construction with imports from the new version helpers and uses formatNttVersion() in the yargs .version() builder.
Status Command Enhancement
cli/src/commands/status.ts
Imports and logs formatNttVersion() at the start of the status command handler.
Symlink Creation Fix
cli/src/tag.ts
Replaces shell ln -fs usage with path-safe fs operations: compute absolute target, unlink existing link path (ignore ENOENT), then create a symlink to overrides.json.
Test Coverage
cli/src/__tests__/bugfix-regression.test.ts
Adds test that initializes a git repo in a temp directory with spaces, commits overrides.json, calls createWorkTree(...), and asserts the overrides.json symlink exists and resolves to the original file.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through paths with spaces and cheer,
swapped fragile shells for links that persevere.
I read the version, printed it bright,
tested the symlink by soft moonlight.
🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes both main changes: fixing symlink creation for paths with spaces and adding version output to the status command.
Linked Issues check ✅ Passed Code changes fully address both issues: fs.symlinkSync handles paths with spaces [#537], and version output added to status command [#512].
Out of Scope Changes check ✅ Passed All changes are directly scoped to the linked issues: symlink fix, version extraction, status output, and test coverage; no extraneous modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/symlink-spaces-in-path

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
cli/src/__tests__/bugfix-regression.test.ts (1)

194-203: Make this a behavior test, not a source grep.

This only proves tag.ts currently contains fs.symlinkSync. It would still pass if createWorkTree() swapped the arguments, resolved the wrong path, or stopped handling spaces correctly while keeping the same call site. Please exercise createWorkTree() from a temp directory with spaces and assert the resulting overrides.json symlink target instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/src/__tests__/bugfix-regression.test.ts` around lines 194 - 203, Replace
the current source-grep test with a behavior test that invokes createWorkTree
from a temporary directory whose path contains spaces (use a tempdir helper to
create "dir with spaces"), call the exported createWorkTree function from tag.ts
to create the work tree, then assert that the resulting overrides.json is a
symlink and that fs.readlinkSync (or fs.realpathSync) on that overrides.json
resolves to the expected absolute path to the real overrides file (verifying
argument order and space handling); ensure the test cleans up the temp dir and
uses the same function name createWorkTree and file name overrides.json as
referenced in the diff.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli/src/tag.ts`:
- Around line 89-90: Prettier is required for this hunk: reformat the two-line
block so it matches project formatting (expand the compact try/catch into a
standard multi-line try { fs.unlinkSync(linkPath); } catch (e: any) { if (e.code
!== "ENOENT") throw e; } and ensure the following fs.symlinkSync(target,
linkPath) line is correctly indented/terminated); run the repository Prettier
config (or npm/yarn format script) to update the formatting for the
fs.unlinkSync/linkPath and fs.symlinkSync(target, linkPath) statements before
merging.

In `@cli/src/version.ts`:
- Around line 11-13: The code reads and splits versionFile into [commit,
installPath, version, remote] without validating the result, so missing fields
can be undefined and later break calls like includes(); update the parsing in
the function that sets versionFile and the variables commit, installPath,
version, remote to validate the array returned by versionFile.split("\n") (or
use destructuring with defaults) and explicitly reject or provide safe defaults
(e.g. version = "ntt version: unknown", commit = "", path = "", remote = "")
when there are fewer than four lines; ensure any downstream logic that calls
methods on these values (e.g. includes) only runs on defined strings.

---

Nitpick comments:
In `@cli/src/__tests__/bugfix-regression.test.ts`:
- Around line 194-203: Replace the current source-grep test with a behavior test
that invokes createWorkTree from a temporary directory whose path contains
spaces (use a tempdir helper to create "dir with spaces"), call the exported
createWorkTree function from tag.ts to create the work tree, then assert that
the resulting overrides.json is a symlink and that fs.readlinkSync (or
fs.realpathSync) on that overrides.json resolves to the expected absolute path
to the real overrides file (verifying argument order and space handling); ensure
the test cleans up the temp dir and uses the same function name createWorkTree
and file name overrides.json as referenced in the diff.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6e3d1bc6-4c99-4771-9beb-aefc442aba3c

📥 Commits

Reviewing files that changed from the base of the PR and between fee796a and 000ba00.

📒 Files selected for processing (4)
  • cli/src/__tests__/bugfix-regression.test.ts
  • cli/src/commands/status.ts
  • cli/src/tag.ts
  • cli/src/version.ts

evgeniko added 3 commits March 6, 2026 15:01
Use nttVersion() null check instead of string comparison in index.ts.
Replace source-level symlink assertion with an end-to-end test that
exercises createWorkTree in a temp repo with spaces in the path.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
cli/src/version.ts (1)

28-30: Consider guarding against empty remote value.

If remote is an empty string (e.g., due to a git error during install), remoteString becomes just "@" which would produce output like ntt v1.0.0 (@abc123). This is a minor edge case since the installer should always write valid data.

Optional defensive guard
-  const remoteString = remote.includes("wormhole-foundation")
+  const remoteString = !remote || remote.includes("wormhole-foundation")
     ? ""
     : `${remote}@`;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/src/version.ts` around lines 28 - 30, The code sets remoteString based on
remote.includes(...) but doesn't guard against remote being empty, which yields
"@" in output; update the remoteString assignment in cli/src/version.ts to first
check that remote is non-empty (e.g., truthy and trimmed) before calling
remote.includes, and only produce `${remote}@` when remote is present and does
not include "wormhole-foundation"; otherwise set remoteString to an empty
string—adjust the expression that defines remoteString to use this guard around
remote.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@cli/src/version.ts`:
- Around line 28-30: The code sets remoteString based on remote.includes(...)
but doesn't guard against remote being empty, which yields "@" in output; update
the remoteString assignment in cli/src/version.ts to first check that remote is
non-empty (e.g., truthy and trimmed) before calling remote.includes, and only
produce `${remote}@` when remote is present and does not include
"wormhole-foundation"; otherwise set remoteString to an empty string—adjust the
expression that defines remoteString to use this guard around remote.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1f0d387a-d76e-48c1-b053-2a1a6c2a75d4

📥 Commits

Reviewing files that changed from the base of the PR and between 50d6a12 and 0940774.

📒 Files selected for processing (3)
  • cli/src/__tests__/bugfix-regression.test.ts
  • cli/src/index.ts
  • cli/src/version.ts

@evgeniko evgeniko merged commit 9607ca7 into main Mar 9, 2026
20 checks passed
@evgeniko evgeniko deleted the fix/symlink-spaces-in-path branch March 9, 2026 12:36
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.

CLI: Symlink fails when working directory path contains spaces cli: print version when running ntt status

3 participants