opencode-skills-collection 3.0.28 → 3.0.29
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/bundled-skills/.antigravity-install-manifest.json +11 -1
- package/bundled-skills/bumblebee/SKILL.md +186 -0
- package/bundled-skills/bumblebee/scripts/render_report.py +362 -0
- package/bundled-skills/complexity-cuts/SKILL.md +254 -0
- package/bundled-skills/decision-navigator/SKILL.md +238 -0
- package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
- package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
- package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
- package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
- package/bundled-skills/docs/users/bundles.md +1 -1
- package/bundled-skills/docs/users/claude-code-skills.md +1 -1
- package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
- package/bundled-skills/docs/users/getting-started.md +1 -1
- package/bundled-skills/docs/users/kiro-integration.md +1 -1
- package/bundled-skills/docs/users/usage.md +4 -4
- package/bundled-skills/docs/users/visual-guide.md +4 -4
- package/bundled-skills/invariant-guard/SKILL.md +307 -0
- package/bundled-skills/lemmaly/SKILL.md +236 -0
- package/bundled-skills/mathguard/SKILL.md +269 -0
- package/bundled-skills/sendblue/sendblue-api/SKILL.md +194 -0
- package/bundled-skills/sendblue/sendblue-cli/SKILL.md +145 -0
- package/bundled-skills/sendblue/sendblue-notify/SKILL.md +173 -0
- package/bundled-skills/sendblue/textme/SKILL.md +232 -0
- package/package.json +1 -1
- package/skills_index.json +220 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sendblue-notify
|
|
3
|
+
description: "Text the user's phone when a long-running task, agent turn, or scheduled job finishes — via @sendblue/cli for outbound, optionally wired to a Claude Code Stop hook for automatic fire."
|
|
4
|
+
category: automation
|
|
5
|
+
risk: safe
|
|
6
|
+
source: community
|
|
7
|
+
source_type: official
|
|
8
|
+
date_added: "2026-05-22"
|
|
9
|
+
author: AnthonyFirth
|
|
10
|
+
tags: [sendblue, imessage, sms, notifications, hooks, claude-code, automation]
|
|
11
|
+
tools: [claude, cursor, gemini]
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Sendblue Notify
|
|
15
|
+
|
|
16
|
+
## Overview
|
|
17
|
+
|
|
18
|
+
Outbound, fire-and-forget notifications from a local Claude Code session, script, or scheduled job to the user's phone via Sendblue. This is the "walk away from the terminal" pattern: kick off something long, get an iMessage when it lands. This skill owns **when to notify and what to say**. Actual sending goes through [[sendblue-cli]]. Hook wiring (so notifications fire automatically) goes through [[update-config]].
|
|
19
|
+
|
|
20
|
+
## When to Use This Skill
|
|
21
|
+
|
|
22
|
+
- Use when the user says "text me when X is done", "ping my phone", "notify me on completion", "let me know when the build/deploy/migration finishes", or "send me an iMessage when…".
|
|
23
|
+
- Use when the user asks to wire a hook that texts on agent stop, `/loop` iteration, or `/schedule` completion.
|
|
24
|
+
- Use when an agent turn is genuinely long-running and the user has gone heads-down on something else.
|
|
25
|
+
- Do **not** use for short, interactive tasks where the user is watching the terminal — the notify is noise.
|
|
26
|
+
|
|
27
|
+
## Prerequisites
|
|
28
|
+
|
|
29
|
+
The CLI must be installed and authenticated:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx @sendblue/cli whoami # confirms creds
|
|
33
|
+
# or, if first run:
|
|
34
|
+
npx @sendblue/cli setup
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The user's phone number must be a verified contact on the account. On the free plan, the contact has to text the Sendblue number once before outbound sends work — confirm with `sendblue contacts` before relying on notify in an unattended workflow.
|
|
38
|
+
|
|
39
|
+
Cache the destination number once per project rather than re-asking. A `NOTIFY_NUMBER` env var or a one-line `.notify-number` file is fine; defer storage strategy to whatever the surrounding project already does.
|
|
40
|
+
|
|
41
|
+
## How It Works
|
|
42
|
+
|
|
43
|
+
### Step 1: Decide whether notify is appropriate
|
|
44
|
+
|
|
45
|
+
Notify is for **long, unattended work** — not chatter. Good triggers:
|
|
46
|
+
|
|
47
|
+
- Agent turns over ~2 minutes (build, large refactor, migration, dataset crunch).
|
|
48
|
+
- `/loop` and `/schedule` jobs that produce a discrete result.
|
|
49
|
+
- CI / deploy completion when watched from the terminal.
|
|
50
|
+
- Multi-step playbooks where the user has gone heads-down on something else.
|
|
51
|
+
|
|
52
|
+
Bad triggers (do not silently wire these):
|
|
53
|
+
|
|
54
|
+
- Every `Stop` event, regardless of duration — produces spam, trains the user to ignore.
|
|
55
|
+
- Read-only or sub-second commands.
|
|
56
|
+
- Anything inside a tight loop.
|
|
57
|
+
|
|
58
|
+
If the user asks for "notify me when done" on a short task, do the obvious one-shot inline send (Example 1) and **do not** install a global hook.
|
|
59
|
+
|
|
60
|
+
### Step 2: Pick a delivery pattern
|
|
61
|
+
|
|
62
|
+
- **One-shot inline send** — default for a single ad-hoc task. No config changes.
|
|
63
|
+
- **`Stop` hook** — opt-in, project-scoped, for sessions the user explicitly wants on automatic notify. Always gate by duration.
|
|
64
|
+
- **End-of-`/loop` or `/schedule` ping** — append the send to the routine's body.
|
|
65
|
+
|
|
66
|
+
### Step 3: Compose the notification copy
|
|
67
|
+
|
|
68
|
+
- **One line, under ~140 chars** — fits in the lock-screen preview.
|
|
69
|
+
- **Lead with outcome** — ✅/❌, "done", "failed", "needs review".
|
|
70
|
+
- **Include something actionable** — branch name, error tail, PR number, duration.
|
|
71
|
+
- **No emojis the user didn't ask for** beyond a single status glyph.
|
|
72
|
+
- **No agent self-narration** ("I have completed the task as requested" — just say what happened).
|
|
73
|
+
|
|
74
|
+
## Examples
|
|
75
|
+
|
|
76
|
+
### Example 1: One-shot inline send
|
|
77
|
+
|
|
78
|
+
For a single task, append the send to the command. This is the default — no config changes, no surprise behavior later.
|
|
79
|
+
|
|
80
|
+
Branch on the task's exit status with an `if`/`else`. Do **not** use a `task && send-success || send-failure` chain: if the task succeeds but the success-send itself returns non-zero, the `||` fires the failure message — so the user sees ❌ even though the task completed. The `if`/`else` keeps the outcome tied solely to the task.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
if long_running_thing; then
|
|
84
|
+
npx @sendblue/cli send +15551234567 "✅ done: $(date +%H:%M)"
|
|
85
|
+
else
|
|
86
|
+
npx @sendblue/cli send +15551234567 "❌ failed: $(date +%H:%M)"
|
|
87
|
+
fi
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Or, when the result is interesting, include a one-line summary:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
RESULT=$(run-migration 2>&1 | tail -1)
|
|
94
|
+
npx @sendblue/cli send +15551234567 "migration done — $RESULT"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Example 2: Claude Code `Stop` hook (opt-in, scoped)
|
|
98
|
+
|
|
99
|
+
Register a `Stop` hook in `.claude/settings.json` (project-scoped) — never in global settings unless asked. Defer the actual file edit to [[update-config]]. The hook command itself should:
|
|
100
|
+
|
|
101
|
+
1. Run cheaply (it fires on *every* `Stop`).
|
|
102
|
+
2. Gate on duration — skip sends for turns under a threshold (e.g. 90s).
|
|
103
|
+
3. Never fail the parent — pipe to `|| true` so a notify error doesn't surface as a hook failure.
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
[ "$CLAUDE_TURN_DURATION_SECONDS" -ge 90 ] && \
|
|
107
|
+
npx @sendblue/cli send "$NOTIFY_NUMBER" "turn done in ${CLAUDE_TURN_DURATION_SECONDS}s" || true
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
(Adjust the env var names to whatever the hook contract actually provides — verify against the current Claude Code hooks reference before writing the config; the harness owns those names, not this skill.)
|
|
111
|
+
|
|
112
|
+
Show the proposed hook config to the user and get confirmation before invoking [[update-config]]. Automated outbound messages are a footgun if the threshold is wrong.
|
|
113
|
+
|
|
114
|
+
### Example 3: End-of-`/loop` or `/schedule` ping
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
/loop 10m "check deploy; npx @sendblue/cli send +15551234567 \"deploy: \$(deploy-status)\""
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
For `/schedule`, the routine itself can shell out at the end. Same copy rules apply.
|
|
121
|
+
|
|
122
|
+
## Composing with textme
|
|
123
|
+
|
|
124
|
+
If the user has `@textme` installed (njerschow/textme — daemon that lets you *text Claude* from your phone), notify is still useful and not redundant. They run in opposite directions:
|
|
125
|
+
|
|
126
|
+
- **textme**: phone → Claude (user initiates from the phone).
|
|
127
|
+
- **sendblue-notify**: local Claude → phone (Claude initiates from a local session).
|
|
128
|
+
|
|
129
|
+
You can install both: textme on a server for inbound, notify as a local `Stop`-hook for outbound. Different problems, same Sendblue account.
|
|
130
|
+
|
|
131
|
+
## Best Practices
|
|
132
|
+
|
|
133
|
+
- ✅ **Default to one-shot inline sends** for single tasks. Only escalate to a hook when the user asks for automatic notify.
|
|
134
|
+
- ✅ **Gate hooks by duration.** A 90s threshold is a sensible starting default.
|
|
135
|
+
- ✅ **Show the hook config before installing it** and get explicit user confirmation.
|
|
136
|
+
- ✅ **Store the destination number per-user** (env var or gitignored file), not in committed config.
|
|
137
|
+
- ❌ **Don't install global hooks** unless the user explicitly asks. Project-scoped is the default.
|
|
138
|
+
- ❌ **Don't let a failed notify fail the parent.** Trail with `|| true`.
|
|
139
|
+
|
|
140
|
+
## Limitations
|
|
141
|
+
|
|
142
|
+
- Notify is outbound-only. For "text Claude from the phone" use the `@textme` skill instead.
|
|
143
|
+
- On the free Sendblue plan, the destination phone must have texted the Sendblue number at least once before outbound succeeds. Verify with `sendblue contacts` before relying on notify in an unattended workflow.
|
|
144
|
+
- This skill does not own credentials, account setup, or the hook config file format. Those belong to [[sendblue-cli]] and [[update-config]] respectively.
|
|
145
|
+
|
|
146
|
+
## Security & Safety Notes
|
|
147
|
+
|
|
148
|
+
- **Lock-screen previews leak.** Anyone holding the phone can read notification copy. Do not embed secrets, customer data, full error stacks, or auth tokens. Link to a log, dashboard, or PR instead.
|
|
149
|
+
- **Automated outbound is a footgun.** A misconfigured `Stop` hook can fire dozens of messages a minute. Always gate by duration and prove the threshold in a dry run before committing.
|
|
150
|
+
- **Per-user numbers.** The destination phone number is a personal identifier — keep it in user-local config (env var, gitignored file), not in committed repo files or CI logs.
|
|
151
|
+
- **Free-plan verification is silent.** If the destination contact hasn't texted in once, sends return an API error but the user just sees "no text arrived". Confirm verification before wiring an unattended hook.
|
|
152
|
+
|
|
153
|
+
## Common Pitfalls
|
|
154
|
+
|
|
155
|
+
- **Spam from over-eager `Stop` hooks.** Always gate by duration. A user who gets pinged every 4 seconds will rip the hook out within an hour.
|
|
156
|
+
- **Hardcoding the destination number in committed files.** Use an env var or gitignored file; the number is per-user, not per-repo.
|
|
157
|
+
- **Letting a failed notify fail the parent.** Always trail with `|| true` in hooks; surface the failure in logs, not by aborting the agent turn.
|
|
158
|
+
- **Free-plan contact gotcha.** If the destination contact hasn't texted in once on a free-plan account, the send silently fails for the user's purposes. Verify with `sendblue contacts` before wiring an unattended hook. Or `sendblue upgrade` to the AI Agent plan.
|
|
159
|
+
- **PII in notification copy.** Lock-screen previews are visible to anyone holding the phone. Don't embed secrets, customer data, or full error stacks — link to a log or PR instead.
|
|
160
|
+
- **Burying the outcome.** "Task complete. Here is a summary of what I did…" wastes the preview line. Lead with ✅/❌ and the verb.
|
|
161
|
+
|
|
162
|
+
## Related Skills
|
|
163
|
+
|
|
164
|
+
- `@sendblue-cli` — Owns the actual send mechanism. This skill calls into it.
|
|
165
|
+
- `@sendblue-api` — HTTP alternative for app code where notify lives inside a long-running service.
|
|
166
|
+
- `@update-config` — Wires the `Stop` hook into `.claude/settings.json`. This skill owns the *what* and *when*; update-config owns the *where*.
|
|
167
|
+
- `@textme` — Inbound counterpart (phone → Claude). Composes well with notify.
|
|
168
|
+
|
|
169
|
+
## Links
|
|
170
|
+
|
|
171
|
+
- Underlying CLI: <https://github.com/sendblue-api/sendblue-cli>
|
|
172
|
+
- Sendblue: <https://sendblue.com>
|
|
173
|
+
- API docs: <https://docs.sendblue.com>
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: textme
|
|
3
|
+
description: "Text Claude from your phone — set up the njerschow/textme daemon so inbound iMessages drive a Claude Code session on your laptop, with voice notes, image input, code execution, and a phone-number whitelist."
|
|
4
|
+
category: automation
|
|
5
|
+
risk: critical
|
|
6
|
+
source: community
|
|
7
|
+
source_repo: njerschow/textme
|
|
8
|
+
source_type: community
|
|
9
|
+
date_added: "2026-05-26"
|
|
10
|
+
author: AnthonyFirth
|
|
11
|
+
tags: [textme, sendblue, imessage, sms, claude-code, daemon, remote-control, automation]
|
|
12
|
+
tools: [claude, cursor, gemini]
|
|
13
|
+
license: "MIT"
|
|
14
|
+
license_source: "https://github.com/njerschow/textme/blob/main/LICENSE"
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# TextMe
|
|
18
|
+
|
|
19
|
+
## Overview
|
|
20
|
+
|
|
21
|
+
[`njerschow/textme`](https://github.com/njerschow/textme) is a local daemon that bridges inbound iMessages (via [Sendblue](https://sendblue.com)) to a Claude Code session on the user's machine. Whitelisted phone numbers can text, send voice notes, send images, and drive Claude through filesystem operations, code execution, and `cd`-based directory navigation — turning the user's phone into a remote control for Claude on their laptop. This is the **inbound** counterpart to outbound notification patterns ([[sendblue-notify]]): textme is phone → Claude; sendblue-notify is Claude → phone.
|
|
22
|
+
|
|
23
|
+
## When to Use This Skill
|
|
24
|
+
|
|
25
|
+
- Use when the user says "text Claude", "text my laptop", "drive Claude from my phone", "I want to send iMessages to Claude", or "let me code from my phone".
|
|
26
|
+
- Use when the user is heads-down away from their desk and wants to kick off, supervise, or interrupt a Claude session via SMS/iMessage.
|
|
27
|
+
- Use when setting up a long-running headless workstation that the user wants to remote-control while travelling or away from the keyboard.
|
|
28
|
+
- Pair with [[sendblue-notify]] for bidirectional flow — outbound completion pings + inbound commands on the same Sendblue account.
|
|
29
|
+
- Do **not** use for outbound-only "text me when X finishes" patterns. That is [[sendblue-notify]] and does not need a daemon.
|
|
30
|
+
|
|
31
|
+
## Prerequisites
|
|
32
|
+
|
|
33
|
+
- macOS or Linux host that stays online (the daemon polls Sendblue continuously).
|
|
34
|
+
- Node.js 18+.
|
|
35
|
+
- An active Sendblue account with API credentials and a provisioned iMessage number — set up via [[sendblue-cli]] (`sendblue setup`, then `sendblue show-keys` to surface API key/secret).
|
|
36
|
+
- Claude Code installed and authenticated on the host (`npm install -g @anthropic-ai/claude-code`).
|
|
37
|
+
- Optional: an OpenAI API key for Whisper voice-note transcription.
|
|
38
|
+
|
|
39
|
+
## How It Works
|
|
40
|
+
|
|
41
|
+
### Step 1: Install the daemon
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git clone https://github.com/njerschow/textme.git
|
|
45
|
+
cd textme/daemon
|
|
46
|
+
npm install
|
|
47
|
+
npm run build
|
|
48
|
+
mkdir -p ~/.config/claude-imessage
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Step 2: Configure credentials and the whitelist
|
|
52
|
+
|
|
53
|
+
Create `~/.config/claude-imessage/config.json`:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"sendblue": {
|
|
58
|
+
"apiKey": "YOUR_SENDBLUE_API_KEY",
|
|
59
|
+
"apiSecret": "YOUR_SENDBLUE_API_SECRET",
|
|
60
|
+
"phoneNumber": "+1SENDBLUE_NUMBER"
|
|
61
|
+
},
|
|
62
|
+
"whitelist": ["+1YOUR_PHONE"],
|
|
63
|
+
"pollIntervalMs": 5000,
|
|
64
|
+
"conversationWindowSize": 20
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The `whitelist` is the **only** authorization gate between an inbound iMessage and code execution on the host. Treat it as a security boundary, not a UX preference. Add only phone numbers the user controls; never add a shared, work, or family number "just in case".
|
|
69
|
+
|
|
70
|
+
For voice transcription, optionally add to `.env` in the daemon directory:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
OPENAI_API_KEY=sk-...
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Step 3: Run the daemon
|
|
77
|
+
|
|
78
|
+
For a quick test run:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
cd textme/daemon
|
|
82
|
+
npm start
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
For persistent operation (recommended once the user has verified behavior):
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pm2 start dist/index.js --name textme
|
|
89
|
+
pm2 save
|
|
90
|
+
pm2 startup
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Or, on macOS, install the launchd service:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
./scripts/install-launchd.sh
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Step 4: Drive Claude from iMessage
|
|
100
|
+
|
|
101
|
+
Once the daemon is running, an iMessage from a whitelisted number to the Sendblue phone number reaches Claude. Built-in commands:
|
|
102
|
+
|
|
103
|
+
| Command | Effect |
|
|
104
|
+
|---|---|
|
|
105
|
+
| `?` | List available commands |
|
|
106
|
+
| `status` | Show current daemon status and working directory |
|
|
107
|
+
| `queue` | Show messages queued for processing |
|
|
108
|
+
| `history` | Recent message history |
|
|
109
|
+
| `home` | `cd` back to home directory |
|
|
110
|
+
| `reset` | Return home and clear conversation history |
|
|
111
|
+
| `cd /path` | Change working directory |
|
|
112
|
+
| `stop` | Cancel the current Claude task |
|
|
113
|
+
| `yes` / `no` | Approve or reject the pending action |
|
|
114
|
+
|
|
115
|
+
Anything else is treated as a Claude prompt and routed to the active session.
|
|
116
|
+
|
|
117
|
+
### Step 5: Verify before relying on it
|
|
118
|
+
|
|
119
|
+
Before using textme in unattended workflows, the user must:
|
|
120
|
+
|
|
121
|
+
1. Send `status` from the whitelisted phone — should get a directory + state reply.
|
|
122
|
+
2. Send a benign command (`pwd`, `ls`) and confirm output arrives.
|
|
123
|
+
3. Send something from a **non-whitelisted** number and confirm it is **ignored**, not echoed.
|
|
124
|
+
4. Pull the plug: kill the daemon and confirm messages stop being processed (no zombie process).
|
|
125
|
+
|
|
126
|
+
If any of these fail, do not enable launchd / pm2 auto-start.
|
|
127
|
+
|
|
128
|
+
## Examples
|
|
129
|
+
|
|
130
|
+
### Example 1: Initial setup walk-through
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# 1. Make sure sendblue CLI is set up and creds work
|
|
134
|
+
sendblue whoami
|
|
135
|
+
|
|
136
|
+
# 2. Grab Sendblue API key & secret (these are NOT the CLI's bearer token)
|
|
137
|
+
sendblue show-keys
|
|
138
|
+
|
|
139
|
+
# 3. Clone + build the daemon
|
|
140
|
+
git clone https://github.com/njerschow/textme.git
|
|
141
|
+
cd textme/daemon && npm install && npm run build
|
|
142
|
+
|
|
143
|
+
# 4. Fill in ~/.config/claude-imessage/config.json with the values from step 2
|
|
144
|
+
# and YOUR personal phone number as the only whitelist entry
|
|
145
|
+
|
|
146
|
+
# 5. Start, send "?" from your phone, confirm response
|
|
147
|
+
npm start
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Example 2: Composing with `sendblue-notify`
|
|
151
|
+
|
|
152
|
+
Wire outbound completion pings via [[sendblue-notify]] *and* inbound control via textme — they share the same Sendblue account but solve opposite problems:
|
|
153
|
+
|
|
154
|
+
- Claude finishes a long task → texts user via [[sendblue-notify]] (`Stop` hook).
|
|
155
|
+
- User replies "look at the diff" → textme routes that into Claude → Claude responds back via Sendblue.
|
|
156
|
+
|
|
157
|
+
### Example 3: Tail the daemon log
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# pm2
|
|
161
|
+
pm2 logs textme
|
|
162
|
+
|
|
163
|
+
# Standalone
|
|
164
|
+
tail -f ~/.local/log/claude-imessage.log
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Example 4: MCP-only alternative (no daemon)
|
|
168
|
+
|
|
169
|
+
If the user wants Sendblue messaging available to Claude Code as tools but does **not** want a polling daemon listening for inbound commands, they can register Sendblue as an MCP server instead:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
claude mcp add sendblue_api \
|
|
173
|
+
--env SENDBLUE_API_API_KEY=your-api-key \
|
|
174
|
+
--env SENDBLUE_API_API_SECRET=your-api-secret \
|
|
175
|
+
-- npx -y sendblue-api-mcp --client=claude-code --tools=all
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
This gives Claude outbound Sendblue tools inside a session but does **not** open the inbound phone-controls-Claude channel that textme provides. Pick textme when "text Claude from anywhere" is the goal; pick MCP when Claude only needs to send.
|
|
179
|
+
|
|
180
|
+
## Best Practices
|
|
181
|
+
|
|
182
|
+
- ✅ **Whitelist exactly one phone number to start.** The whitelist is the security boundary; expand it slowly and only to numbers the user controls.
|
|
183
|
+
- ✅ **Run the daemon as a regular user**, never as root or via `sudo`.
|
|
184
|
+
- ✅ **Start in a sandbox directory** for first tests (`cd ~/textme-sandbox`), not in `~` or a real repo.
|
|
185
|
+
- ✅ **Verify the non-whitelist ignore path** before enabling auto-start. A daemon that processes any sender is an open shell on the host.
|
|
186
|
+
- ✅ **Keep `pollIntervalMs` ≥ 5000** unless the user understands the Sendblue rate limits and cost implications.
|
|
187
|
+
- ❌ **Don't share the Sendblue number publicly.** Even with a whitelist, the host is doing per-message work; a flood from an unknown sender still costs polling cycles.
|
|
188
|
+
- ❌ **Don't store `config.json` in a repo, dotfiles backup, or cloud sync** — it contains API credentials and the user's phone number.
|
|
189
|
+
- ❌ **Don't run the daemon on a shared machine** without considering what every other user of that machine can now reach by sending an SMS.
|
|
190
|
+
|
|
191
|
+
## Limitations
|
|
192
|
+
|
|
193
|
+
- **Outbound-only flows do not need this skill.** For "text me when X finishes" use [[sendblue-notify]]; running a daemon is overkill.
|
|
194
|
+
- **Voice transcription requires a separate OpenAI API key.** Without it, voice notes are dropped or surfaced as un-transcribed audio depending on daemon version.
|
|
195
|
+
- **The daemon polls on an interval** — there is no push delivery. Expect single-digit-second latency between message receipt and Claude response.
|
|
196
|
+
- **One conversation, one machine.** This is a per-host daemon, not a multi-tenant service. Two daemons sharing one Sendblue number will both try to handle every inbound message.
|
|
197
|
+
- **Sendblue free-plan verification still applies.** The user's phone must have texted the Sendblue number once before outbound responses from Claude reach the user (see [[sendblue-cli]] limitations).
|
|
198
|
+
|
|
199
|
+
## Security & Safety Notes
|
|
200
|
+
|
|
201
|
+
textme is a **remote code execution surface gated only by a phone-number whitelist**. Treat it accordingly.
|
|
202
|
+
|
|
203
|
+
- **Whitelist is the security boundary.** Anyone who can spoof or hijack a whitelisted number can drive Claude on the host. Be deliberate about which numbers go on the list and remove them when they no longer need access. <!-- security-allowlist: documented remote-control daemon; required disclosure per quality-bar.md -->
|
|
204
|
+
- **Sendblue API credentials are sensitive.** `config.json` contains an API key, API secret, and the user's phone number. Mode it `600`, keep it out of dotfile repos, and never paste it into shared logs, gists, or screenshots.
|
|
205
|
+
- **The daemon inherits the user's privileges.** It can read, write, and execute anything the running user can. Do not run as root, do not run from a directory with secrets the user does not want exposed to inbound SMS, and prefer a dedicated host or VM if available.
|
|
206
|
+
- **Claude Code's permission model still applies inside the daemon-driven session** — destructive actions still surface confirmation prompts. textme exposes the `yes`/`no` reply path for those prompts, which means the *phone number* is making the approval. Make sure the whitelist matches the trust level of approving destructive operations remotely.
|
|
207
|
+
- **Inbound messages are untrusted input.** Treat textme prompts as user input from the open internet (with phone-number authentication). Do not pipe their contents into `eval`, shell substitution, or scripts that bypass Claude Code's review.
|
|
208
|
+
- **Lock-screen previews of replies leak.** When Claude responds to a message, the reply lands on the user's lock screen unredacted. Don't ask Claude over textme to surface secrets, tokens, or customer data via SMS — link to a local log or PR instead.
|
|
209
|
+
- **Daemon liveness is a footgun.** If pm2/launchd is auto-restarting the daemon, the user must remember to stop it before changing whitelist entries, rotating credentials, or rebooting into an untrusted state.
|
|
210
|
+
|
|
211
|
+
## Common Pitfalls
|
|
212
|
+
|
|
213
|
+
- **Whitelist drift.** A number added "for a demo" never gets removed. Audit `whitelist` whenever the host changes hands or scope.
|
|
214
|
+
- **`apiKey` / `apiSecret` confusion.** Sendblue's API credentials (from `sendblue show-keys`) are distinct from the CLI's local bearer token in `~/.sendblue/credentials.json`. textme needs the *API* credentials, not the CLI auth file.
|
|
215
|
+
- **Free-plan silent send failures.** If the user's phone never texted the Sendblue number first, outbound replies from Claude silently fail. Verify with `sendblue contacts` before relying on the loop.
|
|
216
|
+
- **Auto-start before verification.** Installing the launchd plist or `pm2 save`-ing the daemon before testing the non-whitelist ignore path will baseline a potentially open daemon at boot. Verify first, persist second.
|
|
217
|
+
- **Running the daemon in `~` or a repo with secrets.** The working directory at startup is exposed to anything textme is told to do (`ls`, `cat`, etc.). Start in a sandbox directory and `cd` deliberately.
|
|
218
|
+
- **Confusing textme with the Sendblue MCP.** They look similar but the MCP variant only gives Claude *outbound* Sendblue tools — it does not open an inbound channel. If the user wants "text Claude from my phone", they need textme; if they want "Claude can send a text mid-session", the MCP is lighter.
|
|
219
|
+
|
|
220
|
+
## Related Skills
|
|
221
|
+
|
|
222
|
+
- `@sendblue-notify` — Outbound counterpart (Claude → phone). Composes with textme to make the loop bidirectional.
|
|
223
|
+
- `@sendblue-cli` — Account setup, credential management, and the `show-keys` command that surfaces the API key/secret textme needs.
|
|
224
|
+
- `@sendblue-api` — HTTP API reference for users who want to build a custom inbound handler instead of using textme's daemon.
|
|
225
|
+
- `@update-config` — If wiring textme alongside Claude Code hooks (e.g. a `Stop` hook that pings the user), use this for the settings.json edits.
|
|
226
|
+
|
|
227
|
+
## Links
|
|
228
|
+
|
|
229
|
+
- Repository: <https://github.com/njerschow/textme>
|
|
230
|
+
- License: <https://github.com/njerschow/textme/blob/main/LICENSE> (MIT)
|
|
231
|
+
- Sendblue: <https://sendblue.com>
|
|
232
|
+
- Sendblue docs: <https://docs.sendblue.com>
|
package/package.json
CHANGED