@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.
- package/bin/index.js +71 -1
- package/bin/mcp.js +71 -1
- package/bin/serve.js +70 -0
- package/connectors/connect-asana/.env.example +11 -0
- package/connectors/connect-asana/CLAUDE.md +128 -0
- package/connectors/connect-asana/README.md +193 -0
- package/connectors/connect-asana/package.json +52 -0
- package/connectors/connect-asana/src/api/client.ts +119 -0
- package/connectors/connect-asana/src/api/index.ts +319 -0
- package/connectors/connect-asana/src/cli/index.ts +731 -0
- package/connectors/connect-asana/src/index.ts +19 -0
- package/connectors/connect-asana/src/types/index.ts +270 -0
- package/connectors/connect-asana/src/utils/config.ts +171 -0
- package/connectors/connect-asana/src/utils/output.ts +119 -0
- package/connectors/connect-asana/tsconfig.json +16 -0
- package/connectors/connect-clickup/.env.example +11 -0
- package/connectors/connect-clickup/CLAUDE.md +128 -0
- package/connectors/connect-clickup/README.md +193 -0
- package/connectors/connect-clickup/package.json +52 -0
- package/connectors/connect-clickup/src/api/client.ts +116 -0
- package/connectors/connect-clickup/src/api/index.ts +400 -0
- package/connectors/connect-clickup/src/cli/index.ts +625 -0
- package/connectors/connect-clickup/src/index.ts +19 -0
- package/connectors/connect-clickup/src/types/index.ts +591 -0
- package/connectors/connect-clickup/src/utils/config.ts +157 -0
- package/connectors/connect-clickup/src/utils/output.ts +119 -0
- package/connectors/connect-clickup/tsconfig.json +16 -0
- package/connectors/connect-confluence/.env.example +11 -0
- package/connectors/connect-confluence/CLAUDE.md +272 -0
- package/connectors/connect-confluence/README.md +193 -0
- package/connectors/connect-confluence/package.json +53 -0
- package/connectors/connect-confluence/scripts/release.ts +179 -0
- package/connectors/connect-confluence/src/api/client.ts +213 -0
- package/connectors/connect-confluence/src/api/example.ts +48 -0
- package/connectors/connect-confluence/src/api/index.ts +51 -0
- package/connectors/connect-confluence/src/cli/index.ts +254 -0
- package/connectors/connect-confluence/src/index.ts +103 -0
- package/connectors/connect-confluence/src/types/index.ts +237 -0
- package/connectors/connect-confluence/src/utils/auth.ts +274 -0
- package/connectors/connect-confluence/src/utils/bulk.ts +212 -0
- package/connectors/connect-confluence/src/utils/config.ts +326 -0
- package/connectors/connect-confluence/src/utils/output.ts +175 -0
- package/connectors/connect-confluence/src/utils/settings.ts +114 -0
- package/connectors/connect-confluence/src/utils/storage.ts +198 -0
- package/connectors/connect-confluence/tsconfig.json +16 -0
- package/connectors/connect-jira/.env.example +11 -0
- package/connectors/connect-jira/CLAUDE.md +128 -0
- package/connectors/connect-jira/README.md +193 -0
- package/connectors/connect-jira/package.json +53 -0
- package/connectors/connect-jira/src/api/client.ts +131 -0
- package/connectors/connect-jira/src/api/index.ts +266 -0
- package/connectors/connect-jira/src/cli/index.ts +653 -0
- package/connectors/connect-jira/src/index.ts +23 -0
- package/connectors/connect-jira/src/types/index.ts +448 -0
- package/connectors/connect-jira/src/utils/config.ts +179 -0
- package/connectors/connect-jira/src/utils/output.ts +119 -0
- package/connectors/connect-jira/tsconfig.json +16 -0
- package/connectors/connect-linear/CLAUDE.md +88 -0
- package/connectors/connect-linear/README.md +201 -0
- package/connectors/connect-linear/package.json +45 -0
- package/connectors/connect-linear/src/api/client.ts +62 -0
- package/connectors/connect-linear/src/api/index.ts +46 -0
- package/connectors/connect-linear/src/api/issues.ts +247 -0
- package/connectors/connect-linear/src/api/projects.ts +179 -0
- package/connectors/connect-linear/src/api/teams.ts +125 -0
- package/connectors/connect-linear/src/api/users.ts +112 -0
- package/connectors/connect-linear/src/cli/index.ts +560 -0
- package/connectors/connect-linear/src/index.ts +27 -0
- package/connectors/connect-linear/src/types/index.ts +275 -0
- package/connectors/connect-linear/src/utils/config.ts +249 -0
- package/connectors/connect-linear/src/utils/output.ts +119 -0
- package/connectors/connect-linear/tsconfig.json +16 -0
- package/connectors/connect-slack/.env.example +7 -0
- package/connectors/connect-slack/CLAUDE.md +69 -0
- package/connectors/connect-slack/README.md +150 -0
- package/connectors/connect-slack/package.json +44 -0
- package/connectors/connect-slack/src/api/channels.ts +112 -0
- package/connectors/connect-slack/src/api/client.ts +97 -0
- package/connectors/connect-slack/src/api/index.ts +42 -0
- package/connectors/connect-slack/src/api/messages.ts +127 -0
- package/connectors/connect-slack/src/api/users.ts +110 -0
- package/connectors/connect-slack/src/cli/index.ts +494 -0
- package/connectors/connect-slack/src/index.ts +21 -0
- package/connectors/connect-slack/src/types/index.ts +263 -0
- package/connectors/connect-slack/src/utils/config.ts +297 -0
- package/connectors/connect-slack/src/utils/output.ts +119 -0
- package/connectors/connect-slack/tsconfig.json +16 -0
- package/connectors/connect-telegram/.env.example +2 -0
- package/connectors/connect-telegram/package.json +49 -0
- package/connectors/connect-todoist/.env.example +11 -0
- package/connectors/connect-todoist/CLAUDE.md +104 -0
- package/connectors/connect-todoist/README.md +193 -0
- package/connectors/connect-todoist/package.json +52 -0
- package/connectors/connect-todoist/src/api/client.ts +117 -0
- package/connectors/connect-todoist/src/api/index.ts +188 -0
- package/connectors/connect-todoist/src/cli/index.ts +990 -0
- package/connectors/connect-todoist/src/index.ts +21 -0
- package/connectors/connect-todoist/src/types/index.ts +240 -0
- package/connectors/connect-todoist/src/utils/config.ts +157 -0
- package/connectors/connect-todoist/src/utils/output.ts +119 -0
- package/connectors/connect-todoist/tsconfig.json +16 -0
- package/connectors/connect-trello/.env.example +11 -0
- package/connectors/connect-trello/CLAUDE.md +128 -0
- package/connectors/connect-trello/README.md +193 -0
- package/connectors/connect-trello/package.json +53 -0
- package/connectors/connect-trello/src/api/client.ts +128 -0
- package/connectors/connect-trello/src/api/index.ts +278 -0
- package/connectors/connect-trello/src/cli/index.ts +737 -0
- package/connectors/connect-trello/src/index.ts +21 -0
- package/connectors/connect-trello/src/types/index.ts +314 -0
- package/connectors/connect-trello/src/utils/config.ts +182 -0
- package/connectors/connect-trello/src/utils/output.ts +119 -0
- package/connectors/connect-trello/tsconfig.json +16 -0
- package/connectors/connect-whatsapp/.env.example +11 -0
- package/connectors/connect-whatsapp/CLAUDE.md +113 -0
- package/connectors/connect-whatsapp/README.md +193 -0
- package/connectors/connect-whatsapp/package.json +53 -0
- package/connectors/connect-whatsapp/src/api/client.ts +133 -0
- package/connectors/connect-whatsapp/src/api/index.ts +365 -0
- package/connectors/connect-whatsapp/src/cli/index.ts +686 -0
- package/connectors/connect-whatsapp/src/index.ts +25 -0
- package/connectors/connect-whatsapp/src/types/index.ts +502 -0
- package/connectors/connect-whatsapp/src/utils/config.ts +179 -0
- package/connectors/connect-whatsapp/src/utils/output.ts +119 -0
- package/connectors/connect-whatsapp/tsconfig.json +16 -0
- package/dist/index.js +70 -0
- package/package.json +1 -1
|
@@ -0,0 +1,731 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { Asana } from '../api';
|
|
5
|
+
import {
|
|
6
|
+
getAccessToken,
|
|
7
|
+
setAccessToken,
|
|
8
|
+
clearConfig,
|
|
9
|
+
getConfigDir,
|
|
10
|
+
setProfileOverride,
|
|
11
|
+
getCurrentProfile,
|
|
12
|
+
setCurrentProfile,
|
|
13
|
+
listProfiles,
|
|
14
|
+
createProfile,
|
|
15
|
+
deleteProfile,
|
|
16
|
+
profileExists,
|
|
17
|
+
loadProfile,
|
|
18
|
+
} from '../utils/config';
|
|
19
|
+
import type { OutputFormat } from '../utils/output';
|
|
20
|
+
import { success, error, info, print, warn } from '../utils/output';
|
|
21
|
+
|
|
22
|
+
const CONNECTOR_NAME = 'connect-asana';
|
|
23
|
+
const VERSION = '0.0.1';
|
|
24
|
+
|
|
25
|
+
const program = new Command();
|
|
26
|
+
|
|
27
|
+
program
|
|
28
|
+
.name(CONNECTOR_NAME)
|
|
29
|
+
.description('Asana connector - Projects, tasks, workspaces, and teams management')
|
|
30
|
+
.version(VERSION)
|
|
31
|
+
.option('-t, --token <token>', 'Access token (overrides config)')
|
|
32
|
+
.option('-f, --format <format>', 'Output format (json, pretty)', 'pretty')
|
|
33
|
+
.option('-p, --profile <profile>', 'Use a specific profile')
|
|
34
|
+
.hook('preAction', (thisCommand) => {
|
|
35
|
+
const opts = thisCommand.opts();
|
|
36
|
+
if (opts.profile) {
|
|
37
|
+
if (!profileExists(opts.profile)) {
|
|
38
|
+
error(`Profile "${opts.profile}" does not exist. Create it with "${CONNECTOR_NAME} profile create ${opts.profile}"`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
setProfileOverride(opts.profile);
|
|
42
|
+
}
|
|
43
|
+
if (opts.token) {
|
|
44
|
+
process.env.ASANA_ACCESS_TOKEN = opts.token;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
function getFormat(cmd: Command): OutputFormat {
|
|
49
|
+
const parent = cmd.parent;
|
|
50
|
+
return (parent?.opts().format || 'pretty') as OutputFormat;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getClient(): Asana {
|
|
54
|
+
const accessToken = getAccessToken();
|
|
55
|
+
if (!accessToken) {
|
|
56
|
+
error(`No access token configured. Run "${CONNECTOR_NAME} config set-token <token>" or set ASANA_ACCESS_TOKEN environment variable.`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
return new Asana({ accessToken });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ============================================
|
|
63
|
+
// Profile Commands
|
|
64
|
+
// ============================================
|
|
65
|
+
const profileCmd = program
|
|
66
|
+
.command('profile')
|
|
67
|
+
.description('Manage configuration profiles');
|
|
68
|
+
|
|
69
|
+
profileCmd
|
|
70
|
+
.command('list')
|
|
71
|
+
.description('List all profiles')
|
|
72
|
+
.action(() => {
|
|
73
|
+
const profiles = listProfiles();
|
|
74
|
+
const current = getCurrentProfile();
|
|
75
|
+
|
|
76
|
+
if (profiles.length === 0) {
|
|
77
|
+
info('No profiles found. Use "profile create <name>" to create one.');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
success(`Profiles:`);
|
|
82
|
+
profiles.forEach(p => {
|
|
83
|
+
const isActive = p === current ? chalk.green(' (active)') : '';
|
|
84
|
+
console.log(` ${p}${isActive}`);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
profileCmd
|
|
89
|
+
.command('use <name>')
|
|
90
|
+
.description('Switch to a profile')
|
|
91
|
+
.action((name: string) => {
|
|
92
|
+
if (!profileExists(name)) {
|
|
93
|
+
error(`Profile "${name}" does not exist. Create it with "profile create ${name}"`);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
setCurrentProfile(name);
|
|
97
|
+
success(`Switched to profile: ${name}`);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
profileCmd
|
|
101
|
+
.command('create <name>')
|
|
102
|
+
.description('Create a new profile')
|
|
103
|
+
.option('--token <token>', 'Access token')
|
|
104
|
+
.option('--use', 'Switch to this profile after creation')
|
|
105
|
+
.action((name: string, opts) => {
|
|
106
|
+
if (profileExists(name)) {
|
|
107
|
+
error(`Profile "${name}" already exists`);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
createProfile(name, {
|
|
112
|
+
accessToken: opts.token,
|
|
113
|
+
});
|
|
114
|
+
success(`Profile "${name}" created`);
|
|
115
|
+
|
|
116
|
+
if (opts.use) {
|
|
117
|
+
setCurrentProfile(name);
|
|
118
|
+
info(`Switched to profile: ${name}`);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
profileCmd
|
|
123
|
+
.command('delete <name>')
|
|
124
|
+
.description('Delete a profile')
|
|
125
|
+
.action((name: string) => {
|
|
126
|
+
if (name === 'default') {
|
|
127
|
+
error('Cannot delete the default profile');
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
if (deleteProfile(name)) {
|
|
131
|
+
success(`Profile "${name}" deleted`);
|
|
132
|
+
} else {
|
|
133
|
+
error(`Profile "${name}" not found`);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
profileCmd
|
|
139
|
+
.command('show [name]')
|
|
140
|
+
.description('Show profile configuration')
|
|
141
|
+
.action((name?: string) => {
|
|
142
|
+
const profileName = name || getCurrentProfile();
|
|
143
|
+
const config = loadProfile(profileName);
|
|
144
|
+
const active = getCurrentProfile();
|
|
145
|
+
|
|
146
|
+
console.log(chalk.bold(`Profile: ${profileName}${profileName === active ? chalk.green(' (active)') : ''}`));
|
|
147
|
+
info(`Access Token: ${config.accessToken ? `${config.accessToken.substring(0, 8)}...` : chalk.gray('not set')}`);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// ============================================
|
|
151
|
+
// Config Commands
|
|
152
|
+
// ============================================
|
|
153
|
+
const configCmd = program
|
|
154
|
+
.command('config')
|
|
155
|
+
.description('Manage CLI configuration (for active profile)');
|
|
156
|
+
|
|
157
|
+
configCmd
|
|
158
|
+
.command('set-token <token>')
|
|
159
|
+
.description('Set access token')
|
|
160
|
+
.action((token: string) => {
|
|
161
|
+
setAccessToken(token);
|
|
162
|
+
success(`Access token saved to profile: ${getCurrentProfile()}`);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
configCmd
|
|
166
|
+
.command('show')
|
|
167
|
+
.description('Show current configuration')
|
|
168
|
+
.action(() => {
|
|
169
|
+
const profileName = getCurrentProfile();
|
|
170
|
+
const accessToken = getAccessToken();
|
|
171
|
+
|
|
172
|
+
console.log(chalk.bold(`Active Profile: ${profileName}`));
|
|
173
|
+
info(`Config directory: ${getConfigDir()}`);
|
|
174
|
+
info(`Access Token: ${accessToken ? `${accessToken.substring(0, 8)}...` : chalk.gray('not set')}`);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
configCmd
|
|
178
|
+
.command('clear')
|
|
179
|
+
.description('Clear configuration for active profile')
|
|
180
|
+
.action(() => {
|
|
181
|
+
clearConfig();
|
|
182
|
+
success(`Configuration cleared for profile: ${getCurrentProfile()}`);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// ============================================
|
|
186
|
+
// Workspace Commands
|
|
187
|
+
// ============================================
|
|
188
|
+
const workspaceCmd = program
|
|
189
|
+
.command('workspace')
|
|
190
|
+
.description('Workspace management');
|
|
191
|
+
|
|
192
|
+
workspaceCmd
|
|
193
|
+
.command('list')
|
|
194
|
+
.description('List all workspaces')
|
|
195
|
+
.action(async () => {
|
|
196
|
+
try {
|
|
197
|
+
const client = getClient();
|
|
198
|
+
const result = await client.listWorkspaces();
|
|
199
|
+
print(result, getFormat(workspaceCmd));
|
|
200
|
+
} catch (err) {
|
|
201
|
+
error(String(err));
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
workspaceCmd
|
|
207
|
+
.command('get <gid>')
|
|
208
|
+
.description('Get a workspace by GID')
|
|
209
|
+
.action(async (gid: string) => {
|
|
210
|
+
try {
|
|
211
|
+
const client = getClient();
|
|
212
|
+
const result = await client.getWorkspace(gid);
|
|
213
|
+
print(result, getFormat(workspaceCmd));
|
|
214
|
+
} catch (err) {
|
|
215
|
+
error(String(err));
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// ============================================
|
|
221
|
+
// User Commands
|
|
222
|
+
// ============================================
|
|
223
|
+
const userCmd = program
|
|
224
|
+
.command('user')
|
|
225
|
+
.description('User management');
|
|
226
|
+
|
|
227
|
+
userCmd
|
|
228
|
+
.command('me')
|
|
229
|
+
.description('Get current user')
|
|
230
|
+
.action(async () => {
|
|
231
|
+
try {
|
|
232
|
+
const client = getClient();
|
|
233
|
+
const result = await client.getMe();
|
|
234
|
+
print(result, getFormat(userCmd));
|
|
235
|
+
} catch (err) {
|
|
236
|
+
error(String(err));
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
userCmd
|
|
242
|
+
.command('get <gid>')
|
|
243
|
+
.description('Get a user by GID')
|
|
244
|
+
.action(async (gid: string) => {
|
|
245
|
+
try {
|
|
246
|
+
const client = getClient();
|
|
247
|
+
const result = await client.getUser(gid);
|
|
248
|
+
print(result, getFormat(userCmd));
|
|
249
|
+
} catch (err) {
|
|
250
|
+
error(String(err));
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
userCmd
|
|
256
|
+
.command('list')
|
|
257
|
+
.description('List users in a workspace')
|
|
258
|
+
.requiredOption('--workspace <gid>', 'Workspace GID')
|
|
259
|
+
.action(async (opts) => {
|
|
260
|
+
try {
|
|
261
|
+
const client = getClient();
|
|
262
|
+
const result = await client.listUsersInWorkspace(opts.workspace);
|
|
263
|
+
print(result, getFormat(userCmd));
|
|
264
|
+
} catch (err) {
|
|
265
|
+
error(String(err));
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// ============================================
|
|
271
|
+
// Team Commands
|
|
272
|
+
// ============================================
|
|
273
|
+
const teamCmd = program
|
|
274
|
+
.command('team')
|
|
275
|
+
.description('Team management');
|
|
276
|
+
|
|
277
|
+
teamCmd
|
|
278
|
+
.command('list')
|
|
279
|
+
.description('List teams in a workspace/organization')
|
|
280
|
+
.requiredOption('--workspace <gid>', 'Workspace/Organization GID')
|
|
281
|
+
.action(async (opts) => {
|
|
282
|
+
try {
|
|
283
|
+
const client = getClient();
|
|
284
|
+
const result = await client.listTeamsInWorkspace(opts.workspace);
|
|
285
|
+
print(result, getFormat(teamCmd));
|
|
286
|
+
} catch (err) {
|
|
287
|
+
error(String(err));
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
teamCmd
|
|
293
|
+
.command('get <gid>')
|
|
294
|
+
.description('Get a team by GID')
|
|
295
|
+
.action(async (gid: string) => {
|
|
296
|
+
try {
|
|
297
|
+
const client = getClient();
|
|
298
|
+
const result = await client.getTeam(gid);
|
|
299
|
+
print(result, getFormat(teamCmd));
|
|
300
|
+
} catch (err) {
|
|
301
|
+
error(String(err));
|
|
302
|
+
process.exit(1);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// ============================================
|
|
307
|
+
// Project Commands
|
|
308
|
+
// ============================================
|
|
309
|
+
const projectCmd = program
|
|
310
|
+
.command('project')
|
|
311
|
+
.description('Project management');
|
|
312
|
+
|
|
313
|
+
projectCmd
|
|
314
|
+
.command('list')
|
|
315
|
+
.description('List projects')
|
|
316
|
+
.option('--workspace <gid>', 'Filter by workspace GID')
|
|
317
|
+
.option('--team <gid>', 'Filter by team GID')
|
|
318
|
+
.option('--archived', 'Include archived projects')
|
|
319
|
+
.option('--limit <number>', 'Maximum results', '100')
|
|
320
|
+
.action(async (opts) => {
|
|
321
|
+
try {
|
|
322
|
+
const client = getClient();
|
|
323
|
+
const result = await client.listProjects({
|
|
324
|
+
workspace: opts.workspace,
|
|
325
|
+
team: opts.team,
|
|
326
|
+
archived: opts.archived,
|
|
327
|
+
limit: parseInt(opts.limit),
|
|
328
|
+
});
|
|
329
|
+
print(result, getFormat(projectCmd));
|
|
330
|
+
} catch (err) {
|
|
331
|
+
error(String(err));
|
|
332
|
+
process.exit(1);
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
projectCmd
|
|
337
|
+
.command('get <gid>')
|
|
338
|
+
.description('Get a project by GID')
|
|
339
|
+
.action(async (gid: string) => {
|
|
340
|
+
try {
|
|
341
|
+
const client = getClient();
|
|
342
|
+
const result = await client.getProject(gid);
|
|
343
|
+
print(result, getFormat(projectCmd));
|
|
344
|
+
} catch (err) {
|
|
345
|
+
error(String(err));
|
|
346
|
+
process.exit(1);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
projectCmd
|
|
351
|
+
.command('create')
|
|
352
|
+
.description('Create a new project')
|
|
353
|
+
.requiredOption('--name <name>', 'Project name')
|
|
354
|
+
.option('--workspace <gid>', 'Workspace GID')
|
|
355
|
+
.option('--team <gid>', 'Team GID')
|
|
356
|
+
.option('--notes <notes>', 'Project notes')
|
|
357
|
+
.option('--color <color>', 'Project color')
|
|
358
|
+
.option('--public', 'Make project public')
|
|
359
|
+
.action(async (opts) => {
|
|
360
|
+
try {
|
|
361
|
+
const client = getClient();
|
|
362
|
+
const result = await client.createProject({
|
|
363
|
+
name: opts.name,
|
|
364
|
+
workspace: opts.workspace,
|
|
365
|
+
team: opts.team,
|
|
366
|
+
notes: opts.notes,
|
|
367
|
+
color: opts.color,
|
|
368
|
+
public: opts.public,
|
|
369
|
+
});
|
|
370
|
+
success('Project created!');
|
|
371
|
+
print(result, getFormat(projectCmd));
|
|
372
|
+
} catch (err) {
|
|
373
|
+
error(String(err));
|
|
374
|
+
process.exit(1);
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
projectCmd
|
|
379
|
+
.command('delete <gid>')
|
|
380
|
+
.description('Delete a project')
|
|
381
|
+
.action(async (gid: string) => {
|
|
382
|
+
try {
|
|
383
|
+
const client = getClient();
|
|
384
|
+
await client.deleteProject(gid);
|
|
385
|
+
success(`Project ${gid} deleted`);
|
|
386
|
+
} catch (err) {
|
|
387
|
+
error(String(err));
|
|
388
|
+
process.exit(1);
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// ============================================
|
|
393
|
+
// Section Commands
|
|
394
|
+
// ============================================
|
|
395
|
+
const sectionCmd = program
|
|
396
|
+
.command('section')
|
|
397
|
+
.description('Section management');
|
|
398
|
+
|
|
399
|
+
sectionCmd
|
|
400
|
+
.command('list')
|
|
401
|
+
.description('List sections in a project')
|
|
402
|
+
.requiredOption('--project <gid>', 'Project GID')
|
|
403
|
+
.action(async (opts) => {
|
|
404
|
+
try {
|
|
405
|
+
const client = getClient();
|
|
406
|
+
const result = await client.listSections(opts.project);
|
|
407
|
+
print(result, getFormat(sectionCmd));
|
|
408
|
+
} catch (err) {
|
|
409
|
+
error(String(err));
|
|
410
|
+
process.exit(1);
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
sectionCmd
|
|
415
|
+
.command('get <gid>')
|
|
416
|
+
.description('Get a section by GID')
|
|
417
|
+
.action(async (gid: string) => {
|
|
418
|
+
try {
|
|
419
|
+
const client = getClient();
|
|
420
|
+
const result = await client.getSection(gid);
|
|
421
|
+
print(result, getFormat(sectionCmd));
|
|
422
|
+
} catch (err) {
|
|
423
|
+
error(String(err));
|
|
424
|
+
process.exit(1);
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
sectionCmd
|
|
429
|
+
.command('create')
|
|
430
|
+
.description('Create a new section')
|
|
431
|
+
.requiredOption('--project <gid>', 'Project GID')
|
|
432
|
+
.requiredOption('--name <name>', 'Section name')
|
|
433
|
+
.action(async (opts) => {
|
|
434
|
+
try {
|
|
435
|
+
const client = getClient();
|
|
436
|
+
const result = await client.createSection(opts.project, { name: opts.name });
|
|
437
|
+
success('Section created!');
|
|
438
|
+
print(result, getFormat(sectionCmd));
|
|
439
|
+
} catch (err) {
|
|
440
|
+
error(String(err));
|
|
441
|
+
process.exit(1);
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
sectionCmd
|
|
446
|
+
.command('delete <gid>')
|
|
447
|
+
.description('Delete a section')
|
|
448
|
+
.action(async (gid: string) => {
|
|
449
|
+
try {
|
|
450
|
+
const client = getClient();
|
|
451
|
+
await client.deleteSection(gid);
|
|
452
|
+
success(`Section ${gid} deleted`);
|
|
453
|
+
} catch (err) {
|
|
454
|
+
error(String(err));
|
|
455
|
+
process.exit(1);
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
// ============================================
|
|
460
|
+
// Task Commands
|
|
461
|
+
// ============================================
|
|
462
|
+
const taskCmd = program
|
|
463
|
+
.command('task')
|
|
464
|
+
.description('Task management');
|
|
465
|
+
|
|
466
|
+
taskCmd
|
|
467
|
+
.command('list')
|
|
468
|
+
.description('List tasks')
|
|
469
|
+
.option('--project <gid>', 'Filter by project GID')
|
|
470
|
+
.option('--section <gid>', 'Filter by section GID')
|
|
471
|
+
.option('--assignee <gid>', 'Filter by assignee GID (requires --workspace)')
|
|
472
|
+
.option('--workspace <gid>', 'Workspace GID (required with --assignee)')
|
|
473
|
+
.option('--limit <number>', 'Maximum results', '100')
|
|
474
|
+
.action(async (opts) => {
|
|
475
|
+
try {
|
|
476
|
+
const client = getClient();
|
|
477
|
+
const result = await client.listTasks({
|
|
478
|
+
project: opts.project,
|
|
479
|
+
section: opts.section,
|
|
480
|
+
assignee: opts.assignee,
|
|
481
|
+
workspace: opts.workspace,
|
|
482
|
+
limit: parseInt(opts.limit),
|
|
483
|
+
});
|
|
484
|
+
print(result, getFormat(taskCmd));
|
|
485
|
+
} catch (err) {
|
|
486
|
+
error(String(err));
|
|
487
|
+
process.exit(1);
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
taskCmd
|
|
492
|
+
.command('get <gid>')
|
|
493
|
+
.description('Get a task by GID')
|
|
494
|
+
.action(async (gid: string) => {
|
|
495
|
+
try {
|
|
496
|
+
const client = getClient();
|
|
497
|
+
const result = await client.getTask(gid);
|
|
498
|
+
print(result, getFormat(taskCmd));
|
|
499
|
+
} catch (err) {
|
|
500
|
+
error(String(err));
|
|
501
|
+
process.exit(1);
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
taskCmd
|
|
506
|
+
.command('create')
|
|
507
|
+
.description('Create a new task')
|
|
508
|
+
.requiredOption('--name <name>', 'Task name')
|
|
509
|
+
.option('--workspace <gid>', 'Workspace GID')
|
|
510
|
+
.option('--projects <gids>', 'Project GIDs (comma-separated)')
|
|
511
|
+
.option('--assignee <gid>', 'Assignee GID')
|
|
512
|
+
.option('--notes <notes>', 'Task notes')
|
|
513
|
+
.option('--due-on <date>', 'Due date (YYYY-MM-DD)')
|
|
514
|
+
.action(async (opts) => {
|
|
515
|
+
try {
|
|
516
|
+
const client = getClient();
|
|
517
|
+
const result = await client.createTask({
|
|
518
|
+
name: opts.name,
|
|
519
|
+
workspace: opts.workspace,
|
|
520
|
+
projects: opts.projects?.split(','),
|
|
521
|
+
assignee: opts.assignee,
|
|
522
|
+
notes: opts.notes,
|
|
523
|
+
due_on: opts.dueOn,
|
|
524
|
+
});
|
|
525
|
+
success('Task created!');
|
|
526
|
+
print(result, getFormat(taskCmd));
|
|
527
|
+
} catch (err) {
|
|
528
|
+
error(String(err));
|
|
529
|
+
process.exit(1);
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
taskCmd
|
|
534
|
+
.command('update <gid>')
|
|
535
|
+
.description('Update a task')
|
|
536
|
+
.option('--name <name>', 'Task name')
|
|
537
|
+
.option('--assignee <gid>', 'Assignee GID')
|
|
538
|
+
.option('--notes <notes>', 'Task notes')
|
|
539
|
+
.option('--due-on <date>', 'Due date (YYYY-MM-DD)')
|
|
540
|
+
.option('--completed', 'Mark as completed')
|
|
541
|
+
.action(async (gid: string, opts) => {
|
|
542
|
+
try {
|
|
543
|
+
const client = getClient();
|
|
544
|
+
const result = await client.updateTask(gid, {
|
|
545
|
+
name: opts.name,
|
|
546
|
+
assignee: opts.assignee,
|
|
547
|
+
notes: opts.notes,
|
|
548
|
+
due_on: opts.dueOn,
|
|
549
|
+
completed: opts.completed,
|
|
550
|
+
});
|
|
551
|
+
success('Task updated!');
|
|
552
|
+
print(result, getFormat(taskCmd));
|
|
553
|
+
} catch (err) {
|
|
554
|
+
error(String(err));
|
|
555
|
+
process.exit(1);
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
taskCmd
|
|
560
|
+
.command('delete <gid>')
|
|
561
|
+
.description('Delete a task')
|
|
562
|
+
.action(async (gid: string) => {
|
|
563
|
+
try {
|
|
564
|
+
const client = getClient();
|
|
565
|
+
await client.deleteTask(gid);
|
|
566
|
+
success(`Task ${gid} deleted`);
|
|
567
|
+
} catch (err) {
|
|
568
|
+
error(String(err));
|
|
569
|
+
process.exit(1);
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
taskCmd
|
|
574
|
+
.command('subtasks <gid>')
|
|
575
|
+
.description('List subtasks of a task')
|
|
576
|
+
.action(async (gid: string) => {
|
|
577
|
+
try {
|
|
578
|
+
const client = getClient();
|
|
579
|
+
const result = await client.getSubtasks(gid);
|
|
580
|
+
print(result, getFormat(taskCmd));
|
|
581
|
+
} catch (err) {
|
|
582
|
+
error(String(err));
|
|
583
|
+
process.exit(1);
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
// ============================================
|
|
588
|
+
// Tag Commands
|
|
589
|
+
// ============================================
|
|
590
|
+
const tagCmd = program
|
|
591
|
+
.command('tag')
|
|
592
|
+
.description('Tag management');
|
|
593
|
+
|
|
594
|
+
tagCmd
|
|
595
|
+
.command('list')
|
|
596
|
+
.description('List tags in a workspace')
|
|
597
|
+
.requiredOption('--workspace <gid>', 'Workspace GID')
|
|
598
|
+
.action(async (opts) => {
|
|
599
|
+
try {
|
|
600
|
+
const client = getClient();
|
|
601
|
+
const result = await client.listTags(opts.workspace);
|
|
602
|
+
print(result, getFormat(tagCmd));
|
|
603
|
+
} catch (err) {
|
|
604
|
+
error(String(err));
|
|
605
|
+
process.exit(1);
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
tagCmd
|
|
610
|
+
.command('get <gid>')
|
|
611
|
+
.description('Get a tag by GID')
|
|
612
|
+
.action(async (gid: string) => {
|
|
613
|
+
try {
|
|
614
|
+
const client = getClient();
|
|
615
|
+
const result = await client.getTag(gid);
|
|
616
|
+
print(result, getFormat(tagCmd));
|
|
617
|
+
} catch (err) {
|
|
618
|
+
error(String(err));
|
|
619
|
+
process.exit(1);
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
tagCmd
|
|
624
|
+
.command('create')
|
|
625
|
+
.description('Create a new tag')
|
|
626
|
+
.requiredOption('--name <name>', 'Tag name')
|
|
627
|
+
.requiredOption('--workspace <gid>', 'Workspace GID')
|
|
628
|
+
.option('--color <color>', 'Tag color')
|
|
629
|
+
.action(async (opts) => {
|
|
630
|
+
try {
|
|
631
|
+
const client = getClient();
|
|
632
|
+
const result = await client.createTag({
|
|
633
|
+
name: opts.name,
|
|
634
|
+
workspace: opts.workspace,
|
|
635
|
+
color: opts.color,
|
|
636
|
+
});
|
|
637
|
+
success('Tag created!');
|
|
638
|
+
print(result, getFormat(tagCmd));
|
|
639
|
+
} catch (err) {
|
|
640
|
+
error(String(err));
|
|
641
|
+
process.exit(1);
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
// ============================================
|
|
646
|
+
// Story (Comment) Commands
|
|
647
|
+
// ============================================
|
|
648
|
+
const storyCmd = program
|
|
649
|
+
.command('story')
|
|
650
|
+
.alias('comment')
|
|
651
|
+
.description('Story/Comment management');
|
|
652
|
+
|
|
653
|
+
storyCmd
|
|
654
|
+
.command('list')
|
|
655
|
+
.description('List stories/comments on a task')
|
|
656
|
+
.requiredOption('--task <gid>', 'Task GID')
|
|
657
|
+
.action(async (opts) => {
|
|
658
|
+
try {
|
|
659
|
+
const client = getClient();
|
|
660
|
+
const result = await client.listStories(opts.task);
|
|
661
|
+
print(result, getFormat(storyCmd));
|
|
662
|
+
} catch (err) {
|
|
663
|
+
error(String(err));
|
|
664
|
+
process.exit(1);
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
storyCmd
|
|
669
|
+
.command('create')
|
|
670
|
+
.description('Add a comment to a task')
|
|
671
|
+
.requiredOption('--task <gid>', 'Task GID')
|
|
672
|
+
.requiredOption('--text <text>', 'Comment text')
|
|
673
|
+
.action(async (opts) => {
|
|
674
|
+
try {
|
|
675
|
+
const client = getClient();
|
|
676
|
+
const result = await client.createStory(opts.task, { text: opts.text });
|
|
677
|
+
success('Comment added!');
|
|
678
|
+
print(result, getFormat(storyCmd));
|
|
679
|
+
} catch (err) {
|
|
680
|
+
error(String(err));
|
|
681
|
+
process.exit(1);
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
storyCmd
|
|
686
|
+
.command('delete <gid>')
|
|
687
|
+
.description('Delete a story/comment')
|
|
688
|
+
.action(async (gid: string) => {
|
|
689
|
+
try {
|
|
690
|
+
const client = getClient();
|
|
691
|
+
await client.deleteStory(gid);
|
|
692
|
+
success(`Story ${gid} deleted`);
|
|
693
|
+
} catch (err) {
|
|
694
|
+
error(String(err));
|
|
695
|
+
process.exit(1);
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
// ============================================
|
|
700
|
+
// Search Commands
|
|
701
|
+
// ============================================
|
|
702
|
+
const searchCmd = program
|
|
703
|
+
.command('search')
|
|
704
|
+
.description('Search tasks');
|
|
705
|
+
|
|
706
|
+
searchCmd
|
|
707
|
+
.command('tasks')
|
|
708
|
+
.description('Search tasks in a workspace')
|
|
709
|
+
.requiredOption('--workspace <gid>', 'Workspace GID')
|
|
710
|
+
.option('--text <text>', 'Search text')
|
|
711
|
+
.option('--projects <gids>', 'Filter by project GIDs (comma-separated)')
|
|
712
|
+
.option('--assignee <gid>', 'Filter by assignee GID')
|
|
713
|
+
.option('--completed', 'Include completed tasks')
|
|
714
|
+
.action(async (opts) => {
|
|
715
|
+
try {
|
|
716
|
+
const client = getClient();
|
|
717
|
+
const result = await client.searchTasks(opts.workspace, {
|
|
718
|
+
text: opts.text,
|
|
719
|
+
'projects.any': opts.projects,
|
|
720
|
+
'assignee.any': opts.assignee,
|
|
721
|
+
completed: opts.completed,
|
|
722
|
+
});
|
|
723
|
+
print(result, getFormat(searchCmd));
|
|
724
|
+
} catch (err) {
|
|
725
|
+
error(String(err));
|
|
726
|
+
process.exit(1);
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
// Parse and execute
|
|
731
|
+
program.parse();
|