@krodak/clickup-cli 1.1.0 → 1.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "clickup-cli",
3
3
  "description": "ClickUp CLI skills for managing tasks, sprints, comments, checklists, custom fields, tags, and time tracking via the cup command",
4
- "version": "1.1.0",
4
+ "version": "1.2.0",
5
5
  "author": {
6
6
  "name": "Krzysztof Rodak"
7
7
  },
package/README.md CHANGED
@@ -232,7 +232,7 @@ Status: :white_check_mark: implemented | :construction: planned | :no_entry_sign
232
232
  | List spaces | `cup spaces` | :white_check_mark: |
233
233
  | List lists | `cup lists <spaceId>` | :white_check_mark: |
234
234
  | Check auth | `cup auth` | :white_check_mark: |
235
- | List folders | `cup folders <spaceId>` | :construction: |
235
+ | List folders | `cup folders <spaceId>` | :white_check_mark: |
236
236
  | List members | `cup members` | :construction: |
237
237
 
238
238
  ### Goals & Key Results
@@ -245,13 +245,14 @@ Status: :white_check_mark: implemented | :construction: planned | :no_entry_sign
245
245
 
246
246
  ### Docs
247
247
 
248
- | Feature | Command | Status |
249
- | ----------------- | ------------------------------------ | ------------------ |
250
- | Search docs | `cup docs [query]` | :white_check_mark: |
251
- | View page content | `cup doc <docId> <pageId>` | :white_check_mark: |
252
- | Create doc | `cup doc-create <title>` | :white_check_mark: |
253
- | Create page | `cup doc-page-create <docId> <name>` | :white_check_mark: |
254
- | Edit page | `cup doc-page-edit <docId> <pageId>` | :white_check_mark: |
248
+ | Feature | Command | Status |
249
+ | ---------------- | ------------------------------------ | ------------------ |
250
+ | Search docs | `cup docs [query]` | :white_check_mark: |
251
+ | View doc / page | `cup doc <docId> [pageId]` | :white_check_mark: |
252
+ | All page content | `cup doc-pages <docId>` | :white_check_mark: |
253
+ | Create doc | `cup doc-create <title>` | :white_check_mark: |
254
+ | Create page | `cup doc-page-create <docId> <name>` | :white_check_mark: |
255
+ | Edit page | `cup doc-page-edit <docId> <pageId>` | :white_check_mark: |
255
256
 
256
257
  ### Attachments
257
258
 
package/dist/index.js CHANGED
@@ -443,6 +443,21 @@ var ClickUpClient = class {
443
443
  body: JSON.stringify(updates)
444
444
  });
445
445
  }
446
+ async getDoc(workspaceId, docId) {
447
+ return this.requestV3(`/workspaces/${workspaceId}/docs/${docId}`);
448
+ }
449
+ async getDocPageListing(workspaceId, docId) {
450
+ const data = await this.requestV3(
451
+ `/workspaces/${workspaceId}/docs/${docId}/pagelisting`
452
+ );
453
+ return data.pages ?? [];
454
+ }
455
+ async getDocPages(workspaceId, docId) {
456
+ const data = await this.requestV3(
457
+ `/workspaces/${workspaceId}/docs/${docId}/pages?content_format=text/md`
458
+ );
459
+ return data.pages ?? [];
460
+ }
446
461
  };
447
462
 
448
463
  // src/config.ts
@@ -2091,7 +2106,7 @@ function bashCompletion(name) {
2091
2106
  cword=$COMP_CWORD
2092
2107
  fi
2093
2108
 
2094
- local commands="init auth tasks task update create sprint sprints subtasks comment comment-edit comment-delete comments replies reply activity lists spaces inbox assigned open search summary overdue assign depend link attach move field delete tag checklist time docs doc doc-create doc-page-create doc-page-edit config completion"
2109
+ local commands="init auth tasks task update create sprint sprints subtasks comment comment-edit comment-delete comments replies reply activity lists spaces inbox assigned open search summary overdue assign depend link attach move field delete tag checklist time docs doc doc-create doc-pages doc-page-create doc-page-edit folders config completion"
2095
2110
 
2096
2111
  if [[ $cword -eq 1 ]]; then
2097
2112
  COMPREPLY=($(compgen -W "$commands --help --version" -- "$cur"))
@@ -2221,6 +2236,12 @@ function bashCompletion(name) {
2221
2236
  doc)
2222
2237
  COMPREPLY=($(compgen -W "--json" -- "$cur"))
2223
2238
  ;;
2239
+ doc-pages)
2240
+ COMPREPLY=($(compgen -W "--json" -- "$cur"))
2241
+ ;;
2242
+ folders)
2243
+ COMPREPLY=($(compgen -W "--name --json" -- "$cur"))
2244
+ ;;
2224
2245
  doc-create)
