Skip to content

feat: add ccs plugin for symlink auto-setup and delegation workflows#96

Open
masseater wants to merge 2 commits into
masterfrom
feat/ccs-plugin
Open

feat: add ccs plugin for symlink auto-setup and delegation workflows#96
masseater wants to merge 2 commits into
masterfrom
feat/ccs-plugin

Conversation

@masseater
Copy link
Copy Markdown
Owner

@masseater masseater commented Jun 3, 2026

Summary

  • CCS (Claude Code Sessions) 環境管理を独立プラグインとして新規追加
  • SessionStart hook で CCS シンボリックリンクチェーンの自動検証・修復を実行
  • ccs-delegation スキルで最適プロファイル選択による CCS CLI 委譲を提供
  • ccs-handoff スキルをmutilsから移行し、プロファイル間のセッション引き継ぎを実現
  • /ccs /ccs:continue スラッシュコマンドを追加

Test plan

  • bun install 後に hook が正常にロードされることを確認
  • SessionStart で CCS symlink chain が検証・修復されることを確認
  • /ccs コマンドでプロファイル選択・委譲が動作することを確認
  • /ccs:continue で前回セッション継続が動作することを確認
  • ccs-handoff スキルが mutils から正しく移行されていることを確認

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced CCS (Claude Code Sessions) plugin for environment autoconfiguration and session management
    • Added /ccs and /ccs:continue commands for delegating work to models with automatic profile selection
    • Added new delegation and handoff skills for enhanced task management
  • Documentation

    • Added comprehensive guides covering plugin usage, command syntax, configuration, and troubleshooting

masseater and others added 2 commits June 3, 2026 12:33
Move ccs-handoff skill from mutils to the new ccs plugin and integrate
ccs-delegation skill and /ccs commands. The SessionStart hook validates
and repairs the CCS shared-context symlink chain automatically.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces a new ccs (Claude Code Sessions) plugin that enables environment autoconfiguration, session management, and task delegation workflows. The plugin includes SessionStart hooks for dependency installation and symlink synchronization, slash commands for task delegation and continuation, a comprehensive delegation skill specification, and corresponding plugin registry updates.

Changes

Claude Code Sessions Plugin

Layer / File(s) Summary
Plugin metadata and configuration
plugins/ccs/plugin.json, plugins/ccs/package.json, plugins/ccs/tsconfig.json, plugins/ccs/hooks/hooks.json, plugins/ccs/AGENTS.md, plugins/ccs/CLAUDE.md
Plugin defines metadata (name, version, author), ESM TypeScript module with linting/check/typecheck scripts and dependencies (cc-plugin-lib, yaml), TypeScript config extending shared @repo/ts-config, and hook entry point configuration for SessionStart initialization. Plugin documentation describes purpose, features, and development commands.
SessionStart hook implementations
plugins/ccs/hooks/install-deps.sh, plugins/ccs/hooks/entry/ccs-symlink-check.ts
Dependency installation hook strips dev dependencies and installs production packages into CLAUDE_PLUGIN_ROOT. Symlink check hook reads ~/.ccs/config.yaml, filters accounts by context_mode: "shared", and establishes symlink relationships between instance directories (~/.ccs/instances/<name>) and shared contexts (~/.ccs/shared/..., ~/.claude/...) for projects, sessions, and continuity subdirectories (file-history, session-env, shell-snapshots, todos).
Command definitions for delegation
plugins/ccs/commands/ccs.md, plugins/ccs/commands/ccs/continue.md
/ccs command documentation describes parsing arguments, profile selection from ~/.ccs/config.json, prompt enhancement, and delegation via CCS CLI with examples (simple tasks, long-context, reasoning, forcing profile, nested commands). /ccs:continue resumes the last delegation session by detecting the profile from ~/.ccs/delegation-sessions.json, parsing follow-up arguments, and executing via CCS CLI.
CCS delegation skill specification and documentation
plugins/ccs/skills/ccs-delegation/SKILL.md, plugins/ccs/skills/ccs-delegation/CLAUDE.md.template, plugins/ccs/skills/ccs-delegation/references/troubleshooting.md
Skill spec v3.0.0 defines delegation concept, invocation patterns for /ccs [task] and /ccs:continue [follow-up], protocol including override-flag parsing (--{profile}), profile discovery, deterministic selection logic, prompt enhancement, Bash execution, and result reporting (cost/duration/session/exit code). Skill template documents activation, auto-delegation rules, and example prompts. Troubleshooting guide provides quick-reference error codes, comprehensive error catalog with root causes/resolutions, resolution patterns (validation/session/debug), diagnostic toolkit (profile/test delegation), and emergency recovery steps.
Skill relocation and namespace migration
plugins/ccs/skills/ccs-handoff/SKILL.md, plugins/mutils/AGENTS.md
The ccs-handoff skill identifier is updated from mutils:ccs-handoff to ccs:ccs-handoff as the skill transitions into the new CCS plugin namespace. The mutils plugin registry entry for the old identifier is removed.
Plugin registry and build system integration
AGENTS.md, .claude-plugin/marketplace.json, knip.json
Root AGENTS.md includes new CCS plugin entry in the auto-generated plugin list. Marketplace configuration is reformatted (whitespace/indentation normalized). Build configuration adds plugins/ccs workspace with entry globs for hooks/entry/*.ts and skills/*/*.ts.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • masseater/claude-code-plugin#80: Establishes the SessionStart hook pattern with install-deps.sh, which is reused directly in this PR's hook configuration.
  • masseater/claude-code-plugin#75: Updates to plugins/mutils/AGENTS.md and the ccs-handoff skill documentation that overlap with this PR's skill namespace migration.
  • masseater/claude-code-plugin#77: Standardizes skill identifiers to the <plugin>:<skill> namespace format, matching the mutils:ccs-handoffccs:ccs-handoff rename in this PR.

