Guidance for AI agents (and humans) working in this repo.
yuque-open-cli — a command-line interface for the Yuque (语雀) Open API, sibling of
yuque-mcp-server and built with the same
engineering conventions.
bin.ts → cli.ts (commander program, error → exit code)
└── commands/<domain>.ts (flags, confirmation, rendering)
└── client/api/<domain>.ts (typed calls, envelope unwrap)
└── client/http.ts (auth header, retry/backoff, YuqueError)
src/bin.ts— process entry point; installs quietEPIPEhandling and assignsprocess.exitCodefromrunCli.src/cli.ts— ownsbuildProgram, global options, domain registration, and the singlerunClierror-to-exit-code mapping.src/config.ts— resolves token (--token>YUQUE_TOKEN>YUQUE_PERSONAL_TOKEN), host (--host>YUQUE_HOST> default), and timeout (--timeout>YUQUE_TIMEOUT_MS> 30000);normalizeHoststrips trailing slashes and/api/v2.src/context.ts—getContextbuilds the per-action HTTP/output context; call it only inside actions so--helpnever requires a token.src/errors.ts— sole definition of CLI error classes, API status hints, and the stable exit-code contract.src/output.ts—printJson/printOk/printTable/printRecord, TTY color honoringNO_COLOR, and CJK/emoji display-width alignment.src/confirm.ts—confirmDestructiveenforces the interactive confirmation or--yesgate for destructive actions.src/commands/— Commander-facing domain modules; each exports oneregister<Domain>Commandsfunction and owns flags, action orchestration, and rendering.src/commands/auth.ts—registerAuthCommandsregisters connectivity and authenticated-account status commands.src/commands/book.ts—registerBookCommandsregisters book list/get/create/update/delete, pagination, validation, and delete confirmation.src/commands/doc.ts—registerDocCommandsregisters document CRUD, body/file handling, pagination, and version reads.src/commands/group.ts—registerGroupCommandsregisters group-member list/set/remove, pagination, roles, and removal confirmation.src/commands/note.ts—registerNoteCommandsregisters note list/get/create/update,has_morepagination, file-backed content, and note rendering.src/commands/resource.ts—registerResourceCommandsregisters structured-board get/create/update, locator validation, and text/JSON DSL handling.src/commands/search.ts—registerSearchCommandsregisters typed doc/book search and maps the book surface name to the API'srepovalue.src/commands/stats.ts—registerStatsCommandsregisters aggregate/member/book/doc statistics, filters, sorting, and page draining.src/commands/toc.ts—registerTocCommandsregisters TOC tree reads and cross-field-validated node updates.src/commands/user.ts—registerUserCommandsregisters current-user and user-group reads with role filtering and pagination.src/client/http.ts— the only HTTP exit; adds auth/base URL/timeout, normalizes errors, retries 429 for every method, and retries 502/503/504 or network failures only for GET.src/client/api/— thin domain wrappers:YuqueHttpis the first argument, typedApiEnveloperesponses are awaited, andres.datais returned.src/client/api/book.ts— book owner collection and id-or-namespace item API wrappers.src/client/api/doc.ts— document CRUD, global-id lookup, and published-version API wrappers.src/client/api/group.ts— group-member list/update/remove API wrappers.src/client/api/note.ts— note CRUD-without-delete wrappers, including the create and double-wrapped update response quirks.src/client/api/resource.ts— structured-board read/create/update wrappers using the public wire field names.src/client/api/search.ts— doc/repo search API wrapper.src/client/api/stats.ts— group aggregate and paged member/book/doc statistics API wrappers, including the live-array correction to the spec types.src/client/api/toc.ts— book TOC read/update API wrappers and update-body shape.src/client/api/user.ts— heartbeat, current-user, and user-groups API wrappers.src/client/book-ref.ts— parses a book reference as a numeric id orgroup/slugand produces the encoded/repos/...base path.src/client/paginate.ts— drains offset-paged or explicithas_moreendpoints for--all.src/client/types.gen.ts— generated fromspec/yuque-openapi.yaml; edit the spec and runnpm run gen:types, never edit this file directly.src/client/types.ts— thin compatibility adapter over the generated schemas; preserves public type names, live-API extensions, and index signatures for--jsonpass-through.spec/yuque-openapi.yaml— vendored upstream OpenAPI contract and source of truth for the supported operation surface.scripts/smoke-dist-cli.js— spawnsdist/bin.jsas an installed CLI would and checks version/help plus representative usage/auth exit codes.tests/commands/— mocked command-action unit tests for exact requests, rendering, validation, confirmation, and exit behavior.tests/client/— transport retry/error and book-reference unit contracts.tests/docs/— programmatic drift locks for README command coverage and this repository guide.tests/e2e/— built-binary functional tests: always-on mock-server wire assertions plus env-gated real-API coverage.tests/spec-coverage.test.ts— pins every operationId + method + path to registered leaf commands and locks the expected command list.
- Command surface is locked 1:1 against
spec/yuque-openapi.yamlbytests/spec-coverage.test.ts; adding/renaming a command or refreshing the spec requires a deliberate mapping update. - Both READMEs are locked by
tests/docs/command-surface-docs.test.tsfor the exact heading count and exact registered leaf-command set; keep README.md and README.zh-CN.md strictly isomorphic. - Exit codes are stable and scripts may rely on them:
0 success · 1 API/unknown error · 2 usage error · 3 auth error · 4 not found · 5 rate limited(src/errors.ts). - All HTTP goes through
src/client/http.ts; command handlers never call axios directly. - Human output goes through
src/output.ts;--jsonprints the complete unprojected payload returned by the API wrapper. npm run checkis the merge gate and matches CI: lint, format check, generated-type drift check, typecheck, coverage-enforced unit tests, one build, dist smoke, then functional e2e.- New or changed commands need always-on mock-server coverage in
tests/e2e/, not only unit coverage; seetests/e2e/README.mdfor the optional real-API gates.
- Add the typed wrapper in
src/client/api/<domain>.ts; takehttpfirst andreturn res.datato unwrapApiEnvelope. - Register the subcommand in
src/commands/<domain>.ts; action order is local flag validation (UsageError) →confirmDestructivefor destructive work, beforegetContext→getContext(cmd)→ API call →if (ctx.json) { printJson(raw); return; }with the complete wrapper result →printOk/printTable/printRecord. - For a new domain, add the import at the top of
src/cli.tsand call its register function insidebuildProgram. - Update the
OPERATION_TO_COMMANDSoperationId + method + path tuples andEXPECTED_LEAF_COMMANDSintests/spec-coverage.test.ts. - Update
README.mdandREADME.zh-CN.md: increment each commands-section heading count and add an isomorphic command-table row;tests/docs/command-surface-docs.test.tslocks both. - Regenerate the pinned help surface —
UPDATE_HELP_GOLDEN=1 npx vitest run tests/docs/help-surface.test.ts— and review thetests/docs/help-surface.golden.jsondiff; any new or changed flag must appear there deliberately. - Add
tests/commands/unit coverage with exact request assertions and the expected process exit code. - Add a
tests/e2e/mock-suite case with wire-level request assertions against the built binary. - Run
npm run checkand require the complete gate to pass.
- ESM is mandatory (
package.jsonhas"type": "module"); relative TypeScript imports use.jssuffixes because those are the emitted runtime paths. - Runtime support is Node >= 20, as pinned by
package.json#engines. - For new flag validation, use
Option.choicesfor enums or an argument parser that throwsUsageError; both map to exit 2 becausebuildProgramcallsexitOverridebefore registration and Commander copies that callback into subcommands (tests/commands/flag-validation.test.tslocks this). Put cross-field and semantic-required checks inside the action and throwUsageError; do not introduce newInvalidArgumentErrorparsers. - Destructive actions call
confirmDestructivebeforegetContext, so confirmation precedes auth/client setup; every such command supports--yes. - Convenience scripts:
npm run lint:fixfixes lint,npm run formatwrites formatting,npm run test:watchwatches unit tests,npm run test:coverageruns coverage, andnpm run devrunssrc/bin.tsdirectly through tsx. - Smoke and e2e exercise built
distoutput:npm run smoke:distandnpm run test:e2ebuild first, whilenpm run checkbuilds once then calls their:builtvariants. - Release work includes updating
CHANGELOG.md; npm'sprepublishOnlylifecycle runs the build before publication.