Contributing to Citadel
March 31, 2026 · View on GitHub
Contributions are welcome. Issues, bug reports, new skills, and hook improvements all help.
Reporting Issues
Open an issue on GitHub. Include:
- What you expected to happen
- What actually happened
- Error messages (full text, not screenshots of text)
- Your OS, shell, and Node version
Submitting Pull Requests
- Fork the repo
- Create a branch from
main(e.g.,fix/issue-10-descriptionorfeat/new-skill) - Make your changes
- Run
node hooks_src/smoke-test.jsto verify hooks are healthy - Open a PR against
main
Branch protection is enabled. All changes go through a PR. Direct pushes to main are blocked.
What to watch out for
Cross-platform compatibility. Citadel runs on Windows, macOS, and Linux. Before submitting:
- Hook definitions live in
hooks/hooks-template.jsonand are installed per-project viascripts/install-hooks.js - The Claude installer is compatibility-aware by default; use
--hook-profile latestin tests or fixtures when you need the full modern hook surface deterministically - Do NOT hardcode
/bin/bash,/bin/sh, or other Unix-only paths - Do NOT assume forward-slash path separators in Node scripts (use
path.join()) - Test on your platform and note which platform you tested on in the PR
Plugin architecture. Citadel is distributed as a Claude Code plugin. Users install it once and it works across all projects — no per-project file copying required.
- Hook scripts live in
hooks_src/and are installed per-project viascripts/install-hooks.js - The
init-projectSessionStart hook auto-scaffolds per-project state (.planning/,.citadel/scripts/) - Per-project configuration lives in
.claude/harness.json(generated by/do setup)
Adding a New Skill
Skills live in skills/{name}/SKILL.md (one directory per skill). Every skill needs:
---
name: skill-name
description: >-
One or two sentences explaining what the skill does.
user-invocable: true
auto-trigger: false
---
Follow the patterns in existing skills. Read 2-3 before writing your own.
Users can also create project-level custom skills in their project's .claude/skills/ directory using /create-skill.
Adding a New Hook
Hooks live in hooks_src/. Before adding one:
- Read
harness-health-util.jsfor shared utilities (telemetry, config, validation) - Use
execFileSync(notexecSync) to avoid shell injection - Use
require('./harness-health-util')for the project root path - Use
process.env.CLAUDE_PROJECT_DIR || process.cwd()for the user's project root - Add your hook to
hooks/hooks-template.json— the installer resolves${CLAUDE_PLUGIN_ROOT}at install time - If the hook requires a newer Claude Code event, update the compatibility gating in
runtimes/claude-code/generators/hook-support.js - Run
node hooks_src/smoke-test.jsto make sure the smoke test picks it up
Plugin Directory Structure
citadel/
.claude-plugin/ # Plugin manifest
skills/ # Built-in skill definitions ({name}.md per skill)
agents/ # Sub-agent definitions
hooks/
hooks-template.json # Hook definitions (resolved by install-hooks.js)
hooks_src/ # Hook script implementations
scripts/ # Utility scripts (synced to projects by init-project)
.planning/
_templates/ # Templates (copied to projects by init-project)
.claude/
agent-context/ # Rules injected into sub-agents
docs/ # Reference documentation
Opt-in Hooks
Some hooks are not included in the default hooks/hooks-template.json. They are available in hooks_src/ for users who want them:
-
external-action-gate.js— Blocks git push, PR creation, issue comments, and other external actions until the user approves. Add to your project's.claude/settings.local.json:{ "hooks": { "PreToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "node '${CLAUDE_PLUGIN_ROOT}/hooks_src/external-action-gate.js'", "timeout": 5 }] }] } } -
issue-monitor.js— Checks for new GitHub issues on session start. Add to.claude/settings.local.json:{ "hooks": { "SessionStart": [{ "hooks": [{ "type": "command", "command": "node '${CLAUDE_PLUGIN_ROOT}/hooks_src/issue-monitor.js'", "timeout": 20 }] }] } }
Migrating from copy-based install? These hooks previously lived at
.claude/hooks/. The paths in yoursettings.local.jsonneed to change fromnode .claude/hooks/external-action-gate.jsto the${CLAUDE_PLUGIN_ROOT}form shown above.
Code Style
- Node.js scripts use CommonJS (
require), not ESM - Keep hooks fast (under 5s for PreToolUse, under 30s for PostToolUse)
- Fail-closed for security hooks (exit 2 on error), fail-open for non-critical hooks (exit 0 on error)
- No external dependencies. Hooks use only Node built-ins.