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 +3 -0
- package/.github/workflows/publish.yml +1 -2
- package/.github/workflows/test.yml +19 -0
- package/README.md +71 -146
- package/docs/index.html +468 -0
- package/package.json +1 -1
- package/src/app.ts +20 -14
- /package/{assets → docs/assets}/planning-streaming.png +0 -0
- /package/{assets → docs/assets}/streaming-feedback.png +0 -0
- /package/{assets → docs/assets}/sub-agent.png +0 -0
package/.env.example
ADDED
|
@@ -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
|
-
|
|
3
|
+
[](https://github.com/raja-jamwal/compass/actions/workflows/test.yml)
|
|
4
|
+
[](https://github.com/raja-jamwal/compass/actions/workflows/publish.yml)
|
|
5
|
+
[](https://www.npmjs.com/package/compass-agent)
|
|
4
6
|
|
|
5
|
-
|
|
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
|
-
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
bunx compass-agent
|
|
15
|
+
```
|
|
8
16
|
|
|
9
|
-
|
|
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,
|
|
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
|
|
31
|
+
Every thread is an independent Claude session. Subsequent messages in the same thread resume the conversation with full context.
|
|
22
32
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-

|
|
48
|
+

|
|
40
49
|
|
|
41
50
|
### `$teach` — Team knowledge base
|
|
42
51
|
|
|
43
|
-
Store team conventions
|
|
52
|
+
Store team conventions that get injected into every Claude session across your workspace.
|
|
44
53
|
|
|
45
54
|
| Command | Description |
|
|
46
55
|
|---------|-------------|
|
|
47
|
-
| `$teach
|
|
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>` |
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+

|
|
90
81
|
|
|
91
82
|
### Git worktree isolation
|
|
92
83
|
|
|
93
|
-
When the CWD is inside a git
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
94
|
+
### More features
|
|
116
95
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
ALLOWED_USERS
|
|
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
|
|
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
|
-
####
|
|
173
|
-
|
|
174
|
-
If you prefer to run from source:
|
|
146
|
+
#### Running from source
|
|
175
147
|
|
|
176
148
|
```bash
|
|
177
|
-
git clone https://github.com/
|
|
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
|
|
207
|
-
types.ts Shared TypeScript interfaces
|
|
176
|
+
db.ts SQLite schema and typed prepared statements (bun:sqlite)
|
|
177
|
+
types.ts Shared TypeScript interfaces
|
|
208
178
|
handlers/
|
|
209
|
-
assistant.ts
|
|
210
|
-
stream.ts Claude CLI streaming — NDJSON parsing,
|
|
179
|
+
assistant.ts Thread lifecycle — session 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,
|
|
182
|
+
blocks.ts Block Kit builders — dashboard, stop button, feedback, prompts
|
|
213
183
|
lib/
|
|
214
|
-
log.ts
|
|
215
|
-
worktree.ts Git worktree lifecycle
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
|
286
|
-
| `ENV_*` | No | Variables prefixed with `ENV_` are injected into
|
|
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`) |
|
package/docs/index.html
ADDED
|
@@ -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 << '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
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
|