Poem

🐰 A plugin springs forth to delegate with care,
SessionStart hooks prepare the land so fair,
Skills weave through /ccs commands, swift and true,
Symlinks dance between the old and new! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main changes: adding a new CCS plugin with symlink auto-setup and delegation workflows as documented in the objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ccs-plugin
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feat/ccs-plugin

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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugins/ccs/commands/ccs.md`:
- Around line 12-18: Add a language identifier to the fenced code block that
shows the /ccs examples so markdownlint MD040 is satisfied; locate the
triple-backtick block containing the lines beginning with "/ccs \"refactor
auth.js to use async/await\"", and change the opening fence from ``` to ```text
(or another appropriate language tag) so the example is explicitly marked as
plain text.

In `@plugins/ccs/commands/ccs/continue.md`:
- Around line 12-16: The fenced code block containing the example slash commands
(the three lines starting with "/ccs:continue ...") is missing a fence language
and triggers markdownlint MD040; update the opening triple-backtick so it
declares a language (e.g., ```text) for that code fence in
plugins/ccs/commands/ccs/continue.md so the example block that includes the
"/ccs:continue" variants is marked with a language identifier.

In `@plugins/ccs/hooks/entry/ccs-symlink-check.ts`:
- Around line 26-39: ensureSymlink currently never makes changes and only
handles the "missing target" case; update it to repair broken or wrong-type
entries and create the symlink when needed. Specifically, in function
ensureSymlink(linkPath, target) check if linkPath exists: if it's a symlink and
already points to target return false; otherwise remove the existing
file/dir/symlink (handle directories, files, and dangling symlinks), ensure
parent dir exists via ensureDir(path.dirname(linkPath)), ensure target exists
via ensureDir(target) if necessary, create the symlink with
fs.symlinkSync(target, linkPath), and return true to indicate a change. Apply
the same fix to the sibling occurrence referenced in the review (the other block
at lines 127-135).
- Around line 96-107: The parsed YAML may be null or not an object so avoid
directly dereferencing config.accounts; after calling parseYaml(raw) in the try
block, validate that config is a non-null object (e.g. typeof config ===
"object" && config !== null) and that it has an accounts property (or use
optional chaining) before assigning const accounts = config.accounts; if the
shape is invalid, return context.success({}) as currently done; update
references to the symbols parseYaml, config, configPath, and accounts to include
this guard so malformed/empty configs do not throw at runtime.
- Around line 64-83: The replaceWithSymlink function currently falls through and
calls fs.symlinkSync(target, src) when src is an existing regular file, causing
EEXIST; update replaceWithSymlink to handle regular files by removing them
before creating the symlink (e.g., detect stat.isFile() or !isSymbolicLink() &&
!isDirectory() and call fs.unlinkSync(src) prior to fs.symlinkSync(target,
src)), preserving the existing behaviors for symlinks and directories (keep the
current checks for stat.isSymbolicLink() and stat.isDirectory() and reuse
copyDirContents for directories).

