Skip to content

Commit 7494ef6

Browse files
committed
Initial commit
0 parents  commit 7494ef6

27 files changed

+4460
-0
lines changed

.claude-plugin/marketplace.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "cardmagic",
3+
"plugins": [
4+
{
5+
"name": "notes",
6+
"source": "./",
7+
"description": "Search and browse Apple Notes with fuzzy matching",
8+
"version": "1.0.0"
9+
}
10+
]
11+
}

.claude-plugin/plugin.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "notes",
3+
"description": "Search and browse Apple Notes from the command line",
4+
"version": "1.0.0",
5+
"author": {
6+
"name": "Lucas Carlson"
7+
},
8+
"repository": "https://github.com/cardmagic/notes",
9+
"keywords": ["macos", "apple", "notes", "search", "mcp"]
10+
}

.github/workflows/ci.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
build:
11+
runs-on: macos-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
- uses: pnpm/action-setup@v4
15+
with:
16+
version: 9
17+
- uses: actions/setup-node@v4
18+
with:
19+
node-version: '22'
20+
cache: 'pnpm'
21+
- run: pnpm install
22+
- run: pnpm run typecheck
23+
- run: pnpm run lint
24+
- run: pnpm run build

.github/workflows/publish.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Publish to npm
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
8+
jobs:
9+
publish:
10+
runs-on: macos-latest
11+
permissions:
12+
contents: read
13+
id-token: write
14+
steps:
15+
- uses: actions/checkout@v4
16+
- uses: pnpm/action-setup@v4
17+
with:
18+
version: 9
19+
- uses: actions/setup-node@v4
20+
with:
21+
node-version: '22'
22+
cache: 'pnpm'
23+
registry-url: 'https://registry.npmjs.org'
24+
- run: pnpm install
25+
- run: pnpm run build
26+
- run: npm publish --access public --provenance
27+
env:
28+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules/
2+
dist/
3+
*.log
4+
.DS_Store
5+
.claude

.pnpmrc.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"pnpm": {"onlyBuiltDependencies": ["better-sqlite3", "esbuild"]}}

CLAUDE.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Commands
6+
7+
```bash
8+
pnpm build # Compile TypeScript to dist/
9+
pnpm dev # Watch mode for development
10+
pnpm typecheck # Type check without emitting
11+
pnpm lint # Run oxlint
12+
pnpm test # Run tests
13+
pnpm test:watch # Run tests in watch mode
14+
15+
make install # Build and link globally
16+
make clean # Remove dist/ and node_modules/
17+
```
18+
19+
## Architecture
20+
21+
Dual-mode CLI/MCP tool for searching Apple Notes:
22+
23+
```
24+
src/
25+
├── index.ts # Entry point - routes to CLI or MCP mode based on --mcp flag
26+
├── cli.ts # Commander-based CLI (search, recent, folders, read commands)
27+
├── mcp.ts # MCP server exposing tools via @modelcontextprotocol/sdk
28+
├── indexer.ts # Builds search indexes from Apple Notes database
29+
├── searcher.ts # Queries indexes with fuzzy matching via MiniSearch
30+
├── formatter.ts # Terminal output formatting with chalk
31+
└── types.ts # Shared types and Apple date conversion utilities
32+
```
33+
34+
**Data flow:**
35+
1. `indexer.ts` reads Apple's `NoteStore.sqlite` database from `~/Library/Group Containers/group.com.apple.notes/`
36+
2. Extracts text from gzipped note body data (protobuf-like format)
37+
3. Creates two indexes in `~/.notes/`: FTS5 SQLite for exact search, MiniSearch JSON for fuzzy matching
38+
4. `searcher.ts` queries MiniSearch for fuzzy results, then fetches full data from SQLite
39+
40+
**Key dependencies:**
41+
- `better-sqlite3`: Read Apple Notes DB and create FTS5 index
42+
- `minisearch`: Fuzzy search with typo tolerance
43+
- `@modelcontextprotocol/sdk`: MCP server for Claude Code integration
44+
45+
## Claude Code Plugin
46+
47+
This repo is also a Claude Code plugin with skills and slash commands:
48+
49+
```
50+
.claude-plugin/
51+
├── plugin.json # Plugin manifest
52+
└── marketplace.json # Marketplace definition (name: cardmagic)
53+
54+
skills/notes/SKILL.md # Auto-invoked skill for note queries
55+
commands/
56+
├── search.md # /notes:search slash command
57+
├── recent.md # /notes:recent slash command
58+
├── folders.md # /notes:folders slash command
59+
├── folder.md # /notes:folder slash command
60+
└── read.md # /notes:read slash command
61+
```
62+
63+
## Releasing
64+
65+
When asked to "bump version to X" or "tag vX.Y.Z":
66+
67+
1. Update `package.json` version field to the new version
68+
2. Update `server.json` version field to match
69+
3. Commit: `git add package.json server.json && git commit -m "chore: bump version to X.Y.Z"`
70+
4. Tag: `git tag vX.Y.Z`
71+
5. Push: `git push && git push origin vX.Y.Z`
72+
73+
GitHub Actions will automatically publish to npm on version tags.
74+
75+
**First-time setup:**
76+
1. Create GitHub environment `npm` at repo settings
77+
2. Publish manually once: `npm login && npm publish --access public`
78+
3. Add NPM_TOKEN secret for publishing

