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.
@@ -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
+ ![Agentic task visualization with sub-agents](assets/sub-agent.png)
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
+ ![Working directory set in channel](assets/streaming-feedback.png)
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
+ ![Streaming response with planning and sub-agents](assets/planning-streaming.png)
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`)