In `@plugins/ccs/hooks/install-deps.sh`:
- Around line 6-11: The install marker node_modules/.cc-installed is never
invalidated when package.json changes; update the script so it records a
checksum or timestamp of package.json alongside the marker and compares it on
startup: compute a hash (or mtime) of package.json, read the stored value from
node_modules/.cc-installed (or a companion file), and only exit early if they
match; otherwise run the existing steps (delete bun.lock, bun install
--production, mkdir -p node_modules, touch/update node_modules/.cc-installed)
and write the new checksum/timestamp into the marker so future runs detect
changes.

In `@plugins/ccs/package.json`:
- Around line 10-17: The package.json in the CCS plugin is missing the
prerequisite dependency "plugin-mutils"; update the "dependencies" section in
plugins/ccs/package.json to add "plugin-mutils" (matching the package name used
in plugins/mutils/package.json) so the prereq rule is satisfied, keeping
versioning consistent with your repo policy (e.g., use the same version spec
style as the other entries).

In `@plugins/ccs/skills/ccs-delegation/references/troubleshooting.md`:
- Around line 9-10: The two cross-reference links in troubleshooting.md point to
non-existent docs (headless-workflow.md and delegation-guidelines.md); update
troubleshooting.md to either point to the correct existing filenames/paths or
add the missing documents. Locate the references to headless-workflow.md and
delegation-guidelines.md in the troubleshooting.md content and: a) if equivalent
docs already exist elsewhere in the repo, replace the two filenames with the
correct relative paths/names, or b) if they are missing, create new markdown
files named headless-workflow.md and delegation-guidelines.md with the intended
content and add them to the same docs directory so the links resolve.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 864a652d-abdc-491a-ab6b-8545150c2bfd

📥 Commits

Reviewing files that changed from the base of the PR and between 1a68328 and a17088f.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (19)
  • .claude-plugin/marketplace.json
  • AGENTS.md
  • knip.json
  • plugins/ccs/AGENTS.md
  • plugins/ccs/CLAUDE.md
  • plugins/ccs/commands/ccs.md
  • plugins/ccs/commands/ccs/continue.md
  • plugins/ccs/hooks/entry/ccs-symlink-check.ts
  • plugins/ccs/hooks/hooks.json
  • plugins/ccs/hooks/install-deps.sh
  • plugins/ccs/package.json
  • plugins/ccs/plugin.json
  • plugins/ccs/skills/ccs-delegation/CLAUDE.md.template
  • plugins/ccs/skills/ccs-delegation/SKILL.md
  • plugins/ccs/skills/ccs-delegation/references/troubleshooting.md
  • plugins/ccs/skills/ccs-handoff/SKILL.md
  • plugins/ccs/skills/ccs-handoff/ccs-handoff.ts
  • plugins/ccs/tsconfig.json
  • plugins/mutils/AGENTS.md
💤 Files with no reviewable changes (1)
  • plugins/mutils/AGENTS.md

Comment on lines +12 to +18
```
/ccs "refactor auth.js to use async/await" # Simple task
/ccs "analyze entire architecture" # Long-context task
/ccs "think about caching strategy" # Reasoning task
/ccs --glm "add tests for UserService" # Force specific profile
/ccs "/cook create landing page" # Nested slash command
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add a language identifier to the fenced example block.

The code fence at Line 12 has no language, which triggers markdownlint MD040.

Proposed fix
-```
+```text
 /ccs "refactor auth.js to use async/await"    # Simple task
 /ccs "analyze entire architecture"            # Long-context task
 /ccs "think about caching strategy"           # Reasoning task
 /ccs --glm "add tests for UserService"        # Force specific profile
 /ccs "/cook create landing page"              # Nested slash command
</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 12-12: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/ccs/commands/ccs.md` around lines 12 - 18, Add a language identifier
to the fenced code block that shows the /ccs examples so markdownlint MD040 is
satisfied; locate the triple-backtick block containing the lines beginning with
"/ccs \"refactor auth.js to use async/await\"", and change the opening fence
from ``` to ```text (or another appropriate language tag) so the example is
explicitly marked as plain text.

