@maimemo/memo-api is the official CLI tool for the Maimemo (墨墨) Open API. It provides a command-line interface—memo-api—for managing vocabulary notes, interpretations, phrases, notepads, and study records on the Maimemo platform.
- Language: TypeScript (strict mode, ES2022 target)
- Runtime: Node.js >= 18
- Module system: ESM (
"type": "module") - CLI framework: yargs v18
- Package manager: npm
- Single-package repository (not a monorepo)
src/
index.ts -- CLI entry point (yargs program assembly, middleware, global options)
client.ts -- HTTP client wrapping fetch (auth, timeout, error handling)
config.ts -- Config file reader (~/.config/memo-api.json)
output.ts -- Output formatters (columns with CJK alignment, JSON, errors)
stdin.ts -- Stdin reading & JSON parsing for piped input
commands/ -- One file per API resource (auth, note, interp, phrase, notepad, voc, study)
lib/ -- Shared utilities (auth, oidc, confirm, default-fields, helpers, mock, word-resolve)
types/ -- TypeScript type definitions mirroring protobuf API schemas
test/ -- Test files mirroring src/ structure
dist/ -- Compiled JS output (tsc, git-ignored)
bin/memo-api.js -- CLI binary entry (shebang, re-exports dist/index.js)
All API requests go to https://open.maimemo.com/open/. OIDC authentication requests go to https://accounts.maimemo.com/oidc. The CLI follows a memo-api <resource> <action> pattern.
npm install- Requires Node.js >= 18
- No
.envfile needed; the tool reads API tokens from~/.config/memo-api.json(fieldapiTokenor OIDCat/rt), theMAIMEMO_API_TOKENenvironment variable, or the-T/--tokenCLI flag
npm run dev # Watch mode — recompiles src/ to dist/ on changes
npm run cli # Run CLI directly from TypeScript source (tsx src/index.ts)
npm run build # One-off TypeScript compilation (tsc)- Always run
npm run buildbefore testing, as tests import fromsrc/via tsx - Before publishing:
npm run prepublishOnlyauto-runsnpm run build - To test the CLI as a user would, run
npm run cliwith flags, e.g.:npx tsx src/index.ts voc get --spelling=apple -T $TOKEN
npm test # Run all tests (node test runner + tsx loader)All tests use Node.js built-in node:test and node:assert/strict. HTTP calls in tests use undici.MockAgent to mock fetch responses — no real network calls are made.
Test file conventions:
- Unit tests:
test/*.test.ts,test/lib/*.test.ts - Integration tests:
test/commands/*.test.ts(mocked HTTP, full command logic) - End-to-end tests:
test/e2e/*.test.ts(real CLI execution viaexecFile) - Test files mirror
src/structure
Running specific tests:
node --import tsx --test test/output.test.ts # Single file
node --import tsx --test --test-name-pattern="printColumns" test/output.test.ts # Matching namesCoverage:
node --import tsx --experimental-test-coverage --test test/**/*.test.tsWriting new tests:
- Import from
node:test(describe,it,beforeEach,afterEach) - Import from
node:assert/strict - For HTTP mocking, use
new MockAgent()fromundici, call.disableNetConnect(), andsetGlobalDispatcher(mockAgent) - Redirect
process.stdout.write/process.stderr.writeto capture and assert output - Mock
process.exit(the codebase calls it directly on errors) by replacing it with a function that throws - Always restore mocked globals in
afterEach
- TypeScript strict mode —
strict: true,noUncheckedIndexedAccess: true - Target: ES2022
- Module: NodeNext (
.jsextensions on relative imports, even in.tsfiles) - No linter or formatter configured — rely on
tscfor correctness - File naming: kebab-case (
word-resolve.ts,default-fields.ts) - Import ordering: external packages first (
yargs,chalk), then internal (./client.js,./output.js) - Error messages: Chinese, written to stderr via
printError(message) - Output: JSON to stdout via
printJson(), tab-separated columns viaprintColumns()with CJK-aware alignment (string-width) - Token injection: Token is resolved by global middleware in
src/index.ts(now async) and injected intoargvvia(argv as any).token. Handlers access it via(argv as any).token. Token resolution is skipped for--help,completion, andauth logincommands.
memo-api [global options] <resource> <action> [arguments...]
Resources:
auth login / whoami
note list / create / update / delete
interp list / create / update / delete (alias: interpretation)
phrase list / create / update / delete
notepad list / get / create / update / delete
voc get / query (alias: vocabulary)
study progress / today / records / add / advance
Global options:
-T, --token API Token
-O, --output Output format: json | column (default: column)
--no-color Disable colored output
--verbose Show request details and raw response
-H, --no-header Skip header row in column output
--fields <fields> Select fields (comma-separated; + to add, - to remove)
Stdin JSON mode: Any command that accepts pipe input will bypass CLI argument validation if valid JSON is received on stdin. This allows programmatic usage like:
echo '{"spelling":"apple"}' | memo-api voc get- Build:
npm run build→tsc→ outputs todist/ - Publish:
npm publish(auto-runsnpm run buildviaprepublishOnly) - Published files: only
dist/andbin/(specified inpackage.json.files) - CI/CD: No CI pipeline is configured in this repository
- Create
src/types/<resource>.tswith TypeScript types for request/response shapes (skip for auth — uses openid-client types) - Create
src/commands/<resource>.tsexporting aCommandModulewith subcommands - Add default column fields in
src/lib/default-fields.ts(skip for auth — whoami outputs uid/name directly) - Register the command in
src/index.tsviaprogram.command() - Create tests in
test/commands/<resource>.test.tsusingMockAgent
- Before making changes: Read the relevant plan documents in
docs/plans/(currentlycli.mdandproject.md) - After making changes: Sync updates back to the plan documents to keep them accurate
- Test coverage: Every change must be covered by tests. Add or update test files in
test/mirroring thesrc/structure
- Build must pass:
npm run build - All tests must pass:
npm test - No formal linting step, but TypeScript compilation must succeed without errors
- Keep the KISS principle — one file, one responsibility, no DI containers or frameworks
- No database — the tool is a stateless CLI client that calls a remote HTTP API
- Config file:
~/.config/memo-api.jsonwith{ "apiToken": "...", "at": {...}, "rt": {...} } - Timeout: HTTP requests time out after 30 seconds
- Token priority:
-Tflag >MAIMEMO_API_TOKENenv var > configapiToken> config OIDCat(auto-refreshed) - CJK support: Column alignment uses
string-width(not.length) for accurate CJK character width