@cardmagic/notes 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Lucas Carlson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -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,0 +1,10 @@
1
+ /**
2
+ * Get text from all PDF attachments in Notes
3
+ * Returns a map of noteId (database ID) -> extracted PDF text
4
+ */
5
+ export declare function extractAllPdfText(): Map<string, string>;
6
+ /**
7
+ * Get the note key format used in extractAllPdfText
8
+ */
9
+ export declare function getNoteKey(noteDbId: number): string;
10
+ //# sourceMappingURL=attachments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.d.ts","sourceRoot":"","sources":["../src/attachments.ts"],"names":[],"mappings":"AAsGA;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAkCvD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEnD"}
@@ -0,0 +1,122 @@
1
+ import { execSync } from 'node:child_process';
2
+ import { existsSync, readdirSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import Database from 'better-sqlite3';
6
+ const PAPER_CACHE_DIR = join(homedir(), 'Library/Group Containers/group.com.apple.notes/Library/Caches/Paper');
7
+ const NOTES_DB_PATH = join(homedir(), 'Library/Group Containers/group.com.apple.notes/NoteStore.sqlite');
8
+ /**
9
+ * Get PDF attachments from the Notes database
10
+ */
11
+ function getPdfAttachmentsFromDb() {
12
+ try {
13
+ const db = new Database(NOTES_DB_PATH, { readonly: true });
14
+ const query = db.prepare(`
15
+ SELECT
16
+ a.Z_PK as attachmentId,
17
+ a.ZNOTE as noteId,
18
+ n.ZTITLE1 as noteTitle,
19
+ a.ZFILENAME as attachmentName,
20
+ a.ZIDENTIFIER as contentIdentifier,
21
+ a.ZTYPEUTI as typeUti
22
+ FROM ZICCLOUDSYNCINGOBJECT a
23
+ JOIN ZICCLOUDSYNCINGOBJECT n ON a.ZNOTE = n.Z_PK
24
+ WHERE a.Z_ENT = 5
25
+ AND (a.ZTYPEUTI = 'com.adobe.pdf' OR a.ZTYPEUTI = 'com.apple.paper.doc.pdf')
26
+ AND a.ZIDENTIFIER IS NOT NULL
27
+ `);
28
+ const rows = query.all();
29
+ db.close();
30
+ return rows;
31
+ }
32
+ catch {
33
+ return [];
34
+ }
35
+ }
36
+ /**
37
+ * Find the cached file for a given UUID in the Paper cache
38
+ */
39
+ function findCachedFile(uuid) {
40
+ const bundlePath = join(PAPER_CACHE_DIR, `${uuid}.bundle`, 'Assets.bundle');
41
+ if (!existsSync(bundlePath)) {
42
+ return null;
43
+ }
44
+ try {
45
+ const files = readdirSync(bundlePath);
46
+ for (const file of files) {
47
+ const filePath = join(bundlePath, file);
48
+ // Check if it's a PDF
49
+ try {
50
+ const result = execSync(`file "${filePath}"`, { encoding: 'utf-8', timeout: 5000 });
51
+ if (result.includes('PDF')) {
52
+ return filePath;
53
+ }
54
+ }
55
+ catch {
56
+ continue;
57
+ }
58
+ }
59
+ }
60
+ catch {
61
+ return null;
62
+ }
63
+ return null;
64
+ }
65
+ /**
66
+ * Extract text from a PDF file using pdftotext
67
+ */
68
+ function extractPdfText(pdfPath) {
69
+ try {
70
+ const text = execSync(`pdftotext "${pdfPath}" - 2>/dev/null`, {
71
+ encoding: 'utf-8',
72
+ timeout: 30000,
73
+ maxBuffer: 10 * 1024 * 1024,
74
+ });
75
+ return text.trim();
76
+ }
77
+ catch {
78
+ return '';
79
+ }
80
+ }
81
+ /**
82
+ * Get text from all PDF attachments in Notes
83
+ * Returns a map of noteId (database ID) -> extracted PDF text
84
+ */
85
+ export function extractAllPdfText() {
86
+ const pdfTextByNote = new Map();
87
+ // Check if pdftotext is available
88
+ try {
89
+ execSync('which pdftotext', { encoding: 'utf-8' });
90
+ }
91
+ catch {
92
+ // pdftotext not installed, skip PDF extraction
93
+ return pdfTextByNote;
94
+ }
95
+ const attachments = getPdfAttachmentsFromDb();
96
+ for (const att of attachments) {
97
+ if (!att.contentIdentifier)
98
+ continue;
99
+ const cachedPath = findCachedFile(att.contentIdentifier);
100
+ if (cachedPath) {
101
+ const text = extractPdfText(cachedPath);
102
+ if (text) {
103
+ const noteKey = `note_${att.noteId}`;
104
+ const attachmentLabel = att.attachmentName || 'PDF Attachment';
105
+ // Append to existing text for this note (note may have multiple PDFs)
106
+ const existing = pdfTextByNote.get(noteKey) || '';
107
+ const combined = existing
108
+ ? `${existing}\n\n--- ${attachmentLabel} ---\n\n${text}`
109
+ : `--- ${attachmentLabel} ---\n\n${text}`;
110
+ pdfTextByNote.set(noteKey, combined);
111
+ }
112
+ }
113
+ }
114
+ return pdfTextByNote;
115
+ }
116
+ /**
117
+ * Get the note key format used in extractAllPdfText
118
+ */
119
+ export function getNoteKey(noteDbId) {
120
+ return `note_${noteDbId}`;
121
+ }
122
+ //# sourceMappingURL=attachments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.js","sourceRoot":"","sources":["../src/attachments.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare function runCli(): void;
2
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAgMA,wBAAgB,MAAM,IAAI,IAAI,CAE7B"}
package/dist/cli.js ADDED
@@ -0,0 +1,180 @@
1
+ import { Command } from 'commander';
2
+ import { readFileSync } from 'node:fs';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { dirname, join } from 'node:path';
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
7
+ import { buildIndex, updateIndex, indexNeedsRebuild } from './indexer.js';
8
+ import { searchNotes, getRecentNotes, getNoteById, getNotesInFolder, listFolders, getNoteStats, closeConnections, } from './searcher.js';
9
+ import { formatSearchResults, formatNoteList, formatFolderList, formatStats, formatIndexProgress, formatNote, } from './formatter.js';
10
+ const program = new Command();
11
+ program
12
+ .name('notes')
13
+ .description('CLI tool to search and browse Apple Notes')
14
+ .version(pkg.version);
15
+ program
16
+ .command('search <query>')
17
+ .description('Search notes with fuzzy matching')
18
+ .option('-l, --limit <number>', 'Maximum results to return', '20')
19
+ .option('-f, --folder <name>', 'Filter by folder name')
20
+ .option('-a, --after <date>', 'Only notes modified after this date (YYYY-MM-DD)')
21
+ .action(async (query, options) => {
22
+ try {
23
+ const results = await searchNotes(query, {
24
+ limit: parseInt(options.limit, 10),
25
+ folder: options.folder,
26
+ after: options.after,
27
+ });
28
+ console.log(formatSearchResults(results, query));
29
+ }
30
+ catch (error) {
31
+ console.error('Error:', error.message);
32
+ process.exit(1);
33
+ }
34
+ finally {
35
+ closeConnections();
36
+ }
37
+ });
38
+ program
39
+ .command('recent')
40
+ .description('Show most recent notes')
41
+ .option('-l, --limit <number>', 'Maximum notes to return', '20')
42
+ .action(async (options) => {
43
+ try {
44
+ const notes = await getRecentNotes(parseInt(options.limit, 10));
45
+ console.log(formatNoteList(notes));
46
+ }
47
+ catch (error) {
48
+ console.error('Error:', error.message);
49
+ process.exit(1);
50
+ }
51
+ finally {
52
+ closeConnections();
53
+ }
54
+ });
55
+ program
56
+ .command('read <id>')
57
+ .description('Read a note by ID')
58
+ .action(async (id) => {
59
+ try {
60
+ const noteId = parseInt(id, 10);
61
+ if (isNaN(noteId)) {
62
+ console.error('Invalid note ID');
63
+ process.exit(1);
64
+ }
65
+ const note = await getNoteById(noteId);
66
+ if (!note) {
67
+ console.error('Note not found');
68
+ process.exit(1);
69
+ }
70
+ console.log(formatNote(note, true));
71
+ if (note.body) {
72
+ console.log('\n--- Content ---\n');
73
+ console.log(note.body);
74
+ }
75
+ }
76
+ catch (error) {
77
+ console.error('Error:', error.message);
78
+ process.exit(1);
79
+ }
80
+ finally {
81
+ closeConnections();
82
+ }
83
+ });
84
+ program
85
+ .command('folder <name>')
86
+ .description('List notes in a folder')
87
+ .option('-l, --limit <number>', 'Maximum notes to return', '50')
88
+ .option('-a, --after <date>', 'Only notes modified after this date (YYYY-MM-DD)')
89
+ .action(async (name, options) => {
90
+ try {
91
+ const notes = await getNotesInFolder(name, {
92
+ limit: parseInt(options.limit, 10),
93
+ after: options.after,
94
+ });
95
+ console.log(formatNoteList(notes));
96
+ }
97
+ catch (error) {
98
+ console.error('Error:', error.message);
99
+ process.exit(1);
100
+ }
101
+ finally {
102
+ closeConnections();
103
+ }
104
+ });
105
+ program
106
+ .command('folders')
107
+ .description('List all folders')
108
+ .option('-l, --limit <number>', 'Maximum folders to return', '50')
109
+ .action(async (options) => {
110
+ try {
111
+ const folders = await listFolders(parseInt(options.limit, 10));
112
+ console.log(formatFolderList(folders));
113
+ }
114
+ catch (error) {
115
+ console.error('Error:', error.message);
116
+ process.exit(1);
117
+ }
118
+ finally {
119
+ closeConnections();
120
+ }
121
+ });
122
+ program
123
+ .command('index')
124
+ .description('Update or rebuild the search index')
125
+ .option('-f, --force', 'Force full rebuild (default is incremental update)')
126
+ .action(async (options) => {
127
+ try {
128
+ if (options.force) {
129
+ console.log('Rebuilding entire index...\n');
130
+ const stats = await buildIndex(progress => {
131
+ const msg = progress.message ? ` ${progress.message}` : '';
132
+ process.stdout.write('\r' + formatIndexProgress(progress).split('\n').join(' | ') + msg);
133
+ });
134
+ console.log('\n\n');
135
+ console.log(formatStats(stats));
136
+ }
137
+ else {
138
+ if (!indexNeedsRebuild()) {
139
+ console.log('Index is up to date.');
140
+ return;
141
+ }
142
+ console.log('Updating index...\n');
143
+ const result = await updateIndex(progress => {
144
+ const msg = progress.message ? ` ${progress.message}` : '';
145
+ process.stdout.write('\r' + formatIndexProgress(progress).split('\n').join(' | ') + msg);
146
+ });
147
+ console.log('\n\n');
148
+ console.log(`Updated ${result.updated} notes, removed ${result.deleted} deleted notes.`);
149
+ console.log(formatStats(result));
150
+ }
151
+ }
152
+ catch (error) {
153
+ console.error('Error:', error.message);
154
+ process.exit(1);
155
+ }
156
+ });
157
+ program
158
+ .command('stats')
159
+ .description('Show index statistics')
160
+ .action(async () => {
161
+ try {
162
+ const stats = await getNoteStats();
163
+ if (!stats) {
164
+ console.log('No index found. Run "notes index" first.');
165
+ return;
166
+ }
167
+ console.log(formatStats(stats));
168
+ }
169
+ catch (error) {
170
+ console.error('Error:', error.message);
171
+ process.exit(1);
172
+ }
173
+ finally {
174
+ closeConnections();
175
+ }
176
+ });
177
+ export function runCli() {
178
+ program.parse(process.argv);
179
+ }
180
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { IndexedNote, SearchResult, FolderInfo, IndexStats } from './types.js';
2
+ import type { IndexProgress } from './indexer.js';
3
+ export declare function formatDate(unixTimestamp: number): string;
4
+ export declare function formatNote(note: IndexedNote, showBody?: boolean): string;
5
+ export declare function formatSearchResult(result: SearchResult, query: string): string;
6
+ export declare function formatFolder(folder: FolderInfo): string;
7
+ export declare function formatStats(stats: IndexStats): string;
8
+ export declare function formatIndexProgress(progress: IndexProgress): string;
9
+ export declare function formatNoteList(notes: IndexedNote[]): string;
10
+ export declare function formatSearchResults(results: SearchResult[], query: string): string;
11
+ export declare function formatFolderList(folders: FolderInfo[]): string;
12
+ //# sourceMappingURL=formatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACpF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,wBAAgB,UAAU,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAWxD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,EAAE,QAAQ,UAAQ,GAAG,MAAM,CA0BtE;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,MAAM,GACZ,MAAM,CA2BR;AAkBD,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAOvD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAiBrD;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAyBnE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CAM3D;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlF;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CAM9D"}