@andrzejchm/notion-cli 0.10.0 → 0.11.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.
- package/.agents/skills/using-notion-cli/SKILL.md +286 -0
- package/dist/cli.js +143 -14
- package/dist/cli.js.map +1 -1
- package/docs/README.agents.md +14 -0
- package/package.json +2 -1
|
@@ -0,0 +1,286 @@
|
|
|
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, adding comments, or archiving pages — via the `notion` CLI tool in the terminal.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Overview - skill version 0.11.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, or archive pages.
|
|
9
|
+
|
|
10
|
+
> **Version check:** Run `notion --version`. If your installed version is older than 0.11.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 comment`: also need **Read comments** + **Insert comments**
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Authentication
|
|
42
|
+
|
|
43
|
+
Two auth methods are available. If both are configured, **OAuth takes precedence**.
|
|
44
|
+
|
|
45
|
+
| Method | Command | Attribution | Notes |
|
|
46
|
+
| ----------------- | ------------------------------------------------- | ------------------- | ------------------------------------------------------- |
|
|
47
|
+
| Interactive setup | `notion auth login` | — | Guides you to choose; TTY required |
|
|
48
|
+
| OAuth user login | select "OAuth user login" in `notion auth login` | Your Notion account | Browser required; `--manual` for headless |
|
|
49
|
+
| Integration token | select "Integration token" in `notion auth login` | Integration bot | Works in CI/headless; must connect integration to pages |
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
notion auth login # interactive selector — OAuth or integration token
|
|
53
|
+
notion auth login --manual # headless OAuth (prints URL, paste redirect back)
|
|
54
|
+
notion auth status # show current auth state
|
|
55
|
+
notion auth logout # remove a profile (interactive selector)
|
|
56
|
+
notion auth logout --profile <name> # remove specific profile directly
|
|
57
|
+
notion auth list # list all saved profiles
|
|
58
|
+
notion auth use <name> # switch active profile
|
|
59
|
+
notion --profile <name> <command> # use a specific profile for one command
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Headless/CI agents:** Use `NOTION_API_TOKEN=<token>` env var — overrides all config, no TTY needed.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Output Modes
|
|
67
|
+
|
|
68
|
+
| Context | Default | Override |
|
|
69
|
+
| -------------- | ----------------- | -------- |
|
|
70
|
+
| Terminal (TTY) | Formatted tables | `--json` |
|
|
71
|
+
| Piped / agent | Plain text tables | `--json` |
|
|
72
|
+
|
|
73
|
+
`notion read` always outputs **markdown** — in terminal and when piped.
|
|
74
|
+
|
|
75
|
+
Pipe any command to get JSON:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
notion search "query" | jq '.[0].id'
|
|
79
|
+
notion ls | jq '.[] | select(.type == "database")'
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Commands
|
|
85
|
+
|
|
86
|
+
### Search & Discovery
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
notion search "query" # search all pages/databases by title
|
|
90
|
+
notion search "query" --type page # pages only
|
|
91
|
+
notion search "query" --type database # databases only
|
|
92
|
+
notion search "query" --sort asc # sort by last edited time (asc or desc)
|
|
93
|
+
notion ls # list everything accessible to integration
|
|
94
|
+
notion ls --sort desc # sort by last edited time
|
|
95
|
+
notion users # list workspace members
|
|
96
|
+
notion comments <id|url> # list page comments
|
|
97
|
+
notion open <id|url> # open in browser
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Reading Pages
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
notion read <id|url> # markdown (always, even when piped)
|
|
104
|
+
notion read <id|url> --json # raw Notion JSON block tree
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Database Operations
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
notion db schema <id|url> # inspect properties + valid values
|
|
111
|
+
notion db query <id|url> # all rows
|
|
112
|
+
notion db query <id|url> --filter "Status=Done" # filter (repeatable)
|
|
113
|
+
notion db query <id|url> --sort "Created:desc" # sort (repeatable)
|
|
114
|
+
notion db query <id|url> --columns "Title,Status" # limit columns
|
|
115
|
+
notion db query <id|url> --json | jq '.[] | .properties'
|
|
116
|
+
|
|
117
|
+
notion db create --parent <page-id|url> --title "My Database" # create a new database
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Write Operations
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
notion append <id|url> -m "## Heading\nParagraph text" # append markdown blocks to a page
|
|
124
|
+
notion append <id|url> -m "$(cat notes.md)" # append file contents
|
|
125
|
+
|
|
126
|
+
notion create-page --parent <page-id|url> --title "Title" # child page under a page
|
|
127
|
+
notion create-page --parent <page-id|url> --title "Title" -m "# Hello" # with markdown body
|
|
128
|
+
echo "# Content" | notion create-page --parent <page-id|url> --title "Title" # from stdin
|
|
129
|
+
|
|
130
|
+
# Create entry in a database (auto-detected from parent ID)
|
|
131
|
+
notion create-page --parent <db-id|url> --title "New Task"
|
|
132
|
+
notion create-page --parent <db-id|url> --title "Task" --prop "Status=To Do" --prop "Priority=High"
|
|
133
|
+
notion create-page --parent <db-id|url> --title "Task" --prop "Due=2026-04-01" -m "# Details"
|
|
134
|
+
|
|
135
|
+
# Icon and cover
|
|
136
|
+
notion create-page --parent <id|url> --title "Page" --icon "🚀"
|
|
137
|
+
notion create-page --parent <id|url> --title "Page" --cover "https://example.com/img.jpg"
|
|
138
|
+
|
|
139
|
+
URL=$(notion create-page --parent <id|url> --title "Summary" -m "...") # capture URL
|
|
140
|
+
|
|
141
|
+
notion comment <id|url> -m "Reviewed and approved." # add comment to a page
|
|
142
|
+
notion comment <id|url> -m "Reply" --reply-to <discussion-id> # reply to a discussion thread
|
|
143
|
+
notion comment <id|url> -m "Note" --block <block-id> # comment on a specific block
|
|
144
|
+
|
|
145
|
+
notion archive <id|url> # move page to trash
|
|
146
|
+
|
|
147
|
+
notion move <ids|urls...> --to <id|url> # move pages to a new parent page
|
|
148
|
+
notion move <ids|urls...> --to-db <id|url> # move pages to a database parent
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### Updating Page Properties
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
notion update <id|url> --prop "Status=Done" # set a single property
|
|
155
|
+
notion update <id|url> --prop "Status=Done" --prop "Priority=1" # multiple properties
|
|
156
|
+
notion update <id|url> --title "New Title" # set the title
|
|
157
|
+
notion update <id|url> --prop "Due=2026-04-01" # set a date
|
|
158
|
+
notion update <id|url> --prop "Tags=bug,urgent" # multi-select (comma-separated)
|
|
159
|
+
notion update <id|url> --prop "Done=true" # checkbox (true/yes/false/no)
|
|
160
|
+
notion update <id|url> --prop "Status=" # clear a property (empty value)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Supported types: title, rich_text, select, status, multi_select, number, checkbox, url, email, phone_number, date.
|
|
164
|
+
|
|
165
|
+
#### Surgical Editing
|
|
166
|
+
|
|
167
|
+
Search-and-replace specific text, replace the full page, or insert content at a specific location.
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
# Search-and-replace: find text and replace it
|
|
171
|
+
notion edit-page <id|url> --find "old text" --replace "new text"
|
|
172
|
+
|
|
173
|
+
# Multiple search-and-replace operations in one call
|
|
174
|
+
notion edit-page <id|url> --find "old1" --replace "new1" --find "old2" --replace "new2"
|
|
175
|
+
|
|
176
|
+
# Replace all occurrences (not just the first match)
|
|
177
|
+
notion edit-page <id|url> --find "TODO" --replace "DONE" --all
|
|
178
|
+
|
|
179
|
+
# Replace an entire page's content
|
|
180
|
+
notion edit-page <id|url> -m "# Replacement\nNew full-page content"
|
|
181
|
+
|
|
182
|
+
# Pipe replacement content from a file
|
|
183
|
+
cat updated-section.md | notion edit-page <id|url>
|
|
184
|
+
|
|
185
|
+
# Allow deletion of child pages/databases during replace
|
|
186
|
+
notion edit-page <id|url> -m "## Clean Slate" --allow-deleting-content
|
|
187
|
+
|
|
188
|
+
# Append after a matched section (inserts new blocks right after the match)
|
|
189
|
+
notion append <id|url> -m "New content" --after "## Status...end of status"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
> **`--range` (deprecated):** The `--range` flag still works for backward compatibility but uses the older `replace_content_range` API. Prefer `--find`/`--replace` for targeted edits.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## ID Formats
|
|
197
|
+
|
|
198
|
+
All commands accept any of:
|
|
199
|
+
|
|
200
|
+
- `abc123def456789012345678901234ab` (32-char hex)
|
|
201
|
+
- `abc123de-f456-7890-1234-5678901234ab` (UUID)
|
|
202
|
+
- `https://www.notion.so/workspace/Page-Title-abc123` (full URL)
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Agent Patterns
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
# Find page by title, read it
|
|
210
|
+
PAGE_ID=$(notion search "Meeting Notes" --type page | jq -r '.[0].id')
|
|
211
|
+
notion read "$PAGE_ID"
|
|
212
|
+
|
|
213
|
+
# Get database ID, then query with filter
|
|
214
|
+
DB_ID=$(notion search "Project Tracker" --type database | jq -r '.[0].id')
|
|
215
|
+
notion db query "$DB_ID" --filter "Status=In Progress" | jq '.[] | .properties'
|
|
216
|
+
|
|
217
|
+
# Always check schema before filtering (see valid property names/values)
|
|
218
|
+
notion db schema "$DB_ID"
|
|
219
|
+
notion db query "$DB_ID" --filter "Status=Done"
|
|
220
|
+
|
|
221
|
+
# Extract page section
|
|
222
|
+
notion read "$PAGE_ID" | grep -A 10 "## Action Items"
|
|
223
|
+
|
|
224
|
+
# Summarize a page and append the summary back
|
|
225
|
+
SUMMARY=$(notion read "$PAGE_ID" | your-summarize-command)
|
|
226
|
+
notion append "$PAGE_ID" -m "## AI Summary\n$SUMMARY"
|
|
227
|
+
|
|
228
|
+
# Create a page and capture its URL for further use
|
|
229
|
+
URL=$(notion create-page --parent "$PAGE_ID" --title "Report $(date +%Y-%m-%d)" -m "# Report\n...")
|
|
230
|
+
echo "Created: $URL"
|
|
231
|
+
|
|
232
|
+
# Pipe command output into a new page
|
|
233
|
+
my-report-command | notion create-page --parent "$PAGE_ID" --title "Auto Report"
|
|
234
|
+
|
|
235
|
+
# Surgically update specific text on a page
|
|
236
|
+
# 1. Read the page to find the text you want to change
|
|
237
|
+
notion read "$PAGE_ID"
|
|
238
|
+
# 2. Use --find/--replace to swap specific text
|
|
239
|
+
notion edit-page "$PAGE_ID" --find "Status: In Progress" --replace "Status: Done"
|
|
240
|
+
|
|
241
|
+
# Multiple replacements in one call
|
|
242
|
+
notion edit-page "$PAGE_ID" \
|
|
243
|
+
--find "Status: In Progress" --replace "Status: Done" \
|
|
244
|
+
--find "Blocked: yes" --replace "Blocked: none"
|
|
245
|
+
|
|
246
|
+
# Create a database entry with properties
|
|
247
|
+
DB_ID=$(notion search "Tasks" --type database | jq -r '.[0].id')
|
|
248
|
+
notion db schema "$DB_ID" # check property names and valid values first
|
|
249
|
+
notion create-page --parent "$DB_ID" --title "Fix login bug" \
|
|
250
|
+
--prop "Status=To Do" --prop "Priority=High" --prop "Due=2026-04-15"
|
|
251
|
+
|
|
252
|
+
# Insert a new sub-section after an existing section
|
|
253
|
+
notion append "$PAGE_ID" -m "## New Sub-section\nContent here" \
|
|
254
|
+
--after "## Existing Section...last line of section"
|
|
255
|
+
|
|
256
|
+
# Move pages to a different parent
|
|
257
|
+
ARCHIVE_ID=$(notion search "Archive" --type page | jq -r '.[0].id')
|
|
258
|
+
notion move "$PAGE_ID" --to "$ARCHIVE_ID"
|
|
259
|
+
|
|
260
|
+
# Move multiple pages into a database
|
|
261
|
+
notion move page1-id page2-id --to-db "$DB_ID"
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Troubleshooting
|
|
267
|
+
|
|
268
|
+
**404 / page not found** — Share the page with your integration: page → `⋯` → **Add connections**.
|
|
269
|
+
|
|
270
|
+
**401 / unauthorized** — Run `notion auth login` or set `NOTION_API_TOKEN`.
|
|
271
|
+
|
|
272
|
+
**Search returns nothing** — Search is title-only. Page must be shared with integration.
|
|
273
|
+
|
|
274
|
+
**Empty db query** — Run `notion db schema <id>` to see valid property names and values.
|
|
275
|
+
|
|
276
|
+
**`notion auth login` requires TTY** — Use `NOTION_API_TOKEN` env var in agents, or use `notion auth login --manual` for headless OAuth.
|
|
277
|
+
|
|
278
|
+
**`notion comment` returns "Insufficient permissions"** — Enable **Read comments** + **Insert comments** in integration capabilities: notion.so/profile/integrations/internal → your integration → Capabilities.
|
|
279
|
+
|
|
280
|
+
**`notion append` / `notion create-page` returns "Insufficient permissions"** — Enable **Insert content** in integration capabilities.
|
|
281
|
+
|
|
282
|
+
**`notion edit-page` returns "Insufficient permissions"** — Enable **Update content** in integration capabilities.
|
|
283
|
+
|
|
284
|
+
**`--find` text not found** — Run `notion read <id>` to see the exact page content. The `--find` value must match text on the page exactly.
|
|
285
|
+
|
|
286
|
+
**`--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/dist/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { readFileSync } from "fs";
|
|
5
|
-
import { dirname, join as
|
|
4
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
5
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
|
-
import { Command as
|
|
7
|
+
import { Command as Command26 } from "commander";
|
|
8
8
|
|
|
9
9
|
// src/commands/append.ts
|
|
10
10
|
import { Command } from "commander";
|
|
@@ -1436,7 +1436,7 @@ _notion_completion() {
|
|
|
1436
1436
|
local cur prev words cword
|
|
1437
1437
|
_init_completion || return
|
|
1438
1438
|
|
|
1439
|
-
local commands="init profile completion --help --version --verbose --color"
|
|
1439
|
+
local commands="init profile completion skill --help --version --verbose --color"
|
|
1440
1440
|
local profile_commands="list use remove"
|
|
1441
1441
|
|
|
1442
1442
|
case "$prev" in
|
|
@@ -1469,6 +1469,7 @@ _notion() {
|
|
|
1469
1469
|
'init:authenticate with Notion and save a profile'
|
|
1470
1470
|
'profile:manage authentication profiles'
|
|
1471
1471
|
'completion:output shell completion script'
|
|
1472
|
+
'skill:install the agent skill file'
|
|
1472
1473
|
)
|
|
1473
1474
|
|
|
1474
1475
|
local -a global_opts
|
|
@@ -1520,6 +1521,7 @@ complete -c notion -l color -d 'force color output'
|
|
|
1520
1521
|
complete -c notion -n '__fish_use_subcommand' -a init -d 'authenticate with Notion and save a profile'
|
|
1521
1522
|
complete -c notion -n '__fish_use_subcommand' -a profile -d 'manage authentication profiles'
|
|
1522
1523
|
complete -c notion -n '__fish_use_subcommand' -a completion -d 'output shell completion script'
|
|
1524
|
+
complete -c notion -n '__fish_use_subcommand' -a skill -d 'install the agent skill file'
|
|
1523
1525
|
|
|
1524
1526
|
# profile subcommands
|
|
1525
1527
|
complete -c notion -n '__fish_seen_subcommand_from profile' -a list -d 'list all authentication profiles'
|
|
@@ -2899,14 +2901,140 @@ function searchCommand() {
|
|
|
2899
2901
|
return cmd;
|
|
2900
2902
|
}
|
|
2901
2903
|
|
|
2902
|
-
// src/commands/
|
|
2904
|
+
// src/commands/skill.ts
|
|
2905
|
+
import {
|
|
2906
|
+
copyFileSync,
|
|
2907
|
+
existsSync,
|
|
2908
|
+
mkdirSync,
|
|
2909
|
+
readFileSync,
|
|
2910
|
+
realpathSync
|
|
2911
|
+
} from "fs";
|
|
2912
|
+
import { homedir as homedir2 } from "os";
|
|
2913
|
+
import { dirname, join as join3 } from "path";
|
|
2914
|
+
import chalk from "chalk";
|
|
2903
2915
|
import { Command as Command23 } from "commander";
|
|
2916
|
+
function skillPath() {
|
|
2917
|
+
if (!process.argv[1]) {
|
|
2918
|
+
throw new Error("Cannot determine install path. Run with: notion skill");
|
|
2919
|
+
}
|
|
2920
|
+
const entryPoint = realpathSync(process.argv[1]);
|
|
2921
|
+
const packageRoot = dirname(dirname(entryPoint));
|
|
2922
|
+
const candidates = [
|
|
2923
|
+
join3(packageRoot, ".agents", "skills", "using-notion-cli", "SKILL.md"),
|
|
2924
|
+
join3(
|
|
2925
|
+
dirname(entryPoint),
|
|
2926
|
+
"..",
|
|
2927
|
+
".agents",
|
|
2928
|
+
"skills",
|
|
2929
|
+
"using-notion-cli",
|
|
2930
|
+
"SKILL.md"
|
|
2931
|
+
)
|
|
2932
|
+
];
|
|
2933
|
+
for (const c2 of candidates) {
|
|
2934
|
+
if (existsSync(c2)) return c2;
|
|
2935
|
+
}
|
|
2936
|
+
throw new Error(
|
|
2937
|
+
"SKILL.md not found. Reinstall with: npm install -g @andrzejchm/notion-cli"
|
|
2938
|
+
);
|
|
2939
|
+
}
|
|
2940
|
+
function getAgentTargets() {
|
|
2941
|
+
const home = homedir2();
|
|
2942
|
+
const targets = [
|
|
2943
|
+
{
|
|
2944
|
+
name: "Claude Code",
|
|
2945
|
+
dir: join3(home, ".claude", "skills", "using-notion-cli")
|
|
2946
|
+
},
|
|
2947
|
+
{ name: "Codex", dir: join3(home, ".agents", "skills", "using-notion-cli") },
|
|
2948
|
+
{
|
|
2949
|
+
name: "OpenCode",
|
|
2950
|
+
dir: join3(home, ".config", "opencode", "skills", "using-notion-cli")
|
|
2951
|
+
}
|
|
2952
|
+
];
|
|
2953
|
+
return targets.map((t) => ({
|
|
2954
|
+
...t,
|
|
2955
|
+
detected: existsSync(dirname(t.dir))
|
|
2956
|
+
}));
|
|
2957
|
+
}
|
|
2958
|
+
function installTo(source, target) {
|
|
2959
|
+
if (!existsSync(target.dir)) mkdirSync(target.dir, { recursive: true });
|
|
2960
|
+
const dest = join3(target.dir, "SKILL.md");
|
|
2961
|
+
copyFileSync(source, dest);
|
|
2962
|
+
return dest;
|
|
2963
|
+
}
|
|
2964
|
+
async function installInteractive(source, targets) {
|
|
2965
|
+
const { checkbox } = await import("@inquirer/prompts");
|
|
2966
|
+
const selected = await checkbox({
|
|
2967
|
+
message: "Install skill for which agents?",
|
|
2968
|
+
choices: targets.map((t) => ({
|
|
2969
|
+
name: `${t.name}${t.detected ? chalk.dim(" (detected)") : ""}`,
|
|
2970
|
+
value: t.name,
|
|
2971
|
+
checked: t.detected
|
|
2972
|
+
}))
|
|
2973
|
+
});
|
|
2974
|
+
if (selected.length === 0) {
|
|
2975
|
+
process.stderr.write("No agents selected.\n");
|
|
2976
|
+
return;
|
|
2977
|
+
}
|
|
2978
|
+
process.stdout.write("\n");
|
|
2979
|
+
for (const name of selected) {
|
|
2980
|
+
const target = targets.find((t) => t.name === name);
|
|
2981
|
+
if (!target) continue;
|
|
2982
|
+
const dest = installTo(source, target);
|
|
2983
|
+
process.stdout.write(` Installed: ${target.name}: ${dest}
|
|
2984
|
+
`);
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
function installNonInteractive(source, targets) {
|
|
2988
|
+
const detected = targets.filter((t) => t.detected);
|
|
2989
|
+
if (detected.length === 0) {
|
|
2990
|
+
process.stderr.write(
|
|
2991
|
+
"No agents detected. Use --path to specify install location:\n notion skill --path ~/.claude/skills/using-notion-cli/SKILL.md\n"
|
|
2992
|
+
);
|
|
2993
|
+
return;
|
|
2994
|
+
}
|
|
2995
|
+
for (const target of detected) {
|
|
2996
|
+
const dest = installTo(source, target);
|
|
2997
|
+
process.stdout.write(`Installed: ${target.name}: ${dest}
|
|
2998
|
+
`);
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
function skillCommand() {
|
|
3002
|
+
const cmd = new Command23("skill");
|
|
3003
|
+
cmd.description("Install the agent skill file for your coding agents").option("--print", "Print the skill file content instead of installing").option("--path <path>", "Install to a specific file path").action(
|
|
3004
|
+
withErrorHandling(async (opts) => {
|
|
3005
|
+
if (opts.print) {
|
|
3006
|
+
process.stdout.write(readFileSync(skillPath(), "utf-8"));
|
|
3007
|
+
return;
|
|
3008
|
+
}
|
|
3009
|
+
const source = skillPath();
|
|
3010
|
+
if (opts.path) {
|
|
3011
|
+
const dir = dirname(opts.path);
|
|
3012
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
3013
|
+
copyFileSync(source, opts.path);
|
|
3014
|
+
process.stdout.write(`Installed to ${opts.path}
|
|
3015
|
+
`);
|
|
3016
|
+
return;
|
|
3017
|
+
}
|
|
3018
|
+
const targets = getAgentTargets();
|
|
3019
|
+
const isTTY = process.stdout.isTTY ?? false;
|
|
3020
|
+
if (isTTY) {
|
|
3021
|
+
await installInteractive(source, targets);
|
|
3022
|
+
} else {
|
|
3023
|
+
installNonInteractive(source, targets);
|
|
3024
|
+
}
|
|
3025
|
+
})
|
|
3026
|
+
);
|
|
3027
|
+
return cmd;
|
|
3028
|
+
}
|
|
3029
|
+
|
|
3030
|
+
// src/commands/update.ts
|
|
3031
|
+
import { Command as Command24 } from "commander";
|
|
2904
3032
|
function collectProps3(val, acc) {
|
|
2905
3033
|
acc.push(val);
|
|
2906
3034
|
return acc;
|
|
2907
3035
|
}
|
|
2908
3036
|
function updateCommand() {
|
|
2909
|
-
const cmd = new
|
|
3037
|
+
const cmd = new Command24("update");
|
|
2910
3038
|
cmd.description("update properties on a Notion page").argument("<id/url>", "Notion page ID or URL").option(
|
|
2911
3039
|
"--prop <property=value>",
|
|
2912
3040
|
"set a property value (repeatable)",
|
|
@@ -2964,7 +3092,7 @@ function updateCommand() {
|
|
|
2964
3092
|
}
|
|
2965
3093
|
|
|
2966
3094
|
// src/commands/users.ts
|
|
2967
|
-
import { Command as
|
|
3095
|
+
import { Command as Command25 } from "commander";
|
|
2968
3096
|
function getEmailOrWorkspace(user) {
|
|
2969
3097
|
if (user.type === "person") {
|
|
2970
3098
|
return user.person.email ?? "\u2014";
|
|
@@ -2976,7 +3104,7 @@ function getEmailOrWorkspace(user) {
|
|
|
2976
3104
|
return "\u2014";
|
|
2977
3105
|
}
|
|
2978
3106
|
function usersCommand() {
|
|
2979
|
-
const cmd = new
|
|
3107
|
+
const cmd = new Command25("users");
|
|
2980
3108
|
cmd.description("list all users in the workspace").option("--json", "output as JSON").action(
|
|
2981
3109
|
withErrorHandling(async (opts) => {
|
|
2982
3110
|
if (opts.json) setOutputMode("json");
|
|
@@ -3003,11 +3131,11 @@ function usersCommand() {
|
|
|
3003
3131
|
|
|
3004
3132
|
// src/cli.ts
|
|
3005
3133
|
var __filename = fileURLToPath(import.meta.url);
|
|
3006
|
-
var __dirname =
|
|
3134
|
+
var __dirname = dirname2(__filename);
|
|
3007
3135
|
var pkg = JSON.parse(
|
|
3008
|
-
|
|
3136
|
+
readFileSync2(join4(__dirname, "../package.json"), "utf-8")
|
|
3009
3137
|
);
|
|
3010
|
-
var program = new
|
|
3138
|
+
var program = new Command26();
|
|
3011
3139
|
program.name("notion").description("Notion CLI \u2014 read Notion pages and databases from the terminal").version(pkg.version);
|
|
3012
3140
|
program.option("--verbose", "show API requests/responses").option("--color", "force color output").option("--json", "force JSON output (overrides TTY detection)").option("--md", "force markdown output for page content");
|
|
3013
3141
|
program.configureOutput({
|
|
@@ -3028,7 +3156,7 @@ program.hook("preAction", (thisCommand) => {
|
|
|
3028
3156
|
setOutputMode("md");
|
|
3029
3157
|
}
|
|
3030
3158
|
});
|
|
3031
|
-
var authCmd = new
|
|
3159
|
+
var authCmd = new Command26("auth").description("manage Notion authentication");
|
|
3032
3160
|
authCmd.action(authDefaultAction(authCmd));
|
|
3033
3161
|
authCmd.addCommand(loginCommand());
|
|
3034
3162
|
authCmd.addCommand(logoutCommand());
|
|
@@ -3038,7 +3166,7 @@ authCmd.addCommand(profileUseCommand());
|
|
|
3038
3166
|
authCmd.addCommand(profileRemoveCommand());
|
|
3039
3167
|
program.addCommand(authCmd);
|
|
3040
3168
|
program.addCommand(initCommand(), { hidden: true });
|
|
3041
|
-
var profileCmd = new
|
|
3169
|
+
var profileCmd = new Command26("profile").description(
|
|
3042
3170
|
"manage authentication profiles"
|
|
3043
3171
|
);
|
|
3044
3172
|
profileCmd.addCommand(profileListCommand());
|
|
@@ -3058,11 +3186,12 @@ program.addCommand(editPageCommand());
|
|
|
3058
3186
|
program.addCommand(updateCommand());
|
|
3059
3187
|
program.addCommand(archiveCommand());
|
|
3060
3188
|
program.addCommand(moveCommand());
|
|
3061
|
-
var dbCmd = new
|
|
3189
|
+
var dbCmd = new Command26("db").description("Database operations");
|
|
3062
3190
|
dbCmd.addCommand(dbCreateCommand());
|
|
3063
3191
|
dbCmd.addCommand(dbSchemaCommand());
|
|
3064
3192
|
dbCmd.addCommand(dbQueryCommand());
|
|
3065
3193
|
program.addCommand(dbCmd);
|
|
3066
3194
|
program.addCommand(completionCommand());
|
|
3195
|
+
program.addCommand(skillCommand());
|
|
3067
3196
|
await program.parseAsync();
|
|
3068
3197
|
//# sourceMappingURL=cli.js.map
|