agent-messenger 2.8.0 → 2.10.0

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.
Files changed (169) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +0 -11
  3. package/dist/package.json +1 -1
  4. package/dist/src/platforms/channeltalk/commands/snapshot.d.ts +4 -2
  5. package/dist/src/platforms/channeltalk/commands/snapshot.d.ts.map +1 -1
  6. package/dist/src/platforms/channeltalk/commands/snapshot.js +86 -31
  7. package/dist/src/platforms/channeltalk/commands/snapshot.js.map +1 -1
  8. package/dist/src/platforms/channeltalkbot/commands/snapshot.d.ts +3 -1
  9. package/dist/src/platforms/channeltalkbot/commands/snapshot.d.ts.map +1 -1
  10. package/dist/src/platforms/channeltalkbot/commands/snapshot.js +110 -60
  11. package/dist/src/platforms/channeltalkbot/commands/snapshot.js.map +1 -1
  12. package/dist/src/platforms/discord/commands/snapshot.d.ts +1 -0
  13. package/dist/src/platforms/discord/commands/snapshot.d.ts.map +1 -1
  14. package/dist/src/platforms/discord/commands/snapshot.js +48 -34
  15. package/dist/src/platforms/discord/commands/snapshot.js.map +1 -1
  16. package/dist/src/platforms/discordbot/commands/snapshot.d.ts +2 -0
  17. package/dist/src/platforms/discordbot/commands/snapshot.d.ts.map +1 -1
  18. package/dist/src/platforms/discordbot/commands/snapshot.js +46 -34
  19. package/dist/src/platforms/discordbot/commands/snapshot.js.map +1 -1
  20. package/dist/src/platforms/slack/commands/snapshot.d.ts.map +1 -1
  21. package/dist/src/platforms/slack/commands/snapshot.js +75 -55
  22. package/dist/src/platforms/slack/commands/snapshot.js.map +1 -1
  23. package/dist/src/platforms/teams/client.d.ts +9 -1
  24. package/dist/src/platforms/teams/client.d.ts.map +1 -1
  25. package/dist/src/platforms/teams/client.js +69 -18
  26. package/dist/src/platforms/teams/client.js.map +1 -1
  27. package/dist/src/platforms/teams/commands/auth.d.ts.map +1 -1
  28. package/dist/src/platforms/teams/commands/auth.js +7 -2
  29. package/dist/src/platforms/teams/commands/auth.js.map +1 -1
  30. package/dist/src/platforms/teams/commands/channel.d.ts.map +1 -1
  31. package/dist/src/platforms/teams/commands/channel.js +18 -3
  32. package/dist/src/platforms/teams/commands/channel.js.map +1 -1
  33. package/dist/src/platforms/teams/commands/file.d.ts.map +1 -1
  34. package/dist/src/platforms/teams/commands/file.js +18 -3
  35. package/dist/src/platforms/teams/commands/file.js.map +1 -1
  36. package/dist/src/platforms/teams/commands/message.d.ts.map +1 -1
  37. package/dist/src/platforms/teams/commands/message.js +24 -4
  38. package/dist/src/platforms/teams/commands/message.js.map +1 -1
  39. package/dist/src/platforms/teams/commands/reaction.d.ts.map +1 -1
  40. package/dist/src/platforms/teams/commands/reaction.js +12 -2
  41. package/dist/src/platforms/teams/commands/reaction.js.map +1 -1
  42. package/dist/src/platforms/teams/commands/snapshot.d.ts +1 -0
  43. package/dist/src/platforms/teams/commands/snapshot.d.ts.map +1 -1
  44. package/dist/src/platforms/teams/commands/snapshot.js +50 -32
  45. package/dist/src/platforms/teams/commands/snapshot.js.map +1 -1
  46. package/dist/src/platforms/teams/commands/team.d.ts.map +1 -1
  47. package/dist/src/platforms/teams/commands/team.js +6 -1
  48. package/dist/src/platforms/teams/commands/team.js.map +1 -1
  49. package/dist/src/platforms/teams/commands/user.d.ts.map +1 -1
  50. package/dist/src/platforms/teams/commands/user.js +18 -3
  51. package/dist/src/platforms/teams/commands/user.js.map +1 -1
  52. package/dist/src/platforms/teams/commands/whoami.d.ts.map +1 -1
  53. package/dist/src/platforms/teams/commands/whoami.js +6 -1
  54. package/dist/src/platforms/teams/commands/whoami.js.map +1 -1
  55. package/dist/src/platforms/teams/credential-manager.d.ts +3 -1
  56. package/dist/src/platforms/teams/credential-manager.d.ts.map +1 -1
  57. package/dist/src/platforms/teams/credential-manager.js +6 -1
  58. package/dist/src/platforms/teams/credential-manager.js.map +1 -1
  59. package/dist/src/platforms/teams/ensure-auth.d.ts.map +1 -1
  60. package/dist/src/platforms/teams/ensure-auth.js +7 -2
  61. package/dist/src/platforms/teams/ensure-auth.js.map +1 -1
  62. package/dist/src/platforms/teams/token-extractor.d.ts +3 -1
  63. package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -1
  64. package/dist/src/platforms/teams/token-extractor.js +67 -10
  65. package/dist/src/platforms/teams/token-extractor.js.map +1 -1
  66. package/dist/src/platforms/teams/types.d.ts +17 -0
  67. package/dist/src/platforms/teams/types.d.ts.map +1 -1
  68. package/dist/src/platforms/teams/types.js +2 -0
  69. package/dist/src/platforms/teams/types.js.map +1 -1
  70. package/dist/src/platforms/webex/client.d.ts +3 -0
  71. package/dist/src/platforms/webex/client.d.ts.map +1 -1
  72. package/dist/src/platforms/webex/client.js +58 -13
  73. package/dist/src/platforms/webex/client.js.map +1 -1
  74. package/dist/src/platforms/webex/commands/auth.d.ts.map +1 -1
  75. package/dist/src/platforms/webex/commands/auth.js +61 -10
  76. package/dist/src/platforms/webex/commands/auth.js.map +1 -1
  77. package/dist/src/platforms/webex/commands/snapshot.d.ts +1 -0
  78. package/dist/src/platforms/webex/commands/snapshot.d.ts.map +1 -1
  79. package/dist/src/platforms/webex/commands/snapshot.js +14 -7
  80. package/dist/src/platforms/webex/commands/snapshot.js.map +1 -1
  81. package/dist/src/platforms/webex/credential-manager.d.ts.map +1 -1
  82. package/dist/src/platforms/webex/credential-manager.js +18 -6
  83. package/dist/src/platforms/webex/credential-manager.js.map +1 -1
  84. package/dist/src/platforms/webex/encryption.d.ts.map +1 -1
  85. package/dist/src/platforms/webex/encryption.js +3 -1
  86. package/dist/src/platforms/webex/encryption.js.map +1 -1
  87. package/dist/src/platforms/webex/ensure-auth.d.ts.map +1 -1
  88. package/dist/src/platforms/webex/ensure-auth.js +10 -2
  89. package/dist/src/platforms/webex/ensure-auth.js.map +1 -1
  90. package/dist/src/platforms/webex/token-extractor.d.ts +1 -0
  91. package/dist/src/platforms/webex/token-extractor.d.ts.map +1 -1
  92. package/dist/src/platforms/webex/token-extractor.js +21 -4
  93. package/dist/src/platforms/webex/token-extractor.js.map +1 -1
  94. package/docs/content/docs/agent-skills.mdx +0 -10
  95. package/docs/content/docs/cli/channeltalk.mdx +18 -8
  96. package/docs/content/docs/cli/channeltalkbot.mdx +16 -6
  97. package/docs/content/docs/cli/discord.mdx +23 -7
  98. package/docs/content/docs/cli/discordbot.mdx +23 -7
  99. package/docs/content/docs/cli/slack.mdx +24 -7
  100. package/docs/content/docs/cli/teams.mdx +24 -8
  101. package/docs/content/docs/cli/webex.mdx +15 -2
  102. package/e2e/webex.e2e.test.ts +57 -0
  103. package/package.json +1 -1
  104. package/skills/agent-channeltalk/SKILL.md +19 -9
  105. package/skills/agent-channeltalk/references/common-patterns.md +10 -9
  106. package/skills/agent-channeltalkbot/SKILL.md +19 -9
  107. package/skills/agent-channeltalkbot/references/common-patterns.md +10 -9
  108. package/skills/agent-discord/SKILL.md +18 -9
  109. package/skills/agent-discord/references/common-patterns.md +8 -7
  110. package/skills/agent-discordbot/SKILL.md +18 -9
  111. package/skills/agent-instagram/SKILL.md +1 -1
  112. package/skills/agent-kakaotalk/SKILL.md +1 -1
  113. package/skills/agent-line/SKILL.md +1 -1
  114. package/skills/agent-slack/SKILL.md +19 -10
  115. package/skills/agent-slack/references/common-patterns.md +4 -7
  116. package/skills/agent-slackbot/SKILL.md +1 -1
  117. package/skills/agent-teams/SKILL.md +18 -9
  118. package/skills/agent-teams/references/common-patterns.md +9 -7
  119. package/skills/agent-telegram/SKILL.md +1 -1
  120. package/skills/agent-webex/SKILL.md +13 -4
  121. package/skills/agent-webex/references/common-patterns.md +8 -2
  122. package/skills/agent-wechatbot/SKILL.md +1 -1
  123. package/skills/agent-whatsapp/SKILL.md +1 -1
  124. package/skills/agent-whatsappbot/SKILL.md +1 -1
  125. package/src/platforms/channeltalk/commands/snapshot.test.ts +58 -26
  126. package/src/platforms/channeltalk/commands/snapshot.ts +107 -33
  127. package/src/platforms/channeltalkbot/commands/snapshot.test.ts +26 -8
  128. package/src/platforms/channeltalkbot/commands/snapshot.ts +131 -64
  129. package/src/platforms/discord/commands/snapshot.test.ts +1 -1
  130. package/src/platforms/discord/commands/snapshot.ts +58 -42
  131. package/src/platforms/discordbot/commands/snapshot.test.ts +40 -18
  132. package/src/platforms/discordbot/commands/snapshot.ts +54 -37
  133. package/src/platforms/slack/commands/snapshot.test.ts +63 -8
  134. package/src/platforms/slack/commands/snapshot.ts +98 -66
  135. package/src/platforms/teams/client.test.ts +34 -30
  136. package/src/platforms/teams/client.ts +92 -20
  137. package/src/platforms/teams/commands/auth.test.ts +6 -2
  138. package/src/platforms/teams/commands/auth.ts +7 -2
  139. package/src/platforms/teams/commands/channel.test.ts +6 -6
  140. package/src/platforms/teams/commands/channel.ts +18 -3
  141. package/src/platforms/teams/commands/file.ts +18 -3
  142. package/src/platforms/teams/commands/message.ts +24 -4
  143. package/src/platforms/teams/commands/reaction.ts +12 -2
  144. package/src/platforms/teams/commands/snapshot.test.ts +1 -1
  145. package/src/platforms/teams/commands/snapshot.ts +59 -39
  146. package/src/platforms/teams/commands/team.test.ts +2 -2
  147. package/src/platforms/teams/commands/team.ts +6 -1
  148. package/src/platforms/teams/commands/user.ts +18 -3
  149. package/src/platforms/teams/commands/whoami.ts +6 -1
  150. package/src/platforms/teams/credential-manager.test.ts +25 -0
  151. package/src/platforms/teams/credential-manager.ts +13 -3
  152. package/src/platforms/teams/ensure-auth.test.ts +6 -1
  153. package/src/platforms/teams/ensure-auth.ts +7 -2
  154. package/src/platforms/teams/token-extractor.ts +77 -12
  155. package/src/platforms/teams/types.test.ts +17 -0
  156. package/src/platforms/teams/types.ts +6 -0
  157. package/src/platforms/webex/client.test.ts +157 -13
  158. package/src/platforms/webex/client.ts +64 -15
  159. package/src/platforms/webex/commands/auth.test.ts +122 -1
  160. package/src/platforms/webex/commands/auth.ts +72 -17
  161. package/src/platforms/webex/commands/snapshot.test.ts +14 -1
  162. package/src/platforms/webex/commands/snapshot.ts +17 -9
  163. package/src/platforms/webex/credential-manager.test.ts +63 -0
  164. package/src/platforms/webex/credential-manager.ts +22 -8
  165. package/src/platforms/webex/encryption.test.ts +54 -0
  166. package/src/platforms/webex/encryption.ts +3 -1
  167. package/src/platforms/webex/ensure-auth.ts +10 -2
  168. package/src/platforms/webex/token-extractor.test.ts +32 -3
  169. package/src/platforms/webex/token-extractor.ts +26 -5
