Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## 1.1.0

Knowledge bases are now `book` across the CLI surface, aligning with Yuque's
own terminology (知识库 = book, matching the API's `V2Book`/`book_id`). The
`repo` command group from 1.0.0 (released the same day) is replaced by `book`:

- `repo list/get/create/update/delete` → `book list/get/create/update/delete`;
`<repo>` arguments are now `<book>`.
- `search --type` takes `doc|book` (`repo` still accepted as a compatibility
alias; the wire value is unchanged).
- Help text and READMEs updated accordingly. HTTP paths (`/repos/...`) and env
variables (`YUQUE_E2E_REPO`) are unchanged.

## 1.0.0

Initial release of `yuque-open-cli` — a spec-driven, scriptable command-line
Expand Down
36 changes: 18 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Once authenticated, your knowledge base is one command away:
yuque search "canary release" --type doc # find that doc you half-remember
yuque doc get team/handbook onboarding > onboarding.md
yuque doc create team/notes --title "Weekly sync" --body-file weekly.md
yuque repo list my-team --group --all --json | jq '.[].name'
yuque book list my-team --group --all --json | jq '.[].name'
```

## Quick Start
Expand All @@ -42,7 +42,7 @@ YUQUE_TOKEN=YOUR_TOKEN npx yuque-open-cli auth status

</details>

**3. Start exploring** — `yuque repo list your-login`, then `yuque doc list <repo>`.
**3. Start exploring** — `yuque book list your-login`, then `yuque doc list <book>`.

## Configuration

Expand All @@ -64,30 +64,30 @@ Each command maps to the [Yuque OpenAPI](https://www.yuque.com/yuque/developer/a
| | `auth status` | Show who you are signed in as |
| **User** | `user info` | Show the authenticated user |
| | `user groups <user>` | List groups a user belongs to |
| **Search** | `search <query>` | Search docs or repos, with paging |
| **Repos** | `repo list <login>` | List repos (知识库) of a user or `--group` |
| | `repo get <repo>` | Show a repo by id or `owner/slug` |
| | `repo create <login>` | Create a repo |
| | `repo update <repo>` | Update name, slug, description, visibility, or TOC |
| | `repo delete <repo>` | Delete a repo — asks for confirmation |
| **Docs** | `doc list <repo>` | List docs in a repo, `--all` drains paging |
| | `doc get <repo> <doc>` | Print a doc's markdown body; also takes a global `<doc-id>`, `--meta` for metadata |
| | `doc create <repo>` | Create a doc from `--body` or `--body-file` |
| | `doc update <repo> <doc>` | Update a doc's body or metadata |
| | `doc delete <repo> <doc>` | Delete a doc — asks for confirmation |
| **Search** | `search <query>` | Search docs or books, with paging |
| **Books** | `book list <login>` | List books (知识库) of a user or `--group` |
| | `book get <book>` | Show a book by id or `owner/slug` |
| | `book create <login>` | Create a book |
| | `book update <book>` | Update name, slug, description, visibility, or TOC |
| | `book delete <book>` | Delete a book — asks for confirmation |
| **Docs** | `doc list <book>` | List docs in a book, `--all` drains paging |
| | `doc get <book> <doc>` | Print a doc's markdown body; also takes a global `<doc-id>`, `--meta` for metadata |
| | `doc create <book>` | Create a doc from `--body` or `--body-file` |
| | `doc update <book> <doc>` | Update a doc's body or metadata |
| | `doc delete <book> <doc>` | Delete a doc — asks for confirmation |
| | `doc versions <doc-id>` | List a doc's version history |
| | `doc version <version-id>` | Show one version's content |
| **TOC** | `toc get <repo>` | Print a repo's table of contents as a tree |
| | `toc update <repo>` | Append, prepend, edit, or remove a TOC node |
| **TOC** | `toc get <book>` | Print a book's table of contents as a tree |
| | `toc update <book>` | Append, prepend, edit, or remove a TOC node |
| **Groups** | `group members <login>` | List members of a group |
| | `group member set <login> <user>` | Add a member or change their role |
| | `group member remove <login> <user>` | Remove a member — asks for confirmation |
| **Stats** | `stats group <login>` | Group-level statistics |
| | `stats members <login>` | Per-member statistics |
| | `stats books <login>` | Per-repo statistics |
| | `stats books <login>` | Per-book statistics |
| | `stats docs <login>` | Per-doc statistics |

Repos accept either a numeric id or an `owner/slug` namespace everywhere. Run `yuque <command> --help` for all flags.
Books (知识库) accept either a numeric id or an `owner/slug` namespace everywhere. Run `yuque <command> --help` for all flags.

## Output & scripting

Expand Down Expand Up @@ -121,7 +121,7 @@ Colors are disabled automatically when piping, or force-off with `NO_COLOR=1`. R
| `A Yuque API token is required` | Set `YUQUE_TOKEN=YOUR_TOKEN` or pass `--token=YOUR_TOKEN` |
| `token invalid or expired` (exit `3`) | [Regenerate the token](https://www.yuque.com/settings/tokens) or fix `YUQUE_TOKEN` / `--token` |
| `rate limited by the Yuque API` (exit `5`) | The CLI retries automatically; slow down `--all` loops |
| `the requested resource does not exist` (exit `4`) | Check the repo id / `owner/slug` namespace and the doc slug |
| `the requested resource does not exist` (exit `4`) | Check the book id / `owner/slug` namespace and the doc slug |
| `npm` command not found | Install [Node.js](https://nodejs.org/) v20 or later |

## Development
Expand Down
28 changes: 14 additions & 14 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
yuque search "灰度发布" --type doc # 找回那篇只记得大概的文档
yuque doc get team/handbook onboarding > onboarding.md
yuque doc create team/notes --title "周会纪要" --body-file weekly.md
yuque repo list my-team --group --all --json | jq '.[].name'
yuque book list my-team --group --all --json | jq '.[].name'
```

## 快速开始
Expand All @@ -42,7 +42,7 @@ YUQUE_TOKEN=YOUR_TOKEN npx yuque-open-cli auth status

</details>

**第三步:开始探索** —— 先 `yuque repo list your-login`,再 `yuque doc list <repo>`。
**第三步:开始探索** —— 先 `yuque book list your-login`,再 `yuque doc list <book>`。

## 配置

Expand All @@ -65,20 +65,20 @@ YUQUE_TOKEN=YOUR_TOKEN npx yuque-open-cli auth status
| **用户** | `user info` | 查看当前 Token 对应的用户 |
| | `user groups <user>` | 列出用户加入的团队 |
| **搜索** | `search <query>` | 搜索文档或知识库,支持分页 |
| **知识库** | `repo list <login>` | 列出用户或团队(`--group`)的知识库 |
| | `repo get <repo>` | 按 id 或 `owner/slug` 查看知识库 |
| | `repo create <login>` | 创建知识库 |
| | `repo update <repo>` | 更新名称、路径、简介、可见性或目录 |
| | `repo delete <repo>` | 删除知识库 —— 需要确认 |
| **文档** | `doc list <repo>` | 列出知识库中的文档,`--all` 拉取全量 |
| | `doc get <repo> <doc>` | 输出文档 markdown 正文;也接受全局 `<doc-id>`,`--meta` 查看元信息 |
| | `doc create <repo>` | 从 `--body` 或 `--body-file` 创建文档 |
| | `doc update <repo> <doc>` | 更新文档正文或元信息 |
| | `doc delete <repo> <doc>` | 删除文档 —— 需要确认 |
| **知识库** | `book list <login>` | 列出用户或团队(`--group`)的知识库 |
| | `book get <book>` | 按 id 或 `owner/slug` 查看知识库 |
| | `book create <login>` | 创建知识库 |
| | `book update <book>` | 更新名称、路径、简介、可见性或目录 |
| | `book delete <book>` | 删除知识库 —— 需要确认 |
| **文档** | `doc list <book>` | 列出知识库中的文档,`--all` 拉取全量 |
| | `doc get <book> <doc>` | 输出文档 markdown 正文;也接受全局 `<doc-id>`,`--meta` 查看元信息 |
| | `doc create <book>` | 从 `--body` 或 `--body-file` 创建文档 |
| | `doc update <book> <doc>` | 更新文档正文或元信息 |
| | `doc delete <book> <doc>` | 删除文档 —— 需要确认 |
| | `doc versions <doc-id>` | 列出文档的版本历史 |
| | `doc version <version-id>` | 查看某个版本的内容 |
| **目录** | `toc get <repo>` | 以树形输出知识库目录 |
| | `toc update <repo>` | 追加、头插、编辑或删除目录节点 |
| **目录** | `toc get <book>` | 以树形输出知识库目录 |
| | `toc update <book>` | 追加、头插、编辑或删除目录节点 |
| **团队** | `group members <login>` | 列出团队成员 |
| | `group member set <login> <user>` | 添加成员或调整角色 |
| | `group member remove <login> <user>` | 移除成员 —— 需要确认 |
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "yuque-open-cli",
"version": "1.0.0",
"version": "1.1.0",
"description": "Command-line interface for Yuque (语雀) — browse, edit, and manage your knowledge base from the terminal",
"type": "module",
"main": "dist/cli.js",
Expand Down
4 changes: 2 additions & 2 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { CliError, YuqueError, exitCodeForStatus } from './errors.js';
import { registerAuthCommands } from './commands/auth.js';
import { registerUserCommands } from './commands/user.js';
import { registerSearchCommands } from './commands/search.js';
import { registerRepoCommands } from './commands/repo.js';
import { registerBookCommands } from './commands/book.js';
import { registerDocCommands } from './commands/doc.js';
import { registerTocCommands } from './commands/toc.js';
import { registerGroupCommands } from './commands/group.js';
Expand Down Expand Up @@ -34,7 +34,7 @@ export function buildProgram(): Command {
registerAuthCommands(program);
registerUserCommands(program);
registerSearchCommands(program);
registerRepoCommands(program);
registerBookCommands(program);
registerDocCommands(program);
registerTocCommands(program);
registerGroupCommands(program);
Expand Down
44 changes: 22 additions & 22 deletions src/client/api/repo.ts → src/client/api/book.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import type { YuqueHttp } from '../http.js';
import { repoBasePath, type RepoRef } from '../repo-ref.js';
import { bookBasePath, type BookRef } from '../book-ref.js';
import type { ApiEnvelope, V2Book, V2BookDetail } from '../types.js';

/** The spec exposes list/create under both /users/:login and /groups/:login. */
export type RepoOwner = 'user' | 'group';
export type BookOwner = 'user' | 'group';

function ownerReposPath(owner: RepoOwner, login: string): string {
function ownerBooksPath(owner: BookOwner, login: string): string {
return `/${owner === 'group' ? 'groups' : 'users'}/${encodeURIComponent(login)}/repos`;
}

export interface ListReposOptions {
export interface ListBooksOptions {
offset?: number;
limit?: number;
/** Spec enum: Book | Design. Omit for no server-side filter. */
Expand All @@ -18,62 +18,62 @@ export interface ListReposOptions {
filterByAbility?: string;
}

export async function listRepos(
export async function listBooks(
http: YuqueHttp,
owner: RepoOwner,
owner: BookOwner,
login: string,
options: ListReposOptions = {}
options: ListBooksOptions = {}
): Promise<V2Book[]> {
const params: Record<string, unknown> = {};
if (options.offset !== undefined) params.offset = options.offset;
if (options.limit !== undefined) params.limit = options.limit;
if (options.type !== undefined) params.type = options.type;
if (options.filterByAbility !== undefined) params.filterByAbility = options.filterByAbility;
const res = await http.get<ApiEnvelope<V2Book[]>>(ownerReposPath(owner, login), params);
const res = await http.get<ApiEnvelope<V2Book[]>>(ownerBooksPath(owner, login), params);
return res.data;
}

export async function getRepo(http: YuqueHttp, ref: RepoRef): Promise<V2BookDetail> {
const res = await http.get<ApiEnvelope<V2BookDetail>>(repoBasePath(ref));
export async function getBook(http: YuqueHttp, ref: BookRef): Promise<V2BookDetail> {
const res = await http.get<ApiEnvelope<V2BookDetail>>(bookBasePath(ref));
return res.data;
}

export interface CreateRepoBody {
export interface CreateBookBody {
name: string;
slug: string;
description?: string;
public?: number;
enhancedPrivacy?: boolean;
}

export async function createRepo(
export async function createBook(
http: YuqueHttp,
owner: RepoOwner,
owner: BookOwner,
login: string,
body: CreateRepoBody
body: CreateBookBody
): Promise<V2Book> {
const res = await http.post<ApiEnvelope<V2Book>>(ownerReposPath(owner, login), body);
const res = await http.post<ApiEnvelope<V2Book>>(ownerBooksPath(owner, login), body);
return res.data;
}

export interface UpdateRepoBody {
export interface UpdateBookBody {
name?: string;
slug?: string;
description?: string;
public?: number;
toc?: string;
}

export async function updateRepo(
export async function updateBook(
http: YuqueHttp,
ref: RepoRef,
body: UpdateRepoBody
ref: BookRef,
body: UpdateBookBody
): Promise<V2Book> {
const res = await http.put<ApiEnvelope<V2Book>>(repoBasePath(ref), body);
const res = await http.put<ApiEnvelope<V2Book>>(bookBasePath(ref), body);
return res.data;
}

export async function deleteRepo(http: YuqueHttp, ref: RepoRef): Promise<V2Book> {
const res = await http.delete<ApiEnvelope<V2Book>>(repoBasePath(ref));
export async function deleteBook(http: YuqueHttp, ref: BookRef): Promise<V2Book> {
const res = await http.delete<ApiEnvelope<V2Book>>(bookBasePath(ref));
return res.data;
}
Loading
Loading