cursor-history 0.6.0 → 0.9.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.
Files changed (101) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +371 -9
  3. package/dist/cli/commands/backup.d.ts +9 -0
  4. package/dist/cli/commands/backup.d.ts.map +1 -0
  5. package/dist/cli/commands/backup.js +168 -0
  6. package/dist/cli/commands/backup.js.map +1 -0
  7. package/dist/cli/commands/export.d.ts.map +1 -1
  8. package/dist/cli/commands/export.js +38 -6
  9. package/dist/cli/commands/export.js.map +1 -1
  10. package/dist/cli/commands/list-backups.d.ts +9 -0
  11. package/dist/cli/commands/list-backups.d.ts.map +1 -0
  12. package/dist/cli/commands/list-backups.js +166 -0
  13. package/dist/cli/commands/list-backups.js.map +1 -0
  14. package/dist/cli/commands/list.d.ts.map +1 -1
  15. package/dist/cli/commands/list.js +44 -9
  16. package/dist/cli/commands/list.js.map +1 -1
  17. package/dist/cli/commands/migrate-session.d.ts +12 -0
  18. package/dist/cli/commands/migrate-session.d.ts.map +1 -0
  19. package/dist/cli/commands/migrate-session.js +125 -0
  20. package/dist/cli/commands/migrate-session.js.map +1 -0
  21. package/dist/cli/commands/migrate.d.ts +13 -0
  22. package/dist/cli/commands/migrate.d.ts.map +1 -0
  23. package/dist/cli/commands/migrate.js +122 -0
  24. package/dist/cli/commands/migrate.js.map +1 -0
  25. package/dist/cli/commands/restore.d.ts +9 -0
  26. package/dist/cli/commands/restore.d.ts.map +1 -0
  27. package/dist/cli/commands/restore.js +192 -0
  28. package/dist/cli/commands/restore.js.map +1 -0
  29. package/dist/cli/commands/search.d.ts.map +1 -1
  30. package/dist/cli/commands/search.js +30 -2
  31. package/dist/cli/commands/search.js.map +1 -1
  32. package/dist/cli/commands/show.d.ts.map +1 -1
  33. package/dist/cli/commands/show.js +31 -3
  34. package/dist/cli/commands/show.js.map +1 -1
  35. package/dist/cli/index.js +10 -0
  36. package/dist/cli/index.js.map +1 -1
  37. package/dist/core/backup.d.ts +86 -0
  38. package/dist/core/backup.d.ts.map +1 -0
  39. package/dist/core/backup.js +727 -0
  40. package/dist/core/backup.js.map +1 -0
  41. package/dist/core/database/debug.d.ts +12 -0
  42. package/dist/core/database/debug.d.ts.map +1 -0
  43. package/dist/core/database/debug.js +16 -0
  44. package/dist/core/database/debug.js.map +1 -0
  45. package/dist/core/database/drivers/better-sqlite3.d.ts +11 -0
  46. package/dist/core/database/drivers/better-sqlite3.d.ts.map +1 -0
  47. package/dist/core/database/drivers/better-sqlite3.js +93 -0
  48. package/dist/core/database/drivers/better-sqlite3.js.map +1 -0
  49. package/dist/core/database/drivers/node-sqlite.d.ts +15 -0
  50. package/dist/core/database/drivers/node-sqlite.d.ts.map +1 -0
  51. package/dist/core/database/drivers/node-sqlite.js +135 -0
  52. package/dist/core/database/drivers/node-sqlite.js.map +1 -0
  53. package/dist/core/database/errors.d.ts +24 -0
  54. package/dist/core/database/errors.d.ts.map +1 -0
  55. package/dist/core/database/errors.js +38 -0
  56. package/dist/core/database/errors.js.map +1 -0
  57. package/dist/core/database/index.d.ts +98 -0
  58. package/dist/core/database/index.d.ts.map +1 -0
  59. package/dist/core/database/index.js +125 -0
  60. package/dist/core/database/index.js.map +1 -0
  61. package/dist/core/database/registry.d.ts +81 -0
  62. package/dist/core/database/registry.d.ts.map +1 -0
  63. package/dist/core/database/registry.js +171 -0
  64. package/dist/core/database/registry.js.map +1 -0
  65. package/dist/core/database/types.d.ts +115 -0
  66. package/dist/core/database/types.d.ts.map +1 -0
  67. package/dist/core/database/types.js +8 -0
  68. package/dist/core/database/types.js.map +1 -0
  69. package/dist/core/migrate.d.ts +40 -0
  70. package/dist/core/migrate.d.ts.map +1 -0
  71. package/dist/core/migrate.js +586 -0
  72. package/dist/core/migrate.js.map +1 -0
  73. package/dist/core/storage.d.ts +84 -10
  74. package/dist/core/storage.d.ts.map +1 -1
  75. package/dist/core/storage.js +319 -33
  76. package/dist/core/storage.js.map +1 -1
  77. package/dist/core/types.d.ts +252 -0
  78. package/dist/core/types.d.ts.map +1 -1
  79. package/dist/lib/backup.d.ts +98 -0
  80. package/dist/lib/backup.d.ts.map +1 -0
  81. package/dist/lib/backup.js +108 -0
  82. package/dist/lib/backup.js.map +1 -0
  83. package/dist/lib/config.d.ts +3 -1
  84. package/dist/lib/config.d.ts.map +1 -1
  85. package/dist/lib/config.js +10 -0
  86. package/dist/lib/config.js.map +1 -1
  87. package/dist/lib/errors.d.ts +229 -0
  88. package/dist/lib/errors.d.ts.map +1 -1
  89. package/dist/lib/errors.js +361 -0
  90. package/dist/lib/errors.js.map +1 -1
  91. package/dist/lib/index.d.ts +133 -24
  92. package/dist/lib/index.d.ts.map +1 -1
  93. package/dist/lib/index.js +212 -49
  94. package/dist/lib/index.js.map +1 -1
  95. package/dist/lib/platform.d.ts +11 -0
  96. package/dist/lib/platform.d.ts.map +1 -1
  97. package/dist/lib/platform.js +32 -0
  98. package/dist/lib/platform.js.map +1 -1
  99. package/dist/lib/types.d.ts +267 -0
  100. package/dist/lib/types.d.ts.map +1 -1
  101. package/package.json +23 -3
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2025 Borui
2
+
3
+ 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, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -1,6 +1,115 @@
1
- # cursor-history
1
+ # Cursor History
2
2
 
