@m13v/s4l 1.7.5-rc.2 → 1.7.5-rc.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/mcp/dist/index.js +571 -79
  2. package/mcp/dist/panel.html +2 -3
  3. package/mcp/dist/redditAuth.js +115 -0
  4. package/mcp/dist/runtime.js +83 -2
  5. package/mcp/dist/setup.js +142 -5
  6. package/mcp/dist/telemetry.js +13 -3
  7. package/mcp/dist/version.json +2 -2
  8. package/mcp/manifest.json +1 -1
  9. package/mcp/menubar/s4l_card.py +32 -6
  10. package/mcp/menubar/s4l_menubar.py +42 -111
  11. package/mcp/menubar/s4l_state.py +54 -187
  12. package/mcp/package.json +1 -1
  13. package/mcp/shared/onboarding-ledger.cjs +63 -4
  14. package/package.json +1 -1
  15. package/scripts/autopilot_stall_watch.py +3 -6
  16. package/scripts/browser_mutex.py +316 -0
  17. package/scripts/claude_job.py +21 -0
  18. package/scripts/config.py +118 -0
  19. package/scripts/counterparty_history.py +44 -6
  20. package/scripts/engage_github.py +7 -3
  21. package/scripts/engage_reddit.py +9 -3
  22. package/scripts/engagement_signal_feedback.py +256 -0
  23. package/scripts/learned_preferences.py +37 -14
  24. package/scripts/memory_snapshot.py +45 -0
  25. package/scripts/merge_review_queue.py +67 -3
  26. package/scripts/post_reddit.py +289 -24
  27. package/scripts/reddit_browser.py +62 -63
  28. package/scripts/reddit_browser_fetch.py +32 -0
  29. package/scripts/release-mcpb.sh +17 -0
  30. package/scripts/reset-test-machine.sh +13 -7
  31. package/scripts/scan_twitter_thread_followups.py +7 -1
  32. package/scripts/schedule_state.py +1 -1
  33. package/scripts/setup_reddit_auth.py +925 -0
  34. package/scripts/snapshot.py +96 -4
  35. package/scripts/stats.py +37 -13
  36. package/scripts/test_no_silent_fallbacks.py +281 -0
  37. package/scripts/trigger_promo_lane_test.sh +91 -0
  38. package/scripts/twitter_browser.py +183 -342
  39. package/scripts/twitter_scan.py +75 -10
  40. package/scripts/watchdog_hung_runs.py +15 -9
  41. package/skill/lib/harness-common.sh +344 -0
  42. package/skill/lib/linkedin-backend.sh +61 -161
  43. package/skill/lib/reddit-backend.sh +39 -125
  44. package/skill/lib/twitter-backend.sh +51 -273
  45. package/skill/lock.sh +57 -0
  46. package/skill/run-reddit-search-launchd.sh +4 -1
  47. package/skill/run-reddit-search.sh +51 -2
  48. package/skill/run-twitter-cycle.sh +44 -21
@@ -187,6 +187,23 @@ fi
187
187
 
188
188
  command -v mcpb >/dev/null || die "mcpb CLI not found (npm i -g @anthropic-ai/mcpb)"
189
189
 
190
+ # ---- 0b. Regression gate: no silent-fallback / bare-python3 reintroductions --
191
+ # scripts/test_no_silent_fallbacks.py locks in two recurring bug classes that
192
+ # have separately shipped to customers more than once (X handle -> hardcoded
193
+ # "m13v_" impersonation, DEFAULT_ACCOUNTS, and bare "python3" subprocess spawns
194
+ # of Playwright-dependent scripts silently dying on a fresh install with no
195
+ # Playwright on PATH -- see bug_twitter_reply_bare_python3_no_playwright.md
196
+ # and bug_twitter_handle_scrape_brittle_m13v_fallback.md). Runs BEFORE any
197
+ # build/pack/publish work so a regression blocks the release, not a customer.
198
+ # Applies to every path that actually builds a new artifact (stable + both
199
+ # --staging and default runs); --promote ships an already-tested artifact
200
+ # byte-for-byte with no rebuild, so it's exempt (handled in step 0 above,
201
+ # which exits before reaching here) -- that artifact already passed this gate
202
+ # when it was first built.
203
+ say "Regression gate: scripts/test_no_silent_fallbacks.py"
204
+ python3 "$REPO_ROOT/scripts/test_no_silent_fallbacks.py" \
205
+ || die "regression gate failed -- a silent-fallback / bare-python3 issue was (re)introduced. See output above; do not release until it's clean or the finding is reviewed into the test's ALLOWLIST with a reason."
206
+
190
207
  # ---- 1. Resolve + WRITE version into the repo-root package.json -------------
191
208
  # The repo-root package.json is the SINGLE source of truth: `npm pack` reads it
