@hasna/connectors 0.3.16 → 0.4.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 (127) hide show
  1. package/bin/index.js +71 -1
  2. package/bin/mcp.js +71 -1
  3. package/bin/serve.js +70 -0
  4. package/connectors/connect-asana/.env.example +11 -0
  5. package/connectors/connect-asana/CLAUDE.md +128 -0
  6. package/connectors/connect-asana/README.md +193 -0
  7. package/connectors/connect-asana/package.json +52 -0
  8. package/connectors/connect-asana/src/api/client.ts +119 -0
  9. package/connectors/connect-asana/src/api/index.ts +319 -0
  10. package/connectors/connect-asana/src/cli/index.ts +731 -0
  11. package/connectors/connect-asana/src/index.ts +19 -0
  12. package/connectors/connect-asana/src/types/index.ts +270 -0
  13. package/connectors/connect-asana/src/utils/config.ts +171 -0
  14. package/connectors/connect-asana/src/utils/output.ts +119 -0
  15. package/connectors/connect-asana/tsconfig.json +16 -0
  16. package/connectors/connect-clickup/.env.example +11 -0
  17. package/connectors/connect-clickup/CLAUDE.md +128 -0
  18. package/connectors/connect-clickup/README.md +193 -0
  19. package/connectors/connect-clickup/package.json +52 -0
  20. package/connectors/connect-clickup/src/api/client.ts +116 -0
  21. package/connectors/connect-clickup/src/api/index.ts +400 -0
  22. package/connectors/connect-clickup/src/cli/index.ts +625 -0
  23. package/connectors/connect-clickup/src/index.ts +19 -0
  24. package/connectors/connect-clickup/src/types/index.ts +591 -0
  25. package/connectors/connect-clickup/src/utils/config.ts +157 -0
  26. package/connectors/connect-clickup/src/utils/output.ts +119 -0
  27. package/connectors/connect-clickup/tsconfig.json +16 -0
  28. package/connectors/connect-confluence/.env.example +11 -0
  29. package/connectors/connect-confluence/CLAUDE.md +272 -0
  30. package/connectors/connect-confluence/README.md +193 -0
  31. package/connectors/connect-confluence/package.json +53 -0
  32. package/connectors/connect-confluence/scripts/release.ts +179 -0
  33. package/connectors/connect-confluence/src/api/client.ts +213 -0
  34. package/connectors/connect-confluence/src/api/example.ts +48 -0
  35. package/connectors/connect-confluence/src/api/index.ts +51 -0
  36. package/connectors/connect-confluence/src/cli/index.ts +254 -0
  37. package/connectors/connect-confluence/src/index.ts +103 -0
  38. package/connectors/connect-confluence/src/types/index.ts +237 -0
  39. package/connectors/connect-confluence/src/utils/auth.ts +274 -0
  40. package/connectors/connect-confluence/src/utils/bulk.ts +212 -0
  41. package/connectors/connect-confluence/src/utils/config.ts +326 -0
  42. package/connectors/connect-confluence/src/utils/output.ts +175 -0
  43. package/connectors/connect-confluence/src/utils/settings.ts +114 -0
  44. package/connectors/connect-confluence/src/utils/storage.ts +198 -0
  45. package/connectors/connect-confluence/tsconfig.json +16 -0
  46. package/connectors/connect-jira/.env.example +11 -0
  47. package/connectors/connect-jira/CLAUDE.md +128 -0
  48. package/connectors/connect-jira/README.md +193 -0
  49. package/connectors/connect-jira/package.json +53 -0
  50. package/connectors/connect-jira/src/api/client.ts +131 -0
  51. package/connectors/connect-jira/src/api/index.ts +266 -0
  52. package/connectors/connect-jira/src/cli/index.ts +653 -0
  53. package/connectors/connect-jira/src/index.ts +23 -0
  54. package/connectors/connect-jira/src/types/index.ts +448 -0
  55. package/connectors/connect-jira/src/utils/config.ts +179 -0
  56. package/connectors/connect-jira/src/utils/output.ts +119 -0
  57. package/connectors/connect-jira/tsconfig.json +16 -0
  58. package/connectors/connect-linear/CLAUDE.md +88 -0
  59. package/connectors/connect-linear/README.md +201 -0
  60. package/connectors/connect-linear/package.json +45 -0
  61. package/connectors/connect-linear/src/api/client.ts +62 -0
  62. package/connectors/connect-linear/src/api/index.ts +46 -0
  63. package/connectors/connect-linear/src/api/issues.ts +247 -0
  64. package/connectors/connect-linear/src/api/projects.ts +179 -0
  65. package/connectors/connect-linear/src/api/teams.ts +125 -0
  66. package/connectors/connect-linear/src/api/users.ts +112 -0
  67. package/connectors/connect-linear/src/cli/index.ts +560 -0
  68. package/connectors/connect-linear/src/index.ts +27 -0
  69. package/connectors/connect-linear/src/types/index.ts +275 -0
  70. package/connectors/connect-linear/src/utils/config.ts +249 -0
  71. package/connectors/connect-linear/src/utils/output.ts +119 -0
  72. package/connectors/connect-linear/tsconfig.json +16 -0
  73. package/connectors/connect-slack/.env.example +7 -0
  74. package/connectors/connect-slack/CLAUDE.md +69 -0
  75. package/connectors/connect-slack/README.md +150 -0
  76. package/connectors/connect-slack/package.json +44 -0
  77. package/connectors/connect-slack/src/api/channels.ts +112 -0
  78. package/connectors/connect-slack/src/api/client.ts +97 -0
  79. package/connectors/connect-slack/src/api/index.ts +42 -0
  80. package/connectors/connect-slack/src/api/messages.ts +127 -0
  81. package/connectors/connect-slack/src/api/users.ts +110 -0
  82. package/connectors/connect-slack/src/cli/index.ts +494 -0
  83. package/connectors/connect-slack/src/index.ts +21 -0
  84. package/connectors/connect-slack/src/types/index.ts +263 -0
  85. package/connectors/connect-slack/src/utils/config.ts +297 -0
  86. package/connectors/connect-slack/src/utils/output.ts +119 -0
  87. package/connectors/connect-slack/tsconfig.json +16 -0
  88. package/connectors/connect-telegram/.env.example +2 -0
  89. package/connectors/connect-telegram/package.json +49 -0
  90. package/connectors/connect-todoist/.env.example +11 -0
  91. package/connectors/connect-todoist/CLAUDE.md +104 -0
  92. package/connectors/connect-todoist/README.md +193 -0
  93. package/connectors/connect-todoist/package.json +52 -0
  94. package/connectors/connect-todoist/src/api/client.ts +117 -0
  95. package/connectors/connect-todoist/src/api/index.ts +188 -0
  96. package/connectors/connect-todoist/src/cli/index.ts +990 -0
  97. package/connectors/connect-todoist/src/index.ts +21 -0
  98. package/connectors/connect-todoist/src/types/index.ts +240 -0
  99. package/connectors/connect-todoist/src/utils/config.ts +157 -0
  100. package/connectors/connect-todoist/src/utils/output.ts +119 -0
  101. package/connectors/connect-todoist/tsconfig.json +16 -0
  102. package/connectors/connect-trello/.env.example +11 -0
  103. package/connectors/connect-trello/CLAUDE.md +128 -0
  104. package/connectors/connect-trello/README.md +193 -0
  105. package/connectors/connect-trello/package.json +53 -0
  106. package/connectors/connect-trello/src/api/client.ts +128 -0
  107. package/connectors/connect-trello/src/api/index.ts +278 -0
  108. package/connectors/connect-trello/src/cli/index.ts +737 -0
  109. package/connectors/connect-trello/src/index.ts +21 -0
  110. package/connectors/connect-trello/src/types/index.ts +314 -0
  111. package/connectors/connect-trello/src/utils/config.ts +182 -0
  112. package/connectors/connect-trello/src/utils/output.ts +119 -0
  113. package/connectors/connect-trello/tsconfig.json +16 -0
  114. package/connectors/connect-whatsapp/.env.example +11 -0
  115. package/connectors/connect-whatsapp/CLAUDE.md +113 -0
  116. package/connectors/connect-whatsapp/README.md +193 -0
  117. package/connectors/connect-whatsapp/package.json +53 -0
  118. package/connectors/connect-whatsapp/src/api/client.ts +133 -0
  119. package/connectors/connect-whatsapp/src/api/index.ts +365 -0
  120. package/connectors/connect-whatsapp/src/cli/index.ts +686 -0
  121. package/connectors/connect-whatsapp/src/index.ts +25 -0
  122. package/connectors/connect-whatsapp/src/types/index.ts +502 -0
  123. package/connectors/connect-whatsapp/src/utils/config.ts +179 -0
  124. package/connectors/connect-whatsapp/src/utils/output.ts +119 -0
  125. package/connectors/connect-whatsapp/tsconfig.json +16 -0
  126. package/dist/index.js +70 -0
  127. package/package.json +1 -1
