@andrzejchm/notion-cli 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,335 @@
1
+ ---
2
+ name: using-notion-cli
3
+ description: Use when reading or writing Notion pages, searching a Notion workspace, querying or creating Notion databases, appending or editing page content, creating pages, updating page properties, moving pages, attaching files, adding comments, or archiving pages — via the `notion` CLI tool in the terminal.
4
+ ---
5
+
6
+ ## Overview - skill version 0.12.0
7
+
8
+ `notion` is a CLI tool for reading and writing Notion content from the terminal or agent workflows. Use it any time you need to interact with Notion: read pages, search, query databases, append or edit content, create pages, update properties, move pages, post comments, attach files, or archive pages.
9
+
10
+ > **Version check:** Run `notion --version`. If your installed version is older than 0.12.0, update with `npm install -g @andrzejchm/notion-cli` and refresh this skill with `notion skill`.
11
+
12
+ ## Setup
13
+
14
+ Install once:
15
+
16
+ ```bash
17
+ npm install -g @andrzejchm/notion-cli
18
+ notion auth login # interactive setup — choose OAuth or integration token
19
+ ```
20
+
21
+ Or set env var (preferred for CI/agents):
22
+
23
+ ```bash
24
+ export NOTION_API_TOKEN=ntn_your_token_here
25
+ ```
26
+
27
+ Get token: https://www.notion.so/profile/integrations/internal
28
+
29
+ Pages must be shared with your integration: open page → `⋯` → **Add connections**.
30
+
31
+ **Integration capabilities** (set at notion.so/profile/integrations/internal → your integration → Capabilities):
32
+
33
+ - Read-only commands: **Read content** only
34
+ - `notion append`, `notion append --after`, `notion create-page` (page parent): also need **Insert content**
35
+ - `notion create-page --parent <db>`: also need **Insert content** + database must be shared with integration
36
+ - `notion edit-page`: also need **Update content** + **Insert content**
37
+ - `notion attach`: also need **Insert content**
38
+ - `notion comment`: also need **Read comments** + **Insert comments**
39
+
40
+ ---
41
+
42
+ ## Authentication
43
+
44
+ Two auth methods are available. If both are configured, **OAuth takes precedence**.
45
+
46
+ | Method | Command | Attribution | Notes |
47
+ | ----------------- | ------------------------------------------------- | ------------------- | ------------------------------------------------------- |
48
+ | Interactive setup | `notion auth login` | — | Guides you to choose; TTY required |
49
+ | OAuth user login | select "OAuth user login" in `notion auth login` | Your Notion account | Browser required; `--manual` for headless |
50
+ | Integration token | select "Integration token" in `notion auth login` | Integration bot | Works in CI/headless; must connect integration to pages |
51
+
52
+ ```bash
53
+ notion auth login # interactive selector — OAuth or integration token
54
+ notion auth login --manual # headless OAuth (prints URL, paste redirect back)
55
+ notion auth status # show current auth state
56
+ notion auth logout # remove a profile (interactive selector)
57
+ notion auth logout --profile <name> # remove specific profile directly
58
+ notion auth list # list all saved profiles
59
+ notion auth use <name> # switch active profile
60
+ notion --profile <name> <command> # use a specific profile for one command
61
+ ```
62
+
63
+ **Headless/CI agents:** Use `NOTION_API_TOKEN=<token>` env var — overrides all config, no TTY needed.
64
+
65
+ ---
66
+
67
+ ## Output Modes
68
+
69
+ | Context | Default | Override |
70
+ | -------------- | ----------------- | -------- |
71
+ | Terminal (TTY) | Formatted tables | `--json` |
72
+ | Piped / agent | Plain text tables | `--json` |
73
+
74
+ `notion read` always outputs **markdown** — in terminal and when piped.
75
+
76
+ Pipe any command to get JSON:
77
+
78
+ ```bash
79
+ notion search "query" | jq '.[0].id'
80
+ notion ls | jq '.[] | select(.type == "database")'
81
+ ```
82
+
83
+ ---
84
+
85
+ ## Commands
86
+
87
+ ### Search & Discovery
88
+
89
+ ```bash
90
+ notion search "query" # search all pages/databases by title
91
+ notion search "query" --type page # pages only
92
+ notion search "query" --type database # databases only
93
+ notion search "query" --sort asc # sort by last edited time (asc or desc)
94
+ notion ls # list everything accessible to integration
95
+ notion ls --sort desc # sort by last edited time
96
+ notion users # list workspace members
97
+ notion comments <id|url> # list page comments
98
+ notion open <id|url> # open in browser
99
+ ```
100
+
101
+ ### Reading Pages
102
+
103
+ ```bash
104
+ notion read <id|url> # markdown (always, even when piped)
105
+ notion read <id|url> --json # raw Notion JSON block tree
106
+ ```
107
+
108
+ ### Database Operations
109
+
110
+ ```bash
111
+ notion db schema <id|url> # inspect properties + valid values
112
+ notion db query <id|url> # all rows
113
+ notion db query <id|url> --filter "Status=Done" # filter (repeatable)
114
+ notion db query <id|url> --sort "Created:desc" # sort (repeatable)
115
+ notion db query <id|url> --columns "Title,Status" # limit columns
116
+ notion db query <id|url> --json | jq '.[] | .properties'
117
+
118
+ notion db create --parent <page-id|url> --title "My Database" # create a new database
119
+ ```
120
+
121
+ ### Write Operations
122
+
123
+ ```bash
124
+ notion append <id|url> -m "## Heading\nParagraph text" # append markdown blocks to a page
125
+ notion append <id|url> -m "$(cat notes.md)" # append file contents
126
+ notion append <id|url> --file screenshot.png # attach a local file
127
+ notion append <id|url> -m "See results:" --file chart.png # markdown + file attachment
128
+ notion append <id|url> --file a.png --file b.pdf # multiple files (repeatable)
129
+
130
+ notion create-page --parent <page-id|url> --title "Title" # child page under a page
131
+ notion create-page --parent <page-id|url> --title "Title" -m "# Hello" # with markdown body
132
+ echo "# Content" | notion create-page --parent <page-id|url> --title "Title" # from stdin
133
+ notion create-page --parent <page-id|url> --title "Report" --file report.pdf # with file attachment
134
+ notion create-page --parent <page-id|url> --title "Notes" -m "# Agenda" --file slides.pdf --file notes.txt
135
+
136
+ # Create entry in a database (auto-detected from parent ID)
137
+ notion create-page --parent <db-id|url> --title "New Task"
138
+ notion create-page --parent <db-id|url> --title "Task" --prop "Status=To Do" --prop "Priority=High"
139
+ notion create-page --parent <db-id|url> --title "Task" --prop "Due=2026-04-01" -m "# Details"
140
+
141
+ # Icon and cover (emoji, URL, or local file path)
142
+ notion create-page --parent <id|url> --title "Page" --icon "🚀"
143
+ notion create-page --parent <id|url> --title "Page" --icon ./logo.png # upload local file as icon
144
+ notion create-page --parent <id|url> --title "Page" --cover "https://example.com/img.jpg"
145
+ notion create-page --parent <id|url> --title "Page" --cover ./banner.jpg # upload local file as cover
146
+
147
+ URL=$(notion create-page --parent <id|url> --title "Summary" -m "...") # capture URL
148
+
149
+ notion comment <id|url> -m "Reviewed and approved." # add comment to a page
150
+ notion comment <id|url> -m "Reply" --reply-to <discussion-id> # reply to a discussion thread
151
+ notion comment <id|url> -m "Note" --block <block-id> # comment on a specific block
152
+
153
+ notion archive <id|url> # move page to trash
154
+
155
+ notion move <ids|urls...> --to <id|url> # move pages to a new parent page
156
+ notion move <ids|urls...> --to-db <id|url> # move pages to a database parent
157
+ ```
158
+
159
+ #### File Attachments
160
+
161
+ Upload local files to Notion and attach them as blocks (image, file, PDF, audio, video). Block type is auto-detected from file extension. Files ≤20 MB upload in one request; larger files are chunked automatically.
162
+
163
+ ```bash
164
+ notion attach <id|url> screenshot.png # attach a single file
165
+ notion attach <id|url> report.pdf data.csv image.png # attach multiple files
166
+ notion attach <id|url> diagram.png --caption "Architecture diagram" # with caption
167
+ notion attach <id|url> file.svg --type image # override auto-detected type
168
+ ```
169
+
170
+ | Flag | Description |
171
+ |------|-------------|
172
+ | `--caption <text>` | Caption for the file block(s) |
173
+ | `--type <type>` | Override block type (`image\|file\|pdf\|audio\|video`) |
174
+ | `--json` | Output JSON response |
175
+
176
+ The `--file <path>` flag (repeatable) is also available on `notion append` and `notion create-page` for inline file attachment alongside markdown content.
177
+
178
+ #### Updating Page Properties
179
+
180
+ ```bash
181
+ notion update <id|url> --prop "Status=Done" # set a single property
182
+ notion update <id|url> --prop "Status=Done" --prop "Priority=1" # multiple properties
183
+ notion update <id|url> --title "New Title" # set the title
184
+ notion update <id|url> --prop "Due=2026-04-01" # set a date
185
+ notion update <id|url> --prop "Tags=bug,urgent" # multi-select (comma-separated)
186
+ notion update <id|url> --prop "Done=true" # checkbox (true/yes/false/no)
187
+ notion update <id|url> --prop "Status=" # clear a property (empty value)
188
+ ```
189
+
190
+ Supported types: title, rich_text, select, status, multi_select, number, checkbox, url, email, phone_number, date, files.
191
+
192
+ ```bash
193
+ # Files property — local file paths or URLs (comma-separated for multiple)
194
+ notion update <id|url> --prop "Attachments=./report.pdf" # upload local file
195
+ notion update <id|url> --prop "Attachments=https://example.com/file.pdf" # external URL
196
+ notion update <id|url> --prop "Attachments=./a.pdf,./b.png" # multiple files
197
+ ```
198
+
199
+ > **Note:** Setting a `files` property **replaces** all existing files (Notion API behavior). To keep existing files, re-include them in the value.
200
+
201
+ #### Surgical Editing
202
+
203
+ Search-and-replace specific text, replace the full page, or insert content at a specific location.
204
+
205
+ ```bash
206
+ # Search-and-replace: find text and replace it
207
+ notion edit-page <id|url> --find "old text" --replace "new text"
208
+
209
+ # Multiple search-and-replace operations in one call
210
+ notion edit-page <id|url> --find "old1" --replace "new1" --find "old2" --replace "new2"
211
+
212
+ # Replace all occurrences (not just the first match)
213
+ notion edit-page <id|url> --find "TODO" --replace "DONE" --all
214
+
215
+ # Replace an entire page's content
216
+ notion edit-page <id|url> -m "# Replacement\nNew full-page content"
217
+
218
+ # Pipe replacement content from a file
219
+ cat updated-section.md | notion edit-page <id|url>
220
+
221
+ # Allow deletion of child pages/databases during replace
222
+ notion edit-page <id|url> -m "## Clean Slate" --allow-deleting-content
223
+
224
+ # Append after a matched section (inserts new blocks right after the match)
225
+ notion append <id|url> -m "New content" --after "## Status...end of status"
226
+ ```
227
+
228
+ > **`--range` (deprecated):** The `--range` flag still works for backward compatibility but uses the older `replace_content_range` API. Prefer `--find`/`--replace` for targeted edits.
229
+
230
+ ---
231
+
232
+ ## ID Formats
233
+
234
+ All commands accept any of:
235
+
236
+ - `abc123def456789012345678901234ab` (32-char hex)
237
+ - `abc123de-f456-7890-1234-5678901234ab` (UUID)
238
+ - `https://www.notion.so/workspace/Page-Title-abc123` (full URL)
239
+
240
+ ---
241
+
242
+ ## Agent Patterns
243
+
244
+ ```bash
245
+ # Find page by title, read it
246
+ PAGE_ID=$(notion search "Meeting Notes" --type page | jq -r '.[0].id')
247
+ notion read "$PAGE_ID"
248
+
249
+ # Get database ID, then query with filter
250
+ DB_ID=$(notion search "Project Tracker" --type database | jq -r '.[0].id')
251
+ notion db query "$DB_ID" --filter "Status=In Progress" | jq '.[] | .properties'
252
+
253
+ # Always check schema before filtering (see valid property names/values)
254
+ notion db schema "$DB_ID"
255
+ notion db query "$DB_ID" --filter "Status=Done"
256
+
257
+ # Extract page section
258
+ notion read "$PAGE_ID" | grep -A 10 "## Action Items"
259
+
260
+ # Summarize a page and append the summary back
261
+ SUMMARY=$(notion read "$PAGE_ID" | your-summarize-command)
262
+ notion append "$PAGE_ID" -m "## AI Summary\n$SUMMARY"
263
+
264
+ # Create a page and capture its URL for further use
265
+ URL=$(notion create-page --parent "$PAGE_ID" --title "Report $(date +%Y-%m-%d)" -m "# Report\n...")
266
+ echo "Created: $URL"
267
+
268
+ # Pipe command output into a new page
269
+ my-report-command | notion create-page --parent "$PAGE_ID" --title "Auto Report"
270
+
271
+ # Surgically update specific text on a page
272
+ # 1. Read the page to find the text you want to change
273
+ notion read "$PAGE_ID"
274
+ # 2. Use --find/--replace to swap specific text
275
+ notion edit-page "$PAGE_ID" --find "Status: In Progress" --replace "Status: Done"
276
+
277
+ # Multiple replacements in one call
278
+ notion edit-page "$PAGE_ID" \
279
+ --find "Status: In Progress" --replace "Status: Done" \
280
+ --find "Blocked: yes" --replace "Blocked: none"
281
+
282
+ # Create a database entry with properties
283
+ DB_ID=$(notion search "Tasks" --type database | jq -r '.[0].id')
284
+ notion db schema "$DB_ID" # check property names and valid values first
285
+ notion create-page --parent "$DB_ID" --title "Fix login bug" \
286
+ --prop "Status=To Do" --prop "Priority=High" --prop "Due=2026-04-15"
287
+
288
+ # Insert a new sub-section after an existing section
289
+ notion append "$PAGE_ID" -m "## New Sub-section\nContent here" \
290
+ --after "## Existing Section...last line of section"
291
+
292
+ # Move pages to a different parent
293
+ ARCHIVE_ID=$(notion search "Archive" --type page | jq -r '.[0].id')
294
+ notion move "$PAGE_ID" --to "$ARCHIVE_ID"
295
+
296
+ # Move multiple pages into a database
297
+ notion move page1-id page2-id --to-db "$DB_ID"
298
+
299
+ # Attach a generated screenshot to a documentation page
300
+ notion attach "$PAGE_ID" ./screenshot.png --caption "Current UI state"
301
+
302
+ # Create a report page with attached artifacts
303
+ notion create-page --parent "$PAGE_ID" --title "Build Report $(date +%Y-%m-%d)" \
304
+ -m "# Build Results\nAll tests passed." --file ./test-results.pdf --file ./coverage.png
305
+
306
+ # Append analysis with supporting data file
307
+ notion append "$PAGE_ID" -m "## Data Analysis\nSee attached CSV:" --file ./export.csv
308
+
309
+ # Upload a local file as icon for a page
310
+ notion update "$PAGE_ID" --icon ./logo.png
311
+ ```
312
+
313
+ ---
314
+
315
+ ## Troubleshooting
316
+
317
+ **404 / page not found** — Share the page with your integration: page → `⋯` → **Add connections**.
318
+
319
+ **401 / unauthorized** — Run `notion auth login` or set `NOTION_API_TOKEN`.
320
+
321
+ **Search returns nothing** — Search is title-only. Page must be shared with integration.
322
+
323
+ **Empty db query** — Run `notion db schema <id>` to see valid property names and values.
324
+
325
+ **`notion auth login` requires TTY** — Use `NOTION_API_TOKEN` env var in agents, or use `notion auth login --manual` for headless OAuth.
326
+
327
+ **`notion comment` returns "Insufficient permissions"** — Enable **Read comments** + **Insert comments** in integration capabilities: notion.so/profile/integrations/internal → your integration → Capabilities.
328
+
329
+ **`notion append` / `notion create-page` returns "Insufficient permissions"** — Enable **Insert content** in integration capabilities.
330
+
331
+ **`notion edit-page` returns "Insufficient permissions"** — Enable **Update content** in integration capabilities.
332
+
333
+ **`--find` text not found** — Run `notion read <id>` to see the exact page content. The `--find` value must match text on the page exactly.
334
+
335
+ **`--after` selector not found** — Run `notion read <id>` to see the exact page content. The selector must match real text: `"start...end"` with ~10 chars from the beginning and end of the target range.
package/README.md CHANGED
@@ -102,6 +102,7 @@ notion ls
102
102
  | `notion comments <id\|url>` | Read page comments |
