AGENT SKILL

watch

File sentinel that monitors the working directory for changes and marker comments, then auto-triggers appropriate skills. Poll-based via git diff against the last scan commit. Writes intake items for batch processing and routes marker actions through /do. Use for automatic reactions to file changes; do NOT use for one-off inspection or tasks needing human judgment per file.

License 许可
MIT
所属仓库
SethGammon/Citadel
最近更新
2026-11-13
Stars
★ 930
Forks
84

01 前言 Front Matter

name
watch
license
MIT
description
File sentinel that monitors the working directory for changes and marker comments, then auto-triggers appropriate skills. Poll-based via git diff against the last scan commit. Writes intake items for batch processing and routes marker actions through /do. Use for automatic reactions to file changes; do NOT use for one-off inspection or tasks needing human judgment per file.
auto-trigger
false
last-updated
1774742400000
user-invocable
true
trigger_keywords
[ "watch", "watch files", "watch changes", "file sentinel", "monitor files", "watch start", "watch stop", "watch scan", "marker comments", "@citadel" ]

02 资源文件 1

03 SKILL.md 正文

/watch -- File Sentinel

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.

Orientation

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

Default execution path (READ FIRST)

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

Default flow — /watch start (no --remote flag)

  1. Do Steps 1 and 2 below (check existing watch, determine baseline commit).
  2. Skip Step 3 — do NOT call CronCreate. Leave cronId: null in state.
  3. Write the state file (Step 4) with status: "watching".
  4. Output:
    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)
    

Opt-in routine flow — /watch start --remote

Only when --remote is explicitly passed:

  1. Confirm: "This will use 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)"
  2. On confirmation, run the full Step 1–5 protocol including CronCreate.

Commands

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)

Protocol

/watch start

Step 1: Check for existing watch

  1. Read .planning/watch-state.json if it exists
  2. If status is "watching":
    • Show current state: last scan time, interval, pending actions count
    • Ask: "A watch is already active. Stop it and start a new one?"
    • If yes: run /watch stop first, then continue
    • If no: abort

Step 2: Determine baseline commit

  1. Run git rev-parse HEAD to get the current commit hash
  2. If not a git repo: fall back to timestamp-based detection (store current time as lastScanTime, skip commit-based diffing)
  3. Store this as lastScanCommit

Step 3: Create poll schedule (--remote only)

CronCreate:
  interval: "{N}m"  (default: 5m)
  command: "/watch scan"

Save the cron ID in the state file.

Step 4: Write 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
  }
}

Step 5: Confirm (--remote only)

Watch started.
  Interval:  every {N}m
  Baseline:  {commit hash, first 7 chars}
  State:     .planning/watch-state.json

/watch stop

  1. Read .planning/watch-state.json. If missing or not "watching": "No watch is active."
  2. CronDelete: {cronId} — if cronId is missing or deletion fails, continue.
  3. Update state: "status": "stopped", "cronId": null. Preserve all other fields.
  4. Output:
    Watch stopped.
      Scans completed:      {stats.scansRun}
      Markers found:        {stats.markersFound}
      Intake items created: {stats.intakeItemsCreated}
      Skills dispatched:    {stats.skillsDispatched}
    

/watch status

  1. If .planning/watch-state.json missing: "No watch configured. Use /watch start to begin."
  2. Output state fields: status, lastScanTime, lastScanCommit, interval, pendingActions.length, stats.
  3. If pendingActions non-empty, list each: [{action}] {file}:{line} -- {description}

/watch scan

Step 1: Load state

  1. Read .planning/watch-state.json
  2. If missing: create default state with lastScanCommit from git rev-parse HEAD and status: "watching".

Step 2: Detect changed files

Git mode (primary):

  1. git diff --name-only {lastScanCommit} HEAD (committed changes)
  2. git diff --name-only (unstaged) and git diff --name-only --cached (staged)
  3. Merge and deduplicate all three lists

Fallback mode (no git):

  1. find . -newer {timestamp_file} -type f
  2. Exclude node_modules/, .git/, .planning/, dist/, build/

If no files changed: update lastScanTime and stats.scansRun, exit early.

Step 3: Scan for marker comments

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.

Step 4: Classify unmarked changes

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)

Step 5: Dispatch markers

For each new marker:

  1. /do {action} in {file} at line {line}: {description}
  2. Log dispatch, add to processedMarkers, increment stats.skillsDispatched

Batch limit: Dispatch at most 5 per scan. Queue overflow in pendingActions.

Step 6: Write intake items

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.

Step 7: Update state

  • lastScanCommit: git rev-parse HEAD
  • lastScanTime: current ISO timestamp
  • Increment stats.scansRun, stats.markersFound
  • Update pendingActions and processedMarkers

Step 8: Report

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


Integration Points

  • Intake pipeline: Writes to .planning/intake/ for /autopilot.
  • Intent router: Routes markers through /do — never invokes skills directly.
  • Daemon: /daemon can start a watch alongside a campaign.

Fringe Cases

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


Contextual Gates

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:

  • Any: start watch and view scan reports
  • Familiar (5+ sessions): triggered skills run autonomously on file change; novices should use with caution and review dispatched actions

Quality Gates

  • Scan completes in under 10 seconds for repos up to 100K lines
  • No duplicate intake items for the same file and classification
  • No re-dispatched already-processed markers
  • Batch limit of 5 dispatches per scan enforced
  • State file updated atomically at end of scan
  • Works on Windows, macOS, Linux (Node.js fs + git CLI)
  • CronCreate failure does not leave watch in inconsistent state

Exit Protocol

  • /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.
  • On error: Clear message with fix. Never leave cron running if state is inconsistent.

04 同仓库更多 Skills

SethGammon
SethGammon/Citadel

workspace

Multi-repo campaign coordinator. Same lifecycle as fleet -- scope claims, discovery relay, wave-based execution -- but the unit of work is a repo, not a file. Coordinates campaigns across repositories with shared context.

★ 930
SethGammon
SethGammon/Citadel

wiki

Markdown-first knowledge base where the LLM acts as librarian. Ingests raw sources, compiles and interlinks topic files, self-maintains an index. No vector DB or embeddings required -- uses LLM-native navigation over structured markdown up to ~400K words.

资料检索与研究
★ 930
SethGammon
SethGammon/Citadel

verify

Self-test the Citadel hook pipeline from within a live session. Exercises real tool calls (Write, Edit, Bash, Read) and checks that hooks fired, telemetry accumulated, and no errors occurred. Reports HOOK HEALTH: PASS or HOOK HEALTH: FAIL with a per-hook breakdown.

测试与质量
★ 930
SethGammon
SethGammon/Citadel

unharness

Safely leave Citadel using the active adoption receipt. Produces a no-write, reviewable plan, preserves a portable archive, removes only exact owned material, and reports modified or externally registered surfaces as retained or unknown. Legacy installs must be imported before exact leave is claimed.

★ 930
SethGammon
SethGammon/Citadel

triage

GitHub issue and PR investigator. Pulls open issues/PRs, classifies them, searches the codebase for root cause or reviews contributed code, proposes fixes with file:line references, and optionally implements fixes. Use for investigating GitHub issues and reviewing PRs; do NOT use for general code review unrelated to GitHub issues.

开发工程师代码审查
★ 930
SethGammon
SethGammon/Citadel

test-gen

Generate and verify tests — happy path, edge cases, error paths — using the project's own framework and patterns

测试与质量
★ 930