Comment on lines +12 to +16
```
/ccs:continue "also update the examples section" # Use last profile
/ccs:continue --glm "add unit tests" # Switch profiles
/ccs:continue "/commit with message" # Nested slash command
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Specify a language for the fenced examples.

Line 12 should declare a fence language to satisfy markdownlint MD040.

Proposed fix
-```
+```text
 /ccs:continue "also update the examples section"  # Use last profile
 /ccs:continue --glm "add unit tests"              # Switch profiles
 /ccs:continue "/commit with message"              # Nested slash command
</details>

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.22.1)</summary>

[warning] 12-12: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @plugins/ccs/commands/ccs/continue.md around lines 12 - 16, The fenced code
block containing the example slash commands (the three lines starting with
"/ccs:continue ...") is missing a fence language and triggers markdownlint
MD040; update the opening triple-backtick so it declares a language (e.g.,

example block that includes the "/ccs:continue" variants is marked with a
language identifier.

Comment on lines +26 to +39
function ensureSymlink(linkPath: string, target: string): boolean {
if (fs.existsSync(linkPath)) {
const stat = fs.lstatSync(linkPath);
if (stat.isSymbolicLink()) {
const current = fs.readlinkSync(linkPath);
if (current === target) return false;
}
}
ensureDir(path.dirname(linkPath));
if (!fs.existsSync(target)) {
ensureDir(target);
}
return false;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

groupDir/projects repair is incomplete and misses broken existing links/directories.

ensureSymlink never mutates anything (always returns false), and the fallback block only handles the “missing path” case. If groupProjects exists but points elsewhere (or is a directory), the chain is not repaired.

Suggested fix
 function ensureSymlink(linkPath: string, target: string): boolean {
-  if (fs.existsSync(linkPath)) {
-    const stat = fs.lstatSync(linkPath);
-    if (stat.isSymbolicLink()) {
-      const current = fs.readlinkSync(linkPath);
-      if (current === target) return false;
-    }
-  }
+  if (fs.existsSync(linkPath)) {
+    const stat = fs.lstatSync(linkPath);
+    if (stat.isSymbolicLink()) {
+      if (fs.readlinkSync(linkPath) === target) return false;
+      fs.unlinkSync(linkPath);
+    } else if (stat.isDirectory()) {
+      if (!fs.existsSync(target)) ensureDir(target);
+      copyDirContents(linkPath, target);
+      fs.rmSync(linkPath, { recursive: true, force: true });
+    } else {
+      fs.rmSync(linkPath, { force: true });
+    }
+  }
   ensureDir(path.dirname(linkPath));
-  if (!fs.existsSync(target)) {
-    ensureDir(target);
-  }
-  return false;
+  if (!fs.existsSync(target)) ensureDir(target);
+  fs.symlinkSync(target, linkPath);
+  return true;
 }

Also applies to: 127-135

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/ccs/hooks/entry/ccs-symlink-check.ts` around lines 26 - 39,
ensureSymlink currently never makes changes and only handles the "missing
target" case; update it to repair broken or wrong-type entries and create the
symlink when needed. Specifically, in function ensureSymlink(linkPath, target)
check if linkPath exists: if it's a symlink and already points to target return
false; otherwise remove the existing file/dir/symlink (handle directories,
files, and dangling symlinks), ensure parent dir exists via
ensureDir(path.dirname(linkPath)), ensure target exists via ensureDir(target) if
necessary, create the symlink with fs.symlinkSync(target, linkPath), and return
true to indicate a change. Apply the same fix to the sibling occurrence
referenced in the review (the other block at lines 127-135).

Comment on lines +64 to +83
if (fs.existsSync(src)) {
const stat = fs.lstatSync(src);
if (stat.isSymbolicLink()) {
const current = fs.readlinkSync(src);
if (current === target) return false;
fs.unlinkSync(src);
fs.symlinkSync(target, src);
return true;
}
if (stat.isDirectory()) {
copyDirContents(src, target);
fs.rmSync(src, { recursive: true, force: true });
fs.symlinkSync(target, src);
return true;
}
}

ensureDir(path.dirname(src));
fs.symlinkSync(target, src);
return true;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

replaceWithSymlink fails when src is an existing regular file.

When src exists as a file, the function falls through to fs.symlinkSync(target, src) and throws EEXIST, aborting the hook.