Makefile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.PHONY: install clean
2+
3+
install:
4+
pnpm install
5+
pnpm run build
6+
pnpm link --global
7+
8+
clean:
9+
rm -rf dist node_modules

README.md

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
# @cardmagic/notes
2+
3+
CLI and MCP server to search and browse Apple Notes with fuzzy matching.
4+
5+
## Features
6+
7+
- **Fuzzy search** - Find notes even with typos using MiniSearch
8+
- **Full-text search** - Searches note titles, snippets, and body content
9+
- **PDF text extraction** - Automatically extracts and indexes text from PDF attachments
10+
- **Folder browsing** - List and filter notes by folder
11+
- **Fast indexing** - SQLite FTS5 + MiniSearch for quick searches across thousands of notes
12+
- **Dual mode** - Use as CLI tool or MCP server for Claude Code integration
13+
14+
## Installation
15+
16+
```bash
17+
# Install globally
18+
npm install -g @cardmagic/notes
19+
20+
# Or with pnpm
21+
pnpm add -g @cardmagic/notes
22+
```
23+
24+
### Requirements
25+
26+
- **macOS** - Reads from Apple Notes database
27+
- **Full Disk Access** - Terminal/IDE needs access to `~/Library/Group Containers/`
28+
- **pdftotext** (optional) - For PDF text extraction
29+
30+
```bash
31+
# Install pdftotext for PDF support
32+
brew install poppler
33+
```
34+
35+
## CLI Usage
36+
37+
### Search notes
38+
39+
```bash
40+
# Fuzzy search
41+
notes search "recipe chocolate"
42+
43+
# Filter by folder
44+
notes search "taxes" --folder "2024"
45+
46+
# Limit results
47+
notes search "meeting" --limit 5
48+
49+
# Filter by date
50+
notes search "project" --after 2024-01-01
51+
```
52+
53+
### Browse notes
54+
55+
```bash
56+
# Recent notes
57+
notes recent
58+
notes recent --limit 10
59+
60+
# List all folders
61+
notes folders
62+
63+
# Notes in a specific folder
64+
notes folder "Recipes"
65+
notes folder "Work" --limit 20
66+
```
67+
68+
### Read a note
69+
70+
```bash
71+
# Get note ID from search results, then read full content
72+
notes read 12345
73+
```
74+
75+
### Manage index
76+
77+
```bash
78+
# Show index statistics
79+
notes stats
80+
81+
# Update index (incremental - only processes changed notes)
82+
notes index
83+
84+
# Force full rebuild
85+
notes index --force
86+
```
87+
88+
The index uses **incremental updates** by default:
89+
- Tracks modification timestamps to detect changed notes
90+
- Only reprocesses notes modified since last index
91+
- Detects and removes deleted notes
92+
- Much faster than full rebuild for small changes
93+
94+
## MCP Server
95+
96+
Run as an MCP server for Claude Code integration:
97+
98+
```bash
99+
notes --mcp
100+
```
101+
102+
### Available Tools
103+
104+
| Tool | Description |
105+
|------|-------------|
106+
| `search_notes` | Fuzzy search through notes |
107+
| `recent_notes` | Get recently modified notes |
108+
| `read_note` | Read full note content by ID |
109+
| `list_folders` | List all folders with note counts |
110+
| `notes_in_folder` | List notes in a specific folder |
111+
| `get_note_stats` | Get index statistics |
112+
113+
### Claude Code Configuration
114+
115+
Add to your MCP settings:
116+
117+
```json
118+
{
119+
"mcpServers": {
120+
"notes": {
121+
"command": "notes",
122+
"args": ["--mcp"]
123+
}
124+
}
125+
}
126+
```
127+
128+
## PDF Text Extraction
129+
130+
PDF attachments in Notes are automatically extracted and indexed when:
131+
132+
1. **pdftotext is installed** - `brew install poppler`
133+
2. **PDF has been viewed** - Notes caches PDFs locally when opened
134+
135+
The extracted text is appended to the note body, making PDF content fully searchable.
136+
137+
### How it works
138+
139+
- PDFs are cached at `~/Library/Group Containers/group.com.apple.notes/Library/Caches/Paper/`
140+
- Each PDF bundle contains the file in `Assets.bundle/`
141+
- Text is extracted using `pdftotext` and indexed with the parent note
142+
143+
### Limitations
144+
145+
- PDFs stored only in iCloud (never opened locally) won't be indexed
146+
- Password-protected PDFs cannot be extracted
147+
- Scanned PDFs without OCR won't have searchable text
148+
149+
## Data Locations
150+
151+
| Data | Path |
152+
|------|------|
153+
| Notes database | `~/Library/Group Containers/group.com.apple.notes/NoteStore.sqlite` |
154+
| PDF cache | `~/Library/Group Containers/group.com.apple.notes/Library/Caches/Paper/` |
155+
| Search index | `~/.notes/index.db` |
156+
| Fuzzy index | `~/.notes/fuzzy.json` |
157+
| Stats | `~/.notes/stats.json` |
158+
159+
## Development
160+
161+
```bash
162+
# Clone and install
163+
git clone https://github.com/cardmagic/notes
164+
cd notes
165+
pnpm install
166+
167+
# Build
168+
pnpm build
169+
170+
# Watch mode
171+
pnpm dev
172+
173+
# Link globally for testing
174+
pnpm link --global
175+
176+
# Type check
177+
pnpm typecheck
178+
179+
# Lint
180+
pnpm lint
181+
```
182+
183+
### Project Structure
184+
185+
```
186+
src/
187+
├── index.ts # Entry point - routes to CLI or MCP
188+
├── cli.ts # Commander-based CLI
189+
├── mcp.ts # MCP server implementation
190+
├── indexer.ts # Builds search indexes from Notes database
191+
├── searcher.ts # Query engine with fuzzy matching
192+
├── attachments.ts # PDF text extraction
193+
├── formatter.ts # Terminal output formatting
194+
└── types.ts # TypeScript types and utilities
195+
```
196+
197+
## Privacy
198+
199+
This tool only reads your local Notes database. No data is sent externally. The search index is stored locally in `~/.notes/`.
200+
201+
## License
202+
203+
MIT
204+
205+
## Author
206+
207+
Lucas Carlson

0 commit comments

Comments
 (0)