From 05146ef8cba9573c4eb247f06f7566f4698e16ab Mon Sep 17 00:00:00 2001 From: auroracapital Date: Sat, 30 May 2026 21:21:09 -0400 Subject: [PATCH 1/4] ops-socials: add owner-autopilot status sub-route MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a read-only routing row + recipe that shells out to a user-configured `$OPS_SOCIAL_AUTOPILOT_CMD` to surface per-channel autopilot state (connected, queue depth, recent fires, next action). No hardcoded paths or owner data — script location is owner-provided via env var; falls back to a clear "not wired" message. Co-Authored-By: Claude Opus 4.7 --- claude-ops/skills/ops-socials/SKILL.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/claude-ops/skills/ops-socials/SKILL.md b/claude-ops/skills/ops-socials/SKILL.md index 4003afa..7003716 100644 --- a/claude-ops/skills/ops-socials/SKILL.md +++ b/claude-ops/skills/ops-socials/SKILL.md @@ -102,6 +102,7 @@ In every recipe below, treat the literal string `$SOCIAL_SET_ID` as a placeholde | **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 to `$OPS_SOCIAL_AUTOPILOT_CMD` (user-configured path to an owner-specific status script that returns per-channel state) | example: `OPS_SOCIAL_AUTOPILOT_CMD=~/tools/-social-autopilot/status.py` | ## Hard rules @@ -225,6 +226,13 @@ Per-tweet drill-down on impressions/engagement: `mcp__x-mcp__get_metrics({ id }) `x-article-publisher-skill` automates X's web UI via Playwright. Hard rule 3 forbids that from this router. Instead: stage a Typefully draft that's a hook + summary + a link to the full piece (your blog, Substack, static page). If you genuinely need a native X Article, publish it manually in the X client. +### "Show me the autopilot status" / "/ops-socials healify" / owner-autopilot read-out +```bash +[ -n "$OPS_SOCIAL_AUTOPILOT_CMD" ] && bash -c "$OPS_SOCIAL_AUTOPILOT_CMD" || echo "no autopilot wired — set OPS_SOCIAL_AUTOPILOT_CMD in your env or $PREFS_PATH/preferences.json" +``` +Returns per-channel state: connected, queue depth, recent fires, next action. Read-only. + + ## Pre-flight check (run when troubleshooting) ```bash From 8bf28acc2c8ee1526adc2aa7f4ec5e900626abe9 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 31 May 2026 01:22:50 +0000 Subject: [PATCH 2/4] Fix autopilot status fallback on command failure Use if/else instead of &&/|| so a non-zero exit from OPS_SOCIAL_AUTOPILOT_CMD does not show the unwired message when autopilot is configured. --- claude-ops/skills/ops-socials/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/claude-ops/skills/ops-socials/SKILL.md b/claude-ops/skills/ops-socials/SKILL.md index 7003716..eb5d1b1 100644 --- a/claude-ops/skills/ops-socials/SKILL.md +++ b/claude-ops/skills/ops-socials/SKILL.md @@ -228,7 +228,7 @@ Per-tweet drill-down on impressions/engagement: `mcp__x-mcp__get_metrics({ id }) ### "Show me the autopilot status" / "/ops-socials healify" / owner-autopilot read-out ```bash -[ -n "$OPS_SOCIAL_AUTOPILOT_CMD" ] && bash -c "$OPS_SOCIAL_AUTOPILOT_CMD" || echo "no autopilot wired — set OPS_SOCIAL_AUTOPILOT_CMD in your env or $PREFS_PATH/preferences.json" +if [ -n "$OPS_SOCIAL_AUTOPILOT_CMD" ]; then bash -c "$OPS_SOCIAL_AUTOPILOT_CMD"; else echo "no autopilot wired — set OPS_SOCIAL_AUTOPILOT_CMD in your env or $PREFS_PATH/preferences.json"; fi ``` Returns per-channel state: connected, queue depth, recent fires, next action. Read-only. From b370791c56969bcd0a18abba5bb1c396f9e3303e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 31 May 2026 01:24:57 +0000 Subject: [PATCH 3/4] Fix ops-socials autopilot cmd prefs resolution and bash -c example Read ops_social.autopilot_cmd from preferences.json when the env var is unset, document the prefs key, and require a full shell command (e.g. python3 path/to/status.py) instead of a bare script path for bash -c. --- claude-ops/skills/ops-socials/SKILL.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/claude-ops/skills/ops-socials/SKILL.md b/claude-ops/skills/ops-socials/SKILL.md index eb5d1b1..56e9a33 100644 --- a/claude-ops/skills/ops-socials/SKILL.md +++ b/claude-ops/skills/ops-socials/SKILL.md @@ -102,7 +102,7 @@ In every recipe below, treat the literal string `$SOCIAL_SET_ID` as a placeholde | **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 to `$OPS_SOCIAL_AUTOPILOT_CMD` (user-configured path to an owner-specific status script that returns per-channel state) | example: `OPS_SOCIAL_AUTOPILOT_CMD=~/tools/-social-autopilot/status.py` | +| **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/-social-autopilot/status.py'`; prefs: `ops_social.autopilot_cmd` in `$PREFS_PATH/preferences.json` | ## Hard rules @@ -227,8 +227,13 @@ Per-tweet drill-down on impressions/engagement: `mcp__x-mcp__get_metrics({ id }) `x-article-publisher-skill` automates X's web UI via Playwright. Hard rule 3 forbids that from this router. Instead: stage a Typefully draft that's a hook + summary + a link to the full piece (your blog, Substack, static page). If you genuinely need a native X Article, publish it manually in the X client. ### "Show me the autopilot status" / "/ops-socials healify" / owner-autopilot read-out +Resolve the command in this order: (1) `$OPS_SOCIAL_AUTOPILOT_CMD` if set; (2) `$PREFS_PATH/preferences.json` → `ops_social.autopilot_cmd`. The value must be a **full shell command** (e.g. `python3 $HOME/tools/-social-autopilot/status.py`), not a bare `.py` path — the recipe runs it via `bash -c`. ```bash -if [ -n "$OPS_SOCIAL_AUTOPILOT_CMD" ]; then bash -c "$OPS_SOCIAL_AUTOPILOT_CMD"; else echo "no autopilot wired — set OPS_SOCIAL_AUTOPILOT_CMD in your env or $PREFS_PATH/preferences.json"; fi +CMD="${OPS_SOCIAL_AUTOPILOT_CMD:-}" +if [ -z "$CMD" ] && [ -f "$PREFS_PATH/preferences.json" ] && command -v jq >/dev/null 2>&1; then + CMD="$(jq -r '.ops_social.autopilot_cmd // empty' "$PREFS_PATH/preferences.json" 2>/dev/null)" +fi +if [ -n "$CMD" ]; then bash -c "$CMD"; else echo "no autopilot wired — set OPS_SOCIAL_AUTOPILOT_CMD or ops_social.autopilot_cmd in $PREFS_PATH/preferences.json"; fi ``` Returns per-channel state: connected, queue depth, recent fires, next action. Read-only. From 8554e97c1ace5eae1fc551861911be403b610ff5 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 31 May 2026 01:26:45 +0000 Subject: [PATCH 4/4] Fix healify alias bypassing project identity resolution Add an autopilot-status check at the start of the identity resolution algorithm so /ops-socials healify runs the owner autopilot recipe instead of treating healify as a marketing project name. --- claude-ops/skills/ops-socials/SKILL.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/claude-ops/skills/ops-socials/SKILL.md b/claude-ops/skills/ops-socials/SKILL.md index 56e9a33..d2ed01f 100644 --- a/claude-ops/skills/ops-socials/SKILL.md +++ b/claude-ops/skills/ops-socials/SKILL.md @@ -61,6 +61,9 @@ This router serves two **strictly separated** classes of identity. Posting to th **Resolution algorithm — run at the start of every flow:** ``` +intent is owner autopilot status (`healify`, "show me the autopilot status", `/ops-socials healify`, owner-autopilot read-out)? +├─ YES → skip project/personal identity resolution; run the Owner autopilot status recipe below (read-only). `healify` here is NOT a project name. +└─ NO → continue intent mentions / implies a named project (project arg, product name, "post for ")? ├─ YES → read marketing.projects..social.engine from $PREFS_PATH/preferences.json │ ├─ engine.primary == "upload-post" → publish via mcp__upload-post__* with engine.upload_post.user.