@m13v/s4l 1.6.202 → 1.6.203-rc.2
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/index.js +88 -46
- package/mcp/dist/panel.html +25 -23
- package/mcp/dist/product-link.html +1 -1
- package/mcp/dist/setup.js +1 -1
- package/mcp/dist/telemetry.js +1 -1
- package/mcp/dist/version.json +2 -2
- package/mcp/manifest.json +1 -1
- package/mcp/menubar/dashboard_server.py +70 -0
- package/mcp/menubar/s4l_card.py +121 -165
- package/mcp/menubar/s4l_menubar.py +30 -20
- package/mcp/menubar/s4l_state.py +6 -11
- package/mcp/package.json +1 -1
- package/package.json +1 -1
- package/scripts/build_persona.py +2 -2
- package/scripts/dev_dashboard_server.py +18 -0
- package/scripts/engage_github.py +25 -0
- package/scripts/engage_reddit.py +14 -1
- package/scripts/feedback_digest.py +7 -5
- package/scripts/pick_search_topic.py +16 -11
- package/scripts/post_github.py +9 -0
- package/scripts/post_reddit.py +11 -0
- package/skill/run-linkedin.sh +12 -0
- package/skill/run-twitter-cycle.sh +15 -12
package/mcp/dist/index.js
CHANGED
|
@@ -1301,7 +1301,7 @@ tool("engagement_mode", {
|
|
|
1301
1301
|
.optional()
|
|
1302
1302
|
.describe("The RAW voice-memo transcript from the onboarding dictation interview, VERBATIM (do NOT " +
|
|
1303
1303
|
"paraphrase or summarize). Persisted to the persona_corpus.txt sidecar (never config.json), " +
|
|
1304
|
-
"capped ~
|
|
1304
|
+
"capped ~100000 chars. This is the grounding pool the drafter quotes real specifics from " +
|
|
1305
1305
|
"(actual projects, numbers, opinions, phrasing), so keep it dense and first-hand."),
|
|
1306
1306
|
voice: z
|
|
1307
1307
|
.any()
|
|
@@ -1794,70 +1794,52 @@ tool("project_config", {
|
|
|
1794
1794
|
}
|
|
1795
1795
|
// Status / discovery mode: no project name supplied, or explicitly asked.
|
|
1796
1796
|
if (args.status === true || !args.name) {
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
//
|
|
1800
|
-
//
|
|
1801
|
-
//
|
|
1802
|
-
//
|
|
1803
|
-
const
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
const
|
|
1808
|
-
const configured = projects.some((p) => p.ready);
|
|
1809
|
-
if (rtReady)
|
|
1810
|
-
completeOnboardingMilestone("runtime_ready");
|
|
1811
|
-
if (x.connected) {
|
|
1812
|
-
completeOnboardingMilestone("x_connected", { state: x.state || "connected" });
|
|
1813
|
-
}
|
|
1814
|
-
if (configured) {
|
|
1815
|
-
completeOnboardingMilestone("project_ready", {
|
|
1816
|
-
missing_count: 0,
|
|
1817
|
-
});
|
|
1818
|
-
}
|
|
1819
|
-
// mode_chosen completes when the user explicitly picked a mode (mode.json
|
|
1820
|
-
// exists) OR this is a legacy install already past setup (a ready product),
|
|
1821
|
-
// so adding this step never regresses an already-onboarded box.
|
|
1822
|
-
if (modeChosen() || configured) {
|
|
1823
|
-
completeOnboardingMilestone("mode_chosen", {
|
|
1824
|
-
source: modeChosen() ? "chosen" : "backfilled_legacy",
|
|
1825
|
-
});
|
|
1826
|
-
}
|
|
1797
|
+
// ONE compute path: buildSnapshot -> scripts/snapshot.py, the same source
|
|
1798
|
+
// the menu bar and browser dashboard read. This branch used to recompute
|
|
1799
|
+
// projects/X/version inline (listManagedProjectStatus + xStatus), which
|
|
1800
|
+
// excluded the persona project and carried no setup_complete — so the
|
|
1801
|
+
// panel's refresh() disagreed with the dashboard tool and the menu bar on
|
|
1802
|
+
// persona-only setups. Do not reintroduce a parallel compute here.
|
|
1803
|
+
const snap = await buildSnapshot();
|
|
1804
|
+
const projects = Array.isArray(snap.projects) ? snap.projects : [];
|
|
1805
|
+
const rtReady = !!snap.runtime_ready;
|
|
1806
|
+
const xConnected = !!snap.x_connected;
|
|
1807
|
+
const configured = (snap.projects_ready || 0) > 0;
|
|
1827
1808
|
return jsonContent({
|
|
1828
1809
|
configured,
|
|
1829
1810
|
projects,
|
|
1830
1811
|
runtime_ready: rtReady,
|
|
1831
|
-
x_connected:
|
|
1832
|
-
x_state:
|
|
1833
|
-
x_handle:
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1812
|
+
x_connected: xConnected,
|
|
1813
|
+
x_state: snap.x_state,
|
|
1814
|
+
x_handle: snap.x_handle ?? null,
|
|
1815
|
+
setup_complete: !!snap.setup_complete,
|
|
1816
|
+
mcp_version: snap.version,
|
|
1817
|
+
latest_version: snap.latest_version,
|
|
1818
|
+
update_available: !!snap.update_available,
|
|
1819
|
+
mode: snap.mode,
|
|
1820
|
+
flags: snap.flags,
|
|
1821
|
+
update_hint: snap.update_available
|
|
1822
|
+
? `A newer version (${snap.latest_version}) is available — you're on ${snap.version}. ` +
|
|
1841
1823
|
`Tell the user and offer to run the \`runtime\` tool with action:'update' ` +
|
|
1842
1824
|
`(or \`npx social-autoposter@latest update\`).`
|
|
1843
1825
|
: undefined,
|
|
1844
1826
|
required_fields: REQUIRED_FIELDS,
|
|
1845
1827
|
recommended_fields: RECOMMENDED_FIELDS,
|
|
1846
1828
|
config_path: configPath(),
|
|
1847
|
-
ready_for_verification:
|
|
1848
|
-
onboarding:
|
|
1829
|
+
ready_for_verification: !!snap.setup_complete,
|
|
1830
|
+
onboarding: snap.onboarding,
|
|
1849
1831
|
next_step: !rtReady
|
|
1850
1832
|
? "Runtime is not ready yet. It provisions automatically on boot — poll runtime action:'status' until ready (only call runtime action:'install' to retry if status shows the boot provision failed or stalled), then continue setup automatically."
|
|
1851
1833
|
: projects.length === 0
|
|
1852
1834
|
? "No projects yet. Discover the product from conversation context and the connected X profile; research its website, infer a conservative complete project, and call project_config. Ask only if no product can be identified." +
|
|
1853
|
-
(
|
|
1835
|
+
(xConnected ? "" : " X is not connected yet either — detect_x_sources, warn about keychain prompts, then run connect_x with confirm:true without a separate permission turn.")
|
|
1854
1836
|
: projects.every((p) => p.ready)
|
|
1855
|
-
? (
|
|
1837
|
+
? (xConnected
|
|
1856
1838
|
? "All configured projects are ready and X is connected. SCHEDULE THE AUTOPILOT: (1) call queue_setup and create each returned task with create_scheduled_task (prompt verbatim; 'already exists' is fine); (2) the autopilot then runs on its own (launchd kicker + queue worker). Call the `dashboard` tool to confirm the schedule is firing (schedule_state 'ok') — that is the terminal step; do NOT wait for or verify a draft card. Do NOT pause to ask the user to review drafts."
|
|
1857
1839
|
: "All configured projects are ready, but X is NOT connected — posting needs a logged-in " +
|
|
1858
1840
|
"x.com session. Detect sources and run project_config action:'connect_x', confirm:true; do not ask whether to proceed.")
|
|
1859
1841
|
: "Some projects are missing required fields (see each project's missing_required). Derive them from config, context, profile_scan, and website research, then call project_config again. Ask only if a required field is genuinely unknowable." +
|
|
1860
|
-
(
|
|
1842
|
+
(xConnected ? "" : " X is also not connected yet; detect sources and run connect_x with confirm:true."),
|
|
1861
1843
|
});
|
|
1862
1844
|
}
|
|
1863
1845
|
// Apply mode (incremental): merge whatever fields were supplied onto the
|
|
@@ -2980,6 +2962,12 @@ function kickerEnv() {
|
|
|
2980
2962
|
S4L_STATE_DIR: sapsStateDir(),
|
|
2981
2963
|
TWITTER_TAIL_LINK_RATE: "0",
|
|
2982
2964
|
TWITTER_PAGE_GEN_RATE: "0",
|
|
2965
|
+
// Rolling virality bar percentile for the draft lane (2026-07-03): the
|
|
2966
|
+
// cycle script now applies the bar to DRAFT_ONLY too, so below-bar picks
|
|
2967
|
+
// never become review cards. p0.90 (not the script's 0.97 default) keeps
|
|
2968
|
+
// card volume useful while cutting bottom-of-pool drafts; cold start
|
|
2969
|
+
// (sample_count < 200) still shows brand-new installs every draft.
|
|
2970
|
+
S4L_TWITTER_VIRALITY_PCTILE: "0.90",
|
|
2983
2971
|
};
|
|
2984
2972
|
}
|
|
2985
2973
|
async function ensureQueueKickerInstalled() {
|
|
@@ -3428,6 +3416,29 @@ async function buildSnapshot() {
|
|
|
3428
3416
|
completeOnboardingMilestone("project_ready", { missing_count: 0 });
|
|
3429
3417
|
if (snap.schedule_state === "ok")
|
|
3430
3418
|
completeOnboardingMilestone("tasks_scheduled");
|
|
3419
|
+
// mode_chosen completes when the user explicitly picked a mode (mode.json
|
|
3420
|
+
// exists) OR this is a legacy install already past setup (a ready project),
|
|
3421
|
+
// so adding this step never regresses an already-onboarded box. (Moved here
|
|
3422
|
+
// from project_config's status branch so ALL milestone truth lives in one
|
|
3423
|
+
// place next to the snapshot.)
|
|
3424
|
+
if (modeChosen() || (snap.projects_ready || 0) > 0) {
|
|
3425
|
+
completeOnboardingMilestone("mode_chosen", {
|
|
3426
|
+
source: modeChosen() ? "chosen" : "backfilled_legacy",
|
|
3427
|
+
});
|
|
3428
|
+
}
|
|
3429
|
+
// profile_scanned has no file signal of its own, but a provisioned persona
|
|
3430
|
+
// project can only come from the scan+dictation flow — treat its presence as
|
|
3431
|
+
// the completed scan so old installs can't stick at "profile pending".
|
|
3432
|
+
if ((Array.isArray(snap.projects) ? snap.projects : []).some((p) => p?.persona)) {
|
|
3433
|
+
completeOnboardingMilestone("profile_scanned", { backfill: true });
|
|
3434
|
+
}
|
|
3435
|
+
// topics_seeded has no cheap live signal (it's a DB fact, not a file fact), so
|
|
3436
|
+
// installs that finished setup before the persona-path completion landed stay
|
|
3437
|
+
// stuck at 7/8 forever. Heal by re-running the idempotent seed for the ready
|
|
3438
|
+
// project(s) and completing the milestone on success. Fire-and-forget: the
|
|
3439
|
+
// ledger flip shows on the next snapshot.
|
|
3440
|
+
if ((snap.projects_ready || 0) > 0)
|
|
3441
|
+
void healTopicsSeeded(snap);
|
|
3431
3442
|
// Persist this snapshot so the menu bar can answer "set up?" the SAME way when
|
|
3432
3443
|
// the loopback server is unreachable (Claude Desktop closed or mid-restart)
|
|
3433
3444
|
// instead of falling back to a divergent local rule. Refreshed on every
|
|
@@ -3436,6 +3447,37 @@ async function buildSnapshot() {
|
|
|
3436
3447
|
persistStatusSummary(snap);
|
|
3437
3448
|
return snap;
|
|
3438
3449
|
}
|
|
3450
|
+
// One attempt per process: a failed seed (offline DB) retries on the next MCP
|
|
3451
|
+
// launch, not on every dashboard poll.
|
|
3452
|
+
let topicsSeedHealAttempted = false;
|
|
3453
|
+
async function healTopicsSeeded(snap) {
|
|
3454
|
+
if (topicsSeedHealAttempted)
|
|
3455
|
+
return;
|
|
3456
|
+
topicsSeedHealAttempted = true;
|
|
3457
|
+
try {
|
|
3458
|
+
const ledger = onboardingLedger();
|
|
3459
|
+
if (ledger?.milestones?.topics_seeded?.status === "complete")
|
|
3460
|
+
return;
|
|
3461
|
+
const ready = (Array.isArray(snap.projects) ? snap.projects : []).filter((p) => p && p.ready && typeof p.name === "string");
|
|
3462
|
+
for (const p of ready) {
|
|
3463
|
+
const seed = await runPython("scripts/seed_search_topics.py", ["--project", p.name], {
|
|
3464
|
+
timeoutMs: 60_000,
|
|
3465
|
+
});
|
|
3466
|
+
if (seed.code === 0) {
|
|
3467
|
+
const m = /planned=(\d+)\s+inserted=(\d+)\s+updated=(\d+)/.exec(seed.stdout);
|
|
3468
|
+
completeOnboardingMilestone("topics_seeded", {
|
|
3469
|
+
project: p.name,
|
|
3470
|
+
topic_count: m ? Number(m[1]) : 0,
|
|
3471
|
+
backfill: true,
|
|
3472
|
+
});
|
|
3473
|
+
return;
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
catch (e) {
|
|
3478
|
+
console.error("[snapshot] topics_seeded heal failed:", e?.message || e);
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3439
3481
|
// ---- dashboard localhost fallback -----------------------------------------
|
|
3440
3482
|
// When the connected host doesn't support MCP Apps UI (Claude Code / Cowork
|
|
3441
3483
|
// today), serve the SAME dist/panel.html from a loopback HTTP server. The page
|