You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

@discountry/clickup-cli

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@discountry/clickup-cli - npm Package Compare versions

Comparing version
1.0.1
to
1.0.2
+1
-1
package.json
{
"name": "@discountry/clickup-cli",
"version": "1.0.1",
"version": "1.0.2",
"description": "A globally installable ClickUp CLI for tasks, comments, and docs.",

@@ -5,0 +5,0 @@ "type": "module",

@@ -14,3 +14,3 @@ # @discountry/clickup-cli

安装 Codex skill:
从当前仓库安装 Codex skill:

@@ -75,11 +75,20 @@ ```bash

clickup docs "API"
clickup docs "Sprint1" --parent-type FOLDER --limit 100
clickup doc abc123
clickup create-doc "Project Notes" --content "# Notes"
clickup page abc123 page456
clickup create-page abc123 "New Section" --content "Hello"
clickup edit-page abc123 page456 --name "Renamed" --content "# Updated"
clickup page abc123 page456 --content-format text/plain
clickup create-page abc123 "New Section" --content "Hello" --sub-title "Summary"
clickup edit-page abc123 page456 --name "Renamed" --sub-title "Updated" --content "# Updated" --content-edit-mode append
```
需要原始 API 输出时加 `--json`。
需要结构化命令输出时加 `--json`。
文档和页面命令:
- `docs` 支持 `--id`、`--creator`、`--deleted`、`--archived`、`--parent-id`、`--parent-type`、`--limit`。
- `doc` 支持 `--max-page-depth`。
- `page` 支持 `--content-format text/md|text/plain`。
- `create-page` 支持 `--sub-title`、`--parent-page-id`、`--content-format`。
- `edit-page` 支持 `--name`、`--sub-title`、`--content`、`--content-edit-mode`、`--content-format`。
## 开发

@@ -86,0 +95,0 @@

@@ -14,3 +14,3 @@ # @discountry/clickup-cli

Install the Codex skill:
Install the Codex skill from this repo:

@@ -75,11 +75,20 @@ ```bash

clickup docs "API"
clickup docs "Sprint1" --parent-type FOLDER --limit 100
clickup doc abc123
clickup create-doc "Project Notes" --content "# Notes"
clickup page abc123 page456
clickup create-page abc123 "New Section" --content "Hello"
clickup edit-page abc123 page456 --name "Renamed" --content "# Updated"
clickup page abc123 page456 --content-format text/plain
clickup create-page abc123 "New Section" --content "Hello" --sub-title "Summary"
clickup edit-page abc123 page456 --name "Renamed" --sub-title "Updated" --content "# Updated" --content-edit-mode append
```
Add `--json` for raw API output.
Add `--json` for structured command output.
Docs and pages:
- `docs` supports `--id`, `--creator`, `--deleted`, `--archived`, `--parent-id`, `--parent-type`, `--limit`.
- `doc` supports `--max-page-depth`.
- `page` supports `--content-format text/md|text/plain`.
- `create-page` supports `--sub-title`, `--parent-page-id`, `--content-format`.
- `edit-page` supports `--name`, `--sub-title`, `--content`, `--content-edit-mode`, `--content-format`.
## Development

@@ -86,0 +95,0 @@

@@ -11,2 +11,14 @@ export const OPTION_DEFINITIONS = [

{ key: 'name', names: ['--name', '-n'], type: 'string', description: 'New page name for `edit-page`' },
{ key: 'subTitle', names: ['--sub-title'], type: 'string', description: 'Page subtitle for `create-page` or `edit-page`' },
{ key: 'parentPageId', names: ['--parent-page-id'], type: 'string', description: 'Parent page id for `create-page`' },
{ key: 'contentFormat', names: ['--content-format'], type: 'string', description: 'Page content format: `text/md` or `text/plain`' },
{ key: 'contentEditMode', names: ['--content-edit-mode'], type: 'string', description: 'Page content update mode: `replace`, `append`, `prepend`' },
{ key: 'id', names: ['--id'], type: 'string', description: 'Doc id filter for `docs`' },
{ key: 'creator', names: ['--creator'], type: 'string', description: 'Creator id filter for `docs`' },
{ key: 'deleted', names: ['--deleted'], type: 'boolean', description: 'Include deleted docs in `docs`' },
{ key: 'archived', names: ['--archived'], type: 'boolean', description: 'Include archived docs in `docs`' },
{ key: 'parentId', names: ['--parent-id'], type: 'string', description: 'Parent doc id filter for `docs`' },
{ key: 'parentType', names: ['--parent-type'], type: 'string', description: 'Parent doc type filter for `docs`' },
{ key: 'limit', names: ['--limit'], type: 'string', description: 'Page size for paginated docs queries' },
{ key: 'maxPageDepth', names: ['--max-page-depth'], type: 'string', description: 'Maximum page depth for `doc` page listings' },
];

@@ -40,3 +52,3 @@

{ name: 'create-page', usage: 'create-page <doc_id> "title"', description: 'Create a page in a doc', section: 'Document Commands' },
{ name: 'edit-page', usage: 'edit-page <doc_id> <page_id>', description: 'Edit a page with --content and/or --name', section: 'Document Commands' },
{ name: 'edit-page', usage: 'edit-page <doc_id> <page_id>', description: 'Edit a page with --name, --sub-title, and/or --content', section: 'Document Commands' },
];