103
103
  | `notion comment [id\|url] -m <text>` | Add a comment to a page, block, or thread |
104
104
  | `notion append <id\|url> -m <markdown>` | Append markdown blocks to a page |
105
+ | `notion attach <id\|url> <file> [files...]` | Upload and attach file(s) to a page |
105
106
  | `notion edit-page <id\|url> --find <old> --replace <new>` | Search-and-replace text on a page |
106
107
  | `notion edit-page <id\|url> -m <markdown>` | Replace entire page content |
107
108
  | `notion create-page --parent <id\|url> --title <title>` | Create a new page, prints URL |
@@ -111,6 +112,29 @@ notion ls
111
112
  | `notion move <ids\|urls...> --to-db <id\|url>` | Move pages to a database parent |
112
113
  | `notion completion bash\|zsh\|fish` | Install shell tab completion |
113
114
 
115
+ ### `notion attach` flags
116
+
117
+ | Flag | Example | Description |
118
+ |------|---------|-------------|
119
+ | `--caption <text>` | `--caption "My screenshot"` | Caption for the file block(s) |
120
+ | `--type <type>` | `--type image` | Override auto-detected block type (`image\|file\|pdf\|audio\|video`) |
121
+ | `--json` | `--json` | Output JSON response |
122
+
123
+ ### `notion append` / `notion create-page` — `--file` flag
124
+
125
+ Both commands accept a repeatable `--file <path>` option to attach local files after the markdown content is written:
126
+
127
+ ```bash
128
+ # Append markdown and attach a file
129
+ notion append "$PAGE_ID" -m "See attached screenshot:" --file screenshot.png
130
+
131
+ # Create a page with an attached PDF
132
+ notion create-page --parent "$PAGE_ID" --title "Report" --file report.pdf
133
+
134
+ # Attach multiple files
135
+ notion append "$PAGE_ID" --file image.png --file data.csv
136
+ ```
137
+
114
138
  ### `notion search` / `notion ls` flags
115
139
 
116
140
  | Flag | Example | Description |
@@ -220,6 +244,7 @@ Write commands require additional capabilities — enable in your integration se
220
244
  | Command | Required capabilities |
221
245
  |---------|----------------------|
222
246
  | `notion append` | Read content, Insert content |
247
+ | `notion attach` | Read content, Insert content |
223
248
  | `notion create-page` | Read content, Insert content |
224
249
  | `notion update` | Read content, Update content |
225
250
  | `notion comment` | Read content, Insert content, Read comments, Insert comments |