@@ -8,7 +8,7 @@ test('snapshot: command is defined', () => {
8
8
  })
9
9
 
10
10
  test('snapshot: command has correct description', () => {
11
- expect(snapshotCommand.description()).toContain('server state')
11
+ expect(snapshotCommand.description()).toContain('server overview')
12
12
  })
13
13
 
14
14
  test('snapshot: command has --channels-only option', () => {
@@ -11,6 +11,7 @@ import type { DiscordChannel } from '../types'
11
11
  export async function snapshotAction(options: {
12
12
  channelsOnly?: boolean
13
13
  usersOnly?: boolean
14
+ full?: boolean
14
15
  limit?: number
15
16
  pretty?: boolean
16
17
  }): Promise<void> {
@@ -25,7 +26,6 @@ export async function snapshotAction(options: {
25
26
 
26
27
  const client = await new DiscordClient().login({ token: config.token as string })
27
28
  const serverId = config.current_server as string
28
- const messageLimit = options.limit || 20
29
29
 
30
30
  const snapshot: Record<string, any> = {}
31
31
 
@@ -35,51 +35,65 @@ export async function snapshotAction(options: {
35
35
  name: server.name,
36
36
  }
37
37
 
38
- if (!options.usersOnly) {
39
- const channels = await client.listChannels(serverId)
38
+ const isFull = options.full || options.channelsOnly || options.usersOnly
39
+ if (isFull) {
40
+ const messageLimit = options.limit || 20
40
41
 
41
- snapshot.channels = channels.map((ch) => ({
42
- id: ch.id,
43
- name: ch.name,
44
- type: ch.type,
45
- topic: ch.topic,
46
- }))
42
+ if (!options.usersOnly) {
43
+ const channels = await client.listChannels(serverId)
47
44
 
48
- if (!options.channelsOnly) {
49
- const isTextChannel = (ch: DiscordChannel) => ch.type === 0 || ch.type === 5
50
- const textChannels = channels.filter(isTextChannel)
51
-
52
- const channelMessages = await parallelMap(
53
- textChannels,
54
- async (channel: DiscordChannel) => {
55
- const messages = await client.getMessages(channel.id, messageLimit)
56
- return messages.map((msg) => ({
57
- ...msg,
58
- channel_name: channel.name,
59
- }))
60
- },
61
- 5,
62
- )
63
-
64
- snapshot.recent_messages = channelMessages.flat().map((msg) => ({
65
- channel_id: msg.channel_id,
66
- channel_name: msg.channel_name,
67
- id: msg.id,
68
- author: msg.author.username,
69
- content: msg.content,
70
- timestamp: msg.timestamp,
45
+ snapshot.channels = channels.map((ch) => ({
46
+ id: ch.id,
47
+ name: ch.name,
48
+ type: ch.type,
49
+ topic: ch.topic,
71
50
  }))
51
+
52
+ if (!options.channelsOnly) {
53
+ const isTextChannel = (ch: DiscordChannel) => ch.type === 0 || ch.type === 5
54
+ const textChannels = channels.filter(isTextChannel)
55
+
56
+ const channelMessages = await parallelMap(
57
+ textChannels,
58
+ async (channel: DiscordChannel) => {
59
+ const messages = await client.getMessages(channel.id, messageLimit)
60
+ return messages.map((msg) => ({
61
+ ...msg,
62
+ channel_name: channel.name,
63
+ }))
64
+ },
65
+ 5,
66
+ )
67
+
68
+ snapshot.recent_messages = channelMessages.flat().map((msg) => ({
69
+ channel_id: msg.channel_id,
70
+ channel_name: msg.channel_name,
71
+ id: msg.id,
72
+ author: msg.author.username,
73
+ content: msg.content,
74
+ timestamp: msg.timestamp,
75
+ }))
76
+ }
72
77
  }
73
- }
74
78
 
75
- if (!options.channelsOnly) {
76
- const users = await client.listUsers(serverId)
79
+ if (!options.channelsOnly) {
80
+ const users = await client.listUsers(serverId)
81
+
82
+ snapshot.members = users.map((u) => ({
83
+ id: u.id,
84
+ username: u.username,
85
+ global_name: u.global_name || null,
86
+ }))
87
+ }
88
+ } else {
89
+ if (!options.usersOnly) {
90
+ const channels = await client.listChannels(serverId)
91
+ const textChannels = channels.filter((ch: DiscordChannel) => ch.type === 0 || ch.type === 5)
92
+ snapshot.channels = textChannels.map((ch) => ({ id: ch.id, name: ch.name }))
93
+ }
77
94
 
78
- snapshot.members = users.map((u) => ({
79
- id: u.id,
80
- username: u.username,
81
- global_name: u.global_name || null,
82
- }))
95
+ snapshot.hint =
96
+ "Use 'message list <channel>' for messages, 'channel info <channel>' for channel details, 'user list' for members."
83
97
  }
84
98
 
85
99
  console.log(formatOutput(snapshot, options.pretty))
@@ -89,14 +103,16 @@ export async function snapshotAction(options: {
89
103
  }
90
104
 
91
105
  export const snapshotCommand = new Command('snapshot')
92
- .description('Get comprehensive server state for AI agents')
106
+ .description('Get server overview for AI agents (brief by default, use --full for comprehensive data)')
107
+ .option('--full', 'Include messages and members (verbose)')
93
108
  .option('--channels-only', 'Include only channels (exclude messages and members)')
94
109
  .option('--users-only', 'Include only members (exclude channels and messages)')
95
- .option('--limit <n>', 'Number of recent messages per channel (default: 20)', '20')
110
+ .option('--limit <n>', 'Number of recent messages per channel with --full (default: 20)', '20')
96
111
  .action(async (options) => {
97
112
  await snapshotAction({
98
113
  channelsOnly: options.channelsOnly,
99
114
  usersOnly: options.usersOnly,
115
+ full: options.full,
100
116
  limit: parseInt(options.limit, 10),
101
117
  pretty: options.pretty,
102
118
  })
@@ -86,23 +86,17 @@ describe('snapshot command', () => {
86
86
  process.env = originalEnv
87
87
  })
88
88
 
89
- describe('default (channels + messages)', () => {
90
- test('returns channels with recent messages', async () => {
89
+ describe('default (brief)', () => {
90
+ test('returns channels with id and name only', async () => {
91
91
  const manager = await setupManager(tempDir)
92
92
 
93
93
  const result = await snapshotAction({ _credManager: manager })
94
94
 
95
95
  expect(result.server_id).toBe('guild1')
96
96
  expect(result.channels).toHaveLength(2)
97
- expect(result.channels?.[0].id).toBe('ch1')
98
- expect(result.channels?.[0].name).toBe('general')
99
- expect(result.channels?.[0].messages).toHaveLength(2)
100
- expect(result.channels?.[0].messages?.[0]).toEqual({
101
- id: 'msg1-ch1',
102
- author: 'alice',
103
- content: 'Hello from ch1',
104
- timestamp: '2025-01-01T00:00:00Z',
105
- })
97
+ expect(result.channels?.[0]).toEqual({ id: 'ch1', name: 'general' })
98
+ expect(result.channels?.[1]).toEqual({ id: 'ch2', name: 'random' })
99
+ expect(result.hint).toBeDefined()
106
100
  })
107
101
 
108
102
  test('filters out non-text channels', async () => {
@@ -114,11 +108,39 @@ describe('snapshot command', () => {
114
108
  expect(channelNames).not.toContain('voice')
115
109
  })
116
110
 
117
- test('fetches messages in parallel for all text channels', async () => {
111
+ test('does not fetch messages', async () => {
118
112
  const manager = await setupManager(tempDir)
119
113
 
120
114
  await snapshotAction({ _credManager: manager })
121
115
 
116
+ expect(mockGetMessages).not.toHaveBeenCalled()
117
+ })
118
+ })
119
+
120
+ describe('--full (channels + messages)', () => {
121
+ test('returns channels with recent messages', async () => {
122
+ const manager = await setupManager(tempDir)
123
+
124
+ const result = await snapshotAction({ _credManager: manager, full: true })
125
+
126
+ expect(result.server_id).toBe('guild1')
127
+ expect(result.channels).toHaveLength(2)
128
+ expect(result.channels?.[0].id).toBe('ch1')
129
+ expect(result.channels?.[0].name).toBe('general')
130
+ expect(result.channels?.[0].messages).toHaveLength(2)
131
+ expect(result.channels?.[0].messages?.[0]).toEqual({
132
+ id: 'msg1-ch1',
133
+ author: 'alice',
134
+ content: 'Hello from ch1',
135
+ timestamp: '2025-01-01T00:00:00Z',
136
+ })
137
+ })
138
+
139
+ test('fetches messages in parallel for all text channels', async () => {
140
+ const manager = await setupManager(tempDir)
141
+
142
+ await snapshotAction({ _credManager: manager, full: true })
143
+
122
144
  expect(mockGetMessages).toHaveBeenCalledTimes(2)
123
145
  expect(mockGetMessages).toHaveBeenCalledWith('ch1', 5)
124
146
  expect(mockGetMessages).toHaveBeenCalledWith('ch2', 5)
@@ -127,7 +149,7 @@ describe('snapshot command', () => {
127
149
  test('respects --limit option', async () => {
128
150
  const manager = await setupManager(tempDir)
129
151
 
130
- await snapshotAction({ _credManager: manager, limit: 10 })
152
+ await snapshotAction({ _credManager: manager, full: true, limit: 10 })
131
153
 
132
154
  expect(mockGetMessages).toHaveBeenCalledWith('ch1', 10)
133
155
  expect(mockGetMessages).toHaveBeenCalledWith('ch2', 10)
@@ -136,17 +158,17 @@ describe('snapshot command', () => {
136
158
  test('defaults limit to 5', async () => {
137
159
  const manager = await setupManager(tempDir)
138
160
 
139
- await snapshotAction({ _credManager: manager })
161
+ await snapshotAction({ _credManager: manager, full: true })
140
162
 
141
163
  expect(mockGetMessages).toHaveBeenCalledWith('ch1', 5)
142
164
  })
143
165
  })
144
166
 
145
- describe('--channels-only', () => {
167
+ describe('--full --channels-only', () => {
146
168
  test('returns only channel list without messages', async () => {
147
169
  const manager = await setupManager(tempDir)
148
170
 
149
- const result = await snapshotAction({ _credManager: manager, channelsOnly: true })
171
+ const result = await snapshotAction({ _credManager: manager, full: true, channelsOnly: true })
150
172
 
151
173
  expect(result.server_id).toBe('guild1')
152
174
  expect(result.channels).toHaveLength(2)
@@ -156,11 +178,11 @@ describe('snapshot command', () => {
156
178
  })
157
179
  })
158
180
 
159
- describe('--users-only', () => {
181
+ describe('--full --users-only', () => {
160
182
  test('returns only user list', async () => {
161
183
  const manager = await setupManager(tempDir)
162
184
 
163
- const result = await snapshotAction({ _credManager: manager, usersOnly: true })
185
+ const result = await snapshotAction({ _credManager: manager, full: true, usersOnly: true })
164
186
 
165
187
  expect(result.server_id).toBe('guild1')
166
188
  expect(result.users).toHaveLength(3)
@@ -10,6 +10,7 @@ import { getClient } from './shared'
10
10
  interface SnapshotOption extends BotOption {
11
11
  channelsOnly?: boolean
12
12
  usersOnly?: boolean
13
+ full?: boolean
13
14
  limit?: number
14
15
  }
15
16
 
@@ -31,6 +32,7 @@ interface SnapshotResult {
31
32
  username: string
32
33
  global_name: string | null
33
34
  }>
35
+ hint?: string
34
36
  error?: string
35
37
  }
36
38
 
@@ -43,53 +45,66 @@ export async function snapshotAction(options: SnapshotOption): Promise<SnapshotR
43
45
  }
44
46
 
45
47
  const client = await getClient(options)
46
- const limit = options.limit ?? 5
47
48
 
48
- if (options.usersOnly) {
49
- const users = await client.listUsers(serverId)
50
- return {
51
- server_id: serverId,
52
- users: users.map((u) => ({
53
- id: u.id,
54
- username: u.username,
55
- global_name: u.global_name ?? null,
56
- })),
49
+ const isFull = options.full || options.channelsOnly || options.usersOnly
50
+ if (isFull) {
51
+ const limit = options.limit ?? 5
52
+
53
+ if (options.usersOnly) {
54
+ const users = await client.listUsers(serverId)
55
+ return {
56
+ server_id: serverId,
57
+ users: users.map((u) => ({
58
+ id: u.id,
59
+ username: u.username,
60
+ global_name: u.global_name ?? null,
61
+ })),
62
+ }
57
63
  }
58
- }
59
64
 
60
- const allChannels = await client.listChannels(serverId)
61
- const textChannels = allChannels.filter((ch) => ch.type === 0)
65
+ const allChannels = await client.listChannels(serverId)
66
+ const textChannels = allChannels.filter((ch) => ch.type === 0)
67
+
68
+ if (options.channelsOnly) {
69
+ return {
70
+ server_id: serverId,
71
+ channels: textChannels.map((ch) => ({
72
+ id: ch.id,
73
+ name: ch.name,
74
+ type: ch.type,
75
+ })),
76
+ }
77
+ }
78
+
79
+ const channelsWithMessages = await Promise.all(
80
+ textChannels.map(async (ch) => {
81
+ const messages = await client.getMessages(ch.id, limit)
82
+ return {
83
+ id: ch.id,
84
+ name: ch.name,
85
+ messages: messages.map((msg) => ({
86
+ id: msg.id,
87
+ author: msg.author.username,
88
+ content: msg.content,
89
+ timestamp: msg.timestamp,
90
+ })),
91
+ }
92
+ }),
93
+ )
62
94
 
63
- if (options.channelsOnly) {
64
95
  return {
65
96
  server_id: serverId,
66
- channels: textChannels.map((ch) => ({
67
- id: ch.id,
68
- name: ch.name,
69
- type: ch.type,
70
- })),
97
+ channels: channelsWithMessages,
71
98
  }
72
99
  }
73
100
 
74
- const channelsWithMessages = await Promise.all(
75
- textChannels.map(async (ch) => {
76
- const messages = await client.getMessages(ch.id, limit)
77
- return {
78
- id: ch.id,
79
- name: ch.name,
80
- messages: messages.map((msg) => ({
81
- id: msg.id,
82
- author: msg.author.username,
83
- content: msg.content,
84
- timestamp: msg.timestamp,
85
- })),
86
- }
87
- }),
88
- )
101
+ const allChannels = await client.listChannels(serverId)
102
+ const textChannels = allChannels.filter((ch) => ch.type === 0)
89
103
 
90
104
  return {
91
105
  server_id: serverId,
92
- channels: channelsWithMessages,
106
+ channels: textChannels.map((ch) => ({ id: ch.id, name: ch.name })),
107
+ hint: "Use 'message list <channel>' for messages, 'channel info <channel>' for channel details, 'user list' for members.",
93
108
  }
94
109
  } catch (error) {
95
110
  return { error: (error as Error).message }
@@ -97,10 +112,11 @@ export async function snapshotAction(options: SnapshotOption): Promise<SnapshotR
97
112
  }
98
113
 
99
114
  export const snapshotCommand = new Command('snapshot')
100
- .description('Server overview for AI agent context')
115
+ .description('Server overview for AI agents (brief by default, use --full for comprehensive data)')
116
+ .option('--full', 'Include messages and members (verbose)')
101
117
  .option('--channels-only', 'List channels only, skip messages')
102
118
  .option('--users-only', 'List users only')
103
- .option('--limit <n>', 'Messages per channel (default: 5)', '5')
119
+ .option('--limit <n>', 'Messages per channel with --full (default: 5)', '5')
104
120
  .option('--server <id>', 'Use specific server')
105
121
  .option('--bot <id>', 'Use specific bot')
106
122
  .option('--pretty', 'Pretty print JSON output')
@@ -108,6 +124,7 @@ export const snapshotCommand = new Command('snapshot')
108
124
  try {
109
125
  const result = await snapshotAction({
110
126
  ...options,
127
+ full: options.full,
111
128
  limit: parseInt(options.limit, 10),
112
129
  })
113
130
  console.log(formatOutput(result, options.pretty))
@@ -34,6 +34,12 @@ test('snapshot command has --limit option', () => {
34
34
  expect(hasLimit).toBe(true)
35
35
  })
36
36
 
37
+ test('snapshot command has --full option', () => {
38
+ const options = snapshotCommand.options
39
+ const hasFull = options.some((opt: any) => opt.long === '--full')
40
+ expect(hasFull).toBe(true)
41
+ })
42
+
37
43
  // Test snapshot logic using spyOn (no global mock pollution)
38
44
  let credManagerSpy: ReturnType<typeof spyOn>
39
45
  let clientTestAuthSpy: ReturnType<typeof spyOn>
@@ -147,11 +153,66 @@ afterEach(() => {
147
153
  clientGetMessagesSpy?.mockRestore()
148
154
  })
149
155
 
156
+ test('brief snapshot (default) returns workspace, channels as {id, name}, and hint', async () => {
157
+ const client = await new SlackClient().login({ token: 'xoxc-test', cookie: 'test-cookie' })
158
+
159
+ const auth = await client.testAuth()
160
+ const channels = await client.listChannels()
161
+ const active = channels.filter((ch) => !ch.is_archived)
162
+
163
+ const snapshot = {
164
+ workspace: {
165
+ id: auth.team_id,
166
+ name: auth.team,
167
+ },
168
+ channels: active.map((ch) => ({ id: ch.id, name: ch.name })),
169
+ hint: "Use 'message list <channel>' for messages, 'channel info <channel>' for channel details, 'user list' for users, 'usergroup list' for groups.",
170
+ }
171
+
172
+ expect(snapshot.workspace).toBeDefined()
173
+ expect(snapshot.workspace.id).toBe('T123')
174
+ expect(snapshot.workspace.name).toBe('Test Workspace')
175
+
176
+ expect(snapshot.channels).toBeDefined()
177
+ expect(snapshot.channels.length).toBe(2)
178
+ expect(snapshot.channels[0]).toEqual({ id: 'C123', name: 'general' })
179
+ expect(snapshot.channels[1]).toEqual({ id: 'C456', name: 'random' })
180
+
181
+ expect((snapshot as any).recent_messages).toBeUndefined()
182
+ expect((snapshot as any).users).toBeUndefined()
183
+ expect((snapshot as any).usergroups).toBeUndefined()
184
+
185
+ expect(snapshot.hint).toContain('message list')
186
+ })
187
+
188
+ test('brief snapshot excludes archived channels', async () => {
189
+ const channelsWithArchived: SlackChannel[] = [
190
+ ...mockChannels,
191
+ {
192
+ id: 'C789',
193
+ name: 'old-channel',
194
+ is_private: false,
195
+ is_archived: true,
196
+ created: 1234567800,
197
+ creator: 'U123',
198
+ topic: { value: 'Archived', creator: 'U123', last_set: 1234567800 },
199
+ purpose: { value: 'Old channel', creator: 'U123', last_set: 1234567800 },
200
+ },
201
+ ]
202
+
203
+ clientListChannelsSpy.mockResolvedValue(channelsWithArchived)
204
+
205
+ const client = await new SlackClient().login({ token: 'xoxc-test', cookie: 'test-cookie' })
206
+ const channels = await client.listChannels()
207
+ const active = channels.filter((ch) => !ch.is_archived)
208
+
209
+ expect(active.length).toBe(2)
210
+ expect(active.every((ch) => !ch.is_archived)).toBe(true)
211
+ })
212
+
150
213
  test('full snapshot returns workspace, channels, messages, and users', async () => {
151
- const credManager = new CredentialManager()
152
214
  const client = await new SlackClient().login({ token: 'xoxc-test', cookie: 'test-cookie' })
153
215
 
154
- const _workspace = await credManager.getWorkspace()
155
216
  const auth = await client.testAuth()
156
217
  const channels = await client.listChannels()
157
218
  const users = await client.listUsers()
@@ -218,10 +279,8 @@ test('full snapshot returns workspace, channels, messages, and users', async ()
218
279
  })
219
280
 
220
281
  test('snapshot with --channels-only excludes messages and users', async () => {
221
- const credManager = new CredentialManager()
222
282
  const client = await new SlackClient().login({ token: 'xoxc-test', cookie: 'test-cookie' })
223
283
 
224
- const _workspace = await credManager.getWorkspace()
225
284
  const auth = await client.testAuth()
226
285
  const channels = await client.listChannels()
227
286
 
@@ -249,10 +308,8 @@ test('snapshot with --channels-only excludes messages and users', async () => {
249
308
  })
250
309
 
251
310
  test('snapshot with --users-only excludes channels and messages', async () => {
252
- const credManager = new CredentialManager()
253
311
  const client = await new SlackClient().login({ token: 'xoxc-test', cookie: 'test-cookie' })
254
312
 
255
- const _workspace = await credManager.getWorkspace()
256
313
  const auth = await client.testAuth()
257
314
  const users = await client.listUsers()
258
315
 
@@ -279,10 +336,8 @@ test('snapshot with --users-only excludes channels and messages', async () => {
279
336
  })
280
337
 
281
338
  test('snapshot respects --limit option for messages', async () => {
282
- const credManager = new CredentialManager()
283
339
  const client = await new SlackClient().login({ token: 'xoxc-test', cookie: 'test-cookie' })
284
340
 
285
- const _workspace = await credManager.getWorkspace()
286
341
  const auth = await client.testAuth()
287
342
  const channels = await client.listChannels()
288
343