192
209
  # to build the embedded pipeline.tgz, so the bundle shell and the bundled
@@ -304,12 +304,18 @@ fi
304
304
  echo
305
305
 
306
306
  # ---- 1c. Claude Desktop scheduled tasks -----------------------------------
307
- # Onboarding creates the queue-worker tasks (and historically the deprecated
308
- # single autopilot) under ~/.claude/scheduled-tasks/. They live OUTSIDE the
309
- # state dir, so a reset that skips them leaves idle workers firing every minute
310
- # against a wiped install. Remove the task dirs. Like the .mcpb registry, the
311
- # host (Claude Desktop) owns the live schedule and rewrites it on quit. Step [0]
312
- # already quit it; this warns only if it came back (--keep-claude / relaunch).
307
+ # Onboarding creates the queue-worker task (current installs: the universal
308
+ # `s4l-worker`; legacy installs: `saps-worker`/`saps-phase1-query`/
309
+ # `saps-phase2b-draft`/the deprecated single `social-autoposter-autopilot`)
310
+ # under ~/.claude/scheduled-tasks/. They live OUTSIDE the state dir, so a
311
+ # reset that skips them leaves idle workers firing every minute against a
312
+ # wiped install. TASK_IDS must be kept in sync with WORKER_TASK_IDS in
313
+ # s4l_menubar.py / schedule_state.py / scheduled_tasks_snapshot.py — this list
314
+ # went stale after the `s4l-worker` rename (2026-07-02) and every "deep reset"
315
+ # since then silently left the live task running (found 2026-07-15). Like the
316
+ # .mcpb registry, the host (Claude Desktop) owns the live schedule and
317
+ # rewrites it on quit. Step [0] already quit it; this warns only if it came
318
+ # back (--keep-claude / relaunch).
313
319
  echo "[1c] Claude Desktop scheduled tasks (queue workers + deprecated autopilot)"
314
320
  SCHED_DIR="$HOME_DIR/.claude/scheduled-tasks"
315
321
  if pgrep -x "Claude" >/dev/null 2>&1; then
@@ -322,7 +328,7 @@ if [ -d "$SCHED_DIR" ]; then
322
328
  [ -e "$d" ] && TASK_IDS+=("$(basename "$d")")
323
329
  done
324
330
  fi
325
- for t in saps-phase1-query saps-phase2b-draft social-autoposter-autopilot; do
331
+ for t in s4l-worker saps-worker saps-phase1-query saps-phase2b-draft social-autoposter-autopilot; do
326
332
  TASK_IDS+=("$t")
327
333
  done
328
334
  if [ "${#TASK_IDS[@]}" -gt 0 ]; then
@@ -65,6 +65,12 @@ DEFAULT_MAX_URLS = 40
65
65
  REPO_DIR = os.path.expanduser("~/social-autoposter")
66
66
  REPLY_PAGE_LIMIT = 500
67
67
 
68
+ # Pinned interpreter, never the literal "python3": twitter_browser.py (below)
69
+ # imports Playwright, and bare "python3" resolves to the caller's system
70
+ # Python on PATH, which has no Playwright on a fresh install (silent
71
+ # failure). See scripts/twitter_post_plan.py:131.
72
+ PYTHON = os.environ.get("S4L_PYTHON") or sys.executable
73
+
68
74
 
69
75
  def load_config():
70
76
  if os.path.exists(CONFIG_PATH):
@@ -137,7 +143,7 @@ def run_browser_scrape(urls, scroll_count=3):
137
143
  f.write(u + "\n")
138
144
  try:
139
145
  proc = subprocess.run(
140
- ["python3", os.path.join(REPO_DIR, "scripts/twitter_browser.py"),
146
+ [PYTHON, os.path.join(REPO_DIR, "scripts/twitter_browser.py"),
141
147
  "thread-followups", urls_path, str(scroll_count)],
142
148
  capture_output=True, text=True, timeout=1800,
143
149
  )
@@ -110,7 +110,7 @@ HEALTHY_DRAIN_MAX_SECONDS = 1200
110
110
  # (an idle queue has no pending job at all, so a quiet pipeline never trips it).
111
111
  STALL_SECONDS = HEALTHY_DRAIN_MAX_SECONDS
112
112
  # A job claimed but still sitting in running/ this long: the worker picked it up
113
- # and then wedged mid-run (the claude -p drafting child died / never spawned).
113
+ # and then wedged mid-run.
114
114
  RUNNING_STALL_SECONDS = HEALTHY_DRAIN_MAX_SECONDS
115
115
  # The producer's "drafting Nm" label alive this long: the end-to-end clock. This
116
116
  # is the only timer that spans claim→die→requeue churn loops, which reset the two