clickup-agent-cli 0.2.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.
@@ -0,0 +1,4034 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+
12
+ // src/errors.ts
13
+ function parseApiError(responseBody, status) {
14
+ let message = `API request failed with status ${status}`;
15
+ let ecode;
16
+ if (responseBody && typeof responseBody === "object") {
17
+ const body = responseBody;
18
+ if (typeof body["ECODE"] === "string") {
19
+ ecode = body["ECODE"];
20
+ }
21
+ if (typeof body["err"] === "string") {
22
+ message = body["err"];
23
+ }
24
+ }
25
+ if (ecode && ECODE_MESSAGES[ecode]) {
26
+ message = ECODE_MESSAGES[ecode];
27
+ } else if (!ecode && STATUS_MESSAGES[status]) {
28
+ message = STATUS_MESSAGES[status];
29
+ }
30
+ return new ClickUpError(message, status, ecode, responseBody);
31
+ }
32
+ function mapToExitCode(error) {
33
+ if (!(error instanceof ClickUpError)) {
34
+ return 1;
35
+ }
36
+ switch (error.status) {
37
+ case 401:
38
+ return 3;
39
+ case 403:
40
+ return 5;
41
+ case 404:
42
+ return 4;
43
+ case 429:
44
+ return 6;
45
+ default:
46
+ if (error.status >= 500) return 1;
47
+ return 1;
48
+ }
49
+ }
50
+ var ClickUpError, ECODE_MESSAGES, STATUS_MESSAGES, EXIT_CODES;
51
+ var init_errors = __esm({
52
+ "src/errors.ts"() {
53
+ "use strict";
54
+ ClickUpError = class extends Error {
55
+ status;
56
+ ecode;
57
+ body;
58
+ constructor(message, status, ecode, body) {
59
+ super(message);
60
+ this.name = "ClickUpError";
61
+ this.status = status;
62
+ this.ecode = ecode;
63
+ this.body = body;
64
+ }
65
+ };
66
+ ECODE_MESSAGES = {
67
+ OAUTH_023: "Invalid or expired token. Run: clickup auth login",
68
+ OAUTH_024: "Token does not have permission for this action.",
69
+ ITEM_015: "Task not found. Check the task ID.",
70
+ TEAM_015: "Workspace not found. Check the workspace ID or run: clickup config set workspace_id <id>"
71
+ };
72
+ STATUS_MESSAGES = {
73
+ 401: "Authentication failed. Run: clickup auth login",
74
+ 403: "Permission denied. Check your access level in ClickUp.",
75
+ 404: "Resource not found. Verify the ID.",
76
+ 429: "Rate limited. Retrying automatically..."
77
+ };
78
+ EXIT_CODES = {
79
+ SUCCESS: 0,
80
+ GENERAL_ERROR: 1,
81
+ INVALID_ARGS: 2,
82
+ AUTH_FAILURE: 3,
83
+ NOT_FOUND: 4,
84
+ PERMISSION_DENIED: 5,
85
+ RATE_LIMITED: 6,
86
+ NETWORK_ERROR: 7
87
+ };
88
+ }
89
+ });
90
+
91
+ // src/client.ts
92
+ var client_exports = {};
93
+ __export(client_exports, {
94
+ ClickUpClient: () => ClickUpClient
95
+ });
96
+ import { readFileSync } from "fs";
97
+ import { basename } from "path";
98
+ var DEFAULT_BASE_URL, DEFAULT_MAX_RETRIES, DEFAULT_RETRY_DELAY, DEFAULT_TIMEOUT, RETRYABLE_STATUSES, NON_RETRYABLE_STATUSES, ClickUpClient;
99
+ var init_client = __esm({
100
+ "src/client.ts"() {
101
+ "use strict";
102
+ init_errors();
103
+ DEFAULT_BASE_URL = "https://api.clickup.com/api/v2";
104
+ DEFAULT_MAX_RETRIES = 3;
105
+ DEFAULT_RETRY_DELAY = 1e3;
106
+ DEFAULT_TIMEOUT = 3e4;
107
+ RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
108
+ NON_RETRYABLE_STATUSES = /* @__PURE__ */ new Set([400, 401, 403, 404, 422]);
109
+ ClickUpClient = class {
110
+ token;
111
+ baseUrl;
112
+ maxRetries;
113
+ retryDelay;
114
+ timeout;
115
+ verbose;
116
+ debug;
117
+ dryRun;
118
+ constructor(options) {
119
+ this.token = options.token;
120
+ this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
121
+ this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
122
+ this.retryDelay = options.retryDelay ?? DEFAULT_RETRY_DELAY;
123
+ this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
124
+ this.verbose = options.verbose ?? false;
125
+ this.debug = options.debug ?? false;
126
+ this.dryRun = options.dryRun ?? false;
127
+ }
128
+ async get(path, params) {
129
+ return this.request("GET", path, void 0, params);
130
+ }
131
+ async post(path, body) {
132
+ return this.request("POST", path, body);
133
+ }
134
+ async put(path, body) {
135
+ return this.request("PUT", path, body);
136
+ }
137
+ async patch(path, body) {
138
+ return this.request("PATCH", path, body);
139
+ }
140
+ async delete(path, body) {
141
+ return this.request("DELETE", path, body);
142
+ }
143
+ async upload(path, filePath, filename) {
144
+ const fileBuffer = readFileSync(filePath);
145
+ const resolvedFilename = filename ?? basename(filePath);
146
+ const formData = new FormData();
147
+ formData.append("attachment", new Blob([fileBuffer]), resolvedFilename);
148
+ const url = `${this.baseUrl}${path}`;
149
+ if (this.dryRun) {
150
+ this.logDryRun("POST", url, "[multipart form data]");
151
+ return {};
152
+ }
153
+ const controller = new AbortController();
154
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
155
+ try {
156
+ const response = await fetch(url, {
157
+ method: "POST",
158
+ headers: { Authorization: this.token },
159
+ body: formData,
160
+ signal: controller.signal
161
+ });
162
+ clearTimeout(timeoutId);
163
+ if (!response.ok) {
164
+ const body = await this.safeJson(response);
165
+ throw parseApiError(body, response.status);
166
+ }
167
+ return await response.json();
168
+ } catch (error) {
169
+ clearTimeout(timeoutId);
170
+ if (error instanceof ClickUpError) throw error;
171
+ throw new ClickUpError(
172
+ `Network error: ${error instanceof Error ? error.message : "Unknown error"}`,
173
+ 0,
174
+ void 0,
175
+ void 0
176
+ );
177
+ }
178
+ }
179
+ async request(method, path, body, params) {
180
+ let url = this.resolveUrl(path);
181
+ if (params) {
182
+ const searchParams = new URLSearchParams();
183
+ for (const [key, value] of Object.entries(params)) {
184
+ if (value === void 0) continue;
185
+ if (Array.isArray(value)) {
186
+ for (const v of value) searchParams.append(key, v);
187
+ } else {
188
+ searchParams.set(key, value);
189
+ }
190
+ }
191
+ const qs = searchParams.toString();
192
+ if (qs) url = `${url}?${qs}`;
193
+ }
194
+ if (this.dryRun) {
195
+ this.logDryRun(method, url, body);
196
+ return {};
197
+ }
198
+ let lastError;
199
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
200
+ if (attempt > 0) {
201
+ const delay = this.retryDelay * Math.pow(2, attempt - 1);
202
+ if (this.verbose || this.debug) {
203
+ process.stderr.write(`Retrying in ${delay}ms (attempt ${attempt + 1}/${this.maxRetries + 1})...
204
+ `);
205
+ }
206
+ await this.sleep(delay);
207
+ }
208
+ const start = Date.now();
209
+ const controller = new AbortController();
210
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
211
+ try {
212
+ const headers = {
213
+ Authorization: this.token
214
+ };
215
+ if (body !== void 0) {
216
+ headers["Content-Type"] = "application/json";
217
+ }
218
+ if (this.debug) {
219
+ const redactedHeaders = { ...headers, Authorization: "<redacted>" };
220
+ process.stderr.write(`[${method}] ${url}
221
+ Headers: ${JSON.stringify(redactedHeaders)}
222
+ `);
223
+ if (body !== void 0) {
224
+ process.stderr.write(`Body: ${JSON.stringify(body, null, 2)}
225
+ `);
226
+ }
227
+ }
228
+ const fetchInit = {
229
+ method,
230
+ headers,
231
+ signal: controller.signal
232
+ };
233
+ if (body !== void 0) {
234
+ fetchInit.body = JSON.stringify(body);
235
+ }
236
+ const response = await fetch(url, fetchInit);
237
+ clearTimeout(timeoutId);
238
+ const elapsed = Date.now() - start;
239
+ if (this.verbose || this.debug) {
240
+ process.stderr.write(`[${method}] ${path} ${response.status} ${response.statusText} (${elapsed}ms)
241
+ `);
242
+ }
243
+ const remaining = response.headers.get("X-RateLimit-Remaining");
244
+ const reset = response.headers.get("X-RateLimit-Reset");
245
+ if (remaining === "0" && reset) {
246
+ const resetTime = parseInt(reset, 10) * 1e3;
247
+ const waitMs = Math.max(0, resetTime - Date.now());
248
+ if (waitMs > 0) {
249
+ process.stderr.write(`Rate limited. Waiting ${Math.ceil(waitMs / 1e3)}s...
250
+ `);
251
+ await this.sleep(waitMs);
252
+ }
253
+ }
254
+ if (!response.ok) {
255
+ const responseBody = await this.safeJson(response);
256
+ const error = parseApiError(responseBody, response.status);
257
+ if (this.debug) {
258
+ process.stderr.write(`Error response: ${JSON.stringify(responseBody, null, 2)}
259
+ `);
260
+ }
261
+ if (NON_RETRYABLE_STATUSES.has(response.status)) {
262
+ throw error;
263
+ }
264
+ if (RETRYABLE_STATUSES.has(response.status) && attempt < this.maxRetries) {
265
+ lastError = error;
266
+ continue;
267
+ }
268
+ throw error;
269
+ }
270
+ const responseData = await this.safeJson(response);
271
+ if (this.debug) {
272
+ process.stderr.write(`Response: ${JSON.stringify(responseData, null, 2)}
273
+ `);
274
+ }
275
+ return responseData;
276
+ } catch (error) {
277
+ clearTimeout(timeoutId);
278
+ if (error instanceof ClickUpError) {
279
+ if (NON_RETRYABLE_STATUSES.has(error.status)) throw error;
280
+ if (attempt >= this.maxRetries) throw error;
281
+ lastError = error;
282
+ continue;
283
+ }
284
+ if (error instanceof DOMException && error.name === "AbortError") {
285
+ throw new ClickUpError("Request timed out", 0, void 0, void 0);
286
+ }
287
+ throw new ClickUpError(
288
+ `Network error: ${error instanceof Error ? error.message : "Unknown error"}`,
289
+ 0,
290
+ void 0,
291
+ void 0
292
+ );
293
+ }
294
+ }
295
+ throw lastError ?? new ClickUpError("Request failed after retries", 0, void 0, void 0);
296
+ }
297
+ resolveUrl(path) {
298
+ if (path.startsWith("/v3/")) {
299
+ const origin = new URL(this.baseUrl).origin;
300
+ return `${origin}/api${path}`;
301
+ }
302
+ return `${this.baseUrl}${path}`;
303
+ }
304
+ logDryRun(method, url, body) {
305
+ process.stderr.write(`[DRY RUN] ${method} ${url}
306
+ `);
307
+ if (body !== void 0 && body !== "[multipart form data]") {
308
+ process.stderr.write(`Body: ${JSON.stringify(body, null, 2)}
309
+ `);
310
+ }
311
+ if (body === "[multipart form data]") {
312
+ process.stderr.write(`Body: ${body}
313
+ `);
314
+ }
315
+ }
316
+ async safeJson(response) {
317
+ try {
318
+ return await response.json();
319
+ } catch {
320
+ return null;
321
+ }
322
+ }
323
+ sleep(ms) {
324
+ return new Promise((resolve) => setTimeout(resolve, ms));
325
+ }
326
+ };
327
+ }
328
+ });
329
+
330
+ // src/config.ts
331
+ import Conf from "conf";
332
+ function isValidConfigKey(key) {
333
+ return CONFIG_KEYS.includes(key);
334
+ }
335
+ function resolveString(key, flagValue) {
336
+ if (flagValue !== void 0) return flagValue;
337
+ const envVal = process.env[ENV_MAP[key]];
338
+ if (envVal !== void 0) return envVal;
339
+ const stored = config.get(key);
340
+ if (typeof stored === "string") return stored;
341
+ return void 0;
342
+ }
343
+ function resolveToken(flagValue) {
344
+ return resolveString("token", flagValue);
345
+ }
346
+ function resolveWorkspaceId(flagValue) {
347
+ return resolveString("workspace_id", flagValue);
348
+ }
349
+ function resolveOutputFormat(flagValue) {
350
+ const val = resolveString("output_format", flagValue);
351
+ if (val) return val;
352
+ return process.stdout.isTTY ? "table" : "json";
353
+ }
354
+ var CONFIG_KEYS, VALID_CONFIG_KEYS, config, ENV_MAP;
355
+ var init_config = __esm({
356
+ "src/config.ts"() {
357
+ "use strict";
358
+ CONFIG_KEYS = ["token", "workspace_id", "output_format", "color", "page_size", "timezone"];
359
+ VALID_CONFIG_KEYS = CONFIG_KEYS;
360
+ config = new Conf({
361
+ projectName: "clickup-cli",
362
+ schema: {
363
+ token: { type: "string" },
364
+ workspace_id: { type: "string" },
365
+ output_format: { type: "string", enum: ["table", "json", "csv", "tsv", "quiet", "id"] },
366
+ color: { type: "boolean", default: true },
367
+ page_size: { type: "number", default: 100 },
368
+ timezone: { type: "string" }
369
+ }
370
+ });
371
+ ENV_MAP = {
372
+ token: "CLICKUP_API_TOKEN",
373
+ workspace_id: "CLICKUP_WORKSPACE_ID",
374
+ output_format: "CLICKUP_OUTPUT_FORMAT",
375
+ color: "CLICKUP_COLOR",
376
+ page_size: "CLICKUP_PAGE_SIZE",
377
+ timezone: "CLICKUP_TIMEZONE"
378
+ };
379
+ }
380
+ });
381
+
382
+ // src/auth.ts
383
+ var auth_exports = {};
384
+ __export(auth_exports, {
385
+ generateCodeChallenge: () => generateCodeChallenge,
386
+ generateCodeVerifier: () => generateCodeVerifier,
387
+ oauthLogin: () => oauthLogin
388
+ });
389
+ import { createServer } from "http";
390
+ import { randomBytes, createHash } from "crypto";
391
+ function generateCodeVerifier() {
392
+ return randomBytes(48).toString("base64url");
393
+ }
394
+ function generateCodeChallenge(verifier) {
395
+ return createHash("sha256").update(verifier).digest("base64url");
396
+ }
397
+ function buildAuthorizeUrl(clientId, codeChallenge) {
398
+ const params = new URLSearchParams({
399
+ client_id: clientId,
400
+ redirect_uri: REDIRECT_URI,
401
+ code_challenge: codeChallenge,
402
+ code_challenge_method: "S256"
403
+ });
404
+ return `${CLICKUP_AUTHORIZE_URL}?${params.toString()}`;
405
+ }
406
+ async function exchangeCodeForToken(code, clientId, clientSecret, codeVerifier) {
407
+ const response = await fetch(CLICKUP_TOKEN_URL, {
408
+ method: "POST",
409
+ headers: { "Content-Type": "application/json" },
410
+ body: JSON.stringify({
411
+ client_id: clientId,
412
+ client_secret: clientSecret,
413
+ code,
414
+ code_verifier: codeVerifier
415
+ })
416
+ });
417
+ if (!response.ok) {
418
+ const body = await response.text();
419
+ throw new Error(`Token exchange failed (${response.status}): ${body}`);
420
+ }
421
+ const data = await response.json();
422
+ const token = data["access_token"];
423
+ if (typeof token !== "string" || !token) {
424
+ throw new Error("No access_token in token exchange response");
425
+ }
426
+ return token;
427
+ }
428
+ function waitForCallback(server) {
429
+ return new Promise((resolve, reject) => {
430
+ const timeout = setTimeout(() => {
431
+ server.close();
432
+ reject(new Error("OAuth callback timed out after 120 seconds"));
433
+ }, 12e4);
434
+ server.on("request", (req, res) => {
435
+ const url = new URL(req.url ?? "/", `http://localhost:${CALLBACK_PORT}`);
436
+ if (url.pathname !== CALLBACK_PATH) {
437
+ res.writeHead(404);
438
+ res.end("Not found");
439
+ return;
440
+ }
441
+ const code = url.searchParams.get("code");
442
+ const error = url.searchParams.get("error");
443
+ if (error) {
444
+ res.writeHead(200, { "Content-Type": "text/html" });
445
+ res.end("<html><body><h1>Authentication failed</h1><p>You can close this tab.</p></body></html>");
446
+ clearTimeout(timeout);
447
+ server.close();
448
+ reject(new Error(`OAuth error: ${error}`));
449
+ return;
450
+ }
451
+ if (!code) {
452
+ res.writeHead(400, { "Content-Type": "text/html" });
453
+ res.end("<html><body><h1>Missing code</h1><p>No authorization code received.</p></body></html>");
454
+ return;
455
+ }
456
+ res.writeHead(200, { "Content-Type": "text/html" });
457
+ res.end("<html><body><h1>Authenticated</h1><p>You can close this tab and return to the terminal.</p></body></html>");
458
+ clearTimeout(timeout);
459
+ server.close();
460
+ resolve(code);
461
+ });
462
+ });
463
+ }
464
+ async function openBrowser(url) {
465
+ const { platform } = process;
466
+ let command;
467
+ if (platform === "darwin") {
468
+ command = "open";
469
+ } else if (platform === "win32") {
470
+ command = "start";
471
+ } else {
472
+ command = "xdg-open";
473
+ }
474
+ const { exec } = await import("child_process");
475
+ return new Promise((resolve) => {
476
+ exec(`${command} "${url}"`, () => {
477
+ resolve();
478
+ });
479
+ });
480
+ }
481
+ async function oauthLogin() {
482
+ const clientId = process.env["CLICKUP_CLIENT_ID"];
483
+ const clientSecret = process.env["CLICKUP_CLIENT_SECRET"];
484
+ if (!clientId) {
485
+ process.stderr.write("Error: CLICKUP_CLIENT_ID environment variable is required for OAuth.\n");
486
+ process.exit(2);
487
+ throw new Error("unreachable");
488
+ }
489
+ if (!clientSecret) {
490
+ process.stderr.write("Error: CLICKUP_CLIENT_SECRET environment variable is required for OAuth.\n");
491
+ process.exit(2);
492
+ throw new Error("unreachable");
493
+ }
494
+ const codeVerifier = generateCodeVerifier();
495
+ const codeChallenge = generateCodeChallenge(codeVerifier);
496
+ const server = createServer();
497
+ await new Promise((resolve, reject) => {
498
+ server.listen(CALLBACK_PORT, () => resolve());
499
+ server.on("error", reject);
500
+ });
501
+ const authorizeUrl = buildAuthorizeUrl(clientId, codeChallenge);
502
+ process.stderr.write(`Opening browser for authentication...
503
+ `);
504
+ process.stderr.write(`If the browser does not open, visit:
505
+ ${authorizeUrl}
506
+ `);
507
+ await openBrowser(authorizeUrl);
508
+ const code = await waitForCallback(server);
509
+ process.stderr.write("Exchanging code for token...\n");
510
+ const token = await exchangeCodeForToken(code, clientId, clientSecret, codeVerifier);
511
+ config.set("token", token);
512
+ return token;
513
+ }
514
+ var CLICKUP_AUTHORIZE_URL, CLICKUP_TOKEN_URL, CALLBACK_PORT, CALLBACK_PATH, REDIRECT_URI;
515
+ var init_auth = __esm({
516
+ "src/auth.ts"() {
517
+ "use strict";
518
+ init_config();
519
+ CLICKUP_AUTHORIZE_URL = "https://app.clickup.com/api";
520
+ CLICKUP_TOKEN_URL = "https://api.clickup.com/api/v2/oauth/token";
521
+ CALLBACK_PORT = 9876;
522
+ CALLBACK_PATH = "/callback";
523
+ REDIRECT_URI = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;
524
+ }
525
+ });
526
+
527
+ // src/cli.ts
528
+ init_client();
529
+ init_errors();
530
+ init_config();
531
+ import { Command } from "commander";
532
+
533
+ // src/commands/auth-cmd.ts
534
+ init_config();
535
+ function registerAuthCommands(program, getClient) {
536
+ const auth = program.command("auth").description("Manage authentication");
537
+ auth.command("login").description("Authenticate with ClickUp").option("--token <token>", "Personal API token").option("--oauth", "Use OAuth2 PKCE browser flow (requires CLICKUP_CLIENT_ID and CLICKUP_CLIENT_SECRET)").action(async (opts) => {
538
+ if (opts.oauth) {
539
+ const { oauthLogin: oauthLogin2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
540
+ const token2 = await oauthLogin2();
541
+ const { ClickUpClient: ClientClass2 } = await Promise.resolve().then(() => (init_client(), client_exports));
542
+ const client2 = new ClientClass2({ token: token2 });
543
+ try {
544
+ const userData = await client2.get("/user");
545
+ process.stdout.write(`Authenticated as ${userData.user.username} (${userData.user.email})
546
+ `);
547
+ } catch {
548
+ process.stderr.write("Warning: Token obtained but validation failed.\n");
549
+ }
550
+ return;
551
+ }
552
+ let token = opts.token;
553
+ if (!token) {
554
+ if (!process.stdin.isTTY) {
555
+ process.stderr.write("Error: No token provided. Use --token <token> or --oauth in non-interactive mode.\n");
556
+ process.exit(2);
557
+ return;
558
+ }
559
+ const { password } = await import("@inquirer/prompts");
560
+ token = await password({
561
+ message: "Enter your ClickUp API token:",
562
+ mask: "*"
563
+ });
564
+ }
565
+ if (!token) {
566
+ process.stderr.write("Error: No token provided.\n");
567
+ process.exit(2);
568
+ return;
569
+ }
570
+ const { ClickUpClient: ClientClass } = await Promise.resolve().then(() => (init_client(), client_exports));
571
+ const client = new ClientClass({ token });
572
+ try {
573
+ const userData = await client.get("/user");
574
+ config.set("token", token);
575
+ process.stdout.write(`Authenticated as ${userData.user.username} (${userData.user.email})
576
+ `);
577
+ } catch {
578
+ process.stderr.write("Error: Invalid token. Authentication failed.\n");
579
+ process.exit(3);
580
+ }
581
+ });
582
+ auth.command("logout").description("Remove stored credentials").action(() => {
583
+ config.delete("token");
584
+ process.stdout.write("Logged out. Token removed.\n");
585
+ });
586
+ auth.command("status").description("Show current authentication state").action(async () => {
587
+ const token = resolveToken();
588
+ if (!token) {
589
+ process.stdout.write("Not authenticated. Run: clickup auth login\n");
590
+ return;
591
+ }
592
+ const prefix = token.slice(0, 8);
593
+ process.stdout.write(`Token: ${prefix}...
594
+ `);
595
+ const workspaceId = config.get("workspace_id");
596
+ if (workspaceId) {
597
+ process.stdout.write(`Workspace: ${workspaceId}
598
+ `);
599
+ }
600
+ try {
601
+ const client = getClient();
602
+ const userData = await client.get("/user");
603
+ process.stdout.write(`User: ${userData.user.username} (${userData.user.email})
604
+ `);
605
+ process.stdout.write("Status: Valid\n");
606
+ } catch {
607
+ process.stdout.write("Status: Invalid or expired\n");
608
+ }
609
+ });
610
+ auth.command("token").description("Print current token to stdout").action(() => {
611
+ const token = resolveToken();
612
+ if (!token) {
613
+ process.stderr.write("Error: No token found. Run: clickup auth login\n");
614
+ process.exit(3);
615
+ return;
616
+ }
617
+ process.stdout.write(token + "\n");
618
+ });
619
+ }
620
+
621
+ // src/commands/config-cmd.ts
622
+ init_config();
623
+ function registerConfigCommands(program) {
624
+ const configCmd = program.command("config").description("Manage CLI configuration");
625
+ configCmd.command("set").description("Set a configuration value").argument("<key>", "Config key").argument("<value>", "Config value").action((key, value) => {
626
+ if (!isValidConfigKey(key)) {
627
+ process.stderr.write(`Error: Unknown config key "${key}". Valid keys: ${VALID_CONFIG_KEYS.join(", ")}
628
+ `);
629
+ process.exit(2);
630
+ return;
631
+ }
632
+ if (key === "color") {
633
+ config.set(key, value === "true" || value === "1");
634
+ } else if (key === "page_size") {
635
+ const num = parseInt(value, 10);
636
+ if (isNaN(num) || num <= 0) {
637
+ process.stderr.write("Error: page_size must be a positive number.\n");
638
+ process.exit(2);
639
+ return;
640
+ }
641
+ config.set(key, num);
642
+ } else {
643
+ config.set(key, value);
644
+ }
645
+ process.stdout.write(`Set ${key} = ${value}
646
+ `);
647
+ });
648
+ configCmd.command("get").description("Get a configuration value").argument("<key>", "Config key").action((key) => {
649
+ if (!isValidConfigKey(key)) {
650
+ process.stderr.write(`Error: Unknown config key "${key}". Valid keys: ${VALID_CONFIG_KEYS.join(", ")}
651
+ `);
652
+ process.exit(2);
653
+ return;
654
+ }
655
+ const value = config.get(key);
656
+ if (value === void 0) {
657
+ process.stdout.write("(not set)\n");
658
+ } else {
659
+ process.stdout.write(`${value}
660
+ `);
661
+ }
662
+ });
663
+ configCmd.command("list").description("List all configuration values").action(() => {
664
+ const store = config.store;
665
+ const keys = VALID_CONFIG_KEYS;
666
+ for (const key of keys) {
667
+ const value = store[key];
668
+ if (value !== void 0) {
669
+ process.stdout.write(`${key} = ${value}
670
+ `);
671
+ }
672
+ }
673
+ });
674
+ configCmd.command("reset").description("Reset all configuration to defaults").option("--confirm", "Skip confirmation prompt").action(async (opts) => {
675
+ if (!opts.confirm) {
676
+ if (!process.stdin.isTTY) {
677
+ process.stderr.write("Error: Add --confirm to proceed with this destructive action.\n");
678
+ process.exit(2);
679
+ return;
680
+ }
681
+ const { confirm } = await import("@inquirer/prompts");
682
+ const answer = await confirm({
683
+ message: "Reset all configuration to defaults?",
684
+ default: false
685
+ });
686
+ if (!answer) {
687
+ process.stdout.write("Cancelled.\n");
688
+ return;
689
+ }
690
+ }
691
+ config.clear();
692
+ process.stdout.write("Configuration reset to defaults.\n");
693
+ });
694
+ configCmd.command("path").description("Print the config file path").action(() => {
695
+ process.stdout.write(config.path + "\n");
696
+ });
697
+ }
698
+
699
+ // src/commands/workspace.ts
700
+ init_config();
701
+
702
+ // src/output.ts
703
+ import Table from "cli-table3";
704
+ function formatOutput(data, columns, opts) {
705
+ const rows = Array.isArray(data) ? data : [data];
706
+ let processed = applyFilter(rows, opts.filter);
707
+ processed = applySort(processed, opts.sort);
708
+ if (opts.limit !== void 0 && opts.limit > 0) {
709
+ processed = processed.slice(0, opts.limit);
710
+ }
711
+ const activeCols = filterColumns(columns, opts.fields);
712
+ const format = opts.format ?? (process.stdout.isTTY ? "table" : "json");
713
+ switch (format) {
714
+ case "table":
715
+ printTable(processed, activeCols, opts);
716
+ break;
717
+ case "json":
718
+ printJson(processed);
719
+ break;
720
+ case "csv":
721
+ printDelimited(processed, activeCols, ",", opts);
722
+ break;
723
+ case "tsv":
724
+ printDelimited(processed, activeCols, " ", opts);
725
+ break;
726
+ case "quiet":
727
+ printQuiet(processed);
728
+ break;
729
+ case "id":
730
+ printId(processed);
731
+ break;
732
+ case "md":
733
+ printMarkdown(processed, activeCols, opts);
734
+ break;
735
+ default:
736
+ printJson(processed);
737
+ }
738
+ }
739
+ function filterColumns(columns, fields) {
740
+ if (!fields) return columns;
741
+ const wanted = new Set(fields.split(",").map((f) => f.trim()));
742
+ return columns.filter((c) => wanted.has(c.key));
743
+ }
744
+ function applyFilter(rows, filter) {
745
+ if (!filter) return rows;
746
+ const eqIndex = filter.indexOf("=");
747
+ if (eqIndex === -1) return rows;
748
+ const key = filter.slice(0, eqIndex);
749
+ const value = filter.slice(eqIndex + 1);
750
+ return rows.filter((row) => {
751
+ if (row && typeof row === "object") {
752
+ const record = row;
753
+ return String(record[key] ?? "") === value;
754
+ }
755
+ return false;
756
+ });
757
+ }
758
+ function applySort(rows, sort) {
759
+ if (!sort) return rows;
760
+ const parts = sort.split(":");
761
+ const field = parts[0];
762
+ const desc = parts[1] === "desc";
763
+ return [...rows].sort((a, b) => {
764
+ const aObj = a;
765
+ const bObj = b;
766
+ const aVal = aObj[field];
767
+ const bVal = bObj[field];
768
+ if (aVal === bVal) return 0;
769
+ if (aVal === void 0 || aVal === null) return 1;
770
+ if (bVal === void 0 || bVal === null) return -1;
771
+ const result = String(aVal).localeCompare(String(bVal), void 0, { numeric: true });
772
+ return desc ? -result : result;
773
+ });
774
+ }
775
+ function getValue(row, key) {
776
+ if (row && typeof row === "object") {
777
+ const record = row;
778
+ const val = record[key];
779
+ if (val === null || val === void 0) return "";
780
+ if (typeof val === "object") return JSON.stringify(val);
781
+ return String(val);
782
+ }
783
+ return "";
784
+ }
785
+ function truncate(str, width) {
786
+ if (str.length <= width) return str;
787
+ return str.slice(0, width - 3) + "...";
788
+ }
789
+ function printTable(rows, columns, opts) {
790
+ if (columns.length === 0) {
791
+ printJson(rows);
792
+ return;
793
+ }
794
+ const tableOpts = {
795
+ colWidths: columns.map((c) => c.width),
796
+ style: { head: opts.noColor ? [] : ["cyan"] }
797
+ };
798
+ if (!opts.noHeader) {
799
+ tableOpts["head"] = columns.map((c) => c.header);
800
+ }
801
+ const table = new Table(tableOpts);
802
+ for (const row of rows) {
803
+ table.push(columns.map((col) => truncate(getValue(row, col.key), col.width - 2)));
804
+ }
805
+ process.stdout.write(table.toString() + "\n");
806
+ }
807
+ function printJson(rows) {
808
+ process.stdout.write(JSON.stringify(rows.length === 1 ? rows[0] : rows, null, 2) + "\n");
809
+ }
810
+ function printDelimited(rows, columns, delimiter, opts) {
811
+ if (!opts.noHeader) {
812
+ process.stdout.write(columns.map((c) => c.header).join(delimiter) + "\n");
813
+ }
814
+ for (const row of rows) {
815
+ const values = columns.map((col) => {
816
+ const val = getValue(row, col.key);
817
+ if (delimiter === "," && (val.includes(",") || val.includes('"') || val.includes("\n"))) {
818
+ return `"${val.replace(/"/g, '""')}"`;
819
+ }
820
+ return val;
821
+ });
822
+ process.stdout.write(values.join(delimiter) + "\n");
823
+ }
824
+ }
825
+ function printQuiet(rows) {
826
+ for (const row of rows) {
827
+ const id = getValue(row, "id");
828
+ if (id) process.stdout.write(id + "\n");
829
+ }
830
+ }
831
+ function printId(rows) {
832
+ if (rows.length > 0) {
833
+ const id = getValue(rows[0], "id");
834
+ if (id) process.stdout.write(id + "\n");
835
+ }
836
+ }
837
+ function printMarkdown(rows, columns, opts) {
838
+ if (columns.length === 0) {
839
+ printJson(rows);
840
+ return;
841
+ }
842
+ if (!opts.noHeader) {
843
+ process.stdout.write("| " + columns.map((c) => c.header).join(" | ") + " |\n");
844
+ process.stdout.write("| " + columns.map((c) => "-".repeat(Math.max(c.header.length, 3))).join(" | ") + " |\n");
845
+ }
846
+ for (const row of rows) {
847
+ process.stdout.write("| " + columns.map((col) => getValue(row, col.key).replace(/\|/g, "\\|")).join(" | ") + " |\n");
848
+ }
849
+ }
850
+
851
+ // src/schema.ts
852
+ var registry = /* @__PURE__ */ new Map();
853
+ function registerSchema(resource, action, description, fields) {
854
+ if (!registry.has(resource)) {
855
+ registry.set(resource, /* @__PURE__ */ new Map());
856
+ }
857
+ registry.get(resource).set(action, { action, description, fields });
858
+ }
859
+ function getResources() {
860
+ return Array.from(registry.keys()).sort();
861
+ }
862
+ function getActions(resource) {
863
+ const actions = registry.get(resource);
864
+ if (!actions) return void 0;
865
+ return Array.from(actions.values());
866
+ }
867
+ function getFields(resource, action) {
868
+ return registry.get(resource)?.get(action);
869
+ }
870
+
871
+ // src/commands/workspace.ts
872
+ registerSchema("workspace", "list", "List all workspaces accessible to the authenticated user", []);
873
+ registerSchema("workspace", "get", "Get workspace details", [
874
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" }
875
+ ]);
876
+ registerSchema("workspace", "seats", "Show seat usage for a workspace", [
877
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" }
878
+ ]);
879
+ registerSchema("workspace", "plan", "Show current billing plan for a workspace", [
880
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" }
881
+ ]);
882
+ registerSchema("workspace", "members", "List all members in a workspace", [
883
+ { flag: "--workspace-id", type: "string", required: false, description: "Workspace ID" }
884
+ ]);
885
+ var WORKSPACE_MEMBER_COLUMNS = [
886
+ { key: "id", header: "ID", width: 12 },
887
+ { key: "username", header: "Username", width: 22 },
888
+ { key: "email", header: "Email", width: 32 },
889
+ { key: "role", header: "Role", width: 10 }
890
+ ];
891
+ var WORKSPACE_COLUMNS = [
892
+ { key: "id", header: "ID", width: 14 },
893
+ { key: "name", header: "Name", width: 30 },
894
+ { key: "color", header: "Color", width: 10 },
895
+ { key: "member_count", header: "Members", width: 10 }
896
+ ];
897
+ var SEAT_COLUMNS = [
898
+ { key: "type", header: "Type", width: 12 },
899
+ { key: "filled", header: "Filled", width: 10 },
900
+ { key: "total", header: "Total", width: 10 },
901
+ { key: "empty", header: "Empty", width: 10 }
902
+ ];
903
+ function requireWorkspaceId(program) {
904
+ const globalOpts = program.opts();
905
+ const workspaceId = resolveWorkspaceId(globalOpts["workspaceId"]);
906
+ if (!workspaceId) {
907
+ process.stderr.write("Error: No workspace ID. Use --workspace-id or run: clickup config set workspace_id <id>\n");
908
+ process.exit(2);
909
+ return void 0;
910
+ }
911
+ return workspaceId;
912
+ }
913
+ function registerWorkspaceCommands(program, getClient) {
914
+ const workspace = program.command("workspace").description("Manage workspaces");
915
+ workspace.command("list").description("List all workspaces").action(async () => {
916
+ const client = getClient();
917
+ const data = await client.get("/team");
918
+ const rows = data.teams.map((t) => ({
919
+ ...t,
920
+ member_count: t.members?.length ?? 0
921
+ }));
922
+ formatOutput(rows, WORKSPACE_COLUMNS, getOutputOptions(program));
923
+ });
924
+ workspace.command("get").description("Get workspace details").action(async () => {
925
+ const workspaceId = requireWorkspaceId(program);
926
+ if (!workspaceId) return;
927
+ const client = getClient();
928
+ const data = await client.get(`/team/${workspaceId}`);
929
+ const row = {
930
+ ...data.team,
931
+ member_count: Array.isArray(data.team["members"]) ? data.team["members"].length : 0
932
+ };
933
+ formatOutput(row, WORKSPACE_COLUMNS, getOutputOptions(program));
934
+ });
935
+ workspace.command("seats").description("Show seat usage").action(async () => {
936
+ const workspaceId = requireWorkspaceId(program);
937
+ if (!workspaceId) return;
938
+ const client = getClient();
939
+ const data = await client.get(`/team/${workspaceId}/seats`);
940
+ const rows = [];
941
+ if (data.members) {
942
+ rows.push({
943
+ type: "Members",
944
+ filled: data.members.filled_members_count ?? 0,
945
+ total: data.members.total_member_seats ?? 0,
946
+ empty: data.members.empty_member_seats ?? 0
947
+ });
948
+ }
949
+ if (data.guests) {
950
+ rows.push({
951
+ type: "Guests",
952
+ filled: data.guests.filled_guest_count ?? 0,
953
+ total: data.guests.total_guest_seats ?? 0,
954
+ empty: data.guests.empty_guest_seats ?? 0
955
+ });
956
+ }
957
+ formatOutput(rows, SEAT_COLUMNS, getOutputOptions(program));
958
+ });
959
+ workspace.command("plan").description("Show current billing plan").action(async () => {
960
+ const workspaceId = requireWorkspaceId(program);
961
+ if (!workspaceId) return;
962
+ const client = getClient();
963
+ const data = await client.get(`/team/${workspaceId}/plan`);
964
+ formatOutput(data, [
965
+ { key: "plan_id", header: "Plan ID", width: 12 },
966
+ { key: "plan_name", header: "Plan", width: 30 }
967
+ ], getOutputOptions(program));
968
+ });
969
+ workspace.command("members").description("List all members in workspace").action(async () => {
970
+ const workspaceId = requireWorkspaceId(program);
971
+ if (!workspaceId) return;
972
+ const client = getClient();
973
+ const data = await client.get(
974
+ `/team/${workspaceId}/member`
975
+ );
976
+ const rows = (data.members ?? []).map((m) => ({
977
+ id: m.user["id"],
978
+ username: m.user["username"],
979
+ email: m.user["email"],
980
+ role: m.role
981
+ }));
982
+ formatOutput(rows, WORKSPACE_MEMBER_COLUMNS, getOutputOptions(program));
983
+ });
984
+ }
985
+
986
+ // src/commands/space.ts
987
+ init_config();
988
+ registerSchema("space", "list", "List spaces in a workspace", [
989
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
990
+ { flag: "--archived", type: "boolean", required: false, description: "Include archived spaces" }
991
+ ]);
992
+ registerSchema("space", "get", "Get space details", [
993
+ { flag: "<space-id>", type: "string", required: true, description: "Space ID" }
994
+ ]);
995
+ registerSchema("space", "create", "Create a new space", [
996
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
997
+ { flag: "--name", type: "string", required: true, description: "Space name" },
998
+ { flag: "--multiple-assignees", type: "boolean", required: false, description: "Enable multiple assignees" },
999
+ { flag: "--features", type: "string", required: false, description: "Feature flags as JSON" }
1000
+ ]);
1001
+ registerSchema("space", "update", "Update a space", [
1002
+ { flag: "<space-id>", type: "string", required: true, description: "Space ID" },
1003
+ { flag: "--name", type: "string", required: false, description: "New space name" },
1004
+ { flag: "--color", type: "string", required: false, description: "Space color" },
1005
+ { flag: "--private", type: "boolean", required: false, description: "Make space private or public" }
1006
+ ]);
1007
+ registerSchema("space", "delete", "Delete a space", [
1008
+ { flag: "<space-id>", type: "string", required: true, description: "Space ID" },
1009
+ { flag: "--confirm", type: "boolean", required: false, description: "Skip confirmation prompt" }
1010
+ ]);
1011
+ var SPACE_COLUMNS = [
1012
+ { key: "id", header: "ID", width: 14 },
1013
+ { key: "name", header: "Name", width: 30 },
1014
+ { key: "private", header: "Private", width: 10 },
1015
+ { key: "color", header: "Color", width: 10 },
1016
+ { key: "multiple_assignees", header: "Multi-Assign", width: 14 }
1017
+ ];
1018
+ function requireWorkspaceId2(program) {
1019
+ const globalOpts = program.opts();
1020
+ const workspaceId = resolveWorkspaceId(globalOpts["workspaceId"]);
1021
+ if (!workspaceId) {
1022
+ process.stderr.write("Error: No workspace ID. Use --workspace-id or run: clickup config set workspace_id <id>\n");
1023
+ process.exit(2);
1024
+ return void 0;
1025
+ }
1026
+ return workspaceId;
1027
+ }
1028
+ function registerSpaceCommands(program, getClient) {
1029
+ const space = program.command("space").description("Manage spaces");
1030
+ space.command("list").description("List spaces in a workspace").option("--archived", "Include archived spaces").action(async (opts) => {
1031
+ const workspaceId = requireWorkspaceId2(program);
1032
+ if (!workspaceId) return;
1033
+ const client = getClient();
1034
+ const data = await client.get(`/team/${workspaceId}/space`, {
1035
+ archived: opts.archived ? "true" : "false"
1036
+ });
1037
+ formatOutput(data.spaces, SPACE_COLUMNS, getOutputOptions(program));
1038
+ });
1039
+ space.command("get").description("Get space details").argument("<space-id>", "Space ID").action(async (spaceId) => {
1040
+ const client = getClient();
1041
+ const data = await client.get(`/space/${spaceId}`);
1042
+ formatOutput(data, SPACE_COLUMNS, getOutputOptions(program));
1043
+ });
1044
+ space.command("create").description("Create a new space").requiredOption("--name <name>", "Space name").option("--multiple-assignees", "Enable multiple assignees").option("--features <json>", "Feature flags as JSON").action(async (opts) => {
1045
+ const workspaceId = requireWorkspaceId2(program);
1046
+ if (!workspaceId) return;
1047
+ const client = getClient();
1048
+ const body = { name: opts.name };
1049
+ if (opts.multipleAssignees !== void 0) {
1050
+ body["multiple_assignees"] = opts.multipleAssignees;
1051
+ }
1052
+ if (opts.features) {
1053
+ body["features"] = JSON.parse(opts.features);
1054
+ }
1055
+ const data = await client.post(`/team/${workspaceId}/space`, body);
1056
+ formatOutput(data, SPACE_COLUMNS, getOutputOptions(program));
1057
+ });
1058
+ space.command("update").description("Update a space").argument("<space-id>", "Space ID").option("--name <name>", "New space name").option("--color <hex>", "Space color").option("--private <bool>", "Make space private or public").action(async (spaceId, opts) => {
1059
+ const client = getClient();
1060
+ const body = {};
1061
+ if (opts.name !== void 0) body["name"] = opts.name;
1062
+ if (opts.color !== void 0) body["color"] = opts.color;
1063
+ if (opts.private !== void 0) body["admin_can_manage"] = opts.private === "true";
1064
+ const data = await client.put(`/space/${spaceId}`, body);
1065
+ formatOutput(data, SPACE_COLUMNS, getOutputOptions(program));
1066
+ });
1067
+ space.command("delete").description("Delete a space").argument("<space-id>", "Space ID").option("--confirm", "Skip confirmation prompt").action(async (spaceId, opts) => {
1068
+ const client = getClient();
1069
+ if (!opts.confirm) {
1070
+ if (!process.stdin.isTTY) {
1071
+ process.stderr.write("Error: Use --confirm to delete in non-interactive mode.\n");
1072
+ process.exit(2);
1073
+ return;
1074
+ }
1075
+ const { confirm } = await import("@inquirer/prompts");
1076
+ const yes = await confirm({ message: `Delete space ${spaceId}?` });
1077
+ if (!yes) {
1078
+ process.stdout.write("Cancelled.\n");
1079
+ return;
1080
+ }
1081
+ }
1082
+ await client.delete(`/space/${spaceId}`);
1083
+ process.stdout.write(`Deleted space ${spaceId}
1084
+ `);
1085
+ });
1086
+ }
1087
+
1088
+ // src/commands/folder.ts
1089
+ registerSchema("folder", "list", "List folders in a space", [
1090
+ { flag: "--space-id", type: "string", required: true, description: "Space ID" },
1091
+ { flag: "--archived", type: "boolean", required: false, description: "Include archived folders" }
1092
+ ]);
1093
+ registerSchema("folder", "get", "Get folder details", [
1094
+ { flag: "<folder-id>", type: "string", required: true, description: "Folder ID" }
1095
+ ]);
1096
+ registerSchema("folder", "create", "Create a new folder", [
1097
+ { flag: "--space-id", type: "string", required: true, description: "Space ID" },
1098
+ { flag: "--name", type: "string", required: true, description: "Folder name" }
1099
+ ]);
1100
+ registerSchema("folder", "update", "Update a folder", [
1101
+ { flag: "<folder-id>", type: "string", required: true, description: "Folder ID" },
1102
+ { flag: "--name", type: "string", required: false, description: "New folder name" }
1103
+ ]);
1104
+ registerSchema("folder", "delete", "Delete a folder", [
1105
+ { flag: "<folder-id>", type: "string", required: true, description: "Folder ID" },
1106
+ { flag: "--confirm", type: "boolean", required: false, description: "Skip confirmation prompt" }
1107
+ ]);
1108
+ var FOLDER_COLUMNS = [
1109
+ { key: "id", header: "ID", width: 14 },
1110
+ { key: "name", header: "Name", width: 30 },
1111
+ { key: "task_count", header: "Tasks", width: 10 },
1112
+ { key: "archived", header: "Archived", width: 10 }
1113
+ ];
1114
+ function registerFolderCommands(program, getClient) {
1115
+ const folder = program.command("folder").description("Manage folders");
1116
+ folder.command("list").description("List folders in a space").requiredOption("--space-id <id>", "Space ID").option("--archived", "Include archived folders").action(async (opts) => {
1117
+ const client = getClient();
1118
+ const data = await client.get(`/space/${opts.spaceId}/folder`, {
1119
+ archived: opts.archived ? "true" : "false"
1120
+ });
1121
+ formatOutput(data.folders, FOLDER_COLUMNS, getOutputOptions(program));
1122
+ });
1123
+ folder.command("get").description("Get folder details").argument("<folder-id>", "Folder ID").action(async (folderId) => {
1124
+ const client = getClient();
1125
+ const data = await client.get(`/folder/${folderId}`);
1126
+ formatOutput(data, FOLDER_COLUMNS, getOutputOptions(program));
1127
+ });
1128
+ folder.command("create").description("Create a new folder").requiredOption("--space-id <id>", "Space ID").requiredOption("--name <name>", "Folder name").action(async (opts) => {
1129
+ const client = getClient();
1130
+ const data = await client.post(`/space/${opts.spaceId}/folder`, {
1131
+ name: opts.name
1132
+ });
1133
+ formatOutput(data, FOLDER_COLUMNS, getOutputOptions(program));
1134
+ });
1135
+ folder.command("update").description("Update a folder").argument("<folder-id>", "Folder ID").option("--name <name>", "New folder name").action(async (folderId, opts) => {
1136
+ const client = getClient();
1137
+ const body = {};
1138
+ if (opts.name !== void 0) body["name"] = opts.name;
1139
+ const data = await client.put(`/folder/${folderId}`, body);
1140
+ formatOutput(data, FOLDER_COLUMNS, getOutputOptions(program));
1141
+ });
1142
+ folder.command("delete").description("Delete a folder").argument("<folder-id>", "Folder ID").option("--confirm", "Skip confirmation prompt").action(async (folderId, opts) => {
1143
+ const client = getClient();
1144
+ if (!opts.confirm) {
1145
+ if (!process.stdin.isTTY) {
1146
+ process.stderr.write("Error: Use --confirm to delete in non-interactive mode.\n");
1147
+ process.exit(2);
1148
+ return;
1149
+ }
1150
+ const { confirm } = await import("@inquirer/prompts");
1151
+ const yes = await confirm({ message: `Delete folder ${folderId}?` });
1152
+ if (!yes) {
1153
+ process.stdout.write("Cancelled.\n");
1154
+ return;
1155
+ }
1156
+ }
1157
+ await client.delete(`/folder/${folderId}`);
1158
+ process.stdout.write(`Deleted folder ${folderId}
1159
+ `);
1160
+ });
1161
+ }
1162
+
1163
+ // src/commands/list.ts
1164
+ registerSchema("list", "list", "List lists in a folder", [
1165
+ { flag: "--folder-id", type: "string", required: true, description: "Folder ID" },
1166
+ { flag: "--archived", type: "boolean", required: false, description: "Include archived lists" }
1167
+ ]);
1168
+ registerSchema("list", "list-folderless", "List folderless lists in a space", [
1169
+ { flag: "--space-id", type: "string", required: true, description: "Space ID" },
1170
+ { flag: "--archived", type: "boolean", required: false, description: "Include archived lists" }
1171
+ ]);
1172
+ registerSchema("list", "get", "Get list details", [
1173
+ { flag: "<list-id>", type: "string", required: true, description: "List ID" }
1174
+ ]);
1175
+ registerSchema("list", "create", "Create a new list in a folder", [
1176
+ { flag: "--folder-id", type: "string", required: true, description: "Folder ID" },
1177
+ { flag: "--name", type: "string", required: true, description: "List name" },
1178
+ { flag: "--content", type: "string", required: false, description: "List description" },
1179
+ { flag: "--due-date", type: "string", required: false, description: "Due date (Unix ms)" },
1180
+ { flag: "--priority", type: "integer", required: false, description: "Priority (1-4)" },
1181
+ { flag: "--status", type: "string", required: false, description: "Default status" }
1182
+ ]);
1183
+ registerSchema("list", "create-folderless", "Create a folderless list in a space", [
1184
+ { flag: "--space-id", type: "string", required: true, description: "Space ID" },
1185
+ { flag: "--name", type: "string", required: true, description: "List name" },
1186
+ { flag: "--content", type: "string", required: false, description: "List description" }
1187
+ ]);
1188
+ registerSchema("list", "update", "Update a list", [
1189
+ { flag: "<list-id>", type: "string", required: true, description: "List ID" },
1190
+ { flag: "--name", type: "string", required: false, description: "New list name" },
1191
+ { flag: "--content", type: "string", required: false, description: "New description" },
1192
+ { flag: "--due-date", type: "string", required: false, description: "Due date (Unix ms)" },
1193
+ { flag: "--unset-status", type: "boolean", required: false, description: "Remove default status" }
1194
+ ]);
1195
+ registerSchema("list", "delete", "Delete a list", [
1196
+ { flag: "<list-id>", type: "string", required: true, description: "List ID" },
1197
+ { flag: "--confirm", type: "boolean", required: false, description: "Skip confirmation prompt" }
1198
+ ]);
1199
+ registerSchema("list", "add-task", "Add a task to a list", [
1200
+ { flag: "<list-id>", type: "string", required: true, description: "List ID" },
1201
+ { flag: "--task-id", type: "string", required: true, description: "Task ID" }
1202
+ ]);
1203
+ registerSchema("list", "remove-task", "Remove a task from a list", [
1204
+ { flag: "<list-id>", type: "string", required: true, description: "List ID" },
1205
+ { flag: "--task-id", type: "string", required: true, description: "Task ID" }
1206
+ ]);
1207
+ var LIST_COLUMNS = [
1208
+ { key: "id", header: "ID", width: 14 },
1209
+ { key: "name", header: "Name", width: 30 },
1210
+ { key: "content", header: "Description", width: 25 },
1211
+ { key: "task_count", header: "Tasks", width: 10 },
1212
+ { key: "archived", header: "Archived", width: 10 }
1213
+ ];
1214
+ function registerListCommands(program, getClient) {
1215
+ const list = program.command("list").description("Manage lists");
1216
+ list.command("list").description("List lists in a folder").requiredOption("--folder-id <id>", "Folder ID").option("--archived", "Include archived lists").action(async (opts) => {
1217
+ const client = getClient();
1218
+ const data = await client.get(`/folder/${opts.folderId}/list`, {
1219
+ archived: opts.archived ? "true" : "false"
1220
+ });
1221
+ formatOutput(data.lists, LIST_COLUMNS, getOutputOptions(program));
1222
+ });
1223
+ list.command("list-folderless").description("List folderless lists in a space").requiredOption("--space-id <id>", "Space ID").option("--archived", "Include archived lists").action(async (opts) => {
1224
+ const client = getClient();
1225
+ const data = await client.get(`/space/${opts.spaceId}/list`, {
1226
+ archived: opts.archived ? "true" : "false"
1227
+ });
1228
+ formatOutput(data.lists, LIST_COLUMNS, getOutputOptions(program));
1229
+ });
1230
+ list.command("get").description("Get list details").argument("<list-id>", "List ID").action(async (listId) => {
1231
+ const client = getClient();
1232
+ const data = await client.get(`/list/${listId}`);
1233
+ formatOutput(data, LIST_COLUMNS, getOutputOptions(program));
1234
+ });
1235
+ list.command("create").description("Create a new list in a folder").requiredOption("--folder-id <id>", "Folder ID").requiredOption("--name <name>", "List name").option("--content <desc>", "List description").option("--due-date <ts>", "Due date (Unix ms)").option("--priority <n>", "Priority (1-4)", parseInt).option("--status <s>", "Default status").action(async (opts) => {
1236
+ const client = getClient();
1237
+ const body = { name: opts.name };
1238
+ if (opts.content !== void 0) body["content"] = opts.content;
1239
+ if (opts.dueDate !== void 0) body["due_date"] = parseInt(opts.dueDate, 10);
1240
+ if (opts.priority !== void 0) body["priority"] = opts.priority;
1241
+ if (opts.status !== void 0) body["status"] = opts.status;
1242
+ const data = await client.post(`/folder/${opts.folderId}/list`, body);
1243
+ formatOutput(data, LIST_COLUMNS, getOutputOptions(program));
1244
+ });
1245
+ list.command("create-folderless").description("Create a folderless list in a space").requiredOption("--space-id <id>", "Space ID").requiredOption("--name <name>", "List name").option("--content <desc>", "List description").action(async (opts) => {
1246
+ const client = getClient();
1247
+ const body = { name: opts.name };
1248
+ if (opts.content !== void 0) body["content"] = opts.content;
1249
+ const data = await client.post(`/space/${opts.spaceId}/list`, body);
1250
+ formatOutput(data, LIST_COLUMNS, getOutputOptions(program));
1251
+ });
1252
+ list.command("update").description("Update a list").argument("<list-id>", "List ID").option("--name <name>", "New list name").option("--content <desc>", "New description").option("--due-date <ts>", "Due date (Unix ms)").option("--unset-status", "Remove default status").action(async (listId, opts) => {
1253
+ const client = getClient();
1254
+ const body = {};
1255
+ if (opts.name !== void 0) body["name"] = opts.name;
1256
+ if (opts.content !== void 0) body["content"] = opts.content;
1257
+ if (opts.dueDate !== void 0) body["due_date"] = parseInt(opts.dueDate, 10);
1258
+ if (opts.unsetStatus) body["unset_status"] = true;
1259
+ const data = await client.put(`/list/${listId}`, body);
1260
+ formatOutput(data, LIST_COLUMNS, getOutputOptions(program));
1261
+ });
1262
+ list.command("delete").description("Delete a list").argument("<list-id>", "List ID").option("--confirm", "Skip confirmation prompt").action(async (listId, opts) => {
1263
+ const client = getClient();
1264
+ if (!opts.confirm) {
1265
+ if (!process.stdin.isTTY) {
1266
+ process.stderr.write("Error: Use --confirm to delete in non-interactive mode.\n");
1267
+ process.exit(2);
1268
+ return;
1269
+ }
1270
+ const { confirm } = await import("@inquirer/prompts");
1271
+ const yes = await confirm({ message: `Delete list ${listId}?` });
1272
+ if (!yes) {
1273
+ process.stdout.write("Cancelled.\n");
1274
+ return;
1275
+ }
1276
+ }
1277
+ await client.delete(`/list/${listId}`);
1278
+ process.stdout.write(`Deleted list ${listId}
1279
+ `);
1280
+ });
1281
+ list.command("add-task").description("Add a task to a list").argument("<list-id>", "List ID").requiredOption("--task-id <id>", "Task ID").action(async (listId, opts) => {
1282
+ const client = getClient();
1283
+ await client.post(`/list/${listId}/task/${opts.taskId}`);
1284
+ process.stdout.write(`Added task ${opts.taskId} to list ${listId}
1285
+ `);
1286
+ });
1287
+ list.command("remove-task").description("Remove a task from a list").argument("<list-id>", "List ID").requiredOption("--task-id <id>", "Task ID").action(async (listId, opts) => {
1288
+ const client = getClient();
1289
+ await client.delete(`/list/${listId}/task/${opts.taskId}`);
1290
+ process.stdout.write(`Removed task ${opts.taskId} from list ${listId}
1291
+ `);
1292
+ });
1293
+ }
1294
+
1295
+ // src/commands/task.ts
1296
+ init_config();
1297
+ registerSchema("task", "list", "List tasks in a list", [
1298
+ { flag: "--list-id", type: "string", required: true, description: "List ID" },
1299
+ { flag: "--archived", type: "boolean", required: false, description: "Include archived tasks" },
1300
+ { flag: "--include-closed", type: "boolean", required: false, description: "Include closed tasks" },
1301
+ { flag: "--subtasks", type: "boolean", required: false, description: "Include subtasks" },
1302
+ { flag: "--page", type: "integer", required: false, description: "Page number (0-indexed)" },
1303
+ { flag: "--status", type: "string[]", required: false, description: "Filter by status (repeatable)" },
1304
+ { flag: "--assignee", type: "string[]", required: false, description: "Filter by assignee ID (repeatable)" },
1305
+ { flag: "--tag", type: "string[]", required: false, description: "Filter by tag name (repeatable)" },
1306
+ { flag: "--order-by", type: "string", required: false, description: "Sort field (id|created|updated|due_date)" },
1307
+ { flag: "--reverse", type: "boolean", required: false, description: "Reverse sort order" }
1308
+ ]);
1309
+ registerSchema("task", "search", "Search tasks across a workspace", [
1310
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
1311
+ { flag: "--query", type: "string", required: false, description: "Full-text search query" },
1312
+ { flag: "--include-closed", type: "boolean", required: false, description: "Include closed tasks" },
1313
+ { flag: "--status", type: "string[]", required: false, description: "Filter by status (repeatable)" },
1314
+ { flag: "--assignee", type: "string[]", required: false, description: "Filter by assignee ID (repeatable)" },
1315
+ { flag: "--tag", type: "string[]", required: false, description: "Filter by tag name (repeatable)" },
1316
+ { flag: "--page", type: "integer", required: false, description: "Page number (0-indexed)" }
1317
+ ]);
1318
+ registerSchema("task", "get", "Get task details", [
1319
+ { flag: "<task-id>", type: "string", required: true, description: "Task ID" },
1320
+ { flag: "--include-subtasks", type: "boolean", required: false, description: "Include subtasks" },
1321
+ { flag: "--include-markdown-description", type: "boolean", required: false, description: "Return description as Markdown" }
1322
+ ]);
1323
+ registerSchema("task", "create", "Create a new task", [
1324
+ { flag: "--list-id", type: "string", required: true, description: "List ID" },
1325
+ { flag: "--name", type: "string", required: true, description: "Task name" },
1326
+ { flag: "--description", type: "string", required: false, description: "Plain text description" },
1327
+ { flag: "--markdown-description", type: "string", required: false, description: "Markdown description" },
1328
+ { flag: "--status", type: "string", required: false, description: "Initial status" },
1329
+ { flag: "--priority", type: "integer", required: false, description: "Priority (1=urgent, 2=high, 3=normal, 4=low)" },
1330
+ { flag: "--due-date", type: "string", required: false, description: "Due date (Unix ms)" },
1331
+ { flag: "--start-date", type: "string", required: false, description: "Start date (Unix ms)" },
1332
+ { flag: "--assignee", type: "string[]", required: false, description: "Assignee user ID (repeatable)" },
1333
+ { flag: "--tag", type: "string[]", required: false, description: "Tag name (repeatable)" },
1334
+ { flag: "--time-estimate", type: "integer", required: false, description: "Time estimate in ms" },
1335
+ { flag: "--parent", type: "string", required: false, description: "Parent task ID (creates subtask)" }
1336
+ ]);
1337
+ registerSchema("task", "update", "Update a task", [
1338
+ { flag: "<task-id>", type: "string", required: true, description: "Task ID" },
1339
+ { flag: "--name", type: "string", required: false, description: "New task name" },
1340
+ { flag: "--description", type: "string", required: false, description: "New description" },
1341
+ { flag: "--status", type: "string", required: false, description: "New status" },
1342
+ { flag: "--priority", type: "integer", required: false, description: "New priority (1-4)" },
1343
+ { flag: "--due-date", type: "string", required: false, description: "New due date (Unix ms)" },
1344
+ { flag: "--assignee-add", type: "string[]", required: false, description: "Add assignee (repeatable)" },
1345
+ { flag: "--assignee-remove", type: "string[]", required: false, description: "Remove assignee (repeatable)" }
1346
+ ]);
1347
+ registerSchema("task", "delete", "Delete a task", [
1348
+ { flag: "<task-id>", type: "string", required: true, description: "Task ID" },
1349
+ { flag: "--confirm", type: "boolean", required: false, description: "Skip confirmation prompt" }
1350
+ ]);
1351
+ registerSchema("task", "time-in-status", "Get time spent in each status for a task", [
1352
+ { flag: "<task-id>", type: "string", required: true, description: "Task ID" }
1353
+ ]);
1354
+ var TASK_COLUMNS = [
1355
+ { key: "id", header: "ID", width: 12 },
1356
+ { key: "name", header: "Name", width: 30 },
1357
+ { key: "status", header: "Status", width: 15 },
1358
+ { key: "priority", header: "Priority", width: 10 },
1359
+ { key: "assignees", header: "Assignees", width: 20 },
1360
+ { key: "due_date", header: "Due Date", width: 15 }
1361
+ ];
1362
+ var TIME_IN_STATUS_COLUMNS = [
1363
+ { key: "status", header: "Status", width: 20 },
1364
+ { key: "color", header: "Color", width: 10 },
1365
+ { key: "total_time", header: "Total Time", width: 20 }
1366
+ ];
1367
+ function requireWorkspaceId3(program) {
1368
+ const globalOpts = program.opts();
1369
+ const workspaceId = resolveWorkspaceId(globalOpts["workspaceId"]);
1370
+ if (!workspaceId) {
1371
+ process.stderr.write("Error: No workspace ID. Use --workspace-id or run: clickup config set workspace_id <id>\n");
1372
+ process.exit(2);
1373
+ return void 0;
1374
+ }
1375
+ return workspaceId;
1376
+ }
1377
+ function collect(value, previous) {
1378
+ return previous.concat([value]);
1379
+ }
1380
+ function buildTaskListParams(opts) {
1381
+ const params = {};
1382
+ if (opts.archived) params["archived"] = "true";
1383
+ if (opts.includeClosed) params["include_closed"] = "true";
1384
+ if (opts.subtasks) params["subtasks"] = "true";
1385
+ if (opts.page !== void 0) params["page"] = String(opts.page);
1386
+ if (opts.orderBy) params["order_by"] = opts.orderBy;
1387
+ if (opts.reverse) params["reverse"] = "true";
1388
+ if (opts.dueDateGt) params["due_date_gt"] = opts.dueDateGt;
1389
+ if (opts.dueDateLt) params["due_date_lt"] = opts.dueDateLt;
1390
+ if (opts.dateCreatedGt) params["date_created_gt"] = opts.dateCreatedGt;
1391
+ if (opts.dateCreatedLt) params["date_created_lt"] = opts.dateCreatedLt;
1392
+ if (opts.dateUpdatedGt) params["date_updated_gt"] = opts.dateUpdatedGt;
1393
+ if (opts.dateUpdatedLt) params["date_updated_lt"] = opts.dateUpdatedLt;
1394
+ const statuses = opts.status;
1395
+ if (statuses?.length) params["statuses[]"] = statuses;
1396
+ const assignees = opts.assignee;
1397
+ if (assignees?.length) params["assignees[]"] = assignees;
1398
+ const tags = opts.tag;
1399
+ if (tags?.length) params["tags[]"] = tags;
1400
+ const customFields = opts.customField;
1401
+ if (customFields?.length) {
1402
+ for (const cf of customFields) params["custom_fields"] = JSON.stringify(customFields.map((f) => JSON.parse(f)));
1403
+ }
1404
+ return params;
1405
+ }
1406
+ function registerTaskCommands(program, getClient) {
1407
+ const task = program.command("task").description("Manage tasks");
1408
+ task.command("list").description("List tasks in a list").requiredOption("--list-id <id>", "List ID").option("--archived", "Include archived tasks").option("--include-closed", "Include tasks in closed status").option("--subtasks", "Include subtasks in results").option("--page <n>", "Page number (0-indexed)", parseInt).option("--status <s>", "Filter by status (repeatable)", collect, []).option("--assignee <id>", "Filter by assignee ID (repeatable)", collect, []).option("--tag <name>", "Filter by tag name (repeatable)", collect, []).option("--due-date-gt <ts>", "Tasks due after timestamp").option("--due-date-lt <ts>", "Tasks due before timestamp").option("--date-created-gt <ts>", "Tasks created after timestamp").option("--date-created-lt <ts>", "Tasks created before timestamp").option("--date-updated-gt <ts>", "Tasks updated after timestamp").option("--date-updated-lt <ts>", "Tasks updated before timestamp").option("--custom-field <json>", "Custom field filter as JSON (repeatable)", collect, []).option("--order-by <field>", "Sort by field (id|created|updated|due_date)").option("--reverse", "Reverse sort order").action(async (opts) => {
1409
+ const client = getClient();
1410
+ const params = buildTaskListParams(opts);
1411
+ const data = await client.get(`/list/${opts.listId}/task`, params);
1412
+ formatOutput(data.tasks, TASK_COLUMNS, getOutputOptions(program));
1413
+ });
1414
+ task.command("search").description("Search tasks across a workspace").option("--query <text>", "Full-text search query").option("--include-closed", "Include tasks in closed status").option("--subtasks", "Include subtasks").option("--page <n>", "Page number (0-indexed)", parseInt).option("--status <s>", "Filter by status (repeatable)", collect, []).option("--assignee <id>", "Filter by assignee ID (repeatable)", collect, []).option("--tag <name>", "Filter by tag name (repeatable)", collect, []).option("--priority <n>", "Filter by priority (repeatable)", collect, []).option("--list-id <id>", "Scope to list IDs (repeatable)", collect, []).option("--folder-id <id>", "Scope to folder IDs (repeatable)", collect, []).option("--space-id <id>", "Scope to space IDs (repeatable)", collect, []).option("--project-id <id>", "Scope to project IDs (repeatable)", collect, []).option("--due-date-gt <ts>", "Tasks due after timestamp").option("--due-date-lt <ts>", "Tasks due before timestamp").option("--date-created-gt <ts>", "Tasks created after timestamp").option("--date-created-lt <ts>", "Tasks created before timestamp").option("--custom-field <json>", "Custom field filter as JSON (repeatable)", collect, []).option("--order-by <field>", "Sort by field (id|created|updated|due_date)").option("--reverse", "Reverse sort order").action(async (opts) => {
1415
+ const workspaceId = requireWorkspaceId3(program);
1416
+ if (!workspaceId) return;
1417
+ const client = getClient();
1418
+ const params = {};
1419
+ if (opts.query) params["query"] = opts.query;
1420
+ if (opts.includeClosed) params["include_closed"] = "true";
1421
+ if (opts.subtasks) params["subtasks"] = "true";
1422
+ if (opts.page !== void 0) params["page"] = String(opts.page);
1423
+ if (opts.orderBy) params["order_by"] = opts.orderBy;
1424
+ if (opts.reverse) params["reverse"] = "true";
1425
+ if (opts.dueDateGt) params["due_date_gt"] = opts.dueDateGt;
1426
+ if (opts.dueDateLt) params["due_date_lt"] = opts.dueDateLt;
1427
+ if (opts.dateCreatedGt) params["date_created_gt"] = opts.dateCreatedGt;
1428
+ if (opts.dateCreatedLt) params["date_created_lt"] = opts.dateCreatedLt;
1429
+ const statuses = opts.status;
1430
+ if (statuses.length) params["statuses[]"] = statuses;
1431
+ const assignees = opts.assignee;
1432
+ if (assignees.length) params["assignees[]"] = assignees;
1433
+ const tags = opts.tag;
1434
+ if (tags.length) params["tags[]"] = tags;
1435
+ const priorities = opts.priority;
1436
+ if (priorities.length) params["priorities[]"] = priorities;
1437
+ const listIds = opts.listId;
1438
+ if (listIds.length) params["list_ids[]"] = listIds;
1439
+ const folderIds = opts.folderId;
1440
+ if (folderIds.length) params["folder_ids[]"] = folderIds;
1441
+ const spaceIds = opts.spaceId;
1442
+ if (spaceIds.length) params["space_ids[]"] = spaceIds;
1443
+ const projectIds = opts.projectId;
1444
+ if (projectIds.length) params["project_ids[]"] = projectIds;
1445
+ const customFields = opts.customField;
1446
+ if (customFields.length) params["custom_fields"] = JSON.stringify(customFields.map((f) => JSON.parse(f)));
1447
+ const data = await client.get(`/team/${workspaceId}/task`, params);
1448
+ formatOutput(data.tasks, TASK_COLUMNS, getOutputOptions(program));
1449
+ });
1450
+ task.command("get").description("Get task details").argument("<task-id>", "Task ID").option("--include-subtasks", "Include subtasks").option("--include-markdown-description", "Return description as Markdown").action(async (taskId, opts) => {
1451
+ const client = getClient();
1452
+ const params = {};
1453
+ if (opts.includeSubtasks) params["include_subtasks"] = "true";
1454
+ if (opts.includeMarkdownDescription) params["include_markdown_description"] = "true";
1455
+ const data = await client.get(`/task/${taskId}`, params);
1456
+ formatOutput(data, TASK_COLUMNS, getOutputOptions(program));
1457
+ });
1458
+ task.command("create").description("Create a new task").requiredOption("--list-id <id>", "List ID").requiredOption("--name <name>", "Task name").option("--description <desc>", "Plain text description").option("--markdown-description <md>", "Markdown description (overrides --description)").option("--status <s>", "Initial status").option("--priority <n>", "Priority (1=urgent, 2=high, 3=normal, 4=low)", parseInt).option("--due-date <date>", "Due date (Unix ms)").option("--start-date <date>", "Start date (Unix ms)").option("--assignee <id>", "Assignee user ID (repeatable)", collect, []).option("--tag <name>", "Tag name (repeatable)", collect, []).option("--time-estimate <ms>", "Time estimate in milliseconds", parseInt).option("--notify-all", "Notify all assignees and watchers").option("--parent <task-id>", "Parent task ID (creates subtask)").option("--links-to <task-id>", "Link to another task").option("--custom-field <id=value>", "Set custom field (repeatable)", collect, []).option("--check-required-custom-fields", "Reject if required custom fields are missing").action(async (opts) => {
1459
+ const client = getClient();
1460
+ const body = { name: opts.name };
1461
+ if (opts.markdownDescription !== void 0) body["markdown_description"] = opts.markdownDescription;
1462
+ else if (opts.description !== void 0) body["description"] = opts.description;
1463
+ if (opts.status !== void 0) body["status"] = opts.status;
1464
+ if (opts.priority !== void 0) body["priority"] = opts.priority;
1465
+ if (opts.dueDate !== void 0) body["due_date"] = parseInt(opts.dueDate, 10);
1466
+ if (opts.startDate !== void 0) body["start_date"] = parseInt(opts.startDate, 10);
1467
+ const assignees = opts.assignee;
1468
+ if (assignees.length) body["assignees"] = assignees.map((a) => parseInt(a, 10));
1469
+ const tags = opts.tag;
1470
+ if (tags.length) body["tags"] = tags;
1471
+ if (opts.timeEstimate !== void 0) body["time_estimate"] = opts.timeEstimate;
1472
+ if (opts.notifyAll) body["notify_all"] = true;
1473
+ if (opts.parent !== void 0) body["parent"] = opts.parent;
1474
+ if (opts.linksTo !== void 0) body["links_to"] = opts.linksTo;
1475
+ const customFields = opts.customField;
1476
+ if (customFields.length) {
1477
+ body["custom_fields"] = customFields.map((cf) => {
1478
+ const eqIdx = cf.indexOf("=");
1479
+ if (eqIdx === -1) throw new Error(`Invalid custom field format: ${cf}. Expected: <id>=<value>`);
1480
+ const id = cf.slice(0, eqIdx);
1481
+ let value = cf.slice(eqIdx + 1);
1482
+ try {
1483
+ value = JSON.parse(value);
1484
+ } catch {
1485
+ }
1486
+ return { id, value };
1487
+ });
1488
+ }
1489
+ if (opts.checkRequiredCustomFields) body["check_required_custom_fields"] = true;
1490
+ const data = await client.post(`/list/${opts.listId}/task`, body);
1491
+ formatOutput(data, TASK_COLUMNS, getOutputOptions(program));
1492
+ });
1493
+ task.command("update").description("Update a task").argument("<task-id>", "Task ID").option("--name <name>", "New task name").option("--description <desc>", "New description").option("--status <s>", "New status").option("--priority <n>", "New priority (1-4)", parseInt).option("--due-date <date>", "New due date (Unix ms)").option("--start-date <date>", "New start date (Unix ms)").option("--time-estimate <ms>", "New time estimate in milliseconds", parseInt).option("--assignee-add <id>", "Add assignee (repeatable)", collect, []).option("--assignee-remove <id>", "Remove assignee (repeatable)", collect, []).option("--archived <bool>", "Archive or unarchive").action(async (taskId, opts) => {
1494
+ const client = getClient();
1495
+ const body = {};
1496
+ if (opts.name !== void 0) body["name"] = opts.name;
1497
+ if (opts.description !== void 0) body["description"] = opts.description;
1498
+ if (opts.status !== void 0) body["status"] = opts.status;
1499
+ if (opts.priority !== void 0) body["priority"] = opts.priority;
1500
+ if (opts.dueDate !== void 0) body["due_date"] = parseInt(opts.dueDate, 10);
1501
+ if (opts.startDate !== void 0) body["start_date"] = parseInt(opts.startDate, 10);
1502
+ if (opts.timeEstimate !== void 0) body["time_estimate"] = opts.timeEstimate;
1503
+ const addIds = opts.assigneeAdd;
1504
+ const remIds = opts.assigneeRemove;
1505
+ if (addIds.length || remIds.length) {
1506
+ body["assignees"] = {
1507
+ add: addIds.map((a) => parseInt(a, 10)),
1508
+ rem: remIds.map((a) => parseInt(a, 10))
1509
+ };
1510
+ }
1511
+ if (opts.archived !== void 0) body["archived"] = opts.archived === "true";
1512
+ const data = await client.put(`/task/${taskId}`, body);
1513
+ formatOutput(data, TASK_COLUMNS, getOutputOptions(program));
1514
+ });
1515
+ task.command("delete").description("Delete a task").argument("<task-id>", "Task ID").option("--confirm", "Skip confirmation prompt").action(async (taskId, opts) => {
1516
+ const client = getClient();
1517
+ if (!opts.confirm) {
1518
+ if (!process.stdin.isTTY) {
1519
+ process.stderr.write("Error: Use --confirm to delete in non-interactive mode.\n");
1520
+ process.exit(2);
1521
+ return;
1522
+ }
1523
+ const { confirm } = await import("@inquirer/prompts");
1524
+ const yes = await confirm({ message: `Delete task ${taskId}?` });
1525
+ if (!yes) {
1526
+ process.stdout.write("Cancelled.\n");
1527
+ return;
1528
+ }
1529
+ }
1530
+ await client.delete(`/task/${taskId}`);
1531
+ process.stdout.write(`Deleted task ${taskId}
1532
+ `);
1533
+ });
1534
+ task.command("time-in-status").description("Get time spent in each status").argument("<task-id>", "Task ID").action(async (taskId) => {
1535
+ const client = getClient();
1536
+ const data = await client.get(`/task/${taskId}/time_in_status`);
1537
+ const entries = data.status_history ?? [];
1538
+ if (data.current_status) entries.push(data.current_status);
1539
+ formatOutput(entries, TIME_IN_STATUS_COLUMNS, getOutputOptions(program));
1540
+ });
1541
+ task.command("bulk-time-in-status").description("Get time-in-status for multiple tasks").requiredOption("--task-id <id>", "Task ID (repeatable)", collect, []).action(async (opts) => {
1542
+ const client = getClient();
1543
+ const taskIds = opts.taskId;
1544
+ const results = [];
1545
+ for (const id of taskIds) {
1546
+ const data = await client.get(`/task/${id}/time_in_status`);
1547
+ const entries = data.status_history ?? [];
1548
+ if (data.current_status) entries.push(data.current_status);
1549
+ results.push({ task_id: id, statuses: entries });
1550
+ }
1551
+ formatOutput(results, [
1552
+ { key: "task_id", header: "Task ID", width: 14 },
1553
+ { key: "statuses", header: "Statuses", width: 50 }
1554
+ ], getOutputOptions(program));
1555
+ });
1556
+ }
1557
+
1558
+ // src/commands/checklist.ts
1559
+ registerSchema("checklist", "create", "Create a checklist on a task", [
1560
+ { flag: "--task-id", type: "string", required: true, description: "Task ID" },
1561
+ { flag: "--name", type: "string", required: true, description: "Checklist name" }
1562
+ ]);
1563
+ registerSchema("checklist", "update", "Update a checklist", [
1564
+ { flag: "<checklist-id>", type: "string", required: true, description: "Checklist ID" },
1565
+ { flag: "--name", type: "string", required: false, description: "New checklist name" },
1566
+ { flag: "--position", type: "integer", required: false, description: "Position (0-indexed)" }
1567
+ ]);
1568
+ registerSchema("checklist", "delete", "Delete a checklist", [
1569
+ { flag: "<checklist-id>", type: "string", required: true, description: "Checklist ID" },
1570
+ { flag: "--confirm", type: "boolean", required: false, description: "Skip confirmation prompt" }
1571
+ ]);
1572
+ registerSchema("checklist", "add-item", "Add an item to a checklist", [
1573
+ { flag: "<checklist-id>", type: "string", required: true, description: "Checklist ID" },
1574
+ { flag: "--name", type: "string", required: true, description: "Item name" },
1575
+ { flag: "--assignee", type: "string", required: false, description: "Assignee user ID" },
1576
+ { flag: "--resolved", type: "boolean", required: false, description: "Mark as resolved" }
1577
+ ]);
1578
+ registerSchema("checklist", "update-item", "Update a checklist item", [
1579
+ { flag: "<checklist-id>", type: "string", required: true, description: "Checklist ID" },
1580
+ { flag: "--item-id", type: "string", required: true, description: "Item ID" },
1581
+ { flag: "--name", type: "string", required: false, description: "New item name" },
1582
+ { flag: "--resolved", type: "boolean", required: false, description: "Resolved status" },
1583
+ { flag: "--assignee", type: "string", required: false, description: "Assignee user ID" }
1584
+ ]);
1585
+ registerSchema("checklist", "delete-item", "Delete a checklist item", [
1586
+ { flag: "<checklist-id>", type: "string", required: true, description: "Checklist ID" },
1587
+ { flag: "--item-id", type: "string", required: true, description: "Item ID" },
1588
+ { flag: "--confirm", type: "boolean", required: false, description: "Skip confirmation prompt" }
1589
+ ]);
1590
+ var CHECKLIST_COLUMNS = [
1591
+ { key: "id", header: "ID", width: 20 },
1592
+ { key: "name", header: "Name", width: 30 },
1593
+ { key: "resolved", header: "Resolved", width: 10 },
1594
+ { key: "unresolved", header: "Unresolved", width: 10 }
1595
+ ];
1596
+ function registerChecklistCommands(program, getClient) {
1597
+ const checklist = program.command("checklist").description("Manage checklists");
1598
+ checklist.command("create").description("Create a checklist on a task").requiredOption("--task-id <id>", "Task ID").requiredOption("--name <name>", "Checklist name").action(async (opts) => {
1599
+ const client = getClient();
1600
+ const data = await client.post(`/task/${opts.taskId}/checklist`, { name: opts.name });
1601
+ formatOutput(data.checklist, CHECKLIST_COLUMNS, getOutputOptions(program));
1602
+ });
1603
+ checklist.command("update").description("Update a checklist").argument("<checklist-id>", "Checklist ID").option("--name <name>", "New checklist name").option("--position <n>", "Position (0-indexed)", parseInt).action(async (checklistId, opts) => {
1604
+ const client = getClient();
1605
+ const body = {};
1606
+ if (opts.name !== void 0) body["name"] = opts.name;
1607
+ if (opts.position !== void 0) body["position"] = opts.position;
1608
+ const data = await client.put(`/checklist/${checklistId}`, body);
1609
+ formatOutput(data.checklist, CHECKLIST_COLUMNS, getOutputOptions(program));
1610
+ });
1611
+ checklist.command("delete").description("Delete a checklist").argument("<checklist-id>", "Checklist ID").option("--confirm", "Skip confirmation prompt").action(async (checklistId, opts) => {
1612
+ const client = getClient();
1613
+ if (!opts.confirm) {
1614
+ if (!process.stdin.isTTY) {
1615
+ process.stderr.write("Error: Use --confirm to delete in non-interactive mode.\n");
1616
+ process.exit(2);
1617
+ return;
1618
+ }
1619
+ const { confirm } = await import("@inquirer/prompts");
1620
+ const yes = await confirm({ message: `Delete checklist ${checklistId}?` });
1621
+ if (!yes) {
1622
+ process.stdout.write("Cancelled.\n");
1623
+ return;
1624
+ }
1625
+ }
1626
+ await client.delete(`/checklist/${checklistId}`);
1627
+ process.stdout.write(`Deleted checklist ${checklistId}
1628
+ `);
1629
+ });
1630
+ checklist.command("add-item").description("Add an item to a checklist").argument("<checklist-id>", "Checklist ID").requiredOption("--name <name>", "Item name").option("--assignee <id>", "Assignee user ID").option("--resolved <bool>", "Mark as resolved").option("--parent <item-id>", "Parent item ID").action(async (checklistId, opts) => {
1631
+ const client = getClient();
1632
+ const body = { name: opts.name };
1633
+ if (opts.assignee !== void 0) body["assignee"] = parseInt(opts.assignee, 10);
1634
+ if (opts.resolved !== void 0) body["resolved"] = opts.resolved === "true";
1635
+ if (opts.parent !== void 0) body["parent"] = opts.parent;
1636
+ const data = await client.post(`/checklist/${checklistId}/checklist_item`, body);
1637
+ formatOutput(data.checklist, CHECKLIST_COLUMNS, getOutputOptions(program));
1638
+ });
1639
+ checklist.command("update-item").description("Update a checklist item").argument("<checklist-id>", "Checklist ID").requiredOption("--item-id <id>", "Item ID").option("--name <name>", "New item name").option("--resolved <bool>", "Mark as resolved/unresolved").option("--assignee <id>", "Assignee user ID").option("--parent <item-id>", "Parent item ID").action(async (checklistId, opts) => {
1640
+ const client = getClient();
1641
+ const body = {};
1642
+ if (opts.name !== void 0) body["name"] = opts.name;
1643
+ if (opts.resolved !== void 0) body["resolved"] = opts.resolved === "true";
1644
+ if (opts.assignee !== void 0) body["assignee"] = parseInt(opts.assignee, 10);
1645
+ if (opts.parent !== void 0) body["parent"] = opts.parent;
1646
+ const data = await client.put(`/checklist/${checklistId}/checklist_item/${opts.itemId}`, body);
1647
+ formatOutput(data.checklist, CHECKLIST_COLUMNS, getOutputOptions(program));
1648
+ });
1649
+ checklist.command("delete-item").description("Delete a checklist item").argument("<checklist-id>", "Checklist ID").requiredOption("--item-id <id>", "Item ID").option("--confirm", "Skip confirmation prompt").action(async (checklistId, opts) => {
1650
+ const client = getClient();
1651
+ if (!opts.confirm) {
1652
+ if (!process.stdin.isTTY) {
1653
+ process.stderr.write("Error: Use --confirm to delete in non-interactive mode.\n");
1654
+ process.exit(2);
1655
+ return;
1656
+ }
1657
+ const { confirm } = await import("@inquirer/prompts");
1658
+ const yes = await confirm({ message: `Delete checklist item ${opts.itemId}?` });
1659
+ if (!yes) {
1660
+ process.stdout.write("Cancelled.\n");
1661
+ return;
1662
+ }
1663
+ }
1664
+ await client.delete(`/checklist/${checklistId}/checklist_item/${opts.itemId}`);
1665
+ process.stdout.write(`Deleted checklist item ${opts.itemId}
1666
+ `);
1667
+ });
1668
+ }
1669
+
1670
+ // src/commands/custom-field.ts
1671
+ registerSchema("field", "list", "List custom fields for a list, folder, space, or workspace", [
1672
+ { flag: "--list-id", type: "string", required: false, description: "List ID" },
1673
+ { flag: "--folder-id", type: "string", required: false, description: "Folder ID" },
1674
+ { flag: "--space-id", type: "string", required: false, description: "Space ID" },
1675
+ { flag: "--workspace-id", type: "string", required: false, description: "Workspace ID" }
1676
+ ]);
1677
+ registerSchema("field", "set", "Set a custom field value on a task", [
1678
+ { flag: "--task-id", type: "string", required: true, description: "Task ID" },
1679
+ { flag: "--field-id", type: "string", required: true, description: "Field ID" },
1680
+ { flag: "--value", type: "string", required: true, description: "Field value" }
1681
+ ]);
1682
+ registerSchema("field", "remove", "Remove a custom field value from a task", [
1683
+ { flag: "--task-id", type: "string", required: true, description: "Task ID" },
1684
+ { flag: "--field-id", type: "string", required: true, description: "Field ID" }
1685
+ ]);
1686
+ var FIELD_COLUMNS = [
1687
+ { key: "id", header: "ID", width: 20 },
1688
+ { key: "name", header: "Name", width: 25 },
1689
+ { key: "type", header: "Type", width: 15 },
1690
+ { key: "required", header: "Required", width: 10 },
1691
+ { key: "date_created", header: "Created", width: 15 }
1692
+ ];
1693
+ function registerFieldCommands(program, getClient) {
1694
+ const field = program.command("field").description("Manage custom fields");
1695
+ field.command("list").description("List custom fields (provide one ID flag)").option("--list-id <id>", "List ID").option("--folder-id <id>", "Folder ID").option("--space-id <id>", "Space ID").action(async (opts) => {
1696
+ const client = getClient();
1697
+ const globalWorkspaceId = program.opts()["workspaceId"];
1698
+ let endpoint;
1699
+ if (opts.listId) {
1700
+ endpoint = `/list/${opts.listId}/field`;
1701
+ } else if (opts.folderId) {
1702
+ endpoint = `/folder/${opts.folderId}/field`;
1703
+ } else if (opts.spaceId) {
1704
+ endpoint = `/space/${opts.spaceId}/field`;
1705
+ } else if (globalWorkspaceId) {
1706
+ endpoint = `/team/${globalWorkspaceId}/field`;
1707
+ } else {
1708
+ process.stderr.write("Error: Provide one of --list-id, --folder-id, --space-id, or --workspace-id.\n");
1709
+ process.exit(2);
1710
+ return;
1711
+ }
1712
+ const data = await client.get(endpoint);
1713
+ formatOutput(data.fields, FIELD_COLUMNS, getOutputOptions(program));
1714
+ });
1715
+ field.command("set").description("Set a custom field value on a task").requiredOption("--task-id <id>", "Task ID").requiredOption("--field-id <fid>", "Field ID").requiredOption("--value <value>", "Field value").action(async (opts) => {
1716
+ const client = getClient();
1717
+ let parsedValue = opts.value;
1718
+ try {
1719
+ parsedValue = JSON.parse(opts.value);
1720
+ } catch {
1721
+ }
1722
+ const data = await client.post(`/task/${opts.taskId}/field/${opts.fieldId}`, { value: parsedValue });
1723
+ formatOutput(data, FIELD_COLUMNS, getOutputOptions(program));
1724
+ });
1725
+ field.command("remove").description("Remove a custom field value from a task").requiredOption("--task-id <id>", "Task ID").requiredOption("--field-id <fid>", "Field ID").action(async (opts) => {
1726
+ const client = getClient();
1727
+ await client.delete(`/task/${opts.taskId}/field/${opts.fieldId}`);
1728
+ process.stdout.write(`Removed field ${opts.fieldId} from task ${opts.taskId}
1729
+ `);
1730
+ });
1731
+ }
1732
+
1733
+ // src/commands/tag.ts
1734
+ registerSchema("tag", "list", "List tags in a space", [
1735
+ { flag: "--space-id", type: "string", required: true, description: "Space ID" }
1736
+ ]);
1737
+ registerSchema("tag", "create", "Create a tag in a space", [
1738
+ { flag: "--space-id", type: "string", required: true, description: "Space ID" },
1739
+ { flag: "--name", type: "string", required: true, description: "Tag name" },
1740
+ { flag: "--fg-color", type: "string", required: false, description: "Text color hex" },
1741
+ { flag: "--bg-color", type: "string", required: false, description: "Background color hex" }
1742
+ ]);
1743
+ registerSchema("tag", "update", "Update a tag in a space", [
1744
+ { flag: "--space-id", type: "string", required: true, description: "Space ID" },
1745
+ { flag: "--name", type: "string", required: true, description: "Current tag name" },
1746
+ { flag: "--new-name", type: "string", required: false, description: "New tag name" },
1747
+ { flag: "--fg-color", type: "string", required: false, description: "New text color" },
1748
+ { flag: "--bg-color", type: "string", required: false, description: "New background color" }
1749
+ ]);
1750
+ registerSchema("tag", "delete", "Delete a tag from a space", [
1751
+ { flag: "--space-id", type: "string", required: true, description: "Space ID" },
1752
+ { flag: "--name", type: "string", required: true, description: "Tag name" },
1753
+ { flag: "--confirm", type: "boolean", required: false, description: "Skip confirmation prompt" }
1754
+ ]);
1755
+ registerSchema("tag", "add", "Add a tag to a task", [
1756
+ { flag: "--task-id", type: "string", required: true, description: "Task ID" },
1757
+ { flag: "--name", type: "string", required: true, description: "Tag name" }
1758
+ ]);
1759
+ registerSchema("tag", "remove", "Remove a tag from a task", [
1760
+ { flag: "--task-id", type: "string", required: true, description: "Task ID" },
1761
+ { flag: "--name", type: "string", required: true, description: "Tag name" }
1762
+ ]);
1763
+ var TAG_COLUMNS = [
1764
+ { key: "name", header: "Name", width: 25 },
1765
+ { key: "tag_fg", header: "FG Color", width: 12 },
1766
+ { key: "tag_bg", header: "BG Color", width: 12 }
1767
+ ];
1768
+ function registerTagCommands(program, getClient) {
1769
+ const tag = program.command("tag").description("Manage tags");
1770
+ tag.command("list").description("List tags in a space").requiredOption("--space-id <id>", "Space ID").action(async (opts) => {
1771
+ const client = getClient();
1772
+ const data = await client.get(`/space/${opts.spaceId}/tag`);
1773
+ formatOutput(data.tags, TAG_COLUMNS, getOutputOptions(program));
1774
+ });
1775
+ tag.command("create").description("Create a tag in a space").requiredOption("--space-id <id>", "Space ID").requiredOption("--name <name>", "Tag name").option("--fg-color <hex>", "Text color (e.g. #FFFFFF)").option("--bg-color <hex>", "Background color (e.g. #8B5CF6)").action(async (opts) => {
1776
+ const client = getClient();
1777
+ const tagObj = { name: opts.name };
1778
+ if (opts.fgColor !== void 0) tagObj["tag_fg"] = opts.fgColor;
1779
+ if (opts.bgColor !== void 0) tagObj["tag_bg"] = opts.bgColor;
1780
+ await client.post(`/space/${opts.spaceId}/tag`, { tag: tagObj });
1781
+ process.stdout.write(`Created tag "${opts.name}"
1782
+ `);
1783
+ });
1784
+ tag.command("update").description("Update a tag in a space").requiredOption("--space-id <id>", "Space ID").requiredOption("--name <name>", "Current tag name").option("--new-name <name>", "New tag name").option("--fg-color <hex>", "New text color").option("--bg-color <hex>", "New background color").action(async (opts) => {
1785
+ const client = getClient();
1786
+ const tagObj = {};
1787
+ if (opts.newName !== void 0) tagObj["name"] = opts.newName;
1788
+ if (opts.fgColor !== void 0) tagObj["tag_fg"] = opts.fgColor;
1789
+ if (opts.bgColor !== void 0) tagObj["tag_bg"] = opts.bgColor;
1790
+ await client.put(`/space/${opts.spaceId}/tag/${encodeURIComponent(opts.name)}`, { tag: tagObj });
1791
+ process.stdout.write(`Updated tag "${opts.name}"
1792
+ `);
1793
+ });
1794
+ tag.command("delete").description("Delete a tag from a space").requiredOption("--space-id <id>", "Space ID").requiredOption("--name <name>", "Tag name").option("--confirm", "Skip confirmation prompt").action(async (opts) => {
1795
+ const client = getClient();
1796
+ if (!opts.confirm) {
1797
+ if (!process.stdin.isTTY) {
1798
+ process.stderr.write("Error: Use --confirm to delete in non-interactive mode.\n");
1799
+ process.exit(2);
1800
+ return;
1801
+ }
1802
+ const { confirm } = await import("@inquirer/prompts");
1803
+ const yes = await confirm({ message: `Delete tag "${opts.name}"?` });
1804
+ if (!yes) {
1805
+ process.stdout.write("Cancelled.\n");
1806
+ return;
1807
+ }
1808
+ }
1809
+ await client.delete(`/space/${opts.spaceId}/tag/${encodeURIComponent(opts.name)}`);
1810
+ process.stdout.write(`Deleted tag "${opts.name}"
1811
+ `);
1812
+ });
1813
+ tag.command("add").description("Add a tag to a task").requiredOption("--task-id <id>", "Task ID").requiredOption("--name <name>", "Tag name").action(async (opts) => {
1814
+ const client = getClient();
1815
+ await client.post(`/task/${opts.taskId}/tag/${encodeURIComponent(opts.name)}`);
1816
+ process.stdout.write(`Added tag "${opts.name}" to task ${opts.taskId}
1817
+ `);
1818
+ });
1819
+ tag.command("remove").description("Remove a tag from a task").requiredOption("--task-id <id>", "Task ID").requiredOption("--name <name>", "Tag name").action(async (opts) => {
1820
+ const client = getClient();
1821
+ await client.delete(`/task/${opts.taskId}/tag/${encodeURIComponent(opts.name)}`);
1822
+ process.stdout.write(`Removed tag "${opts.name}" from task ${opts.taskId}
1823
+ `);
1824
+ });
1825
+ }
1826
+
1827
+ // src/commands/dependency.ts
1828
+ registerSchema("dependency", "add", "Add a dependency between tasks", [
1829
+ { flag: "--task-id", type: "string", required: true, description: "Task ID" },
1830
+ { flag: "--depends-on", type: "string", required: false, description: "This task depends on the given task" },
1831
+ { flag: "--dependency-of", type: "string", required: false, description: "This task is a dependency of the given task" }
1832
+ ]);
1833
+ registerSchema("dependency", "remove", "Remove a dependency between tasks", [
1834
+ { flag: "--task-id", type: "string", required: true, description: "Task ID" },
1835
+ { flag: "--depends-on", type: "string", required: false, description: "Remove depends-on relationship" },
1836
+ { flag: "--dependency-of", type: "string", required: false, description: "Remove dependency-of relationship" }
1837
+ ]);
1838
+ var DEPENDENCY_COLUMNS = [
1839
+ { key: "task_id", header: "Task ID", width: 14 },
1840
+ { key: "depends_on", header: "Depends On", width: 14 },
1841
+ { key: "dependency_of", header: "Dependency Of", width: 14 }
1842
+ ];
1843
+ function registerDependencyCommands(program, getClient) {
1844
+ const dependency = program.command("dependency").description("Manage task dependencies");
1845
+ dependency.command("add").description("Add a dependency between tasks").requiredOption("--task-id <id>", "Task ID").option("--depends-on <id>", "This task depends on the given task").option("--dependency-of <id>", "This task is a dependency of the given task").action(async (opts) => {
1846
+ if (!opts.dependsOn && !opts.dependencyOf) {
1847
+ process.stderr.write("Error: Provide --depends-on or --dependency-of.\n");
1848
+ process.exit(2);
1849
+ return;
1850
+ }
1851
+ const client = getClient();
1852
+ const body = {};
1853
+ if (opts.dependsOn) body["depends_on"] = opts.dependsOn;
1854
+ if (opts.dependencyOf) body["dependency_of"] = opts.dependencyOf;
1855
+ const data = await client.post(`/task/${opts.taskId}/dependency`, body);
1856
+ formatOutput(data, DEPENDENCY_COLUMNS, getOutputOptions(program));
1857
+ });
1858
+ dependency.command("remove").description("Remove a dependency between tasks").requiredOption("--task-id <id>", "Task ID").option("--depends-on <id>", "Remove depends-on relationship").option("--dependency-of <id>", "Remove dependency-of relationship").action(async (opts) => {
1859
+ if (!opts.dependsOn && !opts.dependencyOf) {
1860
+ process.stderr.write("Error: Provide --depends-on or --dependency-of.\n");
1861
+ process.exit(2);
1862
+ return;
1863
+ }
1864
+ const client = getClient();
1865
+ const params = {};
1866
+ if (opts.dependsOn) params["depends_on"] = opts.dependsOn;
1867
+ if (opts.dependencyOf) params["dependency_of"] = opts.dependencyOf;
1868
+ await client.delete(`/task/${opts.taskId}/dependency?${new URLSearchParams(params).toString()}`);
1869
+ process.stdout.write(`Removed dependency from task ${opts.taskId}
1870
+ `);
1871
+ });
1872
+ }
1873
+
1874
+ // src/commands/relation.ts
1875
+ registerSchema("relation", "add", "Add a relation between tasks", [
1876
+ { flag: "--task-id", type: "string", required: true, description: "Task ID" },
1877
+ { flag: "--links-to", type: "string", required: true, description: "Task ID to link to" }
1878
+ ]);
1879
+ registerSchema("relation", "remove", "Remove a relation between tasks", [
1880
+ { flag: "--task-id", type: "string", required: true, description: "Task ID" },
1881
+ { flag: "--links-to", type: "string", required: true, description: "Task ID to unlink from" }
1882
+ ]);
1883
+ var RELATION_COLUMNS = [
1884
+ { key: "task_id", header: "Task ID", width: 14 },
1885
+ { key: "links_to", header: "Links To", width: 14 }
1886
+ ];
1887
+ function registerRelationCommands(program, getClient) {
1888
+ const relation = program.command("relation").description("Manage task relations");
1889
+ relation.command("add").description("Add a relation between tasks").requiredOption("--task-id <id>", "Task ID").requiredOption("--links-to <id>", "Task ID to link to").action(async (opts) => {
1890
+ const client = getClient();
1891
+ const data = await client.post(`/task/${opts.taskId}/link/${opts.linksTo}`);
1892
+ formatOutput(data, RELATION_COLUMNS, getOutputOptions(program));
1893
+ });
1894
+ relation.command("remove").description("Remove a relation between tasks").requiredOption("--task-id <id>", "Task ID").requiredOption("--links-to <id>", "Task ID to unlink from").action(async (opts) => {
1895
+ const client = getClient();
1896
+ await client.delete(`/task/${opts.taskId}/link/${opts.linksTo}`);
1897
+ process.stdout.write(`Removed relation between task ${opts.taskId} and ${opts.linksTo}
1898
+ `);
1899
+ });
1900
+ }
1901
+
1902
+ // src/commands/attachment.ts
1903
+ registerSchema("attachment", "upload", "Upload a file to a task", [
1904
+ { flag: "--task-id", type: "string", required: true, description: "Task ID" },
1905
+ { flag: "--file", type: "string", required: true, description: "Local file path" },
1906
+ { flag: "--filename", type: "string", required: false, description: "Override display filename" }
1907
+ ]);
1908
+ var ATTACHMENT_COLUMNS = [
1909
+ { key: "id", header: "ID", width: 20 },
1910
+ { key: "title", header: "Title", width: 30 },
1911
+ { key: "url", header: "URL", width: 40 },
1912
+ { key: "type", header: "Type", width: 15 },
1913
+ { key: "size", header: "Size", width: 10 }
1914
+ ];
1915
+ function registerAttachmentCommands(program, getClient) {
1916
+ const attachment = program.command("attachment").description("Manage attachments");
1917
+ attachment.command("upload").description("Upload a file to a task").requiredOption("--task-id <id>", "Task ID").requiredOption("--file <path>", "Local file path").option("--filename <name>", "Override display filename").action(async (opts) => {
1918
+ const client = getClient();
1919
+ const data = await client.upload(`/task/${opts.taskId}/attachment`, opts.file, opts.filename);
1920
+ formatOutput(data, ATTACHMENT_COLUMNS, getOutputOptions(program));
1921
+ });
1922
+ }
1923
+
1924
+ // src/commands/comment.ts
1925
+ registerSchema("comment", "list", "List comments on a task, list, or view", [
1926
+ { flag: "--task-id", type: "string", required: false, description: "Task ID (provide one parent)" },
1927
+ { flag: "--list-id", type: "string", required: false, description: "List ID (provide one parent)" },
1928
+ { flag: "--view-id", type: "string", required: false, description: "View ID (provide one parent)" },
1929
+ { flag: "--start", type: "string", required: false, description: "Pagination start timestamp" },
1930
+ { flag: "--start-id", type: "string", required: false, description: "Pagination start comment ID" }
1931
+ ]);
1932
+ registerSchema("comment", "create", "Create a comment on a task, list, or view", [
1933
+ { flag: "--task-id", type: "string", required: false, description: "Task ID (provide one parent)" },
1934
+ { flag: "--list-id", type: "string", required: false, description: "List ID (provide one parent)" },
1935
+ { flag: "--view-id", type: "string", required: false, description: "View ID (provide one parent)" },
1936
+ { flag: "--text", type: "string", required: true, description: "Comment text" },
1937
+ { flag: "--assignee", type: "string", required: false, description: "Assignee user ID" },
1938
+ { flag: "--notify-all", type: "boolean", required: false, description: "Notify all watchers" }
1939
+ ]);
1940
+ registerSchema("comment", "update", "Update a comment", [
1941
+ { flag: "<comment-id>", type: "string", required: true, description: "Comment ID" },
1942
+ { flag: "--text", type: "string", required: true, description: "New comment text" },
1943
+ { flag: "--assignee", type: "string", required: false, description: "Assignee user ID" },
1944
+ { flag: "--resolved", type: "boolean", required: false, description: "Mark as resolved" }
1945
+ ]);
1946
+ registerSchema("comment", "delete", "Delete a comment", [
1947
+ { flag: "<comment-id>", type: "string", required: true, description: "Comment ID" },
1948
+ { flag: "--confirm", type: "boolean", required: false, description: "Skip confirmation prompt" }
1949
+ ]);
1950
+ registerSchema("comment", "list-threaded", "List threaded replies to a comment", [
1951
+ { flag: "<comment-id>", type: "string", required: true, description: "Comment ID" }
1952
+ ]);
1953
+ registerSchema("comment", "reply", "Reply to a comment (threaded)", [
1954
+ { flag: "<comment-id>", type: "string", required: true, description: "Comment ID" },
1955
+ { flag: "--text", type: "string", required: true, description: "Reply text" },
1956
+ { flag: "--assignee", type: "string", required: false, description: "Assignee user ID" },
1957
+ { flag: "--notify-all", type: "boolean", required: false, description: "Notify all watchers" }
1958
+ ]);
1959
+ var COMMENT_COLUMNS = [
1960
+ { key: "id", header: "ID", width: 12 },
1961
+ { key: "comment_text", header: "Text", width: 40 },
1962
+ { key: "user_name", header: "User", width: 15 },
1963
+ { key: "assignee_name", header: "Assignee", width: 15 },
1964
+ { key: "resolved", header: "Resolved", width: 10 },
1965
+ { key: "date", header: "Date", width: 15 }
1966
+ ];
1967
+ function resolveParent(opts) {
1968
+ const parents = [];
1969
+ if (opts.taskId) parents.push({ type: "task", id: opts.taskId });
1970
+ if (opts.listId) parents.push({ type: "list", id: opts.listId });
1971
+ if (opts.viewId) parents.push({ type: "view", id: opts.viewId });
1972
+ if (parents.length === 0) {
1973
+ process.stderr.write("Error: Provide one of --task-id, --list-id, or --view-id.\n");
1974
+ process.exit(2);
1975
+ return void 0;
1976
+ }
1977
+ if (parents.length > 1) {
1978
+ process.stderr.write("Error: Provide only one of --task-id, --list-id, or --view-id.\n");
1979
+ process.exit(2);
1980
+ return void 0;
1981
+ }
1982
+ return parents[0];
1983
+ }
1984
+ function formatComments(comments) {
1985
+ return comments.map((c) => ({
1986
+ ...c,
1987
+ user_name: c["user"]?.["username"] ?? "",
1988
+ assignee_name: c["assignee"]?.["username"] ?? ""
1989
+ }));
1990
+ }
1991
+ function registerCommentCommands(program, getClient) {
1992
+ const comment = program.command("comment").description("Manage comments");
1993
+ comment.command("list").description("List comments on a task, list, or view").option("--task-id <id>", "Task ID").option("--list-id <id>", "List ID").option("--view-id <id>", "View ID").option("--start <ts>", "Pagination: start after this Unix ms timestamp").option("--start-id <id>", "Pagination: start after this comment ID").action(async (opts) => {
1994
+ const parent = resolveParent(opts);
1995
+ if (!parent) return;
1996
+ const client = getClient();
1997
+ const params = {};
1998
+ if (opts.start) params["start"] = opts.start;
1999
+ if (opts.startId) params["start_id"] = opts.startId;
2000
+ const data = await client.get(`/${parent.type}/${parent.id}/comment`, params);
2001
+ formatOutput(formatComments(data.comments), COMMENT_COLUMNS, getOutputOptions(program));
2002
+ });
2003
+ comment.command("create").description("Create a comment on a task, list, or view").option("--task-id <id>", "Task ID").option("--list-id <id>", "List ID").option("--view-id <id>", "View ID").requiredOption("--text <text>", "Comment text").option("--assignee <id>", "Assignee user ID").option("--notify-all", "Notify all watchers").action(async (opts) => {
2004
+ const parent = resolveParent(opts);
2005
+ if (!parent) return;
2006
+ const client = getClient();
2007
+ const body = { comment_text: opts.text };
2008
+ if (opts.assignee !== void 0) body["assignee"] = parseInt(opts.assignee, 10);
2009
+ if (opts.notifyAll) body["notify_all"] = true;
2010
+ const data = await client.post(`/${parent.type}/${parent.id}/comment`, body);
2011
+ process.stdout.write(`Created comment ${data["id"] ?? ""}
2012
+ `);
2013
+ });
2014
+ comment.command("update").description("Update a comment").argument("<comment-id>", "Comment ID").requiredOption("--text <text>", "New comment text").option("--assignee <id>", "Assignee user ID").option("--resolved <bool>", "Mark as resolved (true/false)").action(async (commentId, opts) => {
2015
+ const client = getClient();
2016
+ const body = { comment_text: opts.text };
2017
+ if (opts.assignee !== void 0) body["assignee"] = parseInt(opts.assignee, 10);
2018
+ if (opts.resolved !== void 0) body["resolved"] = opts.resolved === "true";
2019
+ await client.put(`/comment/${commentId}`, body);
2020
+ process.stdout.write(`Updated comment ${commentId}
2021
+ `);
2022
+ });
2023
+ comment.command("delete").description("Delete a comment").argument("<comment-id>", "Comment ID").option("--confirm", "Skip confirmation prompt").action(async (commentId, opts) => {
2024
+ const client = getClient();
2025
+ if (!opts.confirm) {
2026
+ if (!process.stdin.isTTY) {
2027
+ process.stderr.write("Error: Use --confirm to delete in non-interactive mode.\n");
2028
+ process.exit(2);
2029
+ return;
2030
+ }
2031
+ const { confirm } = await import("@inquirer/prompts");
2032
+ const yes = await confirm({ message: `Delete comment ${commentId}?` });
2033
+ if (!yes) {
2034
+ process.stdout.write("Cancelled.\n");
2035
+ return;
2036
+ }
2037
+ }
2038
+ await client.delete(`/comment/${commentId}`);
2039
+ process.stdout.write(`Deleted comment ${commentId}
2040
+ `);
2041
+ });
2042
+ comment.command("list-threaded").description("List threaded replies to a comment").argument("<comment-id>", "Comment ID").action(async (commentId) => {
2043
+ const client = getClient();
2044
+ const data = await client.get(`/comment/${commentId}/thread`);
2045
+ formatOutput(formatComments(data.comments), COMMENT_COLUMNS, getOutputOptions(program));
2046
+ });
2047
+ comment.command("reply").description("Reply to a comment (threaded)").argument("<comment-id>", "Comment ID").requiredOption("--text <text>", "Reply text").option("--assignee <id>", "Assignee user ID").option("--notify-all", "Notify all watchers").action(async (commentId, opts) => {
2048
+ const client = getClient();
2049
+ const body = { comment_text: opts.text };
2050
+ if (opts.assignee !== void 0) body["assignee"] = parseInt(opts.assignee, 10);
2051
+ if (opts.notifyAll) body["notify_all"] = true;
2052
+ const data = await client.post(`/comment/${commentId}/thread`, body);
2053
+ process.stdout.write(`Created reply ${data["id"] ?? ""}
2054
+ `);
2055
+ });
2056
+ }
2057
+
2058
+ // src/commands/time-tracking.ts
2059
+ init_config();
2060
+
2061
+ // src/dates.ts
2062
+ var RELATIVE_OFFSET_RE = /^([+-]\d+)([dwmh])$/;
2063
+ var UNIX_MS_RE = /^\d{13,}$/;
2064
+ var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
2065
+ var ISO_DATETIME_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/;
2066
+ var DAY_NAMES = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
2067
+ function parseDate(input) {
2068
+ const raw = input.trim();
2069
+ const lower = raw.toLowerCase();
2070
+ if (UNIX_MS_RE.test(raw)) {
2071
+ return parseInt(raw, 10);
2072
+ }
2073
+ const now = /* @__PURE__ */ new Date();
2074
+ switch (lower) {
2075
+ case "today": {
2076
+ return startOfDay(now).getTime();
2077
+ }
2078
+ case "tomorrow": {
2079
+ const d = startOfDay(now);
2080
+ d.setDate(d.getDate() + 1);
2081
+ return d.getTime();
2082
+ }
2083
+ case "yesterday": {
2084
+ const d = startOfDay(now);
2085
+ d.setDate(d.getDate() - 1);
2086
+ return d.getTime();
2087
+ }
2088
+ }
2089
+ const offsetMatch = RELATIVE_OFFSET_RE.exec(lower);
2090
+ if (offsetMatch) {
2091
+ const amount = parseInt(offsetMatch[1], 10);
2092
+ const unit = offsetMatch[2];
2093
+ const ms = unitToMs(unit, amount);
2094
+ return now.getTime() + ms;
2095
+ }
2096
+ if (lower.startsWith("next ")) {
2097
+ const dayName = lower.slice(5);
2098
+ const dayIndex = DAY_NAMES.indexOf(dayName);
2099
+ if (dayIndex !== -1) {
2100
+ const today = now.getDay();
2101
+ let daysAhead = dayIndex - today;
2102
+ if (daysAhead <= 0) daysAhead += 7;
2103
+ const target = startOfDay(now);
2104
+ target.setDate(target.getDate() + daysAhead);
2105
+ return target.getTime();
2106
+ }
2107
+ }
2108
+ if (ISO_DATE_RE.test(raw)) {
2109
+ const d = /* @__PURE__ */ new Date(raw + "T00:00:00");
2110
+ if (!isNaN(d.getTime())) return d.getTime();
2111
+ }
2112
+ if (ISO_DATETIME_RE.test(raw)) {
2113
+ const d = new Date(raw);
2114
+ if (!isNaN(d.getTime())) return d.getTime();
2115
+ }
2116
+ throw new Error(`Unable to parse date: "${input}"`);
2117
+ }
2118
+ function startOfDay(date) {
2119
+ const d = new Date(date);
2120
+ d.setHours(0, 0, 0, 0);
2121
+ return d;
2122
+ }
2123
+ function unitToMs(unit, amount) {
2124
+ switch (unit) {
2125
+ case "m":
2126
+ return amount * 60 * 1e3;
2127
+ case "h":
2128
+ return amount * 60 * 60 * 1e3;
2129
+ case "d":
2130
+ return amount * 24 * 60 * 60 * 1e3;
2131
+ case "w":
2132
+ return amount * 7 * 24 * 60 * 60 * 1e3;
2133
+ default:
2134
+ return 0;
2135
+ }
2136
+ }
2137
+
2138
+ // src/commands/time-tracking.ts
2139
+ registerSchema("time", "list", "List time entries for a task or workspace", [
2140
+ { flag: "--task-id", type: "string", required: false, description: "Task ID (or use --workspace-id for workspace-wide)" },
2141
+ { flag: "--workspace-id", type: "string", required: false, description: "Workspace ID" },
2142
+ { flag: "--start", type: "string", required: false, description: "Start date" },
2143
+ { flag: "--end", type: "string", required: false, description: "End date" },
2144
+ { flag: "--assignee", type: "string[]", required: false, description: "Assignee user ID (repeatable)" }
2145
+ ]);
2146
+ registerSchema("time", "get", "Get a specific time entry", [
2147
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
2148
+ { flag: "<timer-id>", type: "string", required: true, description: "Time entry ID" }
2149
+ ]);
2150
+ registerSchema("time", "create", "Create a time entry on a task", [
2151
+ { flag: "--task-id", type: "string", required: true, description: "Task ID" },
2152
+ { flag: "--duration", type: "string", required: true, description: "Duration in milliseconds" },
2153
+ { flag: "--start", type: "string", required: true, description: "Start time (Unix ms or date string)" },
2154
+ { flag: "--description", type: "string", required: false, description: "Description" },
2155
+ { flag: "--billable", type: "boolean", required: false, description: "Billable" },
2156
+ { flag: "--tag", type: "string[]", required: false, description: "Tag name (repeatable)" }
2157
+ ]);
2158
+ registerSchema("time", "update", "Update a time entry", [
2159
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
2160
+ { flag: "<timer-id>", type: "string", required: true, description: "Time entry ID" },
2161
+ { flag: "--description", type: "string", required: false, description: "Description" },
2162
+ { flag: "--duration", type: "string", required: false, description: "Duration in ms" },
2163
+ { flag: "--start", type: "string", required: false, description: "Start time" },
2164
+ { flag: "--billable", type: "boolean", required: false, description: "Billable" },
2165
+ { flag: "--tag", type: "string[]", required: false, description: "Tag name (repeatable)" }
2166
+ ]);
2167
+ registerSchema("time", "delete", "Delete a time entry", [
2168
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
2169
+ { flag: "<timer-id>", type: "string", required: true, description: "Time entry ID" }
2170
+ ]);
2171
+ registerSchema("time", "running", "Get current running timer", [
2172
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
2173
+ { flag: "--assignee", type: "string", required: false, description: "Assignee user ID" }
2174
+ ]);
2175
+ registerSchema("time", "start", "Start a running timer", [
2176
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
2177
+ { flag: "--task-id", type: "string", required: true, description: "Task ID" },
2178
+ { flag: "--description", type: "string", required: false, description: "Description" },
2179
+ { flag: "--billable", type: "boolean", required: false, description: "Billable" }
2180
+ ]);
2181
+ registerSchema("time", "stop", "Stop the running timer", [
2182
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" }
2183
+ ]);
2184
+ registerSchema("time", "tags", "List time tracking tags", [
2185
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" }
2186
+ ]);
2187
+ var TIME_ENTRY_COLUMNS = [
2188
+ { key: "id", header: "ID", width: 12 },
2189
+ { key: "task_id", header: "Task", width: 12 },
2190
+ { key: "duration", header: "Duration", width: 12 },
2191
+ { key: "start", header: "Start", width: 18 },
2192
+ { key: "end", header: "End", width: 18 },
2193
+ { key: "description", header: "Description", width: 25 },
2194
+ { key: "billable", header: "Billable", width: 10 },
2195
+ { key: "user_name", header: "User", width: 15 }
2196
+ ];
2197
+ var TIME_TAG_COLUMNS = [
2198
+ { key: "name", header: "Name", width: 25 },
2199
+ { key: "creator", header: "Creator", width: 12 },
2200
+ { key: "status", header: "Status", width: 12 }
2201
+ ];
2202
+ var TIME_HISTORY_COLUMNS = [
2203
+ { key: "id", header: "ID", width: 12 },
2204
+ { key: "field", header: "Field", width: 15 },
2205
+ { key: "before", header: "Before", width: 20 },
2206
+ { key: "after", header: "After", width: 20 },
2207
+ { key: "date", header: "Date", width: 18 }
2208
+ ];
2209
+ function collect2(value, previous) {
2210
+ return previous.concat([value]);
2211
+ }
2212
+ function requireWorkspaceId4(program) {
2213
+ const globalOpts = program.opts();
2214
+ const workspaceId = resolveWorkspaceId(globalOpts["workspaceId"]);
2215
+ if (!workspaceId) {
2216
+ process.stderr.write("Error: No workspace ID. Use --workspace-id or run: clickup config set workspace_id <id>\n");
2217
+ process.exit(2);
2218
+ return void 0;
2219
+ }
2220
+ return workspaceId;
2221
+ }
2222
+ function formatEntries(entries) {
2223
+ return entries.map((e) => ({
2224
+ ...e,
2225
+ task_id: e["task"]?.["id"] ?? "",
2226
+ user_name: e["user"]?.["username"] ?? ""
2227
+ }));
2228
+ }
2229
+ function registerTimeTrackingCommands(program, getClient) {
2230
+ const time = program.command("time").description("Manage time tracking");
2231
+ time.command("list").description("List time entries (by task or workspace-wide)").option("--task-id <id>", "Task ID").option("--start <date>", "Start date (ISO 8601, relative, or Unix ms)").option("--end <date>", "End date").option("--assignee <id>", "Assignee user ID (repeatable)", collect2, []).action(async (opts) => {
2232
+ const client = getClient();
2233
+ if (opts.taskId) {
2234
+ const data = await client.get(`/task/${opts.taskId}/time`);
2235
+ formatOutput(formatEntries(data.data), TIME_ENTRY_COLUMNS, getOutputOptions(program));
2236
+ } else {
2237
+ const workspaceId = requireWorkspaceId4(program);
2238
+ if (!workspaceId) return;
2239
+ const params = {};
2240
+ if (opts.start) params["start_date"] = String(parseDate(opts.start));
2241
+ if (opts.end) params["end_date"] = String(parseDate(opts.end));
2242
+ if (opts.assignee.length) params["assignee"] = opts.assignee;
2243
+ const data = await client.get(`/team/${workspaceId}/time_entries`, params);
2244
+ formatOutput(formatEntries(data.data), TIME_ENTRY_COLUMNS, getOutputOptions(program));
2245
+ }
2246
+ });
2247
+ time.command("get").description("Get a specific time entry").argument("<timer-id>", "Time entry ID").action(async (timerId) => {
2248
+ const workspaceId = requireWorkspaceId4(program);
2249
+ if (!workspaceId) return;
2250
+ const client = getClient();
2251
+ const data = await client.get(`/team/${workspaceId}/time_entries/${timerId}`);
2252
+ formatOutput(formatEntries([data.data]), TIME_ENTRY_COLUMNS, getOutputOptions(program));
2253
+ });
2254
+ time.command("create").description("Create a time entry on a task").requiredOption("--task-id <id>", "Task ID").requiredOption("--duration <ms>", "Duration in milliseconds").requiredOption("--start <ts>", "Start time (Unix ms or date string)").option("--description <desc>", "Description").option("--assignee <id>", "Assignee user ID").option("--billable <bool>", "Billable (true/false)").option("--tag <name>", "Tag name (repeatable)", collect2, []).action(async (opts) => {
2255
+ const client = getClient();
2256
+ const body = {
2257
+ duration: parseInt(opts.duration, 10),
2258
+ start: String(parseDate(opts.start))
2259
+ };
2260
+ if (opts.description !== void 0) body["description"] = opts.description;
2261
+ if (opts.assignee !== void 0) body["assignee"] = parseInt(opts.assignee, 10);
2262
+ if (opts.billable !== void 0) body["billable"] = opts.billable === "true";
2263
+ if (opts.tag.length) body["tags"] = opts.tag.map((t) => ({ name: t }));
2264
+ const data = await client.post(`/task/${opts.taskId}/time`, body);
2265
+ process.stdout.write(`Created time entry ${data.data?.id ?? ""}
2266
+ `);
2267
+ });
2268
+ time.command("update").description("Update a time entry").argument("<timer-id>", "Time entry ID").option("--description <desc>", "Description").option("--duration <ms>", "Duration in milliseconds").option("--start <ts>", "Start time (Unix ms or date string)").option("--tag-action <action>", "Tag action (add or remove)").option("--tag <name>", "Tag name (repeatable)", collect2, []).option("--billable <bool>", "Billable (true/false)").action(async (timerId, opts) => {
2269
+ const workspaceId = requireWorkspaceId4(program);
2270
+ if (!workspaceId) return;
2271
+ const client = getClient();
2272
+ const body = {};
2273
+ if (opts.description !== void 0) body["description"] = opts.description;
2274
+ if (opts.duration !== void 0) body["duration"] = parseInt(opts.duration, 10);
2275
+ if (opts.start !== void 0) body["start"] = String(parseDate(opts.start));
2276
+ if (opts.billable !== void 0) body["billable"] = opts.billable === "true";
2277
+ if (opts.tag.length) {
2278
+ body["tags"] = opts.tag.map((t) => ({ name: t }));
2279
+ if (opts.tagAction) body["tag_action"] = opts.tagAction;
2280
+ }
2281
+ await client.put(`/team/${workspaceId}/time_entries/${timerId}`, body);
2282
+ process.stdout.write(`Updated time entry ${timerId}
2283
+ `);
2284
+ });
2285
+ time.command("delete").description("Delete a time entry").argument("<timer-id>", "Time entry ID").action(async (timerId) => {
2286
+ const workspaceId = requireWorkspaceId4(program);
2287
+ if (!workspaceId) return;
2288
+ const client = getClient();
2289
+ await client.delete(`/team/${workspaceId}/time_entries/${timerId}`);
2290
+ process.stdout.write(`Deleted time entry ${timerId}
2291
+ `);
2292
+ });
2293
+ time.command("history").description("Get time entry change history").argument("<timer-id>", "Time entry ID").action(async (timerId) => {
2294
+ const workspaceId = requireWorkspaceId4(program);
2295
+ if (!workspaceId) return;
2296
+ const client = getClient();
2297
+ const data = await client.get(`/team/${workspaceId}/time_entries/${timerId}/history`);
2298
+ formatOutput(data.data, TIME_HISTORY_COLUMNS, getOutputOptions(program));
2299
+ });
2300
+ time.command("running").description("Get current running timer").option("--assignee <id>", "Assignee user ID").action(async (opts) => {
2301
+ const workspaceId = requireWorkspaceId4(program);
2302
+ if (!workspaceId) return;
2303
+ const client = getClient();
2304
+ const params = {};
2305
+ if (opts.assignee) params["assignee"] = opts.assignee;
2306
+ const data = await client.get(`/team/${workspaceId}/time_entries/current`, params);
2307
+ if (data.data) {
2308
+ formatOutput(formatEntries([data.data]), TIME_ENTRY_COLUMNS, getOutputOptions(program));
2309
+ } else {
2310
+ process.stdout.write("No running timer.\n");
2311
+ }
2312
+ });
2313
+ time.command("start").description("Start a running timer").requiredOption("--task-id <id>", "Task ID").option("--description <desc>", "Description").option("--billable <bool>", "Billable (true/false)").option("--tag <name>", "Tag name (repeatable)", collect2, []).action(async (opts) => {
2314
+ const workspaceId = requireWorkspaceId4(program);
2315
+ if (!workspaceId) return;
2316
+ const client = getClient();
2317
+ const body = { tid: opts.taskId };
2318
+ if (opts.description !== void 0) body["description"] = opts.description;
2319
+ if (opts.billable !== void 0) body["billable"] = opts.billable === "true";
2320
+ if (opts.tag.length) body["tags"] = opts.tag.map((t) => ({ name: t }));
2321
+ const data = await client.post(`/team/${workspaceId}/time_entries/start`, body);
2322
+ process.stdout.write(`Started timer ${data.data?.id ?? ""}
2323
+ `);
2324
+ });
2325
+ time.command("stop").description("Stop the running timer").action(async () => {
2326
+ const workspaceId = requireWorkspaceId4(program);
2327
+ if (!workspaceId) return;
2328
+ const client = getClient();
2329
+ const data = await client.post(`/team/${workspaceId}/time_entries/stop`);
2330
+ process.stdout.write(`Stopped timer ${data.data?.id ?? ""}
2331
+ `);
2332
+ });
2333
+ time.command("tags").description("List time tracking tags").action(async () => {
2334
+ const workspaceId = requireWorkspaceId4(program);
2335
+ if (!workspaceId) return;
2336
+ const client = getClient();
2337
+ const data = await client.get(`/team/${workspaceId}/time_entries/tags`);
2338
+ formatOutput(data.data, TIME_TAG_COLUMNS, getOutputOptions(program));
2339
+ });
2340
+ time.command("add-tags").description("Add tags to time entries").option("--timer-id <id>", "Time entry ID (repeatable)", collect2, []).option("--tag <name>", "Tag name (repeatable)", collect2, []).action(async (opts) => {
2341
+ const workspaceId = requireWorkspaceId4(program);
2342
+ if (!workspaceId) return;
2343
+ const client = getClient();
2344
+ if (!opts.timerId.length || !opts.tag.length) {
2345
+ process.stderr.write("Error: --timer-id and --tag are required.\n");
2346
+ process.exit(2);
2347
+ return;
2348
+ }
2349
+ await client.post(`/team/${workspaceId}/time_entries/tags`, {
2350
+ time_entry_ids: opts.timerId,
2351
+ tags: opts.tag.map((t) => ({ name: t }))
2352
+ });
2353
+ process.stdout.write(`Added tags to ${opts.timerId.length} time entries.
2354
+ `);
2355
+ });
2356
+ time.command("remove-tags").description("Remove tags from time entries").option("--timer-id <id>", "Time entry ID (repeatable)", collect2, []).option("--tag <name>", "Tag name (repeatable)", collect2, []).action(async (opts) => {
2357
+ const workspaceId = requireWorkspaceId4(program);
2358
+ if (!workspaceId) return;
2359
+ const client = getClient();
2360
+ if (!opts.timerId.length || !opts.tag.length) {
2361
+ process.stderr.write("Error: --timer-id and --tag are required.\n");
2362
+ process.exit(2);
2363
+ return;
2364
+ }
2365
+ await client.delete(`/team/${workspaceId}/time_entries/tags`, {
2366
+ time_entry_ids: opts.timerId,
2367
+ tags: opts.tag.map((t) => ({ name: t }))
2368
+ });
2369
+ process.stdout.write(`Removed tags from ${opts.timerId.length} time entries.
2370
+ `);
2371
+ });
2372
+ time.command("rename-tag").description("Rename a time tracking tag").requiredOption("--name <name>", "Current tag name").requiredOption("--new-name <name>", "New tag name").action(async (opts) => {
2373
+ const workspaceId = requireWorkspaceId4(program);
2374
+ if (!workspaceId) return;
2375
+ const client = getClient();
2376
+ await client.put(`/team/${workspaceId}/time_entries/tags`, {
2377
+ name: opts.name,
2378
+ new_name: opts.newName
2379
+ });
2380
+ process.stdout.write(`Renamed tag "${opts.name}" to "${opts.newName}"
2381
+ `);
2382
+ });
2383
+ }
2384
+
2385
+ // src/commands/goal.ts
2386
+ init_config();
2387
+ registerSchema("goal", "list", "List goals in a workspace", [
2388
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
2389
+ { flag: "--include-completed", type: "boolean", required: false, description: "Include completed goals" }
2390
+ ]);
2391
+ registerSchema("goal", "get", "Get a goal", [
2392
+ { flag: "<goal-id>", type: "string", required: true, description: "Goal ID" }
2393
+ ]);
2394
+ registerSchema("goal", "create", "Create a goal", [
2395
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
2396
+ { flag: "--name", type: "string", required: true, description: "Goal name" },
2397
+ { flag: "--due-date", type: "string", required: false, description: "Due date (Unix ms)" },
2398
+ { flag: "--description", type: "string", required: false, description: "Description" },
2399
+ { flag: "--multiple-owners", type: "boolean", required: false, description: "Allow multiple owners" },
2400
+ { flag: "--owner", type: "string[]", required: false, description: "Owner user ID (repeatable)" },
2401
+ { flag: "--color", type: "string", required: false, description: "Color hex code" }
2402
+ ]);
2403
+ registerSchema("goal", "update", "Update a goal", [
2404
+ { flag: "<goal-id>", type: "string", required: true, description: "Goal ID" },
2405
+ { flag: "--name", type: "string", required: false, description: "Goal name" },
2406
+ { flag: "--due-date", type: "string", required: false, description: "Due date (Unix ms)" },
2407
+ { flag: "--description", type: "string", required: false, description: "Description" },
2408
+ { flag: "--color", type: "string", required: false, description: "Color hex code" }
2409
+ ]);
2410
+ registerSchema("goal", "delete", "Delete a goal", [
2411
+ { flag: "<goal-id>", type: "string", required: true, description: "Goal ID" },
2412
+ { flag: "--confirm", type: "boolean", required: false, description: "Skip confirmation prompt" }
2413
+ ]);
2414
+ registerSchema("goal", "add-key-result", "Add a key result to a goal", [
2415
+ { flag: "<goal-id>", type: "string", required: true, description: "Goal ID" },
2416
+ { flag: "--name", type: "string", required: true, description: "Key result name" },
2417
+ { flag: "--type", type: "string", required: true, description: "Type (number, currency, boolean, percentage, automatic)" },
2418
+ { flag: "--steps-start", type: "string", required: false, description: "Starting value" },
2419
+ { flag: "--steps-end", type: "string", required: false, description: "Target value" },
2420
+ { flag: "--unit", type: "string", required: false, description: "Unit label" }
2421
+ ]);
2422
+ registerSchema("goal", "update-key-result", "Update a key result", [
2423
+ { flag: "<key-result-id>", type: "string", required: true, description: "Key result ID" },
2424
+ { flag: "--name", type: "string", required: false, description: "Key result name" },
2425
+ { flag: "--steps-current", type: "string", required: false, description: "Current value" },
2426
+ { flag: "--note", type: "string", required: false, description: "Progress note" }
2427
+ ]);
2428
+ registerSchema("goal", "delete-key-result", "Delete a key result", [
2429
+ { flag: "<key-result-id>", type: "string", required: true, description: "Key result ID" },
2430
+ { flag: "--confirm", type: "boolean", required: false, description: "Skip confirmation prompt" }
2431
+ ]);
2432
+ var GOAL_COLUMNS = [
2433
+ { key: "id", header: "ID", width: 20 },
2434
+ { key: "name", header: "Name", width: 30 },
2435
+ { key: "description", header: "Description", width: 25 },
2436
+ { key: "due_date", header: "Due Date", width: 15 },
2437
+ { key: "color", header: "Color", width: 10 },
2438
+ { key: "percent_completed", header: "% Done", width: 8 },
2439
+ { key: "owner_names", header: "Owners", width: 20 }
2440
+ ];
2441
+ function collect3(value, previous) {
2442
+ return previous.concat([value]);
2443
+ }
2444
+ function requireWorkspaceId5(program) {
2445
+ const globalOpts = program.opts();
2446
+ const workspaceId = resolveWorkspaceId(globalOpts["workspaceId"]);
2447
+ if (!workspaceId) {
2448
+ process.stderr.write("Error: No workspace ID. Use --workspace-id or run: clickup config set workspace_id <id>\n");
2449
+ process.exit(2);
2450
+ return void 0;
2451
+ }
2452
+ return workspaceId;
2453
+ }
2454
+ function formatGoals(goals) {
2455
+ return goals.map((g) => ({
2456
+ ...g,
2457
+ owner_names: Array.isArray(g["owners"]) ? g["owners"].map((o) => o["username"] ?? "").join(", ") : ""
2458
+ }));
2459
+ }
2460
+ function registerGoalCommands(program, getClient) {
2461
+ const goal = program.command("goal").description("Manage goals");
2462
+ goal.command("list").description("List goals").option("--include-completed", "Include completed goals").action(async (opts) => {
2463
+ const workspaceId = requireWorkspaceId5(program);
2464
+ if (!workspaceId) return;
2465
+ const client = getClient();
2466
+ const params = {};
2467
+ if (opts.includeCompleted) params["include_completed"] = "true";
2468
+ const data = await client.get(`/team/${workspaceId}/goal`, params);
2469
+ formatOutput(formatGoals(data.goals), GOAL_COLUMNS, getOutputOptions(program));
2470
+ });
2471
+ goal.command("get").description("Get a goal").argument("<goal-id>", "Goal ID").action(async (goalId) => {
2472
+ const client = getClient();
2473
+ const data = await client.get(`/goal/${goalId}`);
2474
+ formatOutput(formatGoals([data.goal]), GOAL_COLUMNS, getOutputOptions(program));
2475
+ });
2476
+ goal.command("create").description("Create a goal").requiredOption("--name <name>", "Goal name").option("--due-date <date>", "Due date (Unix ms)").option("--description <desc>", "Description").option("--multiple-owners", "Allow multiple owners").option("--owner <id>", "Owner user ID (repeatable)", collect3, []).option("--color <color>", "Color hex code").action(async (opts) => {
2477
+ const workspaceId = requireWorkspaceId5(program);
2478
+ if (!workspaceId) return;
2479
+ const client = getClient();
2480
+ const body = { name: opts.name };
2481
+ if (opts.dueDate !== void 0) body["due_date"] = opts.dueDate;
2482
+ if (opts.description !== void 0) body["description"] = opts.description;
2483
+ if (opts.multipleOwners) body["multiple_owners"] = true;
2484
+ if (opts.owner.length) body["owners"] = opts.owner.map((id) => parseInt(id, 10));
2485
+ if (opts.color !== void 0) body["color"] = opts.color;
2486
+ const data = await client.post(`/team/${workspaceId}/goal`, body);
2487
+ process.stdout.write(`Created goal ${data.goal?.id ?? ""}
2488
+ `);
2489
+ });
2490
+ goal.command("update").description("Update a goal").argument("<goal-id>", "Goal ID").option("--name <name>", "Goal name").option("--due-date <date>", "Due date (Unix ms)").option("--description <desc>", "Description").option("--color <color>", "Color hex code").action(async (goalId, opts) => {
2491
+ const client = getClient();
2492
+ const body = {};
2493
+ if (opts.name !== void 0) body["name"] = opts.name;
2494
+ if (opts.dueDate !== void 0) body["due_date"] = opts.dueDate;
2495
+ if (opts.description !== void 0) body["description"] = opts.description;
2496
+ if (opts.color !== void 0) body["color"] = opts.color;
2497
+ await client.put(`/goal/${goalId}`, body);
2498
+ process.stdout.write(`Updated goal ${goalId}
2499
+ `);
2500
+ });
2501
+ goal.command("delete").description("Delete a goal").argument("<goal-id>", "Goal ID").option("--confirm", "Skip confirmation prompt").action(async (goalId, opts) => {
2502
+ const client = getClient();
2503
+ if (!opts.confirm) {
2504
+ if (!process.stdin.isTTY) {
2505
+ process.stderr.write("Error: Use --confirm to delete in non-interactive mode.\n");
2506
+ process.exit(2);
2507
+ return;
2508
+ }
2509
+ const { confirm } = await import("@inquirer/prompts");
2510
+ const yes = await confirm({ message: `Delete goal ${goalId}?` });
2511
+ if (!yes) {
2512
+ process.stdout.write("Cancelled.\n");
2513
+ return;
2514
+ }
2515
+ }
2516
+ await client.delete(`/goal/${goalId}`);
2517
+ process.stdout.write(`Deleted goal ${goalId}
2518
+ `);
2519
+ });
2520
+ goal.command("add-key-result").description("Add a key result to a goal").argument("<goal-id>", "Goal ID").requiredOption("--name <name>", "Key result name").requiredOption("--type <type>", "Type (number, currency, boolean, percentage, automatic)").option("--steps-start <n>", "Starting value").option("--steps-end <n>", "Target value").option("--unit <unit>", "Unit label").option("--task-ids <id>", "Task ID (repeatable, for automatic type)", collect3, []).option("--list-ids <id>", "List ID (repeatable, for automatic type)", collect3, []).action(async (goalId, opts) => {
2521
+ const client = getClient();
2522
+ const body = { name: opts.name, type: opts.type };
2523
+ if (opts.stepsStart !== void 0) body["steps_start"] = parseFloat(opts.stepsStart);
2524
+ if (opts.stepsEnd !== void 0) body["steps_end"] = parseFloat(opts.stepsEnd);
2525
+ if (opts.unit !== void 0) body["unit"] = opts.unit;
2526
+ if (opts.taskIds.length) body["task_ids"] = opts.taskIds;
2527
+ if (opts.listIds.length) body["list_ids"] = opts.listIds;
2528
+ const data = await client.post(`/goal/${goalId}/key_result`, body);
2529
+ process.stdout.write(`Created key result ${data.key_result?.id ?? ""}
2530
+ `);
2531
+ });
2532
+ goal.command("update-key-result").description("Update a key result").argument("<key-result-id>", "Key result ID").option("--name <name>", "Key result name").option("--steps-current <n>", "Current value").option("--note <note>", "Progress note").action(async (keyResultId, opts) => {
2533
+ const client = getClient();
2534
+ const body = {};
2535
+ if (opts.name !== void 0) body["name"] = opts.name;
2536
+ if (opts.stepsCurrent !== void 0) body["steps_current"] = parseFloat(opts.stepsCurrent);
2537
+ if (opts.note !== void 0) body["note"] = opts.note;
2538
+ await client.put(`/key_result/${keyResultId}`, body);
2539
+ process.stdout.write(`Updated key result ${keyResultId}
2540
+ `);
2541
+ });
2542
+ goal.command("delete-key-result").description("Delete a key result").argument("<key-result-id>", "Key result ID").option("--confirm", "Skip confirmation prompt").action(async (keyResultId, opts) => {
2543
+ const client = getClient();
2544
+ if (!opts.confirm) {
2545
+ if (!process.stdin.isTTY) {
2546
+ process.stderr.write("Error: Use --confirm to delete in non-interactive mode.\n");
2547
+ process.exit(2);
2548
+ return;
2549
+ }
2550
+ const { confirm } = await import("@inquirer/prompts");
2551
+ const yes = await confirm({ message: `Delete key result ${keyResultId}?` });
2552
+ if (!yes) {
2553
+ process.stdout.write("Cancelled.\n");
2554
+ return;
2555
+ }
2556
+ }
2557
+ await client.delete(`/key_result/${keyResultId}`);
2558
+ process.stdout.write(`Deleted key result ${keyResultId}
2559
+ `);
2560
+ });
2561
+ }
2562
+
2563
+ // src/commands/view.ts
2564
+ registerSchema("view", "list", "List views for a workspace, space, folder, or list", [
2565
+ { flag: "--workspace-id", type: "string", required: false, description: "Workspace ID (provide one parent)" },
2566
+ { flag: "--space-id", type: "string", required: false, description: "Space ID (provide one parent)" },
2567
+ { flag: "--folder-id", type: "string", required: false, description: "Folder ID (provide one parent)" },
2568
+ { flag: "--list-id", type: "string", required: false, description: "List ID (provide one parent)" }
2569
+ ]);
2570
+ registerSchema("view", "get", "Get a view", [
2571
+ { flag: "<view-id>", type: "string", required: true, description: "View ID" }
2572
+ ]);
2573
+ registerSchema("view", "create", "Create a view", [
2574
+ { flag: "--space-id", type: "string", required: false, description: "Space ID (provide one parent)" },
2575
+ { flag: "--folder-id", type: "string", required: false, description: "Folder ID (provide one parent)" },
2576
+ { flag: "--list-id", type: "string", required: false, description: "List ID (provide one parent)" },
2577
+ { flag: "--name", type: "string", required: true, description: "View name" },
2578
+ { flag: "--type", type: "string", required: true, description: "View type (list, board, calendar, etc.)" }
2579
+ ]);
2580
+ registerSchema("view", "update", "Update a view", [
2581
+ { flag: "<view-id>", type: "string", required: true, description: "View ID" },
2582
+ { flag: "--name", type: "string", required: false, description: "View name" },
2583
+ { flag: "--settings", type: "string", required: false, description: "Settings (JSON string)" },
2584
+ { flag: "--grouping", type: "string", required: false, description: "Grouping config (JSON string)" },
2585
+ { flag: "--sorting", type: "string", required: false, description: "Sorting config (JSON string)" },
2586
+ { flag: "--filters", type: "string", required: false, description: "Filters config (JSON string)" }
2587
+ ]);
2588
+ registerSchema("view", "delete", "Delete a view", [
2589
+ { flag: "<view-id>", type: "string", required: true, description: "View ID" },
2590
+ { flag: "--confirm", type: "boolean", required: false, description: "Skip confirmation prompt" }
2591
+ ]);
2592
+ registerSchema("view", "tasks", "Get tasks in a view", [
2593
+ { flag: "<view-id>", type: "string", required: true, description: "View ID" },
2594
+ { flag: "--page", type: "string", required: false, description: "Page number (zero-indexed)" }
2595
+ ]);
2596
+ var VIEW_COLUMNS = [
2597
+ { key: "id", header: "ID", width: 20 },
2598
+ { key: "name", header: "Name", width: 30 },
2599
+ { key: "type", header: "Type", width: 12 },
2600
+ { key: "date_created", header: "Created", width: 15 },
2601
+ { key: "creator_name", header: "Creator", width: 15 }
2602
+ ];
2603
+ var VIEW_TASK_COLUMNS = [
2604
+ { key: "id", header: "ID", width: 12 },
2605
+ { key: "name", header: "Name", width: 35 },
2606
+ { key: "status_name", header: "Status", width: 15 },
2607
+ { key: "assignee_names", header: "Assignees", width: 20 }
2608
+ ];
2609
+ function resolveViewParent(opts, program) {
2610
+ const parents = [];
2611
+ const globalWsId = program.opts()["workspaceId"];
2612
+ if (globalWsId) parents.push({ segment: "team", id: globalWsId });
2613
+ if (opts.spaceId) parents.push({ segment: "space", id: opts.spaceId });
2614
+ if (opts.folderId) parents.push({ segment: "folder", id: opts.folderId });
2615
+ if (opts.listId) parents.push({ segment: "list", id: opts.listId });
2616
+ if (parents.length === 0) {
2617
+ process.stderr.write("Error: Provide one of --workspace-id, --space-id, --folder-id, or --list-id.\n");
2618
+ process.exit(2);
2619
+ return void 0;
2620
+ }
2621
+ if (parents.length > 1) {
2622
+ process.stderr.write("Error: Provide only one of --workspace-id, --space-id, --folder-id, or --list-id.\n");
2623
+ process.exit(2);
2624
+ return void 0;
2625
+ }
2626
+ return parents[0];
2627
+ }
2628
+ function formatViews(views) {
2629
+ return views.map((v) => ({
2630
+ ...v,
2631
+ creator_name: v["creator"]?.["username"] ?? ""
2632
+ }));
2633
+ }
2634
+ function formatViewTasks(tasks) {
2635
+ return tasks.map((t) => ({
2636
+ ...t,
2637
+ status_name: t["status"]?.["status"] ?? "",
2638
+ assignee_names: Array.isArray(t["assignees"]) ? t["assignees"].map((a) => a["username"] ?? "").join(", ") : ""
2639
+ }));
2640
+ }
2641
+ function registerViewCommands(program, getClient) {
2642
+ const view = program.command("view").description("Manage views");
2643
+ view.command("list").description("List views for a workspace, space, folder, or list").option("--space-id <id>", "Space ID").option("--folder-id <id>", "Folder ID").option("--list-id <id>", "List ID").action(async (opts) => {
2644
+ const parent = resolveViewParent(opts, program);
2645
+ if (!parent) return;
2646
+ const client = getClient();
2647
+ const data = await client.get(`/${parent.segment}/${parent.id}/view`);
2648
+ formatOutput(formatViews(data.views), VIEW_COLUMNS, getOutputOptions(program));
2649
+ });
2650
+ view.command("get").description("Get a view").argument("<view-id>", "View ID").action(async (viewId) => {
2651
+ const client = getClient();
2652
+ const data = await client.get(`/view/${viewId}`);
2653
+ formatOutput(formatViews([data.view]), VIEW_COLUMNS, getOutputOptions(program));
2654
+ });
2655
+ view.command("create").description("Create a view").option("--space-id <id>", "Space ID").option("--folder-id <id>", "Folder ID").option("--list-id <id>", "List ID").requiredOption("--name <name>", "View name").requiredOption("--type <type>", "View type (list, board, calendar, gantt, table, timeline, activity, map, workload)").action(async (opts) => {
2656
+ const parent = resolveViewParent(opts, program);
2657
+ if (!parent) return;
2658
+ const client = getClient();
2659
+ const body = { name: opts.name, type: opts.type };
2660
+ const data = await client.post(`/${parent.segment}/${parent.id}/view`, body);
2661
+ process.stdout.write(`Created view ${data.view?.id ?? ""}
2662
+ `);
2663
+ });
2664
+ view.command("update").description("Update a view").argument("<view-id>", "View ID").option("--name <name>", "View name").option("--settings <json>", "Settings (JSON string)").option("--grouping <json>", "Grouping config (JSON string)").option("--sorting <json>", "Sorting config (JSON string)").option("--filters <json>", "Filters config (JSON string)").action(async (viewId, opts) => {
2665
+ const client = getClient();
2666
+ const body = {};
2667
+ if (opts.name !== void 0) body["name"] = opts.name;
2668
+ for (const field of ["settings", "grouping", "sorting", "filters"]) {
2669
+ if (opts[field] !== void 0) {
2670
+ try {
2671
+ body[field] = JSON.parse(opts[field]);
2672
+ } catch {
2673
+ process.stderr.write(`Error: Invalid JSON for --${field}.
2674
+ `);
2675
+ process.exit(2);
2676
+ return;
2677
+ }
2678
+ }
2679
+ }
2680
+ await client.put(`/view/${viewId}`, body);
2681
+ process.stdout.write(`Updated view ${viewId}
2682
+ `);
2683
+ });
2684
+ view.command("delete").description("Delete a view").argument("<view-id>", "View ID").option("--confirm", "Skip confirmation prompt").action(async (viewId, opts) => {
2685
+ const client = getClient();
2686
+ if (!opts.confirm) {
2687
+ if (!process.stdin.isTTY) {
2688
+ process.stderr.write("Error: Use --confirm to delete in non-interactive mode.\n");
2689
+ process.exit(2);
2690
+ return;
2691
+ }
2692
+ const { confirm } = await import("@inquirer/prompts");
2693
+ const yes = await confirm({ message: `Delete view ${viewId}?` });
2694
+ if (!yes) {
2695
+ process.stdout.write("Cancelled.\n");
2696
+ return;
2697
+ }
2698
+ }
2699
+ await client.delete(`/view/${viewId}`);
2700
+ process.stdout.write(`Deleted view ${viewId}
2701
+ `);
2702
+ });
2703
+ view.command("tasks").description("Get tasks in a view").argument("<view-id>", "View ID").option("--page <n>", "Page number (zero-indexed)").action(async (viewId, opts) => {
2704
+ const client = getClient();
2705
+ const params = {};
2706
+ if (opts.page !== void 0) params["page"] = opts.page;
2707
+ const data = await client.get(`/view/${viewId}/task`, params);
2708
+ formatOutput(formatViewTasks(data.tasks), VIEW_TASK_COLUMNS, getOutputOptions(program));
2709
+ });
2710
+ }
2711
+
2712
+ // src/commands/webhook.ts
2713
+ init_config();
2714
+ registerSchema("webhook", "list", "List webhooks in a workspace", [
2715
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" }
2716
+ ]);
2717
+ registerSchema("webhook", "create", "Create a webhook", [
2718
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
2719
+ { flag: "--endpoint", type: "string", required: true, description: "Webhook endpoint URL" },
2720
+ { flag: "--event", type: "string[]", required: false, description: "Event to subscribe to (repeatable)" },
2721
+ { flag: "--space-id", type: "string", required: false, description: "Scope to space" },
2722
+ { flag: "--folder-id", type: "string", required: false, description: "Scope to folder" },
2723
+ { flag: "--list-id", type: "string", required: false, description: "Scope to list" },
2724
+ { flag: "--task-id", type: "string", required: false, description: "Scope to task" }
2725
+ ]);
2726
+ registerSchema("webhook", "update", "Update a webhook", [
2727
+ { flag: "<webhook-id>", type: "string", required: true, description: "Webhook ID" },
2728
+ { flag: "--endpoint", type: "string", required: false, description: "New endpoint URL" },
2729
+ { flag: "--event", type: "string[]", required: false, description: "Events (repeatable, replaces existing)" },
2730
+ { flag: "--status", type: "string", required: false, description: "Status (active or inactive)" }
2731
+ ]);
2732
+ registerSchema("webhook", "delete", "Delete a webhook", [
2733
+ { flag: "<webhook-id>", type: "string", required: true, description: "Webhook ID" },
2734
+ { flag: "--confirm", type: "boolean", required: false, description: "Skip confirmation prompt" }
2735
+ ]);
2736
+ registerSchema("webhook", "events", "List available webhook event types", []);
2737
+ var WEBHOOK_COLUMNS = [
2738
+ { key: "id", header: "ID", width: 20 },
2739
+ { key: "endpoint", header: "Endpoint", width: 40 },
2740
+ { key: "events_count", header: "Events", width: 8 },
2741
+ { key: "health_status", header: "Health", width: 10 }
2742
+ ];
2743
+ var WEBHOOK_EVENTS = [
2744
+ "taskCreated",
2745
+ "taskUpdated",
2746
+ "taskDeleted",
2747
+ "taskPriorityUpdated",
2748
+ "taskStatusUpdated",
2749
+ "taskAssigneeUpdated",
2750
+ "taskDueDateUpdated",
2751
+ "taskTagUpdated",
2752
+ "taskMoved",
2753
+ "taskCommentPosted",
2754
+ "taskCommentUpdated",
2755
+ "taskTimeEstimateUpdated",
2756
+ "taskTimeTrackedUpdated",
2757
+ "listCreated",
2758
+ "listUpdated",
2759
+ "listDeleted",
2760
+ "folderCreated",
2761
+ "folderUpdated",
2762
+ "folderDeleted",
2763
+ "spaceCreated",
2764
+ "spaceUpdated",
2765
+ "spaceDeleted",
2766
+ "goalCreated",
2767
+ "goalUpdated",
2768
+ "goalDeleted",
2769
+ "goalKeyResultCreated",
2770
+ "goalKeyResultUpdated",
2771
+ "goalKeyResultDeleted"
2772
+ ];
2773
+ function collect4(value, previous) {
2774
+ return previous.concat([value]);
2775
+ }
2776
+ function requireWorkspaceId6(program) {
2777
+ const globalOpts = program.opts();
2778
+ const workspaceId = resolveWorkspaceId(globalOpts["workspaceId"]);
2779
+ if (!workspaceId) {
2780
+ process.stderr.write("Error: No workspace ID. Use --workspace-id or run: clickup config set workspace_id <id>\n");
2781
+ process.exit(2);
2782
+ return void 0;
2783
+ }
2784
+ return workspaceId;
2785
+ }
2786
+ function formatWebhooks(webhooks) {
2787
+ return webhooks.map((w) => ({
2788
+ ...w,
2789
+ events_count: Array.isArray(w["events"]) ? w["events"].length : 0,
2790
+ health_status: w["health"]?.["status"] ?? "unknown"
2791
+ }));
2792
+ }
2793
+ function registerWebhookCommands(program, getClient) {
2794
+ const webhook = program.command("webhook").description("Manage webhooks");
2795
+ webhook.command("list").description("List webhooks").action(async () => {
2796
+ const workspaceId = requireWorkspaceId6(program);
2797
+ if (!workspaceId) return;
2798
+ const client = getClient();
2799
+ const data = await client.get(`/team/${workspaceId}/webhook`);
2800
+ formatOutput(formatWebhooks(data.webhooks), WEBHOOK_COLUMNS, getOutputOptions(program));
2801
+ });
2802
+ webhook.command("create").description("Create a webhook").requiredOption("--endpoint <url>", "Webhook endpoint URL").option("--event <event>", "Event to subscribe to (repeatable)", collect4, []).option("--space-id <id>", "Scope to space").option("--folder-id <id>", "Scope to folder").option("--list-id <id>", "Scope to list").option("--task-id <id>", "Scope to task").action(async (opts) => {
2803
+ const workspaceId = requireWorkspaceId6(program);
2804
+ if (!workspaceId) return;
2805
+ const client = getClient();
2806
+ const body = {
2807
+ endpoint: opts.endpoint,
2808
+ events: opts.event
2809
+ };
2810
+ if (opts.spaceId !== void 0) body["space_id"] = opts.spaceId;
2811
+ if (opts.folderId !== void 0) body["folder_id"] = opts.folderId;
2812
+ if (opts.listId !== void 0) body["list_id"] = opts.listId;
2813
+ if (opts.taskId !== void 0) body["task_id"] = opts.taskId;
2814
+ const data = await client.post(`/team/${workspaceId}/webhook`, body);
2815
+ process.stdout.write(`Created webhook ${data.id ?? ""}
2816
+ `);
2817
+ });
2818
+ webhook.command("update").description("Update a webhook").argument("<webhook-id>", "Webhook ID").option("--endpoint <url>", "New endpoint URL").option("--event <event>", "Event to subscribe to (repeatable, replaces existing)", collect4, []).option("--status <status>", "Status (active or inactive)").action(async (webhookId, opts) => {
2819
+ const client = getClient();
2820
+ const body = {};
2821
+ if (opts.endpoint !== void 0) body["endpoint"] = opts.endpoint;
2822
+ if (opts.event.length) body["events"] = opts.event;
2823
+ if (opts.status !== void 0) body["status"] = opts.status;
2824
+ await client.put(`/webhook/${webhookId}`, body);
2825
+ process.stdout.write(`Updated webhook ${webhookId}
2826
+ `);
2827
+ });
2828
+ webhook.command("delete").description("Delete a webhook").argument("<webhook-id>", "Webhook ID").option("--confirm", "Skip confirmation prompt").action(async (webhookId, opts) => {
2829
+ const client = getClient();
2830
+ if (!opts.confirm) {
2831
+ if (!process.stdin.isTTY) {
2832
+ process.stderr.write("Error: Use --confirm to delete in non-interactive mode.\n");
2833
+ process.exit(2);
2834
+ return;
2835
+ }
2836
+ const { confirm } = await import("@inquirer/prompts");
2837
+ const yes = await confirm({ message: `Delete webhook ${webhookId}?` });
2838
+ if (!yes) {
2839
+ process.stdout.write("Cancelled.\n");
2840
+ return;
2841
+ }
2842
+ }
2843
+ await client.delete(`/webhook/${webhookId}`);
2844
+ process.stdout.write(`Deleted webhook ${webhookId}
2845
+ `);
2846
+ });
2847
+ webhook.command("events").description("List available webhook event types").action(() => {
2848
+ const rows = WEBHOOK_EVENTS.map((e) => ({ event: e }));
2849
+ formatOutput(rows, [{ key: "event", header: "Event", width: 35 }], getOutputOptions(program));
2850
+ });
2851
+ }
2852
+
2853
+ // src/commands/user.ts
2854
+ init_config();
2855
+ var USER_COLUMNS = [
2856
+ { key: "id", header: "ID", width: 12 },
2857
+ { key: "username", header: "Username", width: 20 },
2858
+ { key: "email", header: "Email", width: 30 },
2859
+ { key: "role", header: "Role", width: 8 },
2860
+ { key: "custom_role_id", header: "Custom Role", width: 14 }
2861
+ ];
2862
+ function requireWorkspaceId7(program) {
2863
+ const globalOpts = program.opts();
2864
+ const workspaceId = resolveWorkspaceId(globalOpts["workspaceId"]);
2865
+ if (!workspaceId) {
2866
+ process.stderr.write("Error: No workspace ID. Use --workspace-id or run: clickup config set workspace_id <id>\n");
2867
+ process.exit(2);
2868
+ return void 0;
2869
+ }
2870
+ return workspaceId;
2871
+ }
2872
+ registerSchema("user", "invite", "Invite a user to a workspace", [
2873
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
2874
+ { flag: "--email", type: "string", required: true, description: "Email address to invite" },
2875
+ { flag: "--admin", type: "boolean", required: false, description: "Make the user an admin" }
2876
+ ]);
2877
+ registerSchema("user", "get", "Get a user in a workspace", [
2878
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
2879
+ { flag: "<user-id>", type: "string", required: true, description: "User ID" }
2880
+ ]);
2881
+ registerSchema("user", "update", "Update a user in a workspace", [
2882
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
2883
+ { flag: "<user-id>", type: "string", required: true, description: "User ID" },
2884
+ { flag: "--username", type: "string", required: false, description: "New username" },
2885
+ { flag: "--admin", type: "boolean", required: false, description: "Set admin status" },
2886
+ { flag: "--custom-role-id", type: "integer", required: false, description: "Custom role ID" }
2887
+ ]);
2888
+ registerSchema("user", "remove", "Remove a user from a workspace", [
2889
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
2890
+ { flag: "<user-id>", type: "string", required: true, description: "User ID" },
2891
+ { flag: "--confirm", type: "boolean", required: false, description: "Skip confirmation prompt" }
2892
+ ]);
2893
+ function registerUserCommands(program, getClient) {
2894
+ const user = program.command("user").description("Manage workspace users");
2895
+ user.command("invite").description("Invite a user to a workspace").requiredOption("--email <email>", "Email address to invite").option("--admin <bool>", "Make the user an admin").action(async (opts) => {
2896
+ const workspaceId = requireWorkspaceId7(program);
2897
+ if (!workspaceId) return;
2898
+ const client = getClient();
2899
+ const body = { email: opts.email };
2900
+ if (opts.admin !== void 0) body["admin"] = opts.admin === "true";
2901
+ const data = await client.post(`/team/${workspaceId}/user`, body);
2902
+ process.stdout.write(`Invited ${opts.email} to workspace ${data.team?.id ?? workspaceId}
2903
+ `);
2904
+ });
2905
+ user.command("get").description("Get a user in a workspace").argument("<user-id>", "User ID").action(async (userId) => {
2906
+ const workspaceId = requireWorkspaceId7(program);
2907
+ if (!workspaceId) return;
2908
+ const client = getClient();
2909
+ const data = await client.get(`/team/${workspaceId}/user/${userId}`);
2910
+ const userObj = data.member?.user ?? data.member;
2911
+ formatOutput(userObj, USER_COLUMNS, getOutputOptions(program));
2912
+ });
2913
+ user.command("update").description("Update a user in a workspace").argument("<user-id>", "User ID").option("--username <name>", "New username").option("--admin <bool>", "Set admin status").option("--custom-role-id <id>", "Custom role ID").action(async (userId, opts) => {
2914
+ const workspaceId = requireWorkspaceId7(program);
2915
+ if (!workspaceId) return;
2916
+ const client = getClient();
2917
+ const body = {};
2918
+ if (opts.username !== void 0) body["username"] = opts.username;
2919
+ if (opts.admin !== void 0) body["admin"] = opts.admin === "true";
2920
+ if (opts.customRoleId !== void 0) body["custom_role_id"] = parseInt(opts.customRoleId, 10);
2921
+ await client.put(`/team/${workspaceId}/user/${userId}`, body);
2922
+ process.stdout.write(`Updated user ${userId}
2923
+ `);
2924
+ });
2925
+ user.command("remove").description("Remove a user from a workspace").argument("<user-id>", "User ID").option("--confirm", "Skip confirmation prompt").action(async (userId, opts) => {
2926
+ const workspaceId = requireWorkspaceId7(program);
2927
+ if (!workspaceId) return;
2928
+ const client = getClient();
2929
+ if (!opts.confirm) {
2930
+ if (!process.stdin.isTTY) {
2931
+ process.stderr.write("Error: Use --confirm to remove in non-interactive mode.\n");
2932
+ process.exit(2);
2933
+ return;
2934
+ }
2935
+ const { confirm } = await import("@inquirer/prompts");
2936
+ const yes = await confirm({ message: `Remove user ${userId} from workspace?` });
2937
+ if (!yes) {
2938
+ process.stdout.write("Cancelled.\n");
2939
+ return;
2940
+ }
2941
+ }
2942
+ await client.delete(`/team/${workspaceId}/user/${userId}`);
2943
+ process.stdout.write(`Removed user ${userId}
2944
+ `);
2945
+ });
2946
+ }
2947
+
2948
+ // src/commands/group.ts
2949
+ init_config();
2950
+ var GROUP_COLUMNS = [
2951
+ { key: "id", header: "ID", width: 14 },
2952
+ { key: "name", header: "Name", width: 25 },
2953
+ { key: "member_count", header: "Members", width: 10 },
2954
+ { key: "date_created", header: "Created", width: 15 }
2955
+ ];
2956
+ function collect5(value, previous) {
2957
+ return previous.concat([value]);
2958
+ }
2959
+ function requireWorkspaceId8(program) {
2960
+ const globalOpts = program.opts();
2961
+ const workspaceId = resolveWorkspaceId(globalOpts["workspaceId"]);
2962
+ if (!workspaceId) {
2963
+ process.stderr.write("Error: No workspace ID. Use --workspace-id or run: clickup config set workspace_id <id>\n");
2964
+ process.exit(2);
2965
+ return void 0;
2966
+ }
2967
+ return workspaceId;
2968
+ }
2969
+ registerSchema("group", "list", "List user groups in a workspace", [
2970
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
2971
+ { flag: "--group-id", type: "string[]", required: false, description: "Filter by group IDs (repeatable)" }
2972
+ ]);
2973
+ registerSchema("group", "create", "Create a user group", [
2974
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
2975
+ { flag: "--name", type: "string", required: true, description: "Group name" },
2976
+ { flag: "--member-id", type: "integer[]", required: false, description: "Member user IDs (repeatable)" }
2977
+ ]);
2978
+ registerSchema("group", "update", "Update a user group", [
2979
+ { flag: "<group-id>", type: "string", required: true, description: "Group ID" },
2980
+ { flag: "--name", type: "string", required: false, description: "New group name" },
2981
+ { flag: "--add-member", type: "integer[]", required: false, description: "User IDs to add (repeatable)" },
2982
+ { flag: "--remove-member", type: "integer[]", required: false, description: "User IDs to remove (repeatable)" }
2983
+ ]);
2984
+ registerSchema("group", "delete", "Delete a user group", [
2985
+ { flag: "<group-id>", type: "string", required: true, description: "Group ID" },
2986
+ { flag: "--confirm", type: "boolean", required: false, description: "Skip confirmation prompt" }
2987
+ ]);
2988
+ function registerGroupCommands(program, getClient) {
2989
+ const group = program.command("group").description("Manage user groups");
2990
+ group.command("list").description("List user groups").option("--group-id <id>", "Filter by group ID (repeatable)", collect5, []).action(async (opts) => {
2991
+ const workspaceId = requireWorkspaceId8(program);
2992
+ if (!workspaceId) return;
2993
+ const client = getClient();
2994
+ const params = {};
2995
+ if (opts.groupId.length) params["group_ids"] = opts.groupId.join(",");
2996
+ const data = await client.get(`/team/${workspaceId}/group`, params);
2997
+ const rows = data.groups.map((g) => ({
2998
+ ...g,
2999
+ member_count: Array.isArray(g.members) ? g.members.length : 0
3000
+ }));
3001
+ formatOutput(rows, GROUP_COLUMNS, getOutputOptions(program));
3002
+ });
3003
+ group.command("create").description("Create a user group").requiredOption("--name <name>", "Group name").option("--member-id <id>", "Member user ID (repeatable)", collect5, []).action(async (opts) => {
3004
+ const workspaceId = requireWorkspaceId8(program);
3005
+ if (!workspaceId) return;
3006
+ const client = getClient();
3007
+ const body = { name: opts.name };
3008
+ if (opts.memberId.length) {
3009
+ body["members"] = opts.memberId.map((id) => ({ id: parseInt(id, 10) }));
3010
+ }
3011
+ const data = await client.post(`/team/${workspaceId}/group`, body);
3012
+ process.stdout.write(`Created group ${data["id"] ?? ""}
3013
+ `);
3014
+ });
3015
+ group.command("update").description("Update a user group").argument("<group-id>", "Group ID").option("--name <name>", "New group name").option("--add-member <id>", "User ID to add (repeatable)", collect5, []).option("--remove-member <id>", "User ID to remove (repeatable)", collect5, []).action(async (groupId, opts) => {
3016
+ const client = getClient();
3017
+ const body = {};
3018
+ if (opts.name !== void 0) body["name"] = opts.name;
3019
+ const members = {};
3020
+ if (opts.addMember.length) members["add"] = opts.addMember.map((id) => ({ id: parseInt(id, 10) }));
3021
+ if (opts.removeMember.length) members["rem"] = opts.removeMember.map((id) => ({ id: parseInt(id, 10) }));
3022
+ if (Object.keys(members).length) body["members"] = members;
3023
+ await client.put(`/group/${groupId}`, body);
3024
+ process.stdout.write(`Updated group ${groupId}
3025
+ `);
3026
+ });
3027
+ group.command("delete").description("Delete a user group").argument("<group-id>", "Group ID").option("--confirm", "Skip confirmation prompt").action(async (groupId, opts) => {
3028
+ const client = getClient();
3029
+ if (!opts.confirm) {
3030
+ if (!process.stdin.isTTY) {
3031
+ process.stderr.write("Error: Use --confirm to delete in non-interactive mode.\n");
3032
+ process.exit(2);
3033
+ return;
3034
+ }
3035
+ const { confirm } = await import("@inquirer/prompts");
3036
+ const yes = await confirm({ message: `Delete group ${groupId}?` });
3037
+ if (!yes) {
3038
+ process.stdout.write("Cancelled.\n");
3039
+ return;
3040
+ }
3041
+ }
3042
+ await client.delete(`/group/${groupId}`);
3043
+ process.stdout.write(`Deleted group ${groupId}
3044
+ `);
3045
+ });
3046
+ }
3047
+
3048
+ // src/commands/guest.ts
3049
+ init_config();
3050
+ var GUEST_COLUMNS = [
3051
+ { key: "id", header: "ID", width: 12 },
3052
+ { key: "username", header: "Username", width: 20 },
3053
+ { key: "email", header: "Email", width: 30 },
3054
+ { key: "can_edit_tags", header: "Edit Tags", width: 10 },
3055
+ { key: "can_see_time_spent", header: "See Time", width: 10 },
3056
+ { key: "can_see_time_estimated", header: "See Est.", width: 10 }
3057
+ ];
3058
+ function requireWorkspaceId9(program) {
3059
+ const globalOpts = program.opts();
3060
+ const workspaceId = resolveWorkspaceId(globalOpts["workspaceId"]);
3061
+ if (!workspaceId) {
3062
+ process.stderr.write("Error: No workspace ID. Use --workspace-id or run: clickup config set workspace_id <id>\n");
3063
+ process.exit(2);
3064
+ return void 0;
3065
+ }
3066
+ return workspaceId;
3067
+ }
3068
+ function parseBool(val) {
3069
+ if (val === void 0) return void 0;
3070
+ return val === "true";
3071
+ }
3072
+ registerSchema("guest", "invite", "Invite a guest to a workspace", [
3073
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
3074
+ { flag: "--email", type: "string", required: true, description: "Email address to invite" },
3075
+ { flag: "--can-edit-tags", type: "boolean", required: false, description: "Allow editing tags" },
3076
+ { flag: "--can-see-time-spent", type: "boolean", required: false, description: "Allow seeing time spent" },
3077
+ { flag: "--can-see-time-estimated", type: "boolean", required: false, description: "Allow seeing time estimates" }
3078
+ ]);
3079
+ registerSchema("guest", "get", "Get a guest in a workspace", [
3080
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
3081
+ { flag: "<guest-id>", type: "string", required: true, description: "Guest ID" }
3082
+ ]);
3083
+ registerSchema("guest", "update", "Update a guest in a workspace", [
3084
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
3085
+ { flag: "<guest-id>", type: "string", required: true, description: "Guest ID" },
3086
+ { flag: "--username", type: "string", required: false, description: "New username" },
3087
+ { flag: "--can-edit-tags", type: "boolean", required: false, description: "Allow editing tags" },
3088
+ { flag: "--can-see-time-spent", type: "boolean", required: false, description: "Allow seeing time spent" },
3089
+ { flag: "--can-see-time-estimated", type: "boolean", required: false, description: "Allow seeing time estimates" }
3090
+ ]);
3091
+ registerSchema("guest", "remove", "Remove a guest from a workspace", [
3092
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
3093
+ { flag: "<guest-id>", type: "string", required: true, description: "Guest ID" },
3094
+ { flag: "--confirm", type: "boolean", required: false, description: "Skip confirmation prompt" }
3095
+ ]);
3096
+ registerSchema("guest", "add-to-task", "Add a guest to a task", [
3097
+ { flag: "<guest-id>", type: "string", required: true, description: "Guest ID" },
3098
+ { flag: "--task-id", type: "string", required: true, description: "Task ID" },
3099
+ { flag: "--permission", type: "string", required: true, description: "Permission level (read, comment, edit, create)" }
3100
+ ]);
3101
+ registerSchema("guest", "remove-from-task", "Remove a guest from a task", [
3102
+ { flag: "<guest-id>", type: "string", required: true, description: "Guest ID" },
3103
+ { flag: "--task-id", type: "string", required: true, description: "Task ID" }
3104
+ ]);
3105
+ registerSchema("guest", "add-to-list", "Add a guest to a list", [
3106
+ { flag: "<guest-id>", type: "string", required: true, description: "Guest ID" },
3107
+ { flag: "--list-id", type: "string", required: true, description: "List ID" },
3108
+ { flag: "--permission", type: "string", required: true, description: "Permission level (read, comment, edit, create)" }
3109
+ ]);
3110
+ registerSchema("guest", "remove-from-list", "Remove a guest from a list", [
3111
+ { flag: "<guest-id>", type: "string", required: true, description: "Guest ID" },
3112
+ { flag: "--list-id", type: "string", required: true, description: "List ID" }
3113
+ ]);
3114
+ registerSchema("guest", "add-to-folder", "Add a guest to a folder", [
3115
+ { flag: "<guest-id>", type: "string", required: true, description: "Guest ID" },
3116
+ { flag: "--folder-id", type: "string", required: true, description: "Folder ID" },
3117
+ { flag: "--permission", type: "string", required: true, description: "Permission level (read, comment, edit, create)" }
3118
+ ]);
3119
+ registerSchema("guest", "remove-from-folder", "Remove a guest from a folder", [
3120
+ { flag: "<guest-id>", type: "string", required: true, description: "Guest ID" },
3121
+ { flag: "--folder-id", type: "string", required: true, description: "Folder ID" }
3122
+ ]);
3123
+ function registerGuestCommands(program, getClient) {
3124
+ const guest = program.command("guest").description("Manage workspace guests");
3125
+ guest.command("invite").description("Invite a guest to a workspace").requiredOption("--email <email>", "Email address to invite").option("--can-edit-tags <bool>", "Allow editing tags").option("--can-see-time-spent <bool>", "Allow seeing time spent").option("--can-see-time-estimated <bool>", "Allow seeing time estimates").action(async (opts) => {
3126
+ const workspaceId = requireWorkspaceId9(program);
3127
+ if (!workspaceId) return;
3128
+ const client = getClient();
3129
+ const body = { email: opts.email };
3130
+ const canEditTags = parseBool(opts.canEditTags);
3131
+ if (canEditTags !== void 0) body["can_edit_tags"] = canEditTags;
3132
+ const canSeeTimeSpent = parseBool(opts.canSeeTimeSpent);
3133
+ if (canSeeTimeSpent !== void 0) body["can_see_time_spent"] = canSeeTimeSpent;
3134
+ const canSeeTimeEstimated = parseBool(opts.canSeeTimeEstimated);
3135
+ if (canSeeTimeEstimated !== void 0) body["can_see_time_estimated"] = canSeeTimeEstimated;
3136
+ const data = await client.post(`/team/${workspaceId}/guest`, body);
3137
+ process.stdout.write(`Invited guest ${data.guest?.id ?? opts.email}
3138
+ `);
3139
+ });
3140
+ guest.command("get").description("Get a guest in a workspace").argument("<guest-id>", "Guest ID").action(async (guestId) => {
3141
+ const workspaceId = requireWorkspaceId9(program);
3142
+ if (!workspaceId) return;
3143
+ const client = getClient();
3144
+ const data = await client.get(`/team/${workspaceId}/guest/${guestId}`);
3145
+ formatOutput(data.guest, GUEST_COLUMNS, getOutputOptions(program));
3146
+ });
3147
+ guest.command("update").description("Update a guest in a workspace").argument("<guest-id>", "Guest ID").option("--username <name>", "New username").option("--can-edit-tags <bool>", "Allow editing tags").option("--can-see-time-spent <bool>", "Allow seeing time spent").option("--can-see-time-estimated <bool>", "Allow seeing time estimates").action(async (guestId, opts) => {
3148
+ const workspaceId = requireWorkspaceId9(program);
3149
+ if (!workspaceId) return;
3150
+ const client = getClient();
3151
+ const body = {};
3152
+ if (opts.username !== void 0) body["username"] = opts.username;
3153
+ const canEditTags = parseBool(opts.canEditTags);
3154
+ if (canEditTags !== void 0) body["can_edit_tags"] = canEditTags;
3155
+ const canSeeTimeSpent = parseBool(opts.canSeeTimeSpent);
3156
+ if (canSeeTimeSpent !== void 0) body["can_see_time_spent"] = canSeeTimeSpent;
3157
+ const canSeeTimeEstimated = parseBool(opts.canSeeTimeEstimated);
3158
+ if (canSeeTimeEstimated !== void 0) body["can_see_time_estimated"] = canSeeTimeEstimated;
3159
+ await client.put(`/team/${workspaceId}/guest/${guestId}`, body);
3160
+ process.stdout.write(`Updated guest ${guestId}
3161
+ `);
3162
+ });
3163
+ guest.command("remove").description("Remove a guest from a workspace").argument("<guest-id>", "Guest ID").option("--confirm", "Skip confirmation prompt").action(async (guestId, opts) => {
3164
+ const workspaceId = requireWorkspaceId9(program);
3165
+ if (!workspaceId) return;
3166
+ const client = getClient();
3167
+ if (!opts.confirm) {
3168
+ if (!process.stdin.isTTY) {
3169
+ process.stderr.write("Error: Use --confirm to remove in non-interactive mode.\n");
3170
+ process.exit(2);
3171
+ return;
3172
+ }
3173
+ const { confirm } = await import("@inquirer/prompts");
3174
+ const yes = await confirm({ message: `Remove guest ${guestId} from workspace?` });
3175
+ if (!yes) {
3176
+ process.stdout.write("Cancelled.\n");
3177
+ return;
3178
+ }
3179
+ }
3180
+ await client.delete(`/team/${workspaceId}/guest/${guestId}`);
3181
+ process.stdout.write(`Removed guest ${guestId}
3182
+ `);
3183
+ });
3184
+ guest.command("add-to-task").description("Add a guest to a task").argument("<guest-id>", "Guest ID").requiredOption("--task-id <id>", "Task ID").requiredOption("--permission <level>", "Permission level (read, comment, edit, create)").action(async (guestId, opts) => {
3185
+ const client = getClient();
3186
+ await client.post(`/task/${opts.taskId}/guest/${guestId}`, { permission_level: opts.permission });
3187
+ process.stdout.write(`Added guest ${guestId} to task ${opts.taskId}
3188
+ `);
3189
+ });
3190
+ guest.command("remove-from-task").description("Remove a guest from a task").argument("<guest-id>", "Guest ID").requiredOption("--task-id <id>", "Task ID").action(async (guestId, opts) => {
3191
+ const client = getClient();
3192
+ await client.delete(`/task/${opts.taskId}/guest/${guestId}`);
3193
+ process.stdout.write(`Removed guest ${guestId} from task ${opts.taskId}
3194
+ `);
3195
+ });
3196
+ guest.command("add-to-list").description("Add a guest to a list").argument("<guest-id>", "Guest ID").requiredOption("--list-id <id>", "List ID").requiredOption("--permission <level>", "Permission level (read, comment, edit, create)").action(async (guestId, opts) => {
3197
+ const client = getClient();
3198
+ await client.post(`/list/${opts.listId}/guest/${guestId}`, { permission_level: opts.permission });
3199
+ process.stdout.write(`Added guest ${guestId} to list ${opts.listId}
3200
+ `);
3201
+ });
3202
+ guest.command("remove-from-list").description("Remove a guest from a list").argument("<guest-id>", "Guest ID").requiredOption("--list-id <id>", "List ID").action(async (guestId, opts) => {
3203
+ const client = getClient();
3204
+ await client.delete(`/list/${opts.listId}/guest/${guestId}`);
3205
+ process.stdout.write(`Removed guest ${guestId} from list ${opts.listId}
3206
+ `);
3207
+ });
3208
+ guest.command("add-to-folder").description("Add a guest to a folder").argument("<guest-id>", "Guest ID").requiredOption("--folder-id <id>", "Folder ID").requiredOption("--permission <level>", "Permission level (read, comment, edit, create)").action(async (guestId, opts) => {
3209
+ const client = getClient();
3210
+ await client.post(`/folder/${opts.folderId}/guest/${guestId}`, { permission_level: opts.permission });
3211
+ process.stdout.write(`Added guest ${guestId} to folder ${opts.folderId}
3212
+ `);
3213
+ });
3214
+ guest.command("remove-from-folder").description("Remove a guest from a folder").argument("<guest-id>", "Guest ID").requiredOption("--folder-id <id>", "Folder ID").action(async (guestId, opts) => {
3215
+ const client = getClient();
3216
+ await client.delete(`/folder/${opts.folderId}/guest/${guestId}`);
3217
+ process.stdout.write(`Removed guest ${guestId} from folder ${opts.folderId}
3218
+ `);
3219
+ });
3220
+ }
3221
+
3222
+ // src/commands/role.ts
3223
+ init_config();
3224
+ var ROLE_COLUMNS = [
3225
+ { key: "id", header: "ID", width: 12 },
3226
+ { key: "name", header: "Name", width: 25 },
3227
+ { key: "custom", header: "Custom", width: 8 },
3228
+ { key: "members_count", header: "Members", width: 10 },
3229
+ { key: "date_created", header: "Created", width: 15 }
3230
+ ];
3231
+ function requireWorkspaceId10(program) {
3232
+ const globalOpts = program.opts();
3233
+ const workspaceId = resolveWorkspaceId(globalOpts["workspaceId"]);
3234
+ if (!workspaceId) {
3235
+ process.stderr.write("Error: No workspace ID. Use --workspace-id or run: clickup config set workspace_id <id>\n");
3236
+ process.exit(2);
3237
+ return void 0;
3238
+ }
3239
+ return workspaceId;
3240
+ }
3241
+ registerSchema("role", "list", "List custom roles in a workspace", [
3242
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" }
3243
+ ]);
3244
+ function registerRoleCommands(program, getClient) {
3245
+ const role = program.command("role").description("Manage custom roles");
3246
+ role.command("list").description("List custom roles").action(async () => {
3247
+ const workspaceId = requireWorkspaceId10(program);
3248
+ if (!workspaceId) return;
3249
+ const client = getClient();
3250
+ const data = await client.get(`/team/${workspaceId}/customroles`);
3251
+ formatOutput(data.custom_roles, ROLE_COLUMNS, getOutputOptions(program));
3252
+ });
3253
+ }
3254
+
3255
+ // src/commands/member.ts
3256
+ init_config();
3257
+ var MEMBER_COLUMNS = [
3258
+ { key: "id", header: "ID", width: 12 },
3259
+ { key: "username", header: "Username", width: 20 },
3260
+ { key: "email", header: "Email", width: 30 },
3261
+ { key: "initials", header: "Initials", width: 10 }
3262
+ ];
3263
+ function resolveMemberParent(opts) {
3264
+ const parents = [];
3265
+ if (opts.taskId) parents.push({ segment: "task", id: opts.taskId });
3266
+ if (opts.listId) parents.push({ segment: "list", id: opts.listId });
3267
+ if (parents.length === 0) {
3268
+ process.stderr.write("Error: Provide one of --task-id or --list-id.\n");
3269
+ process.exit(2);
3270
+ return void 0;
3271
+ }
3272
+ if (parents.length > 1) {
3273
+ process.stderr.write("Error: Provide only one of --task-id or --list-id.\n");
3274
+ process.exit(2);
3275
+ return void 0;
3276
+ }
3277
+ return parents[0];
3278
+ }
3279
+ registerSchema("member", "list", "List members with access to a task or list", [
3280
+ { flag: "--task-id", type: "string", required: false, description: "Task ID (provide one of --task-id or --list-id)" },
3281
+ { flag: "--list-id", type: "string", required: false, description: "List ID (provide one of --task-id or --list-id)" }
3282
+ ]);
3283
+ registerSchema("member", "find", "Find workspace member by name (fuzzy match)", [
3284
+ { flag: "--name", type: "string", required: true, description: "Member name to search for" },
3285
+ { flag: "--workspace-id", type: "string", required: false, description: "Workspace ID" }
3286
+ ]);
3287
+ registerSchema("member", "resolve", "Resolve member names to user IDs (pipe-friendly)", [
3288
+ { flag: "--names", type: "string", required: true, description: "Comma-separated member names" },
3289
+ { flag: "--workspace-id", type: "string", required: false, description: "Workspace ID" }
3290
+ ]);
3291
+ function getWorkspaceId(program, optsId) {
3292
+ const globalOpts = program.opts();
3293
+ const workspaceId = resolveWorkspaceId(optsId ?? globalOpts["workspaceId"]);
3294
+ if (!workspaceId) {
3295
+ process.stderr.write("Error: No workspace ID. Use --workspace-id or run: clickup config set workspace_id <id>\n");
3296
+ process.exit(2);
3297
+ return void 0;
3298
+ }
3299
+ return workspaceId;
3300
+ }
3301
+ function flattenMember(m) {
3302
+ return {
3303
+ id: m.user["id"],
3304
+ username: m.user["username"],
3305
+ email: m.user["email"],
3306
+ role: m.role
3307
+ };
3308
+ }
3309
+ function registerMemberCommands(program, getClient) {
3310
+ const member = program.command("member").description("Manage task and list members");
3311
+ member.command("list").description("List members of a task or list").option("--task-id <id>", "Task ID").option("--list-id <id>", "List ID").action(async (opts) => {
3312
+ const parent = resolveMemberParent(opts);
3313
+ if (!parent) return;
3314
+ const client = getClient();
3315
+ const data = await client.get(`/${parent.segment}/${parent.id}/member`);
3316
+ formatOutput(data.members, MEMBER_COLUMNS, getOutputOptions(program));
3317
+ });
3318
+ member.command("find").description("Find workspace member by name (fuzzy match)").requiredOption("--name <name>", "Member name to search for").option("--workspace-id <id>", "Workspace ID").action(async (opts) => {
3319
+ const workspaceId = getWorkspaceId(program, opts.workspaceId);
3320
+ if (!workspaceId) return;
3321
+ const client = getClient();
3322
+ const data = await client.get(`/team/${workspaceId}/member`);
3323
+ const query = opts.name.toLowerCase();
3324
+ const matches = (data.members ?? []).map(flattenMember).filter((m) => {
3325
+ const username = String(m["username"] ?? "").toLowerCase();
3326
+ const email = String(m["email"] ?? "").toLowerCase();
3327
+ return username.includes(query) || email.includes(query);
3328
+ });
3329
+ if (matches.length === 0) {
3330
+ process.stderr.write(`No members found matching "${opts.name}"
3331
+ `);
3332
+ process.exit(1);
3333
+ return;
3334
+ }
3335
+ formatOutput(matches, MEMBER_COLUMNS, getOutputOptions(program));
3336
+ });
3337
+ member.command("resolve").description("Resolve member names to user IDs (use --format quiet for IDs only)").requiredOption("--names <names>", "Comma-separated member names").option("--workspace-id <id>", "Workspace ID").action(async (opts) => {
3338
+ const workspaceId = getWorkspaceId(program, opts.workspaceId);
3339
+ if (!workspaceId) return;
3340
+ const client = getClient();
3341
+ const data = await client.get(`/team/${workspaceId}/member`);
3342
+ const names = opts.names.split(",").map((n) => n.trim().toLowerCase()).filter(Boolean);
3343
+ const allMembers = (data.members ?? []).map(flattenMember);
3344
+ const resolved = names.map((query) => {
3345
+ const match = allMembers.find((m) => {
3346
+ const username = String(m["username"] ?? "").toLowerCase();
3347
+ const email = String(m["email"] ?? "").toLowerCase();
3348
+ return username.includes(query) || email.includes(query);
3349
+ });
3350
+ return match ?? { id: "", username: query, email: "(not found)", role: "" };
3351
+ });
3352
+ formatOutput(resolved, MEMBER_COLUMNS, getOutputOptions(program));
3353
+ });
3354
+ }
3355
+
3356
+ // src/commands/template.ts
3357
+ init_config();
3358
+ var TEMPLATE_COLUMNS = [
3359
+ { key: "id", header: "ID", width: 20 },
3360
+ { key: "name", header: "Name", width: 40 }
3361
+ ];
3362
+ function requireWorkspaceId11(program) {
3363
+ const globalOpts = program.opts();
3364
+ const workspaceId = resolveWorkspaceId(globalOpts["workspaceId"]);
3365
+ if (!workspaceId) {
3366
+ process.stderr.write("Error: No workspace ID. Use --workspace-id or run: clickup config set workspace_id <id>\n");
3367
+ process.exit(2);
3368
+ return void 0;
3369
+ }
3370
+ return workspaceId;
3371
+ }
3372
+ registerSchema("template", "list", "List task templates in a workspace", [
3373
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
3374
+ { flag: "--page", type: "integer", required: false, description: "Page number (starts at 0)" }
3375
+ ]);
3376
+ function registerTemplateCommands(program, getClient) {
3377
+ const template = program.command("template").description("Manage task templates");
3378
+ template.command("list").description("List task templates").option("--page <n>", "Page number (starts at 0)", parseInt).action(async (opts) => {
3379
+ const workspaceId = requireWorkspaceId11(program);
3380
+ if (!workspaceId) return;
3381
+ const client = getClient();
3382
+ const params = {};
3383
+ if (opts.page !== void 0) params["page"] = String(opts.page);
3384
+ const data = await client.get(`/team/${workspaceId}/taskTemplate`, params);
3385
+ formatOutput(data.templates, TEMPLATE_COLUMNS, getOutputOptions(program));
3386
+ });
3387
+ }
3388
+
3389
+ // src/commands/task-type.ts
3390
+ init_config();
3391
+ var TASK_TYPE_COLUMNS = [
3392
+ { key: "id", header: "ID", width: 12 },
3393
+ { key: "name", header: "Name", width: 25 },
3394
+ { key: "description", header: "Description", width: 40 }
3395
+ ];
3396
+ function requireWorkspaceId12(program) {
3397
+ const globalOpts = program.opts();
3398
+ const workspaceId = resolveWorkspaceId(globalOpts["workspaceId"]);
3399
+ if (!workspaceId) {
3400
+ process.stderr.write("Error: No workspace ID. Use --workspace-id or run: clickup config set workspace_id <id>\n");
3401
+ process.exit(2);
3402
+ return void 0;
3403
+ }
3404
+ return workspaceId;
3405
+ }
3406
+ registerSchema("task-type", "list", "List custom task types in a workspace", [
3407
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" }
3408
+ ]);
3409
+ function registerTaskTypeCommands(program, getClient) {
3410
+ const taskType = program.command("task-type").description("Manage custom task types");
3411
+ taskType.command("list").description("List custom task types").action(async () => {
3412
+ const workspaceId = requireWorkspaceId12(program);
3413
+ if (!workspaceId) return;
3414
+ const client = getClient();
3415
+ const data = await client.get(`/team/${workspaceId}/custom_item`);
3416
+ formatOutput(data.custom_items, TASK_TYPE_COLUMNS, getOutputOptions(program));
3417
+ });
3418
+ }
3419
+
3420
+ // src/commands/schema-cmd.ts
3421
+ var RESOURCE_COLUMNS = [
3422
+ { key: "resource", header: "Resource", width: 20 },
3423
+ { key: "actions", header: "Actions", width: 50 }
3424
+ ];
3425
+ var ACTION_COLUMNS = [
3426
+ { key: "action", header: "Action", width: 15 },
3427
+ { key: "description", header: "Description", width: 50 }
3428
+ ];
3429
+ var FIELD_COLUMNS2 = [
3430
+ { key: "flag", header: "Flag", width: 25 },
3431
+ { key: "type", header: "Type", width: 12 },
3432
+ { key: "required_label", header: "Required", width: 10 },
3433
+ { key: "description", header: "Description", width: 40 }
3434
+ ];
3435
+ function capitalize(s) {
3436
+ return s.charAt(0).toUpperCase() + s.slice(1);
3437
+ }
3438
+ function registerSchemaCommands(program) {
3439
+ program.command("schema").description("Discover available resources, actions, and fields").argument("[query]", 'Resource or resource.action (e.g. "task" or "task.create")').action((query) => {
3440
+ const outputOpts = getOutputOptions(program);
3441
+ if (!query) {
3442
+ const resources = getResources();
3443
+ const rows2 = resources.map((r) => {
3444
+ const actions = getActions(r);
3445
+ return {
3446
+ resource: r,
3447
+ actions: actions ? actions.map((a) => a.action).join(", ") : ""
3448
+ };
3449
+ });
3450
+ formatOutput(rows2, RESOURCE_COLUMNS, outputOpts);
3451
+ return;
3452
+ }
3453
+ const dotIndex = query.indexOf(".");
3454
+ if (dotIndex === -1) {
3455
+ const actions = getActions(query);
3456
+ if (!actions) {
3457
+ process.stderr.write(`Error: Unknown resource "${query}". Run: clickup schema
3458
+ `);
3459
+ process.exit(2);
3460
+ return;
3461
+ }
3462
+ formatOutput(actions, ACTION_COLUMNS, outputOpts);
3463
+ return;
3464
+ }
3465
+ const resource = query.slice(0, dotIndex);
3466
+ const action = query.slice(dotIndex + 1);
3467
+ const actionDef = getFields(resource, action);
3468
+ if (!actionDef) {
3469
+ process.stderr.write(`Error: Unknown action "${query}". Run: clickup schema ${resource}
3470
+ `);
3471
+ process.exit(2);
3472
+ return;
3473
+ }
3474
+ const format = outputOpts.format ?? (process.stdout.isTTY ? "table" : "json");
3475
+ if (format === "json") {
3476
+ const jsonOutput = {
3477
+ resource,
3478
+ action,
3479
+ required: actionDef.fields.filter((f) => f.required).map(({ flag, type, description }) => ({ flag, type, description })),
3480
+ optional: actionDef.fields.filter((f) => !f.required).map(({ flag, type, description }) => ({ flag, type, description }))
3481
+ };
3482
+ process.stdout.write(JSON.stringify(jsonOutput, null, 2) + "\n");
3483
+ return;
3484
+ }
3485
+ process.stdout.write(`${capitalize(resource)} > ${capitalize(action)}
3486
+
3487
+ `);
3488
+ const rows = actionDef.fields.map((f) => ({
3489
+ ...f,
3490
+ required_label: f.required ? "yes" : ""
3491
+ }));
3492
+ formatOutput(rows, FIELD_COLUMNS2, outputOpts);
3493
+ });
3494
+ }
3495
+
3496
+ // src/commands/shared-hierarchy.ts
3497
+ init_config();
3498
+ var TASK_COLUMNS2 = [
3499
+ { key: "id", header: "ID", width: 12 },
3500
+ { key: "name", header: "Name", width: 30 },
3501
+ { key: "type", header: "Type", width: 8 }
3502
+ ];
3503
+ registerSchema("shared-hierarchy", "get", "Get items shared with the authenticated user", [
3504
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" }
3505
+ ]);
3506
+ function requireWorkspaceId13(program) {
3507
+ const globalOpts = program.opts();
3508
+ const workspaceId = resolveWorkspaceId(globalOpts["workspaceId"]);
3509
+ if (!workspaceId) {
3510
+ process.stderr.write("Error: No workspace ID. Use --workspace-id or run: clickup config set workspace_id <id>\n");
3511
+ process.exit(2);
3512
+ return void 0;
3513
+ }
3514
+ return workspaceId;
3515
+ }
3516
+ function registerSharedHierarchyCommands(program, getClient) {
3517
+ const sharedHierarchy = program.command("shared-hierarchy").description("Manage shared hierarchy");
3518
+ sharedHierarchy.command("get").description("Get tasks, lists, and folders shared with the authenticated user").action(async () => {
3519
+ const workspaceId = requireWorkspaceId13(program);
3520
+ if (!workspaceId) return;
3521
+ const client = getClient();
3522
+ const data = await client.get(`/team/${workspaceId}/shared`);
3523
+ const outputOpts = getOutputOptions(program);
3524
+ const format = outputOpts.format ?? (process.stdout.isTTY ? "table" : "json");
3525
+ if (format === "json") {
3526
+ process.stdout.write(JSON.stringify(data.shared, null, 2) + "\n");
3527
+ return;
3528
+ }
3529
+ const rows = [];
3530
+ for (const task of data.shared.tasks ?? []) {
3531
+ rows.push({ id: task.id, name: task.name, type: "task" });
3532
+ }
3533
+ for (const list of data.shared.lists ?? []) {
3534
+ rows.push({ id: list.id, name: list.name, type: "list" });
3535
+ }
3536
+ for (const folder of data.shared.folders ?? []) {
3537
+ rows.push({ id: folder.id, name: folder.name, type: "folder" });
3538
+ }
3539
+ formatOutput(rows, TASK_COLUMNS2, outputOpts);
3540
+ });
3541
+ }
3542
+
3543
+ // src/commands/doc.ts
3544
+ init_config();
3545
+ var DOC_COLUMNS = [
3546
+ { key: "id", header: "ID", width: 15 },
3547
+ { key: "name", header: "Name", width: 35 },
3548
+ { key: "date_created", header: "Created", width: 15 },
3549
+ { key: "date_updated", header: "Updated", width: 15 }
3550
+ ];
3551
+ var PAGE_COLUMNS = [
3552
+ { key: "id", header: "ID", width: 15 },
3553
+ { key: "name", header: "Name", width: 35 },
3554
+ { key: "parent_page", header: "Parent", width: 15 },
3555
+ { key: "date_created", header: "Created", width: 15 }
3556
+ ];
3557
+ registerSchema("doc", "list", "List docs in a workspace", [
3558
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" }
3559
+ ]);
3560
+ registerSchema("doc", "get", "Get a doc by ID", [
3561
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
3562
+ { flag: "--doc-id", type: "string", required: true, description: "Doc ID" }
3563
+ ]);
3564
+ registerSchema("doc", "create", "Create a doc", [
3565
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
3566
+ { flag: "--name", type: "string", required: true, description: "Doc name" },
3567
+ { flag: "--parent-id", type: "string", required: false, description: "Parent ID (space, folder, list, or task)" },
3568
+ { flag: "--parent-type", type: "number", required: false, description: "Parent type (4=space, 5=folder, 6=list, 7=task)" },
3569
+ { flag: "--visibility", type: "string", required: false, description: "Visibility (private, workspace)" }
3570
+ ]);
3571
+ registerSchema("doc", "update", "Update a doc", [
3572
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
3573
+ { flag: "--doc-id", type: "string", required: true, description: "Doc ID" },
3574
+ { flag: "--name", type: "string", required: false, description: "New doc name" }
3575
+ ]);
3576
+ registerSchema("doc", "delete", "Delete a doc", [
3577
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
3578
+ { flag: "--doc-id", type: "string", required: true, description: "Doc ID" },
3579
+ { flag: "--confirm", type: "boolean", required: false, description: "Skip confirmation prompt" }
3580
+ ]);
3581
+ registerSchema("doc", "search", "Search docs in a workspace", [
3582
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
3583
+ { flag: "--query", type: "string", required: true, description: "Search query" }
3584
+ ]);
3585
+ registerSchema("doc", "pages", "List pages in a doc", [
3586
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
3587
+ { flag: "--doc-id", type: "string", required: true, description: "Doc ID" }
3588
+ ]);
3589
+ registerSchema("doc", "page-get", "Get a page from a doc", [
3590
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
3591
+ { flag: "--doc-id", type: "string", required: true, description: "Doc ID" },
3592
+ { flag: "--page-id", type: "string", required: true, description: "Page ID" }
3593
+ ]);
3594
+ registerSchema("doc", "page-create", "Create a page in a doc", [
3595
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
3596
+ { flag: "--doc-id", type: "string", required: true, description: "Doc ID" },
3597
+ { flag: "--name", type: "string", required: true, description: "Page name" },
3598
+ { flag: "--content", type: "string", required: false, description: "Page content (markdown)" },
3599
+ { flag: "--parent-page-id", type: "string", required: false, description: "Parent page ID" }
3600
+ ]);
3601
+ registerSchema("doc", "page-update", "Update a page in a doc", [
3602
+ { flag: "--workspace-id", type: "string", required: true, description: "Workspace ID" },
3603
+ { flag: "--doc-id", type: "string", required: true, description: "Doc ID" },
3604
+ { flag: "--page-id", type: "string", required: true, description: "Page ID" },
3605
+ { flag: "--name", type: "string", required: false, description: "New page name" },
3606
+ { flag: "--content", type: "string", required: false, description: "New page content (markdown)" }
3607
+ ]);
3608
+ function requireWorkspaceId14(program) {
3609
+ const globalOpts = program.opts();
3610
+ const workspaceId = resolveWorkspaceId(globalOpts["workspaceId"]);
3611
+ if (!workspaceId) {
3612
+ process.stderr.write("Error: No workspace ID. Use --workspace-id or run: clickup config set workspace_id <id>\n");
3613
+ process.exit(2);
3614
+ return void 0;
3615
+ }
3616
+ return workspaceId;
3617
+ }
3618
+ function registerDocCommands(program, getClient) {
3619
+ const doc = program.command("doc").description("Manage docs and pages");
3620
+ doc.command("list").description("List docs in a workspace").action(async () => {
3621
+ const workspaceId = requireWorkspaceId14(program);
3622
+ if (!workspaceId) return;
3623
+ const client = getClient();
3624
+ const data = await client.get(`/v3/workspaces/${workspaceId}/docs`);
3625
+ formatOutput(data.docs, DOC_COLUMNS, getOutputOptions(program));
3626
+ });
3627
+ doc.command("get").description("Get a doc by ID").requiredOption("--doc-id <id>", "Doc ID").action(async (opts) => {
3628
+ const workspaceId = requireWorkspaceId14(program);
3629
+ if (!workspaceId) return;
3630
+ const client = getClient();
3631
+ const data = await client.get(`/v3/workspaces/${workspaceId}/docs/${opts.docId}`);
3632
+ formatOutput(data, DOC_COLUMNS, getOutputOptions(program));
3633
+ });
3634
+ doc.command("create").description("Create a doc").requiredOption("--name <name>", "Doc name").option("--parent-id <id>", "Parent ID").option("--parent-type <type>", "Parent type (4=space, 5=folder, 6=list, 7=task)", parseInt).option("--visibility <visibility>", "Visibility (private, workspace)").action(async (opts) => {
3635
+ const workspaceId = requireWorkspaceId14(program);
3636
+ if (!workspaceId) return;
3637
+ const client = getClient();
3638
+ const body = { name: opts.name };
3639
+ if (opts.parentId) {
3640
+ body["parent"] = { id: opts.parentId, type: opts.parentType ?? 4 };
3641
+ }
3642
+ if (opts.visibility) body["visibility"] = opts.visibility;
3643
+ const data = await client.post(`/v3/workspaces/${workspaceId}/docs`, body);
3644
+ formatOutput(data, DOC_COLUMNS, getOutputOptions(program));
3645
+ });
3646
+ doc.command("update").description("Update a doc").requiredOption("--doc-id <id>", "Doc ID").option("--name <name>", "New name").action(async (opts) => {
3647
+ const workspaceId = requireWorkspaceId14(program);
3648
+ if (!workspaceId) return;
3649
+ const client = getClient();
3650
+ const body = {};
3651
+ if (opts.name) body["name"] = opts.name;
3652
+ const data = await client.put(`/v3/workspaces/${workspaceId}/docs/${opts.docId}`, body);
3653
+ formatOutput(data, DOC_COLUMNS, getOutputOptions(program));
3654
+ });
3655
+ doc.command("delete").description("Delete a doc").requiredOption("--doc-id <id>", "Doc ID").option("--confirm", "Skip confirmation prompt").action(async (opts) => {
3656
+ const workspaceId = requireWorkspaceId14(program);
3657
+ if (!workspaceId) return;
3658
+ if (!opts.confirm) {
3659
+ if (!process.stdin.isTTY) {
3660
+ process.stderr.write("Error: Use --confirm to delete in non-interactive mode.\n");
3661
+ process.exit(2);
3662
+ return;
3663
+ }
3664
+ const { confirm } = await import("@inquirer/prompts");
3665
+ const yes = await confirm({ message: `Delete doc ${opts.docId}?` });
3666
+ if (!yes) {
3667
+ process.stderr.write("Cancelled.\n");
3668
+ return;
3669
+ }
3670
+ }
3671
+ const client = getClient();
3672
+ await client.delete(`/v3/workspaces/${workspaceId}/docs/${opts.docId}`);
3673
+ process.stdout.write(`Doc ${opts.docId} deleted.
3674
+ `);
3675
+ });
3676
+ doc.command("search").description("Search docs in a workspace").requiredOption("--query <text>", "Search query").action(async (opts) => {
3677
+ const workspaceId = requireWorkspaceId14(program);
3678
+ if (!workspaceId) return;
3679
+ const client = getClient();
3680
+ const data = await client.get(`/v3/workspaces/${workspaceId}/docs`, {
3681
+ query: opts.query
3682
+ });
3683
+ formatOutput(data.docs, DOC_COLUMNS, getOutputOptions(program));
3684
+ });
3685
+ doc.command("pages").description("List pages in a doc").requiredOption("--doc-id <id>", "Doc ID").action(async (opts) => {
3686
+ const workspaceId = requireWorkspaceId14(program);
3687
+ if (!workspaceId) return;
3688
+ const client = getClient();
3689
+ const data = await client.get(
3690
+ `/v3/workspaces/${workspaceId}/docs/${opts.docId}/pages`
3691
+ );
3692
+ formatOutput(data.pages, PAGE_COLUMNS, getOutputOptions(program));
3693
+ });
3694
+ doc.command("page-get").description("Get a page from a doc").requiredOption("--doc-id <id>", "Doc ID").requiredOption("--page-id <id>", "Page ID").action(async (opts) => {
3695
+ const workspaceId = requireWorkspaceId14(program);
3696
+ if (!workspaceId) return;
3697
+ const client = getClient();
3698
+ const data = await client.get(
3699
+ `/v3/workspaces/${workspaceId}/docs/${opts.docId}/pages/${opts.pageId}`
3700
+ );
3701
+ formatOutput(data, PAGE_COLUMNS, getOutputOptions(program));
3702
+ });
3703
+ doc.command("page-create").description("Create a page in a doc").requiredOption("--doc-id <id>", "Doc ID").requiredOption("--name <name>", "Page name").option("--content <text>", "Page content (markdown)").option("--parent-page-id <id>", "Parent page ID").action(async (opts) => {
3704
+ const workspaceId = requireWorkspaceId14(program);
3705
+ if (!workspaceId) return;
3706
+ const client = getClient();
3707
+ const body = { name: opts.name };
3708
+ if (opts.content) body["content"] = opts.content;
3709
+ if (opts.parentPageId) body["parent_page"] = opts.parentPageId;
3710
+ const data = await client.post(
3711
+ `/v3/workspaces/${workspaceId}/docs/${opts.docId}/pages`,
3712
+ body
3713
+ );
3714
+ formatOutput(data, PAGE_COLUMNS, getOutputOptions(program));
3715
+ });
3716
+ doc.command("page-update").description("Update a page in a doc").requiredOption("--doc-id <id>", "Doc ID").requiredOption("--page-id <id>", "Page ID").option("--name <name>", "New name").option("--content <text>", "New content (markdown)").action(async (opts) => {
3717
+ const workspaceId = requireWorkspaceId14(program);
3718
+ if (!workspaceId) return;
3719
+ const client = getClient();
3720
+ const body = {};
3721
+ if (opts.name) body["name"] = opts.name;
3722
+ if (opts.content) body["content"] = opts.content;
3723
+ const data = await client.put(
3724
+ `/v3/workspaces/${workspaceId}/docs/${opts.docId}/pages/${opts.pageId}`,
3725
+ body
3726
+ );
3727
+ formatOutput(data, PAGE_COLUMNS, getOutputOptions(program));
3728
+ });
3729
+ }
3730
+
3731
+ // src/commands/skill-cmd.ts
3732
+ import { readdirSync, readFileSync as readFileSync2, existsSync } from "fs";
3733
+ import { join, dirname } from "path";
3734
+ import { fileURLToPath } from "url";
3735
+ var SKILL_COLUMNS = [
3736
+ { key: "name", header: "Name", width: 28 },
3737
+ { key: "type", header: "Type", width: 10 },
3738
+ { key: "description", header: "Description", width: 55 }
3739
+ ];
3740
+ registerSchema("skill", "list", "List all available skills", []);
3741
+ registerSchema("skill", "show", "Show a skill by name", [
3742
+ { flag: "<name>", type: "string", required: true, description: "Skill name" }
3743
+ ]);
3744
+ registerSchema("skill", "path", "Print the file system path to a skill directory", [
3745
+ { flag: "<name>", type: "string", required: true, description: "Skill name" }
3746
+ ]);
3747
+ function findSkillsDir() {
3748
+ const thisDir = dirname(fileURLToPath(import.meta.url));
3749
+ const bundledDir = join(thisDir, "..", "skills");
3750
+ if (existsSync(bundledDir)) return bundledDir;
3751
+ const projectDir = join(thisDir, "..", "..", "skills");
3752
+ if (existsSync(projectDir)) return projectDir;
3753
+ return void 0;
3754
+ }
3755
+ function parseFrontmatter(content) {
3756
+ const fm = {};
3757
+ if (!content.startsWith("---")) {
3758
+ return { frontmatter: fm, body: content };
3759
+ }
3760
+ const endIndex = content.indexOf("\n---", 3);
3761
+ if (endIndex === -1) {
3762
+ return { frontmatter: fm, body: content };
3763
+ }
3764
+ const yamlBlock = content.slice(4, endIndex);
3765
+ for (const line of yamlBlock.split("\n")) {
3766
+ const colonIndex = line.indexOf(":");
3767
+ if (colonIndex === -1) continue;
3768
+ const key = line.slice(0, colonIndex).trim();
3769
+ let value = line.slice(colonIndex + 1).trim();
3770
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
3771
+ value = value.slice(1, -1);
3772
+ }
3773
+ if (key) fm[key] = value;
3774
+ }
3775
+ const body = content.slice(endIndex + 4).trimStart();
3776
+ return { frontmatter: fm, body };
3777
+ }
3778
+ function classifySkill(fm) {
3779
+ if (fm["user-invocable"] === "false") return "root";
3780
+ if (fm["disable-model-invocation"] === "true") return "recipe";
3781
+ return "sub-skill";
3782
+ }
3783
+ function loadSkills(skillsDir) {
3784
+ const skills = [];
3785
+ let entries;
3786
+ try {
3787
+ entries = readdirSync(skillsDir);
3788
+ } catch {
3789
+ return skills;
3790
+ }
3791
+ for (const entry of entries) {
3792
+ const skillFile = join(skillsDir, entry, "SKILL.md");
3793
+ if (!existsSync(skillFile)) continue;
3794
+ try {
3795
+ const content = readFileSync2(skillFile, "utf-8");
3796
+ const { frontmatter } = parseFrontmatter(content);
3797
+ skills.push({
3798
+ name: frontmatter["name"] || entry,
3799
+ description: (frontmatter["description"] || "").slice(0, 100),
3800
+ type: classifySkill(frontmatter),
3801
+ path: join(skillsDir, entry),
3802
+ frontmatter
3803
+ });
3804
+ } catch {
3805
+ }
3806
+ }
3807
+ return skills.sort((a, b) => {
3808
+ const typeOrder = { root: 0, "sub-skill": 1, recipe: 2 };
3809
+ const aOrder = typeOrder[a.type] ?? 3;
3810
+ const bOrder = typeOrder[b.type] ?? 3;
3811
+ if (aOrder !== bOrder) return aOrder - bOrder;
3812
+ return a.name.localeCompare(b.name);
3813
+ });
3814
+ }
3815
+ function findSkill(skillsDir, name) {
3816
+ const skillDir = join(skillsDir, name);
3817
+ const skillFile = join(skillDir, "SKILL.md");
3818
+ if (!existsSync(skillFile)) return void 0;
3819
+ try {
3820
+ const content = readFileSync2(skillFile, "utf-8");
3821
+ return { skillDir, content };
3822
+ } catch {
3823
+ return void 0;
3824
+ }
3825
+ }
3826
+ function registerSkillCommands(program) {
3827
+ const skill = program.command("skill").description("Manage and inspect agent skills");
3828
+ skill.command("list").description("List all available skills").action(() => {
3829
+ const skillsDir = findSkillsDir();
3830
+ if (!skillsDir) {
3831
+ process.stderr.write("Error: Skills directory not found.\n");
3832
+ process.exit(4);
3833
+ return;
3834
+ }
3835
+ const skills = loadSkills(skillsDir);
3836
+ formatOutput(skills, SKILL_COLUMNS, getOutputOptions(program));
3837
+ });
3838
+ skill.command("show").description("Output SKILL.md content to stdout").argument("<name>", "Skill name").action((name) => {
3839
+ const skillsDir = findSkillsDir();
3840
+ if (!skillsDir) {
3841
+ process.stderr.write("Error: Skills directory not found.\n");
3842
+ process.exit(4);
3843
+ return;
3844
+ }
3845
+ const result = findSkill(skillsDir, name);
3846
+ if (!result) {
3847
+ process.stderr.write(`Error: Skill "${name}" not found. Run: clickup skill list
3848
+ `);
3849
+ process.exit(4);
3850
+ return;
3851
+ }
3852
+ const outputOpts = getOutputOptions(program);
3853
+ const format = outputOpts.format ?? (process.stdout.isTTY ? "table" : "json");
3854
+ if (format === "json") {
3855
+ const { frontmatter } = parseFrontmatter(result.content);
3856
+ const output = {
3857
+ name: frontmatter["name"] || name,
3858
+ description: frontmatter["description"] || "",
3859
+ type: classifySkill(frontmatter),
3860
+ path: result.skillDir,
3861
+ frontmatter
3862
+ };
3863
+ process.stdout.write(JSON.stringify(output, null, 2) + "\n");
3864
+ return;
3865
+ }
3866
+ process.stdout.write(result.content);
3867
+ if (!result.content.endsWith("\n")) {
3868
+ process.stdout.write("\n");
3869
+ }
3870
+ });
3871
+ skill.command("path").description("Print file system path to a skill directory").argument("<name>", "Skill name").action((name) => {
3872
+ const skillsDir = findSkillsDir();
3873
+ if (!skillsDir) {
3874
+ process.stderr.write("Error: Skills directory not found.\n");
3875
+ process.exit(4);
3876
+ return;
3877
+ }
3878
+ const result = findSkill(skillsDir, name);
3879
+ if (!result) {
3880
+ process.stderr.write(`Error: Skill "${name}" not found. Run: clickup skill list
3881
+ `);
3882
+ process.exit(4);
3883
+ return;
3884
+ }
3885
+ process.stdout.write(result.skillDir + "\n");
3886
+ });
3887
+ }
3888
+
3889
+ // src/commands/chat.ts
3890
+ init_config();
3891
+ import { z } from "zod";
3892
+ var ChannelSchema = z.object({
3893
+ id: z.string(),
3894
+ name: z.string(),
3895
+ type: z.string().optional(),
3896
+ member_count: z.number().optional()
3897
+ });
3898
+ var CHANNEL_COLUMNS = [
3899
+ { key: "id", header: "ID", width: 20 },
3900
+ { key: "name", header: "Name", width: 35 },
3901
+ { key: "type", header: "Type", width: 12 },
3902
+ { key: "member_count", header: "Members", width: 10 }
3903
+ ];
3904
+ registerSchema("chat", "channels", "List chat channels in workspace", [
3905
+ { flag: "--workspace-id", type: "string", required: false, description: "Workspace ID" }
3906
+ ]);
3907
+ registerSchema("chat", "send", "Send a message to a chat channel", [
3908
+ { flag: "--channel-id", type: "string", required: true, description: "Channel ID" },
3909
+ { flag: "--message", type: "string", required: true, description: "Message text" },
3910
+ { flag: "--workspace-id", type: "string", required: false, description: "Workspace ID" }
3911
+ ]);
3912
+ function registerChatCommands(program, getClient) {
3913
+ const chat = program.command("chat").description("Manage chat channels and messages");
3914
+ chat.command("channels").description("List chat channels in workspace").option("--workspace-id <id>", "Workspace ID").action(async (opts) => {
3915
+ const globalOpts = program.opts();
3916
+ const workspaceId = resolveWorkspaceId(opts.workspaceId ?? globalOpts["workspaceId"]);
3917
+ if (!workspaceId) {
3918
+ process.stderr.write("Error: No workspace ID. Use --workspace-id or run: clickup config set workspace_id <id>\n");
3919
+ process.exit(2);
3920
+ return;
3921
+ }
3922
+ const client = getClient();
3923
+ const data = await client.get(`/team/${workspaceId}/channel`);
3924
+ const rows = Array.isArray(data) ? data : data.channels ?? [];
3925
+ formatOutput(rows, CHANNEL_COLUMNS, getOutputOptions(program));
3926
+ });
3927
+ chat.command("send").description("Send a message to a chat channel").requiredOption("--channel-id <id>", "Channel ID").requiredOption("--message <text>", "Message text").option("--workspace-id <id>", "Workspace ID").action(async (opts) => {
3928
+ const globalOpts = program.opts();
3929
+ const workspaceId = resolveWorkspaceId(opts.workspaceId ?? globalOpts["workspaceId"]);
3930
+ if (!workspaceId) {
3931
+ process.stderr.write("Error: No workspace ID. Use --workspace-id or run: clickup config set workspace_id <id>\n");
3932
+ process.exit(2);
3933
+ return;
3934
+ }
3935
+ const client = getClient();
3936
+ const result = await client.post(
3937
+ `/team/${workspaceId}/channel/${opts.channelId}/message`,
3938
+ { content: opts.message }
3939
+ );
3940
+ formatOutput(result, [
3941
+ { key: "id", header: "Message ID", width: 24 },
3942
+ { key: "content", header: "Content", width: 50 }
3943
+ ], getOutputOptions(program));
3944
+ });
3945
+ }
3946
+
3947
+ // src/cli.ts
3948
+ var VERSION = "0.2.0";
3949
+ function createProgram() {
3950
+ const program = new Command();
3951
+ program.name("clickup").description("ClickUp CLI - Manage ClickUp workspaces from the terminal").version(VERSION).option("--token <token>", "API token").option("--workspace-id <id>", "Workspace ID").option("--format <format>", "Output format (table|json|csv|tsv|quiet|id|md)").option("--no-color", "Disable colors").option("--no-header", "Omit column headers").option("--fields <fields>", "Show only specified fields (comma-separated)").option("--filter <filter>", "Client-side filter (key=value)").option("--sort <sort>", "Sort by field (field[:asc|:desc])").option("--limit <n>", "Limit results", parseInt).option("--verbose", "Show request details").option("--debug", "Full debug output").option("--dry-run", "Print what would be sent without executing");
3952
+ return program;
3953
+ }
3954
+ function createClient(program) {
3955
+ const globalOpts = program.opts();
3956
+ const token = resolveToken(globalOpts["token"]);
3957
+ if (!token) {
3958
+ process.stderr.write("Error: No API token found. Run: clickup auth login\n");
3959
+ process.exit(EXIT_CODES.AUTH_FAILURE);
3960
+ }
3961
+ const clientOpts = { token };
3962
+ if (globalOpts["verbose"]) clientOpts.verbose = true;
3963
+ if (globalOpts["debug"]) clientOpts.debug = true;
3964
+ if (globalOpts["dryRun"]) clientOpts.dryRun = true;
3965
+ return new ClickUpClient(clientOpts);
3966
+ }
3967
+ function getOutputOptions(program) {
3968
+ const opts = program.opts();
3969
+ const result = {
3970
+ format: resolveOutputFormat(opts["format"]),
3971
+ noHeader: opts["header"] === false,
3972
+ noColor: opts["color"] === false
3973
+ };
3974
+ const fields = opts["fields"];
3975
+ if (fields) result.fields = fields;
3976
+ const sort = opts["sort"];
3977
+ if (sort) result.sort = sort;
3978
+ const limit = opts["limit"];
3979
+ if (limit !== void 0) result.limit = limit;
3980
+ const filter = opts["filter"];
3981
+ if (filter) result.filter = filter;
3982
+ return result;
3983
+ }
3984
+ function run() {
3985
+ const program = createProgram();
3986
+ registerAuthCommands(program, () => createClient(program));
3987
+ registerConfigCommands(program);
3988
+ registerWorkspaceCommands(program, () => createClient(program));
3989
+ registerSpaceCommands(program, () => createClient(program));
3990
+ registerFolderCommands(program, () => createClient(program));
3991
+ registerListCommands(program, () => createClient(program));
3992
+ registerTaskCommands(program, () => createClient(program));
3993
+ registerChecklistCommands(program, () => createClient(program));
3994
+ registerFieldCommands(program, () => createClient(program));
3995
+ registerTagCommands(program, () => createClient(program));
3996
+ registerDependencyCommands(program, () => createClient(program));
3997
+ registerRelationCommands(program, () => createClient(program));
3998
+ registerAttachmentCommands(program, () => createClient(program));
3999
+ registerCommentCommands(program, () => createClient(program));
4000
+ registerTimeTrackingCommands(program, () => createClient(program));
4001
+ registerGoalCommands(program, () => createClient(program));
4002
+ registerViewCommands(program, () => createClient(program));
4003
+ registerWebhookCommands(program, () => createClient(program));
4004
+ registerUserCommands(program, () => createClient(program));
4005
+ registerGroupCommands(program, () => createClient(program));
4006
+ registerGuestCommands(program, () => createClient(program));
4007
+ registerRoleCommands(program, () => createClient(program));
4008
+ registerMemberCommands(program, () => createClient(program));
4009
+ registerTemplateCommands(program, () => createClient(program));
4010
+ registerTaskTypeCommands(program, () => createClient(program));
4011
+ registerSharedHierarchyCommands(program, () => createClient(program));
4012
+ registerDocCommands(program, () => createClient(program));
4013
+ registerChatCommands(program, () => createClient(program));
4014
+ registerSchemaCommands(program);
4015
+ registerSkillCommands(program);
4016
+ program.parseAsync(process.argv).catch((error) => {
4017
+ if (error instanceof ClickUpError) {
4018
+ process.stderr.write(`Error: ${error.message}
4019
+ `);
4020
+ process.exit(mapToExitCode(error));
4021
+ }
4022
+ if (error instanceof Error) {
4023
+ process.stderr.write(`Error: ${error.message}
4024
+ `);
4025
+ } else {
4026
+ process.stderr.write(`Error: ${String(error)}
4027
+ `);
4028
+ }
4029
+ process.exit(EXIT_CODES.GENERAL_ERROR);
4030
+ });
4031
+ }
4032
+
4033
+ // bin/clickup.ts
4034
+ run();