watch
/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)
- Do Steps 1 and 2 below (check existing watch, determine baseline commit).
- Skip Step 3 — do NOT call
CronCreate. LeavecronId: nullin state. - Write the state file (Step 4) with
status: "watching". - 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:
- 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)" - 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
- Read
.planning/watch-state.jsonif it exists - If
statusis"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 stopfirst, then continue - If no: abort
Step 2: Determine baseline commit
- Run
git rev-parse HEADto get the current commit hash - If not a git repo: fall back to timestamp-based detection (store current
time as
lastScanTime, skip commit-based diffing) - 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
- Read
.planning/watch-state.json. If missing or not"watching": "No watch is active." CronDelete: {cronId}— if cronId is missing or deletion fails, continue.- Update state:
"status": "stopped", "cronId": null. Preserve all other fields. - Output:
Watch stopped. Scans completed: {stats.scansRun} Markers found: {stats.markersFound} Intake items created: {stats.intakeItemsCreated} Skills dispatched: {stats.skillsDispatched}
/watch status
- If
.planning/watch-state.jsonmissing: "No watch configured. Use/watch startto begin." - Output state fields: status, lastScanTime, lastScanCommit, interval, pendingActions.length, stats.
- If
pendingActionsnon-empty, list each:[{action}] {file}:{line} -- {description}
/watch scan
Step 1: Load state
- Read
.planning/watch-state.json - If missing: create default state with
lastScanCommitfromgit rev-parse HEADandstatus: "watching".
Step 2: Detect changed files
Git mode (primary):
git diff --name-only {lastScanCommit} HEAD(committed changes)git diff --name-only(unstaged) andgit diff --name-only --cached(staged)- Merge and deduplicate all three lists
Fallback mode (no git):
find . -newer {timestamp_file} -type f- 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: Compare against processedMarkers (stored as "{file}:{line}:{action}"). Skip already-processed markers. Remove from processedMarkers when the file is modified again.
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:
/do {action} in {file} at line {line}: {description}- Log dispatch, add to
processedMarkers, incrementstats.skillsDispatched
Batch limit: Dispatch at most 5 per scan. Queue overflow in pendingActions.
Step 6: Write intake items
Filename: watch-{timestamp}-{index}.md in .planning/intake/
---
source: watch
priority: {normal|high}
created: {ISO timestamp}
---
# {brief description}
File: {file path}
Change type: {new|modified|deleted}
Classification: {test change|doc change|source change|config change|marker overflow}
{If marker: Action: {action}, Description: {description}}
Detected by /watch scan at {ISO timestamp}.
Before writing, check for duplicates (glob .planning/intake/watch-*, grep for file path).
Step 7: Update state
lastScanCommit:git rev-parse HEADlastScanTime: current ISO timestamp- Increment
stats.scansRun,stats.markersFound - Update
pendingActionsandprocessedMarkers
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:
/daemoncan start a watch alongside a campaign. - Session-start hook:
init-project.jstriggers a scan if state is"watching".
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: Skip if lastScanTime within last 60 seconds.
Marker removed: Remove stale entries from processedMarkers on each scan.
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.