@krodak/clickup-cli 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +11 -8
- package/dist/index.js +247 -4
- package/package.json +1 -1
- package/skills/clickup-cli/SKILL.md +24 -0
|
@@ -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.
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Krzysztof Rodak"
|
|
7
7
|
},
|
package/README.md
CHANGED
|
@@ -29,6 +29,10 @@ Install the CLI, add the skill file to your agent, and it works with ClickUp. No
|
|
|
29
29
|
|
|
30
30
|
The agent reads the skill file, picks the right `cup` commands, and handles everything. You don't need to learn the CLI - the agent does.
|
|
31
31
|
|
|
32
|
+
## Why a CLI and not MCP?
|
|
33
|
+
|
|
34
|
+
A CLI + skill file has fewer moving parts. No server process, no protocol layer. The agent already knows how to run shell commands - the skill file teaches it which ones exist. For tool-use with coding agents, CLI + instructions tends to work better than MCP in practice.
|
|
35
|
+
|
|
32
36
|
## Install
|
|
33
37
|
|
|
34
38
|
You need Node 22+ and a ClickUp personal API token (`pk_...` from [ClickUp Settings > Apps](https://app.clickup.com/settings/apps)).
|
|
@@ -241,10 +245,13 @@ Status: :white_check_mark: implemented | :construction: planned | :no_entry_sign
|
|
|
241
245
|
|
|
242
246
|
### Docs
|
|
243
247
|
|
|
244
|
-
| Feature | Command
|
|
245
|
-
| ----------------- |
|
|
246
|
-
| Search docs | `cup docs
|
|
247
|
-
| View page content | `cup doc <
|
|
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
255
|
|
|
249
256
|
### Attachments
|
|
250
257
|
|
|
@@ -338,10 +345,6 @@ Custom ID resolution uses the `teamId` from your config, which is required (`cup
|
|
|
338
345
|
|
|
339
346
|
**Task links with custom IDs:** The `cup link` command passes both task IDs in a single API request. When both IDs are custom, this works correctly. However, mixing custom and native IDs in a single link command may not work as expected because the ClickUp API applies the `custom_task_ids` flag to all IDs in the request.
|
|
340
347
|
|
|
341
|
-
## Why a CLI and not MCP?
|
|
342
|
-
|
|
343
|
-
A CLI + skill file has fewer moving parts. No server process, no protocol layer. The agent already knows how to run shell commands - the skill file teaches it which ones exist. For tool-use with coding agents, CLI + instructions tends to work better than MCP in practice.
|
|
344
|
-
|
|
345
348
|
## Development
|
|
346
349
|
|
|
347
350
|
```bash
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { createRequire } from "module";
|
|
|
7
7
|
|
|
8
8
|
// src/api.ts
|
|
9
9
|
var BASE_URL = "https://api.clickup.com/api/v2";
|
|
10
|
+
var BASE_URL_V3 = "https://api.clickup.com/api/v3";
|
|
10
11
|
var MAX_PAGES = 100;
|
|
11
12
|
function isCustomTaskId(id) {
|
|
12
13
|
return /^[A-Z]+-\d+$/i.test(id);
|
|
@@ -56,6 +57,29 @@ var ClickUpClient = class {
|
|
|
56
57
|
}
|
|
57
58
|
return data;
|
|
58
59
|
}
|
|
60
|
+
async requestV3(path, options = {}) {
|
|
61
|
+
const res = await fetch(`${BASE_URL_V3}${path}`, {
|
|
62
|
+
...options,
|
|
63
|
+
signal: AbortSignal.timeout(3e4),
|
|
64
|
+
headers: {
|
|
65
|
+
Authorization: this.apiToken,
|
|
66
|
+
...options.body ? { "Content-Type": "application/json" } : {},
|
|
67
|
+
...options.headers
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
let data;
|
|
71
|
+
try {
|
|
72
|
+
data = await res.json();
|
|
73
|
+
} catch {
|
|
74
|
+
throw new Error(`ClickUp API error ${res.status}: response was not valid JSON`);
|
|
75
|
+
}
|
|
76
|
+
if (!res.ok) {
|
|
77
|
+
const raw = data.err ?? data.error ?? data.ECODE ?? res.statusText;
|
|
78
|
+
const msg = typeof raw === "string" ? raw : JSON.stringify(raw);
|
|
79
|
+
throw new Error(`ClickUp API error ${res.status}: ${msg}`);
|
|
80
|
+
}
|
|
81
|
+
return data;
|
|
82
|
+
}
|
|
59
83
|
async getMe() {
|
|
60
84
|
if (this.meCache) return this.meCache;
|
|
61
85
|
const data = await this.request("/user");
|
|
@@ -383,6 +407,42 @@ var ClickUpClient = class {
|
|
|
383
407
|
}
|
|
384
408
|
return data;
|
|
385
409
|
}
|
|
410
|
+
async getDocs(workspaceId) {
|
|
411
|
+
const data = await this.requestV3(`/workspaces/${workspaceId}/docs`);
|
|
412
|
+
return data.docs ?? [];
|
|
413
|
+
}
|
|
414
|
+
async getDocPage(workspaceId, docId, pageId) {
|
|
415
|
+
return this.requestV3(
|
|
416
|
+
`/workspaces/${workspaceId}/docs/${docId}/pages/${pageId}?content_format=text/md`
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
async createDoc(workspaceId, title, content, parentId) {
|
|
420
|
+
const body = { title };
|
|
421
|
+
if (content) body.content = content;
|
|
422
|
+
if (parentId) {
|
|
423
|
+
body.parent_id = parentId;
|
|
424
|
+
body.parent_type = "doc";
|
|
425
|
+
}
|
|
426
|
+
return this.requestV3(`/workspaces/${workspaceId}/docs`, {
|
|
427
|
+
method: "POST",
|
|
428
|
+
body: JSON.stringify(body)
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
async createDocPage(workspaceId, docId, name, content, parentPageId) {
|
|
432
|
+
const body = { name, content_format: "text/md" };
|
|
433
|
+
if (content) body.content = content;
|
|
434
|
+
if (parentPageId) body.parent_page_id = parentPageId;
|
|
435
|
+
return this.requestV3(`/workspaces/${workspaceId}/docs/${docId}/pages`, {
|
|
436
|
+
method: "POST",
|
|
437
|
+
body: JSON.stringify(body)
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
async editDocPage(workspaceId, docId, pageId, updates) {
|
|
441
|
+
return this.requestV3(`/workspaces/${workspaceId}/docs/${docId}/pages/${pageId}`, {
|
|
442
|
+
method: "PUT",
|
|
443
|
+
body: JSON.stringify(updates)
|
|
444
|
+
});
|
|
445
|
+
}
|
|
386
446
|
};
|
|
387
447
|
|
|
388
448
|
// src/config.ts
|
|
@@ -2031,7 +2091,7 @@ function bashCompletion(name) {
|
|
|
2031
2091
|
cword=$COMP_CWORD
|
|
2032
2092
|
fi
|
|
2033
2093
|
|
|
2034
|
-
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 config completion"
|
|
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"
|
|
2035
2095
|
|
|
2036
2096
|
if [[ $cword -eq 1 ]]; then
|
|
2037
2097
|
COMPREPLY=($(compgen -W "$commands --help --version" -- "$cur"))
|
|
@@ -2155,6 +2215,21 @@ function bashCompletion(name) {
|
|
|
2155
2215
|
attach)
|
|
2156
2216
|
COMPREPLY=($(compgen -f -- "$cur"))
|
|
2157
2217
|
;;
|
|
2218
|
+
docs)
|
|
2219
|
+
COMPREPLY=($(compgen -W "--json" -- "$cur"))
|
|
2220
|
+
;;
|
|
2221
|
+
doc)
|
|
2222
|
+
COMPREPLY=($(compgen -W "--json" -- "$cur"))
|
|
2223
|
+
;;
|
|
2224
|
+
doc-create)
|
|
2225
|
+
COMPREPLY=($(compgen -W "-c --content --json" -- "$cur"))
|
|
2226
|
+
;;
|
|
2227
|
+
doc-page-create)
|
|
2228
|
+
COMPREPLY=($(compgen -W "-c --content --parent-page --json" -- "$cur"))
|
|
2229
|
+
;;
|
|
2230
|
+
doc-page-edit)
|
|
2231
|
+
COMPREPLY=($(compgen -W "--name -c --content --json" -- "$cur"))
|
|
2232
|
+
;;
|
|
2158
2233
|
config)
|
|
2159
2234
|
if [[ $cword -eq 2 ]]; then
|
|
2160
2235
|
COMPREPLY=($(compgen -W "get set path" -- "$cur"))
|
|
@@ -2215,6 +2290,11 @@ _${name}() {
|
|
|
2215
2290
|
'reply:Reply to a comment'
|
|
2216
2291
|
'link:Add or remove a link between two tasks'
|
|
2217
2292
|
'attach:Upload a file attachment to a task'
|
|
2293
|
+
'docs:List workspace docs'
|
|
2294
|
+
'doc:View a doc page'
|
|
2295
|
+
'doc-create:Create a new doc'
|
|
2296
|
+
'doc-page-create:Create a page in a doc'
|
|
2297
|
+
'doc-page-edit:Edit a doc page'
|
|
2218
2298
|
'config:Manage CLI configuration'
|
|
2219
2299
|
'completion:Output shell completion script'
|
|
2220
2300
|
)
|
|
@@ -2537,6 +2617,39 @@ _${name}() {
|
|
|
2537
2617
|
'2:file_path:_files' \\
|
|
2538
2618
|
'--json[Force JSON output]'
|
|
2539
2619
|
;;
|
|
2620
|
+
docs)
|
|
2621
|
+
_arguments \\
|
|
2622
|
+
'1:query:' \\
|
|
2623
|
+
'--json[Force JSON output]'
|
|
2624
|
+
;;
|
|
2625
|
+
doc)
|
|
2626
|
+
_arguments \\
|
|
2627
|
+
'1:doc_id:' \\
|
|
2628
|
+
'2:page_id:' \\
|
|
2629
|
+
'--json[Force JSON output]'
|
|
2630
|
+
;;
|
|
2631
|
+
doc-create)
|
|
2632
|
+
_arguments \\
|
|
2633
|
+
'1:title:' \\
|
|
2634
|
+
'(-c --content)'{-c,--content}'[Initial content]:text:' \\
|
|
2635
|
+
'--json[Force JSON output]'
|
|
2636
|
+
;;
|
|
2637
|
+
doc-page-create)
|
|
2638
|
+
_arguments \\
|
|
2639
|
+
'1:doc_id:' \\
|
|
2640
|
+
'2:name:' \\
|
|
2641
|
+
'(-c --content)'{-c,--content}'[Page content]:text:' \\
|
|
2642
|
+
'--parent-page[Parent page ID]:page_id:' \\
|
|
2643
|
+
'--json[Force JSON output]'
|
|
2644
|
+
;;
|
|
2645
|
+
doc-page-edit)
|
|
2646
|
+
_arguments \\
|
|
2647
|
+
'1:doc_id:' \\
|
|
2648
|
+
'2:page_id:' \\
|
|
2649
|
+
'--name[New page name]:text:' \\
|
|
2650
|
+
'(-c --content)'{-c,--content}'[New page content]:text:' \\
|
|
2651
|
+
'--json[Force JSON output]'
|
|
2652
|
+
;;
|
|
2540
2653
|
config)
|
|
2541
2654
|
local -a config_cmds
|
|
2542
2655
|
config_cmds=(
|
|
@@ -2611,6 +2724,11 @@ complete -c ${name} -n __fish_use_subcommand -a replies -d 'List threaded replie
|
|
|
2611
2724
|
complete -c ${name} -n __fish_use_subcommand -a reply -d 'Reply to a comment'
|
|
2612
2725
|
complete -c ${name} -n __fish_use_subcommand -a link -d 'Add or remove a link between two tasks'
|
|
2613
2726
|
complete -c ${name} -n __fish_use_subcommand -a attach -d 'Upload a file attachment to a task'
|
|
2727
|
+
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'
|
|
2729
|
+
complete -c ${name} -n __fish_use_subcommand -a doc-create -d 'Create a new doc'
|
|
2730
|
+
complete -c ${name} -n __fish_use_subcommand -a doc-page-create -d 'Create a page in a doc'
|
|
2731
|
+
complete -c ${name} -n __fish_use_subcommand -a doc-page-edit -d 'Edit a doc page'
|
|
2614
2732
|
complete -c ${name} -n __fish_use_subcommand -a config -d 'Manage CLI configuration'
|
|
2615
2733
|
complete -c ${name} -n __fish_use_subcommand -a completion -d 'Output shell completion script'
|
|
2616
2734
|
|
|
@@ -2764,6 +2882,21 @@ complete -c ${name} -n '__fish_seen_subcommand_from comment-edit' -l resolved -d
|
|
|
2764
2882
|
complete -c ${name} -n '__fish_seen_subcommand_from comment-edit' -l unresolved -d 'Mark comment as unresolved'
|
|
2765
2883
|
complete -c ${name} -n '__fish_seen_subcommand_from comment-edit' -l json -d 'Force JSON output'
|
|
2766
2884
|
|
|
2885
|
+
complete -c ${name} -n '__fish_seen_subcommand_from docs' -l json -d 'Force JSON output'
|
|
2886
|
+
|
|
2887
|
+
complete -c ${name} -n '__fish_seen_subcommand_from doc' -l json -d 'Force JSON output'
|
|
2888
|
+
|
|
2889
|
+
complete -c ${name} -n '__fish_seen_subcommand_from doc-create' -s c -l content -d 'Initial content'
|
|
2890
|
+
complete -c ${name} -n '__fish_seen_subcommand_from doc-create' -l json -d 'Force JSON output'
|
|
2891
|
+
|
|
2892
|
+
complete -c ${name} -n '__fish_seen_subcommand_from doc-page-create' -s c -l content -d 'Page content'
|
|
2893
|
+
complete -c ${name} -n '__fish_seen_subcommand_from doc-page-create' -l parent-page -d 'Parent page ID'
|
|
2894
|
+
complete -c ${name} -n '__fish_seen_subcommand_from doc-page-create' -l json -d 'Force JSON output'
|
|
2895
|
+
|
|
2896
|
+
complete -c ${name} -n '__fish_seen_subcommand_from doc-page-edit' -l name -d 'New page name'
|
|
2897
|
+
complete -c ${name} -n '__fish_seen_subcommand_from doc-page-edit' -s c -l content -d 'New page content'
|
|
2898
|
+
complete -c ${name} -n '__fish_seen_subcommand_from doc-page-edit' -l json -d 'Force JSON output'
|
|
2899
|
+
|
|
2767
2900
|
complete -c ${name} -n '__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from get set path' -a get -d 'Print a config value'
|
|
2768
2901
|
complete -c ${name} -n '__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from get set path' -a set -d 'Set a config value'
|
|
2769
2902
|
complete -c ${name} -n '__fish_seen_subcommand_from config; and not __fish_seen_subcommand_from get set path' -a path -d 'Print config file path'
|
|
@@ -3142,8 +3275,52 @@ async function attachFile(config, taskId, filePath) {
|
|
|
3142
3275
|
return client.createTaskAttachment(taskId, filePath);
|
|
3143
3276
|
}
|
|
3144
3277
|
|
|
3145
|
-
// src/commands/
|
|
3278
|
+
// src/commands/docs.ts
|
|
3146
3279
|
import chalk7 from "chalk";
|
|
3280
|
+
async function listDocs(config, query) {
|
|
3281
|
+
const client = new ClickUpClient(config);
|
|
3282
|
+
const docs = await client.getDocs(config.teamId);
|
|
3283
|
+
if (query) {
|
|
3284
|
+
const lower = query.toLowerCase();
|
|
3285
|
+
return docs.filter((d) => d.name.toLowerCase().includes(lower));
|
|
3286
|
+
}
|
|
3287
|
+
return docs;
|
|
3288
|
+
}
|
|
3289
|
+
function formatDocs(docs) {
|
|
3290
|
+
if (docs.length === 0) return "No docs found";
|
|
3291
|
+
return docs.map((d) => `${chalk7.bold(d.name)} ${chalk7.dim(d.id)}`).join("\n");
|
|
3292
|
+
}
|
|
3293
|
+
function formatDocsMarkdown(docs) {
|
|
3294
|
+
if (docs.length === 0) return "No docs found";
|
|
3295
|
+
return docs.map((d) => `- **${d.name}** (${d.id})`).join("\n");
|
|
3296
|
+
}
|
|
3297
|
+
|
|
3298
|
+
// src/commands/doc.ts
|
|
3299
|
+
async function getDocPage(config, docId, pageId) {
|
|
3300
|
+
const client = new ClickUpClient(config);
|
|
3301
|
+
return client.getDocPage(config.teamId, docId, pageId);
|
|
3302
|
+
}
|
|
3303
|
+
async function createDoc(config, title, content) {
|
|
3304
|
+
if (!title.trim()) throw new Error("Doc title cannot be empty");
|
|
3305
|
+
const client = new ClickUpClient(config);
|
|
3306
|
+
const doc = await client.createDoc(config.teamId, title, content);
|
|
3307
|
+
return { id: doc.id, title: doc.name ?? title };
|
|
3308
|
+
}
|
|
3309
|
+
async function createDocPage(config, docId, name, content, parentPageId) {
|
|
3310
|
+
if (!name.trim()) throw new Error("Page name cannot be empty");
|
|
3311
|
+
const client = new ClickUpClient(config);
|
|
3312
|
+
return client.createDocPage(config.teamId, docId, name, content, parentPageId);
|
|
3313
|
+
}
|
|
3314
|
+
async function editDocPage(config, docId, pageId, updates) {
|
|
3315
|
+
if (!updates.name && !updates.content) {
|
|
3316
|
+
throw new Error("Provide --name or --content to update");
|
|
3317
|
+
}
|
|
3318
|
+
const client = new ClickUpClient(config);
|
|
3319
|
+
return client.editDocPage(config.teamId, docId, pageId, updates);
|
|
3320
|
+
}
|
|
3321
|
+
|
|
3322
|
+
// src/commands/time.ts
|
|
3323
|
+
import chalk8 from "chalk";
|
|
3147
3324
|
async function startTimer(config, taskId, description) {
|
|
3148
3325
|
const client = new ClickUpClient(config);
|
|
3149
3326
|
return client.startTimeEntry(config.teamId, taskId, description);
|
|
@@ -3179,8 +3356,8 @@ function formatTimeEntry(entry) {
|
|
|
3179
3356
|
const isRunning = entry.duration < 0;
|
|
3180
3357
|
const elapsed = isRunning ? Date.now() - Number(entry.start) : entry.duration;
|
|
3181
3358
|
const durationStr = formatDuration(elapsed);
|
|
3182
|
-
const status = isRunning ?
|
|
3183
|
-
lines.push(`${
|
|
3359
|
+
const status = isRunning ? chalk8.green("RUNNING") : "";
|
|
3360
|
+
lines.push(`${chalk8.bold(taskName)} ${chalk8.dim(taskId)} ${status}`);
|
|
3184
3361
|
lines.push(
|
|
3185
3362
|
` ${durationStr} - ${formatTimestamp(entry.start)}${entry.description ? ` - ${entry.description}` : ""}`
|
|
3186
3363
|
);
|
|
@@ -3746,6 +3923,72 @@ timeCmd.command("list").description("List recent time entries (default: last 7 d
|
|
|
3746
3923
|
}
|
|
3747
3924
|
})
|
|
3748
3925
|
);
|
|
3926
|
+
program.command("docs [query]").description("List workspace docs (optionally filter by name)").option("--json", "Force JSON output even in terminal").action(
|
|
3927
|
+
wrapAction(async (query, opts) => {
|
|
3928
|
+
const config = loadConfig();
|
|
3929
|
+
const docs = await listDocs(config, query);
|
|
3930
|
+
if (shouldOutputJson(opts.json ?? false)) {
|
|
3931
|
+
console.log(JSON.stringify(docs, null, 2));
|
|
3932
|
+
} else if (isTTY()) {
|
|
3933
|
+
console.log(formatDocs(docs));
|
|
3934
|
+
} else {
|
|
3935
|
+
console.log(formatDocsMarkdown(docs));
|
|
3936
|
+
}
|
|
3937
|
+
})
|
|
3938
|
+
);
|
|
3939
|
+
program.command("doc <docId> <pageId>").description("View a doc page").option("--json", "Force JSON output even in terminal").action(
|
|
3940
|
+
wrapAction(async (docId, pageId, opts) => {
|
|
3941
|
+
const config = loadConfig();
|
|
3942
|
+
const page = await getDocPage(config, docId, pageId);
|
|
3943
|
+
if (shouldOutputJson(opts.json ?? false)) {
|
|
3944
|
+
console.log(JSON.stringify(page, null, 2));
|
|
3945
|
+
} else {
|
|
3946
|
+
if (page.name) console.log(`# ${page.name}
|
|
3947
|
+
`);
|
|
3948
|
+
console.log(page.content ?? "");
|
|
3949
|
+
}
|
|
3950
|
+
})
|
|
3951
|
+
);
|
|
3952
|
+
program.command("doc-create <title>").description("Create a new doc").option("-c, --content <text>", "Initial content (markdown)").option("--json", "Force JSON output even in terminal").action(
|
|
3953
|
+
wrapAction(async (title, opts) => {
|
|
3954
|
+
const config = loadConfig();
|
|
3955
|
+
const result = await createDoc(config, title, opts.content);
|
|
3956
|
+
if (shouldOutputJson(opts.json ?? false)) {
|
|
3957
|
+
console.log(JSON.stringify(result, null, 2));
|
|
3958
|
+
} else {
|
|
3959
|
+
console.log(`Created doc "${result.title}" (${result.id})`);
|
|
3960
|
+
}
|
|
3961
|
+
})
|
|
3962
|
+
);
|
|
3963
|
+
program.command("doc-page-create <docId> <name>").description("Create a page in a doc").option("-c, --content <text>", "Page content (markdown)").option("--parent-page <pageId>", "Parent page ID for nesting").option("--json", "Force JSON output even in terminal").action(
|
|
3964
|
+
wrapAction(
|
|
3965
|
+
async (docId, name, opts) => {
|
|
3966
|
+
const config = loadConfig();
|
|
3967
|
+
const page = await createDocPage(config, docId, name, opts.content, opts.parentPage);
|
|
3968
|
+
if (shouldOutputJson(opts.json ?? false)) {
|
|
3969
|
+
console.log(JSON.stringify(page, null, 2));
|
|
3970
|
+
} else {
|
|
3971
|
+
console.log(`Created page "${page.name}" (${page.id}) in doc ${docId}`);
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
3974
|
+
)
|
|
3975
|
+
);
|
|
3976
|
+
program.command("doc-page-edit <docId> <pageId>").description("Edit a doc page").option("--name <text>", "New page name").option("-c, --content <text>", "New page content (markdown)").option("--json", "Force JSON output even in terminal").action(
|
|
3977
|
+
wrapAction(
|
|
3978
|
+
async (docId, pageId, opts) => {
|
|
3979
|
+
const config = loadConfig();
|
|
3980
|
+
const page = await editDocPage(config, docId, pageId, {
|
|
3981
|
+
name: opts.name,
|
|
3982
|
+
content: opts.content
|
|
3983
|
+
});
|
|
3984
|
+
if (shouldOutputJson(opts.json ?? false)) {
|
|
3985
|
+
console.log(JSON.stringify(page, null, 2));
|
|
3986
|
+
} else {
|
|
3987
|
+
console.log(`Updated page "${page.name}" (${page.id})`);
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3990
|
+
)
|
|
3991
|
+
);
|
|
3749
3992
|
var configCmd = program.command("config").description("Manage CLI configuration");
|
|
3750
3993
|
configCmd.command("get <key>").description("Print a config value").action(
|
|
3751
3994
|
wrapAction(async (key) => {
|
package/package.json
CHANGED
|
@@ -52,6 +52,8 @@ 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 docs [query] [--json]` | List workspace docs (optionally filter by name) |
|
|
56
|
+
| `cup doc <docId> <pageId> [--json]` | View a doc page (markdown content) |
|
|
55
57
|
|
|
56
58
|
### Write
|
|
57
59
|
|
|
@@ -83,6 +85,9 @@ All commands support `--help` for full flag details.
|
|
|
83
85
|
| `cup time status [--json]` | Show currently running timer |
|
|
84
86
|
| `cup time log <taskId> <duration> [-d desc] [--json]` | Log manual time entry (e.g. "2h", "30m") |
|
|
85
87
|
| `cup time list [--days n] [--task id] [--json]` | List recent time entries |
|
|
88
|
+
| `cup doc-create <title> [-c content] [--json]` | Create a new doc |
|
|
89
|
+
| `cup doc-page-create <docId> <name> [-c content] [--parent-page pageId] [--json]` | Create a page in a doc |
|
|
90
|
+
| `cup doc-page-edit <docId> <pageId> [--name text] [-c content] [--json]` | Edit a doc page |
|
|
86
91
|
| `cup config get <key>` / `cup config set <key> <value>` / `cup config path` | Manage CLI config (keys: apiToken, teamId, sprintFolderId) |
|
|
87
92
|
| `cup completion <shell>` | Shell completions (bash/zsh/fish) |
|
|
88
93
|
|
|
@@ -124,6 +129,11 @@ All commands support `--help` for full flag details.
|
|
|
124
129
|
| `cup link` + custom IDs | Both IDs must be the same type (both custom or both native). Mixing may not work |
|
|
125
130
|
| `cup link` | Link/unlink tasks (different from dependencies) |
|
|
126
131
|
| `cup attach` | Upload files to tasks. Attachments shown in `cup task` detail view |
|
|
132
|
+
| `cup docs` | List and search workspace docs by name |
|
|
133
|
+
| `cup doc` | View a doc page content (markdown) |
|
|
134
|
+
| `cup doc-create` | Create a new doc with optional initial content |
|
|
135
|
+
| `cup doc-page-create` | Create a page in a doc, optionally nested under a parent page |
|
|
136
|
+
| `cup doc-page-edit` | Edit a doc page name or content |
|
|
127
137
|
| `cup task` | Shows custom fields, checklists, attachments, dependencies, and linked tasks in detail view |
|
|
128
138
|
| `cup lists` | Discovers list IDs needed for `--list` and `cup create -l` |
|
|
129
139
|
| Errors | stderr with exit code 1 |
|
|
@@ -195,6 +205,20 @@ cup time list --days 7 # recent entries
|
|
|
195
205
|
cup delete abc123def --confirm # irreversible!
|
|
196
206
|
```
|
|
197
207
|
|
|
208
|
+
### Work with docs
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
cup docs # list all docs
|
|
212
|
+
cup docs "design" # search docs by name
|
|
213
|
+
cup doc <docId> <pageId> # view page content
|
|
214
|
+
cup doc-create "Architecture Notes" # create a doc
|
|
215
|
+
cup doc-create "Notes" -c "# Draft" # create with content
|
|
216
|
+
cup doc-page-create <docId> "New Section" # add page to doc
|
|
217
|
+
cup doc-page-create <docId> "Sub" --parent-page <pageId> # nested page
|
|
218
|
+
cup doc-page-edit <docId> <pageId> --name "Renamed" # rename page
|
|
219
|
+
cup doc-page-edit <docId> <pageId> -c "# Updated content" # edit content
|
|
220
|
+
```
|
|
221
|
+
|
|
198
222
|
### Discover workspace structure
|
|
199
223
|
|
|
200
224
|
```bash
|