78 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
傅洋 979ba83d9e docs: remove demo GIF from README (#60)
Co-authored-by: fuyang <fuyang@nemovideo.ai>
2026-06-23 15:33:13 +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
ahnbu 7989ed49d9 fix(util): support app.notion.com copied page URLs (#58)
Normalize copied Notion app page URLs by extracting the page id from /p/... links.

Fixes 4ier/notion-cli#57.

Co-authored-by: Codex <noreply@openai.com>
2026-06-23 15:03:14 +08:00
Fourier 7d1b8b286f docs(design): explicitly rule out MCP server mode
A CLI is already a native agent affordance. exec("notion ...") with
JSON output is more direct than wrapping the same calls in MCP's
protocol layer, tool descriptors, and stdio lifecycle. Adding an MCP
surface would just expand maintenance load without giving agents
anything they can't already do.

Add this to Non-goals so the position is clear in the spec, not just
in a closed tracking issue.
2026-05-16 21:34:42 +08:00
Fourier a61d695096 docs: position notion-cli relative to Notion's official 'ntn' CLI
Notion shipped an official CLI ('ntn', https://ntn.dev) in 2026,
focused on Workers deployment plus convenience commands for pages /
files / raw API. The two tools have meaningfully different scopes,
but the names are close enough that users will inevitably ask 'why
not just use ntn?'. This commit answers that up front:

- README: add a 'How does this compare' section between Install and
  Quick Start with a side-by-side table and clear pick-this-when guidance.
- DESIGN.md: add a 'Non-goals' section (OAuth, Workers, TUI, schema
  migrations) and a 'Relationship to ntn' section that names what each
  tool owns. Move the OAuth rationale into the Non-goals list and link
  to it from the Authentication section instead of duplicating the prose.

No code changes.
2026-05-16 21:24:17 +08:00
4ier 15ad78faf8 docs(skill): update SKILL.md for v0.6 + v0.7 command surface
Covers everything shipped in v0.6.0 + v0.7.0:

v0.6.0:
- npm pkg name corrected to @4ier/notion-cli
- auth status / doctor integration type line
- file upload stdin (-) and URL sources + --name
- block append/insert media flags family (image/file/video/audio/pdf
  × url/file/upload)
- block append --file batching + code-lang normalization + --on-oversize
- api command /v1 auto-prepend, --body @file, --body -
- error hint ('→' lines) and the internal-integration workaround

v0.7.0:
- page archive / trash / delete aliases with soft-delete wording
- page property (paginated relation/rollup fetch)
- page markdown / set-markdown (server-side markdown I/O)
- block update --file / --markdown
- comment update / delete
- file get <upload-id>

Adds an agent-oriented 'Tips' section that calls out the biggest
behavior changes: prefer page set-markdown over block append --file for
long documents, and page property over page view for >25-item
relations.
2026-04-30 14:19:22 +08:00
4ier 93f53dbf41 chore(changelog): record v0.7.0 entries for #33 #34 #35 #36 #37 #38 v0.7.0 2026-04-30 14:13:49 +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
4ier 31726aa8e7 chore(changelog): record v0.6.0 entries for #21 #22 #23 #24 #25 #26 v0.6.0 2026-04-30 13:01:57 +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
Fourier 1d0db95bf6 docs(design): remove OAuth/public-integration promise, document why
DESIGN.md previously listed 'notion auth login' (browser OAuth) and
'OAuth flow (for public integrations)' under Post-MVP, which misled
readers into expecting support. Per issue #20:

- Remove the interactive 'auth login' line (OAuth flow never shipped)
- Remove 'OAuth flow (for public integrations)' from Post-MVP list
- Add explicit 'not planned' note explaining the Notion-side constraints
  (no PKCE, no device flow, client_secret required) that make OAuth
  impractical for a publicly-distributed OSS binary

Closes #20
2026-04-28 17:40:32 +08:00
windzu 820f13a3bd feat(block): add --image-url flag for external image blocks (#19)
Thanks @windzu!
v0.5.0
2026-04-21 15:51:32 +08:00
Fourier cbbb11b47f ci(release): add secret pre-flight, npm dry-run, and post-publish verify
- Pre-flight checks HOMEBREW_TAP_TOKEN and NPM_TOKEN are non-empty
  (catches empty-secret mistakes before anything runs)
- npm publish step now runs whoami + publish --dry-run before real publish
  (catches auth failures at the same step, not mid-release)
- Verify step polls npm view to confirm the version landed
  (fail fast if publish silently didn't work)

Prevents the v0.4.0 ghost release where GitHub had the tag but npm didn't.
v0.4.0
2026-04-18 14:13:02 +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 fb1e0ebfed feat: merge npm package into main repo
Move npm distribution files from notion-cli-npm into npm/ directory.
Release workflow now auto-publishes to npm after goreleaser completes.

- npm/: package.json, install.js, bin/notion (shell wrapper), bin/notion.cmd
- release.yml: added Node.js setup + npm publish step
- Version synced from git tag automatically

Requires NPM_TOKEN secret to be configured in GitHub repo settings.
2026-04-07 14:11:29 +08:00
Fourier b51752dcc8 docs: update npm package name to @4ier/notion-cli
The old notion-cli-go package has been deprecated.
Fixes #12
2026-04-07 11:21:15 +08:00
Fourier 5ab5a7db14 ci: disable errcheck in test files 2026-04-07 09:38:34 +08:00
Fourier dc2f4c304a fix: check errcheck on Flags().Set() in search_test v0.3.2 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
v0.3.1
2026-04-07 06:36:32 +08:00
傅洋 b66ed74b77 Merge pull request #13 from 4ier/fix/upload-timeout-context
refactor(client): use context.WithTimeout for upload timeout
2026-03-20 11:26:22 +08:00
Fourier f1d4d119c6 fix(client): replace go1.25 multipart.FileContentDisposition with compatible impl
multipart.FileContentDisposition was introduced in Go 1.25 but go.mod
targets 1.24, causing CI build failures. Replace with manual
Content-Disposition header construction with proper quote escaping.
2026-03-20 11:20:32 +08:00
Fourier 218de498fa refactor(client): use context.WithTimeout for upload instead of mutating httpClient
Replace the pattern of temporarily swapping httpClient.Timeout (not
concurrency-safe) with context.WithTimeout on the request. This is
the idiomatic Go approach and safe for concurrent use.
2026-03-20 09:04:57 +08:00
傅洋 3d0fb6b096 Merge pull request #11 from Acring/fix/file-upload-timeout
fix(client): increase timeout for file upload requests
2026-03-20 09:03:50 +08:00
liu.zhen 5a0989489e fix(client): increase timeout for file upload requests
The default 30s HTTP client timeout is too short for file uploads,
causing "context deadline exceeded" errors. Use a dedicated 5-minute
timeout for the UploadFileContent method.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 11:49:38 +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 9e8b80720d chore(gitignore): add notion.exe to ignored binaries
Windows build artifact — should not be tracked alongside the unix binary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 20:34:42 +09: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 ad95149a25 docs(changelog): GFM table + 인라인 서식 구현 이력 기록
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 19:53:36 +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
傅洋 9ff1532c32 Merge pull request #8 from 4ier/docs/msys-path-workaround
docs: add MSYS/Git Bash path workaround
2026-03-12 17:50:09 +08:00
傅洋 90010ed02b Merge pull request #6 from ahnbu/fix/auth-doctor-profile-token
fix(auth): doctor reads token from current profile
2026-03-12 17:49:58 +08:00
傅洋 e1a1f54d31 Merge pull request #4 from hypn4/docs/fix-auth-cli-flags
docs: fix auth CLI flags to match implementation
2026-03-12 17:49:38 +08:00
Fourier 44e8ab0ed2 docs: add MSYS/Git Bash path workaround
Closes #7
2026-03-12 16:37:01 +08: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
hypn4 aa7e0e5f14 docs: fix auth CLI flags to match actual implementation
README.md and DESIGN.md documented `--token` which doesn't exist.
The actual flag is `--with-token` (boolean, reads token from stdin).
Also added missing `switch` subcommand to README command table.
2026-03-11 11:15:12 +09:00