Skip to content

fix: session commnad path issue#551

Closed
AryanTejani wants to merge 1 commit intoaffaan-m:mainfrom
AryanTejani:fix/session-command-issue
Closed

fix: session commnad path issue#551
AryanTejani wants to merge 1 commit intoaffaan-m:mainfrom
AryanTejani:fix/session-command-issue

Conversation

@AryanTejani
Copy link
Contributor

@AryanTejani AryanTejani commented Mar 17, 2026

Summary

  • Fix session commands failing after plugin marketplace install when CLAUDE_PLUGIN_ROOT is not set
  • Replace broken ~/.claude fallback with multi-path plugin root resolver

Problem

When ECC is installed via the Claude Code plugin marketplace, /everything-claude-code:sessions commands fail with:
Cannot find module '/home/.../.claude/scripts/lib/session-manager'
The inline scripts fall back to ~/.claude when CLAUDE_PLUGIN_ROOT is not set, but the plugin scripts actually live in
~/.claude/plugins/cache/everything-claude-code/everything-claude-code/<version>/.

Changes

  • Replace the broken CLAUDE_PLUGIN_ROOT || ~/.claude fallback in all 6 inline script blocks in commands/sessions.md with a self-contained multi-path resolver that searches:
    1. CLAUDE_PLUGIN_ROOT env var (if set)
    2. ~/.claude (manual/local install)
    3. Known plugin paths (plugins/everything-claude-code, plugins/marketplace/everything-claude-code, etc.)
    4. Plugin cache directories (plugins/cache/everything-claude-code/**/)
  • Add scripts/lib/resolve-plugin-root.js — shared resolver utility for file-based scripts
  • Apply the same fix to docs/zh-CN/commands/sessions.md

Test Plan

  • Verified resolver finds plugin in cache directory without CLAUDE_PLUGIN_ROOT set
  • Verified resolver uses env var when CLAUDE_PLUGIN_ROOT is set
  • Both session-manager and session-aliases modules load successfully
  • Existing test suite passes (1183/1186, 3 pre-existing failures in install-apply)

Summary by cubic

Fixes broken session commands after marketplace install by replacing the ~/.claude fallback with a multi-path plugin root resolver. /everything-claude-code:sessions now works even when CLAUDE_PLUGIN_ROOT is not set.

  • Bug Fixes
    • Added scripts/lib/resolve-plugin-root.js to resolve the plugin root via CLAUDE_PLUGIN_ROOT, local installs, known marketplace paths, and cache directories; falls back to ~/.claude.
    • Replaced path logic in all session command inline scripts in commands/sessions.md; mirrored in docs/zh-CN/commands/sessions.md.
    • Verified both session-manager and session-aliases load across install modes.

Written for commit 2e9c3ea. Summary will update on new commits.

Summary by CodeRabbit

  • Documentation

    • Updated session command documentation with new code examples reflecting standardized module resolution patterns.
  • Improvements

    • Implemented enhanced plugin discovery mechanism that supports multiple installation paths and configurations, improving system reliability and compatibility across diverse deployment environments.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 17, 2026

📝 Walkthrough

Walkthrough

A 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

Cohort / File(s) Summary
Documentation Examples
commands/sessions.md, docs/zh-CN/commands/sessions.md
Updated require statements across session-related command examples to use dynamic path resolver instead of hard-coded environment-rooted paths. Applied consistently across list, load, alias, unalias, info, and list-aliases commands.
Plugin Root Resolution
scripts/lib/resolve-plugin-root.js
New module introducing resolvePluginRoot() function that implements a prioritized strategy to locate plugin root: checks CLAUDE_PLUGIN_ROOT env var, relative paths, known marketplace locations, cache directories with deep search, and falls back to ~/.claude default.

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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • PR #142: Introduced session-manager and session-aliases modules that are now dynamically resolved by the new resolvePluginRoot() function instead of using hard-coded paths.

Poem

🐰 A rabbit hops through paths so varied,
No more hard-codes—the roots are carried,
Dynamic resolvers find their way,
Through cache and homes, come what may,
Plugins dance in layouts free,
One function brings harmony! 🎩✨

🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title addresses the main issue (session command path problem) but contains a typo: 'commnad' instead of 'command', reducing clarity.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

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.

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 biome.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 17, 2026

Greptile Summary

This PR fixes session command failures after plugin marketplace installs by replacing the naive CLAUDE_PLUGIN_ROOT || ~/.claude fallback with a multi-path resolver IIFE that searches known install layouts and versioned cache directories. The fix is applied to all 6 inline scripts in both commands/sessions.md and docs/zh-CN/commands/sessions.md, and a companion scripts/lib/resolve-plugin-root.js utility is introduced.