2225
2246
  COMPREPLY=($(compgen -W "-c --content --json" -- "$cur"))
2226
2247
  ;;
@@ -2291,10 +2312,12 @@ _${name}() {
2291
2312
  'link:Add or remove a link between two tasks'
2292
2313
  'attach:Upload a file attachment to a task'
2293
2314
  'docs:List workspace docs'
2294
- 'doc:View a doc page'
2315
+ 'doc:View a doc or doc page'
2295
2316
  'doc-create:Create a new doc'
2317
+ 'doc-pages:List all pages in a doc with content'
2296
2318
  'doc-page-create:Create a page in a doc'
2297
2319
  'doc-page-edit:Edit a doc page'
2320
+ 'folders:List folders in a space'
2298
2321
  'config:Manage CLI configuration'
2299
2322
  'completion:Output shell completion script'
2300
2323
  )
@@ -2628,6 +2651,17 @@ _${name}() {
2628
2651
  '2:page_id:' \\
2629
2652
  '--json[Force JSON output]'
2630
2653
  ;;
2654
+ doc-pages)
2655
+ _arguments \\
2656
+ '1:doc_id:' \\
2657
+ '--json[Force JSON output]'
2658
+ ;;
2659
+ folders)
2660
+ _arguments \\
2661
+ '1:space_id:' \\
2662
+ '--name[Filter by folder name]:text:' \\
2663
+ '--json[Force JSON output]'
2664
+ ;;
2631
2665
  doc-create)
2632
2666
  _arguments \\
2633
2667
  '1:title:' \\
@@ -2725,10 +2759,12 @@ complete -c ${name} -n __fish_use_subcommand -a reply -d 'Reply to a comment'
2725
2759
  complete -c ${name} -n __fish_use_subcommand -a link -d 'Add or remove a link between two tasks'
2726
2760
  complete -c ${name} -n __fish_use_subcommand -a attach -d 'Upload a file attachment to a task'
2727
2761
  complete -c ${name} -n __fish_use_subcommand -a docs -d 'List workspace docs'
2728
- complete -c ${name} -n __fish_use_subcommand -a doc -d 'View a doc page'
2762
+ complete -c ${name} -n __fish_use_subcommand -a doc -d 'View a doc or doc page'
2729
2763
  complete -c ${name} -n __fish_use_subcommand -a doc-create -d 'Create a new doc'
2764
+ complete -c ${name} -n __fish_use_subcommand -a doc-pages -d 'List all pages in a doc with content'
2730
2765
  complete -c ${name} -n __fish_use_subcommand -a doc-page-create -d 'Create a page in a doc'
2731
2766
  complete -c ${name} -n __fish_use_subcommand -a doc-page-edit -d 'Edit a doc page'
2767
+ complete -c ${name} -n __fish_use_subcommand -a folders -d 'List folders in a space'
2732
2768
  complete -c ${name} -n __fish_use_subcommand -a config -d 'Manage CLI configuration'
2733
2769
  complete -c ${name} -n __fish_use_subcommand -a completion -d 'Output shell completion script'
2734
2770
 
@@ -2886,6 +2922,11 @@ complete -c ${name} -n '__fish_seen_subcommand_from docs' -l json -d 'Force JSON
2886
2922
 
2887
2923
  complete -c ${name} -n '__fish_seen_subcommand_from doc' -l json -d 'Force JSON output'
2888
2924
 
2925
+ complete -c ${name} -n '__fish_seen_subcommand_from doc-pages' -l json -d 'Force JSON output'
2926
+
2927
+ complete -c ${name} -n '__fish_seen_subcommand_from folders' -l name -d 'Filter by folder name'
2928
+ complete -c ${name} -n '__fish_seen_subcommand_from folders' -l json -d 'Force JSON output'
2929
+
2889
2930
  complete -c ${name} -n '__fish_seen_subcommand_from doc-create' -s c -l content -d 'Initial content'