3
- CLI tool and library to browse, search, and export your Cursor AI chat history.
3
+ <p align="center">
4
+ <img src="docs/logo.png" alt="cursor-history logo" width="200">
5
+ </p>
6
+
7
+ [![npm version](https://img.shields.io/npm/v/cursor-history.svg)](https://www.npmjs.com/package/cursor-history)
8
+ [![npm downloads](https://img.shields.io/npm/dm/cursor-history.svg)](https://www.npmjs.com/package/cursor-history)
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
+ [![Node.js](https://img.shields.io/badge/Node.js-20%2B-green.svg)](https://nodejs.org/)
11
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0%2B-blue.svg)](https://www.typescriptlang.org/)
12
+
13
+ **The ultimate open-source tool for browsing, searching, exporting, and backing up your Cursor AI chat history.**
14
+
15
+ A POSIX-style CLI tool that does one thing well: access your Cursor AI chat history. Built on Unix philosophy—simple, composable, and focused.
16
+
17
+ ```bash
18
+ # Pipe-friendly: combine with other tools
19
+ cursor-history list --json | jq '.[] | select(.messageCount > 10)'
20
+ cursor-history export 1 | grep -i "api" | head -20
21
+ cursor-history search "bug" --json | jq -r '.[].sessionId' | xargs -I {} cursor-history export {}
22
+ ```
23
+
24
+ Never lose a conversation again. Whether you need to find that perfect code snippet from last week, migrate your history to a new machine, or create reliable backups of all your AI-assisted development sessions—cursor-history has you covered. Free, open-source, and built by the community for the community.
25
+
26
+ ## Example Output
27
+
28
+ ### List Sessions
29
+
30
+ <pre>
31
+ <span style="color: #888">cursor-history list</span>
32
+
33
+ <span style="color: #5fd7ff">cursor-history</span> - Chat History Browser
34
+
35
+ <span style="color: #5fd7ff">Sessions (showing 3 of 42):</span>
36
+
37
+ <span style="color: #af87ff">#1</span> <span style="color: #87d787">12/26 09:15 AM</span> <span style="color: #d7d787">cursor_chat_history</span>
38
+ <span style="color: #888">15 messages · Updated 2 min ago</span>
39
+ <span style="color: #fff">"Help me fix the migration path issue..."</span>
40
+
41
+ <span style="color: #af87ff">#2</span> <span style="color: #87d787">12/25 03:22 PM</span> <span style="color: #d7d787">my-react-app</span>
42
+ <span style="color: #888">8 messages · Updated 18 hours ago</span>
43
+ <span style="color: #fff">"Add authentication to the app..."</span>
44
+
45
+ <span style="color: #af87ff">#3</span> <span style="color: #87d787">12/24 11:30 AM</span> <span style="color: #d7d787">api-server</span>
46
+ <span style="color: #888">23 messages · Updated 2 days ago</span>
47
+ <span style="color: #fff">"Create REST endpoints for users..."</span>
48
+ </pre>
49
+
50
+ ### Show Session Details
51
+
52
+ <pre>
53
+ <span style="color: #888">cursor-history show 1</span>
54
+
55
+ <span style="color: #5fd7ff">Session #1</span> · <span style="color: #d7d787">cursor_chat_history</span>
56
+ <span style="color: #888">15 messages · Created 12/26 09:15 AM</span>
57
+
58
+ ────────────────────────────────────────
59
+
60
+ <span style="color: #87d787">You:</span> <span style="color: #888">09:15:23 AM</span>
61
+
62
+ Help me fix the migration path issue in the codebase
63
+
64
+ ────────────────────────────────────────
65
+
66
+ <span style="color: #af87ff">Assistant:</span> <span style="color: #888">09:15:45 AM</span>
67
+
68
+ I'll help you fix the migration path issue. Let me first examine
69
+ the relevant files.
70
+
71
+ ────────────────────────────────────────
72
+
73
+ <span style="color: #d7af5f">Tool:</span> <span style="color: #888">09:15:46 AM</span>
74
+ <span style="color: #d7af5f">🔧 Read File</span>
75
+ <span style="color: #888">File:</span> <span style="color: #5fd7ff">src/core/migrate.ts</span>
76
+ <span style="color: #888">Content:</span> <span style="color: #fff">export function migrateSession(sessionId: string...</span>
77
+ <span style="color: #87d787">Status: ✓ completed</span>
78
+
79
+ ────────────────────────────────────────
80
+
81
+ <span style="color: #d7af5f">Tool:</span> <span style="color: #888">09:16:02 AM</span>
82
+ <span style="color: #d7af5f">🔧 Edit File</span>
83
+ <span style="color: #888">File:</span> <span style="color: #5fd7ff">src/core/migrate.ts</span>
84
+
85
+ <span style="color: #87d787">```diff</span>
86
+ <span style="color: #87d787"> + function transformPath(path: string): string {</span>
87
+ <span style="color: #87d787"> + return path.replace(sourcePrefix, destPrefix);</span>
88
+ <span style="color: #87d787"> + }</span>
89
+ <span style="color: #87d787">```</span>
90
+
91
+ <span style="color: #87d787">Status: ✓ completed</span>
92
+
93
+ ────────────────────────────────────────
94
+
95
+ <span style="color: #5f87d7">Thinking:</span> <span style="color: #888">09:16:02 AM</span>
96
+ <span style="color: #5f87d7">💭</span> <span style="color: #888">Now I need to update the function to call transformPath
97
+ for each file reference in the bubble data...</span>
98
+
99
+ ────────────────────────────────────────
100
+
101
+ <span style="color: #af87ff">Assistant:</span> <span style="color: #888">09:16:30 AM</span>
102
+
103
+ I've added the path transformation logic. The migration will now
104
+ update all file paths when moving sessions between workspaces.
105
+
106
+ ────────────────────────────────────────
107
+
108
+ <span style="color: #ff5f5f">Error:</span> <span style="color: #888">09:17:01 AM</span>
109
+ <span style="color: #ff5f5f">❌</span> <span style="color: #ff5f5f">Build failed: Cannot find module './utils'</span>
110
+
111
+ ────────────────────────────────────────
112
+ </pre>
4
113
 
5
114
  ## Features
6
115
 
@@ -14,6 +123,8 @@ CLI tool and library to browse, search, and export your Cursor AI chat history.
14
123
  - Message timestamps
15
124
  - **Search** - Find conversations by keyword with highlighted matches
16
125
  - **Export** - Save sessions as Markdown or JSON files
126
+ - **Migrate** - Move or copy sessions between workspaces (e.g., when renaming projects)
127
+ - **Backup & Restore** - Create full backups of all chat history and restore when needed
17
128
  - **Cross-platform** - Works on macOS, Windows, and Linux
18
129
 
19
130
  ## Installation
@@ -47,9 +158,65 @@ cursor-history list
47
158
 
48
159
  ## Requirements
49
160
 
50
- - Node.js 20+
161
+ - Node.js 20+ (Node.js 22.5+ recommended for built-in SQLite support)
51
162
  - Cursor IDE (with existing chat history)
52
163
 
164
+ ## SQLite Driver Configuration
165
+
166
+ cursor-history supports two SQLite drivers for maximum compatibility:
167
+
168
+ | Driver | Description | Node.js Version |
169
+ |--------|-------------|-----------------|
170
+ | `node:sqlite` | Built-in Node.js SQLite module (no native bindings) | 22.5+ |
171
+ | `better-sqlite3` | Native bindings via better-sqlite3 | 20+ |
172
+
173
+ ### Automatic Driver Selection
174
+
175
+ By default, cursor-history automatically selects the best available driver:
176
+
177
+ 1. **node:sqlite** (preferred) - Works on Node.js 22.5+ without native compilation
178
+ 2. **better-sqlite3** (fallback) - Works on older Node.js versions
179
+
180
+ ### Manual Driver Selection
181
+
182
+ You can force a specific driver using the environment variable:
183
+
184
+ ```bash
185
+ # Force better-sqlite3
186
+ CURSOR_HISTORY_SQLITE_DRIVER=better-sqlite3 cursor-history list
187
+
188
+ # Force node:sqlite (requires Node.js 22.5+)
189
+ CURSOR_HISTORY_SQLITE_DRIVER=node:sqlite cursor-history list
190
+ ```
191
+
192
+ ### Debug Driver Selection
193
+
194
+ To see which driver is being used:
195
+
196
+ ```bash
197
+ DEBUG=cursor-history:* cursor-history list
198
+ ```
199
+
200
+ ### Library API Driver Control
201
+
202
+ When using cursor-history as a library, you can control the driver programmatically:
203
+
204
+ ```typescript
205
+ import { setDriver, getActiveDriver, listSessions } from 'cursor-history';
206
+
207
+ // Force a specific driver before any operations
208
+ setDriver('better-sqlite3');
209
+
210
+ // Check which driver is active
211
+ const driver = getActiveDriver();
212
+ console.log(`Using driver: ${driver}`);
213
+
214
+ // Or configure via LibraryConfig
215
+ const result = await listSessions({
216
+ sqliteDriver: 'node:sqlite' // Force node:sqlite for this call
217
+ });
218
+ ```
219
+
53
220
  ## Usage
54
221
 
55
222
  ### List Sessions
@@ -128,6 +295,65 @@ cursor-history export --all -o ./exports/
128
295
  cursor-history export 1 --force
129
296
  ```
130
297
 
298
+ ### Migrate Sessions
299
+
300
+ ```bash
301
+ # Move a single session to another workspace
302
+ cursor-history migrate-session 1 /path/to/new/project
303
+
304
+ # Move multiple sessions (comma-separated indices or IDs)
305
+ cursor-history migrate-session 1,3,5 /path/to/project
306
+
307
+ # Copy instead of move (keeps original)
308
+ cursor-history migrate-session --copy 1 /path/to/project
309
+
310
+ # Preview what would happen without making changes
311
+ cursor-history migrate-session --dry-run 1 /path/to/project
312
+
313
+ # Move all sessions from one workspace to another
314
+ cursor-history migrate /old/project /new/project
315
+
316
+ # Copy all sessions (backup)
317
+ cursor-history migrate --copy /project /backup/project
318
+
319
+ # Force merge with existing sessions at destination
320
+ cursor-history migrate --force /old/project /existing/project
321
+ ```
322
+
323
+ ### Backup & Restore
324
+
325
+ ```bash
326
+ # Create a backup of all chat history
327
+ cursor-history backup
328
+
329
+ # Create backup to specific file
330
+ cursor-history backup -o ~/my-backup.zip
331
+
332
+ # Overwrite existing backup
333
+ cursor-history backup --force
334
+
335
+ # List available backups
336
+ cursor-history list-backups
337
+
338
+ # List backups in a specific directory
339
+ cursor-history list-backups -d /path/to/backups
340
+
341
+ # Restore from a backup
342
+ cursor-history restore ~/cursor-history-backups/backup.zip
343
+
344
+ # Restore to a custom location
345
+ cursor-history restore backup.zip --target /custom/cursor/data
346
+
347
+ # Force overwrite existing data
348
+ cursor-history restore backup.zip --force
349
+
350
+ # View sessions from a backup without restoring
351
+ cursor-history list --backup ~/backup.zip
352
+ cursor-history show 1 --backup ~/backup.zip
353
+ cursor-history search "query" --backup ~/backup.zip
354
+ cursor-history export 1 --backup ~/backup.zip
355
+ ```
356
+
131
357
  ### Global Options
132
358
 
133
359
  ```bash
@@ -213,6 +439,79 @@ for (const match of results) {
213
439
  const markdown = exportSessionToMarkdown(0);
214
440
  ```
215
441
 
442
+ ### Migration API
443
+
444
+ ```typescript
445
+ import { migrateSession, migrateWorkspace } from 'cursor-history';
446
+
447
+ // Move a session to another workspace
448
+ const results = migrateSession({
449
+ sessions: 3, // index or ID
450
+ destination: '/path/to/new/project'
451
+ });
452
+
453
+ // Copy multiple sessions (keeps originals)
454
+ const results = migrateSession({
455
+ sessions: [1, 3, 5],
456
+ destination: '/path/to/project',
457
+ mode: 'copy'
458
+ });
459
+
460
+ // Migrate all sessions between workspaces
461
+ const result = migrateWorkspace({
462
+ source: '/old/project',
463
+ destination: '/new/project'
464
+ });
465
+ console.log(`Migrated ${result.successCount} sessions`);
466
+ ```
467
+
468
+ ### Backup API
469
+
470
+ ```typescript
471
+ import {
472
+ createBackup,
473
+ restoreBackup,
474
+ validateBackup,
475
+ listBackups,
476
+ getDefaultBackupDir
477
+ } from 'cursor-history';
478
+
479
+ // Create a backup
480
+ const result = await createBackup({
481
+ outputPath: '~/my-backup.zip',
482
+ force: true,
483
+ onProgress: (progress) => {
484
+ console.log(`${progress.phase}: ${progress.filesCompleted}/${progress.totalFiles}`);
485
+ }
486
+ });
487
+ console.log(`Backup created: ${result.backupPath}`);
488
+ console.log(`Sessions: ${result.manifest.stats.sessionCount}`);
489
+
490
+ // Validate a backup
491
+ const validation = validateBackup('~/backup.zip');
492
+ if (validation.status === 'valid') {
493
+ console.log('Backup is valid');
494
+ } else if (validation.status === 'warnings') {
495
+ console.log('Backup has warnings:', validation.corruptedFiles);
496
+ }
497
+
498
+ // Restore from backup
499
+ const restoreResult = restoreBackup({
500
+ backupPath: '~/backup.zip',
501
+ force: true
502
+ });
503
+ console.log(`Restored ${restoreResult.filesRestored} files`);
504
+
505
+ // List available backups
506
+ const backups = listBackups(); // Scans ~/cursor-history-backups/
507
+ for (const backup of backups) {
508
+ console.log(`${backup.filename}: ${backup.manifest?.stats.sessionCount} sessions`);
509
+ }
510
+
511
+ // Read sessions from backup without restoring
512
+ const sessions = listSessions({ backupPath: '~/backup.zip' });
513
+ ```
514
+
216
515
  ### Available Functions
217
516
 
218
517
  | Function | Description |
@@ -224,17 +523,28 @@ const markdown = exportSessionToMarkdown(0);
224
523
  | `exportSessionToMarkdown(index, config?)` | Export session to Markdown |
225
524
  | `exportAllSessionsToJson(config?)` | Export all sessions to JSON |
226
525
  | `exportAllSessionsToMarkdown(config?)` | Export all sessions to Markdown |
526
+ | `migrateSession(config)` | Move/copy sessions to another workspace |
527
+ | `migrateWorkspace(config)` | Move/copy all sessions between workspaces |
528
+ | `createBackup(config?)` | Create full backup of all chat history |
529
+ | `restoreBackup(config)` | Restore chat history from backup |
530
+ | `validateBackup(path)` | Validate backup integrity |
531
+ | `listBackups(directory?)` | List available backup files |
532
+ | `getDefaultBackupDir()` | Get default backup directory path |
227
533
  | `getDefaultDataPath()` | Get platform-specific Cursor data path |
534
+ | `setDriver(name)` | Set SQLite driver ('better-sqlite3' or 'node:sqlite') |
535
+ | `getActiveDriver()` | Get currently active SQLite driver name |
228
536
 
229
537
  ### Configuration Options
230
538
 
231
539
  ```typescript
232
540
  interface LibraryConfig {
233
- dataPath?: string; // Custom Cursor data path
234
- workspace?: string; // Filter by workspace path
235
- limit?: number; // Pagination limit
236
- offset?: number; // Pagination offset
237
- context?: number; // Search context lines
541
+ dataPath?: string; // Custom Cursor data path
542
+ workspace?: string; // Filter by workspace path
543
+ limit?: number; // Pagination limit
544
+ offset?: number; // Pagination offset
545
+ context?: number; // Search context lines
546
+ backupPath?: string; // Read from backup file instead of live data
547
+ sqliteDriver?: 'better-sqlite3' | 'node:sqlite'; // Force specific SQLite driver
238
548
  }
239
549
  ```
240
550
 
@@ -243,8 +553,14 @@ interface LibraryConfig {
243
553
  ```typescript
244
554
  import {
245
555
  listSessions,
556
+ createBackup,
246
557
  isDatabaseLockedError,
247
- isDatabaseNotFoundError
558
+ isDatabaseNotFoundError,
559
+ isSessionNotFoundError,
560
+ isWorkspaceNotFoundError,
561
+ isBackupError,
562
+ isRestoreError,
563
+ isInvalidBackupError
248
564
  } from 'cursor-history';
249
565
 
250
566
  try {
@@ -254,6 +570,23 @@ try {
254
570
  console.error('Database locked - close Cursor and retry');
255
571
  } else if (isDatabaseNotFoundError(err)) {
256
572
  console.error('Cursor data not found');
573
+ } else if (isSessionNotFoundError(err)) {
574
+ console.error('Session not found');
575
+ } else if (isWorkspaceNotFoundError(err)) {
576
+ console.error('Workspace not found - open project in Cursor first');
577
+ }
578
+ }
579
+
580
+ // Backup-specific errors
581
+ try {
582
+ const result = await createBackup();
583
+ } catch (err) {
584
+ if (isBackupError(err)) {
585
+ console.error('Backup failed:', err.message);
586
+ } else if (isInvalidBackupError(err)) {
587
+ console.error('Invalid backup file');
588
+ } else if (isRestoreError(err)) {
589
+ console.error('Restore failed:', err.message);
257
590
  }
258
591
  }
259
592
  ```
@@ -300,6 +633,35 @@ This project uses GitHub Actions for automatic NPM publishing. To release a new
300
633
  2. Go to your GitHub repository settings → Secrets and variables → Actions
301
634
  3. Add a new repository secret named `NPM_TOKEN` with your NPM token
302
635
 
636
+ ## Contributing
637
+
638
+ We welcome contributions from the community! Here's how you can help:
639
+
640
+ ### Reporting Issues
641
+
642
+ - **Bug reports**: [Open an issue](https://github.com/S2thend/cursor_chat_history/issues/new) with steps to reproduce, expected vs actual behavior, and your environment (OS, Node.js version)
643
+ - **Feature requests**: [Open an issue](https://github.com/S2thend/cursor_chat_history/issues/new) describing the feature and its use case
644
+
645
+ ### Submitting Pull Requests
646
+
647
+ 1. Fork the repository
648
+ 2. Create a feature branch (`git checkout -b feature/my-feature`)
649
+ 3. Make your changes
650
+ 4. Run tests and linting (`npm test && npm run lint`)
651
+ 5. Commit your changes (`git commit -m 'Add my feature'`)
652
+ 6. Push to your fork (`git push origin feature/my-feature`)
653
+ 7. [Open a Pull Request](https://github.com/S2thend/cursor_chat_history/pulls)
654
+
655
+ ### Development Setup
656
+
657
+ ```bash
658
+ git clone https://github.com/S2thend/cursor_chat_history.git
659
+ cd cursor_chat_history
660
+ npm install
661
+ npm run build
662
+ npm test
663
+ ```
664
+
303
665
  ## License
304
666
 
305
667
  MIT
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Backup command - create full backup of all chat history
3
+ */
4
+ import type { Command } from 'commander';
5
+ /**
6
+ * Register the backup command
7
+ */
8
+ export declare function registerBackupCommand(program: Command): void;
9
+ //# sourceMappingURL=backup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/backup.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsGzC;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoF5D"}
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Backup command - create full backup of all chat history
3
+ */
4
+ import pc from 'picocolors';
5
+ import { createBackup } from '../../core/backup.js';
6
+ import { handleError, ExitCode } from '../errors.js';
7
+ import { expandPath, contractPath } from '../../lib/platform.js';
8
+ /**
9
+ * Format file size for display
10
+ */
11
+ function formatSize(bytes) {
12
+ if (bytes < 1024) {
13
+ return `${bytes} B`;
14
+ }
15
+ if (bytes < 1024 * 1024) {
16
+ return `${(bytes / 1024).toFixed(1)} KB`;
17
+ }
18
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
19
+ }
20
+ /**
21
+ * Format duration for display
22
+ */
23
+ function formatDuration(ms) {
24
+ if (ms < 1000) {
25
+ return `${ms}ms`;
26
+ }
27
+ return `${(ms / 1000).toFixed(1)}s`;
28
+ }
29
+ /**
30
+ * T021: Progress display for backup command
31
+ */
32
+ function displayProgress(progress) {
33
+ const phases = {
34
+ scanning: '🔍 Scanning for database files...',
35
+ 'backing-up': '📦 Backing up databases...',
36
+ compressing: '🗜️ Compressing into zip...',
37
+ finalizing: '✨ Finalizing backup...',
38
+ };
39
+ const phaseText = phases[progress.phase];
40
+ const fileProgress = progress.totalFiles > 0
41
+ ? ` [${progress.filesCompleted}/${progress.totalFiles}]`
42
+ : '';
43
+ const currentFile = progress.currentFile ? ` ${pc.dim(progress.currentFile)}` : '';
44
+ // Clear line and print progress
45
+ process.stdout.write(`\r${phaseText}${fileProgress}${currentFile}`.padEnd(80));
46
+ }
47
+ /**
48
+ * Format backup result for JSON output
49
+ */
50
+ function formatBackupResultJson(result) {
51
+ return JSON.stringify({
52
+ success: result.success,
53
+ backupPath: result.backupPath,
54
+ durationMs: result.durationMs,
55
+ ...(result.error && { error: result.error }),
56
+ manifest: result.manifest,
57
+ }, null, 2);
58
+ }
59
+ /**
60
+ * Format backup result for human-readable output
61
+ */
62
+ function formatBackupResult(result) {
63
+ const lines = [];
64
+ if (result.success) {
65
+ lines.push(pc.green('✓ Backup created successfully!'));
66
+ lines.push('');
67
+ lines.push(` ${pc.bold('Location:')} ${contractPath(result.backupPath)}`);
68
+ lines.push(` ${pc.bold('Size:')} ${formatSize(result.manifest.stats.totalSize)}`);
69
+ lines.push(` ${pc.bold('Sessions:')} ${result.manifest.stats.sessionCount}`);
70
+ lines.push(` ${pc.bold('Workspaces:')} ${result.manifest.stats.workspaceCount}`);
71
+ lines.push(` ${pc.bold('Files:')} ${result.manifest.files.length} database files`);
72
+ lines.push(` ${pc.bold('Duration:')} ${formatDuration(result.durationMs)}`);
73
+ }
74
+ else {
75
+ lines.push(pc.red('✗ Backup failed'));
76
+ lines.push('');
77
+ if (result.error) {
78
+ lines.push(` ${pc.bold('Error:')} ${result.error}`);
79
+ }
80
+ }
81
+ return lines.join('\n');
82
+ }
83
+ /**
84
+ * Register the backup command
85
+ */
86
+ export function registerBackupCommand(program) {
87
+ program
88
+ .command('backup')
89
+ .description('Create a full backup of all Cursor chat history')
90
+ .option('-o, --output <path>', 'Output file path (default: ~/cursor-history-backups/<timestamp>.zip)')
91
+ .option('-f, --force', 'Overwrite existing backup file')
92
+ .action(async (options, command) => {
93
+ const globalOptions = command.parent?.opts();
94
+ const useJson = options.json ?? globalOptions?.json ?? false;
95
+ const customPath = options.dataPath ?? globalOptions?.dataPath;
96
+ try {
97
+ // Resolve output path if provided
98
+ const outputPath = options.output ? expandPath(options.output) : undefined;
99
+ // Show progress if not JSON mode
100
+ const onProgress = useJson ? undefined : displayProgress;
101
+ // Create backup
102
+ const result = await createBackup({
103
+ sourcePath: customPath ? expandPath(customPath) : undefined,
104
+ outputPath,
105
+ force: options.force ?? false,
106
+ onProgress,
107
+ });
108
+ // Clear progress line
109
+ if (!useJson) {
110
+ process.stdout.write('\r'.padEnd(80) + '\r');
111
+ }
112
+ // Handle different error cases with appropriate exit codes
113
+ if (!result.success) {
114
+ // T022: No data to backup
115
+ if (result.error?.includes('No Cursor data found')) {
116
+ if (useJson) {
117
+ console.log(formatBackupResultJson(result));
118
+ }
119
+ else {
120
+ console.error(pc.yellow('No Cursor data found to backup.'));
121
+ console.error(pc.dim('Make sure Cursor has been used and has chat history.'));
122
+ }
123
+ process.exit(ExitCode.USAGE_ERROR);
124
+ }
125
+ // T023: File exists without --force
126
+ if (result.error?.includes('already exists')) {
127
+ if (useJson) {
128
+ console.log(formatBackupResultJson(result));
129
+ }
130
+ else {
131
+ console.error(pc.red('Backup file already exists.'));
132
+ console.error(pc.dim('Use --force to overwrite.'));
133
+ }
134
+ process.exit(ExitCode.NOT_FOUND);
135
+ }
136
+ // T024: Insufficient disk space
137
+ if (result.error?.includes('Insufficient disk space')) {
138
+ if (useJson) {
139
+ console.log(formatBackupResultJson(result));
140
+ }
141
+ else {
142
+ console.error(pc.red('Insufficient disk space for backup.'));
143
+ }
144
+ process.exit(ExitCode.IO_ERROR);
145
+ }
146
+ // Generic error
147
+ if (useJson) {
148
+ console.log(formatBackupResultJson(result));
149
+ }
150
+ else {
151
+ console.error(formatBackupResult(result));
152
+ }
153
+ process.exit(ExitCode.GENERAL_ERROR);
154
+ }
155
+ // Success
156
+ if (useJson) {
157
+ console.log(formatBackupResultJson(result));
158
+ }
159
+ else {
160
+ console.log(formatBackupResult(result));
161
+ }
162
+ }
163
+ catch (error) {
164
+ handleError(error);
165
+ }
166
+ });
167
+ }
168
+ //# sourceMappingURL=backup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup.js","sourceRoot":"","sources":["../../../src/cli/commands/backup.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/export.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwBzC;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoJ5D"}
1
+ {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/export.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2BzC;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiM5D"}