agent-messenger 2.8.0 → 2.9.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 (74) 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/commands/snapshot.d.ts +1 -0
  24. package/dist/src/platforms/teams/commands/snapshot.d.ts.map +1 -1
  25. package/dist/src/platforms/teams/commands/snapshot.js +44 -31
  26. package/dist/src/platforms/teams/commands/snapshot.js.map +1 -1
  27. package/dist/src/platforms/webex/commands/snapshot.d.ts +1 -0
  28. package/dist/src/platforms/webex/commands/snapshot.d.ts.map +1 -1
  29. package/dist/src/platforms/webex/commands/snapshot.js +14 -7
  30. package/dist/src/platforms/webex/commands/snapshot.js.map +1 -1
  31. package/docs/content/docs/agent-skills.mdx +0 -10
  32. package/docs/content/docs/cli/channeltalk.mdx +18 -8
  33. package/docs/content/docs/cli/channeltalkbot.mdx +16 -6
  34. package/docs/content/docs/cli/discord.mdx +23 -7
  35. package/docs/content/docs/cli/discordbot.mdx +23 -7
  36. package/docs/content/docs/cli/slack.mdx +24 -7
  37. package/docs/content/docs/cli/teams.mdx +24 -8
  38. package/docs/content/docs/cli/webex.mdx +15 -2
  39. package/package.json +1 -1
  40. package/skills/agent-channeltalk/SKILL.md +19 -9
  41. package/skills/agent-channeltalk/references/common-patterns.md +10 -9
  42. package/skills/agent-channeltalkbot/SKILL.md +19 -9
  43. package/skills/agent-channeltalkbot/references/common-patterns.md +10 -9
  44. package/skills/agent-discord/SKILL.md +18 -9
  45. package/skills/agent-discord/references/common-patterns.md +8 -7
  46. package/skills/agent-discordbot/SKILL.md +18 -9
  47. package/skills/agent-instagram/SKILL.md +1 -1
  48. package/skills/agent-kakaotalk/SKILL.md +1 -1
  49. package/skills/agent-line/SKILL.md +1 -1
  50. package/skills/agent-slack/SKILL.md +19 -10
  51. package/skills/agent-slack/references/common-patterns.md +4 -7
  52. package/skills/agent-slackbot/SKILL.md +1 -1
  53. package/skills/agent-teams/SKILL.md +18 -9
  54. package/skills/agent-teams/references/common-patterns.md +9 -7
  55. package/skills/agent-telegram/SKILL.md +1 -1
  56. package/skills/agent-webex/SKILL.md +13 -4
  57. package/skills/agent-webex/references/common-patterns.md +8 -2
  58. package/skills/agent-wechatbot/SKILL.md +1 -1
  59. package/skills/agent-whatsapp/SKILL.md +1 -1
  60. package/skills/agent-whatsappbot/SKILL.md +1 -1
  61. package/src/platforms/channeltalk/commands/snapshot.test.ts +58 -26
  62. package/src/platforms/channeltalk/commands/snapshot.ts +107 -33
  63. package/src/platforms/channeltalkbot/commands/snapshot.test.ts +26 -8
  64. package/src/platforms/channeltalkbot/commands/snapshot.ts +131 -64
  65. package/src/platforms/discord/commands/snapshot.test.ts +1 -1
  66. package/src/platforms/discord/commands/snapshot.ts +58 -42
  67. package/src/platforms/discordbot/commands/snapshot.test.ts +40 -18
  68. package/src/platforms/discordbot/commands/snapshot.ts +54 -37
  69. package/src/platforms/slack/commands/snapshot.test.ts +63 -8
  70. package/src/platforms/slack/commands/snapshot.ts +98 -66
  71. package/src/platforms/teams/commands/snapshot.test.ts +1 -1
  72. package/src/platforms/teams/commands/snapshot.ts +53 -38
  73. package/src/platforms/webex/commands/snapshot.test.ts +14 -1
  74. package/src/platforms/webex/commands/snapshot.ts +17 -9