Key observations:

  • Dead code: resolve-plugin-root.js is exported but never imported anywhere in the codebase. The inline scripts do not delegate to it, so it provides no runtime value in this PR.
  • Resolver inconsistency: The shared utility includes two extra resolution steps (check relative to __dirname, and a broad scan of ~/.claude/plugins/**) that the inline IIFE omits, meaning the two implementations can already produce different results for some install layouts.
  • 12 copies of duplicated logic: The inline IIFE is copy-pasted 12 times across the two Markdown files; any future change to the resolver must be applied 12 places.
  • Variable shadowing: The outer IIFE parameter e (holding process.env.CLAUDE_PLUGIN_ROOT) is shadowed by the for(const e of ...) loop variable in all 12 occurrences.
  • String concatenation for paths: _r+'/scripts/lib/...' is used instead of path.join() in all require calls.

Confidence Score: 3/5

  • The fix addresses a real regression but introduces dead code and two resolver implementations that are already diverging — needs cleanup before merge.
  • The core resolver logic is functionally correct and solves the reported marketplace install failure. However, the newly added shared utility (resolve-plugin-root.js) is never referenced anywhere, the inline IIFE is more limited than the utility it was supposed to complement, and the 12-copy duplication will become a maintenance burden. These are not runtime blockers for the primary bug fix, but the dead-code file and resolver inconsistency lower confidence.
  • scripts/lib/resolve-plugin-root.js — confirm whether it is intentionally unused or if there are missing consumers; commands/sessions.md — all 6 inline script blocks carry the same issues (missing step 5, variable shadowing, string concatenation).

Important Files Changed

Filename Overview
scripts/lib/resolve-plugin-root.js New shared resolver utility with 6-step plugin root discovery logic, but it is never imported or referenced anywhere in the codebase — making it dead code in this PR.
commands/sessions.md Replaces the simple `CLAUDE_PLUGIN_ROOT
docs/zh-CN/commands/sessions.md Mirror of commands/sessions.md changes — same inline IIFE resolver applied to all 6 script blocks, carrying the same inconsistencies with the shared utility.

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
Loading

Last reviewed commit: 2e9c3ea

return path.join(home, '.claude');
}

module.exports = { resolvePluginRoot };
Copy link
Contributor

Choose a reason for hiding this comment

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

P2 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());
Copy link
Contributor

Choose a reason for hiding this comment

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

P2 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());
Copy link
Contributor

Choose a reason for hiding this comment

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

P2 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:

Suggested change
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());

Comment on lines +29 to +30
const sm = require(_r+'/scripts/lib/session-manager');
const aa = require(_r+'/scripts/lib/session-aliases');
Copy link
Contributor

Choose a reason for hiding this comment

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

P2 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.

Copy link
Contributor

@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 (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.md and docs/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.js module exists, consider having the inline scripts attempt to require it 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

📥 Commits

Reviewing files that changed from the base of the PR and between 7cf07ca and 2e9c3ea.

📒 Files selected for processing (3)
  • commands/sessions.md
  • docs/zh-CN/commands/sessions.md
  • scripts/lib/resolve-plugin-root.js

Comment on lines +28 to +30
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');
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.

Comment on lines +11 to +14
// 1. Env var (always wins)
if (process.env.CLAUDE_PLUGIN_ROOT && process.env.CLAUDE_PLUGIN_ROOT.trim()) {
return process.env.CLAUDE_PLUGIN_ROOT;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

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;
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 17, 2026

Choose a reason for hiding this comment

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

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>
Fix with Cubic

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());
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 17, 2026

Choose a reason for hiding this comment

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

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>
Fix with Cubic

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());
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 17, 2026

Choose a reason for hiding this comment

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

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>
Fix with Cubic

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());
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 17, 2026

Choose a reason for hiding this comment

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

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>
Fix with Cubic

@affaan-m
Copy link
Owner

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
const _r=((e,p,f,h)=>{if(e&&e.trim())return e;const m='scripts/lib/session-manager.js';...
```

This is repeated in every subcommand across both commands/sessions.md and docs/zh-CN/commands/sessions.md.

Suggestion: You've already created scripts/lib/resolve-plugin-root.js (good!). Could you use a readable require of that module instead of the inlined minified code? Something like:

```js
const _r = require('./scripts/lib/resolve-plugin-root')();
```

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.

@AryanTejani
Copy link
Contributor Author

sure will do

@affaan-m
Copy link
Owner

Closing — superseded by recent changes. The session command path issues have been addressed in later PRs.

@affaan-m affaan-m closed this Mar 20, 2026
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