@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/.env.example +14 -0
- package/README.md +25 -283
- package/bin/tether.ts +398 -32
- package/docs/agents.md +205 -0
- package/docs/architecture.md +117 -0
- package/docs/cli.md +277 -0
- package/docs/configuration.md +160 -0
- package/docs/discord-setup.md +122 -0
- package/docs/installation.md +151 -0
- package/docs/troubleshooting.md +74 -0
- package/package.json +2 -1
- package/src/api.ts +89 -6
- package/src/bot.ts +114 -31
- package/src/config.ts +392 -0
- package/src/db.ts +6 -0
- package/src/features/pause-resume.ts +3 -3
- package/src/queue.ts +1 -0
- package/src/worker.ts +14 -2
- package/src/spawner.ts +0 -110
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).
|