-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathcommands.go
More file actions
112 lines (100 loc) · 3.5 KB
/
commands.go
File metadata and controls
112 lines (100 loc) · 3.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package slagent
import (
"fmt"
"strings"
)
// handleCommand processes /open, /lock, /close, /observe, and /help commands.
// Returns (handled, feedback): handled is true for known commands,
// feedback is a status message to post in the thread.
func (t *Thread) handleCommand(userID, cmd string) (bool, string) {
cmd = strings.TrimSpace(cmd)
if strings.HasPrefix(cmd, "/help") {
return true, t.helpText()
}
handled, feedback := t.HandleCommand(userID, cmd)
if !handled {
return false, ""
}
// Update thread parent to reflect new access state (only if we created the thread)
if !t.joined {
t.updateTitle()
}
return true, feedback
}
// parseInstancePrefix checks if text starts with a :shortcode: prefix targeting an instance.
// Accepts any combination of trailing colons and spaces after the shortcode:
// - ":fox_face:: msg" — double colon
// - ":fox_face: msg" — single colon (space)
// - ":fox_face: : msg" — single colon, space, colon
// - ":fox_face:msg" — no space
//
// Returns the shortcode (instance ID), remaining text, and whether a prefix was found.
func parseInstancePrefix(text string) (instanceID, rest string, targeted bool) {
if !strings.HasPrefix(text, ":") {
return "", text, false
}
// Find the closing colon of the shortcode
end := strings.Index(text[1:], ":")
if end < 0 {
return "", text, false
}
shortcode := text[1 : end+1]
// Verify it's a known identity emoji
if _, ok := identityEmojis[shortcode]; !ok {
return "", text, false
}
// Consume any trailing combination of spaces and colons after ":shortcode:"
rest = text[end+2:]
rest = strings.TrimLeft(rest, " :")
return shortcode, rest, true
}
// ParseMessage extracts the target instance and cleaned text from a Slack message.
// Strips leading @mentions, then checks for :shortcode: prefix.
func ParseMessage(text string) (instanceID, cleaned string, targeted bool) {
return parseMessage(text)
}
func parseMessage(text string) (instanceID, cleaned string, targeted bool) {
s := text
// Strip leading @mentions (Slack format: <@U123>)
for strings.HasPrefix(s, "<@") {
if idx := strings.Index(s, ">"); idx >= 0 {
s = strings.TrimLeft(s[idx+1:], " ")
} else {
break
}
}
return parseInstancePrefix(s)
}
// helpText returns the help message for the thread.
func (t *Thread) helpText() string {
emoji := t.emoji
id := t.instanceID
return fmt.Sprintf(""+
"*slaude — thread commands*\n"+
"\n"+
"*Targeting* (type `:%s:` in Slack, renders as %s)\n"+
" `:%s: message` — address this instance\n"+
" `:%s: /command` — send command exclusively to this instance\n"+
" Messages without prefix are broadcast to all instances.\n"+
"\n"+
"*Access control* (owner only)\n"+
" `:%s: /open` — open thread for everyone\n"+
" `:%s: /open @user` — allow specific users\n"+
" `:%s: /lock` — lock to owner only\n"+
" `:%s: /lock @user` — ban specific users\n"+
" `:%s: /close` — alias for /lock\n"+
" `:%s: /observe` — toggle observe mode (read all, respond to owner)\n"+
"\n"+
"*Session* (owner only)\n"+
" `:%s: /sandbox` — toggle sandbox on/off (restarts session)\n"+
"\n"+
"*Control*\n"+
" `stop` — interrupt current turn (all instances, anyone)\n"+
" `:%s: stop` — interrupt this instance only\n"+
" `quit` — terminate session (owner only)\n"+
" `:%s: quit` — terminate this instance only\n"+
" `:%s: /help` — show this help\n"+
" Other `/commands` are forwarded to Claude.",
id, emoji,
id, id, id, id, id, id, id, id, id, id, id, id)
}