compass-agent 2.0.4
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/.github/workflows/publish.yml +24 -0
- package/README.md +294 -0
- package/bun.lock +326 -0
- package/manifest.yml +66 -0
- package/package.json +25 -0
- package/src/app.ts +786 -0
- package/src/db.ts +398 -0
- package/src/handlers/assistant.ts +332 -0
- package/src/handlers/cwd-modal.test.ts +188 -0
- package/src/handlers/cwd-modal.ts +63 -0
- package/src/handlers/setStatus.test.ts +118 -0
- package/src/handlers/stream.test.ts +137 -0
- package/src/handlers/stream.ts +908 -0
- package/src/lib/log.ts +16 -0
- package/src/lib/thread-context.ts +99 -0
- package/src/lib/worktree.ts +103 -0
- package/src/mcp/server.ts +286 -0
- package/src/types.ts +118 -0
- package/src/ui/blocks.ts +155 -0
- package/tests/blocks.test.ts +73 -0
- package/tests/db.test.ts +261 -0
- package/tests/thread-context.test.ts +183 -0
- package/tests/utils.test.ts +75 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- uses: oven-sh/setup-bun@v2
|
|
16
|
+
|
|
17
|
+
- name: Set version from release tag
|
|
18
|
+
run: npm version "${GITHUB_REF_NAME#v}" --no-git-tag-version
|
|
19
|
+
|
|
20
|
+
- run: bun install --frozen-lockfile
|
|
21
|
+
|
|
22
|
+
- run: npm publish --access public
|
|
23
|
+
env:
|
|
24
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/README.md
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# Compass
|
|
2
|
+
|
|
3
|
+
A Slack app that connects Claude Code to your workspace. Every thread becomes a coding session — set a working directory, ask questions, and get answers with full access to your local filesystem. Claude runs on your machine, streams responses in real-time, and remembers context across messages.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
The bot runs locally on your machine using Slack's Socket Mode (no public URL needed). When you send a message in the App Home, it spawns a `claude` CLI process, streams the output back to Slack using the native streaming API, and maintains session continuity across messages in the same thread.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
You (Slack thread) → Bot (Socket Mode) → Claude CLI (local) → Your filesystem
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Each thread is an isolated session with its own working directory, Claude session ID, and optionally its own git worktree.
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
|
|
19
|
+
### Per-thread sessions
|
|
20
|
+
|
|
21
|
+
Every App Home thread is an independent Claude session. The bot tracks session IDs so subsequent messages in the same thread resume the same Claude conversation with full context.
|
|
22
|
+
|
|
23
|
+
Session lifecycle:
|
|
24
|
+
1. First message → new Claude session created
|
|
25
|
+
2. Claude's `system.init` event stores the real session ID in SQLite
|
|
26
|
+
3. Subsequent messages use `--resume <session_id>` to continue the conversation
|
|
27
|
+
|
|
28
|
+
### `$cwd` — Working directory
|
|
29
|
+
|
|
30
|
+
Each thread requires a working directory before Claude can run. This is where Claude will read/write files.
|
|
31
|
+
|
|
32
|
+
| Command | Description |
|
|
33
|
+
|---------|-------------|
|
|
34
|
+
| `$cwd` | Opens an interactive picker with recent directories and an "Add new" button |
|
|
35
|
+
| `$cwd /path/to/project` | Sets the directory directly |
|
|
36
|
+
|
|
37
|
+
The picker remembers previously used directories. CWD is stored per-thread in SQLite.
|
|
38
|
+
|
|
39
|
+

|
|
40
|
+
|
|
41
|
+
### `$teach` — Team knowledge base
|
|
42
|
+
|
|
43
|
+
Store team conventions and instructions that get injected into every Claude session via `--append-system-prompt`.
|
|
44
|
+
|
|
45
|
+
| Command | Description |
|
|
46
|
+
|---------|-------------|
|
|
47
|
+
| `$teach` | Shows usage help |
|
|
48
|
+
| `$teach <instruction>` | Adds a new teaching (strips surrounding quotes) |
|
|
49
|
+
| `$teach list` | Lists all active teachings with IDs |
|
|
50
|
+
| `$teach remove <id>` | Soft-deletes a teaching by ID |
|
|
51
|
+
|
|
52
|
+
Examples:
|
|
53
|
+
```
|
|
54
|
+
$teach Use TypeScript for all new files
|
|
55
|
+
$teach Always write tests before implementation
|
|
56
|
+
$teach Use pnpm instead of npm
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Teachings are workspace-wide and persist across sessions. They appear in Claude's system prompt as:
|
|
60
|
+
```
|
|
61
|
+
Team conventions:
|
|
62
|
+
- Use TypeScript for all new files
|
|
63
|
+
- Always write tests before implementation
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Streaming responses
|
|
67
|
+
|
|
68
|
+
Responses stream to Slack token-by-token using Slack's native `chatStream` API (`chat.startStream` / `chat.appendStream` / `chat.stopStream`). This gives a smooth typing effect instead of chunked updates.
|
|
69
|
+
|
|
70
|
+
If streaming fails (e.g., missing permissions or API errors), the bot falls back to throttled `chat.update` calls (every 750ms).
|
|
71
|
+
|
|
72
|
+
Claude's tool calls are visualized as an agentic timeline — each tool invocation (file reads, code edits, shell commands) appears as a step that progresses from in-progress to complete.
|
|
73
|
+
|
|
74
|
+

