NoteShift is a TypeScript-first CLI tool and programmatic library for moving notes between Notion, Google Keep, Memos, AFFiNE, and local files — without vendor lock-in.
# Install globally
npm install -g noteshift
# Configure adapters interactively
noteshift config init
# Transfer notes from Notion to Memos
noteshift transfer --from my-notion --to my-memos
# Backup to local Markdown files
noteshift backup --from my-notion --output ./backup
Everything you need to manage your notes across platforms
Move notes between any two supported platforms with a single command. Preserves tags, pinned state, and rich metadata.
Snapshot all your notes to Markdown or JSON files with full metadata. Restore back to any platform at any time.
Run automated backups via the built-in node-cron daemon or generate OS-native schedulers (crontab, launchd, schtasks).
Handles duplicate notes with interactive diff-based prompts or automatic strategies: overwrite, skip, rename, or merge.
Preview every planned create / update / skip action in a formatted table before committing a single write.
Use all adapters and the transfer engine directly in your own Node.js or TypeScript projects via the noteshift package.
Exponential-backoff retry on transient API errors (5 retries, 500ms–60s). Built-in rate limiting per platform to avoid quota violations.
Run noteshift without arguments to launch a full interactive terminal UI — guided menus for every operation.
Connect any source to any destination
Official Notion API. Supports pages and database entries. Requires an integration token.
Enterprise mode (Google Workspace) for full read/write. Personal mode reads from Google Takeout exports.
Self-hosted Memos instance via its REST API. Supports memo content, tags, pinned state, and visibility.
Connects to self-hosted or cloud AFFiNE workspaces via GraphQL API and WebSocket protocol.
Markdown (.md) and JSON files on your filesystem. Reads YAML frontmatter for metadata.
| Source ↓ / Dest → | Notion | Google Keep | Memos | AFFiNE | Local |
|---|---|---|---|---|---|
| Notion | — | Enterprise | ✓ | ✓ | ✓ |
| Google Keep | ✓ | — | ✓ | ✓ | ✓ |
| Memos | ✓ | Enterprise | — | ✓ | ✓ |
| AFFiNE | ✓ | Enterprise | ✓ | — | ✓ |
| Local | ✓ | Enterprise | ✓ | ✓ | — |
✓ = Full support · Enterprise = Google Workspace accounts only · — = Same platform
Up and running in under five minutes
NoteShift requires Node.js ≥ 18. Check your version:
node --version
Install globally via npm:
npm install -g noteshift
Or as a project dependency:
npm install noteshift
Run the interactive setup wizard to connect your platforms:
noteshift config init
Then test your connections and start transferring:
noteshift status
noteshift transfer --from my-notion --to my-memos --dry-run
Complete guides, references, and API docs — optimized for GitHub Pages
NoteShift is a Node.js CLI tool and library. All you need is Node.js ≥ 18 and an npm account (optional, for publishing).
npm install -g noteshift
Run the interactive wizard to configure your adapters:
noteshift config init
The wizard will:
# Test all adapters
noteshift status
# Test a specific adapter
noteshift status --adapter my-notion
A green ✓ next to each adapter means the connection is working.
# Always preview first
noteshift transfer --from my-notion --to my-memos --dry-run
# Then run for real
noteshift transfer --from my-notion --to my-memos
Run noteshift with no arguments to open the full interactive TUI:
noteshift
The transfer command moves notes from one adapter to another. Both --from and --to must be adapter names you configured.
noteshift transfer --from my-notion --to my-memos
# Filter by tags
noteshift transfer --from my-notion --to my-memos --tags work,important
# Only notes updated after a date
noteshift transfer --from my-notion --to my-memos --updated-after 2026-01-01
# Limit number of notes
noteshift transfer --from my-notion --to my-memos --limit 50
# Include archived notes (excluded by default)
noteshift transfer --from my-notion --to my-memos --include-archived
Preview all planned operations without writing anything:
noteshift transfer --from my-notion --to my-memos --dry-run
When a note with the same title already exists on the destination:
noteshift transfer --from my-notion --to my-memos --conflict overwrite
NoteShift can snapshot your notes to local files and restore them later to any platform.
noteshift backup --from my-notion --output ./backup --format markdown
noteshift backup --from my-memos --output ./backup.json --format json
noteshift backup --from my-notion --output ./backup --dry-run
Restore notes from a previous backup to any destination adapter:
# Restore from directory
noteshift restore --to my-memos --input ./backup
# With conflict strategy
noteshift restore --to my-memos --input ./backup --conflict skip
# Dry run first
noteshift restore --to my-memos --input ./backup --dry-run
Each note is saved as a .md file with YAML frontmatter:
---
id: abc123
title: My Note
tags: [work, project]
pinned: false
archived: false
createdAt: 2026-01-15T09:00:00Z
updatedAt: 2026-01-20T14:30:00Z
source: notion
---
# My Note
Note content here...
NoteShift stores config at a platform-specific path. Run noteshift config path to see the exact location.
{
"adapters": {
"my-notion": {
"platform": "notion",
"token": "secret_... or ntn_...",
"parentId": "database-or-page-id"
}
}
}
{
"adapters": {
"keep-work": {
"platform": "google-keep",
"mode": "enterprise",
"serviceAccountKeyFile": "/path/to/key.json",
"userId": "user@company.com"
}
}
}
{
"adapters": {
"keep-personal": {
"platform": "google-keep",
"mode": "personal",
"takeoutDirectory": "/path/to/Takeout/Keep"
}
}
}
{
"adapters": {
"my-memos": {
"platform": "memos",
"baseUrl": "http://localhost:5230",
"token": "your-api-token"
}
}
}
{
"adapters": {
"my-affine": {
"platform": "affine",
"baseUrl": "http://localhost:3010",
"token": "your-token",
"workspaceId": "workspace-id"
}
}
}
{
"adapters": {
"local-backup": {
"platform": "local",
"directory": "/home/user/notes",
"format": "markdown"
}
}
}
Complete reference for all NoteShift commands and flags.
confignoteshift config init # Interactive setup wizard
noteshift config show # Print config as JSON
noteshift config path # Print config file path
noteshift config adapter add <name> # Add or update an adapter
noteshift config adapter remove <name> # Remove an adapter
noteshift config adapter list # List all adapters
noteshift config reset # Delete all configuration
statusnoteshift status # Test all adapters
noteshift status --adapter my-notion # Test one adapter
transfernoteshift transfer
-f, --from <name> Source adapter (required)
-t, --to <name> Destination adapter (required)
--tags <t1,t2> Tag filter
--updated-after <ISO> Date filter
--limit <n> Max notes
--include-archived Include archived notes
--conflict <strategy> ask|overwrite|skip|rename|merge
--batch-size <n> Batch size (default: 10)
--dry-run Preview without writing
backupnoteshift backup
-f, --from <name> Source adapter (required)
-o, --output <path> Output directory or file (required)
--format <fmt> markdown|json (default: markdown)
--tags <t1,t2> Tag filter
--limit <n> Max notes
--dry-run Preview without writing
restorenoteshift restore
-t, --to <name> Destination adapter (required)
-i, --input <path> Backup directory or file (required)
--conflict <strategy> ask|overwrite|skip|rename|merge
--dry-run Preview without writing
schedulenoteshift schedule list # List all schedules
noteshift schedule add # Add a new schedule
noteshift schedule disable <id> # Disable a schedule
noteshift schedule enable <id> # Enable a schedule
noteshift schedule remove <id> # Remove a schedule
daemonnoteshift daemon start # Start the background daemon
noteshift daemon stop # Stop the daemon
noteshift daemon status # Check daemon status
os-schedulenoteshift os-schedule generate # Generate OS-native schedule config
# Supports: Linux crontab, macOS launchd, Windows schtasks
NoteShift provides two ways to automate backups: a built-in Node.js daemon and OS-native scheduler generators.
The daemon runs inside Node.js using node-cron. Ideal for personal machines.
# Add a daily backup at 2 AM
noteshift schedule add \
--cron "0 2 * * *" \
--task backup \
--from my-notion \
--output ./backup
# Start the daemon
noteshift daemon start
┌───── minute (0-59)
│ ┌─── hour (0-23)
│ │ ┌─ day (1-31)
│ │ │ ┌ month (1-12)
│ │ │ │ ┌ weekday (0-6, Sun=0)
* * * * *
Generate a native schedule config for your OS:
# Linux — outputs a crontab entry
noteshift os-schedule generate --type crontab
# macOS — outputs a launchd .plist file
noteshift os-schedule generate --type launchd
# Windows — outputs an schtasks XML file
noteshift os-schedule generate --type schtasks
All adapters and the transfer engine are exported from the noteshift package. Install as a library:
npm install noteshift
import { NotionAdapter } from 'noteshift';
const adapter = new NotionAdapter({
platform: 'notion',
token: 'secret_...',
parentId: 'database-or-page-id', // optional
});
const notes = await adapter.listNotes();
await adapter.createNote({ title: 'Hello', content: '# Hello' });
import { MemosAdapter } from 'noteshift';
const adapter = new MemosAdapter({
platform: 'memos',
baseUrl: 'http://localhost:5230',
token: 'your-api-token',
});
const notes = await adapter.listNotes();
await adapter.createNote({ title: 'Hello', content: 'World' });
import { GoogleKeepAdapter } from 'noteshift';
// Enterprise (service account)
const adapter = new GoogleKeepAdapter({
platform: 'google-keep',
mode: 'enterprise',
serviceAccountKeyFile: '/path/to/key.json',
userId: 'user@company.com',
});
// Personal (read-only Takeout)
const reader = new GoogleKeepAdapter({
platform: 'google-keep',
mode: 'personal',
takeoutDirectory: '/path/to/Takeout/Keep',
});
import { LocalAdapter } from 'noteshift';
const adapter = new LocalAdapter({
platform: 'local',
directory: '/home/user/notes',
format: 'markdown', // or 'json'
});
import { AffineAdapter } from 'noteshift';
const adapter = new AffineAdapter({
platform: 'affine',
baseUrl: 'http://localhost:3010',
token: 'your-token',
workspaceId: 'workspace-id',
});
import { TransferEngine, NotionAdapter, MemosAdapter } from 'noteshift';
const source = new NotionAdapter({ platform: 'notion', token: '...' });
const dest = new MemosAdapter({ platform: 'memos', baseUrl: '...', token: '...' });
const engine = new TransferEngine({ conflictStrategy: 'skip' });
const result = await engine.transfer(source, dest, {
dryRun: false,
tags: ['work'],
limit: 100,
});
console.log(`Created: ${result.created}, Updated: ${result.updated}, Skipped: ${result.skipped}`);
interface NoteAdapter {
connect(): Promise<void>;
disconnect(): Promise<void>;
listNotes(filter?: NoteFilter): Promise<Note[]>;
getNote(id: string): Promise<Note>;
createNote(note: CreateNoteInput): Promise<Note>;
updateNote(id: string, note: UpdateNoteInput): Promise<Note>;
deleteNote(id: string): Promise<void>;
testConnection(): Promise<boolean>;
}
NoteShift is open source software licensed under the MIT License.
MIT License
Copyright © 2025–2026 SkyLostTR (@Keeftraum). All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software.
See the LICENSE file in the repository for the complete terms.
NoteShift is built on these excellent open-source packages:
Contributions are welcome! Please:
git checkout -b feat/my-feature)git clone https://github.com/SkyLostTR/NoteShift.git
cd NoteShift
npm install
npm run build
npm test
NoteShift is a free, open-source project maintained by SkyLost_TR (Keeftraum). If it saves you time, consider supporting its development!
Browse the full NoteShift wiki directly from this GitHub Pages site.
Overview and table of contents
Install, configure & run your first transfer
Config schema & adapter options
Filters, conflicts & batch options
Snapshot & restore notes locally
Daemon & OS-native scheduler setup
All commands, flags & examples
Programmatic usage & types
Adapter-specific guides:
NoteShift is a free, open-source project. If you find it useful, consider supporting its development!
Every contribution helps: sponsoring, starring the repo, reporting bugs, or improving docs and adapters.