@yeongjaeyou/claude-code-config 0.4.0 → 0.5.1

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.
Files changed (38) hide show
  1. package/.claude/commands/ask-codex.md +131 -345
  2. package/.claude/commands/ask-deepwiki.md +15 -15
  3. package/.claude/commands/ask-gemini.md +134 -352
  4. package/.claude/commands/code-review.md +41 -40
  5. package/.claude/commands/commit-and-push.md +35 -36
  6. package/.claude/commands/council.md +318 -0
  7. package/.claude/commands/edit-notebook.md +34 -33
  8. package/.claude/commands/gh/create-issue-label.md +19 -17
  9. package/.claude/commands/gh/decompose-issue.md +66 -65
  10. package/.claude/commands/gh/init-project.md +46 -52
  11. package/.claude/commands/gh/post-merge.md +74 -79
  12. package/.claude/commands/gh/resolve-issue.md +38 -46
  13. package/.claude/commands/plan.md +15 -14
  14. package/.claude/commands/tm/convert-prd.md +53 -53
  15. package/.claude/commands/tm/post-merge.md +92 -112
  16. package/.claude/commands/tm/resolve-issue.md +148 -154
  17. package/.claude/commands/tm/review-prd-with-codex.md +272 -279
  18. package/.claude/commands/tm/sync-to-github.md +189 -212
  19. package/.claude/guidelines/cv-guidelines.md +30 -0
  20. package/.claude/guidelines/id-reference.md +34 -0
  21. package/.claude/guidelines/work-guidelines.md +17 -0
  22. package/.claude/skills/notion-md-uploader/SKILL.md +252 -0
  23. package/.claude/skills/notion-md-uploader/references/notion_block_types.md +323 -0
  24. package/.claude/skills/notion-md-uploader/references/setup_guide.md +156 -0
  25. package/.claude/skills/notion-md-uploader/scripts/__pycache__/markdown_parser.cpython-311.pyc +0 -0
  26. package/.claude/skills/notion-md-uploader/scripts/__pycache__/notion_client.cpython-311.pyc +0 -0
  27. package/.claude/skills/notion-md-uploader/scripts/__pycache__/notion_converter.cpython-311.pyc +0 -0
  28. package/.claude/skills/notion-md-uploader/scripts/markdown_parser.py +607 -0
  29. package/.claude/skills/notion-md-uploader/scripts/notion_client.py +337 -0
  30. package/.claude/skills/notion-md-uploader/scripts/notion_converter.py +477 -0
  31. package/.claude/skills/notion-md-uploader/scripts/upload_md.py +298 -0
  32. package/.claude/skills/skill-creator/LICENSE.txt +202 -0
  33. package/.claude/skills/skill-creator/SKILL.md +209 -0
  34. package/.claude/skills/skill-creator/scripts/init_skill.py +303 -0
  35. package/.claude/skills/skill-creator/scripts/package_skill.py +110 -0
  36. package/.claude/skills/skill-creator/scripts/quick_validate.py +65 -0
  37. package/README.md +159 -129
  38. package/package.json +1 -1
