revert-ai 1.1.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.
@@ -0,0 +1,589 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import chalk from 'chalk';
5
+ import inquirer from 'inquirer';
6
+ import { SessionTracker } from '../src/core/SessionTracker.js';
7
+ import { UndoManager } from '../src/core/UndoManager.js';
8
+ import { formatDistance } from '../src/utils/formatting.js';
9
+ import { Operation, OperationType } from '../src/core/Operation.js';
10
+ import { AgentDetector } from '../src/core/AgentDetector.js';
11
+ import { OperationPreview } from '../src/core/OperationPreview.js';
12
+ import { i18n } from '../src/i18n/i18n.js';
13
+ import { UndoTracker } from '../src/core/UndoTracker.js';
14
+ import { RedoManager } from '../src/core/RedoManager.js';
15
+ import { GitHelper } from '../src/utils/gitHelper.js';
16
+ import { getDependentOperations } from '../src/core/DependencyCascading.js';
17
+ import path from 'path';
18
+
19
+ // Initialize i18n
20
+ await i18n.init();
21
+
22
+ const program = new Command();
23
+
24
+ program
25
+ .name('revert-ai')
26
+ .description('Undo individual steps performed by Claude Code within a session')
27
+ .version('1.1.1');
28
+
29
+ program
30
+ .command('list')
31
+ .description(i18n.t('cmd.list.description'))
32
+ .option('-a, --all', i18n.t('opt.all'))
33
+ .option('-s, --session <id>', i18n.t('opt.session'))
34
+ .option('--claude', i18n.t('opt.claude'), true)
35
+ .option('--local', i18n.t('opt.local'))
36
+ .action(async (options) => {
37
+ try {
38
+ let operations = [];
39
+
40
+ if (!options.local) {
41
+ const detection = await AgentDetector.detect();
42
+ if (!detection) {
43
+ console.log(chalk.yellow('No active AI agent session (Claude Code or Aider) found in this directory.'));
44
+ return;
45
+ }
46
+
47
+ const { agent, parser } = detection;
48
+ const sessionFile = await parser.getCurrentSessionFile();
49
+ operations = await parser.parseSessionFile(sessionFile, options.all);
50
+ console.log(chalk.bold(`\nOperations from ${agent === 'claude' ? 'Claude Code' : 'Aider'} session:\n`));
51
+ } else {
52
+ // Use local revert-ai tracking
53
+ const sessionId = options.session || await SessionTracker.getCurrentSession();
54
+ if (!sessionId) {
55
+ console.log(chalk.yellow('No local revert-ai session found.'));
56
+ return;
57
+ }
58
+
59
+ const tracker = new SessionTracker(sessionId);
60
+ await tracker.init();
61
+ operations = await tracker.getOperations(options.all);
62
+ console.log(chalk.bold(`\nOperations in local session ${sessionId}:\n`));
63
+ }
64
+
65
+ if (operations.length === 0) {
66
+ console.log(chalk.yellow('No operations found.'));
67
+ return;
68
+ }
69
+
70
+ operations.forEach((op, index) => {
71
+ const status = op.undone ? chalk.red('[UNDONE]') : chalk.green('[ACTIVE]');
72
+ const time = formatDistance(op.timestamp);
73
+
74
+ console.log(`${index + 1}. ${status} ${chalk.cyan(op.type)} - ${time}`);
75
+ console.log(` ID: ${op.id}`);
76
+
77
+ switch (op.type) {
78
+ case 'file_create':
79
+ case 'file_edit':
80
+ case 'file_delete':
81
+ console.log(` File: ${op.data.filePath}`);
82
+ break;
83
+ case 'file_rename':
84
+ console.log(` From: ${op.data.oldPath}`);
85
+ console.log(` To: ${op.data.newPath}`);
86
+ break;
87
+ case 'directory_create':
88
+ case 'directory_delete':
89
+ console.log(` Directory: ${op.data.dirPath}`);
90
+ break;
91
+ case 'bash_command':
92
+ console.log(` Command: ${op.data.command}`);
93
+ break;
94
+ }
95
+ console.log('');
96
+ });
97
+ } catch (error) {
98
+ console.error(chalk.red(`Error: ${error.message}`));
99
+ }
100
+ });
101
+
102
+ program
103
+ .command('undo [operation-id]')
104
+ .description('Undo operations from the current Claude Code session')
105
+ .option('-s, --session <id>', 'Specify session ID')
106
+ .option('-y, --yes', 'Skip confirmation')
107
+ .option('--local', 'Use local revert-ai tracking instead of Claude sessions')
108
+ .action(async (operationId, options) => {
109
+ try {
110
+ let operations = [];
111
+ let sessionFile = null;
112
+
113
+ if (options.local) {
114
+ // Use local revert-ai tracking
115
+ const sessionId = options.session || await SessionTracker.getCurrentSession();
116
+ if (!sessionId) {
117
+ console.log(chalk.yellow('No local revert-ai session found.'));
118
+ return;
119
+ }
120
+
121
+ const tracker = new SessionTracker(sessionId);
122
+ await tracker.init();
123
+ operations = await tracker.getOperations();
124
+ } else {
125
+ const detection = await AgentDetector.detect();
126
+ if (!detection) {
127
+ console.log(chalk.yellow('No active AI agent session found in this directory.'));
128
+ return;
129
+ }
130
+ const { parser } = detection;
131
+ sessionFile = await parser.getCurrentSessionFile();
132
+ operations = await parser.parseSessionFile(sessionFile);
133
+ }
134
+
135
+ if (operations.length === 0) {
136
+ console.log(chalk.yellow('No operations to undo.'));
137
+ return;
138
+ }
139
+
140
+ // Reverse operations so most recent is first
141
+ operations.reverse();
142
+
143
+ let selectedIndex = 0;
144
+
145
+ if (!operationId) {
146
+ const choices = operations.map((op, index) => {
147
+ const dependentOps = getDependentOperations(index, operations, process.cwd());
148
+ let name = `${op.type} - ${formatDistance(op.timestamp)}`;
149
+
150
+ if (dependentOps.length > 1) {
151
+ name += chalk.red(` (+ ${dependentOps.length - 1} dependent ops will be undone)`);
152
+ }
153
+
154
+ return {
155
+ name: name,
156
+ value: index,
157
+ short: `${op.type} (${dependentOps.length} ops)`
158
+ };
159
+ });
160
+
161
+ console.log(chalk.yellow('\n⚠️ AST Cascading undo: Selecting an operation will undo it and only other subsequent operations that depend on it.\n'));
162
+
163
+ const answer = await inquirer.prompt([{
164
+ type: 'list',
165
+ name: 'selectedIndex',
166
+ message: 'Select operation to undo:',
167
+ choices: choices,
168
+ pageSize: 15
169
+ }]);
170
+
171
+ selectedIndex = answer.selectedIndex;
172
+ } else {
173
+ selectedIndex = operations.findIndex(op => op.id === operationId);
174
+ if (selectedIndex === -1) {
175
+ console.log(chalk.red(`Operation ${operationId} not found.`));
176
+ return;
177
+ }
178
+ }
179
+
180
+ const operationsToUndo = getDependentOperations(selectedIndex, operations, process.cwd());
181
+
182
+ if (!options.yes) {
183
+ console.log(chalk.yellow(`\\nThis will undo ${operationsToUndo.length} operation(s):\\n`));
184
+
185
+ for (let i = 0; i < operationsToUndo.length; i++) {
186
+ const op = operationsToUndo[i];
187
+ console.log(`${chalk.bold(`${i + 1}.`)} ${chalk.cyan(op.type)} - ${formatDistance(op.timestamp)}`);
188
+
189
+ const preview = await OperationPreview.generatePreview(op);
190
+ console.log(` ${preview.preview.replace(/\\n/g, '\\n ')}`);
191
+ console.log('');
192
+ }
193
+
194
+ const confirm = await inquirer.prompt([{
195
+ type: 'confirm',
196
+ name: 'proceed',
197
+ message: `Are you sure you want to undo these ${operationsToUndo.length} operations?`,
198
+ default: false
199
+ }]);
200
+
201
+ if (!confirm.proceed) {
202
+ console.log(chalk.yellow('Undo cancelled.'));
203
+ return;
204
+ }
205
+ if (GitHelper.isWorkspaceDirty()) {
206
+ const gitAnswer = await inquirer.prompt([{
207
+ type: 'confirm',
208
+ name: 'stash',
209
+ message: '⚠️ You have uncommitted changes in your repository. Would you like to stash them before proceeding?',
210
+ default: true
211
+ }]);
212
+
213
+ if (gitAnswer.stash) {
214
+ console.log(chalk.cyan('\nStashing uncommitted changes...'));
215
+ GitHelper.saveStash(`revert-ai: Pre-undo stash for ${operationsToUndo.length} operations`);
216
+ }
217
+ }
218
+
219
+ const undoManager = new UndoManager();
220
+ await undoManager.init();
221
+
222
+ const undoTracker = new UndoTracker();
223
+ await undoTracker.init();
224
+
225
+ console.log(chalk.cyan(`\\nUndoing ${operationsToUndo.length} operations...\\n`));
226
+
227
+ let successCount = 0;
228
+ let failCount = 0;
229
+
230
+ for (const operation of operationsToUndo) {
231
+ const result = await undoManager.undo(operation);
232
+
233
+ if (result.success) {
234
+ successCount++;
235
+ console.log(chalk.green(`✓ ${result.message}`));
236
+ if (result.backupPath) {
237
+ console.log(chalk.gray(` Backup saved to: ${result.backupPath}`));
238
+ }
239
+
240
+ // Mark operation as undone if using Claude Code sessions
241
+ if (sessionFile) {
242
+ await undoTracker.markAsUndone(operation.id, sessionFile);
243
+ }
244
+ } else {
245
+ failCount++;
246
+ console.log(chalk.red(`✗ ${result.message}`));
247
+ }
248
+ }
249
+
250
+ console.log(chalk.bold(`\\nCompleted: ${chalk.green(successCount)} successful, ${chalk.red(failCount)} failed`));
251
+
252
+ } catch (error) {
253
+ console.error(chalk.red(`Error: ${error.message}`));
254
+ }
255
+ });
256
+
257
+ program
258
+ .command('redo [operation-id]')
259
+ .description(i18n.t('cmd.redo.description'))
260
+ .option('-s, --session <id>', i18n.t('opt.session'))
261
+ .option('-y, --yes', i18n.t('opt.yes'))
262
+ .option('--local', 'Use local revert-ai tracking instead of Claude sessions')
263
+ .action(async (operationId, options) => {
264
+ try {
265
+ let operations = [];
266
+ let sessionFile = null;
267
+
268
+ if (options.local) {
269
+ // Use local revert-ai tracking
270
+ const sessionId = options.session || await SessionTracker.getCurrentSession();
271
+ if (!sessionId) {
272
+ console.log(chalk.yellow('No local revert-ai session found.'));
273
+ return;
274
+ }
275
+
276
+ const tracker = new SessionTracker(sessionId);
277
+ await tracker.init();
278
+ // For local tracking, we'd need to implement redo tracking
279
+ console.log(chalk.yellow('Redo for local tracking is not yet implemented.'));
280
+ return;
281
+ } else {
282
+ const detection = await AgentDetector.detect();
283
+ if (!detection) {
284
+ console.log(chalk.yellow('No active AI agent session found in this directory.'));
285
+ return;
286
+ }
287
+ const { parser } = detection;
288
+ sessionFile = await parser.getCurrentSessionFile();
289
+
290
+ // Get all operations including undone ones
291
+ const originalOperations = await parser.parseSessionFile(sessionFile, true);
292
+ const undoTracker = new UndoTracker();
293
+ await undoTracker.init();
294
+
295
+ operations = await undoTracker.getUndoneOperationsList(originalOperations, sessionFile);
296
+ }
297
+
298
+ if (operations.length === 0) {
299
+ console.log(chalk.yellow(i18n.t('msg.no_operations_to_redo')));
300
+ return;
301
+ }
302
+
303
+ // Operations are already in reverse order (most recent undo first)
304
+ let selectedIndex = 0;
305
+
306
+ if (!operationId) {
307
+ const choices = operations.map((op, index) => {
308
+ const operationsToRedo = index + 1;
309
+ let name = `${op.type} - ${formatDistance(op.timestamp)}`;
310
+
311
+ if (operationsToRedo > 1) {
312
+ name += chalk.green(` (+ ${operationsToRedo - 1} more will be redone)`);
313
+ }
314
+
315
+ return {
316
+ name: name,
317
+ value: index,
318
+ short: `${op.type} (${operationsToRedo} ops)`
319
+ };
320
+ });
321
+
322
+ console.log(chalk.yellow('\\n⚠️ Cascading redo: Selecting an operation will redo it and ALL undone operations that came before it.\\n'));
323
+
324
+ const answer = await inquirer.prompt([{
325
+ type: 'list',
326
+ name: 'selectedIndex',
327
+ message: i18n.t('prompt.select_operation_redo'),
328
+ choices: choices,
329
+ pageSize: 15
330
+ }]);
331
+
332
+ selectedIndex = answer.selectedIndex;
333
+ } else {
334
+ selectedIndex = operations.findIndex(op => op.id === operationId);
335
+ if (selectedIndex === -1) {
336
+ console.log(chalk.red(`Operation ${operationId} not found.`));
337
+ return;
338
+ }
339
+ }
340
+
341
+ const operationsToRedo = operations.slice(0, selectedIndex + 1);
342
+
343
+ if (!options.yes) {
344
+ console.log(chalk.yellow(`\\n${i18n.t('header.this_will_redo', { count: operationsToRedo.length })}\\n`));
345
+
346
+ for (let i = 0; i < operationsToRedo.length; i++) {
347
+ const op = operationsToRedo[i];
348
+ console.log(`${chalk.bold(`${i + 1}.`)} ${chalk.cyan(op.type)} - ${formatDistance(op.timestamp)}`);
349
+ console.log('');
350
+ }
351
+
352
+ const confirm = await inquirer.prompt([{
353
+ type: 'confirm',
354
+ name: 'proceed',
355
+ message: i18n.t('prompt.confirm_redo', { count: operationsToRedo.length }),
356
+ default: false
357
+ }]);
358
+
359
+ if (!confirm.proceed) {
360
+ console.log(chalk.yellow('Redo cancelled.'));
361
+ return;
362
+ }
363
+ }
364
+
365
+ const redoManager = new RedoManager();
366
+ await redoManager.init();
367
+
368
+ const undoTracker = new UndoTracker();
369
+ await undoTracker.init();
370
+
371
+ console.log(chalk.cyan(`\\n${i18n.t('header.redoing', { count: operationsToRedo.length })}\\n`));
372
+
373
+ let successCount = 0;
374
+ let failCount = 0;
375
+
376
+ // Redo operations in reverse order (oldest undone operation first)
377
+ for (const operation of operationsToRedo.reverse()) {
378
+ const result = await redoManager.redo(operation);
379
+
380
+ if (result.success) {
381
+ successCount++;
382
+ console.log(chalk.green(`✓ ${result.message}`));
383
+ if (result.backupPath) {
384
+ console.log(chalk.gray(` Backup saved to: ${result.backupPath}`));
385
+ }
386
+
387
+ // Mark operation as redone (remove from undone list)
388
+ if (sessionFile) {
389
+ await undoTracker.markAsRedone(operation.id, sessionFile);
390
+ }
391
+ } else {
392
+ failCount++;
393
+ console.log(chalk.red(`✗ ${result.message}`));
394
+ }
395
+ }
396
+
397
+ console.log(chalk.bold(`\\nCompleted: ${chalk.green(successCount)} successful, ${chalk.red(failCount)} failed`));
398
+
399
+ } catch (error) {
400
+ console.error(chalk.red(`Error: ${error.message}`));
401
+ }
402
+ });
403
+
404
+ program
405
+ .command('preview [operation-id]')
406
+ .description('Preview what would be undone without making changes')
407
+ .option('-s, --session <id>', 'Specify session ID')
408
+ .option('--local', 'Use local revert-ai tracking instead of Claude sessions')
409
+ .action(async (operationId, options) => {
410
+ try {
411
+ let operations = [];
412
+
413
+ if (options.local) {
414
+ const sessionId = options.session || await SessionTracker.getCurrentSession();
415
+ if (!sessionId) {
416
+ console.log(chalk.yellow('No local revert-ai session found.'));
417
+ return;
418
+ }
419
+
420
+ const tracker = new SessionTracker(sessionId);
421
+ await tracker.init();
422
+ operations = await tracker.getOperations();
423
+ } else {
424
+ const detection = await AgentDetector.detect();
425
+ if (!detection) {
426
+ console.log(chalk.yellow('No active AI agent session found in this directory.'));
427
+ return;
428
+ }
429
+ const { parser } = detection;
430
+ const sessionFile = await parser.getCurrentSessionFile();
431
+ operations = await parser.parseSessionFile(sessionFile);
432
+ }
433
+
434
+ if (operations.length === 0) {
435
+ console.log(chalk.yellow('No operations found.'));
436
+ return;
437
+ }
438
+
439
+ operations.reverse();
440
+
441
+ let selectedIndex = 0;
442
+
443
+ if (!operationId) {
444
+ const choices = operations.map((op, index) => {
445
+ const dependentOps = getDependentOperations(index, operations, process.cwd());
446
+ let name = `${op.type} - ${formatDistance(op.timestamp)}`;
447
+
448
+ if (dependentOps.length > 1) {
449
+ name += chalk.gray(` (+ ${dependentOps.length - 1} dependent ops would be undone)`);
450
+ }
451
+
452
+ return {
453
+ name: name,
454
+ value: index
455
+ };
456
+ });
457
+
458
+ const answer = await inquirer.prompt([{
459
+ type: 'list',
460
+ name: 'selectedIndex',
461
+ message: 'Select operation to preview:',
462
+ choices: choices,
463
+ pageSize: 15
464
+ }]);
465
+
466
+ selectedIndex = answer.selectedIndex;
467
+ } else {
468
+ selectedIndex = operations.findIndex(op => op.id === operationId);
469
+ if (selectedIndex === -1) {
470
+ console.log(chalk.red(`Operation ${operationId} not found.`));
471
+ return;
472
+ }
473
+ }
474
+
475
+ const operationsToUndo = getDependentOperations(selectedIndex, operations, process.cwd());
476
+
477
+ console.log(chalk.blue(`\\n📋 Preview: Would undo ${operationsToUndo.length} operation(s):\\n`));
478
+
479
+ for (let i = 0; i < operationsToUndo.length; i++) {
480
+ const op = operationsToUndo[i];
481
+ console.log(`${chalk.bold(`${i + 1}.`)} ${chalk.cyan(op.type)} - ${formatDistance(op.timestamp)}`);
482
+
483
+ const preview = await OperationPreview.generatePreview(op);
484
+ console.log(` ${preview.preview.replace(/\\n/g, '\\n ')}`);
485
+ console.log('');
486
+ }
487
+
488
+ console.log(chalk.gray('💡 To actually perform these undos, run: revert-ai undo'));
489
+
490
+ } catch (error) {
491
+ console.error(chalk.red(`Error: ${error.message}`));
492
+ }
493
+ });
494
+
495
+ program
496
+ .command('sessions')
497
+ .description('List all available Claude Code sessions')
498
+ .option('--local', 'Show local revert-ai sessions instead of Claude sessions')
499
+ .action(async (options) => {
500
+ try {
501
+ if (options.local) {
502
+ const sessions = await SessionTracker.listSessions();
503
+ const currentSession = await SessionTracker.getCurrentSession();
504
+
505
+ if (sessions.length === 0) {
506
+ console.log(chalk.yellow('No local sessions found.'));
507
+ return;
508
+ }
509
+
510
+ console.log(chalk.bold('\nAvailable local sessions:\n'));
511
+
512
+ sessions.forEach(session => {
513
+ const isCurrent = session === currentSession;
514
+ const marker = isCurrent ? chalk.green('→ ') : ' ';
515
+ console.log(`${marker}${session}`);
516
+ });
517
+ } else {
518
+ const detection = await AgentDetector.detect();
519
+ if (!detection) {
520
+ console.log(chalk.yellow('No active AI agent session found in this directory.'));
521
+ return;
522
+ }
523
+ const { parser } = detection;
524
+ const sessions = await parser.getAllSessions();
525
+
526
+ if (sessions.length === 0) {
527
+ console.log(chalk.yellow('No active sessions found.'));
528
+ return;
529
+ }
530
+
531
+ console.log(chalk.bold('\nAvailable sessions:\n'));
532
+
533
+ const currentProjectDir = await parser.getCurrentProjectDir();
534
+ const currentProjectDirName = path.basename(currentProjectDir);
535
+
536
+ sessions.forEach(session => {
537
+ const isCurrent = session.rawProjectDir === currentProjectDirName;
538
+ const marker = isCurrent ? chalk.green('→ ') : ' ';
539
+ console.log(`${marker}${chalk.cyan(session.id)}`);
540
+ console.log(` Project: ${session.project}`);
541
+ });
542
+ }
543
+ } catch (error) {
544
+ console.error(chalk.red(`Error: ${error.message}`));
545
+ }
546
+ });
547
+
548
+ program
549
+ .command('session <id>')
550
+ .description(i18n.t('cmd.session.description'))
551
+ .action(async (sessionId) => {
552
+ try {
553
+ await SessionTracker.setCurrentSession(sessionId);
554
+ console.log(chalk.green(`Switched to session: ${sessionId}`));
555
+ } catch (error) {
556
+ console.error(chalk.red(`Error: ${error.message}`));
557
+ }
558
+ });
559
+
560
+ program
561
+ .command('language [lang]')
562
+ .description(i18n.t('cmd.language.description'))
563
+ .action(async (lang) => {
564
+ try {
565
+ if (!lang) {
566
+ // Show current language and available options
567
+ const current = i18n.getCurrentLanguage();
568
+ const available = i18n.getAvailableLanguages();
569
+
570
+ console.log(chalk.bold(`\\nCurrent language: ${chalk.cyan(current.name)} (${current.code})\\n`));
571
+ console.log(chalk.bold('Available languages:'));
572
+ available.forEach(({ code, name }) => {
573
+ const marker = code === current.code ? chalk.green('→ ') : ' ';
574
+ console.log(`${marker}${code} - ${name}`);
575
+ });
576
+ console.log(chalk.gray('\\nUsage: revert-ai language <code>'));
577
+ return;
578
+ }
579
+
580
+ await i18n.setLanguage(lang);
581
+ const newLang = i18n.getCurrentLanguage();
582
+ console.log(chalk.green(i18n.t('msg.language_set', { language: newLang.name })));
583
+ } catch (error) {
584
+ const available = i18n.getAvailableLanguages().map(l => l.code).join(', ');
585
+ console.error(chalk.red(i18n.t('msg.language_invalid', { languages: available })));
586
+ }
587
+ });
588
+
589
+ program.parse(process.argv);
package/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { SessionTracker } from './src/core/SessionTracker.js';
2
+ export { UndoManager } from './src/core/UndoManager.js';
3
+ export { Operation, OperationType } from './src/core/Operation.js';
4
+ export { formatDistance } from './src/utils/formatting.js';
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "revert-ai",
3
+ "version": "1.1.1",
4
+ "description": "Intelligent undo/redo for AI Coding Assistants (Claude Code, Aider, etc.) with cascading safety, visual diffs, and Git checkpoints",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "revert-ai": "./bin/revert-ai.js"
8
+ },
9
+ "scripts": {
10
+ "test": "NODE_OPTIONS=\"--experimental-vm-modules\" jest",
11
+ "test:watch": "NODE_OPTIONS=\"--experimental-vm-modules\" jest --watch",
12
+ "demo": "./demo-language.sh",
13
+ "build:binaries": "npx @yao-pkg/pkg bin/revert-ai.js --targets node18-macos-x64,node18-macos-arm64,node18-linux-x64,node18-win-x64 --out-path dist/",
14
+ "prepublishOnly": "npm test"
15
+ },
16
+ "keywords": [
17
+ "claude",
18
+ "claude-code",
19
+ "undo",
20
+ "redo",
21
+ "revert",
22
+ "cli",
23
+ "ai-development",
24
+ "session-management",
25
+ "file-operations",
26
+ "backup",
27
+ "preview",
28
+ "internationalization",
29
+ "japanese"
30
+ ],
31
+ "author": "Harsha Rajkumar",
32
+ "license": "MIT",
33
+ "type": "module",
34
+ "engines": {
35
+ "node": ">=16.0.0"
36
+ },
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/harsharajkumar-273/revert-ai.git"
40
+ },
41
+ "bugs": {
42
+ "url": "https://github.com/harsharajkumar-273/revert-ai/issues"
43
+ },
44
+ "homepage": "https://github.com/harsharajkumar-273/revert-ai#readme",
45
+ "files": [
46
+ "bin/",
47
+ "src/",
48
+ "README.md",
49
+ "LICENSE"
50
+ ],
51
+ "dependencies": {
52
+ "@babel/parser": "^8.0.4",
53
+ "chalk": "^5.3.0",
54
+ "commander": "^11.1.0",
55
+ "inquirer": "^9.2.12"
56
+ },
57
+ "devDependencies": {
58
+ "jest": "^29.7.0"
59
+ },
60
+ "pkg": {
61
+ "scripts": "src/**/*.js",
62
+ "assets": [
63
+ "README.md",
64
+ "LICENSE"
65
+ ],
66
+ "targets": [
67
+ "node18-macos-x64",
68
+ "node18-macos-arm64",
69
+ "node18-linux-x64",
70
+ "node18-win-x64"
71
+ ],
72
+ "outputPath": "dist"
73
+ }
74
+ }
@@ -0,0 +1,27 @@
1
+ import { ClaudeSessionParser } from './ClaudeSessionParser.js';
2
+ import { AiderSessionParser } from './AiderSessionParser.js';
3
+
4
+ export class AgentDetector {
5
+ /**
6
+ * Automatically detect which AI agent session is active in the current directory.
7
+ * Checks for Aider first, then Claude Code.
8
+ * @returns {Promise<{ agent: string, parser: import('./AgentParser.js').AgentParser } | null>}
9
+ */
10
+ static async detect() {
11
+ // 1. Check Aider first
12
+ const aider = new AiderSessionParser();
13
+ const hasAider = await aider.detect();
14
+ if (hasAider) {
15
+ return { agent: 'aider', parser: aider };
16
+ }
17
+
18
+ // 2. Check Claude Code
19
+ const claude = new ClaudeSessionParser();
20
+ const hasClaude = await claude.getCurrentSessionFile();
21
+ if (hasClaude) {
22
+ return { agent: 'claude', parser: claude };
23
+ }
24
+
25
+ return null;
26
+ }
27
+ }