-
Notifications
You must be signed in to change notification settings - Fork 21
Architecture
How claude-ops works internally — plugin structure, data flow, token efficiency mechanisms, and the security model.
claude-ops/
├── .claude-plugin/
│ └── plugin.json # Plugin manifest, version, userConfig schema
├── skills/ # 14 slash command skills
│ ├── ops/ # Router — dispatches to sub-skills
│ ├── ops-go/ # Morning briefing
│ ├── ops-inbox/ # Unified inbox
│ ├── ops-comms/ # Cross-channel messaging
│ ├── ops-merge/ # Autonomous PR pipeline
│ ├── ops-fires/ # Production incidents
│ ├── ops-deploy/ # Deploy status
│ ├── ops-revenue/ # Cost tracking
│ ├── ops-projects/ # Portfolio dashboard
│ ├── ops-linear/ # Sprint management
│ ├── ops-triage/ # Issue triage
│ ├── ops-next/ # Next action advisor
│ ├── ops-yolo/ # YOLO autonomous mode
│ └── setup/ # Interactive setup wizard
├── agents/ # 9 autonomous agents
│ ├── yolo-ceo.md # CEO synthesizer (claude-opus-4-6)
│ ├── yolo-cto.md # CTO technical analysis (claude-opus-4-6)
│ ├── yolo-cfo.md # CFO financial analysis (claude-opus-4-6)
│ ├── yolo-coo.md # COO operations analysis (claude-opus-4-6)
│ ├── triage-agent.md # Issue investigation and fix (claude-sonnet-4-5)
│ ├── comms-scanner.md # Inbox state scanner (claude-sonnet-4-5)
│ ├── infra-monitor.md # Infrastructure health (claude-sonnet-4-5)
│ ├── project-scanner.md # Project portfolio scanner (claude-sonnet-4-5)
│ └── revenue-tracker.md # Revenue/cost monitor (claude-sonnet-4-5)
├── bin/ # Shell scripts for data gathering
│ ├── ops-gather # Master parallel gatherer (used by /ops:go)
│ ├── ops-git # Git status across all projects → JSON
│ ├── ops-infra # ECS cluster health → JSON
│ ├── ops-prs # Open PRs across all repos → JSON
│ ├── ops-ci # GitHub Actions failures (24h) → JSON
│ ├── ops-unread # Channel unread counts → JSON
│ ├── ops-merge-scan # PR merge readiness data → JSON
│ ├── ops-setup-detect # Current config state → JSON
│ ├── ops-setup-install # Idempotent CLI installer
│ ├── ops-slack-autolink.mjs # Playwright-based Slack token extractor
│ └── ops-telegram-autolink.mjs # Telegram MTProto setup automation
├── hooks/
│ └── hooks.json # SessionStart health check hook
├── telegram-server/ # Bundled MCP server (gram.js MTProto)
├── scripts/
│ ├── registry.json # Per-user project registry (gitignored)
│ ├── registry.example.json # Template
│ └── setup.sh # Config detection for SessionStart hook
└── .mcp.json # MCP server declarations
The most important architectural decision in claude-ops is how data is gathered.
Without optimization, a morning briefing skill would need to:
- Load into model context
- Call Bash to check ECS health
- Wait for response
- Call Bash to check PRs
- Wait for response
- ... repeat for each data source
This is sequential, slow (20+ seconds), and burns tokens for each intermediate step.
All data-gathering skills use ! fence blocks in their SKILL.md prompts:
```!
${CLAUDE_PLUGIN_ROOT}/bin/ops-infra 2>/dev/null || echo '{}'
Claude Code executes shell commands inside `` ``` ``` `` blocks **before** the model context is loaded. The output is injected directly into the skill prompt as pre-populated context.
**Effect:** All data is gathered in parallel before the model receives a single token. The model sees a fully populated dashboard to analyze, not a sequence of tool calls to make.
**Result:** `/ops:go` delivers a complete briefing in under 10 seconds regardless of how many data sources are configured.
### `ops-gather` — master parallel gatherer
The `bin/ops-gather` script runs all individual gatherers simultaneously using bash background processes:
```bash
"$SCRIPT_DIR/ops-git" > "$TMPDIR_OPS/git.json" 2>/dev/null &
"$SCRIPT_DIR/ops-infra" > "$TMPDIR_OPS/infra.json" 2>/dev/null &
"$SCRIPT_DIR/ops-prs" > "$TMPDIR_OPS/prs.json" 2>/dev/null &
"$SCRIPT_DIR/ops-ci" > "$TMPDIR_OPS/ci.json" 2>/dev/null &
"$SCRIPT_DIR/ops-unread" > "$TMPDIR_OPS/unread.json" 2>/dev/null &
wait
All scripts run in parallel and wait collects them. Total wall-clock time is the max of any individual script, not their sum.
/ops:go invoked
│
├── ! bin/ops-infra ──→ ECS cluster data (aws CLI)
├── ! bin/ops-git ──→ Git status per project (git)
├── ! bin/ops-prs ──→ Open PRs per repo (gh CLI)
├── ! bin/ops-ci ──→ CI failures last 24h (gh CLI)
├── ! bin/ops-unread ──→ Channel unread counts (wacli, gog, env flags)
├── ! GSD STATE.md ──→ Active roadmap phases
└── ! gog calendar list ──→ Today's calendar events
│ (all parallel, before model loads)
▼
Model receives complete data snapshot
│
▼
Renders unified dashboard
│
└── Routes to sub-skills on user request
/ops:yolo invoked
│
├── Phase 1: Pre-gather ALL data (same as ops:go + AWS costs + registry)
│
├── Phase 2: Spawn 4 agents in parallel
│ ├── yolo-cto.md → /tmp/yolo-[session]/cto-analysis.md
│ ├── yolo-cfo.md → /tmp/yolo-[session]/cfo-analysis.md
│ ├── yolo-coo.md → /tmp/yolo-[session]/coo-analysis.md
│ └── yolo-ceo.md → reads all three, writes final report
│
├── Phase 3: CEO presents Hard Truths report to user
│
└── Phase 4 (on "YOLO"): Autonomous loop
├── /ops:inbox → process all channels
├── /ops:merge → merge ready PRs
├── /ops:fires → fix active incidents
└── /ops:triage → resolve open issues
/ops:triage invoked
│
├── Sentry MCP: search_issues (unresolved, recent)
├── Linear MCP: list_issues (open, current team)
└── gh CLI: issue list (open, all repos)
│
▼
For each issue:
├── Search codebase for error pattern (Grep)
├── Check recent git log for fix commits
│
├── If fixed in code:
│ ├── Sentry MCP: update_issue (resolved)
│ └── Linear MCP: save_issue (done)
│
└── If active:
└── Spawn triage-agent (claude-sonnet-4-5, effort:high)
├── Root cause investigation
├── Fix implementation (Edit/Write)
├── Branch + PR creation (gh CLI)
└── Linear comment with PR link
All skills that need to know about your projects read from scripts/registry.json. This file is gitignored — it's per-user config, not part of the plugin source.
Schema:
{
"version": "1.0",
"owner": "Your Name",
"projects": [
{
"alias": "myapp",
"paths": ["~/Projects/myapp"],
"repos": ["github-org/myapp"],
"org": "github-org",
"type": "monorepo",
"infra": {
"ecs_clusters": ["myapp-production"],
"platform": "aws"
},
"revenue": {
"model": "saas",
"stage": "growth",
"mrr": 5000
},
"gsd": true,
"priority": 1
}
]
}The registry drives:
- Which git repos are checked in
ops-git - Which ECS clusters are monitored in
ops-infra - Which GitHub repos are queried in
ops-prsandops-ci - Which projects appear in
/ops:projects - Revenue context in
/ops:revenueand YOLO CFO analysis
All sensitive values (Telegram session string, API keys, tokens) are stored in Claude Code's plugin settings (userConfig in plugin.json). Claude Code stores these encrypted in ~/.claude.json. They are passed to the Telegram MCP server as environment variables via .mcp.json's env block — never written to disk as plaintext.
The setup wizard uses umask 077 when writing preferences.json to ensure the file is readable only by the current user (mode 600). This applies to the data directory at ~/.claude/plugins/data/ops-ops-marketplace/.
The setup wizard only appends to shell profiles (~/.zshrc, ~/.bashrc). It never rewrites or truncates them. The export line is idempotent — if already present, it is not added again.
scripts/registry.json contains your project paths, ECS cluster names, and revenue data. It is listed in .gitignore and is never committed to the plugin source repository.
When the setup wizard finds a credential in your environment or dotfiles during auto-scan, it shows you the discovered value and source before using it. Tokens are never silently consumed.
The telegram-server/ directory contains a Node.js MCP server built on gram.js (MTProto protocol). It runs as a local process, declared in .mcp.json:
{
"mcpServers": {
"claude_ops_telegram": {
"command": "node",
"args": ["telegram-server/index.js"],
"env": {
"TELEGRAM_API_ID": "${user_config.telegram_api_id}",
"TELEGRAM_API_HASH": "${user_config.telegram_api_hash}",
"TELEGRAM_PHONE": "${user_config.telegram_phone}",
"TELEGRAM_SESSION": "${user_config.telegram_session}"
}
}
}
}The ${user_config.*} placeholders are resolved by Claude Code from plugin settings at runtime. The server only starts when Telegram credentials are configured.
Tools exposed:
-
list_dialogs— recent conversations with unread counts -
get_messages— messages from a specific chat by username or chat ID -
send_message— send a message to a contact -
search_messages— full-text search across all chats
Why MTProto instead of Bot API: The Bot API only works with bots, which cannot read user DMs. MTProto authenticates as your personal account, giving access to all your conversations.
hooks/hooks.json installs a SessionStart hook that runs on every Claude Code session start:
{
"hooks": {
"SessionStart": [{
"hooks": [{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/setup.sh 2>/dev/null | grep '✗' | head -3 || true"
}]
}]
}
}This runs the config detection script and surfaces the first 3 ✗ (missing/broken) items as a session-start reminder. It is non-blocking — if the script fails for any reason, the session starts normally (|| true).