feat: add Claude Code plugin support#2
Conversation
Enable installation as a Claude Code plugin via `claude plugin add`,
eliminating manual file copying and settings.local.json editing.
Changes:
- Add `.claude-plugin/plugin.json` manifest with metadata and hook reference
- Add `hooks/hooks.json` for automatic PreToolUse/PostToolUse hook registration
using `${CLAUDE_PLUGIN_ROOT}` for portable path resolution
- Add `/consensus-audit` command for manual Codex audit execution
- Add `/consensus-status` command for checking feedback cycle state
- Add `consensus-loop` skill with evidence package writing guide
- Update `context.mjs` REPO_ROOT resolution to support both layouts:
- Legacy: .claude/hooks/consensus-loop/ (3 levels up)
- Plugin: any install location (via `git rev-parse --show-toplevel`)
The legacy manual installation method continues to work unchanged.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
berrzebb
left a comment
There was a problem hiding this comment.
Great work on the plugin structure! The resolveRepoRoot() in context.mjs is well-designed (legacy → git → cwd fallback + execFileSync for injection safety), and the hooks.json/plugin.json layout follows conventions cleanly.
A few issues to address before merge:
High: consensus-status.md wrong file paths
The status command reads files from ${CLAUDE_PLUGIN_ROOT}/:
cat ${CLAUDE_PLUGIN_ROOT}/feedback/claude.md # ← wrong
cat ${CLAUDE_PLUGIN_ROOT}/gpt.md # ← wrong
cat ${CLAUDE_PLUGIN_ROOT}/session.id # ← this one is correct (relative to hooks dir)The watch file (feedback/claude.md) and respond file (gpt.md) live under REPO_ROOT (e.g., docs/feedback/claude.md), not inside the plugin directory. Their paths come from config.json → consensus.watch_file / plugin.respond_file.
Suggested fix: Use a node one-liner to resolve paths from config:
node -e "const c=require('${CLAUDE_PLUGIN_ROOT}/config.json'); const p=require('path'); console.log(require('fs').readFileSync(p.resolve(process.cwd(), c.consensus.watch_file.replace(/[^/]+$/, 'claude.md')), 'utf8'))" 2>/dev/null || echo "No watch file found"Or better yet, create a small status.mjs script that reads config and outputs structured status.
Medium: Missing audit.lock check
The status command should also check for the async audit lock:
cat ${CLAUDE_PLUGIN_ROOT}/audit.lock 2>/dev/null || echo "No audit in progress"This is important because audits now run asynchronously in the background.
Low: Hardcoded English tags in skill/commands
The SKILL.md and commands use [REVIEW_NEEDED], [APPROVED], [CHANGES_REQUESTED]. These are fine as defaults but worth noting that tags are configurable via config.json. Consider adding a note:
> Note: Tag names shown above are defaults. Check your project's
> .claude/hooks/consensus-loop/config.json for actual tag values.
Minor: Platform compatibility in status command
cat may not work on Windows without Git Bash. Using node -e "..." or a dedicated script would be more portable, consistent with the rest of the plugin which uses Node.js throughout.
Overall the PR is solid and the plugin structure is exactly right. The status command path issue is the only blocking item — the rest can be done as follow-ups.
berrzebb
left a comment
There was a problem hiding this comment.
Merging with follow-up fixes for status command paths and tag configurability. Good work on the plugin structure and resolveRepoRoot().
- consensus-status.md: read watch/respond files from REPO_ROOT via config.json (not CLAUDE_PLUGIN_ROOT), add audit.lock check - consensus-audit.md: replace hardcoded tags with config references - SKILL.md: replace [REVIEW_NEEDED]/[APPROVED]/[CHANGES_REQUESTED] with configurable trigger_tag/agree_tag/pending_tag placeholders Follow-up to #2 (plugin support). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Thanks for the plugin support PR! Merged and applied with follow-up fixes in a8878f8. What was merged as-is:
What was fixed in follow-up:
The plugin structure is exactly right — |
feat: add Claude Code plugin support
- consensus-status.md: read watch/respond files from REPO_ROOT via config.json (not CLAUDE_PLUGIN_ROOT), add audit.lock check - consensus-audit.md: replace hardcoded tags with config references - SKILL.md: replace [REVIEW_NEEDED]/[APPROVED]/[CHANGES_REQUESTED] with configurable trigger_tag/agree_tag/pending_tag placeholders Follow-up to #2 (plugin support). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
Enable consensus-loop to be installed as a Claude Code plugin via
claude plugin add, while maintaining full backward compatibility with the existing manual installation method.Before (manual)
After (plugin)
claude plugin add berrzebb/consensus-loop # hooks auto-registered, commands available, skill loadedChanges
.claude-plugin/plugin.jsonhooks/hooks.json${CLAUDE_PLUGIN_ROOT}commands/consensus-audit.md/consensus-auditslash command for manual Codex auditcommands/consensus-status.md/consensus-statusslash command for feedback cycle statusskills/consensus-loop/SKILL.mdcontext.mjsREPO_ROOTnow resolves dynamically: legacy (3-level parent) →git rev-parse→process.cwd()Key Design Decisions
Backward compatibility: The legacy
.claude/hooks/consensus-loop/layout continues to work.resolveRepoRoot()checks for the legacy layout first, then falls back togit rev-parse --show-toplevelfor plugin installations.${CLAUDE_PLUGIN_ROOT}: All hook commands use this environment variable instead of hardcoded paths, making the plugin portable across different installation methods and platforms.execFileSyncoverexecSync: Uses array-based argument passing to prevent shell injection when resolving the git repo root.Test plan
node tests/cl1-verify.test.mjs)resolveRepoRoot()correctly returns git root in both layouts🤖 Generated with Claude Code