ops-socials — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited ops-socials (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
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).
Three reading/posting surfaces, multiple publishing identities. Don't cross the streams — between surfaces _or_ between identities.
This router serves two strictly separated classes of identity. Posting to the wrong one is the cardinal failure mode of this skill.
$SOCIAL_SET_ID, the global Typefully default). This identity is registered at $PREFS_PATH/preferences.json → marketing.social_identities.personal.*.marketing.projects.<project>.social with a social.engine.Resolution algorithm — run at the start of every flow:
intent is owner autopilot status (`my-project`, "show me the autopilot status", `/ops-socials my-project`, owner-autopilot read-out)?
├─ YES → skip project/personal identity resolution; run the Owner autopilot status recipe below (read-only). `my-project` here is NOT a project name.
└─ NO → continue
intent mentions / implies a named project (project arg, product name, "post for <project>")?
├─ YES → read marketing.projects.<project>.social.engine from $PREFS_PATH/preferences.json
│ ├─ engine.primary == "upload-post" → publish via mcp__upload-post__* with engine.upload_post.user.
│ │ ALWAYS pass engine.upload_post.brand_targeting IDs (facebook_page_id, target_linkedin_page_id)
│ │ so brand-admin personal OAuth lands on the BRAND page, never a personal feed.
│ └─ else (null / unprovisioned, "meta-graph", any other value, typo) → FAIL-CLOSED. STOP. Tell the user
│ the project has no supported posting engine in this skill (unprovisioned, unsupported engine,
│ or misconfigured). DO NOT fall back to the personal Typefully set or any other project's
│ channels. DO NOT post.
└─ NO → personal/founder post → Typefully with the personal $SOCIAL_SET_ID (resolution below).The personal Typefully set is NEVER a fallback for a project. An unprovisioned project never silently borrows the personal handle (or another project's). See Hard rule 6.
social_set_id at runtime — never hardcodeThis is a public plugin. The user's Typefully social_set_id and X handle are owner-specific data and MUST NOT be committed.
At the start of any personal/founder flow only — never when handling a project brand via upload-post — resolve $SOCIAL_SET_ID in this order:
$TYPEFULLY_SOCIAL_SET_ID if set.typefully.default_social_set_id (where $PREFS_PATH is the plugin data dir, set by claude-ops).$HOME/.config/typefully/config.json (.default_social_set).mcp__typefully__typefully_list_social_sets(); if exactly one, use it. If multiple and no default configured, ask the user via AskUserQuestion and persist the choice (see /typefully skill's config:set-default).In every recipe below, treat the literal string $SOCIAL_SET_ID as a placeholder for the resolved value.
Personal Typefully only: Any row below that publishes, schedules, or reads analytics via Typefully with the resolved personal $SOCIAL_SET_ID applies only when Resolve the IDENTITY (section above) ends on the personal/founder branch — never for a named project brand (use that project's social.engine or fail-closed).
| Intent | Surface | Default tool |
|---|---|---|
| Post for a named PROJECT brand (product social, not the owner's personal handle) | resolve marketing.projects.<project>.social.engine first (see "Resolve the IDENTITY" above) — only when engine.primary == "upload-post"; otherwise FAIL-CLOSED | mcp__upload-post__post_text / post_photos / post_video with the project's brand_targeting IDs |
| Read X — search, timeline, mentions, user lookup, "what's @x saying about Y", AI-news pulse | invoke skill x-research-skill for agentic multi-pass research; or call mcp__x-mcp__* directly for surgical queries | mcp__x-mcp__search_tweets, get_timeline, get_mentions |
| Long-form X Article (markdown → X Premium Article) | Not via `x-article-publisher-skill` here — that path needs Playwright on X, which hard rule 3 forbids. | Stage a Typefully draft: hook + summary + URL to the full piece (hosted blog/newsletter/static page); publish a native X Article only manually in the X client if needed. |
| LinkedIn voice / human-sounding posts / comments / growth tactics | invoke skill linkedin-skills for CRAFT; publish via Typefully | text drafted in linkedin-skills → handed to typefully_create_draft |
| Short tweet / thread / LinkedIn post / cross-platform | Typefully — mcp__typefully__typefully_create_draft with the resolved $SOCIAL_SET_ID | multi-platform: platforms: ["x","linkedin","threads","bluesky","mastodon"] |
| Schedule | Typefully with schedule_date: "next-free-slot" or ISO | mcp__typefully__typefully_get_queue to inspect |
| Analytics (own posts) | mcp__typefully__typefully_list_social_set_analytics_posts (or mcp__x-mcp__get_metrics per tweet) | replies excluded by default |
| LinkedIn org mention | mcp__typefully__typefully_linkedin_resolve_linkedin_organization_from_url → @[Name](urn:li:organization:ID) | paste into draft body |
| Owner autopilot status | shell out via bash -c to resolved $OPS_SOCIAL_AUTOPILOT_CMD (full shell command to an owner-specific status script) | env: OPS_SOCIAL_AUTOPILOT_CMD='python3 $HOME/tools/<owner>-social-autopilot/status.py'; prefs: ops_social.autopilot_cmd in $PREFS_PATH/preferences.json |
mcp__upload-post__* when engine.primary == "upload-post"). Reading → x-mcp / x-research-skill. Crafting LinkedIn → linkedin-skills. Never invert personal vs project engines. Never post via x-mcp's reply_to_tweet for marketing content — that burns the X-API write quota and skips Typefully's staging/cross-platform path.ok, send, ship it, post it, go, do it) or an AskUserQuestion [Send] selection. upload-post project path (no Typefully draft URL): show the full outbound payload the user will send — exact caption/body, media plan, and brand_targeting / profile identifiers — then require the same explicit per-message approval (plain-chat or AskUserQuestion [Send]) before calling mcp__upload-post__post_*. One approval → one post_* call; no batch sends.social.engine ONLY. Never post a project's content to the personal set, never post personal content to a project engine, never cross-post between projects, and never fall back to _any_ other identity when a project is unprovisioned (fail-closed). For upload-post brands, always pass the project's brand_targeting IDs. The owner-specific identity→channel map lives in $PREFS_PATH/preferences.json (marketing.social_identities + marketing.projects.<p>.social), never in this public file.After identity resolution ends on the personal/founder branch — not for project brands via upload-post — and before composing or staging a personal Typefully draft (single, thread, or cross-platform), read the owner's auto-generated performance learnings if ready, and bias the draft toward what the data shows works:
LEARN="$PREFS_PATH/social-metrics/learnings.md"
if [ -f "$LEARN" ] && ! grep -qE '^status:[[:space:]]*COLLECTING' "$LEARN" 2>/dev/null; then
cat "$LEARN" # ranked "do more / do less" features + top-performer templates
fiCOLLECTING, treat its "Do MORE of" features andtop-performer templates as the default tone/format target, and avoid its "Do LESS of" features. State in one line which learnings you applied (e.g. "biased to medium-length, first-person, punchline close per learnings").
COLLECTING, or the snippet above did not print it, fall backto the house default: a concrete number or scar in the opening line, first-person operator voice, one idea per post, short close.
social set's analytics every few hours, writes a time series, and re-derives the learnings). The loop is: tracker measures live posts → updates learnings → this step biases the next draft. You do not run the tracker from here; you only consume its latest output.
Channel, CLI, and edge-case detail lives in references/ next to this skill. Read those files before acting on a matching channel or sub-command. Do not skip them.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.