@greyesg/notion-board
v0.1.5
Published
Reusable Notion CLI for tickets + sprints Kanban workflows with incremental sync.
Downloads
274
Readme
@greyesg/notion-board
Reusable CLI for managing a Notion tickets board and sprints table with incremental sync.
CLI bin: notion-board
Install
Install from npm:
npm install -D @greyesg/notion-boardAdd a script in your project:
{
"scripts": {
"notion:board": "notion-board"
}
}Setup
- Create
.env.localin your project root. - Fill:
NOTION_TOKENNOTION_DATABASE_IDNOTION_SPRINTS_DATABASE_ID- optional:
NOTION_API_VERSION - optional:
NOTION_SYNC_SKEW_SECONDS - optional:
NOTION_ENV_FILE
Starter template:
NOTION_TOKEN=secret_xxx
NOTION_DATABASE_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
NOTION_SPRINTS_DATABASE_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
NOTION_API_VERSION=2022-06-28
NOTION_SYNC_SKEW_SECONDS=90Optional config file:
- Create
notion-board.config.jsonin your project root when you want to pin exact Notion property mappings or default statuses. - You can also point to a custom location with
NOTION_BOARD_CONFIG=/abs/path/to/notion-board.config.json.
Example:
{
"tickets": {
"properties": {
"title": { "id": "title", "name": "Description" },
"key": { "id": "_%7DM%3A", "name": "Ticket ID" },
"status": { "id": "j%3FEB", "name": "Estado" },
"sprint": { "id": "JYnM", "name": "Iteration" },
"owner": { "id": "OEN%40", "name": "Assignee" },
"description": { "id": "xKBx", "name": "Context" },
"note": { "id": "Fj%5D%3E", "name": "Review Note" }
},
"defaults": {
"createStatus": "Entrada",
"seedStatus": "Entrada"
}
},
"sprints": {
"properties": {
"name": { "id": "title", "name": "Sprint Name" },
"code": { "id": "%3AoFt", "name": "Code" }
}
},
"settings": {
"singleSprint": true
}
}Env lookup order:
NOTION_ENV_FILE- nearest
.env.localwalking up from current working directory - package-local
.env.localfallback
Config lookup order:
NOTION_BOARD_CONFIG- nearest
notion-board.config.jsonwalking up from current working directory
Required Notion schema
Tickets database
- One title property for the ticket title
- One key property (
rich_textortitle) - One status property (
statuspreferred,selectsupported) - One sprint property (
relationto the Sprints database)
Property names can be custom. The CLI auto-detects property roles by type and common aliases such as Ticket ID, Stage, Iteration, Notes, etc.
Optional properties:
TypePriorityOwner- long-text description/details
- note/comments
Last Synced AtSource
Sprints database
- One title property for the sprint name
- One code property (
rich_text,title, orselect) with values likeS0,S1,S2
The CLI works with your existing status labels. It does not require Backlog, Todo, In Progress, In Review, or Done.
Write model:
- one sprint per ticket is the supported write model
- read/inspect flows can surface legacy rows with multiple sprint relations
- deep validation and migration commands flag those rows explicitly
Performance model:
- tickets and sprints snapshots are materialized locally under
.cache/ - once the cache exists, read-heavy commands refresh it with
last_edited_time >= watermark - skew - use
--fullwhen you want to ignore the cache and rebuild it from live Notion - generic parallel reads are intentionally not implemented
- fast full-table reads only happen through explicit sharded filters
First run
Inspect and validate schema/access:
npm run notion:board -- board:inspect
npm run notion:board -- board:validate
npm run notion:board -- board:validate --deepThen sync:
npm run notion:board -- board:sync --since-last-run
npm run notion:board -- sprints:sync --since-last-runLarge-board bootstrap with sharded reads:
npm run notion:board -- board:validate --deep \
--read-shard-property Date \
--read-shard-start 2024-01-01 \
--read-shard-end 2026-12-31 \
--read-shard-workers 6Force a full cache rebuild after manual deletions or board cleanup:
npm run notion:board -- board:validate --deep --full
npm run notion:board -- board:list --full
npm run notion:board -- sprints:sync --fullDaily commands
List tickets:
npm run notion:board -- board:listList recent changes only:
npm run notion:board -- board:list --deltaList tickets with page IDs or JSON output:
npm run notion:board -- board:list --show-page-id
npm run notion:board -- board:list --jsonCreate a ticket:
npm run notion:board -- ticket:create --title "Implement auth guard" --sprint S0 --status "Entrada" --type Feature --priority P1 --description "Protect dashboard routes."Move a ticket:
npm run notion:board -- ticket:move --key S0-001 --status "En curso"
npm run notion:board -- ticket:move --page-id 32caee4a-39a6-8189-90cc-cb8fd56f8ba7 --status "En curso"Update a ticket:
npm run notion:board -- ticket:update --key S0-001 --status "En revisión" --note "Ready for verification."
npm run notion:board -- ticket:update --page-id 32caee4a-39a6-8189-90cc-cb8fd56f8ba7 --status "Done"Create and manage sprints explicitly:
npm run notion:board -- sprint:create --code S7 --name "Sprint 7 - Mobile polish"
npm run notion:board -- sprint:update --code S7 --name "Sprint 7 - Mobile and analytics polish"
npm run notion:board -- sprint:rename --code S7 --name "Sprint 7 - Final polish" --new-code S8Seed from a markdown plan:
npm run notion:board -- sprints:seed --from <path-to-plan.md> --sprint S0..S6
npm run notion:board -- board:seed --from <path-to-plan.md> --sprint S0..S6 --initial-status "Entrada"Legacy-safe migrations:
npm run notion:board -- migrate:keys --dry-run
npm run notion:board -- migrate:keys --apply
npm run notion:board -- migrate:keys --sprint S0 --apply --overwrite
npm run notion:board -- migrate:sprints --from-property "Legacy Sprint" --match code --dry-run
npm run notion:board -- migrate:sprints --from-property "Legacy Sprint" --match name --applyBulk apply acceleration:
npm run notion:board -- migrate:keys --apply --concurrency-write 3
npm run notion:board -- migrate:sprints --from-property "Legacy Sprint" --apply --concurrency-write 3
npm run notion:board -- board:seed --from product/plan.md --concurrency-write 3Sharded read flags:
--read-shard-property <property-name|created_time>--read-shard-start <YYYY-MM-DD>--read-shard-end <YYYY-MM-DD>--read-shard-workers <n>
Write concurrency flag:
--concurrency-write <n>
Cache rebuild flag:
--full
Migration defaults:
- preview-only unless
--applyis passed - fill-only unless
--overwriteis passed - page targeting supported with
--page-id
Safety
ticket:moveandticket:updateuse read-before-write.- Conflict detection uses
last_edited_time. ticket:createandticket:update --sprintdo not auto-create sprint rows.- If a sprint row is missing, create it deliberately with
sprint:createorsprints:seed. board:inspect --deepandboard:validate --deepsurface missing keys, missing sprint relations, multi-sprint rows, and unknown sprint relations.- cache-backed commands print whether they used a cache hit or miss, whether the run was a full bootstrap or delta refresh, and any shard retry/split stats
- use
--fullafter manual deletions, permission changes, or large cleanup passes so stale cached rows are dropped immediately - If the CLI cannot infer a default status safely, it fails and asks you to pass
--statusor--initial-status. - Use
--forceonly when you explicitly want to override conflict protection.
Recommended large legacy-board flow:
- Build or refresh cache with
board:validate --deeporboard:list. - Stage or infer sprint mapping outside the CLI.
- Run
migrate:sprints --dry-run. - Run
migrate:sprints --apply --concurrency-write 3. - Run
migrate:keys --dry-run. - Run
migrate:keys --apply --concurrency-write 3.
AGENTS.md Guidance
Suggested snippet for repos using this package:
- This repo uses `@greyesg/notion-board` to inspect and manage the linked Notion tickets/sprints databases.
- Before any Notion write operation, run `npx notion-board board:inspect` or `npx notion-board board:validate`.
- Prefer `--page-id` for repair/migration work on legacy boards and `Key` for normal day-to-day collaboration.Local development
Inside this package repo:
npm install
npm test
npm run build
npm pack