/ops-social-planner — engine-agnostic planned-content viewer
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).
Read-only dashboard of every scheduled post and ad, grouped identity/project → channel → time,
regardless of posting engine. Personal/founder identity and project brands stay strictly separated
(see /ops-socials identity rules). See SPEC.md for the full design.
What it does
bin/ops-social-planner collectreads$PREFS_PATH/preferences.json(marketing.social_identities.personal.*+marketing.projects.*.social), dispatches a per-engine fetcher keyed onsocial.engine.primary, normalizes to one schema, and writes$OPS_DATA_DIR/social-planner/state.json(owner data — never committed, Rule 0).- It then serves the static UI (
ui/) on localhost and opens the browser.
Engines
engine.primary |
Source | Status |
|---|---|---|
typefully |
GET /v2/social-sets/{id}/drafts (Bearer; key from ~/.config/typefully/config.json) |
✅ wired |
upload-post |
GET /api/uploadposts/schedule (Apikey; key from engine.upload_post.api_key_ref) |
✅ wired |
meta-graph / meta-ads |
Graph Ads API per project BM/token | 🔌 hook (UI shows "pending") |
google-ads |
GAQL per project customer id | 🔌 hook |
null / unprovisioned |
— | shown fail-closed (0 items) |
Adding an engine = adding one fetch<Engine>() in bin/ops-social-planner + a dispatch branch. No UI change.
Run it
"${CLAUDE_PLUGIN_ROOT}/bin/ops-social-planner" # collect → serve → open
"${CLAUDE_PLUGIN_ROOT}/bin/ops-social-planner" collect # regenerate state.json only
"${CLAUDE_PLUGIN_ROOT}/bin/ops-social-planner" serve --port 7937 # serve existing state
The UI renders ui/state.sample.json (synthetic, PII-free) when no live state exists, so it works
with zero credentials — useful for the PR preview.
Agent enrichment path (optional, when invoked as a skill)
The headless collector covers posts on wired engines. When richer output is wanted, the agent can:
- Pull ads via MCP (
mcp__meta__*, Google Ads) per project and mergekind:"ad"items intostate.json. - Replace the heuristic
rationalewith an LLM-written one (slot + sequence + channel intent). - Re-run
serveto refresh. Do not publish or edit from here — routing/mutation belongs to/ops-socials+/ops-marketing.
Guarantees
- Read-only. No secrets in the client. Server binds
127.0.0.1only. - Identity separation honored: personal vs project never merged; unprovisioned projects render fail-closed.
- No owner data committed — only
ui/+bin/+SPEC.md+ the synthetic fixture.