compass-agent 2.0.6 → 2.0.8

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 ADDED
@@ -0,0 +1,3 @@
1
+ SLACK_APP_TOKEN=xapp-1-...
2
+ SLACK_BOT_TOKEN=xoxb-...
3
+ ALLOWED_USERS=
@@ -3,8 +3,7 @@ name: Publish to npm
3
3
  on:
4
4
  release:
5
5
  types: [published]
6
- push:
7
- branches: [main]
6
+ workflow_dispatch:
8
7
 
9
8
  jobs:
10
9
  publish:
@@ -0,0 +1,19 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - uses: oven-sh/setup-bun@v2
16
+
17
+ - run: bun install --frozen-lockfile
18
+
19
+ - run: bun test
package/README.md CHANGED
@@ -1,62 +1,70 @@
1
- # Compass
1
+ # 🧭 Compass
2
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.
3
+ [![Tests](https://github.com/raja-jamwal/compass/actions/workflows/test.yml/badge.svg)](https://github.com/raja-jamwal/compass/actions/workflows/test.yml)
4
+ [![Publish to npm](https://github.com/raja-jamwal/compass/actions/workflows/publish.yml/badge.svg)](https://github.com/raja-jamwal/compass/actions/workflows/publish.yml)
5
+ [![npm version](https://img.shields.io/npm/v/compass-agent)](https://www.npmjs.com/package/compass-agent)
4
6
 
5
- ![Agentic task visualization with sub-agents](assets/sub-agent.png)
7
+ Bring workforce of Claude Codes to your Slack workspace. Every thread becomes an isolated coding session — with its own working directory, git worktree, and full access to your local filesystem. Claude runs on your machine, streams responses in real-time, and your whole team can use it simultaneously without conflicts.
6
8
 
7
- ## How it works
9
+ ![Agentic task visualization with sub-agents](docs/assets/sub-agent.png)
10
+
11
+ ## Quick start
12
+
13
+ ```bash
14
+ bunx compass-agent
15
+ ```
8
16
 
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.
17
+ That's it. The bot connects via Socket Mode no servers, no ngrok, no cloud deployment. See [Setup](#setup) for first-time configuration.
18
+
19
+ ## How it works
10
20
 
11
21
  ```
12
22
  You (Slack thread) → Bot (Socket Mode) → Claude CLI (local) → Your filesystem
13
23
  ```
14
24
 
15
- Each thread is an isolated session with its own working directory, Claude session ID, and optionally its own git worktree.
25
+ The bot runs locally using Slack's Socket Mode. When you message it, it spawns a `claude` CLI process, streams output back to Slack in real-time, and maintains session continuity across messages. Each thread is an isolated session with its own working directory, session ID, and git worktree.
16
26
 
17
27
  ## Features
18
28
 
19
29
  ### Per-thread sessions
20
30
 
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.
31
+ Every thread is an independent Claude session. Subsequent messages in the same thread resume the conversation with full context.
22
32
 
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
33
+ 1. First message creates a new Claude session
34
+ 2. `system.init` stores the session ID in SQLite
35
+ 3. Follow-up messages use `--resume <session_id>` to continue
27
36
 
28
37
  ### `$cwd` — Working directory
29
38
 
30
- Each thread requires a working directory before Claude can run. This is where Claude will read/write files.
39
+ Set the working directory for Claude to read/write files.
31
40
 
32
41
  | Command | Description |
33
42
  |---------|-------------|
34
- | `$cwd` | Opens an interactive picker with recent directories and an "Add new" button |
43
+ | `$cwd` | Opens an interactive picker with recent directories |
35
44
  | `$cwd /path/to/project` | Sets the directory directly |
36
45
 
37
46
  The picker remembers previously used directories. CWD is stored per-thread in SQLite.
38
47
 
39
- ![Working directory set in channel](assets/streaming-feedback.png)
48
+ ![Working directory set in channel](docs/assets/streaming-feedback.png)
40
49
 
41
50
  ### `$teach` — Team knowledge base
42
51
 
43
- Store team conventions and instructions that get injected into every Claude session via `--append-system-prompt`.
52
+ Store team conventions that get injected into every Claude session across your workspace.
44
53
 
45
54
  | Command | Description |
46
55
  |---------|-------------|
47
- | `$teach` | Shows usage help |
48
- | `$teach <instruction>` | Adds a new teaching (strips surrounding quotes) |
56
+ | `$teach <instruction>` | Adds a new convention |
49
57
  | `$teach list` | Lists all active teachings with IDs |
50
- | `$teach remove <id>` | Soft-deletes a teaching by ID |
58
+ | `$teach remove <id>` | Removes a teaching by ID |
51
59
 
52
- Examples:
53
60
  ```
54
61
  $teach Use TypeScript for all new files
55
62
  $teach Always write tests before implementation
56
63
  $teach Use pnpm instead of npm
57
64
  ```
58
65
 
59
- Teachings are workspace-wide and persist across sessions. They appear in Claude's system prompt as:
66
+ These appear in Claude's system prompt as:
67
+
60
68
  ```
61
69
  Team conventions:
62
70
  - Use TypeScript for all new files
@@ -65,62 +73,30 @@ Team conventions:
65
73
 
66
74
  ### Streaming responses
67
75
 
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.
76
+ Responses stream token-by-token using Slack's native `chatStream` API, with automatic fallback to throttled `chat.update` calls if streaming isn't available.
73
77
 
74
- ![Streaming response with planning and sub-agents](assets/planning-streaming.png)
78
+ Tool calls are visualized as an agentic timeline — each invocation (file reads, edits, shell commands) appears as a step that progresses from in-progress to complete.
75
79
 
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
80
+ ![Streaming response with planning and sub-agents](docs/assets/planning-streaming.png)
90
81
 
91
82
  ### Git worktree isolation
92
83
 
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
84
+ When the CWD is inside a git repo, the bot automatically creates a worktree for each thread. Parallel threads can make code changes without conflicting with each other or your main working tree.
107
85
 
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
86
+ 1. First message detects if CWD is in a git repo
87
+ 2. Creates a worktree at `<repo>/trees/slack-<thread_ts>` on branch `slack/<thread_ts>`
88
+ 3. Copies `.env` files from the main repo
89
+ 4. Claude runs in the worktree instead of the raw CWD
90
+ 5. Subsequent messages reuse the existing worktree
112
91
 
113
- This data powers the "Recent Activity" section on the App Home dashboard.
92
+ An hourly cleanup job removes worktrees idle for 24+ hours, skipping any with active processes or uncommitted changes. If the CWD is not a git repo, Claude runs directly in it.
114
93
 
115
- ### User whitelist
94
+ ### More features
116
95
 
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.
96
+ - **Stop button** Every response includes a red Stop button that sends `SIGTERM` to Claude. Partial responses are preserved.
97
+ - **App Home dashboard** — Live stats (active sessions, teachings, worktrees), recent sessions with status indicators, usage logs with cost tracking, and quick actions for managing teachings.
98
+ - **Usage logging** — Every invocation logs session, user, model, tokens, cost, duration, and turns to SQLite. Powers the dashboard's "Recent Activity" section.
99
+ - **User whitelist** — Set `ALLOWED_USERS` in `.env` to restrict access by Slack user ID.
124
100
 
125
101
  ## Setup
126
102
 
@@ -152,29 +128,25 @@ EOF
152
128
  ### 3. Run
153
129
 
154
130
  ```bash
155
- bunx compass
131
+ bunx compass-agent
156
132
  ```
157
133
 
158
- That's it. The bot connects via Socket Mode — no ngrok or public URL needed.
159
-
160
134
  You can also point to a specific env file:
161
135
 
162
136
  ```bash
163
- bunx compass --env-file /path/to/.env
137
+ bunx compass-agent --env-file /path/to/.env
164
138
  ```
165
139
 
166
- Or pass tokens directly as environment variables:
140
+ Or pass tokens directly:
167
141
 
168
142
  ```bash
169
- SLACK_APP_TOKEN=xapp-... SLACK_BOT_TOKEN=xoxb-... bunx compass
143
+ SLACK_APP_TOKEN=xapp-... SLACK_BOT_TOKEN=xoxb-... bunx compass-agent
170
144
  ```
171
145
 
172
- #### Alternative: clone and run locally
173
-
174
- If you prefer to run from source:
146
+ #### Running from source
175
147
 
176
148
  ```bash
177
- git clone https://github.com/anthropics/compass.git
149
+ git clone https://github.com/raja-jamwal/compass.git
178
150
  cd compass
179
151
  cp .env.example .env # edit with your tokens
180
152
  bun install
@@ -183,8 +155,6 @@ bun start
183
155
 
184
156
  #### Environment loading precedence
185
157
 
186
- When multiple sources provide the same variable, higher priority wins:
187
-
188
158
  1. Real environment variables (highest)
189
159
  2. `--env-file <path>`
190
160
  3. `~/.compass/.env`
@@ -203,77 +173,40 @@ When multiple sources provide the same variable, higher priority wins:
203
173
  ```
204
174
  src/
205
175
  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)
176
+ db.ts SQLite schema and typed prepared statements (bun:sqlite)
177
+ types.ts Shared TypeScript interfaces
208
178
  handlers/
209
- assistant.ts Assistant handlersthreadStarted, userMessage, commands
210
- stream.ts Claude CLI streaming — NDJSON parsing, task chunks, usage logging
179
+ assistant.ts Thread lifecyclesession management, commands, message routing
180
+ stream.ts Claude CLI streaming — NDJSON parsing, tool timeline, usage logging
211
181
  ui/
212
- blocks.ts Block Kit builders — stop button, feedback, disclaimer, prompts, dashboard
182
+ blocks.ts Block Kit builders — dashboard, stop button, feedback, prompts
213
183
  lib/
214
- log.ts Shared logging helpers (ts, log, logErr, toSqliteDatetime)
215
- worktree.ts Git worktree lifecycle operations (create, remove, detect)
184
+ log.ts Structured logging helpers
185
+ worktree.ts Git worktree lifecycle (create, remove, detect, cleanup)
216
186
  mcp/
217
187
  server.ts MCP server — reminders, teachings, channel CWD tools
218
188
  manifest.yml Slack app manifest (scopes, events, features)
219
189
  sessions.db SQLite database (auto-created on first run)
220
190
  ```
221
191
 
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
192
  ### Message flow
239
193
 
240
194
  ```
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
195
+ Slack message (Socket Mode)
196
+ Auth check (subtype, bot, allowed user)
197
+ Command check ($cwd, $teach)
198
+ Concurrency check (one process per thread)
199
+ Session lookup (resume or create)
200
+ CWD gate
201
+ Worktree setup (detect git, create/reuse)
202
+ Post "Thinking..." with Stop button
203
+ Start chatStream (or fallback to chat.update)
204
+ Spawn claude CLI with team teachings
205
+ Parse NDJSON stream (init, text_delta, tool calls, result)
206
+ Stream to Slack in real-time
207
+ Finalize: log usage, clean up stop button
273
208
  ```
274
209
 
275
- Errors go to stderr via `logErr()`. The format is `TIMESTAMP [CHANNEL_ID] message`.
276
-
277
210
  ## Configuration
278
211
 
279
212
  | Variable | Required | Description |
@@ -282,13 +215,5 @@ Errors go to stderr via `logErr()`. The format is `TIMESTAMP [CHANNEL_ID] messag
282
215
  | `SLACK_BOT_TOKEN` | Yes | Bot user OAuth token (`xoxb-...`) |
283
216
  | `ALLOWED_USERS` | No | Comma-separated Slack user IDs to whitelist |
284
217
  | `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`)
218
+ | `CLAUDE_ADDITIONAL_ARGS` | No | Extra CLI args appended to every `claude` invocation |
219
+ | `ENV_*` | No | Variables prefixed with `ENV_` are injected into Claude's environment (e.g. `ENV_ANTHROPIC_API_KEY=sk-...` sets `ANTHROPIC_API_KEY`) |
@@ -0,0 +1,468 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Compass — Bring Claude Code to Slack</title>
7
+ <meta name="description" content="Bring a workforce of Claude Codes to your Slack workspace. Per-thread AI coding sessions with full local filesystem access.">
8
+ <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🧭</text></svg>">
9
+ <link rel="preconnect" href="https://fonts.googleapis.com">
10
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
12
+ <script src="https://cdn.tailwindcss.com"></script>
13
+ <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
14
+ <script>
15
+ tailwind.config = {
16
+ theme: {
17
+ extend: {
18
+ fontFamily: {
19
+ sans: ['Inter', '-apple-system', 'BlinkMacSystemFont', 'sans-serif'],
20
+ mono: ['JetBrains Mono', 'monospace'],
21
+ },
22
+ colors: {
23
+ apple: {
24
+ blue: '#007AFF',
25
+ indigo: '#5856D6',
26
+ purple: '#AF52DE',
27
+ pink: '#FF2D55',
28
+ gray: {
29
+ 50: '#F9FAFB',
30
+ 100: '#F2F2F7',
31
+ 200: '#E5E5EA',
32
+ 300: '#D1D1D6',
33
+ 400: '#AEAEB2',
34
+ 500: '#8E8E93',
35
+ 600: '#636366',
36
+ 700: '#48484A',
37
+ 800: '#2C2C2E',
38
+ 900: '#1C1C1E',
39
+ }
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
45
+ </script>
46
+ <style>
47
+ * { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
48
+
49
+ .glass-nav {
50
+ background: rgba(255, 255, 255, 0.72);
51
+ backdrop-filter: saturate(180%) blur(20px);
52
+ -webkit-backdrop-filter: saturate(180%) blur(20px);
53
+ }
54
+
55
+ .hero-gradient {
56
+ background: linear-gradient(180deg, #f5f5f7 0%, #ffffff 100%);
57
+ }
58
+
59
+ .text-gradient {
60
+ background: linear-gradient(135deg, #007AFF 0%, #5856D6 50%, #AF52DE 100%);
61
+ -webkit-background-clip: text;
62
+ -webkit-text-fill-color: transparent;
63
+ background-clip: text;
64
+ }
65
+
66
+ .card-glass {
67
+ background: rgba(255, 255, 255, 0.6);
68
+ backdrop-filter: blur(12px);
69
+ -webkit-backdrop-filter: blur(12px);
70
+ }
71
+
72
+ .screenshot-shadow {
73
+ box-shadow:
74
+ 0 2px 4px rgba(0, 0, 0, 0.04),
75
+ 0 8px 16px rgba(0, 0, 0, 0.06),
76
+ 0 24px 48px rgba(0, 0, 0, 0.08);
77
+ }
78
+
79
+ .feature-card-hover {
80
+ transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94), box-shadow 0.3s ease;
81
+ }
82
+ .feature-card-hover:hover {
83
+ transform: translateY(-4px);
84
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.08);
85
+ }
86
+
87
+ .flow-connector {
88
+ width: 40px;
89
+ height: 2px;
90
+ background: linear-gradient(90deg, #D1D1D6, #AEAEB2);
91
+ }
92
+
93
+ .install-box {
94
+ background: #1C1C1E;
95
+ transition: box-shadow 0.2s ease;
96
+ }
97
+ .install-box:hover {
98
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
99
+ }
100
+
101
+ .step-number {
102
+ background: linear-gradient(135deg, #007AFF, #5856D6);
103
+ }
104
+
105
+ .section-fade {
106
+ opacity: 0;
107
+ transform: translateY(30px);
108
+ transition: opacity 0.6s ease, transform 0.6s ease;
109
+ }
110
+ .section-fade.visible {
111
+ opacity: 1;
112
+ transform: translateY(0);
113
+ }
114
+
115
+ .config-row {
116
+ transition: background 0.15s ease;
117
+ }
118
+ .config-row:hover {
119
+ background: #F9FAFB;
120
+ }
121
+
122
+ @media (max-width: 768px) {
123
+ .flow-connector { display: none; }
124
+ .screenshot-row { flex-direction: column !important; }
125
+ }
126
+ </style>
127
+ </head>
128
+ <body class="font-sans text-apple-gray-900 bg-white antialiased">
129
+
130
+ <!-- Nav -->
131
+ <nav class="glass-nav fixed top-3 left-1/2 -translate-x-1/2 z-50 border border-apple-gray-200/60 rounded-2xl max-w-4xl w-[calc(100%-2rem)]">
132
+ <div class="px-6 h-12 flex items-center justify-between">
133
+ <a href="#" class="text-base font-semibold tracking-tight flex items-center gap-2 text-apple-gray-900 no-underline">
134
+ 🧭 Compass
135
+ </a>
136
+ <div class="flex items-center gap-6">
137
+ <a href="#features" class="hidden sm:block text-sm text-apple-gray-600 hover:text-apple-gray-900 no-underline transition-colors">Features</a>
138
+ <a href="#screenshots" class="hidden sm:block text-sm text-apple-gray-600 hover:text-apple-gray-900 no-underline transition-colors">Screenshots</a>
139
+ <a href="#setup" class="hidden sm:block text-sm text-apple-gray-600 hover:text-apple-gray-900 no-underline transition-colors">Setup</a>
140
+ <a href="https://www.npmjs.com/package/compass-agent" class="hidden sm:block text-sm text-apple-gray-600 hover:text-apple-gray-900 no-underline transition-colors">npm</a>
141
+ <a href="https://github.com/raja-jamwal/compass" class="inline-flex items-center gap-1.5 text-sm font-medium text-white bg-apple-gray-900 hover:bg-apple-gray-800 px-4 py-1.5 rounded-full no-underline transition-colors">
142
+ <svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
143
+ GitHub
144
+ </a>
145
+ </div>
146
+ </div>
147
+ </nav>
148
+
149
+ <!-- Hero -->
150
+ <section class="hero-gradient pt-32 pb-20 sm:pt-40 sm:pb-28">
151
+ <div class="max-w-4xl mx-auto px-6 text-center">
152
+ <div class="flex gap-2 justify-center mb-8 flex-wrap">
153
+ <a href="https://github.com/raja-jamwal/compass/actions/workflows/test.yml"><img src="https://github.com/raja-jamwal/compass/actions/workflows/test.yml/badge.svg" alt="Tests"></a>
154
+ <a href="https://github.com/raja-jamwal/compass/actions/workflows/publish.yml"><img src="https://github.com/raja-jamwal/compass/actions/workflows/publish.yml/badge.svg" alt="Publish"></a>
155
+ <a href="https://www.npmjs.com/package/compass-agent"><img src="https://img.shields.io/npm/v/compass-agent" alt="npm version"></a>
156
+ </div>
157
+
158
+ <h1 class="text-5xl sm:text-7xl font-extrabold tracking-tight leading-[1.05] mb-6">
159
+ Bring your workforce of<br>
160
+ <span class="text-gradient">Claude Code to Slack</span>
161
+ </h1>
162
+
163
+ <p class="text-lg sm:text-xl text-apple-gray-500 max-w-2xl mx-auto mb-10 leading-relaxed font-light">
164
+ Every thread becomes an isolated coding session — with its own working directory, git worktree, and full local filesystem access. Your whole team, working in parallel, zero conflicts.
165
+ </p>
166
+
167
+ <div class="flex flex-col sm:flex-row gap-3 justify-center items-center mb-16">
168
+ <div id="install-cmd" class="install-box rounded-xl px-5 py-3 flex items-center gap-3 cursor-pointer select-none">
169
+ <span class="text-apple-gray-500 font-mono text-sm select-none">$</span>
170
+ <span class="text-green-400 font-mono text-sm sm:text-base">bunx compass-agent</span>
171
+ <span id="copy-icon" class="text-apple-gray-500 text-xs ml-2 transition-all">
172
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
173
+ </span>
174
+ </div>
175
+ <a href="https://github.com/raja-jamwal/compass" class="inline-flex items-center gap-2 bg-apple-blue hover:bg-blue-600 text-white font-semibold text-sm px-7 py-3 rounded-full no-underline transition-colors shadow-lg shadow-blue-500/20">
176
+ View on GitHub
177
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path d="M5 12h14m-7-7l7 7-7 7"/></svg>
178
+ </a>
179
+ <a href="https://www.youtube.com/watch?v=EH_9WAe-PZA" class="inline-flex items-center gap-2 bg-red-600 hover:bg-red-700 text-white font-semibold text-sm px-7 py-3 rounded-full no-underline transition-colors shadow-lg shadow-red-500/20">
180
+ <svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M23.498 6.186a3.016 3.016 0 00-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 00.502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 002.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 002.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>
181
+ Watch Demo
182
+ </a>
183
+ </div>
184
+
185
+ <div class="relative max-w-4xl mx-auto">
186
+ <div class="absolute -inset-4 bg-gradient-to-b from-apple-blue/5 to-apple-purple/5 rounded-3xl blur-2xl"></div>
187
+ <div class="relative rounded-2xl border border-apple-gray-200 screenshot-shadow overflow-hidden" style="aspect-ratio: 16/9;">
188
+ <iframe class="w-full h-full" src="https://www.youtube.com/embed/EH_9WAe-PZA" title="Compass Demo" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
189
+ </div>
190
+ </div>
191
+ </div>
192
+ </section>
193
+
194
+ <!-- How it works -->
195
+ <section id="how-it-works" class="py-24 bg-white section-fade">
196
+ <div class="max-w-5xl mx-auto px-6">
197
+ <div class="text-center mb-16">
198
+ <p class="text-sm font-semibold text-apple-blue uppercase tracking-wider mb-3">How it works</p>
199
+ <h2 class="text-3xl sm:text-4xl font-bold tracking-tight mb-4">Local-first, zero infrastructure</h2>
200
+ <p class="text-lg text-apple-gray-500 max-w-xl mx-auto font-light">Runs on your machine using Slack's Socket Mode. No servers to deploy, no URLs to expose.</p>
201
+ </div>
202
+
203
+ <div class="flex items-center justify-center flex-wrap gap-4">
204
+ <div class="card-glass border border-apple-gray-200 rounded-2xl p-6 text-center min-w-[150px] feature-card-hover">
205
+ <div class="text-3xl mb-2">💬</div>
206
+ <div class="font-semibold text-sm">Slack Thread</div>
207
+ <div class="text-xs text-apple-gray-500 mt-1">You send a message</div>
208
+ </div>
209
+ <div class="flow-connector hidden sm:block"></div>
210
+ <div class="card-glass border border-apple-gray-200 rounded-2xl p-6 text-center min-w-[150px] feature-card-hover">
211
+ <div class="text-3xl mb-2">🔌</div>
212
+ <div class="font-semibold text-sm">Socket Mode</div>
213
+ <div class="text-xs text-apple-gray-500 mt-1">Bot receives locally</div>
214
+ </div>
215
+ <div class="flow-connector hidden sm:block"></div>
216
+ <div class="card-glass border border-apple-gray-200 rounded-2xl p-6 text-center min-w-[150px] feature-card-hover">
217
+ <div class="text-3xl mb-2">🤖</div>
218
+ <div class="font-semibold text-sm">Claude CLI</div>
219
+ <div class="text-xs text-apple-gray-500 mt-1">Spawns a session</div>
220
+ </div>
221
+ <div class="flow-connector hidden sm:block"></div>
222
+ <div class="card-glass border border-apple-gray-200 rounded-2xl p-6 text-center min-w-[150px] feature-card-hover">
223
+ <div class="text-3xl mb-2">📂</div>
224
+ <div class="font-semibold text-sm">Filesystem</div>
225
+ <div class="text-xs text-apple-gray-500 mt-1">Reads & writes files</div>
226
+ </div>
227
+ </div>
228
+ </div>
229
+ </section>
230
+
231
+ <!-- Features -->
232
+ <section id="features" class="py-24 bg-apple-gray-50 section-fade">
233
+ <div class="max-w-5xl mx-auto px-6">
234
+ <div class="text-center mb-16">
235
+ <p class="text-sm font-semibold text-apple-indigo uppercase tracking-wider mb-3">Features</p>
236
+ <h2 class="text-3xl sm:text-4xl font-bold tracking-tight mb-4">Everything your team needs</h2>
237
+ <p class="text-lg text-apple-gray-500 max-w-xl mx-auto font-light">Built for real development workflows, not demos.</p>
238
+ </div>
239
+
240
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
241
+ <div class="bg-white border border-apple-gray-200 rounded-2xl p-7 feature-card-hover">
242
+ <div class="w-10 h-10 rounded-xl bg-blue-50 flex items-center justify-center text-xl mb-4">🧵</div>
243
+ <h3 class="text-base font-semibold mb-2">Per-thread sessions</h3>
244
+ <p class="text-sm text-apple-gray-500 leading-relaxed">Every thread is an independent Claude session. Follow-up messages resume with full context using <code class="text-xs bg-apple-gray-100 text-apple-indigo px-1.5 py-0.5 rounded font-mono">--resume</code>.</p>
245
+ </div>
246
+
247
+ <div class="bg-white border border-apple-gray-200 rounded-2xl p-7 feature-card-hover">
248
+ <div class="w-10 h-10 rounded-xl bg-indigo-50 flex items-center justify-center text-xl mb-4">📁</div>
249
+ <h3 class="text-base font-semibold mb-2"><code class="text-sm bg-apple-gray-100 text-apple-indigo px-1.5 py-0.5 rounded font-mono">$cwd</code> Working directory</h3>
250
+ <p class="text-sm text-apple-gray-500 leading-relaxed">Set the directory Claude works in. Interactive picker remembers recent directories, or set directly with a path.</p>
251
+ </div>
252
+
253
+ <div class="bg-white border border-apple-gray-200 rounded-2xl p-7 feature-card-hover">
254
+ <div class="w-10 h-10 rounded-xl bg-purple-50 flex items-center justify-center text-xl mb-4">🎓</div>
255
+ <h3 class="text-base font-semibold mb-2"><code class="text-sm bg-apple-gray-100 text-apple-indigo px-1.5 py-0.5 rounded font-mono">$teach</code> Team knowledge</h3>
256
+ <p class="text-sm text-apple-gray-500 leading-relaxed">Store conventions injected into every session. "Use TypeScript", "Always write tests" — shared across your workspace.</p>
257
+ </div>
258
+
259
+ <div class="bg-white border border-apple-gray-200 rounded-2xl p-7 feature-card-hover">
260
+ <div class="w-10 h-10 rounded-xl bg-orange-50 flex items-center justify-center text-xl mb-4">⚡</div>
261
+ <h3 class="text-base font-semibold mb-2">Streaming responses</h3>
262
+ <p class="text-sm text-apple-gray-500 leading-relaxed">Token-by-token streaming via Slack's native <code class="text-xs bg-apple-gray-100 text-apple-indigo px-1.5 py-0.5 rounded font-mono">chatStream</code> API. Tool calls render as a live agentic timeline.</p>
263
+ </div>
264
+
265
+ <div class="bg-white border border-apple-gray-200 rounded-2xl p-7 feature-card-hover">
266
+ <div class="w-10 h-10 rounded-xl bg-green-50 flex items-center justify-center text-xl mb-4">🌳</div>
267
+ <h3 class="text-base font-semibold mb-2">Git worktree isolation</h3>
268
+ <p class="text-sm text-apple-gray-500 leading-relaxed">Each thread gets its own worktree and branch. Parallel sessions make code changes without conflicts. Auto-cleanup after 24h.</p>
269
+ </div>
270
+
271
+ <div class="bg-white border border-apple-gray-200 rounded-2xl p-7 feature-card-hover">
272
+ <div class="w-10 h-10 rounded-xl bg-pink-50 flex items-center justify-center text-xl mb-4">📊</div>
273
+ <h3 class="text-base font-semibold mb-2">Dashboard & usage tracking</h3>
274
+ <p class="text-sm text-apple-gray-500 leading-relaxed">App Home shows live stats, recent sessions, cost tracking per invocation. Stop button on every response.</p>
275
+ </div>
276
+ </div>
277
+ </div>
278
+ </section>
279
+
280
+ <!-- Screenshots -->
281
+ <section id="screenshots" class="py-24 bg-white section-fade">
282
+ <div class="max-w-5xl mx-auto px-6">
283
+ <div class="text-center mb-16">
284
+ <p class="text-sm font-semibold text-apple-purple uppercase tracking-wider mb-3">In action</p>
285
+ <h2 class="text-3xl sm:text-4xl font-bold tracking-tight mb-4">See it working</h2>
286
+ </div>
287
+
288
+ <div class="space-y-24">
289
+ <div class="flex flex-col md:flex-row items-center gap-12 screenshot-row">
290
+ <div class="flex-1">
291
+ <div class="relative">
292
+ <div class="absolute -inset-3 bg-gradient-to-br from-blue-100/40 to-indigo-100/40 rounded-3xl blur-xl"></div>
293
+ <img src="assets/streaming-feedback.png" alt="Working directory set in Slack" class="relative w-full rounded-2xl border border-apple-gray-200 screenshot-shadow">
294
+ </div>
295
+ </div>
296
+ <div class="flex-1 max-w-md">
297
+ <div class="w-10 h-10 rounded-xl bg-blue-50 flex items-center justify-center text-xl mb-4">📁</div>
298
+ <h3 class="text-2xl font-bold mb-3 tracking-tight">Set a directory, start coding</h3>
299
+ <p class="text-apple-gray-500 leading-relaxed">Point Claude at any project on your machine. The working directory is stored per-thread, so each conversation is scoped to its own codebase.</p>
300
+ </div>
301
+ </div>
302
+
303
+ <div class="flex flex-col md:flex-row-reverse items-center gap-12 screenshot-row">
304
+ <div class="flex-1">
305
+ <div class="relative">
306
+ <div class="absolute -inset-3 bg-gradient-to-br from-purple-100/40 to-pink-100/40 rounded-3xl blur-xl"></div>
307
+ <img src="assets/planning-streaming.png" alt="Streaming with planning and sub-agents" class="relative w-full rounded-2xl border border-apple-gray-200 screenshot-shadow">
308
+ </div>
309
+ </div>
310
+ <div class="flex-1 max-w-md">
311
+ <div class="w-10 h-10 rounded-xl bg-purple-50 flex items-center justify-center text-xl mb-4">⚡</div>
312
+ <h3 class="text-2xl font-bold mb-3 tracking-tight">Agentic tool timeline</h3>
313
+ <p class="text-apple-gray-500 leading-relaxed">Watch Claude think, plan, and execute in real-time. Every tool call — file reads, edits, shell commands, sub-agents — renders as a live progress step.</p>
314
+ </div>
315
+ </div>
316
+ </div>
317
+ </div>
318
+ </section>
319
+
320
+ <!-- Setup -->
321
+ <section id="setup" class="py-24 bg-apple-gray-50 section-fade">
322
+ <div class="max-w-3xl mx-auto px-6">
323
+ <div class="text-center mb-16">
324
+ <p class="text-sm font-semibold text-apple-blue uppercase tracking-wider mb-3">Get started</p>
325
+ <h2 class="text-3xl sm:text-4xl font-bold tracking-tight mb-4">Up and running in 5 minutes</h2>
326
+ <p class="text-lg text-apple-gray-500 max-w-xl mx-auto font-light">Prerequisites: <a href="https://bun.sh" class="text-apple-blue hover:underline">Bun</a>, <a href="https://docs.anthropic.com/en/docs/claude-code" class="text-apple-blue hover:underline">Claude CLI</a>, and a Slack workspace.</p>
327
+ </div>
328
+
329
+ <div class="space-y-4">
330
+ <div class="bg-white border border-apple-gray-200 rounded-2xl p-7 pl-20 relative feature-card-hover">
331
+ <div class="step-number absolute left-6 top-7 w-9 h-9 rounded-xl text-white flex items-center justify-center font-bold text-sm">1</div>
332
+ <h3 class="text-base font-semibold mb-2">Create the Slack app</h3>
333
+ <p class="text-sm text-apple-gray-500 leading-relaxed">Go to <a href="https://api.slack.com/apps" class="text-apple-blue hover:underline">api.slack.com/apps</a>, create from manifest using the included <code class="text-xs bg-apple-gray-100 text-apple-indigo px-1.5 py-0.5 rounded font-mono">manifest.yml</code>, and install to your workspace.</p>
334
+ </div>
335
+
336
+ <div class="bg-white border border-apple-gray-200 rounded-2xl p-7 pl-20 relative feature-card-hover">
337
+ <div class="step-number absolute left-6 top-7 w-9 h-9 rounded-xl text-white flex items-center justify-center font-bold text-sm">2</div>
338
+ <h3 class="text-base font-semibold mb-2">Configure environment</h3>
339
+ <p class="text-sm text-apple-gray-500 leading-relaxed mb-3">Store your tokens in <code class="text-xs bg-apple-gray-100 text-apple-indigo px-1.5 py-0.5 rounded font-mono">~/.compass/.env</code></p>
340
+ <div class="bg-apple-gray-900 rounded-xl p-4 overflow-x-auto">
341
+ <pre class="text-sm font-mono text-green-400 leading-relaxed">mkdir -p ~/.compass
342
+ cat > ~/.compass/.env &lt;&lt; 'EOF'
343
+ SLACK_APP_TOKEN=xapp-1-...
344
+ SLACK_BOT_TOKEN=xoxb-...
345
+ EOF</pre>
346
+ </div>
347
+ </div>
348
+
349
+ <div class="bg-white border border-apple-gray-200 rounded-2xl p-7 pl-20 relative feature-card-hover">
350
+ <div class="step-number absolute left-6 top-7 w-9 h-9 rounded-xl text-white flex items-center justify-center font-bold text-sm">3</div>
351
+ <h3 class="text-base font-semibold mb-2">Run</h3>
352
+ <p class="text-sm text-apple-gray-500 leading-relaxed mb-3">One command. No servers, no ngrok.</p>
353
+ <div class="bg-apple-gray-900 rounded-xl p-4">
354
+ <pre class="text-sm font-mono text-green-400">bunx compass-agent</pre>
355
+ </div>
356
+ </div>
357
+
358
+ <div class="bg-white border border-apple-gray-200 rounded-2xl p-7 pl-20 relative feature-card-hover">
359
+ <div class="step-number absolute left-6 top-7 w-9 h-9 rounded-xl text-white flex items-center justify-center font-bold text-sm">4</div>
360
+ <h3 class="text-base font-semibold mb-2">Verify</h3>
361
+ <p class="text-sm text-apple-gray-500 leading-relaxed">Open the app in Slack, go to the Home tab, start a thread, set <code class="text-xs bg-apple-gray-100 text-apple-indigo px-1.5 py-0.5 rounded font-mono">$cwd</code>, and ask a question.</p>
362
+ </div>
363
+ </div>
364
+ </div>
365
+ </section>
366
+
367
+ <!-- Configuration -->
368
+ <section id="config" class="py-24 bg-white section-fade">
369
+ <div class="max-w-3xl mx-auto px-6">
370
+ <div class="text-center mb-16">
371
+ <p class="text-sm font-semibold text-apple-blue uppercase tracking-wider mb-3">Configuration</p>
372
+ <h2 class="text-3xl sm:text-4xl font-bold tracking-tight mb-4">Environment variables</h2>
373
+ </div>
374
+
375
+ <div class="bg-white border border-apple-gray-200 rounded-2xl overflow-hidden">
376
+ <table class="w-full text-sm">
377
+ <thead>
378
+ <tr class="border-b border-apple-gray-200 bg-apple-gray-50">
379
+ <th class="text-left px-6 py-3 text-xs font-semibold text-apple-gray-500 uppercase tracking-wider">Variable</th>
380
+ <th class="text-left px-4 py-3 text-xs font-semibold text-apple-gray-500 uppercase tracking-wider w-20"></th>
381
+ <th class="text-left px-6 py-3 text-xs font-semibold text-apple-gray-500 uppercase tracking-wider">Description</th>
382
+ </tr>
383
+ </thead>
384
+ <tbody class="divide-y divide-apple-gray-100">
385
+ <tr class="config-row">
386
+ <td class="px-6 py-4"><code class="text-xs bg-apple-gray-100 text-apple-indigo px-1.5 py-0.5 rounded font-mono">SLACK_APP_TOKEN</code></td>
387
+ <td class="px-4 py-4"><span class="text-xs font-semibold text-orange-500">Required</span></td>
388
+ <td class="px-6 py-4 text-apple-gray-500">App-level token for Socket Mode</td>
389
+ </tr>
390
+ <tr class="config-row">
391
+ <td class="px-6 py-4"><code class="text-xs bg-apple-gray-100 text-apple-indigo px-1.5 py-0.5 rounded font-mono">SLACK_BOT_TOKEN</code></td>
392
+ <td class="px-4 py-4"><span class="text-xs font-semibold text-orange-500">Required</span></td>
393
+ <td class="px-6 py-4 text-apple-gray-500">Bot user OAuth token</td>
394
+ </tr>
395
+ <tr class="config-row">
396
+ <td class="px-6 py-4"><code class="text-xs bg-apple-gray-100 text-apple-indigo px-1.5 py-0.5 rounded font-mono">ALLOWED_USERS</code></td>
397
+ <td class="px-4 py-4"><span class="text-xs text-apple-gray-400">Optional</span></td>
398
+ <td class="px-6 py-4 text-apple-gray-500">Comma-separated Slack user IDs to whitelist</td>
399
+ </tr>
400
+ <tr class="config-row">
401
+ <td class="px-6 py-4"><code class="text-xs bg-apple-gray-100 text-apple-indigo px-1.5 py-0.5 rounded font-mono">CLAUDE_PATH</code></td>
402
+ <td class="px-4 py-4"><span class="text-xs text-apple-gray-400">Optional</span></td>
403
+ <td class="px-6 py-4 text-apple-gray-500">Path to <code class="text-xs bg-apple-gray-100 text-apple-indigo px-1 rounded font-mono">claude</code> binary</td>
404
+ </tr>
405
+ <tr class="config-row">
406
+ <td class="px-6 py-4"><code class="text-xs bg-apple-gray-100 text-apple-indigo px-1.5 py-0.5 rounded font-mono">CLAUDE_ADDITIONAL_ARGS</code></td>
407
+ <td class="px-4 py-4"><span class="text-xs text-apple-gray-400">Optional</span></td>
408
+ <td class="px-6 py-4 text-apple-gray-500">Extra CLI args for every invocation</td>
409
+ </tr>
410
+ <tr class="config-row">
411
+ <td class="px-6 py-4"><code class="text-xs bg-apple-gray-100 text-apple-indigo px-1.5 py-0.5 rounded font-mono">ENV_*</code></td>
412
+ <td class="px-4 py-4"><span class="text-xs text-apple-gray-400">Optional</span></td>
413
+ <td class="px-6 py-4 text-apple-gray-500">Injected into Claude's environment (e.g. <code class="text-xs bg-apple-gray-100 text-apple-indigo px-1 rounded font-mono">ENV_ANTHROPIC_API_KEY</code>)</td>
414
+ </tr>
415
+ </tbody>
416
+ </table>
417
+ </div>
418
+ </div>
419
+ </section>
420
+
421
+ <!-- Footer -->
422
+ <footer class="border-t border-apple-gray-200 py-12 bg-apple-gray-50">
423
+ <div class="max-w-5xl mx-auto px-6 text-center">
424
+ <div class="flex gap-8 justify-center mb-6">
425
+ <a href="https://github.com/raja-jamwal/compass" class="text-sm text-apple-gray-500 hover:text-apple-gray-900 no-underline transition-colors">GitHub</a>
426
+ <a href="https://www.npmjs.com/package/compass-agent" class="text-sm text-apple-gray-500 hover:text-apple-gray-900 no-underline transition-colors">npm</a>
427
+ <a href="https://github.com/raja-jamwal/compass/issues" class="text-sm text-apple-gray-500 hover:text-apple-gray-900 no-underline transition-colors">Issues</a>
428
+ </div>
429
+ <p class="text-sm text-apple-gray-400">MIT License</p>
430
+ </div>
431
+ </footer>
432
+
433
+ <script>
434
+ // Copy install command
435
+ $('#install-cmd').on('click', function() {
436
+ navigator.clipboard.writeText('bunx compass-agent');
437
+ const $icon = $('#copy-icon');
438
+ $icon.html('<svg class="w-4 h-4 text-green-400" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path d="M5 13l4 4L19 7"/></svg>');
439
+ setTimeout(() => {
440
+ $icon.html('<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>');
441
+ }, 2000);
442
+ });
443
+
444
+ // Smooth scroll
445
+ $('a[href^="#"]').on('click', function(e) {
446
+ e.preventDefault();
447
+ const target = $($(this).attr('href'));
448
+ if (target.length) {
449
+ $('html, body').animate({ scrollTop: target.offset().top - 48 }, 500);
450
+ }
451
+ });
452
+
453
+ // Scroll fade-in
454
+ const observer = new IntersectionObserver((entries) => {
455
+ entries.forEach(entry => {
456
+ if (entry.isIntersecting) {
457
+ entry.target.classList.add('visible');
458
+ }
459
+ });
460
+ }, { threshold: 0.1 });
461
+
462
+ $('.section-fade').each(function() {
463
+ observer.observe(this);
464
+ });
465
+ </script>
466
+
467
+ </body>
468
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compass-agent",
3
- "version": "2.0.6",
3
+ "version": "2.0.8",
4
4
  "type": "module",
5
5
  "description": "Bring Claude Code to your Slack workspace — per-thread AI coding sessions with full local filesystem access",
6
6
  "bin": {
package/src/app.ts CHANGED
@@ -33,6 +33,26 @@ if (envFileIdx !== -1 && process.argv[envFileIdx + 1]) {
33
33
  Object.assign(process.env, realEnv);
34
34
  // -----------------------------------------------------------------------------------------
35
35
 
36
+ // --- Required env validation ---
37
+ const requiredEnv = ["SLACK_BOT_TOKEN", "SLACK_APP_TOKEN"] as const;
38
+ const missing = requiredEnv.filter((k) => !process.env[k]);
39
+ if (missing.length > 0) {
40
+ console.error(`ERROR: Missing required environment variable(s): ${missing.join(", ")}`);
41
+ process.exit(1);
42
+ }
43
+
44
+ // Validate CLAUDE_PATH exists
45
+ const claudePath = process.env.CLAUDE_PATH || "claude";
46
+ if (path.isAbsolute(claudePath)) {
47
+ if (!fs.existsSync(claudePath)) {
48
+ console.error(`ERROR: CLAUDE_PATH not found: ${claudePath}`);
49
+ process.exit(1);
50
+ }
51
+ } else if (!Bun.which(claudePath)) {
52
+ console.error(`ERROR: CLAUDE_PATH "${claudePath}" not found in PATH. Set CLAUDE_PATH in .env to the full path of the claude binary.`);
53
+ process.exit(1);
54
+ }
55
+
36
56
  import { App } from "@slack/bolt";
37
57
  import { execFileSync } from "child_process";
38
58
  import {
@@ -219,8 +239,6 @@ app.command("/cwd", async ({ command, ack, client }: any) => {
219
239
  });
220
240
  });
221
241
 
222
- const CLAUDE_PATH = process.env.CLAUDE_PATH || "claude";
223
-
224
242
  // ── Modal submissions ───────────────────────────────────────
225
243
 
226
244
  app.view("cwd_modal", async ({ view, ack, client }: any) => {
@@ -647,18 +665,6 @@ async function processMessage({ channelId, threadTs, messageTs, userText, userId
647
665
  // ── Startup ─────────────────────────────────────────────────
648
666
 
649
667
  (async () => {
650
- // Validate CLAUDE_PATH exists before starting
651
- const claudePath = process.env.CLAUDE_PATH || "claude";
652
- // Check if it's an absolute path that exists, or resolve via Bun.which
653
- if (path.isAbsolute(claudePath)) {
654
- if (!fs.existsSync(claudePath)) {
655
- console.error(`ERROR: CLAUDE_PATH not found: ${claudePath}`);
656
- process.exit(1);
657
- }
658
- } else if (!Bun.which(claudePath)) {
659
- console.error(`ERROR: CLAUDE_PATH "${claudePath}" not found in PATH. Set CLAUDE_PATH in .env to the full path of the claude binary.`);
660
- process.exit(1);
661
- }
662
668
  log(null, `Claude CLI found: ${claudePath}`);
663
669
 
664
670
  // Register MCP server (idempotent — overwrites if already exists)
File without changes
File without changes
File without changes