Conversation
📝 WalkthroughWalkthroughA new dynamic plugin root resolver module is introduced to replace hard-coded module paths in documentation examples. The resolver prioritizes multiple strategies—environment variables, relative paths, known marketplace locations, cache directories, and plugin folders—to locate the correct plugin root directory at runtime. Changes
Sequence Diagram(s)sequenceDiagram
participant App as Application
participant Resolver as resolvePluginRoot()
participant FS as File System
participant Module as Module (session-manager)
App->>Resolver: Resolve plugin root directory
Resolver->>Resolver: Check CLAUDE_PLUGIN_ROOT env var
alt Env var set and non-empty
Resolver-->>App: Return env var path
end
Resolver->>FS: Check relative path for marker file
alt Marker found
Resolver-->>App: Return inferred root
end
Resolver->>FS: Check known marketplace paths
alt Marker found in marketplace
Resolver-->>App: Return marketplace path
end
Resolver->>FS: Scan cache and plugin directories
alt Marker found
Resolver-->>App: Return discovered path
end
Resolver-->>App: Return ~/.claude fallback
App->>Module: require(resolved_path + '/scripts/lib/session-manager')
Module-->>App: Module loaded
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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. Comment Tip CodeRabbit can use your project's `biome` configuration to improve the quality of JS/TS/CSS/JSON code reviews.Add a configuration file to your project to customize how CodeRabbit runs |
Greptile SummaryThis PR fixes session command failures after plugin marketplace installs by replacing the naive Key observations:
Confidence Score: 3/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Session command invoked] --> B{CLAUDE_PLUGIN_ROOT set?}
B -- Yes --> C[Use CLAUDE_PLUGIN_ROOT directly]
B -- No --> D[Check ~/.claude]
D -- Found --> C
D -- Not found --> E[Check known plugin marketplace paths\ne.g. ~/.claude/plugins/everything-claude-code]
E -- Found --> C
E -- Not found --> F[Search ~/.claude/plugins/cache/everything-claude-code/**]
F -- Found --> C
F -- Not found --> G[Fallback: use ~/.claude]
C --> H[require session-manager & session-aliases]
G --> H
H --> I[Execute session command]
subgraph "resolve-plugin-root.js ONLY"
J[Step 2: Check path relative to __dirname]
K[Step 5: Broad scan ~/.claude/plugins/**]
end
style J fill:#ffcccc,stroke:#cc0000
style K fill:#ffcccc,stroke:#cc0000
Last reviewed commit: 2e9c3ea |
| return path.join(home, '.claude'); | ||
| } | ||
|
|
||
| module.exports = { resolvePluginRoot }; |
There was a problem hiding this comment.
Shared utility is never imported anywhere
resolve-plugin-root.js is exported here and framed in the PR description as a "shared resolver utility for file-based scripts," but a codebase-wide search shows it is never require()d or referenced by any file — including the inline scripts in commands/sessions.md, which each carry their own copy-pasted IIFE instead of calling this module.
The file is effectively dead code in this PR. Either the inline scripts should be updated to use this shared module (where the inline context allows it), or the file should be removed to avoid maintenance confusion where two independent copies of the same logic can drift out of sync.
| node -e " | ||
| const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager'); | ||
| const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases'); | ||
| const _r=((e,p,f,h)=>{if(e&&e.trim())return e;const m='scripts/lib/session-manager.js';const cs=[p.join(h,'.claude'),p.join(h,'.claude','plugins','everything-claude-code'),p.join(h,'.claude','plugins','everything-claude-code@everything-claude-code'),p.join(h,'.claude','plugins','marketplace','everything-claude-code')];for(const c of cs)if(f.existsSync(p.join(c,m)))return c;const cd=p.join(h,'.claude','plugins','cache','everything-claude-code');try{for(const e of f.readdirSync(cd)){const n=p.join(cd,e);if(f.existsSync(p.join(n,m)))return n;try{for(const i of f.readdirSync(n)){const x=p.join(n,i);if(f.existsSync(p.join(x,m)))return x}}catch(_){}}}catch(_){}return p.join(h,'.claude')})(process.env.CLAUDE_PLUGIN_ROOT,require('path'),require('fs'),require('os').homedir()); |
There was a problem hiding this comment.
Inline resolver is missing "broad plugin directory search" step
The companion scripts/lib/resolve-plugin-root.js includes a step 5 that broadly scans ~/.claude/plugins and ~/.claude/plugins/marketplace for any sub-directory containing the marker file. The inline IIFE used in all 6 script blocks of sessions.md (and the 6 in docs/zh-CN/commands/sessions.md) omits this step entirely.
This means a plugin installed at an unexpected path under ~/.claude/plugins/ (e.g., a scoped or renamed plugin directory) would be found by the shared utility but would still fail with the inline resolver, leaving the bug partially unresolved for that install layout.
The two implementations need to be kept in sync, or — ideally — the inline scripts should delegate to the shared module to avoid this drift.
| node -e " | ||
| const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager'); | ||
| const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases'); | ||
| const _r=((e,p,f,h)=>{if(e&&e.trim())return e;const m='scripts/lib/session-manager.js';const cs=[p.join(h,'.claude'),p.join(h,'.claude','plugins','everything-claude-code'),p.join(h,'.claude','plugins','everything-claude-code@everything-claude-code'),p.join(h,'.claude','plugins','marketplace','everything-claude-code')];for(const c of cs)if(f.existsSync(p.join(c,m)))return c;const cd=p.join(h,'.claude','plugins','cache','everything-claude-code');try{for(const e of f.readdirSync(cd)){const n=p.join(cd,e);if(f.existsSync(p.join(n,m)))return n;try{for(const i of f.readdirSync(n)){const x=p.join(n,i);if(f.existsSync(p.join(x,m)))return x}}catch(_){}}}catch(_){}return p.join(h,'.claude')})(process.env.CLAUDE_PLUGIN_ROOT,require('path'),require('fs'),require('os').homedir()); |
There was a problem hiding this comment.
Variable shadowing of outer parameter
e by inner loop variable
In the inline IIFE ((e,p,f,h)=>{...}), the parameter e holds process.env.CLAUDE_PLUGIN_ROOT. Inside the cache-dir search, the loop for(const e of f.readdirSync(cd)) introduces a block-scoped e that shadows the outer parameter e. While JavaScript allows this and the current logic is correct (the early return guards the env-var check), the shadowing makes the code harder to reason about and is flagged as a warning by most linters.
The same pattern is repeated in all 6 inline blocks in commands/sessions.md and all 6 in docs/zh-CN/commands/sessions.md.
Consider renaming the loop variable (e.g. ent or dir) to eliminate the shadow:
| const _r=((e,p,f,h)=>{if(e&&e.trim())return e;const m='scripts/lib/session-manager.js';const cs=[p.join(h,'.claude'),p.join(h,'.claude','plugins','everything-claude-code'),p.join(h,'.claude','plugins','everything-claude-code@everything-claude-code'),p.join(h,'.claude','plugins','marketplace','everything-claude-code')];for(const c of cs)if(f.existsSync(p.join(c,m)))return c;const cd=p.join(h,'.claude','plugins','cache','everything-claude-code');try{for(const e of f.readdirSync(cd)){const n=p.join(cd,e);if(f.existsSync(p.join(n,m)))return n;try{for(const i of f.readdirSync(n)){const x=p.join(n,i);if(f.existsSync(p.join(x,m)))return x}}catch(_){}}}catch(_){}return p.join(h,'.claude')})(process.env.CLAUDE_PLUGIN_ROOT,require('path'),require('fs'),require('os').homedir()); | |
| const _r=((envRoot,p,f,h)=>{if(envRoot&&envRoot.trim())return envRoot;const m='scripts/lib/session-manager.js';const cs=[p.join(h,'.claude'),p.join(h,'.claude','plugins','everything-claude-code'),p.join(h,'.claude','plugins','everything-claude-code@everything-claude-code'),p.join(h,'.claude','plugins','marketplace','everything-claude-code')];for(const c of cs)if(f.existsSync(p.join(c,m)))return c;const cd=p.join(h,'.claude','plugins','cache','everything-claude-code');try{for(const ent of f.readdirSync(cd)){const n=p.join(cd,ent);if(f.existsSync(p.join(n,m)))return n;try{for(const i of f.readdirSync(n)){const x=p.join(n,i);if(f.existsSync(p.join(x,m)))return x}}catch(_){}}}catch(_){}return p.join(h,'.claude')})(process.env.CLAUDE_PLUGIN_ROOT,require('path'),require('fs'),require('os').homedir()); |
| const sm = require(_r+'/scripts/lib/session-manager'); | ||
| const aa = require(_r+'/scripts/lib/session-aliases'); |
There was a problem hiding this comment.
String concatenation instead of
path.join() for module paths
_r+'/scripts/lib/session-manager' concatenates the resolved root with a forward-slash-prefixed string. If _r ever ends with a path separator (unlikely with the current resolver, but possible), this produces a double-separator path. Using require('path').join(_r, 'scripts', 'lib', 'session-manager') is cross-platform safe and idiomatic.
This pattern appears in all 6 blocks of commands/sessions.md and all 6 blocks of docs/zh-CN/commands/sessions.md.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
commands/sessions.md (1)
28-30: Same minified resolver duplicated across 6 script blocks.As noted in the Chinese documentation file, this identical resolver IIFE appears 6 times. Any bug fix or path addition requires updating all 6 occurrences in both
commands/sessions.mdanddocs/zh-CN/commands/sessions.md(12 total locations). Consider documenting this synchronization requirement or extracting to a shared approach.Also applies to: 70-72, 144-146, 185-186, 215-217, 271-272
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@commands/sessions.md` around lines 28 - 30, The duplicated minified resolver IIFE assigned to _r (the plugin root resolver) is repeated across six script blocks; extract that logic into a single shared helper (e.g., a module or function named resolvePluginRoot or PLUGIN_ROOT_RESOLVER) and replace each inline IIFE with a simple require/import or call that returns the same path used by sm and aa (the require(_r+'/scripts/lib/session-manager') and require(_r+'/scripts/lib/session-aliases') sites), then update both commands/sessions.md and docs/zh-CN/commands/sessions.md to reference the shared helper so future fixes only need one change.docs/zh-CN/commands/sessions.md (1)
29-31: Consider extracting the inline resolver to reduce duplication and improve maintainability.The same minified resolver IIFE is repeated 6 times across this file (lines 29, 72, 147, 189, 220, 277). While this fixes the immediate path resolution issue, maintaining identical minified code in multiple places is error-prone.
Since the shared
scripts/lib/resolve-plugin-root.jsmodule exists, consider having the inline scripts attempt torequireit first (using the same fallback chain), falling back to the inline logic only if needed. Alternatively, document that any changes to the resolver must be synchronized across all occurrences.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/zh-CN/commands/sessions.md` around lines 29 - 31, The repeated minified IIFE stored in _r (the plugin-root resolver) is duplicated across multiple places; refactor to centralize resolution by trying to require the existing scripts/lib/resolve-plugin-root.js first and only falling back to the inline resolver logic when that require fails. Update occurrences that build paths like require(_r+'/scripts/lib/session-manager') and require(_r+'/scripts/lib/session-aliases') to obtain _r via a single helper function (e.g., resolvePluginRoot) that attempts require('scripts/lib/resolve-plugin-root.js') and on failure runs the current fallback chain, so all six instances use the same shared resolution code. Ensure the helper preserves the same return value and behavior as the original IIFE so existing requires keep working.scripts/lib/resolve-plugin-root.js (1)
50-52: Silent error swallowing makes debugging difficult.The empty
catch(_){}blocks discard errors without any indication of what failed. While graceful fallback is appropriate here, consider logging at debug level or at least preserving the error context for troubleshooting. Permission errors (EACCES) in particular might indicate configuration issues users should address.Optional: Add debug logging
- } catch (_) { /* ignore */ } + } catch (err) { + // Debug: console.error('Cache inner scan failed:', err.code); + } } - } catch (_) { /* ignore */ } + } catch (err) { + // Debug: console.error('Cache scan failed:', err.code); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/lib/resolve-plugin-root.js` around lines 50 - 52, The two empty catch blocks in resolve-plugin-root.js silently swallow errors; replace each catch (_){ /* ignore */ } with a catch (err) { /* log debug-level info about err (err.message, err.code) or forward to existing logger */ } so failures (especially EACCES) are recorded for troubleshooting; use the project's debug/logger helper (or console.debug if none) and include the error object and a short context string near the resolvePluginRoot logic to preserve error context while still allowing fallback behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@commands/sessions.md`:
- Around line 28-30: The inline resolver (the IIFE assigned to _r) builds a
candidates array cs that places p.join(h,'.claude') first, which differs from
the shared resolver in scripts/lib/resolve-plugin-root.js; update the cs
ordering inside that IIFE to match the shared module's priority order (i.e.,
move the ~/.claude entry to be the final fallback like the shared resolver),
keeping the subsequent cache directory scan (variable cd and its readdirSync
loops) and the final return p.join(h,'.claude') behavior intact so resolution
priority is consistent with resolve-plugin-root.js.
In `@scripts/lib/resolve-plugin-root.js`:
- Around line 11-14: The code currently returns process.env.CLAUDE_PLUGIN_ROOT
immediately; instead validate that the provided path actually exists and
contains the expected marker file before returning. In resolve-plugin-root.js,
when checking process.env.CLAUDE_PLUGIN_ROOT, resolve the env path, verify the
directory exists and that the marker (the same marker used elsewhere in this
module) is present (e.g., check path.join(envPath, MARKER_FILENAME) exists), and
only return the env value if those checks pass; otherwise log or warn and fall
through to the existing fallback resolution logic so invalid env values don't
bypass lookup.
---
Nitpick comments:
In `@commands/sessions.md`:
- Around line 28-30: The duplicated minified resolver IIFE assigned to _r (the
plugin root resolver) is repeated across six script blocks; extract that logic
into a single shared helper (e.g., a module or function named resolvePluginRoot
or PLUGIN_ROOT_RESOLVER) and replace each inline IIFE with a simple
require/import or call that returns the same path used by sm and aa (the
require(_r+'/scripts/lib/session-manager') and
require(_r+'/scripts/lib/session-aliases') sites), then update both
commands/sessions.md and docs/zh-CN/commands/sessions.md to reference the shared
helper so future fixes only need one change.
In `@docs/zh-CN/commands/sessions.md`:
- Around line 29-31: The repeated minified IIFE stored in _r (the plugin-root
resolver) is duplicated across multiple places; refactor to centralize
resolution by trying to require the existing scripts/lib/resolve-plugin-root.js
first and only falling back to the inline resolver logic when that require
fails. Update occurrences that build paths like
require(_r+'/scripts/lib/session-manager') and
require(_r+'/scripts/lib/session-aliases') to obtain _r via a single helper
function (e.g., resolvePluginRoot) that attempts
require('scripts/lib/resolve-plugin-root.js') and on failure runs the current
fallback chain, so all six instances use the same shared resolution code. Ensure
the helper preserves the same return value and behavior as the original IIFE so
existing requires keep working.
In `@scripts/lib/resolve-plugin-root.js`:
- Around line 50-52: The two empty catch blocks in resolve-plugin-root.js
silently swallow errors; replace each catch (_){ /* ignore */ } with a catch
(err) { /* log debug-level info about err (err.message, err.code) or forward to
existing logger */ } so failures (especially EACCES) are recorded for
troubleshooting; use the project's debug/logger helper (or console.debug if
none) and include the error object and a short context string near the
resolvePluginRoot logic to preserve error context while still allowing fallback
behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 44461a69-4883-4904-a668-87170079818b
📒 Files selected for processing (3)
commands/sessions.mddocs/zh-CN/commands/sessions.mdscripts/lib/resolve-plugin-root.js
| const _r=((e,p,f,h)=>{if(e&&e.trim())return e;const m='scripts/lib/session-manager.js';const cs=[p.join(h,'.claude'),p.join(h,'.claude','plugins','everything-claude-code'),p.join(h,'.claude','plugins','everything-claude-code@everything-claude-code'),p.join(h,'.claude','plugins','marketplace','everything-claude-code')];for(const c of cs)if(f.existsSync(p.join(c,m)))return c;const cd=p.join(h,'.claude','plugins','cache','everything-claude-code');try{for(const e of f.readdirSync(cd)){const n=p.join(cd,e);if(f.existsSync(p.join(n,m)))return n;try{for(const i of f.readdirSync(n)){const x=p.join(n,i);if(f.existsSync(p.join(x,m)))return x}}catch(_){}}}catch(_){}return p.join(h,'.claude')})(process.env.CLAUDE_PLUGIN_ROOT,require('path'),require('fs'),require('os').homedir()); | ||
| const sm = require(_r+'/scripts/lib/session-manager'); | ||
| const aa = require(_r+'/scripts/lib/session-aliases'); |
There was a problem hiding this comment.
Inline resolver differs from the shared module in search order.
The inline resolver's candidate array starts with ~/.claude as the first location to check:
const cs=[p.join(h,'.claude'), p.join(h,'.claude','plugins','everything-claude-code'), ...]However, scripts/lib/resolve-plugin-root.js checks ~/.claude only as the final fallback (line 72). This inconsistency could cause the inline scripts to resolve to a different path than code using the shared module.
To maintain consistency, the inline resolver should match the shared module's priority order.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@commands/sessions.md` around lines 28 - 30, The inline resolver (the IIFE
assigned to _r) builds a candidates array cs that places p.join(h,'.claude')
first, which differs from the shared resolver in
scripts/lib/resolve-plugin-root.js; update the cs ordering inside that IIFE to
match the shared module's priority order (i.e., move the ~/.claude entry to be
the final fallback like the shared resolver), keeping the subsequent cache
directory scan (variable cd and its readdirSync loops) and the final return
p.join(h,'.claude') behavior intact so resolution priority is consistent with
resolve-plugin-root.js.
| // 1. Env var (always wins) | ||
| if (process.env.CLAUDE_PLUGIN_ROOT && process.env.CLAUDE_PLUGIN_ROOT.trim()) { | ||
| return process.env.CLAUDE_PLUGIN_ROOT; | ||
| } |
There was a problem hiding this comment.
Environment variable is trusted without validation, bypassing all fallback strategies.
If CLAUDE_PLUGIN_ROOT is set to an invalid or non-existent path, the function returns it immediately without checking if the marker file exists. This could lead to confusing MODULE_NOT_FOUND errors downstream, defeating the purpose of the multi-path resolver.
Consider validating the env var path before returning:
Proposed fix
// 1. Env var (always wins)
if (process.env.CLAUDE_PLUGIN_ROOT && process.env.CLAUDE_PLUGIN_ROOT.trim()) {
- return process.env.CLAUDE_PLUGIN_ROOT;
+ const envRoot = process.env.CLAUDE_PLUGIN_ROOT.trim();
+ if (fs.existsSync(path.join(envRoot, MARKER))) {
+ return envRoot;
+ }
+ // Env var set but invalid - fall through to other strategies
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/lib/resolve-plugin-root.js` around lines 11 - 14, The code currently
returns process.env.CLAUDE_PLUGIN_ROOT immediately; instead validate that the
provided path actually exists and contains the expected marker file before
returning. In resolve-plugin-root.js, when checking
process.env.CLAUDE_PLUGIN_ROOT, resolve the env path, verify the directory
exists and that the marker (the same marker used elsewhere in this module) is
present (e.g., check path.join(envPath, MARKER_FILENAME) exists), and only
return the env value if those checks pass; otherwise log or warn and fall
through to the existing fallback resolution logic so invalid env values don't
bypass lookup.
There was a problem hiding this comment.
4 issues found across 3 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="scripts/lib/resolve-plugin-root.js">
<violation number="1" location="scripts/lib/resolve-plugin-root.js:47">
P2: Cache resolution returns the first directory hit from unsorted `readdirSync`, which can select a stale plugin version.</violation>
</file>
<file name="commands/sessions.md">
<violation number="1" location="commands/sessions.md:28">
P2: Plugin root resolution in cache is first-match and non-version-aware, so it can load a stale cached plugin version when multiple versions exist.</violation>
<violation number="2" location="commands/sessions.md:28">
P2: Add the broad plugin-directory scan before falling back to `~/.claude`; the inline resolver currently omits that step, so installs under non-canonical subdirectories can still fail to locate `session-manager`.</violation>
</file>
<file name="docs/zh-CN/commands/sessions.md">
<violation number="1" location="docs/zh-CN/commands/sessions.md:29">
P2: Inline root resolver can load stale/inactive plugin versions due to first-match precedence and unsorted cache traversal.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| for (const inner of fs.readdirSync(nested)) { | ||
| const deep = path.join(nested, inner); | ||
| if (fs.statSync(deep).isDirectory() && fs.existsSync(path.join(deep, MARKER))) { | ||
| return deep; |
There was a problem hiding this comment.
P2: Cache resolution returns the first directory hit from unsorted readdirSync, which can select a stale plugin version.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/lib/resolve-plugin-root.js, line 47:
<comment>Cache resolution returns the first directory hit from unsorted `readdirSync`, which can select a stale plugin version.</comment>
<file context>
@@ -0,0 +1,75 @@
+ for (const inner of fs.readdirSync(nested)) {
+ const deep = path.join(nested, inner);
+ if (fs.statSync(deep).isDirectory() && fs.existsSync(path.join(deep, MARKER))) {
+ return deep;
+ }
+ }
</file context>
| node -e " | ||
| const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager'); | ||
| const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases'); | ||
| const _r=((e,p,f,h)=>{if(e&&e.trim())return e;const m='scripts/lib/session-manager.js';const cs=[p.join(h,'.claude'),p.join(h,'.claude','plugins','everything-claude-code'),p.join(h,'.claude','plugins','everything-claude-code@everything-claude-code'),p.join(h,'.claude','plugins','marketplace','everything-claude-code')];for(const c of cs)if(f.existsSync(p.join(c,m)))return c;const cd=p.join(h,'.claude','plugins','cache','everything-claude-code');try{for(const e of f.readdirSync(cd)){const n=p.join(cd,e);if(f.existsSync(p.join(n,m)))return n;try{for(const i of f.readdirSync(n)){const x=p.join(n,i);if(f.existsSync(p.join(x,m)))return x}}catch(_){}}}catch(_){}return p.join(h,'.claude')})(process.env.CLAUDE_PLUGIN_ROOT,require('path'),require('fs'),require('os').homedir()); |
There was a problem hiding this comment.
P2: Plugin root resolution in cache is first-match and non-version-aware, so it can load a stale cached plugin version when multiple versions exist.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At commands/sessions.md, line 28:
<comment>Plugin root resolution in cache is first-match and non-version-aware, so it can load a stale cached plugin version when multiple versions exist.</comment>
<file context>
@@ -25,8 +25,9 @@ Use `/sessions info` when you need operator-surface context for a swarm: branch,
node -e "
-const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager');
-const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
+const _r=((e,p,f,h)=>{if(e&&e.trim())return e;const m='scripts/lib/session-manager.js';const cs=[p.join(h,'.claude'),p.join(h,'.claude','plugins','everything-claude-code'),p.join(h,'.claude','plugins','everything-claude-code@everything-claude-code'),p.join(h,'.claude','plugins','marketplace','everything-claude-code')];for(const c of cs)if(f.existsSync(p.join(c,m)))return c;const cd=p.join(h,'.claude','plugins','cache','everything-claude-code');try{for(const e of f.readdirSync(cd)){const n=p.join(cd,e);if(f.existsSync(p.join(n,m)))return n;try{for(const i of f.readdirSync(n)){const x=p.join(n,i);if(f.existsSync(p.join(x,m)))return x}}catch(_){}}}catch(_){}return p.join(h,'.claude')})(process.env.CLAUDE_PLUGIN_ROOT,require('path'),require('fs'),require('os').homedir());
+const sm = require(_r+'/scripts/lib/session-manager');
+const aa = require(_r+'/scripts/lib/session-aliases');
</file context>
| node -e " | ||
| const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager'); | ||
| const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases'); | ||
| const _r=((e,p,f,h)=>{if(e&&e.trim())return e;const m='scripts/lib/session-manager.js';const cs=[p.join(h,'.claude'),p.join(h,'.claude','plugins','everything-claude-code'),p.join(h,'.claude','plugins','everything-claude-code@everything-claude-code'),p.join(h,'.claude','plugins','marketplace','everything-claude-code')];for(const c of cs)if(f.existsSync(p.join(c,m)))return c;const cd=p.join(h,'.claude','plugins','cache','everything-claude-code');try{for(const e of f.readdirSync(cd)){const n=p.join(cd,e);if(f.existsSync(p.join(n,m)))return n;try{for(const i of f.readdirSync(n)){const x=p.join(n,i);if(f.existsSync(p.join(x,m)))return x}}catch(_){}}}catch(_){}return p.join(h,'.claude')})(process.env.CLAUDE_PLUGIN_ROOT,require('path'),require('fs'),require('os').homedir()); |
There was a problem hiding this comment.
P2: Inline root resolver can load stale/inactive plugin versions due to first-match precedence and unsorted cache traversal.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/zh-CN/commands/sessions.md, line 29:
<comment>Inline root resolver can load stale/inactive plugin versions due to first-match precedence and unsorted cache traversal.</comment>
<file context>
@@ -26,8 +26,9 @@
node -e "
-const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager');
-const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
+const _r=((e,p,f,h)=>{if(e&&e.trim())return e;const m='scripts/lib/session-manager.js';const cs=[p.join(h,'.claude'),p.join(h,'.claude','plugins','everything-claude-code'),p.join(h,'.claude','plugins','everything-claude-code@everything-claude-code'),p.join(h,'.claude','plugins','marketplace','everything-claude-code')];for(const c of cs)if(f.existsSync(p.join(c,m)))return c;const cd=p.join(h,'.claude','plugins','cache','everything-claude-code');try{for(const e of f.readdirSync(cd)){const n=p.join(cd,e);if(f.existsSync(p.join(n,m)))return n;try{for(const i of f.readdirSync(n)){const x=p.join(n,i);if(f.existsSync(p.join(x,m)))return x}}catch(_){}}}catch(_){}return p.join(h,'.claude')})(process.env.CLAUDE_PLUGIN_ROOT,require('path'),require('fs'),require('os').homedir());
+const sm = require(_r+'/scripts/lib/session-manager');
+const aa = require(_r+'/scripts/lib/session-aliases');
</file context>
| node -e " | ||
| const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager'); | ||
| const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases'); | ||
| const _r=((e,p,f,h)=>{if(e&&e.trim())return e;const m='scripts/lib/session-manager.js';const cs=[p.join(h,'.claude'),p.join(h,'.claude','plugins','everything-claude-code'),p.join(h,'.claude','plugins','everything-claude-code@everything-claude-code'),p.join(h,'.claude','plugins','marketplace','everything-claude-code')];for(const c of cs)if(f.existsSync(p.join(c,m)))return c;const cd=p.join(h,'.claude','plugins','cache','everything-claude-code');try{for(const e of f.readdirSync(cd)){const n=p.join(cd,e);if(f.existsSync(p.join(n,m)))return n;try{for(const i of f.readdirSync(n)){const x=p.join(n,i);if(f.existsSync(p.join(x,m)))return x}}catch(_){}}}catch(_){}return p.join(h,'.claude')})(process.env.CLAUDE_PLUGIN_ROOT,require('path'),require('fs'),require('os').homedir()); |
There was a problem hiding this comment.
P2: Add the broad plugin-directory scan before falling back to ~/.claude; the inline resolver currently omits that step, so installs under non-canonical subdirectories can still fail to locate session-manager.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At commands/sessions.md, line 28:
<comment>Add the broad plugin-directory scan before falling back to `~/.claude`; the inline resolver currently omits that step, so installs under non-canonical subdirectories can still fail to locate `session-manager`.</comment>
<file context>
@@ -25,8 +25,9 @@ Use `/sessions info` when you need operator-surface context for a swarm: branch,
node -e "
-const sm = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-manager');
-const aa = require((process.env.CLAUDE_PLUGIN_ROOT||require('path').join(require('os').homedir(),'.claude'))+'/scripts/lib/session-aliases');
+const _r=((e,p,f,h)=>{if(e&&e.trim())return e;const m='scripts/lib/session-manager.js';const cs=[p.join(h,'.claude'),p.join(h,'.claude','plugins','everything-claude-code'),p.join(h,'.claude','plugins','everything-claude-code@everything-claude-code'),p.join(h,'.claude','plugins','marketplace','everything-claude-code')];for(const c of cs)if(f.existsSync(p.join(c,m)))return c;const cd=p.join(h,'.claude','plugins','cache','everything-claude-code');try{for(const e of f.readdirSync(cd)){const n=p.join(cd,e);if(f.existsSync(p.join(n,m)))return n;try{for(const i of f.readdirSync(n)){const x=p.join(n,i);if(f.existsSync(p.join(x,m)))return x}}catch(_){}}}catch(_){}return p.join(h,'.claude')})(process.env.CLAUDE_PLUGIN_ROOT,require('path'),require('fs'),require('os').homedir());
+const sm = require(_r+'/scripts/lib/session-manager');
+const aa = require(_r+'/scripts/lib/session-aliases');
</file context>
|
Thanks for fixing the session command path resolution! The root issue (finding the plugin root across different install locations) is legitimate. However, the approach has a readability concern: the path resolution logic is an inlined, heavily minified IIFE that's very difficult to read and maintain: ```js This is repeated in every subcommand across both Suggestion: You've already created ```js Or if the path to the module itself is the chicken-and-egg problem, at least format the inline version with readable variable names and line breaks. |
|
sure will do |
|
Closing — superseded by recent changes. The session command path issues have been addressed in later PRs. |
Summary
CLAUDE_PLUGIN_ROOTis not set~/.claudefallback with multi-path plugin root resolverProblem
When ECC is installed via the Claude Code plugin marketplace,
/everything-claude-code:sessionscommands fail with:Cannot find module '/home/.../.claude/scripts/lib/session-manager'
The inline scripts fall back to
~/.claudewhenCLAUDE_PLUGIN_ROOTis not set, but the plugin scripts actually live in~/.claude/plugins/cache/everything-claude-code/everything-claude-code/<version>/.Changes
CLAUDE_PLUGIN_ROOT || ~/.claudefallback in all 6 inline script blocks incommands/sessions.mdwith a self-contained multi-path resolver that searches:CLAUDE_PLUGIN_ROOTenv var (if set)~/.claude(manual/local install)plugins/everything-claude-code,plugins/marketplace/everything-claude-code, etc.)plugins/cache/everything-claude-code/**/)scripts/lib/resolve-plugin-root.js— shared resolver utility for file-based scriptsdocs/zh-CN/commands/sessions.mdTest Plan
CLAUDE_PLUGIN_ROOTsetCLAUDE_PLUGIN_ROOTis setsession-managerandsession-aliasesmodules load successfullySummary by cubic
Fixes broken session commands after marketplace install by replacing the
~/.claudefallback with a multi-path plugin root resolver./everything-claude-code:sessionsnow works even whenCLAUDE_PLUGIN_ROOTis not set.scripts/lib/resolve-plugin-root.jsto resolve the plugin root viaCLAUDE_PLUGIN_ROOT, local installs, known marketplace paths, and cache directories; falls back to~/.claude.commands/sessions.md; mirrored indocs/zh-CN/commands/sessions.md.session-managerandsession-aliasesload across install modes.Written for commit 2e9c3ea. Summary will update on new commits.
Summary by CodeRabbit
Documentation
Improvements