From gbrain-notion-sync
Sync Notion PAI second-brain (Projects, To-Do, Inbox, Knowledge Base) into the local gbrain knowledge graph for fast Claude Code retrieval. One-way pull only (Notion is source of truth). Includes interactive first-time init, Windows Task Scheduler installer, and gbrain post-processing hooks. Use when the user says /notion-sync, init notion sync, setup notion sync, sync notion, pull notion, refresh brain, schedule notion sync, install notion sync task, run notion postprocess, notion sync status, notion sync doctor, 初始化 notion, 設定 notion sync, 同步 notion, 拉取 notion, 安裝定時同步, 跑後處理, brain 沒更新, 檢查 notion 同步. Sub-commands: init, setup, pull, schedule, postprocess, status, doctor. Do NOT use for: writing or updating Notion page content (use Notion MCP tools like notion-update-page), general gbrain queries (use mcp__gbrain__* directly), installing gbrain itself (use gbrain CLI per RUNBOOK.md Step 1), or pushing local changes back to Notion (not yet implemented — planned for v0.2).
How this skill is triggered — by the user, by Claude, or both
Slash command
/gbrain-notion-sync:notion-sync [init|setup|pull|schedule|postprocess|status|doctor][init|setup|pull|schedule|postprocess|status|doctor]The summary Claude sees in its skill listing — used to decide when to auto-load this skill
One-way Notion PAI to gbrain sync pipeline, packaged as a Claude Code plugin.
One-way Notion PAI to gbrain sync pipeline, packaged as a Claude Code plugin.
Notion is source of truth; gbrain is an agent-friendly local mirror with hybrid search and graph traversal.
/notion-sync initInteractive first-time setup. Walks the user through creating .env with
all seven required keys, validating each as it is collected. Use this
instead of asking the user to manually copy .env.example and edit values.
The flow is conversational: ask one thing at a time, validate via API
call before moving on, and write .env only once everything checks out.
.env and confirm intenttest -f "$CLAUDE_PLUGIN_ROOT/.env" && echo "EXISTS" || echo "MISSING"
If EXISTS, use AskUserQuestion to ask whether to (a) back up to
.env.bak.<timestamp> then overwrite, (b) keep existing and exit, or
(c) abort. If MISSING, proceed.
If user picks "back up", run:
cp "$CLAUDE_PLUGIN_ROOT/.env" "$CLAUDE_PLUGIN_ROOT/.env.bak.$(date +%s)"
Ask the user in chat (NOT via AskUserQuestion — token paste is long
free text, not a multiple choice):
"Open https://notion.so/my-integrations, create or open an integration, and paste the Internal Integration Secret here (starts with
secret_orntn_):"
Wait for the user's next message. Take the pasted value as NOTION_TOKEN.
Validate immediately:
curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer <TOKEN>" \
-H "Notion-Version: 2022-06-28" \
https://api.notion.com/v1/users/me
Expected: 200. If 401, the token is wrong — ask user to re-paste. If
any other code, report it and let the user decide.
Ask in chat:
"Now your Anthropic API key (from https://console.anthropic.com/, starts with
sk-ant-). Press Enter on a blank line if you want to skip this for now (you can fill it in later)."
No validation — Anthropic doesn't expose a cheap me-endpoint. Store as
ANTHROPIC_API_KEY. If blank, leave the value empty in .env.
GBRAIN_PLUGIN_PATHDefault: the absolute path of $CLAUDE_PLUGIN_ROOT (or pwd if running
outside Claude Code). Use AskUserQuestion to confirm or override.
NOTION_DB_* IDsFor each of the four databases (Projects, To-Do, Inbox, Knowledge Base), ask in chat:
"Paste the Notion page URL OR the 32-character UUID for the Projects database:"
Accept either form. Extract the UUID:
echo "<USER_INPUT>" | grep -oiE '[a-f0-9-]{32,36}' | tr -d '-' | tail -c 33 | head -c 32
This pulls the last 32 hex chars (whether the input is a URL with title prefix, a UUID with dashes, or a bare UUID). Reformat with dashes at positions 8/12/16/20:
UUID_RAW=<the 32-char output>
echo "${UUID_RAW:0:8}-${UUID_RAW:8:4}-${UUID_RAW:12:4}-${UUID_RAW:16:4}-${UUID_RAW:20:12}"
Validate the database is reachable AND the integration is shared with it:
curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer <TOKEN>" \
-H "Notion-Version: 2022-06-28" \
"https://api.notion.com/v1/databases/<FORMATTED_UUID>"
Expected: 200. If 404, either the UUID is wrong OR the integration is
not shared with this database — tell the user to go to Notion (DB page >
... > Connections > Add) and re-validate. Do not move on until 200.
Repeat for Todo, Inbox, Knowledge.
Use AskUserQuestion:
"Install Windows Task Scheduler entry for automatic 15-minute sync?" Options: "Yes, every 15 min" / "Yes, every 5 min" / "No, I'll run manually"
If yes, after writing .env (Step 7), call install-task.ps1 with the
chosen interval.
.envCompose the full file content from the collected values and use the
Write tool to save it to $CLAUDE_PLUGIN_ROOT/.env. Template:
NOTION_TOKEN=<collected>
ANTHROPIC_API_KEY=<collected or empty>
GBRAIN_PLUGIN_PATH=<resolved>
NOTION_DB_PROJECTS=<formatted UUID>
NOTION_DB_TODO=<formatted UUID>
NOTION_DB_INBOX=<formatted UUID>
NOTION_DB_KNOWLEDGE=<formatted UUID>
cd "$CLAUDE_PLUGIN_ROOT" && bun install --ignore-scripts && bun run build
node scripts/doctor.mjs
All seven doctor checks should PASS. If anything fails, report it and offer to re-run the relevant step.
Use AskUserQuestion:
"Run the first sync now? (one-way pull, all four databases)"
If yes:
node scripts/sync-pull.mjs
Then suggest the user run /notion-sync postprocess once the pull
completes (to refresh gbrain's backlink graph).
/notion-sync setupFirst-time environment verification.
Check that ${CLAUDE_PLUGIN_ROOT}/.env exists and contains the seven required
keys. If .env is missing, copy from .env.example first.
# Windows PowerShell
Get-Content "$env:CLAUDE_PLUGIN_ROOT\.env" | Select-String "NOTION_TOKEN|ANTHROPIC_API_KEY|GBRAIN_PLUGIN_PATH|NOTION_DB_"
Expected: seven lines printed (one per required key). Empty values are allowed
during setup but must be filled before /notion-sync pull.
cd "$env:CLAUDE_PLUGIN_ROOT"
bun install --ignore-scripts
bun run build
--ignore-scripts is required on Windows (see Gotchas).
gbrain doctor
Expected: all checks PASS. If engine shows not initialised, run
gbrain apply-migrations --yes once (gbrain 0.35.x lazy-init normally
handles this on first command).
node "$env:CLAUDE_PLUGIN_ROOT/scripts/sync-pull.mjs" --database projects --dry-run
Expected: lists page titles from Projects database without writing to gbrain.
If 401 Unauthorized, the integration is not shared with the database
(see RUNBOOK.md Step 4).
/notion-sync pullOne-shot sync from Notion into gbrain.
node "$env:CLAUDE_PLUGIN_ROOT/scripts/sync-pull.mjs" --database <db-name> --dry-run
<db-name> is one of: projects, todo, inbox, knowledge.
Review the listed pages. Stop here if the count looks wrong (token expired, wrong DB ID, or integration not shared).
node "$env:CLAUDE_PLUGIN_ROOT/scripts/sync-pull.mjs" --database <db-name>
The script fetches all pages, converts Notion blocks to Markdown
(src/block-converter.ts), and upserts to gbrain via gbrain put
(src/gbrain-adapter.ts).
gbrain list pages | wc -l
Compare against Step 1's dry-run count.
/notion-sync scheduleInstall or remove a Windows Task Scheduler entry that runs /notion-sync pull
on a fixed interval (default 15 minutes).
powershell -ExecutionPolicy Bypass -File "$env:CLAUDE_PLUGIN_ROOT\scripts\install-task.ps1" -Interval 15m
Creates Task Scheduler entry gbrain-notion-sync running as the current user.
powershell -ExecutionPolicy Bypass -File "$env:CLAUDE_PLUGIN_ROOT\scripts\install-task.ps1" -Status
Reports next run time and last result code.
powershell -ExecutionPolicy Bypass -File "$env:CLAUDE_PLUGIN_ROOT\scripts\install-task.ps1" -Uninstall
Windows only. macOS/Linux scheduler support is planned for v0.2.
/notion-sync postprocessRun gbrain maintenance after a sync (or batch of syncs). Decoupled from
pull so sync stays fast and predictable.
node "$env:CLAUDE_PLUGIN_ROOT/scripts/postprocess.mjs"
Runs in order, each step's failure does not block the next:
gbrain extract links --source notion — rebuild backlink graphgbrain dream --dry-run — doc consolidation, timeline extractionOPENAI_API_KEY is set) gbrain embed --stale — refresh vector indexgbrain query "<a known PAI keyword>"
Backlink count and chunk count should both be higher than before.
/notion-sync statusShow current sync state.
gbrain list pages
schtasks /Query /TN gbrain-notion-sync /V /FO LIST
Reports Next Run Time, Last Run Time, Last Result (0 = success).
Currently empty (Phase 3 not implemented). Will list .conflict/ entries when
v0.3 ships.
/notion-sync doctorComprehensive health check.
node "$env:CLAUDE_PLUGIN_ROOT/scripts/doctor.mjs"
Checks (in order):
gbrain doctor exit code is 0.env exists and all seven keys are non-emptyNOTION_DB_* IDsgbrain put --help succeeds (CLI alignment)${CLAUDE_PLUGIN_ROOT}/dist/ exists (build artifact present)Exit code 0 if all pass, 1 if any check fails.
GBRAIN_PLUGIN_PATH must be an absolute path. Relative paths cause
gbrain to silently skip the plugin.bun install against gbrain dependencies fails without
--ignore-scripts (gbrain issue #218 — bash-only postinstall scripts).gbrain jobs submit gbrain_sync syntax is outdated. Use the
Task Scheduler entry installed by /notion-sync schedule instead.gbrain jobs work long-running
worker is not supported; periodic execution must come from Task Scheduler.src/notion-client.ts caps at 2 req/s
to leave headroom — do not raise without testing..env and sync-state.db are in .gitignore. Never commit them.gbrain put is the correct CLI command, not gbrain page put
(alignment fix shipped in commit c814887).fetchBlockChildren does
not paginate yet (planned for v0.4)./notion-sync push), sync-state.db/notion-sync conflicts), .conflict/ backupOPENAI_API_KEY)Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub bouob/claude-plugins --plugin gbrain-notion-sync