ntion 0.1.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/README.md +204 -0
- package/dist/audit/log.d.ts +10 -0
- package/dist/audit/log.js +18 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +939 -0
- package/dist/commands/context.d.ts +17 -0
- package/dist/commands/context.js +35 -0
- package/dist/commands/mutation.d.ts +8 -0
- package/dist/commands/mutation.js +102 -0
- package/dist/commands/output.d.ts +7 -0
- package/dist/commands/output.js +22 -0
- package/dist/concurrency/versioning.d.ts +1 -0
- package/dist/concurrency/versioning.js +5 -0
- package/dist/config/paths.d.ts +5 -0
- package/dist/config/paths.js +26 -0
- package/dist/config/store.d.ts +5 -0
- package/dist/config/store.js +72 -0
- package/dist/config/types.d.ts +141 -0
- package/dist/config/types.js +26 -0
- package/dist/contracts/envelope.d.ts +32 -0
- package/dist/contracts/envelope.js +35 -0
- package/dist/errors/cli-error.d.ts +11 -0
- package/dist/errors/cli-error.js +59 -0
- package/dist/errors/codes.d.ts +2 -0
- package/dist/errors/codes.js +9 -0
- package/dist/idempotency/store.d.ts +34 -0
- package/dist/idempotency/store.js +120 -0
- package/dist/notion/client.d.ts +17 -0
- package/dist/notion/client.js +140 -0
- package/dist/notion/mappers.d.ts +5 -0
- package/dist/notion/mappers.js +224 -0
- package/dist/notion/markdown.d.ts +1 -0
- package/dist/notion/markdown.js +237 -0
- package/dist/notion/properties.d.ts +7 -0
- package/dist/notion/properties.js +267 -0
- package/dist/notion/repository.d.ts +142 -0
- package/dist/notion/repository.js +998 -0
- package/dist/notion/types.d.ts +4 -0
- package/dist/notion/types.js +1 -0
- package/dist/utils/json.d.ts +3 -0
- package/dist/utils/json.js +32 -0
- package/package.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# notion-cli
|
|
2
|
+
|
|
3
|
+
Token-efficient Notion CLI built for AI agents like Claude Code and Codex.
|
|
4
|
+
|
|
5
|
+
One command where the official MCP needs twelve. Compact JSON where others dump kilobytes of noise. Zero native dependencies — runs instantly via `npx`.
|
|
6
|
+
|
|
7
|
+
## Why ntion over the official Notion MCP?
|
|
8
|
+
|
|
9
|
+
The official Notion MCP server works, but it was designed for general-purpose access, not for token-constrained AI agents. Every extra byte in a response eats into your context window. Every extra round-trip adds latency and cost.
|
|
10
|
+
|
|
11
|
+
I benchmarked both tools side-by-side on real workflows:
|
|
12
|
+
|
|
13
|
+
### Individual operations
|
|
14
|
+
|
|
15
|
+
| Operation | ntion | MCP | Ratio |
|
|
16
|
+
|---|---|---|---|
|
|
17
|
+
| Search (tasks, pages only) | 743 B | 1,019 B | **1.4x smaller** |
|
|
18
|
+
| Schema (Tasks DB) | 1,868 B | 6,026 B | **3.2x smaller** |
|
|
19
|
+
| Page properties only | 1,004 B | n/a | ntion-only |
|
|
20
|
+
| Page + content (markdown) | 1,227 B | 1,263 B | ~1.0x (tie) |
|
|
21
|
+
|
|
22
|
+
### The real difference: workflows
|
|
23
|
+
|
|
24
|
+
A single "get all my tasks" workflow tells the whole story:
|
|
25
|
+
|
|
26
|
+
| Scale | ntion | MCP |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| 1 task | **1,938 B, 1 call** | 8,308 B, 3 calls (4.3x) |
|
|
29
|
+
| 10 tasks | **~5 KB, 1 call** | ~19.6 KB, 12 calls (3.9x) |
|
|
30
|
+
| 50 tasks | **~20 KB, 1 call** | ~70 KB, 52 calls (3.5x) |
|
|
31
|
+
|
|
32
|
+
### Where the savings come from
|
|
33
|
+
|
|
34
|
+
1. **Batch queries** — ntion returns N records in 1 call. The MCP requires 1 fetch per page. This is the dominant factor and it scales linearly.
|
|
35
|
+
2. **No schema bloat** — MCP's database fetch includes ~2 KB of SQLite DDL, ~800 B of XML boilerplate, and ~1.4 KB of base64 `collectionPropertyOption://` URLs that are never used for reads. ntion returns only actionable data.
|
|
36
|
+
3. **Markdown-first** — Page content defaults to markdown, matching what agents actually consume. No manual format negotiation needed.
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install -g ntion
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Or run directly without installing:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx ntion --help
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
No native compilation, no C++ toolchain required — installs in seconds.
|
|
51
|
+
|
|
52
|
+
## Quick start
|
|
53
|
+
|
|
54
|
+
### 1. Authenticate
|
|
55
|
+
|
|
56
|
+
Create an integration and grab your API key at [notion.so/profile/integrations](https://www.notion.so/profile/integrations).
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
ntion auth
|
|
60
|
+
# Paste your Notion integration token when prompted — done.
|
|
61
|
+
|
|
62
|
+
# Or pass it directly (e.g. in scripts):
|
|
63
|
+
ntion auth --token "secret_xxx"
|
|
64
|
+
|
|
65
|
+
# CI alternative — read token from an environment variable:
|
|
66
|
+
ntion auth --token-env NOTION_API_KEY
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2. Go
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Find your databases
|
|
73
|
+
ntion data-sources list --query "tasks"
|
|
74
|
+
|
|
75
|
+
# Query all tasks in one call
|
|
76
|
+
ntion data-sources query --id <data_source_id> --view full
|
|
77
|
+
|
|
78
|
+
# Read a page with its content as markdown
|
|
79
|
+
ntion pages get --id <page_id> --include-content
|
|
80
|
+
|
|
81
|
+
# Search across your workspace
|
|
82
|
+
ntion search --query "release notes" --limit 25
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Commands
|
|
86
|
+
|
|
87
|
+
### Search
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
ntion search --query "release notes" --limit 25
|
|
91
|
+
ntion search --query "infra" --object page --created-after 2026-01-01T00:00:00Z
|
|
92
|
+
ntion search --query "oncall" --scope <page_or_data_source_id> --created-by <user_id>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Data sources
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
ntion data-sources list --query "tasks"
|
|
99
|
+
ntion data-sources get --id <data_source_id> --view full
|
|
100
|
+
ntion data-sources schema --id <data_source_id>
|
|
101
|
+
ntion data-sources query --id <data_source_id> \
|
|
102
|
+
--filter-json '{"property":"Status","status":{"equals":"In Progress"}}'
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Pages
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
ntion pages get --id <page_id>
|
|
109
|
+
ntion pages get --id <page_id> --include-content --content-format markdown
|
|
110
|
+
|
|
111
|
+
ntion pages create \
|
|
112
|
+
--parent-data-source-id <data_source_id> \
|
|
113
|
+
--properties-json '{"Name":"Ship CLI","Status":"In Progress"}'
|
|
114
|
+
|
|
115
|
+
ntion pages create-bulk \
|
|
116
|
+
--parent-data-source-id <data_source_id> \
|
|
117
|
+
--items-json '[{"properties":{"Name":"Task A"}},{"properties":{"Name":"Task B"}}]' \
|
|
118
|
+
--concurrency 5
|
|
119
|
+
|
|
120
|
+
ntion pages update --id <page_id> --patch-json '{"Status":"Done"}'
|
|
121
|
+
ntion pages archive --id <page_id>
|
|
122
|
+
ntion pages unarchive --id <page_id>
|
|
123
|
+
|
|
124
|
+
ntion pages relate --from-id <page_id> --property Project --to-id <page_id>
|
|
125
|
+
ntion pages unrelate --from-id <page_id> --property Project --to-id <page_id>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Blocks
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# Read as markdown (default)
|
|
132
|
+
ntion blocks get --id <page_or_block_id> --depth 1
|
|
133
|
+
|
|
134
|
+
# Append markdown content
|
|
135
|
+
ntion blocks append --id <page_or_block_id> --markdown "# Title\n\nHello"
|
|
136
|
+
ntion blocks append --id <page_or_block_id> --markdown-file ./notes.md
|
|
137
|
+
|
|
138
|
+
# Surgical insertion
|
|
139
|
+
ntion blocks insert --parent-id <page_or_block_id> --markdown "New intro" --position start
|
|
140
|
+
ntion blocks insert --parent-id <page_or_block_id> --markdown "After this" --after-id <block_id>
|
|
141
|
+
|
|
142
|
+
# Find and replace block ranges
|
|
143
|
+
ntion blocks select \
|
|
144
|
+
--scope-id <page_or_block_id> \
|
|
145
|
+
--selector-json '{"where":{"type":"paragraph","text_contains":"TODO"}}'
|
|
146
|
+
|
|
147
|
+
ntion blocks replace-range \
|
|
148
|
+
--scope-id <page_or_block_id> \
|
|
149
|
+
--start-selector-json '{"where":{"text_contains":"Start"}}' \
|
|
150
|
+
--end-selector-json '{"where":{"text_contains":"End"}}' \
|
|
151
|
+
--markdown "Replacement content"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Health check
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
ntion doctor
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Output format
|
|
161
|
+
|
|
162
|
+
Every response follows the same envelope:
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{"ok": true, "data": {}, "meta": {"request_id": "..."}}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{"ok": false, "error": {"code": "invalid_input", "message": "...", "retryable": false}, "meta": {"request_id": "..."}}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Compact, deterministic, easy to parse — by humans or machines.
|
|
173
|
+
|
|
174
|
+
## Design principles
|
|
175
|
+
|
|
176
|
+
- **Generic** — works with any Notion workspace, no hardcoded schema assumptions
|
|
177
|
+
- **Compact** — deterministic JSON envelopes, minimal bytes per response
|
|
178
|
+
- **Safe** — automatic idempotency for all mutations, built-in conflict detection
|
|
179
|
+
- **Fast** — zero native dependencies, internal schema caching, batch operations
|
|
180
|
+
- **Agent-friendly** — designed for AI agents that pay per token
|
|
181
|
+
|
|
182
|
+
## Exit codes
|
|
183
|
+
|
|
184
|
+
| Code | Meaning |
|
|
185
|
+
|---|---|
|
|
186
|
+
| 0 | Success |
|
|
187
|
+
| 1 | Generic failure |
|
|
188
|
+
| 2 | Invalid input |
|
|
189
|
+
| 3 | Not found |
|
|
190
|
+
| 4 | Conflict |
|
|
191
|
+
| 5 | Retryable upstream error |
|
|
192
|
+
| 6 | Auth/config error |
|
|
193
|
+
|
|
194
|
+
## Storage
|
|
195
|
+
|
|
196
|
+
Config and state are stored in `~/.config/ntion/` (or `$XDG_CONFIG_HOME/ntion/`):
|
|
197
|
+
|
|
198
|
+
- `config.json` — auth and defaults
|
|
199
|
+
- `idempotency.json` — short-lived mutation dedup cache (auto-pruned)
|
|
200
|
+
- `audit.log` — local mutation audit trail
|
|
201
|
+
|
|
202
|
+
## License
|
|
203
|
+
|
|
204
|
+
MIT
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { appendFile } from "node:fs/promises";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { ensureConfigDir, getAuditLogPath } from "../config/paths.js";
|
|
4
|
+
function hashKey(value) {
|
|
5
|
+
if (!value) {
|
|
6
|
+
return undefined;
|
|
7
|
+
}
|
|
8
|
+
return createHash("sha256").update(value).digest("hex");
|
|
9
|
+
}
|
|
10
|
+
export async function appendAuditLog(event) {
|
|
11
|
+
ensureConfigDir();
|
|
12
|
+
const payload = {
|
|
13
|
+
...event,
|
|
14
|
+
idempotency_key_hash: hashKey(event.idempotency_key),
|
|
15
|
+
};
|
|
16
|
+
delete payload.idempotency_key;
|
|
17
|
+
await appendFile(getAuditLogPath(), `${JSON.stringify(payload)}\n`, "utf8");
|
|
18
|
+
}
|
package/dist/cli.d.ts
ADDED