2890
2931
  complete -c ${name} -n '__fish_seen_subcommand_from doc-create' -l json -d 'Force JSON output'
2891
2932
 
@@ -3296,10 +3337,76 @@ function formatDocsMarkdown(docs) {
3296
3337
  }
3297
3338
 
3298
3339
  // src/commands/doc.ts
3340
+ import chalk8 from "chalk";
3341
+ async function getDocInfo(config, docId) {
3342
+ const client = new ClickUpClient(config);
3343
+ const [doc, pages] = await Promise.all([
3344
+ client.getDoc(config.teamId, docId),
3345
+ client.getDocPageListing(config.teamId, docId)
3346
+ ]);
3347
+ return { doc, pages };
3348
+ }
3349
+ function formatDocInfo(doc, pages, indent = 0) {
3350
+ const lines = [];
3351
+ if (indent === 0) {
3352
+ lines.push(`${chalk8.bold(doc.name)} ${chalk8.dim(doc.id)}`);
3353
+ if (pages.length === 0) {
3354
+ lines.push(" (no pages)");
3355
+ }
3356
+ }
3357
+ for (const page of pages) {
3358
+ const prefix = " ".repeat(indent + 1);
3359
+ lines.push(`${prefix}${page.name} ${chalk8.dim(page.id)}`);
3360
+ if (page.pages && page.pages.length > 0) {
3361
+ lines.push(formatDocInfo(doc, page.pages, indent + 1));
3362
+ }
3363
+ }
3364
+ return lines.join("\n");
3365
+ }
3366
+ function formatDocInfoMarkdown(doc, pages, indent = 0) {
3367
+ const lines = [];
3368
+ if (indent === 0) {
3369
+ lines.push(`# ${doc.name}`);
3370
+ lines.push(`ID: ${doc.id}`);
3371
+ lines.push("");
3372
+ if (pages.length === 0) {
3373
+ lines.push("No pages.");
3374
+ return lines.join("\n");
3375
+ }
3376
+ lines.push("## Pages");
3377
+ }
3378
+ for (const page of pages) {
3379
+ const prefix = " ".repeat(indent);
3380
+ lines.push(`${prefix}- **${page.name}** (${page.id})`);
3381
+ if (page.pages && page.pages.length > 0) {
3382
+ lines.push(formatDocInfoMarkdown(doc, page.pages, indent + 1));
3383
+ }
3384
+ }
3385
+ return lines.join("\n");
3386
+ }
3299
3387
  async function getDocPage(config, docId, pageId) {
3300
3388
  const client = new ClickUpClient(config);
3301
3389
  return client.getDocPage(config.teamId, docId, pageId);
3302
3390
  }