@@ -67,10 +67,28 @@ describe('snapshot command', () => {
67
67
  })
68
68
 
69
69
  describe('snapshotAction', () => {
70
- test('returns workspace, groups, user_chats, managers, bots', async () => {
70
+ test('brief snapshot returns workspace, groups (names), chat counts, and hint', async () => {
71
71
  const manager = new ChannelBotCredentialManager(tempDir)
72
72
  const result = await snapshotAction({ _credManager: manager })
73
73
 
74
+ expect(result.error).toBeUndefined()
75
+ expect(result.workspace).toBeDefined()
76
+ expect(result.workspace?.id).toBe('ch1')
77
+ expect(result.workspace?.name).toBe('Test Workspace')
78
+ expect(result.groups).toBeDefined()
79
+ expect(result.groups?.[0]).toEqual({ id: 'grp1', name: 'Team Alpha' })
80
+ expect(result.user_chats).toBeDefined()
81
+ expect(result.user_chats?.opened_count).toBe(1)
82
+ expect(result.hint).toBeDefined()
83
+ expect(result.managers).toBeUndefined()
84
+ expect(result.bots).toBeUndefined()
85
+ expect(mockGetGroupMessages).not.toHaveBeenCalled()
86
+ })
87
+
88
+ test('full snapshot returns workspace, groups, user_chats, managers, bots', async () => {
89
+ const manager = new ChannelBotCredentialManager(tempDir)
90
+ const result = await snapshotAction({ full: true, _credManager: manager })
91
+
74
92
  expect(result.error).toBeUndefined()
75
93
  expect(result.workspace).toBeDefined()
76
94
  expect(result.workspace?.id).toBe('ch1')
@@ -83,7 +101,7 @@ describe('snapshot command', () => {
83
101
 
84
102
  test('groups-only flag skips user_chats, managers, bots', async () => {
85
103
  const manager = new ChannelBotCredentialManager(tempDir)
86
- const result = await snapshotAction({ groupsOnly: true, _credManager: manager })
104
+ const result = await snapshotAction({ full: true, groupsOnly: true, _credManager: manager })
87
105
 
88
106
  expect(result.error).toBeUndefined()
89
107
  expect(result.workspace).toBeDefined()
@@ -95,7 +113,7 @@ describe('snapshot command', () => {
95
113
 
96
114
  test('chats-only flag skips groups, managers, bots', async () => {
97
115
  const manager = new ChannelBotCredentialManager(tempDir)
98
- const result = await snapshotAction({ chatsOnly: true, _credManager: manager })
116
+ const result = await snapshotAction({ full: true, chatsOnly: true, _credManager: manager })
99
117
 
100
118
  expect(result.error).toBeUndefined()
101
119
  expect(result.workspace).toBeDefined()
@@ -105,21 +123,21 @@ describe('snapshot command', () => {
105
123
  expect(result.bots).toBeUndefined()
106
124
  })
107
125
 
108
- test('groups include recent messages', async () => {
126
+ test('full groups include recent messages', async () => {
109
127
  const manager = new ChannelBotCredentialManager(tempDir)
110
- const result = await snapshotAction({ groupsOnly: true, limit: 3, _credManager: manager })
128
+ const result = await snapshotAction({ full: true, groupsOnly: true, limit: 3, _credManager: manager })
111
129
 
112
130
  expect(result.groups?.[0].messages).toBeDefined()
113
131
  expect(result.groups?.[0].messages?.[0].id).toBe('msg1')
114
132
  })
115
133
 
116
- test('user_chats includes counts and recent opened', async () => {
134
+ test('full user_chats includes counts and recent opened', async () => {
117
135
  const manager = new ChannelBotCredentialManager(tempDir)
118
- const result = await snapshotAction({ chatsOnly: true, _credManager: manager })
136
+ const result = await snapshotAction({ full: true, chatsOnly: true, _credManager: manager })
119
137
 
120
138
  expect(result.user_chats?.opened_count).toBe(1)
121
139
  expect(result.user_chats?.recent_opened).toHaveLength(1)
122
- expect(result.user_chats?.recent_opened[0].id).toBe('chat1')
140
+ expect(result.user_chats?.recent_opened?.[0].id).toBe('chat1')
123
141
  })
124
142
  })
125
143
  })
@@ -9,6 +9,7 @@ import { getClient } from './shared'
9
9
  interface SnapshotOption extends WorkspaceOption {
10
10
  groupsOnly?: boolean
11
11
  chatsOnly?: boolean
12
+ full?: boolean
12
13
  limit?: number
13
14
  }
14
15
 
@@ -33,7 +34,7 @@ interface SnapshotResult {
33
34
  opened_count: number
34
35
  snoozed_count: number
35
36
  closed_count: number
36
- recent_opened: Array<{
37
+ recent_opened?: Array<{
37
38
  id: string
38
39
  name?: string
39
40
  user_id?: string
@@ -46,13 +47,13 @@ interface SnapshotResult {
46
47
  }
47
48
  managers?: Array<{ id: string; name: string; description?: string }>
48
49
  bots?: Array<{ id: string; name: string }>
50
+ hint?: string
49
51
  error?: string
50
52
  }
51
53
 
52
54
  export async function snapshotAction(options: SnapshotOption): Promise<SnapshotResult> {
53
55
  try {
54
56
  const client = await getClient(options)
55
- const limit = options.limit ?? 5
56
57
 
57
58
  const channel = await client.getChannel()
58
59
  const workspace = {
@@ -62,71 +63,75 @@ export async function snapshotAction(options: SnapshotOption): Promise<SnapshotR
62
63
  description: channel.description,
63
64
  }
64
65
 
65
- if (options.groupsOnly) {
66
- const groups = await client.listGroups({ limit: 20 })
67
- const groupsWithMessages = await Promise.all(
68
- groups.map(async (g) => {
69
- const messages = await client.getGroupMessages(g.id, { limit, sortOrder: 'desc' })
70
- return {
71
- id: g.id,
72
- name: g.name,
73
- messages: messages.map((m) => ({
74
- id: m.id,
75
- person_type: m.personType,
76
- plain_text: m.plainText,
77
- created_at: m.createdAt,
78
- })),
79
- }
80
- }),
81
- )
82
- return { workspace, groups: groupsWithMessages }
66
+ if (options.full) {
67
+ return buildFullSnapshot(client, workspace, options)
83
68
  }
84
69
 
85
- if (options.chatsOnly) {
86
- const [openedChats, snoozedChats, closedChats] = await Promise.all([
87
- client.listUserChats({ state: 'opened', limit: 10, sortOrder: 'desc' }),
88
- client.listUserChats({ state: 'snoozed', limit: 1 }),
89
- client.listUserChats({ state: 'closed', limit: 1 }),
90
- ])
91
-
92
- const recentOpened = await Promise.all(
93
- openedChats.slice(0, 5).map(async (chat) => {
94
- const messages = await client.getUserChatMessages(chat.id, { limit: 1, sortOrder: 'desc' })
95
- return {
96
- id: chat.id,
97
- name: chat.name,
98
- user_id: chat.userId,
99
- last_message: messages[0]
100
- ? {
101
- id: messages[0].id,
102
- plain_text: messages[0].plainText,
103
- created_at: messages[0].createdAt,
104
- }
105
- : undefined,
106
- }
107
- }),
108
- )
70
+ return buildBriefSnapshot(client, workspace, options)
71
+ } catch (error) {
72
+ return { error: (error as Error).message }
73
+ }
74
+ }
109
75
 
110
- return {
111
- workspace,
112
- user_chats: {
113
- opened_count: openedChats.length,
114
- snoozed_count: snoozedChats.length,
115
- closed_count: closedChats.length,
116
- recent_opened: recentOpened,
117
- },
118
- }
76
+ async function buildBriefSnapshot(
77
+ client: Awaited<ReturnType<typeof getClient>>,
78
+ workspace: SnapshotResult['workspace'],
79
+ options: SnapshotOption,
80
+ ): Promise<SnapshotResult> {
81
+ if (options.groupsOnly) {
82
+ const groups = await client.listGroups({ limit: 20 })
83
+ return {
84
+ workspace,
85
+ groups: groups.map((g) => ({ id: g.id, name: g.name })),
86
+ hint: "Use 'group messages <group>' for messages.",
119
87
  }
88
+ }
120
89
 
121
- const [groups, openedChats, snoozedChats, closedChats, managers, bots] = await Promise.all([
122
- client.listGroups({ limit: 20 }),
90
+ if (options.chatsOnly) {
91
+ const [openedChats, snoozedChats, closedChats] = await Promise.all([
123
92
  client.listUserChats({ state: 'opened', limit: 10, sortOrder: 'desc' }),
124
93
  client.listUserChats({ state: 'snoozed', limit: 1 }),
125
94
  client.listUserChats({ state: 'closed', limit: 1 }),
126
- client.listManagers({ limit: 50 }),
127
- client.listBots({ limit: 50 }),
128
95
  ])
96
+ return {
97
+ workspace,
98
+ user_chats: {
99
+ opened_count: openedChats.length,
100
+ snoozed_count: snoozedChats.length,
101
+ closed_count: closedChats.length,
102
+ },
103
+ hint: "Use 'chat list --state opened' for chat details, 'chat messages <chat>' for messages.",
104
+ }
105
+ }
106
+
107
+ const [groups, openedChats, snoozedChats, closedChats] = await Promise.all([
108
+ client.listGroups({ limit: 20 }),
109
+ client.listUserChats({ state: 'opened', limit: 10, sortOrder: 'desc' }),
110
+ client.listUserChats({ state: 'snoozed', limit: 1 }),
111
+ client.listUserChats({ state: 'closed', limit: 1 }),
112
+ ])
113
+
114
+ return {
115
+ workspace,
116
+ groups: groups.map((g) => ({ id: g.id, name: g.name })),
117
+ user_chats: {
118
+ opened_count: openedChats.length,
119
+ snoozed_count: snoozedChats.length,
120
+ closed_count: closedChats.length,
121
+ },
122
+ hint: "Use 'group messages <group>' for group messages, 'chat list --state opened' for chats, 'manager list' for managers.",
123
+ }
124
+ }
125
+
126
+ async function buildFullSnapshot(
127
+ client: Awaited<ReturnType<typeof getClient>>,
128
+ workspace: SnapshotResult['workspace'],
129
+ options: SnapshotOption,
130
+ ): Promise<SnapshotResult> {
131
+ const limit = options.limit ?? 5
129
132
 
133
+ if (options.groupsOnly) {
134
+ const groups = await client.listGroups({ limit: 20 })
130
135
  const groupsWithMessages = await Promise.all(
131
136
  groups.map(async (g) => {
132
137
  const messages = await client.getGroupMessages(g.id, { limit, sortOrder: 'desc' })
@@ -142,6 +147,15 @@ export async function snapshotAction(options: SnapshotOption): Promise<SnapshotR
142
147
  }
143
148
  }),
144
149
  )
150
+ return { workspace, groups: groupsWithMessages }
151
+ }
152
+
153
+ if (options.chatsOnly) {
154
+ const [openedChats, snoozedChats, closedChats] = await Promise.all([
155
+ client.listUserChats({ state: 'opened', limit: 10, sortOrder: 'desc' }),
156
+ client.listUserChats({ state: 'snoozed', limit: 1 }),
157
+ client.listUserChats({ state: 'closed', limit: 1 }),
158
+ ])
145
159
 
146
160
  const recentOpened = await Promise.all(
147
161
  openedChats.slice(0, 5).map(async (chat) => {
@@ -163,26 +177,78 @@ export async function snapshotAction(options: SnapshotOption): Promise<SnapshotR
163
177
 
164
178
  return {
165
179
  workspace,
166
- groups: groupsWithMessages,
167
180
  user_chats: {
168
181
  opened_count: openedChats.length,
169
182
  snoozed_count: snoozedChats.length,
170
183
  closed_count: closedChats.length,
171
184
  recent_opened: recentOpened,
172
185
  },
173
- managers: managers.map((m) => ({ id: m.id, name: m.name, description: m.description })),
174
- bots: bots.map((b) => ({ id: b.id, name: b.name })),
175
186
  }
176
- } catch (error) {
177
- return { error: (error as Error).message }
187
+ }
188
+
189
+ const [groups, openedChats, snoozedChats, closedChats, managers, bots] = await Promise.all([
190
+ client.listGroups({ limit: 20 }),
191
+ client.listUserChats({ state: 'opened', limit: 10, sortOrder: 'desc' }),
192
+ client.listUserChats({ state: 'snoozed', limit: 1 }),
193
+ client.listUserChats({ state: 'closed', limit: 1 }),
194
+ client.listManagers({ limit: 50 }),
195
+ client.listBots({ limit: 50 }),
196
+ ])
197
+
198
+ const groupsWithMessages = await Promise.all(
199
+ groups.map(async (g) => {
200
+ const messages = await client.getGroupMessages(g.id, { limit, sortOrder: 'desc' })
201
+ return {
202
+ id: g.id,
203
+ name: g.name,
204
+ messages: messages.map((m) => ({
205
+ id: m.id,
206
+ person_type: m.personType,
207
+ plain_text: m.plainText,
208
+ created_at: m.createdAt,
209
+ })),
210
+ }
211
+ }),
212
+ )
213
+
214
+ const recentOpened = await Promise.all(
215
+ openedChats.slice(0, 5).map(async (chat) => {
216
+ const messages = await client.getUserChatMessages(chat.id, { limit: 1, sortOrder: 'desc' })
217
+ return {
218
+ id: chat.id,
219
+ name: chat.name,
220
+ user_id: chat.userId,
221
+ last_message: messages[0]
222
+ ? {
223
+ id: messages[0].id,
224
+ plain_text: messages[0].plainText,
225
+ created_at: messages[0].createdAt,
226
+ }
227
+ : undefined,
228
+ }
229
+ }),
230
+ )
231
+
232
+ return {
233
+ workspace,
234
+ groups: groupsWithMessages,
235
+ user_chats: {
236
+ opened_count: openedChats.length,
237
+ snoozed_count: snoozedChats.length,
238
+ closed_count: closedChats.length,
239
+ recent_opened: recentOpened,
240
+ },
241
+ managers: managers.map((m) => ({ id: m.id, name: m.name, description: m.description })),
242
+ bots: bots.map((b) => ({ id: b.id, name: b.name })),
178
243
  }
179
244
  }
180
245
 
181
246
  export const snapshotCommand = new Command('snapshot')
182
- .description('Workspace overview for AI agent context')
247
+ .description('Workspace overview for AI agents (brief by default, use --full for comprehensive data)')
248
+ .option('--full', 'Include messages, managers, and bots (verbose)')
183
249
  .option('--groups-only', 'List groups only, skip user chats')
184
250
  .option('--chats-only', 'List user chats only, skip groups')
185
- .option('--limit <n>', 'Messages per group/chat (default: 5)', '5')
251
+ .option('--limit <n>', 'Messages per group/chat with --full (default: 5)', '5')
186
252
  .option('--workspace <id>', 'Workspace ID')
187
253
  .option('--bot <name>', 'Bot name')
188
254
  .option('--pretty', 'Pretty print JSON output')
@@ -190,6 +256,7 @@ export const snapshotCommand = new Command('snapshot')
190
256
  try {
191
257
  const result = await snapshotAction({
192
258
  ...options,
259
+ full: options.full,
193
260
  limit: parseInt(options.limit, 10),
194
261
  })
195
262
  console.log(formatOutput(result, options.pretty))
@@ -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)