Suggested fix
   if (fs.existsSync(src)) {
     const stat = fs.lstatSync(src);
 ...
     if (stat.isDirectory()) {
       copyDirContents(src, target);
       fs.rmSync(src, { recursive: true, force: true });
       fs.symlinkSync(target, src);
       return true;
     }
+    // regular file or other existing node
+    fs.rmSync(src, { force: true });
+    fs.symlinkSync(target, src);
+    return true;
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/ccs/hooks/entry/ccs-symlink-check.ts` around lines 64 - 83, The
replaceWithSymlink function currently falls through and calls
fs.symlinkSync(target, src) when src is an existing regular file, causing
EEXIST; update replaceWithSymlink to handle regular files by removing them
before creating the symlink (e.g., detect stat.isFile() or !isSymbolicLink() &&
!isDirectory() and call fs.unlinkSync(src) prior to fs.symlinkSync(target,
src)), preserving the existing behaviors for symlinks and directories (keep the
current checks for stat.isSymbolicLink() and stat.isDirectory() and reuse
copyDirContents for directories).

Comment on lines +96 to +107
let config: CcsConfig;
try {
const raw = fs.readFileSync(configPath, "utf-8");
config = parseYaml(raw) as CcsConfig;
} catch {
return context.success({});
}

const accounts = config.accounts;
if (!accounts) {
return context.success({});
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard parsed YAML shape before dereferencing config.accounts.

parseYaml can return null/non-object; current cast allows config.accounts access to throw at runtime on malformed or empty config.

Suggested fix
-    let config: CcsConfig;
+    let config: CcsConfig | null = null;
     try {
       const raw = fs.readFileSync(configPath, "utf-8");
-      config = parseYaml(raw) as CcsConfig;
+      const parsed = parseYaml(raw);
+      if (parsed && typeof parsed === "object") {
+        config = parsed as CcsConfig;
+      }
     } catch {
       return context.success({});
     }
 
-    const accounts = config.accounts;
+    const accounts = config?.accounts;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let config: CcsConfig;
try {
const raw = fs.readFileSync(configPath, "utf-8");
config = parseYaml(raw) as CcsConfig;
} catch {
return context.success({});
}
const accounts = config.accounts;
if (!accounts) {
return context.success({});
}
let config: CcsConfig | null = null;
try {
const raw = fs.readFileSync(configPath, "utf-8");
const parsed = parseYaml(raw);
if (parsed && typeof parsed === "object") {
config = parsed as CcsConfig;
}
} catch {
return context.success({});
}
const accounts = config?.accounts;
if (!accounts) {
return context.success({});
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/ccs/hooks/entry/ccs-symlink-check.ts` around lines 96 - 107, The
parsed YAML may be null or not an object so avoid directly dereferencing
config.accounts; after calling parseYaml(raw) in the try block, validate that
config is a non-null object (e.g. typeof config === "object" && config !== null)
and that it has an accounts property (or use optional chaining) before assigning
const accounts = config.accounts; if the shape is invalid, return
context.success({}) as currently done; update references to the symbols
parseYaml, config, configPath, and accounts to include this guard so
malformed/empty configs do not throw at runtime.

Comment on lines +6 to +11
[ -f node_modules/.cc-installed ] && exit 0
node -e 'var f=require("fs"),p=JSON.parse(f.readFileSync("package.json","utf8"));delete p.devDependencies;f.writeFileSync("package.json",JSON.stringify(p,null,2)+"\n")'
rm -f bun.lock
bun install --production
mkdir -p node_modules
touch node_modules/.cc-installed
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Stale install marker can leave the plugin running with outdated dependencies.

node_modules/.cc-installed never invalidates when package.json changes, so future sessions may skip required reinstall/upgrade and run with mismatched runtime deps.

Suggested fix
-[ -f node_modules/.cc-installed ] && exit 0
+mkdir -p node_modules
+marker="node_modules/.cc-installed"
+hash_now="$(sha256sum package.json 2>/dev/null | awk '{print $1}')"
+if [ -f "$marker" ] && [ "$(cat "$marker" 2>/dev/null)" = "$hash_now" ]; then
+  exit 0
+fi
 ...
-bun install --production
-mkdir -p node_modules
-touch node_modules/.cc-installed
+bun install --production
+printf '%s\n' "$hash_now" > "$marker"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/ccs/hooks/install-deps.sh` around lines 6 - 11, The install marker
node_modules/.cc-installed is never invalidated when package.json changes;
update the script so it records a checksum or timestamp of package.json
alongside the marker and compares it on startup: compute a hash (or mtime) of
package.json, read the stored value from node_modules/.cc-installed (or a
companion file), and only exit early if they match; otherwise run the existing
steps (delete bun.lock, bun install --production, mkdir -p node_modules,
touch/update node_modules/.cc-installed) and write the new checksum/timestamp
into the marker so future runs detect changes.

Comment thread plugins/ccs/package.json
Comment on lines +10 to +17
"dependencies": {
"@r_masseater/cc-plugin-lib": "0.0.6",
"cc-hooks-ts": "2.1.149",
"yaml": "^2.7.1"
},
"devDependencies": {
"@repo/ts-config": "*"
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify plugin package manifests and inspect whether ccs includes mutils prerequisite.
fd -a package.json plugins | xargs -I{} sh -c 'echo "== {} =="; jq -r ".name, (.dependencies // {} | keys[]?)" "{}"'

Repository: masseater/claude-code-plugin

Length of output: 5418


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show dependency name used by other plugins for mutils/mutils prerequisite
echo "== plugins/mutils/package.json =="
cat plugins/mutils/package.json

echo
echo "== plugins/ccs/package.json =="
cat plugins/ccs/package.json

echo
echo "== Find occurrences of 'mutils' in other plugins package.json =="
rg -n "\"mutils\"|\"plugin-mutils\"|\"`@r_masseater/mutils`\"|mutils" plugins -g 'package.json' || true

Repository: masseater/claude-code-plugin

Length of output: 1260


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== plugins/mutils/package.json (name + version) =="
node -e "const fs=require('fs'); const j=JSON.parse(fs.readFileSync('plugins/mutils/package.json','utf8')); console.log(j.name, '@', j.version);"

echo
echo "== plugins/ccs/package.json (dependencies keys) =="
node -e "const fs=require('fs'); const j=JSON.parse(fs.readFileSync('plugins/ccs/package.json','utf8')); console.log(Object.keys(j.dependencies||{}));"

echo
echo "== Check whether any plugin depends on the mutils prereq by common keys =="
rg -n "\"(mutils|plugin-mutils|`@r_masseater/mutils`)\"" plugins -g 'package.json' || true

Repository: masseater/claude-code-plugin

Length of output: 390


Add plugin-mutils as a prerequisite dependency in plugins/ccs/package.json.

plugins/ccs/package.json dependencies currently include @r_masseater/cc-plugin-lib, cc-hooks-ts, and yaml, but the prereq plugin’s package name is plugin-mutils (plugins/mutils/package.json).
Add plugin-mutils to plugins/ccs/package.json dependencies to satisfy the plugin prerequisite rule.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/ccs/package.json` around lines 10 - 17, The package.json in the CCS
plugin is missing the prerequisite dependency "plugin-mutils"; update the
"dependencies" section in plugins/ccs/package.json to add "plugin-mutils"
(matching the package name used in plugins/mutils/package.json) so the prereq
rule is satisfied, keeping versioning consistent with your repo policy (e.g.,
use the same version spec style as the other entries).

Comment on lines +9 to +10
- Technical details: `headless-workflow.md`
- Decision framework: `delegation-guidelines.md`
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Checking referenced docs..."
fd -i '^headless-workflow\.md$'
fd -i '^delegation-guidelines\.md$'

echo
echo "Checking for relative-path candidates near troubleshooting.md..."
fd -i 'troubleshooting.md|headless-workflow.md|delegation-guidelines.md' plugins

Repository: masseater/claude-code-plugin

Length of output: 230


Fix broken cross-references in plugins/ccs/skills/ccs-delegation/references/troubleshooting.md (lines 9-10)
Lines 9-10 reference headless-workflow.md and delegation-guidelines.md, but neither file exists anywhere in the repo, so these links are broken. Add the missing docs or update the referenced filenames/paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/ccs/skills/ccs-delegation/references/troubleshooting.md` around lines
9 - 10, The two cross-reference links in troubleshooting.md point to
non-existent docs (headless-workflow.md and delegation-guidelines.md); update
troubleshooting.md to either point to the correct existing filenames/paths or
add the missing documents. Locate the references to headless-workflow.md and
delegation-guidelines.md in the troubleshooting.md content and: a) if equivalent
docs already exist elsewhere in the repo, replace the two filenames with the
correct relative paths/names, or b) if they are missing, create new markdown
files named headless-workflow.md and delegation-guidelines.md with the intended
content and add them to the same docs directory so the links resolve.

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