From citadel
Monitors file changes and marker comments (@citadel:) to auto-trigger skills via polling with git diff. Write state files and route actions for batch processing.
How this skill is triggered — by the user, by Claude, or both
Slash command
/citadel:watchThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use when the user wants automatic reactions to file changes or marker comments (`@citadel:`).
Use when the user wants automatic reactions to file changes or marker comments (@citadel:).
Do NOT use for one-off file inspection or tasks that need human judgment per file.
Use when: running a file sentinel that triggers a skill or command automatically when watched paths change. Don't use when: monitoring CI status on a PR (use /pr-watch); running a one-shot verification check (use /verify).
/watch start does NOT call CronCreate by default. Only pass --remote
to use Anthropic's routine system, and only after explicit user confirmation.
CronCreate counts against the 15 routine runs / 24h cap — at the default
5-minute interval a watch exhausts the quota in under an hour.
/watch start (no --remote flag)CronCreate. Leave cronId: null in state.status: "watching".Watch state created: .planning/watch-state.json
Baseline: {commit hash, first 7 chars}
To start real-time watching, run in a separate terminal:
npm run watch:local
For cloud-persistent polling (machine off, user away):
/watch start --remote (uses CronCreate, counts against 15/day cap)
/watch start --remoteOnly when --remote is explicitly passed:
CronCreate, which counts against your 15 routine
runs / 24h quota. At a 5-minute interval this exhausts the quota in under
an hour. Continue? (y/N)"CronCreate.| Command | Behavior |
|---|---|
/watch start | Default: create state, prompt user to run npm run watch:local |
/watch start --remote | Use CronCreate polling (counts against 15/day quota — requires confirmation) |
/watch start --interval {N}m | Set poll interval for --remote mode (default: 5m) |
/watch stop | Stop watching, tear down cron |
/watch status | Show watch state, last scan time, pending actions |
/watch scan | Run a single scan now (manual trigger) |
.planning/watch-state.json if it existsstatus is "watching":
/watch stop first, then continuegit rev-parse HEAD to get the current commit hashlastScanTime, skip commit-based diffing)lastScanCommitCronCreate:
interval: "{N}m" (default: 5m)
command: "/watch scan"
Save the cron ID in the state file.
Write .planning/watch-state.json:
{
"status": "watching",
"lastScanCommit": "abc1234",
"lastScanTime": null,
"interval": "5m",
"cronId": "{id from step 3 or null}",
"pendingActions": {},
"processedMarkers": {},
"stats": {
"scansRun": 0,
"markersFound": 0,
"intakeItemsCreated": 0,
"skillsDispatched": 0
}
}
Watch started.
Interval: every {N}m
Baseline: {commit hash, first 7 chars}
State: .planning/watch-state.json
.planning/watch-state.json. If missing or not "watching": "No watch is active."CronDelete: {cronId} — if cronId is missing or deletion fails, continue."status": "stopped", "cronId": null. Preserve all other fields.Watch stopped.
Scans completed: {stats.scansRun}
Markers found: {stats.markersFound}
Intake items created: {stats.intakeItemsCreated}
Skills dispatched: {stats.skillsDispatched}
.planning/watch-state.json missing: "No watch configured. Use /watch start to begin."pendingActions non-empty, list each: [{action}] {file}:{line} -- {description}.planning/watch-state.jsonlastScanCommit from git rev-parse HEAD and status: "watching".Git mode (primary):
git diff --name-only {lastScanCommit} HEAD (committed changes)git diff --name-only (unstaged) and git diff --name-only --cached (staged)Fallback mode (no git):
find . -newer {timestamp_file} -type fnode_modules/, .git/, .planning/, dist/, build/If no files changed: update lastScanTime and stats.scansRun, exit early.
Search changed files for:
| Pattern | Languages |
|---|---|
// @citadel: {action} {description} | JS, TS, Go, Rust, C, Java |
# @citadel: {action} {description} | Python, Shell, YAML, Ruby |
/* @citadel: {action} {description} */ | CSS, multi-line C-style |
<!-- @citadel: {action} {description} --> | HTML, Markdown |
Action-to-skill mapping:
| Action | Skill |
|---|---|
review | /review |
test | /test-gen |
fix | /systematic-debugging |
document | /doc-gen |
refactor | /refactor |
todo | intake item |
Unknown actions become intake items with the action preserved as metadata.
Deduplication: Every marker gets a stable identity hash: sha256 over {file path}, {action}, and the normalized marker text (trimmed, internal whitespace collapsed), joined with NUL separators and truncated to 16 hex chars. Line numbers are excluded, so the hash survives line shifts. processedMarkers is a map keyed by this hash with {file, action, firstSeen, lastSeen}; pendingActions uses the same hash keys. Both maps are pruned past 500 entries by dropping the oldest lastSeen. Markers whose hash is already in processedMarkers are skipped.
| File pattern | Auto-action |
|---|---|
*.test.*, *.spec.*, __tests__/* | Queue: "run tests" intake item |
*.md in docs/ or project root | Queue: "doc staleness check" intake item |
src/**/*.ts, src/**/*.tsx | Queue: "changed source" intake item |
package.json, tsconfig.json | Queue: "config change" intake item (high priority) |
For each new marker:
/do {action} in {file} at line {line}: {description}processedMarkers, increment stats.skillsDispatchedBatch limit: Dispatch at most 5 per scan. Queue overflow in pendingActions.
Filename: watch-{action}-{file slug}-{epoch ms}.md in .planning/intake/
---
title: "{action} {file}:{line}"
status: pending
priority: normal
target: {file path}
source: watch
marker_hash: {16-char marker hash}
---
Marker comment found at {file}:{line}:
`{raw marker line}`
{description, if any}
Before writing, skip the item if the marker hash is already in processedMarkers, or if any existing .planning/intake/*.md carries a matching marker_hash in its frontmatter. Every new intake item records its marker_hash so future scans (and concurrent processes) can detect it.
lastScanCommit: git rev-parse HEADlastScanTime: current ISO timestampstats.scansRun, stats.markersFoundpendingActions and processedMarkersManual scan:
Scan complete.
Files changed: {N}
Markers found: {new} ({total} total)
Actions dispatched: {N} (batch limit: 5)
Intake items: {N} written to .planning/intake/
Pending actions: {N}
Cron poll: silent.
.planning/intake/ for /autopilot./do — never invokes skills directly./daemon can start a watch alongside a campaign..planning/ missing: Create on first scan.
Not a git repo: Fall back to timestamp detection; warn once.
No files changed: Update stats, exit silently.
Unknown action: Treat as intake item, preserve raw action.
Deleted file: Skip marker scanning; write intake item noting deletion.
Large diff (100+ files): Cap at 50 per scan, queue rest.
Binary files: Skip during marker scanning.
Corrupted state: Reset to defaults, preserve processedMarkers if readable.
CronCreate not available: Warn and suggest manual /watch scan.
Scan overlap: Scans serialize through a lock directory (.planning/watch-state.json.lock, acquired via atomic mkdir with ~10 retries at 100ms). Each scan records scanStartedAt and scanPid in state under the lock, and holds the lock across the state read-modify-write and intake writes. A scan that cannot acquire the lock and finds another scan started under 60 seconds ago logs a skip notice and exits cleanly. A lock older than 30 seconds (by mtime) is treated as stale and removed.
Marker removed: Stale processedMarkers entries age out via the 500-entry oldest-lastSeen prune.
Disclosure: "Starting file watch on [paths]. Triggers [skill] on change. Stop with Ctrl+C."
Reversibility: amber — runs sentinel that triggers other skills on file change; triggered skills may modify files; stop with Ctrl+C and run /watch stop
Trust gates:
/watch start: Output confirmation block. No HANDOFF./watch stop: Output stop summary with lifetime stats./watch scan (manual): Output scan report with counts./watch scan (cron): Silent./watch status: Output current state.npx claudepluginhub sethgammon/citadel --plugin citadelWatches files or commands and triggers actions on changes. Useful for live reloading or automation workflows.
Manages Claude Code repository hooks for adding, configuring, troubleshooting, enabling/disabling, and enforcing rules. Delegates to docs-management for official documentation.
Tracks code changes with structured JSON records, state machine enforcement, and AI session handoff for resuming expired bot sessions. Use for multi-session coding continuity or git-based onboarding.