Fork the repo, push changes to your fork, and open a PR against main.
Reviews are managed through OWNERS/OWNERS_ALIASES. All PRs to main receive automated review from CodeRabbit (shellcheck, markdownlint, ruff). See OWNERS_ALIASES for the current reviewer list.
After cloning the repo, enable the shared git hooks:
make setup-githooksThis installs a pre-commit hook that runs markdownlint on staged .md files. Commits with markdown lint errors will be blocked.
| Type | Location | Guide |
|---|---|---|
| New tool | <tool-name>/ at repo root |
Adding a Tool below |
| Plugin | plugins/<name>/ |
Plugin Contributing Guide |
| Bug fix / enhancement | Component directory | Follow component README |
| Documentation | Markdown files | Code Standards below |
| Environment template | environments/<name>/ |
Follow existing patterns |
Format: <type>(<scope>): <subject>
Types: feat, fix, docs, test, refactor, chore
Scopes: component name — plugins, submodule, ec2-deploy, sno-deploy, etc.
Branch naming: <type>/<description> — e.g., feat/new-tool, fix/deploy-bug, plugin/my-plugin
Examples:
feat(plugins): add cluster-health plugin
fix(sno-deploy): correct subnet mask validation
docs: update prerequisites table
chore(submodule): update two-node-toolbox (abc1234 -> def5678, 5 commits)
- Shebang:
#!/usr/bin/bash set -euo pipefail- Quote all variables
- Must pass shellcheck
- PEP 8
- Must pass ruff
- 2-space indentation
- Quote strings with special characters
- Must pass markdownlint
- Professional, terse, customer-centric — no emojis or filler
- No hardcoded credentials — use environment variables
- Self-documenting code over comments
- First-pass code review is automated by CodeRabbit on all PRs to
main - Name accuracy: component names (tools, plugins, hooks, scripts) must describe what the component actually does — not what it aspires to or tangentially relates to
- Test validation logic: regex patterns, parsing rules, and validation functions require tests with positive and negative cases
- Avoid redundant checks: don't repeatedly validate things that rarely change (e.g., config file existence) — one-time setup belongs in documentation, not runtime checks
- Create a directory at repo root with a
MakefileorREADME.md - Add a
README.mddocumenting purpose, prerequisites, and usage - Update the tool table in root
CLAUDE.md - Add the directory name to the
DOCUMENTED_TOOLSarray in.claude/hooks/detect-new-tools.sh - Commit:
feat(<tool-name>): add <tool-name>
Plugin components serve different roles. Choosing the right one matters.
| Component | User-invocable | Purpose | Example |
|---|---|---|---|
| Skill | Yes (/name) |
User-facing workflows, multi-step procedures, orchestration | /microshift-ci:doctor |
| Agent | No | Focused subtasks spawned by a skill, often run in parallel | release-health:Epic Fetcher |
| Hook | No | Event-driven automation (session start, tool validation) | detect-new-tools.sh |
Use a skill when a user needs to invoke it directly. Skills contain step-by-step instructions and can spawn agents or call other skills.
Use an agent when a skill needs to break work into parallel, isolated subtasks. Agents communicate with their parent skill through files (typically JSON). They don't interact with the user.
Use a hook when behavior should trigger automatically on an event (session start, before/after tool use).
Not all hooks belong in plugins. The delivery mechanism determines who gets the protection.
| Mechanism | Location | Scope | Use when |
|---|---|---|---|
| Repo-level hook | .claude/settings.json |
All contributors automatically | Check is mandatory/protective (destructive command blocking, secret detection) |
| Plugin hook | plugins/<name>/hooks/ |
Only users who install the plugin | Check is optional/workflow-specific |
Rule of thumb: if skipping the check would be a security or data-loss risk, it belongs in .claude/hooks at the repo level — not in an optional plugin.
Place each check at the right stage:
| Stage | Example | Mechanism |
|---|---|---|
| One-time setup | CodeRabbit config, tool installation | Documentation only — verify once, not every session |
| Session start | Submodule staleness, undocumented tools | Repo-level hook (SessionStart) |
| Pre-tool / post-tool | Block destructive git commands, detect secrets in file writes | Repo-level hook (PreToolUse / PostToolUse) |
| Stop (session end) | Markdown linting, cleanup tasks | Repo-level hook (Stop) |
| Pre-commit | Secret scanning, lint checks | Git pre-commit hook or repo-level PreToolUse on Bash matcher |
When adding external tool integration, decide whether to use a traditional CLI tool (invoked via Bash) or an MCP server.
Use a CLI tool when:
- The tool is a one-shot operation (run command, get output)
- The team already uses it outside of Claude Code (e.g.,
oc,aws,gh) - Output is plain text or simple structured data
- No session state is needed between calls
Use an MCP server when:
- The integration needs to maintain state across multiple calls (e.g., an authenticated API session)
- You want to expose multiple related operations as discrete, typed tools rather than parsing CLI flags
- The data source is an API that benefits from structured request/response (e.g., Jira, GitHub, Slack)
- You need Claude to discover available operations dynamically via tool schemas
Rule of thumb: if you'd normally curl or call an API repeatedly with different parameters during a workflow, MCP is likely the better fit. If you'd normally run a shell command and parse the output, keep it as a CLI tool.
Use colon-based namespacing to group skills under a workflow domain. This keeps / autocomplete organized and signals that skills belong together.
Pattern: <domain>:<action>
jira:create-epic — enforces epic-specific field standards
jira:create-story — enforces story-specific acceptance criteria format
jira:link-to-epic — links stories to parent epics
microshift-ci:doctor — orchestrates full CI analysis
microshift-ci:prow-job — analyzes a single Prow job
microshift-ci:test-job — analyzes a single test execution
Each grouped skill lives in its own directory under the plugin's skills/ folder:
plugins/jira/
├── .claude-plugin/
│ └── plugin.json
├── skills/
│ ├── create-epic/
│ │ └── SKILL.md # name: jira:create-epic
│ ├── create-story/
│ │ └── SKILL.md # name: jira:create-story
│ └── link-to-epic/
│ └── SKILL.md # name: jira:link-to-epic
├── agents/
│ └── field-validator.md # spawned by skills to validate fields
└── README.md
The plugin name (jira) becomes the namespace prefix. Individual skills define domain-specific standards (required fields, formatting rules, validation) within their SKILL.md instructions.
For complex workflows, skills orchestrate agents in phases:
- Skill gathers input and configuration
- Skill spawns agents (in parallel where possible), substituting
{VARIABLES}in agent definitions - Agents write results to JSON files in a shared work directory
- Skill reads agent outputs and synthesizes the final result
This repository uses Claude Code extensively. Contributors (human and agent) should maintain the following infrastructure.
- Root CLAUDE.md: repository overview, tool table, common workflows, prerequisites
- Component CLAUDE.md: per-tool guidance scoped to that directory
- When to update: adding/removing tools, changing workflows, modifying prerequisites
- Style: concise, intent-focused, no filler (see
global-claude.md)
| Hook | Purpose |
|---|---|
.claude/hooks/detect-new-tools.sh |
Flags undocumented tool directories at session start |
.claude/hooks/update-submodules.sh |
Checks for stale submodules at session start |
.claude/hooks/detect-new-plugins.sh |
Flags new plugins not yet in marketplace catalog |
When adding a tool, update the DOCUMENTED_TOOLS array in detect-new-tools.sh.
Plugins extend Claude Code capabilities for the team. For plugin contribution details:
These principles apply to all PRs. CodeRabbit enforces them automatically; human reviewers should verify them as well.
Before adding custom tooling, verify the platform doesn't already handle it. Don't build sync scripts, symlink managers, or wrapper layers when the functionality can live directly in a plugin. If you're unsure whether a capability exists, check the Claude Code docs or ask in the PR description.
Changes to .githooks/ affect every contributor on every commit. Pre-commit hooks must:
- Stay minimal — only enforce checks that prevent broken commits (lint, secrets)
- Never block unrelated work — a hook failure must be fixable by the commit author without understanding unrelated subsystems
- Fail fast with clear messages — no silent failures, no ambiguous error output
Adding new verification steps to pre-commit hooks requires explicit justification in the PR description explaining why the check can't be a CI job, a CodeRabbit rule, or a session-start hook instead.
Don't maintain two copies of the same data with a sync script between them. If information lives in one place (e.g., plugin skill definitions), consume it from that location rather than syncing it elsewhere. Sync scripts create drift, add maintenance burden, and break when contributors skip steps.
- All PRs require review from
edge-reviewers(seeOWNERS_ALIASES) - CodeRabbit provides automated review on PRs to
main two-node-toolbox/is excluded from CodeRabbit review (external submodule)- Reviewers check: code quality, security, documentation, and test coverage
- Reviewers also verify architectural fit:
- Component name matches actual behavior
- Mandatory checks use repo-level hooks, not optional plugins
- No duplication of existing components in this repo
- Validation logic (regex, parsers) has test coverage
- Check is placed at the correct lifecycle stage
- No sync scripts or derived state — single source of truth
- Pre-commit hook changes are minimal and justified
- New scripts include justification in the PR description