3391
+ async function getAllDocPages(config, docId) {
3392
+ const client = new ClickUpClient(config);
3393
+ return client.getDocPages(config.teamId, docId);
3394
+ }
3395
+ function formatDocPages(pages) {
3396
+ if (pages.length === 0) return "No pages found";
3397
+ return pages.map((p) => {
3398
+ const header = `# ${p.name}
3399
+ `;
3400
+ return header + (p.content ?? "");
3401
+ }).join("\n\n---\n\n");
3402
+ }
3403
+ function formatDocPagesMarkdown(pages) {
3404
+ if (pages.length === 0) return "No pages found";
3405
+ return pages.map((p) => {
3406
+ const header = `# ${p.name}`;
3407
+ return header + "\n\n" + (p.content ?? "");
3408
+ }).join("\n\n---\n\n");
3409
+ }
3303
3410
  async function createDoc(config, title, content) {
3304
3411
  if (!title.trim()) throw new Error("Doc title cannot be empty");
3305
3412
  const client = new ClickUpClient(config);
@@ -3319,8 +3426,44 @@ async function editDocPage(config, docId, pageId, updates) {
3319
3426
  return client.editDocPage(config.teamId, docId, pageId, updates);
3320
3427
  }
3321
3428
 
3429
+ // src/commands/folders.ts
3430
+ import chalk9 from "chalk";
3431
+ async function listFolders(config, spaceId, nameFilter) {
3432
+ const client = new ClickUpClient(config);
3433
+ const folders = await client.getFolders(spaceId);
3434
+ let filtered = folders;
3435
+ if (nameFilter) {
3436
+ const lower = nameFilter.toLowerCase();
3437
+ filtered = folders.filter((f) => f.name.toLowerCase().includes(lower));
3438
+ }
3439
+ const results = [];
3440
+ for (const folder of filtered) {
3441
+ const lists = await client.getFolderLists(folder.id);
3442
+ results.push({ id: folder.id, name: folder.name, lists });
3443
+ }
3444
+ return results;
3445
+ }
3446
+ function formatFolders(folders) {
3447
+ if (folders.length === 0) return "No folders found";
3448
+ return folders.map((f) => {
3449
+ const header = `${chalk9.bold(f.name)} ${chalk9.dim(f.id)}`;
3450
+ if (f.lists.length === 0) return header;
3451
+ const listLines = f.lists.map((l) => ` ${l.name} ${chalk9.dim(l.id)}`);
3452
+ return [header, ...listLines].join("\n");
3453
+ }).join("\n\n");
3454
+ }
3455
+ function formatFoldersMarkdown(folders) {
3456
+ if (folders.length === 0) return "No folders found";
3457
+ return folders.map((f) => {
3458
+ const header = `- **${f.name}** (${f.id})`;
3459
+ if (f.lists.length === 0) return header;
3460
+ const listLines = f.lists.map((l) => ` - ${l.name} (${l.id})`);
3461
+ return [header, ...listLines].join("\n");
3462
+ }).join("\n");
3463
+ }
3464
+
3322
3465
  // src/commands/time.ts
3323
- import chalk8 from "chalk";
3466
+ import chalk10 from "chalk";
3324
3467
  async function startTimer(config, taskId, description) {
3325
3468
  const client = new ClickUpClient(config);
3326
3469
  return client.startTimeEntry(config.teamId, taskId, description);
@@ -3356,8 +3499,8 @@ function formatTimeEntry(entry) {
3356
3499
  const isRunning = entry.duration < 0;
3357
3500
  const elapsed = isRunning ? Date.now() - Number(entry.start) : entry.duration;
3358
3501
  const durationStr = formatDuration(elapsed);
3359
- const status = isRunning ? chalk8.green("RUNNING") : "";
3360
- lines.push(`${chalk8.bold(taskName)} ${chalk8.dim(taskId)} ${status}`);
3502
+ const status = isRunning ? chalk10.green("RUNNING") : "";
3503
+ lines.push(`${chalk10.bold(taskName)} ${chalk10.dim(taskId)} ${status}`);
3361
3504
  lines.push(
3362
3505
  ` ${durationStr} - ${formatTimestamp(entry.start)}${entry.description ? ` - ${entry.description}` : ""}`
3363
3506
  );
@@ -3936,16 +4079,53 @@ program.command("docs [query]").description("List workspace docs (optionally fil
3936
4079
  }
3937
4080
  })
3938
4081
  );
