@m13v/s4l 1.7.4-rc.29 → 1.7.4-rc.30
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.
- package/mcp/dist/version.json +2 -2
- package/mcp/manifest.json +1 -1
- package/mcp/menubar/s4l_menubar.py +79 -0
- package/mcp/package.json +1 -1
- package/package.json +1 -1
package/mcp/dist/version.json
CHANGED
package/mcp/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"dxt_version": "0.1",
|
|
3
3
|
"name": "social-autoposter",
|
|
4
4
|
"display_name": "S4L",
|
|
5
|
-
"version": "1.7.4-rc.
|
|
5
|
+
"version": "1.7.4-rc.30",
|
|
6
6
|
"description": "Draft, review, approve, and autopilot X/Twitter posts.",
|
|
7
7
|
"long_description": "## **⚠️ The disclaimer above is generic Claude boilerplate.** Anthropic shows the same warning on every plugin regardless of what it does; any plugin has the same level of access as any app you download from the internet.\n\nS4L is an open source product developed by Mediar.ai Incorporated, a VC-backed San Francisco-based startup.\n\nTo get started:\n\n1\\. Copy this prompt: **Set me up on S4L plugin end to end**\n\n2\\. Quit with CMD+Q, reopen Claude, paste into a new chat.\n\nWhat happens next:\n\n* About every 5 minutes S4L scans X for posts that match your topics and drafts replies in your voice.\n* Drafts show up as review cards, usually the first within a few minutes. Nothing is posted automatically; you approve each one.\n* Posting autopilot stays off until you explicitly turn it on.",
|
|
8
8
|
"author": {
|
|
@@ -555,6 +555,9 @@ class S4LMenuBar(rumps.App):
|
|
|
555
555
|
# (0 = not snoozed). Loaded from disk so a menubar restart mid-snooze
|
|
556
556
|
# doesn't re-pop the card the user just put away.
|
|
557
557
|
self._review_snooze_until = self._read_snooze_until()
|
|
558
|
+
# Reveal cadence: when the last fresh card presentation happened (epoch,
|
|
559
|
+
# 0 = never). Loaded from disk so a restart mid-interval doesn't re-pop.
|
|
560
|
+
self._last_presented_at = self._read_last_presented()
|
|
558
561
|
# Unattended-review watchdog state (_maybe_heal_review).
|
|
559
562
|
self._review_heal_at = 0.0
|
|
560
563
|
self._review_unattended_notified = False
|
|
@@ -2667,6 +2670,11 @@ class S4LMenuBar(rumps.App):
|
|
|
2667
2670
|
int(self._review_snooze_until)
|
|
2668
2671
|
if time.time() < self._review_snooze_until
|
|
2669
2672
|
else 0,
|
|
2673
|
+
# Reveal cadence: rebuilds when the preset changes (checkmark +
|
|
2674
|
+
# submenu title) and when a hold starts or lapses (the "Next cards
|
|
2675
|
+
# around HH:MM" line in _state_c).
|
|
2676
|
+
round(st.read_reveal_cadence()),
|
|
2677
|
+
int(self._reveal_hold_until(pending_count)),
|
|
2670
2678
|
)
|
|
2671
2679
|
if sig != self._sig:
|
|
2672
2680
|
self._sig = sig
|
|
@@ -2765,6 +2773,37 @@ class S4LMenuBar(rumps.App):
|
|
|
2765
2773
|
except Exception:
|
|
2766
2774
|
pass
|
|
2767
2775
|
|
|
2776
|
+
def _read_last_presented(self):
|
|
2777
|
+
try:
|
|
2778
|
+
with open(REVEAL_STAMP_FILE) as f:
|
|
2779
|
+
return float((json.load(f) or {}).get("at") or 0.0)
|
|
2780
|
+
except Exception:
|
|
2781
|
+
return 0.0
|
|
2782
|
+
|
|
2783
|
+
def _stamp_presented(self):
|
|
2784
|
+
"""Record a fresh card presentation, mirrored to disk (restart-safe).
|
|
2785
|
+
Starts the next reveal-cadence interval."""
|
|
2786
|
+
self._last_presented_at = time.time()
|
|
2787
|
+
try:
|
|
2788
|
+
with open(REVEAL_STAMP_FILE, "w") as f:
|
|
2789
|
+
json.dump({"at": self._last_presented_at}, f)
|
|
2790
|
+
except Exception:
|
|
2791
|
+
pass
|
|
2792
|
+
|
|
2793
|
+
def _reveal_hold_until(self, pending_count):
|
|
2794
|
+
"""Epoch until which the reveal cadence is actually holding fresh cards
|
|
2795
|
+
back (0.0 = no hold). A hold only exists while there is something to
|
|
2796
|
+
hold: pending drafts, no review in flight, and not snoozed (the snooze
|
|
2797
|
+
label and gate take precedence)."""
|
|
2798
|
+
if pending_count <= 0 or self._review_active:
|
|
2799
|
+
return 0.0
|
|
2800
|
+
now = time.time()
|
|
2801
|
+
if now < self._review_snooze_until:
|
|
2802
|
+
return 0.0
|
|
2803
|
+
cadence = st.read_reveal_cadence()
|
|
2804
|
+
until = self._last_presented_at + cadence
|
|
2805
|
+
return until if (cadence > 0 and now < until) else 0.0
|
|
2806
|
+
|
|
2768
2807
|
def _review_now(self, _=None):
|
|
2769
2808
|
"""Menu: bring the pending draft cards up right now. Clears any snooze;
|
|
2770
2809
|
this is the one presentation path that may take keyboard focus (the
|
|
@@ -2846,6 +2885,17 @@ class S4LMenuBar(rumps.App):
|
|
|
2846
2885
|
sys.stderr.flush()
|
|
2847
2886
|
self._last_review_sig = sig
|
|
2848
2887
|
return
|
|
2888
|
+
# Reveal cadence: hold FRESH pop-ups until the configured interval since
|
|
2889
|
+
# the last presentation has passed (0 = show immediately). Drafting is
|
|
2890
|
+
# untouched; the backlog keeps accumulating and is presented whole once
|
|
2891
|
+
# due. Deliberately after the extend-open-card path above (an on-screen
|
|
2892
|
+
# card keeps growing live) and skipped for focus=True (the menu's
|
|
2893
|
+
# "Review N pending drafts" is an explicit ask). _last_review_sig is NOT
|
|
2894
|
+
# stamped on a hold, so the same set still presents fresh when due.
|
|
2895
|
+
if not focus:
|
|
2896
|
+
cadence = st.read_reveal_cadence()
|
|
2897
|
+
if cadence > 0 and time.time() - self._last_presented_at < cadence:
|
|
2898
|
+
return
|
|
2849
2899
|
with self._review_lock:
|
|
2850
2900
|
self._reset_posting_progress_locked()
|
|
2851
2901
|
self._review_active = True
|
|
@@ -2869,6 +2919,7 @@ class S4LMenuBar(rumps.App):
|
|
|
2869
2919
|
# Record as shown only AFTER the cards are actually up, so a transient
|
|
2870
2920
|
# card-UI failure never permanently suppresses this pending set.
|
|
2871
2921
|
self._last_review_sig = sig
|
|
2922
|
+
self._stamp_presented()
|
|
2872
2923
|
# No macOS notification for fresh drafts, per explicit user
|
|
2873
2924
|
# request (2026-07-03): the card itself is the surface. A missed
|
|
2874
2925
|
# card is the unattended watchdog's job (it heals the window and
|
|
@@ -3457,6 +3508,22 @@ class S4LMenuBar(rumps.App):
|
|
|
3457
3508
|
split_menu.add(it)
|
|
3458
3509
|
items.append(split_menu)
|
|
3459
3510
|
|
|
3511
|
+
# Reveal cadence: how often fresh draft cards may pop (the drafting
|
|
3512
|
+
# pipeline is unchanged; this only paces the pop-up). Always offered:
|
|
3513
|
+
# it is the "don't interrupt me every few minutes" control.
|
|
3514
|
+
cadence = st.read_reveal_cadence()
|
|
3515
|
+
cadence_menu = rumps.MenuItem(
|
|
3516
|
+
f"Show draft cards: {self._cadence_label(cadence).lower()}"
|
|
3517
|
+
)
|
|
3518
|
+
for preset, label in self.REVEAL_CADENCE_PRESETS:
|
|
3519
|
+
it = rumps.MenuItem(
|
|
3520
|
+
label,
|
|
3521
|
+
callback=functools.partial(self._on_cadence_preset, preset),
|
|
3522
|
+
)
|
|
3523
|
+
it.state = 1 if round(cadence) == preset else 0
|
|
3524
|
+
cadence_menu.add(it)
|
|
3525
|
+
items.append(cadence_menu)
|
|
3526
|
+
|
|
3460
3527
|
items.append(rumps.separator)
|
|
3461
3528
|
items.append(rumps.MenuItem("Open dashboard", callback=self._open_dashboard))
|
|
3462
3529
|
# The one entry point for overall feedback (the review card no longer
|
|
@@ -3595,6 +3662,18 @@ class S4LMenuBar(rumps.App):
|
|
|
3595
3662
|
)
|
|
3596
3663
|
)
|
|
3597
3664
|
)
|
|
3665
|
+
else:
|
|
3666
|
+
# Reveal-cadence hold: the backlog exists but the pop-up is being
|
|
3667
|
+
# paced. Same courtesy line as the snooze; "Review N pending
|
|
3668
|
+
# drafts" above shows them right now regardless.
|
|
3669
|
+
hold_until = self._reveal_hold_until(pending_count)
|
|
3670
|
+
if hold_until:
|
|
3671
|
+
items.append(
|
|
3672
|
+
self._label(
|
|
3673
|
+
"Next cards around "
|
|
3674
|
+
+ time.strftime("%H:%M", time.localtime(hold_until))
|
|
3675
|
+
)
|
|
3676
|
+
)
|
|
3598
3677
|
# No bulk-discard item here anymore: "Discard all…" lives in the
|
|
3599
3678
|
# review card's title bar (2026-07-10). Reaching it with no card open
|
|
3600
3679
|
# is "Review N pending drafts" -> Discard all, two clicks.
|
package/mcp/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m13v/s4l-mcp",
|
|
3
|
-
"version": "1.7.4-rc.
|
|
3
|
+
"version": "1.7.4-rc.30",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Desktop MCP client for social-autoposter (X/Twitter rail): manual draft/review/approve loop, autopilot control, and stats. Thin wrapper over the existing pipeline scripts.",
|
|
6
6
|
"license": "MIT",
|
package/package.json
CHANGED