@@ -0,0 +1,252 @@
1
+ ---
2
+ name: notion-md-uploader
3
+ description: Upload Markdown files to Notion pages with full formatting support including headings, lists, code blocks, images, tables, callouts, and todos. This skill should be used when users want to publish documentation, reports, or notes from Markdown files to their Notion workspace with preserved formatting and automatic image uploads.
4
+ ---
5
+
6
+ # Notion Markdown Uploader
7
+
8
+ ## Overview
9
+
10
+ To upload Markdown files to Notion with full formatting preservation, use this skill. It converts Markdown syntax to Notion blocks and handles local image uploads automatically via the Notion File Upload API.
11
+
12
+ ## Prerequisites
13
+
14
+ Before using this skill, ensure the following setup is complete:
15
+
16
+ ### 1. Notion Integration Setup
17
+
18
+ 1. Visit https://www.notion.so/my-integrations
19
+ 2. Click "+ New integration"
20
+ 3. Configure the integration:
21
+ - Name: "MD Uploader" (or preferred name)
22
+ - Associated workspace: Select target workspace
23
+ - Capabilities: Enable Read, Update, and Insert content
24
+ 4. Click "Submit" and copy the API key
25
+
26
+ ### 2. Environment Configuration
27
+
28
+ Set the API key in the environment:
29
+
30
+ ```bash
31
+ # In .env file
32
+ NOTION_API_KEY=ntn_xxxxx
33
+
34
+ # Or export directly
35
+ export NOTION_API_KEY=ntn_xxxxx
36
+ ```
37
+
38
+ ### 3. Page Access
39
+
40
+ Share the target parent page with the integration:
41
+
42
+ 1. Open the Notion page where documents will be uploaded
43
+ 2. Click "..." menu (top right)
44
+ 3. Select "Add connections"
45
+ 4. Find and select the integration
46
+
47
+ ## Quick Start
48
+
49
+ ### Basic Upload
50
+
51
+ ```bash
52
+ uv run python .claude/skills/notion-md-uploader/scripts/upload_md.py \
53
+ docs/report.md \
54
+ --parent-page-id "abc123def456"
55
+ ```
56
+
57
+ ### With Custom Title
58
+
59
+ ```bash
60
+ uv run python .claude/skills/notion-md-uploader/scripts/upload_md.py \
61
+ README.md \
62
+ --parent-page-id "abc123def456" \
63
+ --title "Project Documentation"
64
+ ```
65
+
66
+ ### Dry Run (Preview)
67
+
68
+ ```bash
69
+ uv run python .claude/skills/notion-md-uploader/scripts/upload_md.py \
70
+ docs/analysis.md \
71
+ --parent-page-id "abc123def456" \
72
+ --dry-run
73
+ ```
74
+
75
+ ## Supported Markdown Elements
76
+
77
+ | Element | Markdown Syntax | Notion Block |
78
+ |---------|----------------|--------------|
79
+ | Heading 1 | `# Title` | heading_1 |
80
+ | Heading 2 | `## Subtitle` | heading_2 |
81
+ | Heading 3 | `### Section` | heading_3 |
82
+ | Paragraph | Plain text | paragraph |
83
+ | Bold | `**text**` | bold annotation |
84
+ | Italic | `*text*` | italic annotation |
85
+ | Strikethrough | `~~text~~` | strikethrough annotation |
86
+ | Inline Code | `` `code` `` | code annotation |
87
+ | Link | `[text](url)` | link |
88
+ | Bulleted List | `- item` | bulleted_list_item |
89
+ | Numbered List | `1. item` | numbered_list_item |
90
+ | Code Block | ` ```lang ``` ` | code (with language) |
91
+ | Quote | `> text` | quote |
92
+ | Divider | `---` | divider |
93
+ | Image | `![alt](path)` | image (with upload) |
94
+ | Table | `\| A \| B \|` | table + table_row |
95
+ | Todo | `- [ ] task` | to_do |
96
+ | Callout | `> [!NOTE]` | callout |
97
+
98
+ ## Image Handling
99
+
100
+ ### External URLs
101
+
102
+ Images with HTTP/HTTPS URLs are embedded directly:
103
+
104
+ ```markdown
105
+ ![Logo](https://example.com/logo.png)
106
+ ```
107
+
108
+ ### Local Images
109
+
110
+ Local images are automatically uploaded to Notion:
111
+
112
+ ```markdown
113
+ ![Chart](./figures/chart.png)
114
+ ![Result](../outputs/result.jpg)
115
+ ```
116
+
117
+ The uploader:
118
+ 1. Detects local image paths
119
+ 2. Creates a Notion File Upload object
120
+ 3. Uploads the image binary
121
+ 4. Attaches the uploaded file to an image block
122
+
123
+ Supported formats: `.png`, `.jpg`, `.jpeg`, `.gif`, `.svg`, `.webp`
124
+
125
+ ## Finding Parent Page ID
126
+
127
+ ### Option 1: From URL
128
+
129
+ Copy the page URL and extract the ID:
130
+
131
+ ```
132
+ https://www.notion.so/workspace/Page-Title-abc123def456ghi789
133
+ ^^^^^^^^^^^^^^^^^^^^^^^^
134
+ This is the page ID (32 chars)
135
+ ```
136
+
137
+ ### Option 2: Use Search API
138
+
139
+ ```bash
140
+ # The script accepts full URLs
141
+ uv run python .claude/skills/notion-md-uploader/scripts/upload_md.py \
142
+ report.md \
143
+ --parent-page-id "https://www.notion.so/workspace/Reports-abc123"
144
+ ```
145
+
146
+ ## Workflow Decision Tree
147
+
148
+ ```
149
+ User wants to upload Markdown to Notion
150
+ |
151
+ +-- Is NOTION_API_KEY set?
152
+ | +-- No: Set up integration (see Prerequisites)
153
+ | +-- Yes: Continue
154
+ |
155
+ +-- Is parent page shared with integration?
156
+ | +-- No: Add connection to page
157
+ | +-- Yes: Continue
158
+ |
159
+ +-- Run upload script
160
+ +-- Has local images?
161
+ | +-- Yes: Images uploaded automatically
162
+ | +-- No: Continue
163
+ |
164
+ +-- Success: Page created with URL
165
+ ```
166
+
167
+ ## Scripts
168
+
169
+ ### upload_md.py
170
+
171
+ Main upload script. Run with `--help` for full options:
172
+
173
+ ```bash
174
+ uv run python .claude/skills/notion-md-uploader/scripts/upload_md.py --help
175
+ ```
176
+
177
+ Arguments:
178
+ - `md_file`: Path to Markdown file (required)
179
+ - `--parent-page-id`, `-p`: Notion parent page ID or URL (required)
180
+ - `--title`, `-t`: Custom page title (optional)
181
+ - `--dry-run`: Preview without uploading (optional)
182
+
183
+ ### notion_client.py
184
+
185
+ Low-level Notion API client. Can be imported for custom workflows:
186
+
187
+ ```python
188
+ from scripts.notion_client import NotionClient
189
+
190
+ client = NotionClient()
191
+ page = client.create_page(
192
+ parent_page_id="abc123",
193
+ title="My Page",
194
+ children=[...]
195
+ )
196
+ ```
197
+
198
+ ### markdown_parser.py
199
+
200
+ Markdown parser for converting to AST. Useful for custom processing:
201
+
202
+ ```python
203
+ from scripts.markdown_parser import MarkdownParser
204
+
205
+ parser = MarkdownParser()
206
+ blocks = parser.parse(markdown_text)
207
+ ```
208
+
209
+ ### notion_converter.py
210
+
211
+ Converts parsed Markdown blocks to Notion API format:
212
+
213
+ ```python
214
+ from scripts.notion_converter import NotionBlockConverter
215
+
216
+ converter = NotionBlockConverter(image_uploader=upload_func)
217
+ notion_blocks = converter.convert_blocks(blocks)
218
+ ```
219
+
220
+ ## Limitations
221
+
222
+ 1. **Block Limit**: Notion API allows max 100 blocks per request. The script handles this by chunking.
223
+ 2. **File Size**: Images must be under 20MB (5MB for free workspaces)
224
+ 3. **Nested Lists**: Currently flattened (nested items become top-level)
225
+ 4. **Complex Tables**: Cell formatting may be simplified
226
+ 5. **Attachments**: Only images are auto-uploaded; other files need manual handling
227
+
228
+ ## Troubleshooting
229
+
230
+ ### "Could not find page" Error
231
+
232
+ The parent page is not shared with the integration:
233
+ 1. Open the page in Notion
234
+ 2. Click "..." > "Add connections"
235
+ 3. Select your integration
236
+
237
+ ### "unauthorized" Error
238
+
239
+ API key is invalid or not set:
240
+ 1. Check `NOTION_API_KEY` in `.env`
241
+ 2. Verify the key at https://www.notion.so/my-integrations
242
+
243
+ ### Image Upload Failed
244
+
245
+ 1. Check file exists at the specified path
246
+ 2. Verify file size is under limit
247
+ 3. Ensure file format is supported
248
+
249
+ ## References
250
+
251
+ - `references/notion_block_types.md` - Detailed Notion block type reference
252
+ - `references/setup_guide.md` - Step-by-step integration setup guide
@@ -0,0 +1,323 @@
1
+ # Notion Block Types Reference
2
+
3
+ This document provides a comprehensive reference for Notion API block types used by the uploader.
4
+
5
+ ## Block Object Structure
6
+
7
+ Every Notion block follows this base structure:
8
+
9
+ ```json
10
+ {
11
+ "object": "block",
12
+ "id": "block-uuid",
13
+ "type": "block_type",
14
+ "created_time": "2024-01-01T00:00:00.000Z",
15
+ "last_edited_time": "2024-01-01T00:00:00.000Z",
16
+ "has_children": false,
17
+ "{block_type}": {
18
+ // type-specific properties
19
+ }
20
+ }
21
+ ```
22
+
23
+ ## Text Block Types
24
+
25
+ ### Paragraph
26
+
27
+ ```json
28
+ {
29
+ "type": "paragraph",
30
+ "paragraph": {
31
+ "rich_text": [
32
+ {
33
+ "type": "text",
34
+ "text": { "content": "Paragraph text" },
35
+ "annotations": {
36
+ "bold": false,
37
+ "italic": false,
38
+ "strikethrough": false,
39
+ "underline": false,
40
+ "code": false,
41
+ "color": "default"
42
+ }
43
+ }
44
+ ],
45
+ "color": "default"
46
+ }
47
+ }
48
+ ```
49
+
50
+ ### Headings
51
+
52
+ ```json
53
+ // heading_1, heading_2, heading_3
54
+ {
55
+ "type": "heading_1",
56
+ "heading_1": {
57
+ "rich_text": [{"type": "text", "text": {"content": "Heading"}}],
58
+ "color": "default",
59
+ "is_toggleable": false
60
+ }
61
+ }
62
+ ```
63
+
64
+ ### Quote
65
+
66
+ ```json
67
+ {
68
+ "type": "quote",
69
+ "quote": {
70
+ "rich_text": [{"type": "text", "text": {"content": "Quote text"}}],
71
+ "color": "default"
72
+ }
73
+ }
74
+ ```
75
+
76
+ ### Callout
77
+
78
+ ```json
79
+ {
80
+ "type": "callout",
81
+ "callout": {
82
+ "rich_text": [{"type": "text", "text": {"content": "Note content"}}],
83
+ "icon": {"type": "emoji", "emoji": "i"},
84
+ "color": "default"
85
+ }
86
+ }
87
+ ```
88
+
89
+ ## List Block Types
90
+
91
+ ### Bulleted List Item
92
+
93
+ ```json
94
+ {
95
+ "type": "bulleted_list_item",
96
+ "bulleted_list_item": {
97
+ "rich_text": [{"type": "text", "text": {"content": "Item"}}],
98
+ "color": "default",
99
+ "children": [] // optional nested blocks
100
+ }
101
+ }
102
+ ```
103
+
104
+ ### Numbered List Item
105
+
106
+ ```json
107
+ {
108
+ "type": "numbered_list_item",
109
+ "numbered_list_item": {
110
+ "rich_text": [{"type": "text", "text": {"content": "Item"}}],
111
+ "color": "default",
112
+ "children": []
113
+ }
114
+ }
115
+ ```
116
+
117
+ ### To-Do
118
+
119
+ ```json
120
+ {
121
+ "type": "to_do",
122
+ "to_do": {
123
+ "rich_text": [{"type": "text", "text": {"content": "Task"}}],
124
+ "checked": false,
125
+ "color": "default"
126
+ }
127
+ }
128
+ ```
129
+
130
+ ## Code Block
131
+
132
+ ```json
133
+ {
134
+ "type": "code",
135
+ "code": {
136
+ "rich_text": [{"type": "text", "text": {"content": "print('hello')"}}],
137
+ "language": "python",
138
+ "caption": []
139
+ }
140
+ }
141
+ ```
142
+
143
+ ### Supported Languages
144
+
145
+ ```
146
+ abap, arduino, bash, basic, c, clojure, coffeescript, c++, c#, css,
147
+ dart, diff, docker, elixir, elm, erlang, flow, fortran, f#, gherkin,
148
+ glsl, go, graphql, groovy, haskell, html, java, javascript, json,
149
+ julia, kotlin, latex, less, lisp, livescript, lua, makefile, markdown,
150
+ markup, matlab, mermaid, nix, objective-c, ocaml, pascal, perl, php,
151
+ plain text, powershell, prolog, protobuf, python, r, reason, ruby,
152
+ rust, sass, scala, scheme, scss, shell, sql, swift, typescript,
153
+ vb.net, verilog, vhdl, visual basic, webassembly, xml, yaml
154
+ ```
155
+
156
+ ## Media Block Types
157
+
158
+ ### Image
159
+
160
+ External URL:
161
+ ```json
162
+ {
163
+ "type": "image",
164
+ "image": {
165
+ "type": "external",
166
+ "external": {"url": "https://example.com/image.png"},
167
+ "caption": []
168
+ }
169
+ }
170
+ ```
171
+
172
+ File Upload:
173
+ ```json
174
+ {
175
+ "type": "image",
176
+ "image": {
177
+ "type": "file_upload",
178
+ "file_upload": {"id": "file-upload-uuid"},
179
+ "caption": []
180
+ }
181
+ }
182
+ ```
183
+
184
+ ### File
185
+
186
+ ```json
187
+ {
188
+ "type": "file",
189
+ "file": {
190
+ "type": "file_upload",
191
+ "file_upload": {"id": "file-upload-uuid"},
192
+ "name": "document.pdf"
193
+ }
194
+ }
195
+ ```
196
+
197
+ ## Table Block Types
198
+
199
+ ### Table
200
+
201
+ ```json
202
+ {
203
+ "type": "table",
204
+ "table": {
205
+ "table_width": 3,
206
+ "has_column_header": true,
207
+ "has_row_header": false,
208
+ "children": [/* table_row blocks */]
209
+ }
210
+ }
211
+ ```
212
+
213
+ ### Table Row
214
+
215
+ ```json
216
+ {
217
+ "type": "table_row",
218
+ "table_row": {
219
+ "cells": [
220
+ [{"type": "text", "text": {"content": "Cell 1"}}],
221
+ [{"type": "text", "text": {"content": "Cell 2"}}],
222
+ [{"type": "text", "text": {"content": "Cell 3"}}]
223
+ ]
224
+ }
225
+ }
226
+ ```
227
+
228
+ ## Other Block Types
229
+
230
+ ### Divider
231
+
232
+ ```json
233
+ {
234
+ "type": "divider",
235
+ "divider": {}
236
+ }
237
+ ```
238
+
239
+ ### Bookmark
240
+
241
+ ```json
242
+ {
243
+ "type": "bookmark",
244
+ "bookmark": {
245
+ "url": "https://example.com",
246
+ "caption": []
247
+ }
248
+ }
249
+ ```
250
+
251
+ ## Rich Text Object
252
+
253
+ Rich text objects are used within blocks for formatted text:
254
+
255
+ ```json
256
+ {
257
+ "type": "text",
258
+ "text": {
259
+ "content": "Text content",
260
+ "link": {"url": "https://example.com"} // optional
261
+ },
262
+ "annotations": {
263
+ "bold": false,
264
+ "italic": false,
265
+ "strikethrough": false,
266
+ "underline": false,
267
+ "code": false,
268
+ "color": "default"
269
+ },
270
+ "plain_text": "Text content",
271
+ "href": null
272
+ }
273
+ ```
274
+
275
+ ### Available Colors
276
+
277
+ ```
278
+ default, gray, brown, orange, yellow, green, blue, purple, pink, red
279
+ gray_background, brown_background, orange_background, yellow_background,
280
+ green_background, blue_background, purple_background, pink_background, red_background
281
+ ```
282
+
283
+ ## File Upload Object
284
+
285
+ Used for uploading local files to Notion:
286
+
287
+ ```json
288
+ {
289
+ "object": "file_upload",
290
+ "id": "uuid",
291
+ "status": "uploaded",
292
+ "filename": "image.png",
293
+ "content_type": "image/png",
294
+ "content_length": 12345
295
+ }
296
+ ```
297
+
298
+ ## API Endpoints
299
+
300
+ | Endpoint | Method | Description |
301
+ |----------|--------|-------------|
302
+ | `/v1/pages` | POST | Create a new page |
303
+ | `/v1/blocks/{id}/children` | PATCH | Append blocks to page |
304
+ | `/v1/blocks/{id}/children` | GET | Retrieve child blocks |
305
+ | `/v1/file_uploads` | POST | Create file upload |
306
+ | `/v1/file_uploads/{id}/send` | POST | Upload file content |
307
+
308
+ ## Rate Limits
309
+
310
+ - Average: 3 requests per second
311
+ - Burst: Up to 3 concurrent requests
312
+ - Block limit: 100 blocks per request
313
+
314
+ ## Error Codes
315
+
316
+ | Code | Description |
317
+ |------|-------------|
318
+ | 400 | Invalid request body |
319
+ | 401 | Invalid API key |
320
+ | 403 | Access denied to resource |
321
+ | 404 | Resource not found |
322
+ | 429 | Rate limit exceeded |
323
+ | 500 | Internal server error |
@@ -0,0 +1,156 @@
1
+ # Notion Integration Setup Guide
2
+
3
+ This guide walks through setting up a Notion integration for the Markdown uploader.
4
+
5
+ ## Step 1: Create a Notion Integration
6
+
7
+ 1. Go to https://www.notion.so/my-integrations
8
+ 2. Click "+ New integration"
9
+ 3. Fill in the form:
10
+ - **Name**: Choose a descriptive name (e.g., "MD Uploader")
11
+ - **Logo**: Optional
12
+ - **Associated workspace**: Select your target workspace
13
+ 4. Click "Submit"
14
+
15
+ ## Step 2: Configure Capabilities
16
+
17
+ After creation, configure the integration's capabilities:
18
+
19
+ ### Content Capabilities
20
+
21
+ Enable all of these for full functionality:
22
+
23
+ - **Read content**: Required for verifying pages
24
+ - **Update content**: Required for appending blocks
25
+ - **Insert content**: Required for creating pages and uploading files
26
+
27
+ ### User Capabilities
28
+
29
+ - **Read user information including email addresses**: Not required
30
+ - **No user information**: Sufficient for this tool
31
+
32
+ ### Comment Capabilities
33
+
34
+ - Not required for the uploader
35
+
36
+ ## Step 3: Copy the API Key
37
+
38
+ 1. On the integration page, find the "Internal Integration Secret"
39
+ 2. Click "Show" then "Copy"
40
+ 3. Store this securely - it cannot be retrieved again
41
+
42
+ ## Step 4: Set Environment Variable
43
+
44
+ ### Option A: .env File (Recommended)
45
+
46
+ Create or edit `.env` in your project root:
47
+
48
+ ```bash
49
+ NOTION_API_KEY=ntn_xxxxxxxxxxxxxxxxxxxxx
50
+ ```
51
+
52
+ ### Option B: Export in Shell
53
+
54
+ ```bash
55
+ export NOTION_API_KEY=ntn_xxxxxxxxxxxxxxxxxxxxx
56
+ ```
57
+
58
+ ### Option C: Session-specific
59
+
60
+ ```bash
61
+ NOTION_API_KEY=ntn_xxx uv run python upload_md.py ...
62
+ ```
63
+
64
+ ## Step 5: Share Pages with Integration
65
+
66
+ The integration can only access pages explicitly shared with it.
67
+
68
+ ### Sharing a Single Page
69
+
70
+ 1. Open the target page in Notion
71
+ 2. Click the "..." menu (top right corner)
72
+ 3. Select "Add connections"
73
+ 4. Search for your integration name
74
+ 5. Click to add
75
+
76
+ ### Sharing a Database
77
+
78
+ Same process as above, but on the database page.
79
+
80
+ ### Sharing Child Pages
81
+
82
+ When you share a parent page, all child pages are also accessible.
83
+
84
+ ## Step 6: Find Page ID
85
+
86
+ ### From Browser URL
87
+
88
+ 1. Open the page in Notion
89
+ 2. Copy the URL: `https://www.notion.so/Page-Title-abc123def456ghi789jkl012`
90
+ 3. The ID is the 32-character string at the end: `abc123def456ghi789jkl012`
91
+
92
+ ### From Share Link
93
+
94
+ 1. Click "Share" > "Copy link"
95
+ 2. Extract the ID from the URL
96
+
97
+ ### Format Requirements
98
+
99
+ The script accepts:
100
+ - Raw 32-character ID: `abc123def456ghi789jkl012`
101
+ - UUID format: `abc123de-f456-ghi7-89jk-l012mnopqrst`
102
+ - Full URL: `https://www.notion.so/...`
103
+
104
+ ## Verification
105
+
106
+ Test the setup:
107
+
108
+ ```bash
109
+ uv run python .claude/skills/notion-md-uploader/scripts/upload_md.py \
110
+ --help
111
+ ```
112
+
113
+ Then try a dry run:
114
+
115
+ ```bash
116
+ uv run python .claude/skills/notion-md-uploader/scripts/upload_md.py \
117
+ README.md \
118
+ --parent-page-id YOUR_PAGE_ID \
119
+ --dry-run
120
+ ```
121
+
122
+ ## Troubleshooting
123
+
124
+ ### "unauthorized" Error
125
+
126
+ 1. Verify the API key is correct
127
+ 2. Check there are no extra spaces or newlines
128
+ 3. Regenerate the key if needed
129
+
130
+ ### "Could not find page" Error
131
+
132
+ 1. Confirm the page ID is correct
133
+ 2. Ensure the page is shared with the integration
134
+ 3. Check the integration has the required capabilities
135
+
136
+ ### "validation_error" for File Upload
137
+
138
+ 1. File may exceed size limit (20MB, or 5MB for free plans)
139
+ 2. File type may not be supported
140
+ 3. Check the file exists at the specified path
141
+
142
+ ## Security Best Practices
143
+
144
+ 1. **Never commit API keys**: Add `.env` to `.gitignore`
145
+ 2. **Use environment variables**: Don't hardcode keys
146
+ 3. **Limit access**: Only share necessary pages
147
+ 4. **Rotate keys**: Regenerate periodically
148
+ 5. **Monitor usage**: Check integration logs in Notion
149
+
150
+ ## Workspace Limits
151
+
152
+ | Feature | Free | Plus/Business |
153
+ |---------|------|---------------|
154
+ | Max file size | 5 MB | 5 GB |
155
+ | API rate limit | 3 req/s | 3 req/s |
156
+ | Blocks per request | 100 | 100 |