Skip to content

relay-guard: work when the plugin is reached through a symlinked folder - #57

Closed
darko-mijic wants to merge 1 commit into
mainfrom
fix/symlinked-plugin-path
Closed

darko-mijic wants to merge 1 commit into
mainfrom
fix/symlinked-plugin-path

Conversation

@darko-mijic

@darko-mijic darko-mijic commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

What broke

A plugin can be installed through a symlinked Claude Code config folder, so its recorded install path runs through the symlink. Claude Code then runs relay-guard.mjs through that path, and the relay names the companion through it too (node "<symlinked root>/scripts/companion.mjs" start --from-relay). Node, though, gives the hook module its real path in import.meta.url.

Two comparisons in relay-guard.mjs mixed the two:

  • the entry check (process.argv[1] against import.meta.url) was false, so main() never ran and the PreToolUse hook did nothing at all;
  • the companion match (path.resolve(script) !== COMPANION) would not have recognised the start command either.

Every relay:job then failed at once: start --from-relay needs --relay-transcript and --relay-agent, which the relay hook adds. The hook did not run. The stop-time review gate was unaffected, because its own entry check already resolves both paths.

The fix

Both comparisons resolve symlinks first (fs.realpathSync, falling back to path.resolve for a path that doesn't exist).

Tests

  • New: a plugin reached through a symlinked folder: the hook runs, and knows the companion by either path. It parses a start command named through a symlink, and runs the hook as a process through the symlink, expecting its deny for a stray Bash command. It fails without the fix and passes with it.
  • npm test: 598 pass, 0 fail, 1 skipped.

The CHANGELOG entry goes in with the next release, as usual.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Cw9dodEyoUpvSEJQUeF1FJ

Summary by CodeRabbit

  • Bug Fixes
    • Fixed an issue where the command guard could fail to recognize scripts accessed through a symlink. The guard now correctly identifies these paths and applies its command checks.

RetriggerView in GreptileConfidence Score: 5/5

Safe to merge: the symlink regression, the affected test suite, and additional alias checks passed.

Summary

Fixes relay-guard.mjs when the plugin is reached through a symlinked folder.

  • Relay hooks recognize plugin paths reached through symlinked folders.

No actionable issues found. The change is safe to merge based on the affected behavior checked.

Reviews (1) · Last reviewed commit: "relay-guard: compare resolved paths, so ..." · Reviewed by Greptile

When Claude Code installs the plugin through a symlinked config folder, it runs the hook and the relay
names the companion through the symlink, while Node gives the hook module its real path. The hook's
entry check then never ran main(), and start --from-relay failed with 'the hook did not run'. Both the
entry check and the companion match now compare paths with symlinks resolved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cw9dodEyoUpvSEJQUeF1FJ
@coderabbitai

coderabbitai Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d61f0047-25f4-4b64-8baf-db5c42cee4e8
Commits

Reviewing files that changed from the base of the PR and between 79a6f54 and 40c09a0.

Files selected for processing: 2
  • plugins/relay/scripts/hooks/relay-guard.mjs
  • tests/relay-guard.test.mjs

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Walkthrough

The relay guard now resolves symlinks when it compares script paths. A test covers companion-command parsing and hook execution through a symlink.

Changes: Relay guard path resolution

Layer / File(s) Summary
Resolve paths for guard checks
plugins/relay/scripts/hooks/relay-guard.mjs, tests/relay-guard.test.mjs
The guard resolves paths before comparing companion and direct-execution script paths. The test checks that a symlink-launched hook parses the companion command and denies sw_vers -productVersion with exit status 0.

Priority: Normal

Estimated code review effort: 2 (Simple) | ~8 minutes

Change: Bug fix

Merge Risk: Minimal · up to 40c09

The symlink-aware path comparisons and test coverage address the stated behavior without an identified remaining merge risk.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed 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 clearly and concisely describes the main change: making relay-guard work when the plugin is accessed through a symlinked folder.
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.
Full details: Docstring Coverage

Explanation

Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 2 functions across 2 files.

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

A rabbit checks the paths at night
Through links, the guard sees each one right
The companion command comes through
The hook denies the tested call too
Then hops away beneath the moon

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

@darko-mijic

Copy link
Copy Markdown
Contributor Author

Note

🤖 Claude Opus 5.5 responding on behalf of darko-mijic

Closing as folded into #60. This PR's head commit 40c09a0 is an ancestor of release/2.2.2, so the fix ships unchanged with 2.2.2 and lands on main when #60 merges. This PR will show as closed, not merged. The branch stays in place.

@darko-mijic
darko-mijic deleted the fix/symlinked-plugin-path branch November 3, 2026 14:41
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.

1 participant