---
name: monitor-catalog
description: Trigger background monitors that watch the FTS5 catalog source file and the index staleness — opt-in replacement for the auto-start removed in v1.1.4. Invoked by /monitor.
allowed-tools:
- Bash
---

<purpose>
Trigger CC's plugin-monitor system to arm the two catalog-watch monitors
declared in `monitors/monitors.json`. The monitors entries listen for
`when: on-skill-invoke:monitor-catalog` — invoking this skill is the documented
signal that arms them for the remainder of the session.
</purpose>

<core_principle>
This skill is a pure trigger. It does NOT spawn the monitor processes itself
(CC's runtime owns that lifecycle). It simply emits the skill-invoke event
that the manifest is listening for, then returns a short confirmation.
</core_principle>

<invocation>
Always invoked indirectly via the `/maxvision:monitor` slash
command (see `commands/monitor.md`). User does not call this skill directly —
`disable-model-invocation: true` keeps it out of the auto-discovery listing.

Renamed from `watch-catalog` to `monitor-catalog` to avoid slash command
collision with `bradautomates/claude-video` plugin (which owns `/watch`).
</invocation>

## Steps

### 1. Parse arguments

`$ARGUMENTS` may be empty, `status`, or `stop`.

### 2. Verify monitors manifest is present

```bash
set -euo pipefail
MANIFEST="${CLAUDE_PLUGIN_ROOT}/monitors/monitors.json"
test -f "$MANIFEST" || { echo "FATAL: monitors manifest missing at $MANIFEST"; exit 1; }
```

### 3. Verify the two monitor entries declare the expected trigger

```bash
set -euo pipefail
python3 -c "
import json, sys
with open('${CLAUDE_PLUGIN_ROOT}/monitors/monitors.json', encoding='utf-8') as f:
    manifest = json.load(f)
# Only check the two catalog monitors owned by this skill. Other monitors
# in the manifest (e.g. upstream-drift-check, which gates on
# on-skill-invoke:check-update) are scoped to different skills and must not
# be flagged as mismatched here.
catalog_names = {'catalog-source-changes', 'index-staleness'}
expected = 'on-skill-invoke:monitor-catalog'
# Defensive: warn loudly on any manifest entry that is missing its name
# (would otherwise be silently dropped from the filter below).
nameless = [i for i, m in enumerate(manifest) if not m.get('name')]
if nameless:
    print(f'FATAL: monitors manifest has nameless entries at indices {nameless}', file=sys.stderr)
    sys.exit(1)
catalog = [m for m in manifest if m['name'] in catalog_names]
missing = sorted(catalog_names - {m['name'] for m in catalog})
if missing:
    print(f'FATAL: expected catalog monitors missing from manifest: {missing}', file=sys.stderr)
    sys.exit(1)
mismatched = [m['name'] for m in catalog if m.get('when') != expected]
if mismatched:
    print(f'WARN: catalog monitors do not gate on {expected}: {mismatched}', file=sys.stderr)
    sys.exit(2)
print(f'OK — {len(catalog)}/{len(catalog_names)} catalog monitors gated on {expected}')
"
```

### 4. Branch on sub-command

- **(empty)** — print the canonical arm confirmation, then return. CC's runtime
  observes the skill invocation event and spawns the monitor commands per
  manifest. From this point until session end, mutations to
  `skills/discover-skill/references/skill-sources.json` will surface as
  notifications (throttled per `MONITOR_THROTTLE_S`, default 60s), and an
  index-staleness notification fires once per session if the FTS5 index DB is
  older than 7 days.

- **`status`** — read `~/.claude/cache/maxvision/.monitor-state.json`
  if present. The file is written by the monitor scripts on first emission.
  Print: `armed: true` plus last-notification timestamps if available.

- **`stop`** — print the documented detach guidance:
  > CC's plugin-monitor lifecycle is owned by the runtime. The monitors auto-tear
  > down at session end. To detach mid-session, end the session (Ctrl+D or
  > `/exit`). No CC-public kill API exists as of v2.1.143.

## Output contract

Single short status line, suitable for inclusion in a tool result. No long-form
prose. Examples:

```
armed: catalog-source-changes, index-staleness (manifest OK, 2 monitors)
status: armed=true, last_catalog_notify=2026-05-17T20:14:33Z, last_index_check=2026-05-17T20:00:01Z
stop: not supported — runtime tears down at session end
```

## Error handling

| Condition | Action |
|---|---|
| Manifest file missing | Exit 1 with clear error pointing to expected path |
| Manifest JSON malformed | Exit 1 with parse error + line/column |
| Monitor entry uses `when: always` instead of `when: on-skill-invoke:monitor-catalog` | Exit 2 with warning naming the misconfigured entry — the manifest must be repaired (this means the v1.1.4 refactor was reverted) |
| `CLAUDE_PLUGIN_ROOT` unset | Exit 1 — the skill is not running under CC plugin context |
| `python3` missing | Exit 1 — runtime dependency for the verifier step |

## Notes

- This skill exists ONLY to provide the trigger surface for the on-skill-invoke
  manifest entries. It deliberately has minimal logic — the monitor commands
  themselves (under `scripts/monitor_*.py`) own the watch loop.
- The skill cannot detect whether the monitor is ALREADY armed in this session.
  CC does not currently expose monitor-state to plugin code. The `status`
  sub-command reads a sidecar state file that the monitor scripts write on
  first notification — best effort.
- See spec `.maxvision/specs/2026-05-17-execution-plan-v1.2.0.md` (P0.1-P0.4
  monitor refactor block) for the rationale and the auto-start removal history.
