@m13v/s4l 1.7.1-rc.2 → 1.7.1-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 (45) hide show
  1. package/bin/cli.js +23 -0
  2. package/mcp/dist/index.js +332 -24
  3. package/mcp/dist/onboarding.js +3 -10
  4. package/mcp/dist/panel.html +42 -31
  5. package/mcp/dist/product-link.html +1 -1
  6. package/mcp/dist/runtime.js +101 -1
  7. package/mcp/dist/telemetry.js +80 -3
  8. package/mcp/dist/version.json +2 -2
  9. package/mcp/manifest.json +9 -1
  10. package/mcp/menubar/dashboard_server.py +12 -0
  11. package/mcp/menubar/s4l_card.py +472 -96
  12. package/mcp/menubar/s4l_menubar.py +269 -26
  13. package/mcp/menubar/s4l_state.py +169 -23
  14. package/mcp/package.json +1 -1
  15. package/mcp/shared/doctor.cjs +8 -0
  16. package/mcp/shared/onboarding-ledger.cjs +1 -0
  17. package/package.json +3 -1
  18. package/scripts/active_users.py +3 -6
  19. package/scripts/autopilot_stall_watch.py +200 -16
  20. package/scripts/claude_job.py +6 -0
  21. package/scripts/feedback_digest.py +34 -6
  22. package/scripts/get-latest-staging-mcpb.sh +50 -0
  23. package/scripts/harness_overlay.py +45 -0
  24. package/scripts/identity.py +26 -0
  25. package/scripts/learned_preferences.py +241 -32
  26. package/scripts/link_tail.py +167 -91
  27. package/scripts/mark_event.py +128 -0
  28. package/scripts/memory_snapshot.py +62 -0
  29. package/scripts/pick_twitter_thread_target.py +1 -1
  30. package/scripts/release-mcpb.sh +48 -0
  31. package/scripts/reset-test-machine.sh +85 -3
  32. package/scripts/s4l_mode.py +7 -5
  33. package/scripts/scheduled_tasks_snapshot.py +107 -0
  34. package/scripts/send_gmail_report.py +76 -0
  35. package/scripts/sentry_digest.py +81 -189
  36. package/scripts/sentry_init.py +43 -15
  37. package/scripts/setup_twitter_auth.py +76 -38
  38. package/scripts/snapshot.py +20 -0
  39. package/scripts/twitter_post_plan.py +39 -13
  40. package/skill/dm-outreach-twitter.sh +19 -0
  41. package/skill/engage-twitter.sh +27 -0
  42. package/skill/run-draft-and-publish.sh +13 -12
  43. package/skill/run-linkedin.sh +9 -1
  44. package/skill/run-twitter-cycle.sh +199 -27
  45. package/skill/sentry-digest.sh +125 -12
@@ -1,19 +1,132 @@
1
1
  #!/usr/bin/env bash
2
2
  # sentry-digest.sh — critical (level:error/fatal) Sentry issue digest for the
3
- # s4l Sentry project. Replaces the raw per-issue Sentry alert email; only
4
- # emails when something is NEW or GROWING. Idempotent via
5
- # scripts/state/sentry_digest_ledger.json. Wired by
6
- # launchd/com.m13v.s4l-sentry-digest.plist (every 4 hours).
3
+ # s4l Sentry project. Replaces the raw per-issue Sentry alert email.
4
+ #
5
+ # Two-step design (mirrors ~/fazm/inbox/skill/check-query-insights.sh):
6
+ # 1. scripts/sentry_digest.py pulls issues + diffs against the ledger
7
+ # (mechanical, no LLM). If nothing is NEW or GROWING, exit here, no
8
+ # Claude spawn, no email, no ledger write (ledger is only ever written
9
+ # by the investigating Claude session, see SENTRY-DIGEST-SKILL.md).
10
+ # 2. If something changed, spawn Claude to actually investigate (read the
11
+ # crashing code, check git log, judge actionability) and send ONE
12
+ # human-readable email, not a raw data dump.
13
+ #
14
+ # Wired by launchd/com.m13v.s4l-sentry-digest.plist (every 4 hours).
7
15
 