@@ -0,0 +1,110 @@
1
+ import type { SlackClient } from './client';
2
+ import type { SlackUser, UsersListResponse, AuthTestResponse } from '../types';
3
+
4
+ /**
5
+ * Users API
6
+ */
7
+ export class UsersApi {
8
+ constructor(private readonly client: SlackClient) {}
9
+
10
+ /**
11
+ * List all users in the workspace
12
+ */
13
+ async list(limit = 200): Promise<SlackUser[]> {
14
+ const allUsers: SlackUser[] = [];
15
+ let cursor: string | undefined;
16
+
17
+ do {
18
+ const response = await this.client.get<UsersListResponse>('users.list', {
19
+ limit,
20
+ cursor,
21
+ });
22
+
23
+ allUsers.push(...response.members);
24
+ cursor = response.response_metadata?.next_cursor;
25
+ } while (cursor && allUsers.length < 10000);
26
+
27
+ return allUsers;
28
+ }
29
+
30
+ /**
31
+ * Get user info by ID
32
+ */
33
+ async info(userId: string): Promise<SlackUser> {
34
+ const response = await this.client.get<{ ok: boolean; user: SlackUser }>(
35
+ 'users.info',
36
+ { user: userId }
37
+ );
38
+ return response.user;
39
+ }
40
+
41
+ /**
42
+ * Get current user's identity
43
+ */
44
+ async me(): Promise<AuthTestResponse> {
45
+ return this.client.get<AuthTestResponse>('auth.test');
46
+ }
47
+
48
+ /**
49
+ * Find a user by email
50
+ */
51
+ async findByEmail(email: string): Promise<SlackUser> {
52
+ const response = await this.client.get<{ ok: boolean; user: SlackUser }>(
53
+ 'users.lookupByEmail',
54
+ { email }
55
+ );
56
+ return response.user;
57
+ }
58
+
59
+ /**
60
+ * Find a user by username/display name
61
+ */
62
+ async findByName(name: string): Promise<SlackUser | undefined> {
63
+ const users = await this.list();
64
+ const normalizedName = name.replace(/^@/, '').toLowerCase();
65
+
66
+ return users.find(u =>
67
+ u.name.toLowerCase() === normalizedName ||
68
+ u.profile.display_name?.toLowerCase() === normalizedName ||
69
+ u.real_name?.toLowerCase() === normalizedName
70
+ );
71
+ }
72
+
73
+ /**
74
+ * Get user's presence status
75
+ */
76
+ async presence(userId: string): Promise<string> {
77
+ const response = await this.client.get<{ ok: boolean; presence: string }>(
78
+ 'users.getPresence',
79
+ { user: userId }
80
+ );
81
+ return response.presence;
82
+ }
83
+
84
+ /**
85
+ * Set current user's presence
86
+ */
87
+ async setPresence(presence: 'auto' | 'away'): Promise<void> {
88
+ await this.client.post('users.setPresence', { presence });
89
+ }
90
+
91
+ /**
92
+ * Set current user's status
93
+ */
94
+ async setStatus(text: string, emoji?: string, expiration?: number): Promise<void> {
95
+ await this.client.post('users.profile.set', {
96
+ profile: {
97
+ status_text: text,
98
+ status_emoji: emoji || '',
99
+ status_expiration: expiration || 0,
100
+ },
101
+ });
102
+ }
103
+
104
+ /**
105
+ * Clear current user's status
106
+ */
107
+ async clearStatus(): Promise<void> {
108
+ await this.setStatus('', '', 0);
109
+ }
110
+ }
@@ -0,0 +1,494 @@
1
+ #!/usr/bin/env bun
2
+ import { Command } from 'commander';
3
+ import chalk from 'chalk';
4
+ import { Slack } from '../api';
5
+ import type { OutputFormat } from '../types';
6
+ import {
7
+ getBotToken,
8
+ setBotToken,
9
+ setUserToken,
10
+ getDefaultChannel,
11
+ setDefaultChannel,
12
+ getCurrentProfile,
13
+ setCurrentProfile,
14
+ listProfiles,
15
+ createProfile,
16
+ deleteProfile,
17
+ profileExists,
18
+ clearConfig,
19
+ isAuthenticated,
20
+ setProfileOverride,
21
+ } from '../utils/config';
22
+ import { print, success, error, info, heading } from '../utils/output';
23
+
24
+ const program = new Command();
25
+
26
+ // Helper to get authenticated client
27
+ function getClient(): Slack {
28
+ const token = getBotToken();
29
+ if (!token) {
30
+ console.error(chalk.red('Error: No Slack token configured.'));
31
+ console.error(chalk.yellow('Set token with: connect-slack config set-token <token>'));
32
+ console.error(chalk.yellow('Or set SLACK_BOT_TOKEN environment variable'));
33
+ process.exit(1);
34
+ }
35
+ return new Slack({ accessToken: token });
36
+ }
37
+
38
+ // Global options
39
+ program
40
+ .name('connect-slack')
41
+ .description('Slack API CLI')
42
+ .version('0.0.1')
43
+ .option('-p, --profile <name>', 'Use specific profile')
44
+ .option('-f, --format <format>', 'Output format: json, table, pretty', 'pretty')
45
+ .hook('preAction', (thisCommand) => {
46
+ const opts = thisCommand.opts();
47
+ if (opts.profile) {
48
+ setProfileOverride(opts.profile);
49
+ }
50
+ });
51
+
52
+ // ============================================
53
+ // Auth/Config Commands
54
+ // ============================================
55
+
56
+ const configCmd = program
57
+ .command('config')
58
+ .description('Configuration commands');
59
+
60
+ configCmd
61
+ .command('set-token <token>')
62
+ .description('Set bot token for current profile')
63
+ .action((token: string) => {
64
+ setBotToken(token);
65
+ success(`Bot token saved to profile "${getCurrentProfile()}"`);
66
+ });
67
+
68
+ configCmd
69
+ .command('set-user-token <token>')
70
+ .description('Set user token for current profile')
71
+ .action((token: string) => {
72
+ setUserToken(token);
73
+ success(`User token saved to profile "${getCurrentProfile()}"`);
74
+ });
75
+
76
+ configCmd
77
+ .command('set-channel <channel>')
78
+ .description('Set default channel')
79
+ .action((channel: string) => {
80
+ setDefaultChannel(channel);
81
+ success(`Default channel set to "${channel}"`);
82
+ });
83
+
84
+ configCmd
85
+ .command('show')
86
+ .description('Show current configuration')
87
+ .action(() => {
88
+ const profile = getCurrentProfile();
89
+ const token = getBotToken();
90
+ const channel = getDefaultChannel();
91
+
92
+ heading('Current Configuration');
93
+ print({
94
+ profile,
95
+ authenticated: isAuthenticated(),
96
+ token: token ? `${token.substring(0, 10)}...` : 'Not set',
97
+ defaultChannel: channel || 'Not set',
98
+ });
99
+ });
100
+
101
+ configCmd
102
+ .command('clear')
103
+ .description('Clear configuration for current profile')
104
+ .action(() => {
105
+ clearConfig();
106
+ success('Configuration cleared');
107
+ });
108
+
109
+ // ============================================
110
+ // Profile Commands
111
+ // ============================================
112
+
113
+ const profileCmd = program
114
+ .command('profile')
115
+ .description('Profile management');
116
+
117
+ profileCmd
118
+ .command('list')
119
+ .description('List all profiles')
120
+ .action(() => {
121
+ const profiles = listProfiles();
122
+ const current = getCurrentProfile();
123
+
124
+ if (profiles.length === 0) {
125
+ info('No profiles found. Using default.');
126
+ return;
127
+ }
128
+
129
+ heading('Profiles');
130
+ profiles.forEach(p => {
131
+ const marker = p === current ? chalk.green(' (active)') : '';
132
+ console.log(` ${p}${marker}`);
133
+ });
134
+ });
135
+
136
+ profileCmd
137
+ .command('use <name>')
138
+ .description('Switch to a profile')
139
+ .action((name: string) => {
140
+ if (!profileExists(name)) {
141
+ error(`Profile "${name}" does not exist`);
142
+ process.exit(1);
143
+ }
144
+ setCurrentProfile(name);
145
+ success(`Switched to profile "${name}"`);
146
+ });
147
+
148
+ profileCmd
149
+ .command('create <name>')
150
+ .description('Create a new profile')
151
+ .action((name: string) => {
152
+ try {
153
+ createProfile(name);
154
+ success(`Profile "${name}" created`);
155
+ } catch (e) {
156
+ error((e as Error).message);
157
+ process.exit(1);
158
+ }
159
+ });
160
+
161
+ profileCmd
162
+ .command('delete <name>')
163
+ .description('Delete a profile')
164
+ .action((name: string) => {
165
+ try {
166
+ deleteProfile(name);
167
+ success(`Profile "${name}" deleted`);
168
+ } catch (e) {
169
+ error((e as Error).message);
170
+ process.exit(1);
171
+ }
172
+ });
173
+
174
+ profileCmd
175
+ .command('show')
176
+ .description('Show current profile name')
177
+ .action(() => {
178
+ console.log(getCurrentProfile());
179
+ });
180
+
181
+ // ============================================
182
+ // Test/Auth Commands
183
+ // ============================================
184
+
185
+ program
186
+ .command('test')
187
+ .alias('whoami')
188
+ .description('Test authentication and show current user')
189
+ .action(async () => {
190
+ try {
191
+ const client = getClient();
192
+ const result = await client.test();
193
+ print({
194
+ user: result.user,
195
+ userId: result.user_id,
196
+ team: result.team,
197
+ teamId: result.team_id,
198
+ url: result.url,
199
+ });
200
+ } catch (e) {
201
+ error((e as Error).message);
202
+ process.exit(1);
203
+ }
204
+ });
205
+
206
+ // ============================================
207
+ // Channel Commands
208
+ // ============================================
209
+
210
+ const channelsCmd = program
211
+ .command('channels')
212
+ .description('Channel commands');
213
+
214
+ channelsCmd
215
+ .command('list')
216
+ .description('List channels')
217
+ .option('-t, --types <types>', 'Channel types (public_channel,private_channel)', 'public_channel,private_channel')
218
+ .option('-a, --all', 'Include archived channels')
219
+ .option('-l, --limit <n>', 'Maximum number to return', '100')
220
+ .action(async (opts) => {
221
+ try {
222
+ const client = getClient();
223
+ const format = program.opts().format as OutputFormat;
224
+ const channels = await client.channels.list({
225
+ types: opts.types,
226
+ exclude_archived: !opts.all,
227
+ limit: parseInt(opts.limit, 10),
228
+ });
229
+
230
+ if (format === 'json') {
231
+ print(channels, format);
232
+ } else {
233
+ print(channels.map(c => ({
234
+ id: c.id,
235
+ name: c.name,
236
+ private: c.is_private ? 'yes' : 'no',
237
+ members: c.num_members,
238
+ archived: c.is_archived ? 'yes' : 'no',
239
+ })), format);
240
+ }
241
+ } catch (e) {
242
+ error((e as Error).message);
243
+ process.exit(1);
244
+ }
245
+ });
246
+
247
+ channelsCmd
248
+ .command('info <channel>')
249
+ .description('Get channel info')
250
+ .action(async (channel: string) => {
251
+ try {
252
+ const client = getClient();
253
+ const format = program.opts().format as OutputFormat;
254
+
255
+ // Try to find by name if not an ID
256
+ let channelId = channel;
257
+ if (!channel.startsWith('C')) {
258
+ const found = await client.channels.findByName(channel);
259
+ if (!found) {
260
+ error(`Channel "${channel}" not found`);
261
+ process.exit(1);
262
+ }
263
+ channelId = found.id;
264
+ }
265
+
266
+ const info = await client.channels.info(channelId);
267
+ print(info, format);
268
+ } catch (e) {
269
+ error((e as Error).message);
270
+ process.exit(1);
271
+ }
272
+ });
273
+
274
+ channelsCmd
275
+ .command('join <channel>')
276
+ .description('Join a channel')
277
+ .action(async (channel: string) => {
278
+ try {
279
+ const client = getClient();
280
+ const result = await client.channels.join(channel);
281
+ success(`Joined channel #${result.name}`);
282
+ } catch (e) {
283
+ error((e as Error).message);
284
+ process.exit(1);
285
+ }
286
+ });
287
+
288
+ channelsCmd
289
+ .command('leave <channel>')
290
+ .description('Leave a channel')
291
+ .action(async (channel: string) => {
292
+ try {
293
+ const client = getClient();
294
+ await client.channels.leave(channel);
295
+ success('Left channel');
296
+ } catch (e) {
297
+ error((e as Error).message);
298
+ process.exit(1);
299
+ }
300
+ });
301
+
302
+ // ============================================
303
+ // Message Commands
304
+ // ============================================
305
+
306
+ const messagesCmd = program
307
+ .command('messages')
308
+ .alias('msg')
309
+ .description('Message commands');
310
+
311
+ messagesCmd
312
+ .command('send <channel> <text>')
313
+ .description('Send a message to a channel')
314
+ .option('-t, --thread <ts>', 'Reply to thread')
315
+ .action(async (channel: string, text: string, opts) => {
316
+ try {
317
+ const client = getClient();
318
+
319
+ // Resolve channel name to ID if needed
320
+ let channelId = channel;
321
+ if (!channel.startsWith('C') && !channel.startsWith('D')) {
322
+ const found = await client.channels.findByName(channel);
323
+ if (!found) {
324
+ error(`Channel "${channel}" not found`);
325
+ process.exit(1);
326
+ }
327
+ channelId = found.id;
328
+ }
329
+
330
+ const result = await client.messages.send({
331
+ channel: channelId,
332
+ text,
333
+ thread_ts: opts.thread,
334
+ });
335
+
336
+ success(`Message sent (ts: ${result.ts})`);
337
+ } catch (e) {
338
+ error((e as Error).message);
339
+ process.exit(1);
340
+ }
341
+ });
342
+
343
+ messagesCmd
344
+ .command('history <channel>')
345
+ .description('Get message history')
346
+ .option('-l, --limit <n>', 'Number of messages', '20')
347
+ .action(async (channel: string, opts) => {
348
+ try {
349
+ const client = getClient();
350
+ const format = program.opts().format as OutputFormat;
351
+
352
+ // Resolve channel name to ID if needed
353
+ let channelId = channel;
354
+ if (!channel.startsWith('C') && !channel.startsWith('D')) {
355
+ const found = await client.channels.findByName(channel);
356
+ if (!found) {
357
+ error(`Channel "${channel}" not found`);
358
+ process.exit(1);
359
+ }
360
+ channelId = found.id;
361
+ }
362
+
363
+ const messages = await client.messages.history({
364
+ channel: channelId,
365
+ limit: parseInt(opts.limit, 10),
366
+ });
367
+
368
+ if (format === 'json') {
369
+ print(messages, format);
370
+ } else {
371
+ print(messages.map(m => ({
372
+ ts: m.ts,
373
+ user: m.user || m.bot_id || 'unknown',
374
+ text: m.text?.substring(0, 100) + (m.text && m.text.length > 100 ? '...' : ''),
375
+ })), format);
376
+ }
377
+ } catch (e) {
378
+ error((e as Error).message);
379
+ process.exit(1);
380
+ }
381
+ });
382
+
383
+ messagesCmd
384
+ .command('search <query>')
385
+ .description('Search messages')
386
+ .option('-l, --limit <n>', 'Number of results', '20')
387
+ .action(async (query: string, opts) => {
388
+ try {
389
+ const client = getClient();
390
+ const format = program.opts().format as OutputFormat;
391
+ const messages = await client.messages.search(query, parseInt(opts.limit, 10));
392
+ print(messages, format);
393
+ } catch (e) {
394
+ error((e as Error).message);
395
+ process.exit(1);
396
+ }
397
+ });
398
+
399
+ // ============================================
400
+ // User Commands
401
+ // ============================================
402
+
403
+ const usersCmd = program
404
+ .command('users')
405
+ .description('User commands');
406
+
407
+ usersCmd
408
+ .command('list')
409
+ .description('List users')
410
+ .option('-l, --limit <n>', 'Maximum number to return', '100')
411
+ .action(async (opts) => {
412
+ try {
413
+ const client = getClient();
414
+ const format = program.opts().format as OutputFormat;
415
+ const users = await client.users.list(parseInt(opts.limit, 10));
416
+
417
+ // Filter out bots and deleted users by default
418
+ const activeUsers = users.filter(u => !u.is_bot && !u.deleted);
419
+
420
+ if (format === 'json') {
421
+ print(activeUsers, format);
422
+ } else {
423
+ print(activeUsers.map(u => ({
424
+ id: u.id,
425
+ name: u.name,
426
+ realName: u.real_name,
427
+ email: u.profile.email,
428
+ admin: u.is_admin ? 'yes' : 'no',
429
+ })), format);
430
+ }
431
+ } catch (e) {
432
+ error((e as Error).message);
433
+ process.exit(1);
434
+ }
435
+ });
436
+
437
+ usersCmd
438
+ .command('info <user>')
439
+ .description('Get user info')
440
+ .action(async (user: string) => {
441
+ try {
442
+ const client = getClient();
443
+ const format = program.opts().format as OutputFormat;
444
+
445
+ // Try to find by name if not an ID
446
+ let userInfo;
447
+ if (user.startsWith('U')) {
448
+ userInfo = await client.users.info(user);
449
+ } else {
450
+ userInfo = await client.users.findByName(user);
451
+ if (!userInfo) {
452
+ error(`User "${user}" not found`);
453
+ process.exit(1);
454
+ }
455
+ }
456
+
457
+ print(userInfo, format);
458
+ } catch (e) {
459
+ error((e as Error).message);
460
+ process.exit(1);
461
+ }
462
+ });
463
+
464
+ // ============================================
465
+ // Quick Send Command
466
+ // ============================================
467
+
468
+ program
469
+ .command('send <channel> <text>')
470
+ .description('Quick send a message')
471
+ .action(async (channel: string, text: string) => {
472
+ try {
473
+ const client = getClient();
474
+
475
+ // Resolve channel name to ID if needed
476
+ let channelId = channel;
477
+ if (!channel.startsWith('C') && !channel.startsWith('D')) {
478
+ const found = await client.channels.findByName(channel);
479
+ if (!found) {
480
+ error(`Channel "${channel}" not found`);
481
+ process.exit(1);
482
+ }
483
+ channelId = found.id;
484
+ }
485
+
486
+ const result = await client.send(channelId, text);
487
+ success(`Message sent to #${channel} (ts: ${result.ts})`);
488
+ } catch (e) {
489
+ error((e as Error).message);
490
+ process.exit(1);
491
+ }
492
+ });
493
+
494
+ program.parse();
@@ -0,0 +1,21 @@
1
+ // Main library exports
2
+ export { Slack, SlackClient, ChannelsApi, MessagesApi, UsersApi } from './api';
3
+
4
+ // Type exports
5
+ export type {
6
+ SlackConfig,
7
+ SlackUser,
8
+ SlackUserProfile,
9
+ SlackChannel,
10
+ SlackMessage,
11
+ SlackBlock,
12
+ SlackAttachment,
13
+ SlackApiResponse,
14
+ ChatPostMessageOptions,
15
+ ConversationsListOptions,
16
+ ConversationsHistoryOptions,
17
+ OAuth2Tokens,
18
+ OutputFormat,
19
+ } from './types';
20
+
21
+ export { SlackApiError } from './types';