@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,275 @@
1
+ // Linear API Types
2
+
3
+ export type OutputFormat = 'json' | 'table' | 'pretty';
4
+
5
+ // Configuration
6
+ export interface LinearConfig {
7
+ apiKey: string;
8
+ baseUrl?: string;
9
+ }
10
+
11
+ export interface CliConfig {
12
+ apiKey?: string;
13
+ defaultTeamId?: string;
14
+ }
15
+
16
+ // API Error
17
+ export class LinearApiError extends Error {
18
+ constructor(
19
+ message: string,
20
+ public readonly code: string,
21
+ public readonly statusCode?: number
22
+ ) {
23
+ super(message);
24
+ this.name = 'LinearApiError';
25
+ }
26
+ }
27
+
28
+ // GraphQL response wrapper
29
+ export interface GraphQLResponse<T> {
30
+ data?: T;
31
+ errors?: Array<{
32
+ message: string;
33
+ locations?: Array<{ line: number; column: number }>;
34
+ path?: string[];
35
+ extensions?: Record<string, unknown>;
36
+ }>;
37
+ }
38
+
39
+ // User types
40
+ export interface LinearUser {
41
+ id: string;
42
+ name: string;
43
+ displayName: string;
44
+ email: string;
45
+ avatarUrl?: string;
46
+ active: boolean;
47
+ admin: boolean;
48
+ createdAt: string;
49
+ updatedAt: string;
50
+ }
51
+
52
+ // Team types
53
+ export interface LinearTeam {
54
+ id: string;
55
+ name: string;
56
+ key: string;
57
+ description?: string;
58
+ icon?: string;
59
+ color?: string;
60
+ private: boolean;
61
+ createdAt: string;
62
+ updatedAt: string;
63
+ }
64
+
65
+ // Issue state types
66
+ export interface LinearWorkflowState {
67
+ id: string;
68
+ name: string;
69
+ color: string;
70
+ type: 'backlog' | 'unstarted' | 'started' | 'completed' | 'canceled';
71
+ position: number;
72
+ }
73
+
74
+ // Issue label types
75
+ export interface LinearLabel {
76
+ id: string;
77
+ name: string;
78
+ color: string;
79
+ description?: string;
80
+ }
81
+
82
+ // Issue priority
83
+ export type LinearPriority = 0 | 1 | 2 | 3 | 4; // 0 = no priority, 1 = urgent, 2 = high, 3 = normal, 4 = low
84
+
85
+ // Issue types
86
+ export interface LinearIssue {
87
+ id: string;
88
+ identifier: string;
89
+ title: string;
90
+ description?: string;
91
+ priority: LinearPriority;
92
+ priorityLabel: string;
93
+ estimate?: number;
94
+ sortOrder: number;
95
+ boardOrder: number;
96
+ startedAt?: string;
97
+ completedAt?: string;
98
+ canceledAt?: string;
99
+ autoClosedAt?: string;
100
+ autoArchivedAt?: string;
101
+ dueDate?: string;
102
+ createdAt: string;
103
+ updatedAt: string;
104
+ archivedAt?: string;
105
+ url: string;
106
+ number: number;
107
+ team?: LinearTeam;
108
+ state?: LinearWorkflowState;
109
+ assignee?: LinearUser;
110
+ creator?: LinearUser;
111
+ labels?: { nodes: LinearLabel[] };
112
+ project?: LinearProject;
113
+ parent?: LinearIssue;
114
+ children?: { nodes: LinearIssue[] };
115
+ comments?: { nodes: LinearComment[] };
116
+ }
117
+
118
+ // Comment types
119
+ export interface LinearComment {
120
+ id: string;
121
+ body: string;
122
+ createdAt: string;
123
+ updatedAt: string;
124
+ user?: LinearUser;
125
+ issue?: LinearIssue;
126
+ }
127
+
128
+ // Project types
129
+ export interface LinearProject {
130
+ id: string;
131
+ name: string;
132
+ description?: string;
133
+ icon?: string;
134
+ color?: string;
135
+ state: string;
136
+ progress: number;
137
+ startDate?: string;
138
+ targetDate?: string;
139
+ createdAt: string;
140
+ updatedAt: string;
141
+ lead?: LinearUser;
142
+ teams?: { nodes: LinearTeam[] };
143
+ }
144
+
145
+ // Cycle types
146
+ export interface LinearCycle {
147
+ id: string;
148
+ number: number;
149
+ name?: string;
150
+ description?: string;
151
+ startsAt: string;
152
+ endsAt: string;
153
+ completedAt?: string;
154
+ progress: number;
155
+ createdAt: string;
156
+ updatedAt: string;
157
+ team?: LinearTeam;
158
+ }
159
+
160
+ // Pagination
161
+ export interface PageInfo {
162
+ hasNextPage: boolean;
163
+ hasPreviousPage: boolean;
164
+ startCursor?: string;
165
+ endCursor?: string;
166
+ }
167
+
168
+ export interface Connection<T> {
169
+ nodes: T[];
170
+ pageInfo: PageInfo;
171
+ }
172
+
173
+ // API Response types
174
+ export interface IssuesResponse {
175
+ issues: Connection<LinearIssue>;
176
+ }
177
+
178
+ export interface IssueResponse {
179
+ issue: LinearIssue;
180
+ }
181
+
182
+ export interface TeamsResponse {
183
+ teams: Connection<LinearTeam>;
184
+ }
185
+
186
+ export interface TeamResponse {
187
+ team: LinearTeam;
188
+ }
189
+
190
+ export interface UsersResponse {
191
+ users: Connection<LinearUser>;
192
+ }
193
+
194
+ export interface UserResponse {
195
+ user: LinearUser;
196
+ }
197
+
198
+ export interface ViewerResponse {
199
+ viewer: LinearUser;
200
+ }
201
+
202
+ export interface ProjectsResponse {
203
+ projects: Connection<LinearProject>;
204
+ }
205
+
206
+ export interface ProjectResponse {
207
+ project: LinearProject;
208
+ }
209
+
210
+ // Request options
211
+ export interface ListOptions {
212
+ first?: number;
213
+ after?: string;
214
+ orderBy?: string;
215
+ }
216
+
217
+ export interface IssueListOptions extends ListOptions {
218
+ teamId?: string;
219
+ projectId?: string;
220
+ assigneeId?: string;
221
+ stateId?: string;
222
+ filter?: IssueFilter;
223
+ }
224
+
225
+ export interface IssueFilter {
226
+ team?: { id?: { eq?: string } };
227
+ project?: { id?: { eq?: string } };
228
+ assignee?: { id?: { eq?: string } };
229
+ state?: { name?: { eq?: string }; type?: { eq?: string } };
230
+ priority?: { eq?: number };
231
+ }
232
+
233
+ export interface CreateIssueInput {
234
+ title: string;
235
+ description?: string;
236
+ teamId: string;
237
+ projectId?: string;
238
+ assigneeId?: string;
239
+ priority?: LinearPriority;
240
+ stateId?: string;
241
+ labelIds?: string[];
242
+ estimate?: number;
243
+ dueDate?: string;
244
+ parentId?: string;
245
+ }
246
+
247
+ export interface UpdateIssueInput {
248
+ title?: string;
249
+ description?: string;
250
+ projectId?: string;
251
+ assigneeId?: string;
252
+ priority?: LinearPriority;
253
+ stateId?: string;
254
+ labelIds?: string[];
255
+ estimate?: number;
256
+ dueDate?: string;
257
+ }
258
+
259
+ // Mutation response types
260
+ export interface IssuePayload {
261
+ success: boolean;
262
+ issue?: LinearIssue;
263
+ }
264
+
265
+ export interface CreateIssueResponse {
266
+ issueCreate: IssuePayload;
267
+ }
268
+
269
+ export interface UpdateIssueResponse {
270
+ issueUpdate: IssuePayload;
271
+ }
272
+
273
+ export interface ArchiveIssueResponse {
274
+ issueArchive: IssuePayload;
275
+ }
@@ -0,0 +1,249 @@
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, rmSync } from 'fs';
2
+ import { homedir } from 'os';
3
+ import { join } from 'path';
4
+ import type { CliConfig } from '../types';
5
+
6
+ const CONNECTOR_NAME = 'connect-linear';
7
+ const DEFAULT_PROFILE = 'default';
8
+ const CURRENT_PROFILE_FILE = 'current_profile';
9
+ const PROFILES_DIR = 'profiles';
10
+
11
+ // Store for --profile flag override
12
+ let profileOverride: string | undefined;
13
+
14
+ // Config directory: ~/.connect/connect-linear/
15
+ const BASE_CONFIG_DIR = join(homedir(), '.connect', CONNECTOR_NAME);
16
+
17
+ // ============================================
18
+ // Profile Management
19
+ // ============================================
20
+
21
+ export function setProfileOverride(profile: string | undefined): void {
22
+ profileOverride = profile;
23
+ }
24
+
25
+ export function getProfileOverride(): string | undefined {
26
+ return profileOverride;
27
+ }
28
+
29
+ function ensureBaseConfigDir(): void {
30
+ if (!existsSync(BASE_CONFIG_DIR)) {
31
+ mkdirSync(BASE_CONFIG_DIR, { recursive: true });
32
+ }
33
+ }
34
+
35
+ function getProfilesDir(): string {
36
+ return join(BASE_CONFIG_DIR, PROFILES_DIR);
37
+ }
38
+
39
+ function getCurrentProfileFile(): string {
40
+ return join(BASE_CONFIG_DIR, CURRENT_PROFILE_FILE);
41
+ }
42
+
43
+ /**
44
+ * Get the current active profile name
45
+ */
46
+ export function getCurrentProfile(): string {
47
+ if (profileOverride) {
48
+ return profileOverride;
49
+ }
50
+
51
+ ensureBaseConfigDir();
52
+
53
+ const profilesDir = getProfilesDir();
54
+ if (!existsSync(profilesDir)) {
55
+ mkdirSync(profilesDir, { recursive: true });
56
+ }
57
+
58
+ const currentProfileFile = getCurrentProfileFile();
59
+ if (existsSync(currentProfileFile)) {
60
+ try {
61
+ const profile = readFileSync(currentProfileFile, 'utf-8').trim();
62
+ if (profile && profileExists(profile)) {
63
+ return profile;
64
+ }
65
+ } catch {
66
+ // Fall through to default
67
+ }
68
+ }
69
+
70
+ return DEFAULT_PROFILE;
71
+ }
72
+
73
+ /**
74
+ * Set the current active profile
75
+ */
76
+ export function setCurrentProfile(profile: string): void {
77
+ ensureBaseConfigDir();
78
+
79
+ if (!profileExists(profile) && profile !== DEFAULT_PROFILE) {
80
+ throw new Error(`Profile "${profile}" does not exist. Create it first with "profile create ${profile}"`);
81
+ }
82
+
83
+ writeFileSync(getCurrentProfileFile(), profile);
84
+ }
85
+
86
+ /**
87
+ * Check if a profile exists
88
+ */
89
+ export function profileExists(profile: string): boolean {
90
+ const profileDir = join(getProfilesDir(), profile);
91
+ return existsSync(profileDir);
92
+ }
93
+
94
+ /**
95
+ * List all available profiles
96
+ */
97
+ export function listProfiles(): string[] {
98
+ ensureBaseConfigDir();
99
+
100
+ const profilesDir = getProfilesDir();
101
+ if (!existsSync(profilesDir)) {
102
+ return [];
103
+ }
104
+
105
+ return readdirSync(profilesDir, { withFileTypes: true })
106
+ .filter(dirent => dirent.isDirectory())
107
+ .map(dirent => dirent.name)
108
+ .sort();
109
+ }
110
+
111
+ /**
112
+ * Create a new profile
113
+ */
114
+ export function createProfile(profile: string): void {
115
+ ensureBaseConfigDir();
116
+
117
+ if (profileExists(profile)) {
118
+ throw new Error(`Profile "${profile}" already exists`);
119
+ }
120
+
121
+ // Validate profile name
122
+ if (!/^[a-zA-Z0-9_-]+$/.test(profile)) {
123
+ throw new Error('Profile name can only contain letters, numbers, hyphens, and underscores');
124
+ }
125
+
126
+ const profileDir = join(getProfilesDir(), profile);
127
+ mkdirSync(profileDir, { recursive: true });
128
+ }
129
+
130
+ /**
131
+ * Delete a profile
132
+ */
133
+ export function deleteProfile(profile: string): void {
134
+ if (profile === DEFAULT_PROFILE) {
135
+ throw new Error('Cannot delete the default profile');
136
+ }
137
+
138
+ if (!profileExists(profile)) {
139
+ throw new Error(`Profile "${profile}" does not exist`);
140
+ }
141
+
142
+ const currentProfile = getCurrentProfile();
143
+ if (currentProfile === profile) {
144
+ setCurrentProfile(DEFAULT_PROFILE);
145
+ }
146
+
147
+ const profileDir = join(getProfilesDir(), profile);
148
+ rmSync(profileDir, { recursive: true });
149
+ }
150
+
151
+ /**
152
+ * Get the config directory for the current profile
153
+ */
154
+ function getProfileDir(): string {
155
+ ensureBaseConfigDir();
156
+
157
+ const profilesDir = getProfilesDir();
158
+ if (!existsSync(profilesDir)) {
159
+ mkdirSync(profilesDir, { recursive: true });
160
+ }
161
+
162
+ const profile = getCurrentProfile();
163
+ const profileDir = join(profilesDir, profile);
164
+
165
+ if (!existsSync(profileDir)) {
166
+ mkdirSync(profileDir, { recursive: true });
167
+ }
168
+
169
+ return profileDir;
170
+ }
171
+
172
+ export function getConfigDir(): string {
173
+ return getProfileDir();
174
+ }
175
+
176
+ export function getBaseConfigDir(): string {
177
+ return BASE_CONFIG_DIR;
178
+ }
179
+
180
+ export function ensureConfigDir(): void {
181
+ getProfileDir();
182
+ }
183
+
184
+ // ============================================
185
+ // Config Management
186
+ // ============================================
187
+
188
+ export function loadConfig(): CliConfig {
189
+ ensureConfigDir();
190
+ const configFile = join(getProfileDir(), 'config.json');
191
+
192
+ if (!existsSync(configFile)) {
193
+ return {};
194
+ }
195
+
196
+ try {
197
+ const content = readFileSync(configFile, 'utf-8');
198
+ return JSON.parse(content);
199
+ } catch {
200
+ return {};
201
+ }
202
+ }
203
+
204
+ export function saveConfig(config: CliConfig): void {
205
+ ensureConfigDir();
206
+ const configFile = join(getProfileDir(), 'config.json');
207
+ writeFileSync(configFile, JSON.stringify(config, null, 2));
208
+ }
209
+
210
+ // ============================================
211
+ // API Key Management
212
+ // ============================================
213
+
214
+ export function getApiKey(): string | undefined {
215
+ return process.env.LINEAR_API_KEY || loadConfig().apiKey;
216
+ }
217
+
218
+ export function setApiKey(apiKey: string): void {
219
+ const config = loadConfig();
220
+ config.apiKey = apiKey;
221
+ saveConfig(config);
222
+ }
223
+
224
+ export function getDefaultTeamId(): string | undefined {
225
+ return loadConfig().defaultTeamId;
226
+ }
227
+
228
+ export function setDefaultTeamId(teamId: string): void {
229
+ const config = loadConfig();
230
+ config.defaultTeamId = teamId;
231
+ saveConfig(config);
232
+ }
233
+
234
+ // ============================================
235
+ // Utility Functions
236
+ // ============================================
237
+
238
+ export function clearConfig(): void {
239
+ saveConfig({});
240
+ }
241
+
242
+ export function isAuthenticated(): boolean {
243
+ const apiKey = getApiKey();
244
+ return apiKey !== undefined && apiKey !== '';
245
+ }
246
+
247
+ export function getActiveProfileName(): string {
248
+ return getCurrentProfile();
249
+ }
@@ -0,0 +1,119 @@
1
+ import chalk from 'chalk';
2
+
3
+ export type OutputFormat = 'json' | 'table' | 'pretty';
4
+
5
+ export function formatOutput(data: unknown, format: OutputFormat = 'pretty'): string {
6
+ switch (format) {
7
+ case 'json':
8
+ return JSON.stringify(data, null, 2);
9
+ case 'table':
10
+ return formatAsTable(data);
11
+ case 'pretty':
12
+ default:
13
+ return formatPretty(data);
14
+ }
15
+ }
16
+
17
+ function formatAsTable(data: unknown): string {
18
+ if (!Array.isArray(data)) {
19
+ data = [data];
20
+ }
21
+
22
+ const items = data as Record<string, unknown>[];
23
+ if (items.length === 0) {
24
+ return 'No data';
25
+ }
26
+
27
+ const firstItem = items[0];
28
+ if (!firstItem || typeof firstItem !== 'object') {
29
+ return 'No data';
30
+ }
31
+
32
+ const keys = Object.keys(firstItem);
33
+ const colWidths = keys.map(key => {
34
+ const maxValue = Math.max(
35
+ key.length,
36
+ ...items.map(item => String(item[key] ?? '').length)
37
+ );
38
+ return Math.min(maxValue, 40);
39
+ });
40
+
41
+ const header = keys.map((key, i) => key.padEnd(colWidths[i] ?? 10)).join(' | ');
42
+ const separator = colWidths.map(w => '-'.repeat(w)).join('-+-');
43
+
44
+ const rows = items.map(item =>
45
+ keys.map((key, i) => {
46
+ const value = String(item[key] ?? '');
47
+ const width = colWidths[i] ?? 10;
48
+ return value.length > width
49
+ ? value.substring(0, width - 3) + '...'
50
+ : value.padEnd(width);
51
+ }).join(' | ')
52
+ );
53
+
54
+ return [header, separator, ...rows].join('\n');
55
+ }
56
+
57
+ function formatPretty(data: unknown): string {
58
+ if (Array.isArray(data)) {
59
+ return data.map((item, i) => `${chalk.cyan(`[${i + 1}]`)} ${formatPrettyItem(item)}`).join('\n\n');
60
+ }
61
+ return formatPrettyItem(data);
62
+ }
63
+
64
+ function formatPrettyItem(item: unknown, indent = 0): string {
65
+ if (item === null || item === undefined) {
66
+ return chalk.gray('null');
67
+ }
68
+
69
+ if (typeof item !== 'object') {
70
+ return String(item);
71
+ }
72
+
73
+ const spaces = ' '.repeat(indent);
74
+ const entries = Object.entries(item as Record<string, unknown>);
75
+
76
+ return entries
77
+ .map(([key, value]) => {
78
+ if (Array.isArray(value)) {
79
+ if (value.length === 0) {
80
+ return `${spaces}${chalk.blue(key)}: ${chalk.gray('[]')}`;
81
+ }
82
+ if (typeof value[0] === 'object') {
83
+ return `${spaces}${chalk.blue(key)}:\n${value.map(v => formatPrettyItem(v, indent + 1)).join('\n')}`;
84
+ }
85
+ return `${spaces}${chalk.blue(key)}: ${value.join(', ')}`;
86
+ }
87
+
88
+ if (typeof value === 'object' && value !== null) {
89
+ return `${spaces}${chalk.blue(key)}:\n${formatPrettyItem(value, indent + 1)}`;
90
+ }
91
+
92
+ return `${spaces}${chalk.blue(key)}: ${chalk.white(String(value))}`;
93
+ })
94
+ .join('\n');
95
+ }
96
+
97
+ export function success(message: string): void {
98
+ console.log(chalk.green('OK'), message);
99
+ }
100
+
101
+ export function error(message: string): void {
102
+ console.error(chalk.red('Error:'), message);
103
+ }
104
+
105
+ export function warn(message: string): void {
106
+ console.warn(chalk.yellow('Warning:'), message);
107
+ }
108
+
109
+ export function info(message: string): void {
110
+ console.log(chalk.blue('Info:'), message);
111
+ }
112
+
113
+ export function heading(message: string): void {
114
+ console.log(chalk.bold.cyan(`\n${message}\n`));
115
+ }
116
+
117
+ export function print(data: unknown, format: OutputFormat = 'pretty'): void {
118
+ console.log(formatOutput(data, format));
119
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "esModuleInterop": true,
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "declaration": true,
10
+ "outDir": "./dist",
11
+ "rootDir": "./src",
12
+ "types": ["bun-types"]
13
+ },
14
+ "include": ["src/**/*"],
15
+ "exclude": ["node_modules", "dist", "bin"]
16
+ }
@@ -0,0 +1,7 @@
1
+ # Slack Bot Token
2
+ # Get from: https://api.slack.com/apps → OAuth & Permissions
3
+
4
+ SLACK_BOT_TOKEN=xoxb-your-bot-token
5
+
6
+ # Optional: User Token for user-level API access
7
+ # SLACK_USER_TOKEN=xoxp-your-user-token