3939
- program.command("doc <docId> <pageId>").description("View a doc page").option("--json", "Force JSON output even in terminal").action(
4082
+ program.command("doc <docId> [pageId]").description("View a doc (metadata + page tree) or a specific page").option("--json", "Force JSON output even in terminal").action(
3940
4083
  wrapAction(async (docId, pageId, opts) => {
3941
4084
  const config = loadConfig();
3942
- const page = await getDocPage(config, docId, pageId);
4085
+ if (pageId) {
4086
+ const page = await getDocPage(config, docId, pageId);
4087
+ if (shouldOutputJson(opts.json ?? false)) {
4088
+ console.log(JSON.stringify(page, null, 2));
4089
+ } else {
4090
+ if (page.name) console.log(`# ${page.name}
4091
+ `);
4092
+ console.log(page.content ?? "");
4093
+ }
4094
+ } else {
4095
+ const { doc, pages } = await getDocInfo(config, docId);
4096
+ if (shouldOutputJson(opts.json ?? false)) {
4097
+ console.log(JSON.stringify({ ...doc, pages }, null, 2));
4098
+ } else if (isTTY()) {
4099
+ console.log(formatDocInfo(doc, pages));
4100
+ } else {
4101
+ console.log(formatDocInfoMarkdown(doc, pages));
4102
+ }
4103
+ }
4104
+ })
4105
+ );
4106
+ program.command("doc-pages <docId>").description("List all pages in a doc with content").option("--json", "Force JSON output even in terminal").action(
4107
+ wrapAction(async (docId, opts) => {
4108
+ const config = loadConfig();
4109
+ const pages = await getAllDocPages(config, docId);
3943
4110
  if (shouldOutputJson(opts.json ?? false)) {
3944
- console.log(JSON.stringify(page, null, 2));
4111
+ console.log(JSON.stringify(pages, null, 2));
4112
+ } else if (isTTY()) {
4113
+ console.log(formatDocPages(pages));
3945
4114
  } else {
3946
- if (page.name) console.log(`# ${page.name}
3947
- `);
3948
- console.log(page.content ?? "");
4115
+ console.log(formatDocPagesMarkdown(pages));
4116
+ }
4117
+ })
4118
+ );
4119
+ program.command("folders <spaceId>").description("List folders in a space (with their lists)").option("--name <partial>", "Filter folders by partial name match").option("--json", "Force JSON output even in terminal").action(
4120
+ wrapAction(async (spaceId, opts) => {
4121
+ const config = loadConfig();
4122
+ const folders = await listFolders(config, spaceId, opts.name);
4123
+ if (shouldOutputJson(opts.json ?? false)) {
4124
+ console.log(JSON.stringify(folders, null, 2));
4125
+ } else if (isTTY()) {
4126
+ console.log(formatFolders(folders));
4127
+ } else {
4128
+ console.log(formatFoldersMarkdown(folders));
3949
4129
  }
3950
4130
  })
3951
4131
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krodak/clickup-cli",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "ClickUp CLI for AI agents and humans",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -52,8 +52,10 @@ All commands support `--help` for full flag details.
52
52
  | `cup lists <spaceId> [--name partial] [--json]` | Lists in a space (including folder lists) |
53
53
  | `cup open <query> [--json]` | Open task in browser by ID or name |
54
54
  | `cup auth [--json]` | Check authentication status |
55
+ | `cup folders <spaceId> [--name partial] [--json]` | Folders in a space (with their lists) |
55
56
  | `cup docs [query] [--json]` | List workspace docs (optionally filter by name) |
56
- | `cup doc <docId> <pageId> [--json]` | View a doc page (markdown content) |
57
+ | `cup doc <docId> [pageId] [--json]` | View doc metadata + page tree, or a specific page |
58
+ | `cup doc-pages <docId> [--json]` | All pages in a doc with content |
57
59
 
58
60
  ### Write
59
61
 
@@ -129,8 +131,10 @@ All commands support `--help` for full flag details.
129
131
  | `cup link` + custom IDs | Both IDs must be the same type (both custom or both native). Mixing may not work |
130
132
  | `cup link` | Link/unlink tasks (different from dependencies) |
131
133
  | `cup attach` | Upload files to tasks. Attachments shown in `cup task` detail view |
134
+ | `cup folders` | List folders in a space with their contained lists |
132
135
  | `cup docs` | List and search workspace docs by name |
133
- | `cup doc` | View a doc page content (markdown) |
136
+ | `cup doc` | View doc metadata + page tree (no pageId), or a specific page (with pageId) |
137
+ | `cup doc-pages` | Dump all pages in a doc with full content |
134
138
  | `cup doc-create` | Create a new doc with optional initial content |
135
139
  | `cup doc-page-create` | Create a page in a doc, optionally nested under a parent page |
136
140
  | `cup doc-page-edit` | Edit a doc page name or content |
@@ -210,7 +214,9 @@ cup delete abc123def --confirm # irreversible!
210
214
  ```bash
211
215
  cup docs # list all docs
212
216
  cup docs "design" # search docs by name
217
+ cup doc <docId> # doc metadata + page tree
213
218
  cup doc <docId> <pageId> # view page content
219
+ cup doc-pages <docId> # all pages with content
214
220
  cup doc-create "Architecture Notes" # create a doc
215
221
  cup doc-create "Notes" -c "# Draft" # create with content
216
222
  cup doc-page-create <docId> "New Section" # add page to doc
@@ -224,6 +230,8 @@ cup doc-page-edit <docId> <pageId> -c "# Updated content" # edit content
224
230
  ```bash
225
231
  cup spaces # all spaces
226
232
  cup spaces --name "Engineering" # find space ID by name
233
+ cup folders <spaceId> # folders with their lists
234
+ cup folders <spaceId> --name "sprint" # filter folders by name
227
235
  cup lists <spaceId> # lists in a space (needs ID from cup spaces)
228
236
  cup sprints # all sprints across folders
229
237
  cup auth # verify token works