feat(skills): bulk-add root SKILL.md to 61 skills (native Agent Skills loadability)
Run the additive migration pass from SKILL-MIGRATION-GUIDE: generate a root SKILL.md for every skill that lacked one, copied from its desktop/SKILL.md (or code/SKILL.md), with name set to the directory name and description + body preserved verbatim. - scripts/migrate_skill_root.py: the reusable, non-destructive migrator (dry-run default). - 61 new root SKILL.md (desktop source for most; code/SKILL.md for 61/62/92). - Untouched: 16/17/95 (already had root); desktop/ and code/ packaging left intact. - All 64 root SKILL.md validate: frontmatter <=1024, kebab name, description present. Still MANUAL (no SKILL.md source — commands/README only), need hand-authored root SKILL.md: 81-mac-optimizer, 90-reference-curator, 91-multi-agent-guide, 94-dintel-bootstrap. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
151
custom-skills/32-notion-writer/SKILL.md
Normal file
151
custom-skills/32-notion-writer/SKILL.md
Normal file
@@ -0,0 +1,151 @@
|
||||
---
|
||||
name: 32-notion-writer
|
||||
description: |
|
||||
Markdown to Notion page writer with database row creation support.
|
||||
Triggers: write to Notion, export to Notion, push content, create Notion page.
|
||||
---
|
||||
|
||||
# Notion Writer Skill
|
||||
|
||||
Push markdown content to Notion pages or databases via Claude Code.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python virtual environment at `~/Project/our-claude-skills/custom-skills/02-notion-writer/code/scripts/venv`
|
||||
- Notion integration token (preferred: stored in 1Password — see [Credential handling](#credential-handling) below)
|
||||
- Target pages/databases must be shared with the integration in Notion (Database/Page → ⋯ → Connections → add integration)
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
cd ~/Project/our-claude-skills/custom-skills/02-notion-writer/code/scripts
|
||||
source venv/bin/activate
|
||||
```
|
||||
|
||||
## Credential handling
|
||||
|
||||
**Do NOT store the Notion API key in a `.env` file.** A long-lived plaintext secret on disk is unnecessary risk — Notion integration tokens grant write access to every page/database the integration is connected to.
|
||||
|
||||
### Preferred: fetch from 1Password at runtime
|
||||
|
||||
Store the token once in 1Password (Item: `Notion - Claude Agent`, Vault: `Development`, Field: `api-key`), then fetch on each invocation. The token lives only in process memory — never on disk, never in shell history.
|
||||
|
||||
```bash
|
||||
# One-shot push — token fetched + used + discarded in a single shell line
|
||||
NOTION_API_KEY="$(op read 'op://Development/Notion - Claude Agent/api-key')" \
|
||||
python notion_writer.py --test
|
||||
```
|
||||
|
||||
For repeated use in the same session, scope the variable to a subshell so it leaves no trace:
|
||||
|
||||
```bash
|
||||
(
|
||||
export NOTION_API_KEY="$(op read 'op://Development/Notion - Claude Agent/api-key')"
|
||||
python notion_writer.py --database "$DB_URL" --title "Foo" --file foo.md
|
||||
python notion_writer.py --database "$DB_URL" --title "Bar" --file bar.md
|
||||
)
|
||||
# NOTION_API_KEY is gone from the parent shell
|
||||
```
|
||||
|
||||
### Field name conventions in 1Password
|
||||
|
||||
When you create the Notion integration item, use these field names:
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| `api-key` (CONCEALED) | The integration token (`ntn_...` for current API, `secret_...` for legacy) |
|
||||
| `username` | Integration display name (e.g. "Claude Agent") |
|
||||
| `hostname` | `https://api.notion.com` |
|
||||
| `notesPlain` | Vault for which workspace + which databases this integration is connected to |
|
||||
|
||||
The script reads `NOTION_API_KEY` from the environment — it does not call `op` itself. This keeps the script free of `op`-specific dependencies and lets the same script work in CI/CD environments that inject the secret differently (GitHub Actions secrets, Vault, etc.).
|
||||
|
||||
### Fallback: `.env` file (discouraged)
|
||||
|
||||
If you must use a file (e.g. running on a host without 1Password CLI), place it at `~/Project/our-claude-skills/custom-skills/32-notion-writer/code/.env` with:
|
||||
|
||||
```
|
||||
NOTION_API_KEY=ntn_xxxxxxxx_paste_your_token_here
|
||||
```
|
||||
|
||||
Set the file to mode `600` (`chmod 600 .env`) and never commit it. Add `.env` to `.gitignore` if not already.
|
||||
|
||||
### What NEVER to do
|
||||
|
||||
- ❌ Echo the token to stdout: `echo "$NOTION_API_KEY"` (lands in terminal scrollback + shell history if the var was inline)
|
||||
- ❌ Pass the token as a CLI argument: `--api-key "ntn_..."` (visible in `ps`, history, telemetry)
|
||||
- ❌ Commit a `.env` file to git, even briefly (commit history is forever)
|
||||
- ❌ Paste the token into chat / issues / PRs — assume any pasted secret is compromised within minutes
|
||||
|
||||
### Token rotation
|
||||
|
||||
If a token is exposed (or you reasonably suspect it might be):
|
||||
|
||||
1. Go to <https://www.notion.so/my-integrations> → open the integration → **Reset** the secret
|
||||
2. Update the 1Password item's `api-key` field with the new token
|
||||
3. Verify with `NOTION_API_KEY="$(op read ...)" python notion_writer.py --test`
|
||||
4. The old token is invalidated immediately — any leftover scripts using it will fail loudly
|
||||
|
||||
## Commands
|
||||
|
||||
### Test Connection
|
||||
```bash
|
||||
python notion_writer.py --test
|
||||
```
|
||||
|
||||
### List Accessible Content
|
||||
```bash
|
||||
python notion_writer.py --list
|
||||
python notion_writer.py --list --filter pages
|
||||
python notion_writer.py --list --filter databases
|
||||
```
|
||||
|
||||
### Get Page/Database Info
|
||||
```bash
|
||||
python notion_writer.py -p PAGE_URL --info
|
||||
python notion_writer.py -d DATABASE_URL --info
|
||||
```
|
||||
|
||||
### Write to Page
|
||||
```bash
|
||||
# Append content
|
||||
python notion_writer.py -p PAGE_URL -f content.md
|
||||
|
||||
# Replace content
|
||||
python notion_writer.py -p PAGE_URL -f content.md --replace
|
||||
|
||||
# From stdin
|
||||
cat report.md | python notion_writer.py -p PAGE_URL --stdin
|
||||
```
|
||||
|
||||
### Create Database Row
|
||||
```bash
|
||||
python notion_writer.py -d DATABASE_URL -t "Entry Title" -f content.md
|
||||
```
|
||||
|
||||
## Supported Markdown
|
||||
|
||||
| Markdown | Notion Block |
|
||||
|----------|--------------|
|
||||
| `# Heading` | Heading 1 |
|
||||
| `## Heading` | Heading 2 |
|
||||
| `### Heading` | Heading 3 |
|
||||
| `- item` | Bulleted list |
|
||||
| `1. item` | Numbered list |
|
||||
| `- [ ] task` | To-do (unchecked) |
|
||||
| `- [x] task` | To-do (checked) |
|
||||
| `> quote` | Quote |
|
||||
| `` ```code``` `` | Code block |
|
||||
| `---` | Divider |
|
||||
| Paragraphs | Paragraph |
|
||||
|
||||
## Workflow Example
|
||||
|
||||
Integrate with Jamie YouTube Manager to log video info:
|
||||
```bash
|
||||
# Check video and save to markdown
|
||||
python jamie_youtube_api_test.py VIDEO_URL
|
||||
|
||||
# Write to Notion
|
||||
python notion_writer.py -p LOG_PAGE_URL -f output/video_status.md
|
||||
```
|
||||
Reference in New Issue
Block a user