ops-projects · diff

git:20260822.2cfccdf to git:20260826.6f1e3e0

25 added, 10 removed. Audit A to A.

---
name: ops-projects
description: "OPS on-demand: This skill should be used when the user asks to \"portfolio dashboard\", \"gsd projects\"…"
argument-hint: '[project-alias|--sync|--refresh]'
allowed-tools:
- Bash
- Read
- Grep
- Glob
- WebFetch
- AskUserQuestion
effort: low
maxTurns: 20
disallowedTools:
- Edit
- Write
- NotebookEdit
---
## Runtime Context
Before rendering, load:
1. **Preferences**: `cat ${OPS_DATA_DIR:-$HOME/.claude/plugins/data/ops-ops-marketplace}/preferences.json` — read `owner`, `timezone`
2. **Daemon health**: `cat ${OPS_DATA_DIR}/daemon-health.json` — show service status in dashboard footer
3. **GSD registry**: `cat ${OPS_DATA_DIR}/registry.json` — primary project index (updated by daemon twice daily + on-demand with --sync)
# OPS ► PROJECTS — GSD Portfolio Dashboard
Load `ops-rules` before acting. Public repo (no personal data). Outbound: one draft → one approval → one send. If `AskUserQuestion` / `Workflow` are missing, follow Rule 10 in `ops-rules` (Hermes: numbered options / two-turn Telegram card; `delegate_task`).
## Quick commands
```bash
# Refresh registry data on demand
bash ${CLAUDE_PLUGIN_ROOT}/scripts/ops-gsd-registry-sync.sh
# Show registry contents
cat ${OPS_DATA_DIR}/registry.json
# Show health summary
cat ${OPS_DATA_DIR}/cache/projects_health.json
```
## Data flow
```
- ~/Projects/ ~/gsd-workspaces/
- │ │
- ▼ ▼
- [project]/.planning/ [project]/.planning/
- │ │
- ├── HANDOFF.json ├── HANDOFF.json ← active-phase projects
- ├── STATE.md ├── MILESTONES.md
- ├── ROADMAP.md └── STATE.md
- └── MILESTONES.md
+ $HOME (walked recursively, depth <= OPS_GSD_SCAN_DEPTH, default 5)
+ + any extra roots listed in OPS_GSD_SCAN_ROOTS (colon-separated)
+ ▼
+ <any-dir>/.planning/
+ │
+ ├── HANDOFF.json ← active-phase projects
+ ├── STATE.md
+ ├── ROADMAP.md
+ └── MILESTONES.md
+ │
▼ ops-gsd-registry-sync.sh (daemon: 6am + 6pm)
${OPS_DATA_DIR}/
├── registry.json ← all projects, sorted by priority
└── cache/
└── projects_health.json ← health summary
▼ ops-projects skill reads this
```
+ ### Discovery
+
+ `ops-gsd-registry-sync.sh` does not assume a fixed workspace directory. It walks
+ `$HOME` and records every `.planning/` directory it finds, so any layout works.
+
+ - `OPS_GSD_SCAN_ROOTS` — colon-separated extra roots to walk besides `$HOME`
+ (use this for projects that live outside the home directory)
+ - `OPS_GSD_SCAN_DEPTH` — how deep to walk (default 5)
+ - `OPS_GSD_SCAN_EXCLUDE` — colon-separated extra directory names to skip
+ - `OPS_GSD_INCLUDE_WORKTREES=1` — also scan `.worktrees/` (skipped by default,
+ since worktrees duplicate an existing project)
+
+ Build noise (`node_modules`, `.git`, `.venv`, `dist`, caches) is always skipped.
+
---
## Dashboard output
Build the dashboard from `${OPS_DATA_DIR}/cache/projects_health.json` (fall back to `${OPS_DATA_DIR}/registry.json` if missing). Show:
```
╔══════════════════════════════════════════════════════════════╗
║ OPS ► PROJECTS [owner] [date] [daemon status] ║
╠══════════════════════════════════════════════════════════════╣
║ GSD Portfolio — 23 projects ║
║ ║
║ 🟢 EXECUTING (3) ║
║ my-webapp Phase 12 [executing] branch: main 0 dirty ║
║ my-plugin Phase 16 [executing] branch: main 2 dirty ║
║ my-saas-app Phase 39 [executing] branch: dev 0 dirty ║
║ ║
║ 🟡 PAUSED (4) ║
║ my-project-a Phase 08 [paused] branch: feat 1 dirty ║
║ my-project-b Phase 3 [verifying] branch: main 0 dirty ║
║ ... ║
║ ║
║ 🔴 BLOCKED (1) ║
║ my-ecommerce Phase 7 [await-UAT] branch: prod 0 dirty ║
║ ║
║ ⚪ IDLE / UNTRACKED (15) ║
║ my-side-project Phase 1 [complete] branch: main 0 dirty ║
║ my-other-app — — branch: dev 3 dirty ║
║ ... ║
╠══════════════════════════════════════════════════════════════╣
║ ⚡ Services: message-listener ● | inbox-digest ⏱ 2h | gsd-registry ⏱ 12h ║
╚══════════════════════════════════════════════════════════════╝
```
### Status indicators (from `status` field, case-insensitive)
- **executing** → 🟢
- **paused / verifying / phase_complete** → 🟡
- **human / uat / blocked / pending** → 🔴
- **empty status + has phase** → ⚪ (idle)
- **no phase, no status** → ⚪ (untracked/no GSD)
### Columns per project: ALIAS | PHASE | STATUS | BRANCH | DIRTY | NEXT ACTION
---
## Project deep-dive
If `$ARGUMENTS` contains a project alias, find it in the registry and show:
```
╔══════════════════════════════════════════════════════════════╗
║ PROJECT — [alias] ║
╠══════════════════════════════════════════════════════════════╣
- ║ Path: ~/Projects/[alias]/ ║
+ ║ Path: [path from registry] ║
║ Phase: [phase] Status: [status] ║
║ Milestone: [milestone name] ║
║ Branch: [branch] Dirty: [N] Unpushed: [N] ║
║ Blockers: [N] ║
║ Next: [next_action text] ║
║ ║
║ GSD files: ✓ ROADMAP ✓ MILESTONES [HAS_HANDOFF] STATE ║
╚══════════════════════════════════════════════════════════════╝
Actions:
[1] Open project directory
[2] Continue GSD (/gsd-next [alias])
[3] Git: branch / status / log
[4] Run /ops projects (back to portfolio)
```
Present numbered actions and let the user pick by typing the number or action name.
---
## --sync flag
If `--sync` or `--refresh` is passed, run the registry sync script first, then display the updated dashboard:
```bash
bash ${CLAUDE_PLUGIN_ROOT}/scripts/ops-gsd-registry-sync.sh 2>&1
```
Show "Refreshing..." while running, then present the full updated dashboard.
---
## Error states
- If `registry.json` is missing/empty: show a one-shot sync prompt
```
No project registry found. Run /ops projects --sync to build it.
```
- If daemon health shows `action_needed`: surface that in a warning banner
---
## CLI reference (for manual use)
| Command | What it does |
| ----------------------- | --------------------------------------- |
| `/ops projects` | Show full portfolio |
| `/ops projects [alias]` | Deep-dive on one project |
| `/ops projects --sync` | Force-refresh registry + show dashboard |
| `/ops:setup registry` | Open registry setup (future) |