@@ -43,0 +55,0 @@

@@ -82,7 +82,16 @@ import { CliUsageError } from '../errors.mjs';

async docs({ args }) {
async docs({ args, options }) {
ensureArgRange(args, { max: 1 }, 'docs ["query"]');
const [query] = args;
const app = getApp();
const docs = await app.docService.searchDocs(query ? { query } : {});
const docs = await app.docService.searchDocs({
...(query ? { query } : {}),
...(options.id !== null ? { id: options.id } : {}),
...(options.creator !== null ? { creator: options.creator } : {}),
...(options.deleted ? { deleted: true } : {}),
...(options.archived ? { archived: true } : {}),
...(options.parentId !== null ? { parentId: options.parentId } : {}),
...(options.parentType !== null ? { parentType: options.parentType } : {}),
...(options.limit !== null ? { limit: options.limit } : {}),
});
const text =

@@ -330,3 +339,3 @@ docs.length === 0

async doc({ args }) {
async doc({ args, options }) {
ensureArgRange(args, { min: 1, max: 1 }, 'doc <doc_id>');

@@ -338,3 +347,5 @@ const docId = parseDocId(args[0]);

app.docService.getDoc(docId),
app.docService.getDocPageListing(docId),
app.docService.getDocPageListing(docId, {
...(options.maxPageDepth !== null ? { maxPageDepth: options.maxPageDepth } : {}),
}),
]);

@@ -347,3 +358,3 @@ return formatResult(

async page({ args }) {
async page({ args, options }) {
ensureArgRange(args, { min: 2, max: 2 }, 'page <doc_id> <page_id>');

@@ -355,3 +366,3 @@ const docId = parseDocId(args[0]);

const app = getApp();
const page = await app.docService.getPage(docId, pageId);
const page = await app.docService.getPage(docId, pageId, options.contentFormat ?? 'text/md');
return formatResult(page, formatPage(page));

@@ -369,2 +380,5 @@ },

content: options.content ?? undefined,
contentFormat: options.contentFormat ?? undefined,
parentPageId: options.parentPageId ?? undefined,
subTitle: options.subTitle ?? undefined,
});

@@ -380,6 +394,6 @@ return formatResult(page, `Page created: ${page.name}\nID: ${page.id}`);

requireArg(pageId, 'Page ID required.', 'edit-page <doc_id> <page_id>');
if (!options.content && !options.name) {
if (options.content === null && options.name === null && options.subTitle === null) {
throw new CliUsageError(
'At least --content or --name is required.',
'edit-page <doc_id> <page_id> [--content "content"] [--name "name"]'
'At least --content, --name, or --sub-title is required.',
'edit-page <doc_id> <page_id> [--content "content"] [--name "name"] [--sub-title "subtitle"]'
);

@@ -390,4 +404,7 @@ }

const page = await app.docService.editPage(docId, pageId, {
...(options.content ? { content: options.content } : {}),
...(options.name ? { name: options.name } : {}),
...(options.content !== null ? { content: options.content } : {}),
...(options.name !== null ? { name: options.name } : {}),
...(options.subTitle !== null ? { subTitle: options.subTitle } : {}),
...(options.contentEditMode !== null ? { contentEditMode: options.contentEditMode } : {}),
...(options.contentFormat !== null ? { contentFormat: options.contentFormat } : {}),
});

@@ -394,0 +411,0 @@

@@ -22,2 +22,14 @@ import { CliUsageError } from '../errors.mjs';

name: null,
subTitle: null,
parentPageId: null,
contentFormat: null,
contentEditMode: null,
id: null,
creator: null,
deleted: false,
archived: false,
parentId: null,
parentType: null,
limit: null,
maxPageDepth: null,
};

@@ -24,0 +36,0 @@ }

@@ -30,2 +30,3 @@ import { createApplication } from '../app.mjs';

fetchImpl = globalThis.fetch,
createApplicationImpl = createApplication,
stdout = process.stdout,

@@ -52,3 +53,3 @@ stderr = process.stderr,

if (!application) {
application = createApplication({ env, fetchImpl, now });
application = createApplicationImpl({ env, fetchImpl, now });
}

@@ -55,0 +56,0 @@ return application;

export function createDocService({ client, userService }) {
function buildQueryString(paramsBuilder) {
const params = new URLSearchParams();
paramsBuilder(params);
const query = params.toString();
return query ? `?${query}` : '';
}
async function listDocsPage(workspaceId, options = {}) {
const query = buildQueryString((params) => {
params.set('limit', String(options.limit ?? 100));
if (options.id) {
params.set('id', options.id);
}
if (options.creator) {
params.set('creator', String(options.creator));
}
if (options.deleted) {
params.set('deleted', 'true');
}
if (options.archived) {
params.set('archived', 'true');
}
if (options.parentId) {
params.set('parent_id', options.parentId);
}
if (options.parentType) {
params.set('parent_type', String(options.parentType));
}
if (options.cursor) {
// ClickUp documents the `cursor` parameter, but the docs endpoint currently
// paginates only when the deprecated `next_cursor` parameter is provided.
params.set('next_cursor', options.cursor);
}
});
return client.requestV3(`/workspaces/${workspaceId}/docs${query}`);
}
async function searchDocs(options = {}) {
const workspaceId = await userService.getWorkspaceId();
const params = new URLSearchParams();
if (options.query) {
params.set('query', options.query);
const docs = [];
const seenCursors = new Set();
let cursor = null;
while (true) {
const response = await listDocsPage(workspaceId, { ...options, cursor });
docs.push(...(response.docs ?? []));
cursor = response.next_cursor ?? null;
if (!cursor || seenCursors.has(cursor)) {
break;
}
seenCursors.add(cursor);
}
const query = params.toString();
const response = await client.requestV3(
`/workspaces/${workspaceId}/docs${query ? `?${query}` : ''}`
);
return response.docs ?? [];
if (!options.query) {
return docs;
}
const normalizedQuery = options.query.trim().toLowerCase();
if (!normalizedQuery) {
return docs;
}
return docs.filter((doc) => doc.name?.toLowerCase().includes(normalizedQuery));
}

@@ -20,7 +77,17 @@

async function getDocPageListing(docId) {
async function getDocPageListing(docId, options = {}) {
const workspaceId = await userService.getWorkspaceId();
const query = buildQueryString((params) => {
if (options.maxPageDepth !== undefined) {
params.set('max_page_depth', String(options.maxPageDepth));
}
});
const response = await client.requestV3(
`/workspaces/${workspaceId}/docs/${docId}/pageListing`
`/workspaces/${workspaceId}/docs/${docId}/page_listing${query}`
);
if (Array.isArray(response)) {
return response;
}
return response.pages ?? [];

@@ -31,9 +98,9 @@ }

const workspaceId = await userService.getWorkspaceId();
const query = buildQueryString((params) => {
if (contentFormat) {
params.set('content_format', contentFormat);
}
});
const page = await client.requestV3(
`/workspaces/${workspaceId}/docs/${docId}/pages/${pageId}`,
{
headers: {
Accept: contentFormat,
},
}
`/workspaces/${workspaceId}/docs/${docId}/pages/${pageId}${query}`
);

@@ -84,5 +151,6 @@

name,
...(options.content ? { content: options.content } : {}),
...(options.content !== undefined ? { content: options.content } : {}),
...(options.contentFormat ? { content_format: options.contentFormat } : {}),
...(options.parentPageId ? { parent_page_id: options.parentPageId } : {}),
...(options.subTitle ? { sub_title: options.subTitle } : {}),
...(options.subTitle !== undefined ? { sub_title: options.subTitle } : {}),
},

@@ -104,2 +172,8 @@ });

}
if (updates.contentEditMode !== undefined) {
body.content_edit_mode = updates.contentEditMode;
}
if (updates.contentFormat !== undefined) {
body.content_format = updates.contentFormat;
}

@@ -106,0 +180,0 @@ return client.requestV3(`/workspaces/${workspaceId}/docs/${docId}/pages/${pageId}`, {

@@ -68,2 +68,7 @@ function isBareIdentifier(value) {

const shareMatch = input.match(/\/d\/h\/([a-zA-Z0-9_-]+)(?:\/|$)/);
if (shareMatch) {
return shareMatch[1];
}
return input;

@@ -86,3 +91,13 @@ }

const docCenterMatch = input.match(/\/dc\/[a-zA-Z0-9_-]+\/([a-zA-Z0-9_-]+)(?:[/?#]|$)/);
if (docCenterMatch) {
return docCenterMatch[1];
}
const shareMatch = input.match(/\/d\/h\/[a-zA-Z0-9_-]+\/[^/]+\/([a-zA-Z0-9_-]+)(?:[/?#]|$)/);
if (shareMatch) {
return shareMatch[1];
}
return input;
}