8
- REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
16
+ set -euo pipefail
17
+
18
+ # claude (claude-account-rotator wrapper) and gtimeout must be resolvable
19
+ # regardless of how this script is invoked; launchd's plist PATH covers this,
20
+ # but keep it explicit so a manual run also works.
21
+ export PATH="$HOME/.nvm/versions/node/v20.19.4/bin:/opt/homebrew/bin:/usr/local/bin:$HOME/claude-account-rotator/bin:$PATH"
22
+
23
+ source "$(dirname "$0")/lock.sh"
24
+ acquire_lock "sentry-digest" 1800
25
+
26
+ REPO_DIR="$HOME/social-autoposter"
27
+ SCRIPTS_DIR="$REPO_DIR/scripts"
9
28
  LOG_DIR="$REPO_DIR/skill/logs"
29
+ LEDGER_PATH="$REPO_DIR/scripts/state/sentry_digest_ledger.json"
30
+ CLAUDE_BIN="${CLAUDE_BIN:-$HOME/claude-account-rotator/bin/claude}"
31
+ PYTHON_BIN="${PYTHON_BIN:-/opt/homebrew/bin/python3.11}"
32
+ [ -x "$PYTHON_BIN" ] || PYTHON_BIN="/usr/bin/python3"
10
33
  mkdir -p "$LOG_DIR"
11
- LOG_FILE="$LOG_DIR/sentry-digest-$(date +%Y%m%d).log"
12
34
 
