Package Exports
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@brianlovin/notion-skills) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
notion-skills
Sync agent skills from a Notion database to Claude Code, Codex, OpenCode, Cursor, and Gemini CLI.
What is this?
Modern AI coding agents (Claude Code, Codex, OpenCode, Cursor, Gemini CLI) all read skills — directories with a SKILL.md file that tells the agent what to do and when to do it. They all use the same convention but each looks in a different folder, and authoring skills as files in your ~/.claude/skills/ directory means using a code editor + git for what's basically content management.
notion-skills lets your team author skills as rows in a Notion database, then syncs them down to whichever agent CLIs you use:
- ✏️ Author in Notion's UI — page title is the skill name, properties map to frontmatter, page body is the SKILL.md content. No git, no markdown editor, no PR review.
- 👥 Share across a team — point teammates at the same Notion DB; each runs
syncto get the same skills. - 🎯 Multi-agent — one source of truth, fan out to every agent CLI you use. Skills land via symlinks so an edit in Notion updates every CLI on the next sync.
Prerequisites
You need three things before installing:
- macOS or Linux. Windows isn't supported yet (symlinks need admin/dev mode).
- Node.js 18+. Check with
node --version. ntn0.12 or newer +ntn login. This is Notion's official CLI;notion-skillspiggybacks on it for all API access (no separate OAuth flow, no integration to register).
Install and authenticate ntn
# Install ntn (one of):
brew install ntn
# or download from https://github.com/makenotion/cli
# Verify version
ntn --version
# → ntn 0.12.0 (or newer)
# Log in to your Notion workspace
ntn login
# Confirm everything's wired up
ntn doctor
# → CLI version ✔
# → Default workspace ✔
# → Token valid ✔If ntn doctor is green, you're ready.
Install
npm install -g @brianlovin/notion-skills
notion-skills --versionQuick start
The fastest path from zero to skills syncing in under five minutes:
# 1. Install ntn + log in (see Prerequisites)
brew install ntn && ntn login
# 2. Install notion-skills
npm install -g @brianlovin/notion-skills
# 3. Run the wizard — it does everything
notion-skills initinit is a guided wizard. It asks:
- Database — by default, creates a new Skills database at your workspace root and opens it in your browser. You can also link an existing one by URL.
- Schema upgrade — auto-runs if your DB is missing the properties the skill spec needs.
- Targets — which agent CLIs to sync to (Claude Code, Codex, OpenCode, Cursor, Gemini). Defaults to whichever you have installed.
- Migrate locals — if you already have skills in
~/.claude/skills/etc., it offers to push them up to Notion in one shot.
Then run notion-skills sync to pull everything down as symlinks into your selected target dirs.
Now you can:
- Edit skills in Notion's UI.
- Run
notion-skills syncto pick up changes. - Type
/skill-namein any of your agent CLIs to use them.
What's a "skill"?
A skill is a directory containing a SKILL.md file. The frontmatter tells the agent CLI what the skill does and when to use it; the body is the content the agent reads when invoked.
---
name: deslop
description: Remove AI-generated code slop from the current branch. Use after writing code to clean up unnecessary comments, defensive checks, and inconsistent style.
---
# Remove AI code slop
Check the diff against main and remove all AI-generated slop introduced in this branch...In Notion, that becomes a database row:
- Page title:
deslop - Description property: the one-liner that goes in the frontmatter
- Page body: the markdown content
notion-skills sync reads each row and writes a SKILL.md to your central store, then symlinks it into every target CLI's directory.
Commands
| Command | What it does |
|---|---|
notion-skills init |
Guided wizard for first-time setup. |
notion-skills sync |
Pull pages, write skills, reconcile target dirs. Skips unchanged pages. Offers to upload local-only skills after pulling. |
notion-skills status |
Show auth, scope, per-target symlink health. |
notion-skills doctor [--fix] |
Inspect for drift; safe auto-repairs with --fix. |
notion-skills list |
Print every page in the DB with status: ✓ synced / ✗ excluded / ○ available / ! invalid. |
notion-skills upgrade |
Add any missing skill-spec properties to your Notion DB schema. |
notion-skills migrate [--from <path>] [--overwrite] [--dry-run] |
Push existing local skills into Notion, sync back as symlinks. |
notion-skills login / logout |
Thin wrappers over ntn login / ntn logout. |
Run any command with --help for full options.
init — first-time setup
notion-skills initWalks you through connecting (or creating) a Notion database, picking which agent CLIs to sync to, and uploading any local skills you already have. Idempotent — safe to re-run.
sync — pull skills from Notion
notion-skills sync # pull the latest from Notionsync is incremental — it only re-fetches pages whose last_edited_time or properties have changed since the last sync.
If a page in Notion has been trashed since the last sync, sync prompts before deleting the local copy (default: keep). Local skills that don't exist in Notion are surfaced after the pull, with an option to upload them.
migrate — move existing skills into Notion
If you already have skills authored on disk:
notion-skills migrate # scans your scope's target dirs
notion-skills migrate --from ~/Developer/skill-repo # plus an extra source
notion-skills migrate --overwrite # replace existing Notion pages
notion-skills migrate --dry-run # preview, don't write anything
notion-skills migrate -y # skip confirmationmigrate writes Notion pages first; only after every page is created does it move local copies to a backup at ~/.notion-skills/backup/migrate-<timestamp>/. A failure mid-flight leaves locals untouched.
doctor — diagnose drift
notion-skills doctor # read-only: prints status of every check
notion-skills doctor --fix # auto-fix safe issues (interactive y/n per fix)Checks: ntn install + auth, scope existence, schema match, manifest vs central-store consistency, per-target symlink health.
Concepts
One scope, per machine
State lives at ~/.notion-skills/ and fans out via symlinks to your home-dir agent paths (~/.claude/skills/, ~/.codex/skills/, etc.). The same skills are available across every repo and every agent CLI you use.
For repo-specific skills, just author SKILL.md files directly in
<repo>/.claude/skills/and commit them. Notion is great for cross-repo skills you author and share with your team; git is the right tool for skills tied to a single codebase.
Five supported agents
| Agent | Skills directory |
|---|---|
| Claude Code | ~/.claude/skills/ |
| Codex CLI | ~/.codex/skills/ |
| OpenCode | ~/.config/opencode/skills/ |
| Cursor | ~/.cursor/skills/ |
| Gemini CLI | ~/.gemini/skills/ |
Pick any combination during init. Adding a new agent is one entry in src/known-targets.ts — PRs welcome.
Excluding specific skills
sync syncs every skill in the database by default. If there's a skill you don't want on this machine, add its slug to the optional exclude_skills array in ~/.notion-skills/scope.json:
{
"database_id": "...",
"data_source_id": "...",
"targets": ["claude", "codex"],
"exclude_skills": ["broken-skill"]
}There's no command for this — denylists are rare enough that hand-editing the JSON is the right knob.
Schema
init and upgrade provision/maintain these Notion database properties. You don't author them by hand.
| Property | Frontmatter key | Type |
|---|---|---|
Name |
name (slugified from title) |
title |
Description |
description |
rich_text |
When To Use |
when_to_use |
rich_text |
Argument Hint |
argument-hint |
rich_text |
Arguments |
arguments |
rich_text (space-sep) |
Allowed Tools |
allowed-tools |
rich_text (space-sep, paren-aware) |
Paths |
paths |
rich_text (comma-sep) |
Disable Model Invocation |
disable-model-invocation |
select (default/true/false) |
User Invocable |
user-invocable |
select (default/true/false) |
Model |
model |
select (self-healing) |
Effort |
effort |
select |
Context |
context |
select |
Agent |
agent |
select (self-healing) |
Shell |
shell |
select |
Self-healing selects (Model, Agent) auto-add new options when migrate encounters them — so you can use agent: my-custom-subagent without pre-registering the option.
Spec defaults for Disable Model Invocation, User Invocable, Shell, etc. are encoded as a default option. Empty cells also count as default — both omit the frontmatter key when syncing back to disk.
Common workflows
Adding a new skill
- In Notion, add a row to your skills DB.
- Set
Name(becomes the slug),Description(the when-to-use hint), and any other properties you care about. - Write the skill instructions in the page body.
- Run
notion-skills sync(or wait until your next sync).
Editing a skill
- Edit the page in Notion.
- Run
notion-skills sync.
sync is incremental — only edited pages are re-fetched.
Sharing a curated skill set with a team
Everyone on the team runs notion-skills init once and points at the team's Notion database. From then on, notion-skills sync keeps everyone aligned. Editing in Notion is the canonical path; new skills authored by anyone propagate to teammates the next time they sync.
Migrating from ~/.claude/skills/ files
If you've been authoring skills as files (or symlinks from a shared repo like agent-config), init already detects them and offers to upload in one shot. To re-run that step later:
notion-skills migrate # scan your configured target dirs
notion-skills migrate --from ~/Developer/agents # plus a custom pathLocals are moved to ~/.notion-skills/backup/migrate-<ts>/ after Notion confirms each write. The next sync re-creates them as symlinks pointing at your central store.
Files this CLI touches
~/.notion-skills/
├── scope.json # database id, sync targets, optional exclude list
├── manifest.json # sync state (atomic writes)
├── skills/<name>/ # central source-of-truth
└── backup/migrate-<ts>/ # local copies displaced during migrate
~/.claude/skills/<name> → symlink → ~/.notion-skills/skills/<name>
~/.codex/skills/<name> → symlink → ~/.notion-skills/skills/<name>
~/.cursor/skills/<name> → symlink → ~/.notion-skills/skills/<name>
~/.config/opencode/skills/<name> → symlink → ~/.notion-skills/skills/<name>
~/.gemini/skills/<name> → symlink → ~/.notion-skills/skills/<name>Auth lives in ntn's store (OS keychain by default). rm -rf ~/.notion-skills wipes notion-skills state without affecting auth.
Troubleshooting
notion-skills doctor is the first stop. It reports status across ntn auth, scope, schema, manifest consistency, and symlink health.
Common errors
Errors print a one-line summary plus a suggested next command. The most common cases:
| Error | Fix |
|---|---|
| "Notion auth has expired" or "API token is invalid" | ntn logout && ntn login (the full reset; ntn login alone sometimes leaves a stuck token even when ntn doctor reports green) |
| "Schema doesn't match" | notion-skills upgrade |
| "isn't configured yet" | notion-skills init |
| "Could not find database" | Check the URL/ID; ensure ntn is in the right workspace (ntn doctor). |
| "ntn is too old" | ntn update |
| "Couldn't reach the Notion API" | Check your network. |
Resetting from scratch
# Wipe all notion-skills local state (keeps auth and your Notion DB intact)
rm -rf ~/.notion-skills
notion-skills initRestoring a migrate backup
migrate moves originals to ~/.notion-skills/backup/migrate-<ts>/. To restore:
ls ~/.notion-skills/backup/
# pick the timestamp you want to restore from
mv ~/.notion-skills/backup/migrate-<ts>/<skill-name> ~/.claude/skills/Limitations
- macOS and Linux only. Windows symlink support is on the list.
- Round-trip normalisation. Notion's markdown parser normalises some content on ingest:
- YAML wraps long descriptions across lines
- Multi-line markdown paragraphs become separate Notion blocks
- Code-language aliases get expanded (
ts→typescript) - Table separators get standardised
- Bare domains (
example.com) get auto-linked
- Performance. Each Notion call shells out to
ntn, which adds ~50–100ms per call. A sync of 100 skills with deeply-nested blocks takes a few minutes. Subsequent syncs are fast (incremental). - Auth scope.
notion-skillssees whateverntnis logged into. Switch workspaces withntn login.
Contributing
git clone https://github.com/brianlovin/notion-skills
cd notion-skills
npm install
npm run build
npm test # 105 tests
npm link # use the local build globallySource layout:
src/cli.ts— commander entry pointsrc/commands/— one file per CLI subcommandsrc/notion.ts— Notion API client (shells out tontn)src/sync.ts,src/migrate.ts— pure logic; importable from anywheresrc/schema.ts— single source of truth for the property → frontmatter mappingsrc/known-targets.ts— the registry of supported agent CLIssrc/errors.ts— friendly error translator
Adding a new agent: append a TargetDef to KNOWN_TARGETS in src/known-targets.ts. Tests in test/known-targets.test.mjs will pin the change.
PRs run Build + test against Node 18 / 20 / 22 on Ubuntu via .github/workflows/test.yml. All checks must pass before merge.
Releasing
Releases are automated. To cut a new version:
npm version patch # 0.1.0 → 0.1.1 (or `minor`, `major`)
git push --follow-tagsThe Release workflow on main (.github/workflows/release.yml) detects the version bump in package.json, builds, tests, publishes to npm with provenance, and creates a GitHub Release with auto-generated notes.
Auth uses npm OIDC trusted publishing — no NPM_TOKEN secret. npm mints a short-lived token at publish time, verified against this repo + the release.yml workflow path via the GitHub OIDC provider. The trusted publisher config lives at the package's npm access page.
License
MIT