Skip to content

feat: add Claude Code plugin support#2

Merged
berrzebb merged 1 commit intoberrzebb:mainfrom
dandacompany:feat/claude-code-plugin-support
Mar 17, 2026
Merged

feat: add Claude Code plugin support#2
berrzebb merged 1 commit intoberrzebb:mainfrom
dandacompany:feat/claude-code-plugin-support

Conversation

@dandacompany
Copy link
Contributor

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)

cp -r consensus-loop .claude/hooks/
# manually edit .claude/settings.local.json to register hooks
# manually copy and configure config.json, templates

After (plugin)

claude plugin add berrzebb/consensus-loop
# hooks auto-registered, commands available, skill loaded

Changes

File Purpose
.claude-plugin/plugin.json Plugin manifest with metadata, keywords, hook reference
hooks/hooks.json Auto-registers PreToolUse (session-gate) + PostToolUse (index) hooks using ${CLAUDE_PLUGIN_ROOT}
commands/consensus-audit.md /consensus-audit slash command for manual Codex audit
commands/consensus-status.md /consensus-status slash command for feedback cycle status
skills/consensus-loop/SKILL.md Evidence package writing guide — auto-activates when editing watch file
context.mjs REPO_ROOT now resolves dynamically: legacy (3-level parent) → git rev-parseprocess.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 to git rev-parse --show-toplevel for 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.

execFileSync over execSync: Uses array-based argument passing to prevent shell injection when resolving the git repo root.

Test plan

  • All 16 existing tests pass (node tests/cl1-verify.test.mjs)
  • resolveRepoRoot() correctly returns git root in both layouts
  • Legacy manual installation continues to work unchanged
  • Plugin structure validates against Claude Code plugin conventions

🤖 Generated with Claude Code

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>
Copy link
Owner

@berrzebb berrzebb left a comment

Choose a reason for hiding this comment

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

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

Copy link
Owner

@berrzebb berrzebb left a comment

Choose a reason for hiding this comment

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

Merging with follow-up fixes for status command paths and tag configurability. Good work on the plugin structure and resolveRepoRoot().

@berrzebb berrzebb merged commit 42f8dd4 into berrzebb:main Mar 17, 2026
berrzebb pushed a commit that referenced this pull request Mar 17, 2026
- 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>
@berrzebb
Copy link
Owner

Thanks for the plugin support PR! Merged and applied with follow-up fixes in a8878f8.

What was merged as-is:

  • .claude-plugin/plugin.json — clean manifest
  • hooks/hooks.json${CLAUDE_PLUGIN_ROOT} pattern, correct
  • context.mjs resolveRepoRoot() — excellent dynamic resolution (legacy → git → cwd), execFileSync for injection safety

What was fixed in follow-up:

  • consensus-status.md — file paths now read from config.json via REPO_ROOT instead of ${CLAUDE_PLUGIN_ROOT} (watch/respond files live in the project, not the plugin dir). Also added audit.lock check for async audit status.
  • consensus-audit.md + skills/SKILL.md — replaced hardcoded English tags ([REVIEW_NEEDED], [APPROVED], [CHANGES_REQUESTED]) with configurable references to config.json. Tags are project-specific (e.g., Korean projects use [GPT미검증], [합의완료], [계류]).

The plugin structure is exactly right — ${CLAUDE_PLUGIN_ROOT} for hooks, commands, and skills is the clean way to do this.

berrzebb added a commit that referenced this pull request Mar 20, 2026
feat: add Claude Code plugin support
berrzebb added a commit that referenced this pull request Mar 20, 2026
- 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>
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