@thesammykins/tether 1.0.1 → 1.3.0

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/docs/agents.md ADDED
@@ -0,0 +1,205 @@
1
+ # Agent Setup
2
+
3
+ Tether bridges Discord messages to an AI coding agent CLI. You need at least one agent installed and available on your system PATH.
4
+
5
+ Set the agent type in your config:
6
+
7
+ ```bash
8
+ tether config set AGENT_TYPE claude # or: opencode, codex
9
+ ```
10
+
11
+ ## Claude Code
12
+
13
+ Claude Code is Anthropic's agentic coding tool. It runs natively — no Node.js required.
14
+
15
+ ### Install
16
+
17
+ **Quick install (macOS/Linux):**
18
+
19
+ ```bash
20
+ curl -fsSL https://claude.ai/install.sh | bash
21
+ ```
22
+
23
+ **Homebrew (macOS):**
24
+
25
+ ```bash
26
+ brew install --cask claude-code
27
+ ```
28
+
29
+ ### Authenticate
30
+
31
+ ```bash
32
+ claude
33
+ # Follow prompts to log in with your Anthropic account
34
+ ```
35
+
36
+ You need an active Anthropic account. Claude Code authenticates via browser — no API key needed for interactive use.
37
+
38
+ ### Verify
39
+
40
+ ```bash
41
+ claude --version
42
+ claude --print -p "say hello"
43
+ ```
44
+
45
+ ### How Tether Uses It
46
+
47
+ Tether spawns Claude Code with:
48
+
49
+ ```bash
50
+ claude --print --output-format json --session-id <id> -p "<prompt>"
51
+ # Follow-ups:
52
+ claude --print --output-format json --resume <id> -p "<prompt>"
53
+ ```
54
+
55
+ Key flags:
56
+ - `--print` — Non-interactive mode, returns output to stdout
57
+ - `--session-id` / `--resume` — Session persistence across messages in the same thread
58
+ - `--append-system-prompt` — Injects datetime context and channel info
59
+ - `--output-format json` — Structured output for reliable parsing
60
+
61
+ ---
62
+
63
+ ## OpenCode
64
+
65
+ OpenCode supports multiple AI providers (OpenAI, Anthropic, Google, etc.).
66
+
67
+ ### Install
68
+
69
+ **Quick install:**
70
+
71
+ ```bash
72
+ curl -fsSL https://opencode.ai/install | bash
73
+ ```
74
+
75
+ **npm:**
76
+
77
+ ```bash
78
+ npm install -g opencode
79
+ ```
80
+
81
+ ### Configure
82
+
83
+ OpenCode reads its config from `~/.config/opencode/`. You'll need an API key for your chosen provider:
84
+
85
+ ```bash
86
+ # Set your provider API key (e.g. for Anthropic)
87
+ export ANTHROPIC_API_KEY=sk-ant-...
88
+
89
+ # Or for OpenAI
90
+ export OPENAI_API_KEY=sk-...
91
+ ```
92
+
93
+ See the [OpenCode docs](https://opencode.ai/docs) for full provider configuration.
94
+
95
+ ### Verify
96
+
97
+ ```bash
98
+ opencode --version
99
+ opencode run "say hello"
100
+ ```
101
+
102
+ ### How Tether Uses It
103
+
104
+ Tether spawns OpenCode with:
105
+
106
+ ```bash
107
+ opencode run --format json "<prompt>"
108
+ # Follow-ups:
109
+ opencode run --format json --session <id> "<prompt>"
110
+ ```
111
+
112
+ Key flags:
113
+ - `run` — Execute a prompt
114
+ - `--format json` — Structured output
115
+ - `--session <id>` — Resume an existing session
116
+ - `--cwd <path>` — Set working directory
117
+
118
+ ---
119
+
120
+ ## Codex
121
+
122
+ Codex is OpenAI's CLI coding agent. Requires Node.js 22+.
123
+
124
+ ### Install
125
+
126
+ ```bash
127
+ npm install -g @openai/codex
128
+ ```
129
+
130
+ ### Configure
131
+
132
+ Codex requires an OpenAI API key:
133
+
134
+ ```bash
135
+ export OPENAI_API_KEY=sk-...
136
+ ```
137
+
138
+ ### Verify
139
+
140
+ ```bash
141
+ codex --version
142
+ codex exec "say hello"
143
+ ```
144
+
145
+ ### How Tether Uses It
146
+
147
+ Tether spawns Codex with:
148
+
149
+ ```bash
150
+ codex exec --json "<prompt>"
151
+ # Follow-ups:
152
+ codex exec resume <sessionId> --json "<prompt>"
153
+ ```
154
+
155
+ Key flags:
156
+ - `exec` — Execute a prompt
157
+ - `--json` — Structured output
158
+ - `resume <id>` — Resume an existing session
159
+
160
+ ---
161
+
162
+ ## Having the Agent Set Up Tether
163
+
164
+ If you're already running one of these agents in a project, you can ask it to install and configure Tether:
165
+
166
+ ### Claude Code
167
+
168
+ ```
169
+ Install @thesammykins/tether globally with bun. Configure it with my Discord
170
+ bot token (I'll provide it when prompted), set the agent type to claude, and
171
+ set my allowed channel to 123456789. Then start the bot.
172
+ ```
173
+
174
+ ### OpenCode
175
+
176
+ ```
177
+ Run these commands to set up Tether:
178
+ 1. bun add -g @thesammykins/tether
179
+ 2. tether config set DISCORD_BOT_TOKEN (I'll provide the token)
180
+ 3. tether config set AGENT_TYPE opencode
181
+ 4. tether config set ALLOWED_CHANNELS 123456789
182
+ 5. tether start
183
+ ```
184
+
185
+ ### Codex
186
+
187
+ ```
188
+ Install @thesammykins/tether with: bun add -g @thesammykins/tether
189
+ Then configure it: tether config set AGENT_TYPE codex
190
+ Set the channel: tether config set ALLOWED_CHANNELS 123456789
191
+ I'll set the bot token manually.
192
+ ```
193
+
194
+ > **Note:** The bot token is a secret — you'll always need to provide it interactively via `tether config set DISCORD_BOT_TOKEN` (input is hidden). Don't paste tokens into agent prompts.
195
+
196
+ ## Switching Agents
197
+
198
+ Change agents at any time:
199
+
200
+ ```bash
201
+ tether config set AGENT_TYPE opencode
202
+ tether stop && tether start
203
+ ```
204
+
205
+ Existing thread sessions will not carry over — new messages start fresh sessions with the new agent.
@@ -0,0 +1,117 @@
1
+ # Architecture
2
+
3
+ ## Overview
4
+
5
+ Tether uses a queue-based architecture for reliable, decoupled message processing:
6
+
7
+ ```
8
+ Discord Bot → BullMQ Queue → Agent Worker
9
+ (gateway) (Redis) (spawns CLI)
10
+ ```
11
+
12
+ The bot handles Discord events, the queue provides durability and backpressure, and the worker spawns the appropriate agent CLI to process prompts.
13
+
14
+ ## Components
15
+
16
+ | Component | Path | Role |
17
+ |-----------|------|------|
18
+ | **Bot** | `src/bot.ts` | Discord gateway — receives messages, runs middleware, enqueues jobs |
19
+ | **Worker** | `src/worker.ts` | Job processor — pulls jobs from queue, spawns agent adapter, posts responses |
20
+ | **Adapters** | `src/adapters/` | `AgentAdapter` interface — wraps Claude/OpenCode/Codex CLIs |
21
+ | **Middleware** | `src/middleware/` | Request pipeline — allowlist, rate limiter |
22
+ | **Features** | `src/features/` | Ack emoji, channel context, thread naming, session limits, pause/resume |
23
+ | **Queue** | `src/queue.ts` | BullMQ job queue backed by Redis |
24
+ | **Database** | `src/db.ts` | SQLite — thread→session mapping, channel config |
25
+ | **Config** | `src/config.ts` | Config store — TOML preferences + AES-256-GCM encrypted secrets |
26
+ | **CLI** | `bin/tether.ts` | Entry point — all `tether` commands |
27
+
28
+ ## Message Flow
29
+
30
+ ### Guild Channels
31
+
32
+ 1. User `@mentions` the bot in a Discord channel
33
+ 2. Bot runs the middleware pipeline (allowlist → rate limiter → pause check)
34
+ 3. Bot creates a thread with an auto-generated name
35
+ 4. Bot adds a job to the BullMQ queue
36
+ 5. Worker pulls the job and spawns the configured agent adapter
37
+ 6. Adapter runs the CLI (`claude`/`opencode`/`codex`) with the prompt
38
+ 7. Worker posts the agent's response back to the Discord thread
39
+
40
+ ### Direct Messages
41
+
42
+ 1. User sends a message to the bot in DMs (no `@mention` needed)
43
+ 2. Bot checks user allowlist → rate limiter
44
+ 3. Bot creates or resumes a session for this user
45
+ 4. Bot adds a job to the BullMQ queue
46
+ 5. Worker posts the response back to the DM channel
47
+
48
+ ### BRB Mode
49
+
50
+ When a user sends `brb` in a thread:
51
+
52
+ 1. The session is flagged as "BRB" — the user is away
53
+ 2. If the agent needs to ask a question, it calls `tether ask` instead of its built-in prompt
54
+ 3. `tether ask` sends interactive buttons to the Discord thread
55
+ 4. The CLI blocks until someone clicks a button or types a free-text answer
56
+ 5. The answer is returned to the agent via stdout
57
+
58
+ ## Middleware Pipeline
59
+
60
+ Messages pass through middleware in order. Each middleware can block the message.
61
+
62
+ ```
63
+ Message → Allowlist → Rate Limiter → Pause/Resume → Queue
64
+ ```
65
+
66
+ 1. **Allowlist** (`src/middleware/allowlist.ts`) — Block unauthorized users, channels, or roles. DMs only check `ALLOWED_USERS`.
67
+ 2. **Rate Limiter** (`src/middleware/rate-limiter.ts`) — Sliding window per-user rate limit. Configurable via `RATE_LIMIT_REQUESTS` and `RATE_LIMIT_WINDOW_MS`.
68
+ 3. **Pause/Resume** (`src/features/pause-resume.ts`) — Hold messages if the thread is paused (guild threads only). Users type `pause`/`resume` to toggle.
69
+
70
+ ## Adapter System
71
+
72
+ Adapters implement the `AgentAdapter` interface (`src/adapters/types.ts`):
73
+
74
+ ```typescript
75
+ interface AgentAdapter {
76
+ readonly name: string;
77
+ spawn(options: SpawnOptions): Promise<SpawnResult>;
78
+ }
79
+ ```
80
+
81
+ The adapter registry (`src/adapters/registry.ts`) maps `AGENT_TYPE` to the correct adapter. Each adapter handles:
82
+
83
+ - CLI argument construction
84
+ - Session ID management (new vs. resume)
85
+ - Output parsing (JSON → text)
86
+ - Working directory configuration
87
+
88
+ ## Session Management
89
+
90
+ - **Turn limits** — Max messages per thread/DM session (`MAX_TURNS_PER_SESSION`)
91
+ - **Duration limits** — Max session lifetime (`MAX_SESSION_DURATION_MS`)
92
+ - **Pause/Resume** — Type `pause` to hold messages, `resume` to continue (threads only)
93
+ - **DM Reset** — Type `!reset` in DMs to start a new session
94
+ - **Auto-completion** — React with ✅ on the last message to mark "Done" (threads only)
95
+
96
+ ## Config Resolution
97
+
98
+ Configuration values are resolved in priority order:
99
+
100
+ ```
101
+ Environment variable → Config store (TOML/encrypted) → Default
102
+ ```
103
+
104
+ See [Configuration](configuration.md) for the full key reference.
105
+
106
+ ## Data Storage
107
+
108
+ | Store | Path | Contents |
109
+ |-------|------|----------|
110
+ | SQLite | `./data/threads.db` | Thread→session mapping, channel working dirs |
111
+ | Config | `~/.config/tether/config.toml` | Preferences (plaintext TOML) |
112
+ | Secrets | `~/.config/tether/secrets.enc` | Tokens (AES-256-GCM encrypted) |
113
+ | Redis | (in-memory) | BullMQ job queue |
114
+
115
+ ### Privacy
116
+
117
+ Tether stores only thread-to-session mappings and channel configuration. **No message content, user data, or conversation history is persisted.** Messages pass through the Redis queue transiently and are discarded after processing.
package/docs/cli.md ADDED
@@ -0,0 +1,277 @@
1
+ # CLI Reference
2
+
3
+ Full reference for the `tether` command-line interface.
4
+
5
+ ```bash
6
+ tether <command> [options]
7
+ ```
8
+
9
+ ## Management Commands
10
+
11
+ ### `tether start`
12
+
13
+ Start the Discord bot and job worker as child processes.
14
+
15
+ ```bash
16
+ tether start
17
+ ```
18
+
19
+ Blocks the terminal — both bot and worker run until you stop them. To run in the background:
20
+
21
+ ```bash
22
+ nohup bun run bin/tether.ts start > /tmp/tether.log 2>&1 &
23
+ ```
24
+
25
+ ### `tether stop`
26
+
27
+ Stop all running Tether processes.
28
+
29
+ ```bash
30
+ tether stop
31
+ ```
32
+
33
+ ### `tether status`
34
+
35
+ Show whether the bot and worker are running.
36
+
37
+ ```bash
38
+ tether status
39
+ ```
40
+
41
+ ### `tether health`
42
+
43
+ Check Discord gateway connection status.
44
+
45
+ ```bash
46
+ tether health
47
+ ```
48
+
49
+ ### `tether setup`
50
+
51
+ Interactive setup wizard — walks you through token, agent type, and channel configuration.
52
+
53
+ ```bash
54
+ tether setup
55
+ ```
56
+
57
+ ### `tether help`
58
+
59
+ Show the built-in help text.
60
+
61
+ ```bash
62
+ tether help
63
+ ```
64
+
65
+ ---
66
+
67
+ ## Discord Commands
68
+
69
+ ### `tether send`
70
+
71
+ Send a text message to a channel.
72
+
73
+ ```bash
74
+ tether send <channel-id> "message"
75
+ ```
76
+
77
+ ### `tether embed`
78
+
79
+ Send a rich embed.
80
+
81
+ ```bash
82
+ tether embed <channel-id> "description" [options]
83
+ ```
84
+
85
+ | Option | Description |
86
+ |--------|-------------|
87
+ | `--title "..."` | Embed title |
88
+ | `--url "..."` | Title link URL |
89
+ | `--color <name\|hex>` | `red`, `green`, `blue`, `yellow`, `purple`, `orange`, or `0xHEX` |
90
+ | `--author "..."` | Author name |
91
+ | `--author-url "..."` | Author link |
92
+ | `--author-icon "..."` | Author icon URL |
93
+ | `--thumbnail "..."` | Small image (top right) |
94
+ | `--image "..."` | Large image (bottom) |
95
+ | `--footer "..."` | Footer text |
96
+ | `--footer-icon "..."` | Footer icon URL |
97
+ | `--timestamp` | Add current timestamp |
98
+ | `--field "Name:Value"` | Add a field (append `:inline` for inline) |
99
+
100
+ **Example:**
101
+
102
+ ```bash
103
+ tether embed 123456789 "Status update" \
104
+ --title "Daily Report" \
105
+ --color green \
106
+ --field "Tasks:5 done:inline"
107
+ ```
108
+
109
+ ### `tether file`
110
+
111
+ Send a file attachment.
112
+
113
+ ```bash
114
+ tether file <channel-id> <filepath> ["message"]
115
+ ```
116
+
117
+ ### `tether buttons`
118
+
119
+ Send interactive buttons.
120
+
121
+ ```bash
122
+ tether buttons <channel-id> "prompt" --button label="..." id="..." [options]
123
+ ```
124
+
125
+ Each `--button` takes these key-value pairs:
126
+
127
+ | Key | Required | Description |
128
+ |-----|----------|-------------|
129
+ | `label="..."` | Yes | Button text |
130
+ | `id="..."` | Yes | Custom ID for identifying clicks |
131
+ | `style="..."` | No | `primary`, `secondary`, `success`, `danger` |
132
+ | `reply="..."` | No | Ephemeral reply when clicked |
133
+ | `webhook="..."` | No | URL to POST click data to |
134
+
135
+ **Example:**
136
+
137
+ ```bash
138
+ tether buttons 123456789 "Approve this PR?" \
139
+ --button label="Yes" id="approve" style="success" reply="Approved!" \
140
+ --button label="No" id="reject" style="danger" reply="Rejected"
141
+ ```
142
+
143
+ ### `tether ask`
144
+
145
+ Ask a blocking question with button options. Blocks until someone answers or the timeout expires. Prints the selected answer to stdout.
146
+
147
+ ```bash
148
+ tether ask <channel-id> "question" --option "A" --option "B" [--timeout 300]
149
+ ```
150
+
151
+ - Exit code `0` on answer, `1` on timeout
152
+ - Automatically includes a **"Type answer"** button for free-form text input
153
+ - Default timeout: 300 seconds (5 minutes)
154
+
155
+ **Example:**
156
+
157
+ ```bash
158
+ ANSWER=$(tether ask 123456789 "Deploy to prod?" --option "Yes" --option "No" --timeout 600)
159
+ if [ "$ANSWER" = "Yes" ]; then
160
+ deploy_to_prod
161
+ fi
162
+ ```
163
+
164
+ ### `tether typing`
165
+
166
+ Show the typing indicator in a channel.
167
+
168
+ ```bash
169
+ tether typing <channel-id>
170
+ ```
171
+
172
+ ### `tether edit`
173
+
174
+ Edit an existing message.
175
+
176
+ ```bash
177
+ tether edit <channel-id> <message-id> "new content"
178
+ ```
179
+
180
+ ### `tether delete`
181
+
182
+ Delete a message.
183
+
184
+ ```bash
185
+ tether delete <channel-id> <message-id>
186
+ ```
187
+
188
+ ### `tether rename`
189
+
190
+ Rename a thread.
191
+
192
+ ```bash
193
+ tether rename <thread-id> "new name"
194
+ ```
195
+
196
+ ### `tether reply`
197
+
198
+ Reply to a specific message.
199
+
200
+ ```bash
201
+ tether reply <channel-id> <message-id> "reply text"
202
+ ```
203
+
204
+ ### `tether thread`
205
+
206
+ Create a thread from a message.
207
+
208
+ ```bash
209
+ tether thread <channel-id> <message-id> "thread name"
210
+ ```
211
+
212
+ ### `tether react`
213
+
214
+ Add a reaction to a message.
215
+
216
+ ```bash
217
+ tether react <channel-id> <message-id> "emoji"
218
+ ```
219
+
220
+ ### `tether state`
221
+
222
+ Update a thread status message with a preset or custom text.
223
+
224
+ ```bash
225
+ tether state <channel-id> <message-id> <state>
226
+ ```
227
+
228
+ **Presets:** `processing`, `thinking`, `searching`, `writing`, `done`, `error`, `waiting`
229
+
230
+ ---
231
+
232
+ ## DM Commands
233
+
234
+ Proactive outreach — send messages directly to a user.
235
+
236
+ ### `tether dm` (text)
237
+
238
+ ```bash
239
+ tether dm <user-id> "message"
240
+ ```
241
+
242
+ ### `tether dm --embed`
243
+
244
+ Send a rich embed via DM (same options as `tether embed`).
245
+
246
+ ```bash
247
+ tether dm <user-id> --embed "description" --title "Title" --color green
248
+ ```
249
+
250
+ ### `tether dm --file`
251
+
252
+ Send a file attachment via DM.
253
+
254
+ ```bash
255
+ tether dm <user-id> --file ./report.md "Here's the report"
256
+ ```
257
+
258
+ ---
259
+
260
+ ## Config Commands
261
+
262
+ Manage persistent configuration and encrypted secrets. See [Configuration](configuration.md) for details.
263
+
264
+ ```bash
265
+ tether config set <key> [value] # Set a value (prompts for secrets)
266
+ tether config get <key> # Get resolved value with source
267
+ tether config list # Show all values with sources
268
+ tether config delete <key> # Remove a value
269
+ tether config import [path] # Import from .env file (default: ./.env)
270
+ tether config path # Show config file locations
271
+ ```
272
+
273
+ ---
274
+
275
+ ## HTTP API
276
+
277
+ Tether exposes an HTTP API on port 2643 for external scripts and webhooks. See the [API documentation](../skills/tether/HTTP-API.md).