claude-slack-channel-bots 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/setup-slack-channel-bots/SKILL.md +5 -2
- package/src/server.ts +9 -3
- package/src/session-manager.ts +2 -2
- package/src/tmux.ts +14 -3
package/package.json
CHANGED
|
@@ -331,8 +331,11 @@ Look for an entry inside `hooks.PreToolUse` with:
|
|
|
331
331
|
{ "matcher": "AskUserQuestion", "hooks": [{ "type": "command", "command": "~/.claude/hooks/ask-relay.sh" }] }
|
|
332
332
|
```
|
|
333
333
|
|
|
334
|
-
**If either entry is missing
|
|
335
|
-
the
|
|
334
|
+
**If either entry is missing OR exists but is missing `"timeout": 2000000`**,
|
|
335
|
+
show the user the exact JSON to add or fix. The timeout is critical — without
|
|
336
|
+
it Claude Code uses a short default timeout, kills the hook before the
|
|
337
|
+
long-poll completes, and falls back to TUI approval. This is the complete
|
|
338
|
+
block for both entries:
|
|
336
339
|
|
|
337
340
|
```jsonc
|
|
338
341
|
"PermissionRequest": [
|
package/src/server.ts
CHANGED
|
@@ -1297,13 +1297,17 @@ export async function main(): Promise<void> {
|
|
|
1297
1297
|
// Initialize restart module with adapters bridging tmux + session-manager
|
|
1298
1298
|
initRestart({
|
|
1299
1299
|
isSessionAlive: async (channelId) => {
|
|
1300
|
-
const
|
|
1300
|
+
const cwd = routingConfig?.routes[channelId]?.cwd
|
|
1301
|
+
if (!cwd) return false
|
|
1302
|
+
const name = sessionName(cwd)
|
|
1301
1303
|
const exists = await defaultTmuxClient.hasSession(name)
|
|
1302
1304
|
if (!exists) return false
|
|
1303
1305
|
return isClaudeRunning(name, defaultTmuxClient)
|
|
1304
1306
|
},
|
|
1305
1307
|
killSession: async (channelId) => {
|
|
1306
|
-
const
|
|
1308
|
+
const cwd = routingConfig?.routes[channelId]?.cwd
|
|
1309
|
+
if (!cwd) return
|
|
1310
|
+
const name = sessionName(cwd)
|
|
1307
1311
|
const exists = await defaultTmuxClient.hasSession(name)
|
|
1308
1312
|
if (exists) await defaultTmuxClient.killSession(name)
|
|
1309
1313
|
},
|
|
@@ -1327,7 +1331,9 @@ export async function main(): Promise<void> {
|
|
|
1327
1331
|
|
|
1328
1332
|
// Initialize and start the health-check poller.
|
|
1329
1333
|
const isSessionAliveAdapter = async (channelId: string): Promise<boolean> => {
|
|
1330
|
-
const
|
|
1334
|
+
const cwd = routingConfig?.routes[channelId]?.cwd
|
|
1335
|
+
if (!cwd) return false
|
|
1336
|
+
const name = sessionName(cwd)
|
|
1331
1337
|
const exists = await defaultTmuxClient.hasSession(name)
|
|
1332
1338
|
if (!exists) return false
|
|
1333
1339
|
return isClaudeRunning(name, defaultTmuxClient)
|
package/src/session-manager.ts
CHANGED
|
@@ -47,7 +47,7 @@ export async function launchSession(
|
|
|
47
47
|
writeSessionsFn: (sessions: SessionsMap, path?: string) => void,
|
|
48
48
|
options?: LaunchOptions,
|
|
49
49
|
): Promise<boolean> {
|
|
50
|
-
const name = sessionName(
|
|
50
|
+
const name = sessionName(cwd)
|
|
51
51
|
const pollTimeout = options?.pollTimeout ?? 60_000
|
|
52
52
|
|
|
53
53
|
// Create detached tmux session with the channel's CWD
|
|
@@ -139,7 +139,7 @@ export async function startupSessionManager(
|
|
|
139
139
|
const results: SessionStateResult[] = []
|
|
140
140
|
|
|
141
141
|
for (const [channelId, route] of Object.entries(routingConfig.routes)) {
|
|
142
|
-
const name = sessionName(
|
|
142
|
+
const name = sessionName(route.cwd)
|
|
143
143
|
|
|
144
144
|
try {
|
|
145
145
|
const exists = await tmuxClient.hasSession(name)
|
package/src/tmux.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { $ } from 'bun'
|
|
8
|
+
import { homedir } from 'os'
|
|
8
9
|
|
|
9
10
|
// ---------------------------------------------------------------------------
|
|
10
11
|
// Interface
|
|
@@ -31,9 +32,19 @@ export interface TmuxClient {
|
|
|
31
32
|
// Helpers
|
|
32
33
|
// ---------------------------------------------------------------------------
|
|
33
34
|
|
|
34
|
-
/** Returns the canonical tmux session name for a given
|
|
35
|
-
export function sessionName(
|
|
36
|
-
|
|
35
|
+
/** Returns the canonical tmux session name for a given working directory path. */
|
|
36
|
+
export function sessionName(cwd: string): string {
|
|
37
|
+
const expanded = cwd.startsWith('~') ? homedir() + cwd.slice(1) : cwd
|
|
38
|
+
const normalized = expanded.replace(/[^a-zA-Z0-9_]/g, '_')
|
|
39
|
+
let stripped = normalized.replace(/^_+/, '')
|
|
40
|
+
const MAX_STRIPPED = 239 // 255 - len('slack_bot_') - len('_<6-char-hash>')
|
|
41
|
+
if (stripped.length > MAX_STRIPPED) {
|
|
42
|
+
stripped = stripped.slice(stripped.length - MAX_STRIPPED).replace(/^_+/, '')
|
|
43
|
+
}
|
|
44
|
+
const hasher = new Bun.CryptoHasher('md5')
|
|
45
|
+
hasher.update(expanded)
|
|
46
|
+
const hash = hasher.digest('hex').slice(0, 6)
|
|
47
|
+
return `slack_bot_${stripped}_${hash}`
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
// ---------------------------------------------------------------------------
|