13
- cd "$REPO_DIR" || exit 1
35
+ TS=$(date +%Y-%m-%d_%H%M%S)
36
+ LOG_FILE="$LOG_DIR/sentry-digest-$TS.log"
37
+ SCAN_FILE="$LOG_DIR/sentry-digest-scan-$TS.json"
38
+ OUTCOME_FILE="$LOG_DIR/outcome-sentry-digest-$TS.json"
39
+
40
+ log() { echo "[$(date +%H:%M:%S)] $*" | tee -a "$LOG_FILE"; }
41
+
42
+ cd "$REPO_DIR"
43
+
44
+ log "=== Sentry digest scan: $(date) ==="
45
+ "$PYTHON_BIN" "$SCRIPTS_DIR/sentry_digest.py" --out "$SCAN_FILE" 2>&1 | tee -a "$LOG_FILE"
46
+ SCAN_EXIT=${PIPESTATUS[0]}
47
+ if [ "$SCAN_EXIT" -ne 0 ]; then
48
+ log "ERROR: sentry_digest.py scan failed (exit $SCAN_EXIT)"
49
+ exit 1
50
+ fi
51
+
52
+ FIRST_RUN=$("$PYTHON_BIN" -c "import json; print(json.load(open('$SCAN_FILE'))['firstRun'])")
53
+ NEW_COUNT=$("$PYTHON_BIN" -c "import json; print(json.load(open('$SCAN_FILE'))['newCount'])")
54
+ GROWING_COUNT=$("$PYTHON_BIN" -c "import json; print(json.load(open('$SCAN_FILE'))['growingCount'])")
55
+
56
+ if [ "$FIRST_RUN" = "False" ] && [ "$NEW_COUNT" -eq 0 ] && [ "$GROWING_COUNT" -eq 0 ]; then
57
+ log "Nothing new or growing. Skipping Claude spawn and email."
58
+ find "$LOG_DIR" -name "sentry-digest-scan-*.json" -mtime +7 -delete 2>/dev/null || true
59
+ find "$LOG_DIR" -name "sentry-digest-*.log" -mtime +30 -delete 2>/dev/null || true
60
+ exit 0
61
+ fi
62
+
63
+ # Step 2: spawn Claude to investigate and email
64
+ PROMPT_FILE=$(mktemp)
65
+ cat > "$PROMPT_FILE" <<PROMPT_EOF
66
+ Read ~/social-autoposter/skill/SENTRY-DIGEST-SKILL.md for the full workflow.
67
+
68
+ ## Run metadata
69
+
70
+ - First run (ledger was empty): $FIRST_RUN
71
+ - New issues this scan: $NEW_COUNT
72
+ - Growing issues this scan: $GROWING_COUNT
73
+ - Sentry auth token: read it yourself with
74
+ security find-generic-password -s sentry-auth-token -w
75
+
76
+ ## Scan result (from sentry_digest.py)
77
+
78
+ Full result is on disk at:
79
+ $SCAN_FILE
80
+
81
+ ## Ledger path
82
+
83
+ $LEDGER_PATH
84
+
85
+ ## Outcome file
86
+
87
+ Write your JSON outcome to:
88
+ $OUTCOME_FILE
89
+
90
+ Now run the workflow.
91
+ PROMPT_EOF
92
+
93
+ log "Spawning Claude Code session for investigation..."
94
+ log " Scan file: $SCAN_FILE"
95
+ log " Outcome file: $OUTCOME_FILE"
96
+
97
+ CLAUDE_EXIT=0
98
+ gtimeout 1800 "$CLAUDE_BIN" \
99
+ -p "$(cat "$PROMPT_FILE")" \
100
+ --dangerously-skip-permissions \
101
+ 2>&1 | tee -a "$LOG_FILE" || CLAUDE_EXIT=$?
102
+
103
+ rm -f "$PROMPT_FILE"
104
+
105
+ if [ $CLAUDE_EXIT -eq 124 ]; then
106
+ log "ERROR: Claude Code timed out after 30 minutes"
107
+ elif [ $CLAUDE_EXIT -ne 0 ]; then
108
+ log "WARNING: Claude Code exited with code $CLAUDE_EXIT"
109
+ fi
110
+
111
+ log "--- Post-run validation ---"
112
+ if [ -f "$OUTCOME_FILE" ]; then
113
+ log "Outcome file found:"
114
+ cat "$OUTCOME_FILE" >> "$LOG_FILE"
115
+ REPORT_SENT=$("$PYTHON_BIN" -c "import json; d=json.load(open('$OUTCOME_FILE')); print(d.get('reportEmailSent', False))" 2>/dev/null || echo "False")
116
+ LEDGER_WRITTEN=$("$PYTHON_BIN" -c "import json; d=json.load(open('$OUTCOME_FILE')); print(d.get('ledgerWritten', False))" 2>/dev/null || echo "False")
117
+ SUMMARY=$("$PYTHON_BIN" -c "import json; d=json.load(open('$OUTCOME_FILE')); print(d.get('summary', 'No summary'))" 2>/dev/null || echo "No summary")
118
+ log " reportEmailSent: $REPORT_SENT"
119
+ log " ledgerWritten: $LEDGER_WRITTEN"
120
+ log " summary: $SUMMARY"
121
+ if [ "$LEDGER_WRITTEN" != "True" ]; then
122
+ log "WARNING: agent did not confirm writing the ledger; next run may re-flag the same issues"
123
+ fi
124
+ else
125
+ log "WARNING: No outcome file produced. Agent may not have completed the workflow."
126
+ fi
127
+
128
+ log "=== Done Sentry digest run (claude_exit=$CLAUDE_EXIT) ==="
14
129
 
15
- {
16
- echo "=== $(date -u +%Y-%m-%dT%H:%M:%SZ) sentry-digest run ==="
17
- /usr/bin/python3 scripts/sentry_digest.py
18
- echo
19
- } >> "$LOG_FILE" 2>&1
130
+ find "$LOG_DIR" -name "sentry-digest-*.log" -mtime +30 -delete 2>/dev/null || true
131
+ find "$LOG_DIR" -name "sentry-digest-scan-*.json" -mtime +7 -delete 2>/dev/null || true
132
+ find "$LOG_DIR" -name "outcome-sentry-digest-*.json" -mtime +30 -delete 2>/dev/null || true