43 Commits

Author SHA1 Message Date
Qi Qi a48f66f307 fix(auth): honor NOTION_TOKEN in auth status and doctor (#62) 2026-07-19 07:15:43 +08:00
傅洋 d428bd87d1 docs: translate non-English content to English (#59)
* docs: translate CHANGELOG.md (Korean) and DISTRIBUTION.md (Chinese) to English

* fix(lint): remove unused pageDeleteCmd variable

---------

Co-authored-by: fuyang <fuyang@nemovideo.ai>
2026-06-23 15:14:11 +08:00
傅洋 8e4cd813a2 feat(page): add 'page markdown' and 'page set-markdown' (#47)
Closes #37. Wraps the two 2025 Notion endpoints that do server-side
markdown I/O on a full page:

  GET   /v1/pages/:id/markdown     →  notion page markdown <id>
  PATCH /v1/pages/:id/markdown     →  notion page set-markdown <id>

### page markdown

Prints a page's content as markdown. Strictly better than `block list
--md` for page-level dumps: the server handles toggles, columns, synced
blocks, and databases-as-pages uniformly and always matches what the
Notion UI shows.

  notion page markdown <id>
  notion page markdown <id> > page.md
  notion page markdown <id> --out page.md
  notion page markdown <id> --format json   # full response incl. truncated flag

Surfaces Notion's 'truncated' flag and 'unknown_block_ids' as stderr
notes in default output so silent partial renders don't go unnoticed.

### page set-markdown

Updates a page from markdown in one API call. Supports all four
mutation modes Notion's PATCH endpoint accepts:

  --replace         (default) replace_content: overwrite whole page
  --append          insert_content at end
  --after <anchor>  insert_content after ellipsis anchor ("start...end")
  --range <anchor>  replace_content_range bounded by ellipsis anchor

  --file <path>     (use '-' for stdin)
  --text <str>      inline markdown
  --allow-deleting-content   pass allow flag for destructive modes

Because Notion handles the full markdown → blocks conversion server-side,
this is the cleanest way to write long documents — the 100-children
batching (#21) doesn't apply to set-markdown.

### Implementation

Two pure helpers isolate the fiddly bits for unit testing without network:
- buildSetMarkdownBody: produces the correct envelope for each mode;
  rejects ambiguous flag combinations up front.
- readMarkdownSource: picks the source (--file / --text / stdin); same
  mutual-exclusion rules as the rest of the CLI.

### Tests

- Exhaustive buildSetMarkdownBody coverage across all four modes +
  allow_deleting_content + multi-mode rejection.
- readMarkdownSource: required / conflict / file / stdin / file-missing.

### Smoke

Verified end-to-end against a real workspace: replace via --file,
append --text, markdown read with --out, stdin replace, and multi-mode
rejection all behave as documented.
2026-04-30 14:12:25 +08:00
傅洋 cd7512d776 feat(page): add 'page property' that auto-paginates relation / rollup / rich_text (#46)
Closes #38. Fixes silent truncation of property values with >25 items.

Problem:
  'notion page view' and 'notion page props' both call GET /v1/pages/:id,
  which returns every property in a single response but truncates values
  at 25 items. A relation pointing to 40 pages silently loses 15.

Fix:
  New 'page property' subcommand wraps GET /v1/pages/:id/properties/:id
  and walks next_cursor until has_more is false, merging results[] into
  a single response.

Interface:
  notion page property <page-id> <property-id>
  notion page property <page-id> --name "References"   # resolve id by name
  notion page property <page-id> <property-id> --page-size 50

Implementation:
  - fetchPagePropertyAllPages handles both paginated ('list' object) and
    non-paginated ('property_item' object) response shapes.
  - findPropertyIDByName scans page.properties map for --name lookups;
    error message includes the list of available names.
  - renderPageProperty prints a human-friendly summary; summarizePropertyItem
    covers relation / rich_text / title / people / number types with a
    JSON fallback for anything else.

Tests:
  - TestFetchPagePropertyAllPages_FollowsCursors spins up a 3-page
    httptest server and asserts all 4 items are merged with has_more=false.
  - Passthrough test for 'property_item' (non-paginated) shape.
  - findPropertyIDByName: found / missing / malformed page.

Smoke-tested against a real database: --name resolves correctly, positional
property-id works, 'title' non-paginated path returns the expected single
item, and both conflict / not-found errors fire cleanly.
2026-04-30 14:09:02 +08:00
傅洋 89f9e909fe feat(block): 'block update' accepts --file markdown and --markdown for inline (#45)
Closes #36. Brings 'block update' in line with 'block append' / 'block
insert', which have supported markdown input since earlier releases.

New flags:
  --file <path>   read markdown from a file; must parse to exactly one
                  block. Fails fast on type mismatch because Notion's
                  PATCH /v1/blocks/:id cannot change a block's type.
                  Code-fence language aliases from #22 apply here, so
                  --file with a 'ts' / 'sh' / 'yml' fence still works.
  --markdown      when combined with --text, runs the inline parser
                  (bold / italic / code / strikethrough / links).

Mutual-exclusion and validation:
  - --text and --file are mutually exclusive.
  - --markdown is implied for --file; supplying both is rejected.
  - At least one of --text / --file is required.
  - --file preserves type-specific inner fields (code.language, to_do.
    checked, etc.) from the parsed block.

Logic extracted to buildUpdateBlockBody() so it's unit-testable without
network. Tests cover plain text, --markdown annotations, --file parsing
with language normalization, type mismatch, multi-block rejection, and
missing-file errors.

Smoke-tested end-to-end: paragraph → plain update, paragraph → markdown
update with links/bold, code block → sh-alias update normalized to
'shell', and the type-mismatch guard fires correctly.
2026-04-30 14:06:05 +08:00
傅洋 5ec113f07f feat(comment): add 'comment update' and 'comment delete' (#44)
Wraps two endpoints added in Notion's 2025 API that the CLI didn't yet
expose:

- PATCH /v1/comments/:id  → 'notion comment update <id> --text ...'
- DELETE /v1/comments/:id → 'notion comment delete <id> [<id> ...]'

'update' reuses the existing buildCommentRichText helper so the
--mention-user flag works the same way as 'comment add'.

'delete' follows the 'block delete' pattern: variadic args, per-id
error isolation, and a summary line (N comment(s) deleted). When the
target id is the anchor of a discussion, Notion removes the whole
thread; deleting a reply removes just that one comment — this is
documented in the Long help.

Two new client methods (UpdateComment, DeleteComment) keep the API
plumbing in internal/client alongside AddComment.

Smoke-tested against a real workspace: create comment → update text →
verify new text via 'comment get' → delete → raw DELETE on the same id
returns object_not_found as expected (the synchronous GET path is
eventually consistent server-side, which is a Notion behavior, not a
CLI issue).

Closes #33
2026-04-30 14:03:20 +08:00
傅洋 ac8850fa42 feat(page): make 'archive' canonical, keep 'delete'/'trash' as aliases (#43)
Three changes to de-scare the soft-delete command:

- 'page archive <id>' is the canonical form. This is the term used in
  Notion's UI and in the 2025 API (where the field is renamed in_trash,
  kept backwards-compatible with 'archived').
- 'page delete' and 'page trash' are now cobra aliases that resolve to
  the same RunE. Existing scripts pinned on 'page delete' keep working
  unchanged; the legacy variable pageDeleteCmd also still exists as an
  assignment to pageArchiveCmd for any external code path.
- Help text now makes the soft-delete nature explicit and the success
  line tells the user how to undo ('run notion page restore').

The page restore Long was updated to say 'reverse of archive / delete /
trash' for symmetry.

Aliases are hidden from 'notion page --help' by cobra, so new users see
one canonical command; they surface on 'notion page <alias> --help'.

Closes #35
2026-04-30 14:00:28 +08:00
傅洋 6bb0b0dc01 feat(file): add 'file get <upload-id>' for retrieving a single file upload (#42)
Wraps GET /v1/file_uploads/:id — the dedicated endpoint for checking the
status of a file upload (pending / uploaded / expired), recovering its
content_type/size, or grabbing an existing file_upload id for re-use
inside a block.

Previously this endpoint was only reachable via:
    notion api GET /v1/file_uploads/<id>

The new command prints a human-friendly summary by default and passes
through the raw JSON with --format json.

Tests cover the full-field render and the minimal 'pending' render,
asserting only on output that goes through fmt.Println — keys rendered
via fatih/color bypass our stdout pipe capture (same pattern as the
existing search table tests).

Closes #34
2026-04-30 13:58:17 +08:00
傅洋 087048d192 feat(auth,client): surface integration type and guide past root-page error (#32)
The Notion API returns a technically-accurate but hard-to-action
validation error when an internal integration tries to create a page at
the workspace root:

    validation_error: Provide a parent.page_id or parent.database_id
    parameter to create a page, or use a public integration with
    insert_content capability. Internal integrations aren't owned by a
    single user, so creating workspace-level private pages is not
    supported.

Two small improvements so users don't have to parse that paragraph:

1. `notion auth status` and `notion auth doctor` now derive the
   integration type from bot.owner.type ("workspace" → internal,
   "user" → public) and print it. For internal integrations a follow-up
   line reminds the user they must share a parent page first.

2. client.errorHint matches the internal-integration validation_error
   signature and prints a concrete workaround with the exact next command:

       → Internal integrations can't create pages at the workspace root.
          Workaround: create (or pick) a parent page in the Notion UI,
          share it with this integration, then pass its ID as the parent:
              notion page create <shared-page-id> --title "..."
          To list pages shared with your integration: notion page list

Detection code is small (detectIntegrationType in cmd/auth.go) and
defensive: unknown or missing owner shape returns "" so pre-existing
response fixtures that don't include bot.owner still render fine.

Closes #25
2026-04-30 13:00:38 +08:00
傅洋 b12fca8a7e feat(file): accept stdin and http(s) URLs as upload sources (#31)
Previously `notion file upload` only worked with a local filesystem
path. To upload something that lived behind a URL (chat attachment, CI
artifact, object-storage direct link) you had to curl to a tmp file
first. To pipe bytes into Notion you couldn't at all.

This change introduces a fileSource abstraction and three loaders:

  <path>             local file (existing behavior, unchanged)
  -                  read from stdin (requires --name)
  http(s)://...      HTTP GET, follows redirects, reads Content-Type
                     and Content-Disposition filename if present

A new --name flag overrides the filename in all three modes. It is
required for stdin (we can't guess) and recommended for URLs whose
path segment is opaque (e.g. /download?id=123).

Deliberately scoped out of this PR (use a curl-pipe for these):
  - custom auth headers for the URL loader
  - chunked / streaming upload (single-part, same as before)

Tests use httptest for URL loading, os.Pipe to redirect stdin, and
integration tests wire uploadFromAny through the existing fileUploadAPI
mock to prove every source funnels into the same two-step upload flow.

Closes #26
2026-04-30 13:00:14 +08:00
傅洋 7d0d5f8b77 feat(block): --image-file / --image-upload (and friends) for append/insert (#30)
Before this change there was no first-class CLI path to turn an uploaded
file into a block. Users had to run `notion file upload`, capture the
id, then hand-craft a PATCH /v1/blocks/<id>/children JSON body and send
it via `notion api`. The existing --image-url only accepted external
http(s) links.

This PR adds a symmetric triple for every media type Notion supports:

  --image-url / --image-file / --image-upload
  --file-url  / --file-file  / --file-upload
  --video-url / --video-file / --video-upload
  --audio-url / --audio-file / --audio-upload
  --pdf-url   / --pdf-file   / --pdf-upload

Semantics:
  --<kind>-url     http(s) external URL           → block.<kind>.type = external
  --<kind>-file    local path                     → upload then embed
  --<kind>-upload  existing file_upload id        → embed directly

Flags are mutually exclusive with each other and with --file/positional
text. --caption works with any of them.

Implementation lives in cmd/media_source.go:
  - registerMediaFlags: adds the 15 flags + --caption to a cobra command.
  - resolveMediaSource: validates, returns the single active source.
  - mediaSource.Build: performs the upload (for --*-file) and assembles
    the final block map.

The legacy --image-url flag and its validator/builder are now thin
back-compat wrappers around the new helpers, so existing tests and
usage keep working byte-for-byte.

Closes #23
2026-04-30 12:59:43 +08:00
傅洋 7abb018a88 feat(block): auto-batch >100 children and split oversize rich_text (#29)
Notion rejects PATCH /v1/blocks/<id>/children with either:
  - body.children.length > 100, or
  - body.children[N].<type>.rich_text[0].text.content.length > 2000

Both limits are fixed and known at parse time, so the CLI can absorb
them. Previously even a moderately long markdown file (a postmortem, a
design doc with one ~2KB stack trace, …) required manual splitting.

New helpers in cmd/block_limits.go:
  - chunkChildren: slice the children list into groups of ≤100.
  - appendChildrenBatched: PATCH each chunk sequentially, preserving
    order. The `after` anchor is attached to the FIRST batch only,
    because Notion won't let subsequent inserts reference a block from
    the same transaction. Progress is printed to stderr (not stdout, so
    --format json still pipes cleanly).
  - handleOversizedBlocks + parseOversizeMode: split oversize code
    blocks on newline boundaries by default, with `--on-oversize=
    truncate|fail` escape hatches. Code language and to_do checked
    state are preserved across split chunks.

Partial-failure message now tells the user exactly which batch failed
and how many blocks were already written:

    batch 2/3 failed after writing 100 block(s): <api error>

Wired into both `block append` and `block insert`.

Tests cover: chunk sizes, oversize split/truncate/fail modes, newline
boundary preference, order preservation across batches, `after` anchor
placement, partial-failure error text, and an end-to-end pipeline with a
150-bullet + 3000-char-code-block document.

Closes #21
2026-04-30 12:58:56 +08:00
傅洋 315a2aaf0e feat(block): normalize markdown code-fence aliases to Notion's enum (#28)
Markdown / LLM output routinely uses short language labels (`ts`, `sh`,
`yml`, `py`, …) that the Notion API rejects because code.language is a
fixed enum. Previously every such fence hard-failed the whole PATCH with:

    body.children[N].code.language should be "abap", ..., "typescript",
    "yaml", or "java/c/c++/c#", instead was "ts".

Introduce `normalizeCodeLanguage`:
  - passes through any value that's already in the Notion enum,
  - maps ~50 common aliases (ts→typescript, sh→shell, yml→yaml, py→python,
    cpp→c++, cs→c#, dockerfile→docker, md→markdown, proto→protobuf, …),
  - for unknown values, falls back to 'plain text' and emits a one-line
    stderr warning so a single unrecognized fence can't break a long doc.

Applied to both markdown parsing (`notion block append --file`) and the
`--lang` flag on inline append/insert, so CLI-typed aliases like
`--lang ts` also work.

Closes #22
2026-04-30 12:58:22 +08:00
傅洋 5d37c1b145 fix(api): polish path, body, and help for the escape-hatch command (#27)
- Auto-prefix /v1 when a path starts with / but not /v1/, with a stderr
  note so power users see the rewrite. Previously `notion api GET /users/me`
  returned an opaque 'invalid_request_url' error.
- Support `--body @file.json` (curl-style) and `--body -` for explicit
  stdin reads. The existing implicit stdin fallback for POST/PATCH/PUT
  still works.
- Reject GET requests with a body up-front instead of silently dropping it.
- Route PATCH through c.Patch() unconditionally (previously it hit c.Post
  first and was overwritten, which made error paths confusing).
- Drop the misleading positional-arg example from --help; the flag was
  the only supported form.

Closes #24
2026-04-30 12:58:19 +08:00
windzu 820f13a3bd feat(block): add --image-url flag for external image blocks (#19)
Thanks @windzu!
2026-04-21 15:51:32 +08:00
Max Mannstein df76bac34e feat(comment): add --mention-user flag for real Notion user mentions (#17)
Closes #16
2026-04-14 20:53:57 +08:00
Fourier 13f9ed685c Revert "feat(comment): add --mention-user flag for real Notion user mentions"
This reverts commit 47dc9ead38.
2026-04-14 20:53:31 +08:00
Fourier 47dc9ead38 feat(comment): add --mention-user flag for real Notion user mentions
comment add now supports --mention-user <user-id> (repeatable) to create
true Notion user mentions in comments instead of plain text @-references.

Examples:
  notion comment add <page> --mention-user <id> "Please review"
  notion comment add <page> --mention-user <id1> --mention-user <id2> "Look"

Closes #16
2026-04-14 20:47:47 +08:00
Fourier dc2f4c304a fix: check errcheck on Flags().Set() in search_test 2026-04-07 09:35:28 +08:00
Fourier c03ede56bf test: add auth and search command tests with httptest mocks
- auth_test.go: 18 tests covering login, logout, status, switch, doctor
- search_test.go: 14 tests covering query, filters, pagination, JSON output
- client.go: add baseURL field + NOTION_BASE_URL env for test mocking
2026-04-07 06:36:32 +08:00
傅洋 f0436294df Merge pull request #10 from ahnbu/main
feat(block): add GFM table and inline formatting support
2026-03-15 16:47:23 +08:00
傅洋 c30978b9b1 Merge pull request #3 from charys117/dev
fix: repair notion file upload workflow
2026-03-15 16:47:11 +08:00
ahnbu cc0f36a9eb Merge remote-tracking branch 'upstream/main' 2026-03-14 20:09:56 +09:00
ahnbu 6912bc6209 fix(block): move table_row children into table{} per Notion API spec
Notion API validation requires table_row blocks inside table.children
(not at block top-level). Fixes 'table.children should be defined' error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 20:04:30 +09:00
ahnbu 723eec3108 feat(block): add GFM table and inline formatting support
- parseMarkdownToBlocks(): detect pipe-delimited tables, build Notion
  table/table_row block structure (table_width, has_column_header)
- parseInlineFormatting(): tokenize **bold**, *italic*, _italic_,
  \`code\`, ~~strike~~, [link](url) → rich_text annotations array
- makeTextBlock(): now uses parseInlineFormatting instead of plain text
- renderBlockMarkdown(): add table/table_row cases with GFM separator
- richTextToMarkdown(), richTextItemToMarkdown(): helpers for rendering
  annotated rich_text back to markdown
- isTableSeparator(), splitTableRow(), buildTableBlock(): table helpers
- block_test.go: TestParseMarkdownTable, TestParseInlineFormatting,
  TestIsTableSeparator test suites added; all existing tests pass

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 19:53:20 +09:00
ahnbu 4eea326f69 fix(auth): doctor reads token from current profile
After profile-based login, cfg.Token is empty because MigrateToProfiles
clears it. doctor was only checking the legacy top-level token field,
causing it to always report 'no token found' even after a successful login.

Fixes #5
2026-03-11 14:28:55 +09:00
chenyu 50d29f8ebe fix(file): repair Notion file upload flow 2026-03-10 17:58:29 -07:00
Fourier cf4f0e3e06 feat: support date ranges (start + end) in page set
Use / separator to specify end date:
  notion page set <id> 'Due=2026-03-07T20:00/2026-03-07T21:00'

Single dates continue to work unchanged.

Closes #1
2026-03-11 06:49:32 +08:00
Fourier fe37d1f99b feat(auth): add multi-profile support with switch command
- Add profiles support to config structure
- Add auth switch command for interactive/direct profile switching
- Update auth login to support --profile flag
- Update auth status to show current profile
- Update getToken() to use profile system
- Backward compatible: auto-migrates legacy single-token config
- Add comprehensive tests for profile functionality

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-24 15:29:00 +08:00
Fourier ac1cbe3a18 feat(comment): add reply command for threaded comments
- GET parent comment to find discussion_id
- POST new comment with discussion_id to same thread
- Shows discussion ID in output for reference

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-24 15:28:52 +08:00
Fourier 6199813c76 feat(db): add export command for exporting database to CSV/JSON/MD
- Query all rows with pagination support
- Support --format csv (default), json, md
- Support --output to write to file (default: stdout)
- CSV: headers from property names, rows from property values
- JSON: array of objects with property values
- MD: markdown table format

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-24 15:28:45 +08:00
Fourier 9c25e2d7dc feat(block): add move command for repositioning blocks
- Supports --after to position after a specific block
- Supports --before to position before a specific block
- Supports --parent to move to a different parent block/page
- Uses PATCH /v1/blocks/{id} with parent and after fields

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-24 15:28:32 +08:00
Fourier 9e189e5a82 feat(page): add edit command for editing pages in text editor
- Downloads page blocks as Markdown
- Opens in $EDITOR (or $VISUAL, fallback to vi)
- Parses edited Markdown back to Notion blocks
- Updates page by deleting old blocks and appending new ones
- Supports --editor flag to override default editor

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-24 15:28:23 +08:00
Fourier 4354884991 fix: lint issues — errcheck, gosimple, format verbs 2026-02-19 06:34:12 +08:00
Fourier 4276333c71 ci: pin Go 1.24 for lint job (match go.mod) 2026-02-19 06:14:36 +08:00
Fourier 2827dc1446 fix: rename duplicate TestMapBlockType → TestMapBlockTypeAliases 2026-02-19 06:14:09 +08:00
Fourier 4f7c2f7cdf fix: CI test failures + goreleaser deprecations
- config_test: use t.Setenv for reliable env isolation on CI
- config_test: skip permission test on Windows
- test.yml: align Go matrix with go.mod (1.24)
- goreleaser: fix deprecated formats/homebrew_casks fields
- block: improve --format md rendering with proper indentation
- block: add more markdown parse test cases
- page: minor improvements
2026-02-19 06:11:59 +08:00
Fourier c435c10ac3 release: v0.2.0 — goreleaser, CI, Docker, full README, distribution
- Add goreleaser config (linux/darwin/windows × amd64/arm64)
- Add GitHub Actions: release (goreleaser on tag) + test (3 OS × 3 Go versions + lint)
- Add Dockerfile (Alpine-based)
- Add Homebrew tap (4ier/homebrew-tap) and Scoop bucket (4ier/scoop-bucket)
- Rewrite README: badges, 6 install methods, command table, feature showcase
- Wire version via ldflags
- Update SKILL.md with new commands and install methods
- Add DISTRIBUTION.md plan
2026-02-19 01:34:39 +08:00
Fourier 2fcf8b0f49 feat: complete all remaining features for 100% completeness
- Add --filter-json for raw Notion API filter JSON (OR/nested queries)
- Add --all pagination to db query (all list commands now support --all/--cursor)
- Add semantic error hints with actionable suggestions for 10 API error codes
- Add page create --db for database parent (schema-aware property detection)
- Add block list --depth N for recursive nested block fetching
- Add block list --md for Markdown output rendering
- Add Markdown-to-blocks parser for --file (h1-h3/bullet/numbered/todo/quote/code/divider)
- Add comment get command
- Move todo parsing before bullet to prevent --[ ] matching as bullet
- Add 24 new tests (block markdown parsing + error hints)

Total: 4,310 lines Go, 1,056 lines tests, 124 test cases, 9.6MB binary
2026-02-19 01:18:57 +08:00
Fourier 3304d8044e test: comprehensive test suite — 100 test cases
Coverage:
- cmd/db_test.go: filter parsing (12 cases), sort parsing (5),
  operator mapping (text/number/date), schema option extraction (4)
- cmd/page_test.go: block type mapping (18), property value building
  (15+3+1+1), property extraction (15), rich text extraction (3)
- internal/util/url_test.go: URL/ID resolution (7)
- internal/config/config_test.go: save/load, missing config, file permissions (3)
- internal/client/client_test.go: constructor, debug toggle, constants,
  no token in URL (4)

Security: verified no token/secret leakage in debug output,
config file permissions test ensures 0600.
2026-02-19 00:34:20 +08:00
Fourier 3718c66d85 feat: absorb competitor features — 38 commands
New commands:
- block insert: positional insertion with --after <block-id>
- block append --file: read content from markdown files
- block delete: now supports multiple IDs in one call
- page restore: unarchive pages (reverse of delete)
- page link/unlink: manage relation properties between pages
- db add-bulk: bulk create rows from JSON file with progress
- auth doctor: health check (config, token, workspace, API)

All following our design: human-friendly syntax, dual output,
schema-aware operations. No raw JSON required from users.

3,878 lines of Go. 38 subcommands. Single binary.
2026-02-19 00:17:11 +08:00
Fourier 576690f810 fix: code block append requires language field
Notion API validates that code blocks must include a 'language' field.
Added --lang flag (default: 'plain text') to block append command.
2026-02-18 23:51:25 +08:00
Fourier c66e1d34bb v0.1.0: Full Notion CLI
30 commands covering the entire Notion API:
- auth: login, status, logout
- search: full-text search with type filter
- page: view, list, create, delete, move, open, set, props
- db: list, view, query (filter/sort), create, update, add, open
- block: list, get, append, update, delete
- comment: list, add
- user: me, list, get
- file: list, upload
- api: raw escape hatch for any endpoint

Features:
- Auto-detect TTY: pretty tables in terminal, JSON when piped
- Full Notion IDs in all output (no truncation)
- URL-to-ID resolution (accepts notion.so URLs everywhere)
- Filter syntax for db query: Status=Done, Date>=2026-01-01, Name~=keyword
- Sort syntax: Date:desc, Name:asc
- Property type auto-detection from schema
- Debug mode (--debug) for API troubleshooting
- Shell completion via cobra

3,239 lines of Go. Single binary. Zero dependencies at runtime.
2026-02-18 23:40:14 +08:00