|
|
75
|
+
|
|
76
|
+
### Stop button
|
|
77
|
+
|
|
78
|
+
Every response includes a red "Stop" button. Clicking it sends `SIGTERM` to the Claude process. The partial response is preserved with a "_Stopped by user._" suffix.
|
|
79
|
+
|
|
80
|
+
During streaming mode, the Stop button lives in a separate carrier message that gets deleted after the stream finalizes.
|
|
81
|
+
|
|
82
|
+
### App Home dashboard
|
|
83
|
+
|
|
84
|
+
The Home tab shows a live dashboard when you open it:
|
|
85
|
+
|
|
86
|
+
- **Stats bar** — active sessions, team teachings count, active worktrees, running processes
|
|
87
|
+
- **Recent sessions** — last 10 sessions with CWD and status indicator (green = active, white = idle)
|
|
88
|
+
- **Recent activity** — last 5 usage logs with model, turns, and cost
|
|
89
|
+
- **Quick actions** — "View Teachings" opens a modal listing all teachings; "Add Teaching" opens an input modal
|
|
90
|
+
|
|
91
|
+
### Git worktree isolation
|
|
92
|
+
|
|
93
|
+
When the CWD is inside a git repository, the bot automatically creates a git worktree for each thread. This means parallel threads can make code changes without conflicting with each other or your main working tree.
|
|
94
|
+
|
|
95
|
+
How it works:
|
|
96
|
+
1. On first message, `detectGitRepo()` checks if CWD is in a git repo
|
|
97
|
+
2. If yes, creates a worktree at `<repo>/trees/slack-<thread_ts>` on a new branch `slack/<thread_ts>`
|
|
98
|
+
3. Copies `.env`, `.env.local`, `.env.development` from the main repo
|
|
99
|
+
4. Claude spawns in the worktree directory instead of the raw CWD
|
|
100
|
+
5. Subsequent messages in the same thread reuse the existing worktree
|
|
101
|
+
|
|
102
|
+
Cleanup: An hourly job removes worktrees that have been idle for 24+ hours, skipping any with active processes or uncommitted changes.
|
|
103
|
+
|
|
104
|
+
If the CWD is not a git repo, Claude runs directly in the CWD with no worktree.
|
|
105
|
+
|
|
106
|
+
### Usage logging
|
|
107
|
+
|
|
108
|
+
Every Claude invocation logs to the `usage_logs` table:
|
|
109
|
+
- Session key, user ID, model
|
|
110
|
+
- Input/output tokens, total cost (USD)
|
|
111
|
+
- Duration, number of turns
|
|
112
|
+
|
|
113
|
+
This data powers the "Recent Activity" section on the App Home dashboard.
|
|
114
|
+
|
|
115
|
+
### User whitelist
|
|
116
|
+
|
|
117
|
+
Set `ALLOWED_USERS` in `.env` to restrict who can interact with the bot. Comma-separated Slack user IDs.
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
ALLOWED_USERS=U096GJFBZ54,U0XXXXXXXX
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Leave empty or unset to allow all users.
|
|
124
|
+
|
|
125
|
+
## Setup
|
|
126
|
+
|
|
127
|
+
### Prerequisites
|
|
128
|
+
|
|
129
|
+
- [Bun](https://bun.sh) runtime
|
|
130
|
+
- [Claude CLI](https://docs.anthropic.com/en/docs/claude-code) installed and authenticated
|
|
131
|
+
- A Slack workspace where you can create apps
|
|
132
|
+
|
|
133
|
+
### 1. Create the Slack app
|
|
134
|
+
|
|
135
|
+
1. Go to [api.slack.com/apps](https://api.slack.com/apps) and click "Create New App"
|
|
136
|
+
2. Choose "From an app manifest" and paste the contents of `manifest.yml`
|
|
137
|
+
3. Install the app to your workspace
|
|
138
|
+
4. Under **Settings > Basic Information**, generate an App-Level Token with `connections:write` scope — this is your `SLACK_APP_TOKEN` (starts with `xapp-`)
|
|
139
|
+
5. Under **OAuth & Permissions**, copy the Bot User OAuth Token — this is your `SLACK_BOT_TOKEN` (starts with `xoxb-`)
|
|
140
|
+
|
|
141
|
+
### 2. Configure environment
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
mkdir -p ~/.compass
|
|
145
|
+
cat > ~/.compass/.env << 'EOF'
|
|
146
|
+
SLACK_APP_TOKEN=xapp-1-...
|
|
147
|
+
SLACK_BOT_TOKEN=xoxb-...
|
|
148
|
+
ALLOWED_USERS=U096GJFBZ54
|
|
149
|
+
EOF
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 3. Run
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
bunx compass
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
That's it. The bot connects via Socket Mode — no ngrok or public URL needed.
|
|
159
|
+
|
|
160
|
+
You can also point to a specific env file:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
bunx compass --env-file /path/to/.env
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Or pass tokens directly as environment variables:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
SLACK_APP_TOKEN=xapp-... SLACK_BOT_TOKEN=xoxb-... bunx compass
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
#### Alternative: clone and run locally
|
|
173
|
+
|
|
174
|
+
If you prefer to run from source:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
git clone https://github.com/anthropics/compass.git
|
|
178
|
+
cd compass
|
|
179
|
+
cp .env.example .env # edit with your tokens
|
|
180
|
+
bun install
|
|
181
|
+
bun start
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### Environment loading precedence
|
|
185
|
+
|
|
186
|
+
When multiple sources provide the same variable, higher priority wins:
|
|
187
|
+
|
|
188
|
+
1. Real environment variables (highest)
|
|
189
|
+
2. `--env-file <path>`
|
|
190
|
+
3. `~/.compass/.env`
|
|
191
|
+
4. Local `.env` in the current directory (lowest)
|
|
192
|
+
|
|
193
|
+
### 4. Verify
|
|
194
|
+
|
|
195
|
+
1. Open the app in Slack (find it in the Apps section)
|
|
196
|
+
2. Go to the **Home** tab — you should see the dashboard
|
|
197
|
+
3. Start a new thread in the **Messages** tab
|
|
198
|
+
4. Send `$cwd /path/to/your/project`
|
|
199
|
+
5. Send a question — Claude should respond with streaming text
|
|
200
|
+
|
|
201
|
+
## Architecture
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
src/
|
|
205
|
+
app.ts Entry point — Bolt app, actions, modals, App Home, startup
|
|
206
|
+
db.ts SQLite schema (11 tables) and typed prepared statement exports (bun:sqlite)
|
|
207
|
+
types.ts Shared TypeScript interfaces (row types, runtime types)
|
|
208
|
+
handlers/
|
|
209
|
+
assistant.ts Assistant handlers — threadStarted, userMessage, commands
|
|
210
|
+
stream.ts Claude CLI streaming — NDJSON parsing, task chunks, usage logging
|
|
211
|
+
ui/
|
|
212
|
+
blocks.ts Block Kit builders — stop button, feedback, disclaimer, prompts, dashboard
|
|
213
|
+
lib/
|
|
214
|
+
log.ts Shared logging helpers (ts, log, logErr, toSqliteDatetime)
|
|
215
|
+
worktree.ts Git worktree lifecycle operations (create, remove, detect)
|
|
216
|
+
mcp/
|
|
217
|
+
server.ts MCP server — reminders, teachings, channel CWD tools
|
|
218
|
+
manifest.yml Slack app manifest (scopes, events, features)
|
|
219
|
+
sessions.db SQLite database (auto-created on first run)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Database tables
|
|
223
|
+
|
|
224
|
+
| Table | Purpose |
|
|
225
|
+
|-------|---------|
|
|
226
|
+
| `sessions` | Per-thread session tracking (session_id, cwd, user) |
|
|
227
|
+
| `cwd_history` | Recently used directories for the picker |
|
|
228
|
+
| `team_knowledge` | `$teach` instructions (soft-deletable) |
|
|
229
|
+
| `usage_logs` | Token counts, cost, duration per invocation |
|
|
230
|
+
| `worktrees` | Active git worktree tracking and cleanup state |
|
|
231
|
+
| `feedback` | Thumbs up/down feedback on responses |
|
|
232
|
+
| `annotations` | File annotations (future) |
|
|
233
|
+
| `shared_sessions` | Session sharing via codes (future) |
|
|
234
|
+
| `watched_channels` | Auto-respond channels (future) |
|
|
235
|
+
| `snapshots` | Git state snapshots (future) |
|
|
236
|
+
| `mcp_configs` | MCP server configurations (future) |
|
|
237
|
+
|
|
238
|
+
### Message flow
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
1. Slack message arrives via Socket Mode
|
|
242
|
+
2. Check: subtype? bot? allowed user?
|
|
243
|
+
3. Check: $cwd or $teach command? → handle and return
|
|
244
|
+
4. Check: active process in this thread? → reject
|
|
245
|
+
5. Session lookup: resume existing or create new (pending → system.init)
|
|
246
|
+
6. CWD gate: require working directory
|
|
247
|
+
7. Worktree setup: detect git, create/reuse worktree
|
|
248
|
+
8. Post "Thinking..." message with Stop button
|
|
249
|
+
9. Create chatStream (or fallback to chat.update)
|
|
250
|
+
10. Inject team teachings via --append-system-prompt
|
|
251
|
+
11. Spawn claude CLI with stream-json output
|
|
252
|
+
12. Parse NDJSON events: system.init, text_delta, result
|
|
253
|
+
13. Stream text to Slack via chatStream.append() or throttled chat.update
|
|
254
|
+
14. On close: finalize stream, log usage, clean up stop button
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Logging
|
|
258
|
+
|
|
259
|
+
Every action produces timestamped structured logs to stdout/stderr:
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
2026-02-14T14:32:01.123Z [D0XXXXXX] Incoming message event: user=U096GJ... ts=1707900000.123456
|
|
263
|
+
2026-02-14T14:32:01.124Z [D0XXXXXX] $teach command: arg="Use TypeScript" user=U096GJ...
|
|
264
|
+
2026-02-14T14:32:01.200Z [D0XXXXXX] Worktree lookup: thread=1707900000.123456 existingWt=none
|
|
265
|
+
2026-02-14T14:32:01.201Z [D0XXXXXX] Git detection: cwd=/Users/dev/project isGit=true repoRoot=/Users/dev/project
|
|
266
|
+
2026-02-14T14:32:01.300Z [D0XXXXXX] Created worktree: /Users/dev/project/trees/slack-1707900000-123456
|
|
267
|
+
2026-02-14T14:32:01.400Z [D0XXXXXX] Spawning claude: cwd=/Users/dev/project/trees/slack-1707900000-123456
|
|
268
|
+
2026-02-14T14:32:02.100Z [D0XXXXXX] stream: type=system subtype=init session_id=abc-123
|
|
269
|
+
2026-02-14T14:32:02.500Z [D0XXXXXX] Streamer activated: first append
|
|
270
|
+
2026-02-14T14:32:05.000Z [D0XXXXXX] stream: result turns=3 cost=$0.0142
|
|
271
|
+
2026-02-14T14:32:05.100Z [D0XXXXXX] Usage logged: cost=$0.0142 turns=3
|
|
272
|
+
2026-02-14T14:32:05.200Z [D0XXXXXX] Stream finalized, stop button deleted
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Errors go to stderr via `logErr()`. The format is `TIMESTAMP [CHANNEL_ID] message`.
|
|
276
|
+
|
|
277
|
+
## Configuration
|
|
278
|
+
|
|
279
|
+
| Variable | Required | Description |
|
|
280
|
+
|----------|----------|-------------|
|
|
281
|
+
| `SLACK_APP_TOKEN` | Yes | App-level token (`xapp-...`) for Socket Mode |
|
|
282
|
+
| `SLACK_BOT_TOKEN` | Yes | Bot user OAuth token (`xoxb-...`) |
|
|
283
|
+
| `ALLOWED_USERS` | No | Comma-separated Slack user IDs to whitelist |
|
|
284
|
+
| `CLAUDE_PATH` | No | Path to the `claude` binary (defaults to `claude` in PATH) |
|
|
285
|
+
| `CLAUDE_ADDITIONAL_ARGS` | No | Extra CLI args appended to every `claude` invocation (space-separated) |
|
|
286
|
+
| `ENV_*` | No | Variables prefixed with `ENV_` are injected into the Claude process (e.g. `ENV_ANTHROPIC_API_KEY=sk-...` sets `ANTHROPIC_API_KEY`) |
|
|
287
|
+
|
|
288
|
+
## Manifest scopes
|
|
289
|
+
|
|
290
|
+
**Bot scopes:** `channels:history`, `channels:read`, `chat:write`, `im:history`, `im:read`, `im:write`, `app_mentions:read`, `assistant:write`, `incoming-webhook`, `commands`
|
|
291
|
+
|
|
292
|
+
**Bot events:** `message.channels`, `message.im`, `app_mention`, `app_home_opened`, `assistant_thread_started`
|
|
293
|
+
|
|
294
|
+
**Features:** App Home (messages + home tabs), Bot User, Assistant View, Slash Commands (`/cwd`)
|