@ai-devkit/agent-manager 0.13.0 → 0.15.0

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 (95) hide show
  1. package/.eslintrc.json +1 -1
  2. package/README.md +32 -0
  3. package/dist/AgentManager.d.ts +10 -12
  4. package/dist/AgentManager.d.ts.map +1 -1
  5. package/dist/AgentManager.js +6 -34
  6. package/dist/AgentManager.js.map +1 -1
  7. package/dist/adapters/AgentAdapter.js +2 -5
  8. package/dist/adapters/AgentAdapter.js.map +1 -1
  9. package/dist/adapters/ClaudeCodeAdapter.d.ts +1 -1
  10. package/dist/adapters/ClaudeCodeAdapter.d.ts.map +1 -1
  11. package/dist/adapters/ClaudeCodeAdapter.js +29 -63
  12. package/dist/adapters/ClaudeCodeAdapter.js.map +1 -1
  13. package/dist/adapters/CodexAdapter.d.ts +1 -1
  14. package/dist/adapters/CodexAdapter.d.ts.map +1 -1
  15. package/dist/adapters/CodexAdapter.js +32 -68
  16. package/dist/adapters/CodexAdapter.js.map +1 -1
  17. package/dist/adapters/GeminiCliAdapter.d.ts +1 -1
  18. package/dist/adapters/GeminiCliAdapter.d.ts.map +1 -1
  19. package/dist/adapters/GeminiCliAdapter.js +29 -65
  20. package/dist/adapters/GeminiCliAdapter.js.map +1 -1
  21. package/dist/adapters/OpenCodeAdapter.d.ts +1 -1
  22. package/dist/adapters/OpenCodeAdapter.d.ts.map +1 -1
  23. package/dist/adapters/OpenCodeAdapter.js +20 -59
  24. package/dist/adapters/OpenCodeAdapter.js.map +1 -1
  25. package/dist/adapters/index.d.ts +6 -6
  26. package/dist/adapters/index.d.ts.map +1 -1
  27. package/dist/adapters/index.js +5 -13
  28. package/dist/adapters/index.js.map +1 -1
  29. package/dist/index.d.ts +13 -11
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +9 -22
  32. package/dist/index.js.map +1 -1
  33. package/dist/terminal/TerminalFocusManager.js +13 -17
  34. package/dist/terminal/TerminalFocusManager.js.map +1 -1
  35. package/dist/terminal/TtyWriter.d.ts +1 -1
  36. package/dist/terminal/TtyWriter.d.ts.map +1 -1
  37. package/dist/terminal/TtyWriter.js +11 -15
  38. package/dist/terminal/TtyWriter.js.map +1 -1
  39. package/dist/terminal/index.d.ts +4 -4
  40. package/dist/terminal/index.d.ts.map +1 -1
  41. package/dist/terminal/index.js +3 -9
  42. package/dist/terminal/index.js.map +1 -1
  43. package/dist/utils/ClaudeSessionParser.d.ts +2 -2
  44. package/dist/utils/ClaudeSessionParser.d.ts.map +1 -1
  45. package/dist/utils/ClaudeSessionParser.js +11 -48
  46. package/dist/utils/ClaudeSessionParser.js.map +1 -1
  47. package/dist/utils/applescript.js +1 -4
  48. package/dist/utils/applescript.js.map +1 -1
  49. package/dist/utils/index.d.ts +6 -6
  50. package/dist/utils/index.d.ts.map +1 -1
  51. package/dist/utils/index.js +4 -15
  52. package/dist/utils/index.js.map +1 -1
  53. package/dist/utils/matching.d.ts +2 -2
  54. package/dist/utils/matching.d.ts.map +1 -1
  55. package/dist/utils/matching.js +3 -40
  56. package/dist/utils/matching.js.map +1 -1
  57. package/dist/utils/process.d.ts +1 -1
  58. package/dist/utils/process.d.ts.map +1 -1
  59. package/dist/utils/process.js +12 -52
  60. package/dist/utils/process.js.map +1 -1
  61. package/dist/utils/session.js +8 -49
  62. package/dist/utils/session.js.map +1 -1
  63. package/dist/utils/sortAgents.d.ts +4 -0
  64. package/dist/utils/sortAgents.d.ts.map +1 -0
  65. package/dist/utils/sortAgents.js +48 -0
  66. package/dist/utils/sortAgents.js.map +1 -0
  67. package/package.json +9 -8
  68. package/src/AgentManager.ts +13 -28
  69. package/src/__tests__/AgentManager.test.ts +5 -5
  70. package/src/__tests__/adapters/ClaudeCodeAdapter.test.ts +44 -42
  71. package/src/__tests__/adapters/CodexAdapter.test.ts +32 -30
  72. package/src/__tests__/adapters/GeminiCliAdapter.test.ts +19 -18
  73. package/src/__tests__/adapters/OpenCodeAdapter.test.ts +23 -21
  74. package/src/__tests__/terminal/TtyWriter.test.ts +10 -9
  75. package/src/__tests__/utils/ClaudeSessionParser.test.ts +2 -2
  76. package/src/__tests__/utils/matching.test.ts +4 -4
  77. package/src/__tests__/utils/process.test.ts +6 -5
  78. package/src/__tests__/utils/session.test.ts +8 -7
  79. package/src/adapters/ClaudeCodeAdapter.ts +8 -8
  80. package/src/adapters/CodexAdapter.ts +6 -6
  81. package/src/adapters/GeminiCliAdapter.ts +6 -6
  82. package/src/adapters/OpenCodeAdapter.ts +4 -4
  83. package/src/adapters/index.ts +6 -6
  84. package/src/index.ts +13 -11
  85. package/src/terminal/TerminalFocusManager.ts +2 -2
  86. package/src/terminal/TtyWriter.ts +3 -3
  87. package/src/terminal/index.ts +4 -4
  88. package/src/utils/ClaudeSessionParser.ts +2 -2
  89. package/src/utils/index.ts +6 -6
  90. package/src/utils/matching.ts +2 -2
  91. package/src/utils/process.ts +1 -1
  92. package/src/utils/sortAgents.ts +52 -0
  93. package/tsconfig.json +2 -2
  94. package/vitest.config.ts +20 -0
  95. package/jest.config.js +0 -21
@@ -0,0 +1,48 @@
1
+ import { AgentStatus } from '../adapters/AgentAdapter.js';
2
+ const STATUS_PRIORITY = {
3
+ [AgentStatus.WAITING]: 0,
4
+ [AgentStatus.RUNNING]: 1,
5
+ [AgentStatus.IDLE]: 2,
6
+ [AgentStatus.UNKNOWN]: 3,
7
+ };
8
+ function compareByName(a, b) {
9
+ return a.name.localeCompare(b.name);
10
+ }
11
+ function compareByStatus(a, b) {
12
+ const pa = STATUS_PRIORITY[a.status] ?? 999;
13
+ const pb = STATUS_PRIORITY[b.status] ?? 999;
14
+ if (pa !== pb)
15
+ return pa - pb;
16
+ return compareByName(a, b);
17
+ }
18
+ function compareByPid(a, b) {
19
+ if (a.pid !== b.pid)
20
+ return a.pid - b.pid;
21
+ return compareByName(a, b);
22
+ }
23
+ function compareByLastActive(a, b) {
24
+ const ta = a.lastActive instanceof Date ? a.lastActive.getTime() : new Date(a.lastActive).getTime();
25
+ const tb = b.lastActive instanceof Date ? b.lastActive.getTime() : new Date(b.lastActive).getTime();
26
+ if (tb !== ta)
27
+ return tb - ta; // most recent first
28
+ return compareByName(a, b);
29
+ }
30
+ export function sortAgents(agents, by) {
31
+ const copy = agents.slice();
32
+ switch (by) {
33
+ case 'name':
34
+ copy.sort(compareByName);
35
+ break;
36
+ case 'status':
37
+ copy.sort(compareByStatus);
38
+ break;
39
+ case 'pid':
40
+ copy.sort(compareByPid);
41
+ break;
42
+ case 'lastActive':
43
+ copy.sort(compareByLastActive);
44
+ break;
45
+ }
46
+ return copy;
47
+ }
48
+ //# sourceMappingURL=sortAgents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sortAgents.js","sourceRoot":"","sources":["../../src/utils/sortAgents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkB,MAAM,6BAA6B,CAAC;AAI1E,MAAM,eAAe,GAAgC;IACjD,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;IACxB,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;IACxB,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;IACrB,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;CAC3B,CAAC;AAEF,SAAS,aAAa,CAAC,CAAY,EAAE,CAAY;IAC7C,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACxC,CAAC;AAED,SAAS,eAAe,CAAC,CAAY,EAAE,CAAY;IAC/C,MAAM,EAAE,GAAG,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC;IAC5C,MAAM,EAAE,GAAG,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC;IAC5C,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,EAAE,GAAG,EAAE,CAAC;IAC9B,OAAO,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,YAAY,CAAC,CAAY,EAAE,CAAY;IAC5C,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG;QAAE,OAAO,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC;IAC1C,OAAO,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,mBAAmB,CAAC,CAAY,EAAE,CAAY;IACnD,MAAM,EAAE,GAAG,CAAC,CAAC,UAAU,YAAY,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC;IACpG,MAAM,EAAE,GAAG,CAAC,CAAC,UAAU,YAAY,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC;IACpG,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,oBAAoB;IACnD,OAAO,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAmB,EAAE,EAAgB;IAC5D,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC;IAC5B,QAAQ,EAAE,EAAE,CAAC;QACT,KAAK,MAAM;YACP,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACzB,MAAM;QACV,KAAK,QAAQ;YACT,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YAC3B,MAAM;QACV,KAAK,KAAK;YACN,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACxB,MAAM;QACV,KAAK,YAAY;YACb,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;YAC/B,MAAM;IACd,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC"}
package/package.json CHANGED
@@ -1,19 +1,21 @@
1
1
  {
2
2
  "name": "@ai-devkit/agent-manager",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
+ "type": "module",
4
5
  "description": "Standalone agent detection and management utilities for AI DevKit",
5
6
  "main": "dist/index.js",
6
7
  "types": "dist/index.d.ts",
7
8
  "exports": {
8
9
  ".": {
9
10
  "types": "./dist/index.d.ts",
10
- "default": "./dist/index.js"
11
+ "import": "./dist/index.js"
11
12
  }
12
13
  },
13
14
  "scripts": {
14
15
  "build": "tsc",
15
- "test": "jest",
16
- "test:coverage": "jest --coverage",
16
+ "test": "vitest run",
17
+ "test:watch": "vitest",
18
+ "test:coverage": "vitest run --coverage",
17
19
  "lint": "eslint src --ext .ts",
18
20
  "typecheck": "tsc --noEmit",
19
21
  "clean": "rm -rf dist"
@@ -31,14 +33,13 @@
31
33
  },
32
34
  "devDependencies": {
33
35
  "@types/better-sqlite3": "^7.6.11",
34
- "@types/jest": "^30.0.0",
35
36
  "@types/node": "^20.11.5",
36
37
  "@typescript-eslint/eslint-plugin": "^6.19.1",
37
38
  "@typescript-eslint/parser": "^6.19.1",
39
+ "@vitest/coverage-v8": "^2.1.0",
38
40
  "eslint": "^8.56.0",
39
- "jest": "^29.7.0",
40
- "ts-jest": "^29.4.5",
41
- "typescript": "^5.3.3"
41
+ "typescript": "^5.5.0",
42
+ "vitest": "^2.1.0"
42
43
  },
43
44
  "engines": {
44
45
  "node": ">=20.20.0"
@@ -10,8 +10,16 @@ import type {
10
10
  AgentInfo,
11
11
  SessionSummary,
12
12
  ListSessionsOptions,
13
- } from './adapters/AgentAdapter';
14
- import { AgentStatus } from './adapters/AgentAdapter';
13
+ } from './adapters/AgentAdapter.js';
14
+ import { sortAgents, type AgentSortKey } from './utils/sortAgents.js';
15
+
16
+ export interface ListAgentsOptions {
17
+ /**
18
+ * Sort key for the returned list. Defaults to `status`, which orders
19
+ * waiting → running → idle → unknown, then by name for stability.
20
+ */
21
+ sortBy?: AgentSortKey;
22
+ }
15
23
 
16
24
  /**
17
25
  * Agent Manager Class
@@ -108,7 +116,7 @@ export class AgentManager {
108
116
  * });
109
117
  * ```
110
118
  */
111
- async listAgents(): Promise<AgentInfo[]> {
119
+ async listAgents(options?: ListAgentsOptions): Promise<AgentInfo[]> {
112
120
  const allAgents: AgentInfo[] = [];
113
121
  const errors: Array<{ type: string; error: Error }> = [];
114
122
 
@@ -142,8 +150,8 @@ export class AgentManager {
142
150
  });
143
151
  }
144
152
 
145
- // Sort by status priority (waiting first, then running, then idle)
146
- return this.sortAgentsByStatus(allAgents);
153
+ const sortKey: AgentSortKey = options?.sortBy ?? 'status';
154
+ return sortAgents(allAgents, sortKey);
147
155
  }
148
156
 
149
157
  /**
@@ -193,29 +201,6 @@ export class AgentManager {
193
201
  return merged;
194
202
  }
195
203
 
196
- /**
197
- * Sort agents by status priority
198
- *
199
- * Priority order: waiting > running > idle > unknown
200
- * This ensures agents that need attention appear first.
201
- *
202
- * @param agents Array of agents to sort
203
- * @returns Sorted array of agents
204
- */
205
- private sortAgentsByStatus(agents: AgentInfo[]): AgentInfo[] {
206
- const statusPriority: Record<AgentStatus, number> = {
207
- [AgentStatus.WAITING]: 0,
208
- [AgentStatus.RUNNING]: 1,
209
- [AgentStatus.IDLE]: 2,
210
- [AgentStatus.UNKNOWN]: 3,
211
- };
212
-
213
- return agents.sort((a, b) => {
214
- const priorityA = statusPriority[a.status] ?? 999;
215
- const priorityB = statusPriority[b.status] ?? 999;
216
- return priorityA - priorityB;
217
- });
218
- }
219
204
 
220
205
  /**
221
206
  * Get count of registered adapters
@@ -2,16 +2,16 @@
2
2
  * Tests for AgentManager
3
3
  */
4
4
 
5
- import { describe, it, expect, beforeEach } from '@jest/globals';
6
- import { AgentManager } from '../AgentManager';
5
+
6
+ import { AgentManager } from '../AgentManager.js';
7
7
  import type {
8
8
  AgentAdapter,
9
9
  AgentInfo,
10
10
  AgentType,
11
11
  ConversationMessage,
12
12
  SessionSummary,
13
- } from '../adapters/AgentAdapter';
14
- import { AgentStatus } from '../adapters/AgentAdapter';
13
+ } from '../adapters/AgentAdapter.js';
14
+ import { AgentStatus } from '../adapters/AgentAdapter.js';
15
15
 
16
16
  // Mock adapter for testing
17
17
  class MockAdapter implements AgentAdapter {
@@ -333,7 +333,7 @@ describe('AgentManager', () => {
333
333
  new MockAdapter('codex', [], false, [], true /* failListSessions */),
334
334
  );
335
335
 
336
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => undefined);
336
+ const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
337
337
 
338
338
  try {
339
339
  const result = await manager.listSessions();
@@ -2,40 +2,42 @@
2
2
  * Tests for ClaudeCodeAdapter
3
3
  */
4
4
 
5
+ import type { MockedFunction } from 'vitest';
5
6
  import * as fs from 'fs';
6
7
  import * as path from 'path';
7
- import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals';
8
- import { ClaudeCodeAdapter } from '../../adapters/ClaudeCodeAdapter';
9
- import type { ProcessInfo } from '../../adapters/AgentAdapter';
10
- import { AgentStatus } from '../../adapters/AgentAdapter';
11
- import { listAgentProcesses, enrichProcesses } from '../../utils/process';
12
- import { batchGetSessionFileBirthtimes } from '../../utils/session';
13
- import type { SessionFile } from '../../utils/session';
14
- import { matchProcessesToSessions, generateAgentName } from '../../utils/matching';
15
- import type { MatchResult } from '../../utils/matching';
16
- jest.mock('../../utils/process', () => ({
17
- listAgentProcesses: jest.fn(),
18
- enrichProcesses: jest.fn(),
8
+
9
+ import { ClaudeCodeAdapter } from '../../adapters/ClaudeCodeAdapter.js';
10
+ import type { ProcessInfo } from '../../adapters/AgentAdapter.js';
11
+ import { AgentStatus } from '../../adapters/AgentAdapter.js';
12
+ import { listAgentProcesses, enrichProcesses } from '../../utils/process.js';
13
+ import { batchGetSessionFileBirthtimes } from '../../utils/session.js';
14
+ import type { SessionFile } from '../../utils/session.js';
15
+ import { matchProcessesToSessions, generateAgentName } from '../../utils/matching.js';
16
+ import type { MatchResult } from '../../utils/matching.js';
17
+ import * as os from 'os';
18
+ vi.mock('../../utils/process.js', () => ({
19
+ listAgentProcesses: vi.fn(),
20
+ enrichProcesses: vi.fn(),
19
21
  }));
20
22
 
21
- jest.mock('../../utils/session', () => {
22
- const actual = jest.requireActual('../../utils/session') as typeof import('../../utils/session');
23
+ vi.mock('../../utils/session.js', async () => {
24
+ const actual = await vi.importActual('../../utils/session') as typeof import('../../utils/session');
23
25
  return {
24
26
  ...actual,
25
- batchGetSessionFileBirthtimes: jest.fn(),
27
+ batchGetSessionFileBirthtimes: vi.fn(),
26
28
  };
27
29
  });
28
30
 
29
- jest.mock('../../utils/matching', () => ({
30
- matchProcessesToSessions: jest.fn(),
31
- generateAgentName: jest.fn(),
31
+ vi.mock('../../utils/matching.js', () => ({
32
+ matchProcessesToSessions: vi.fn(),
33
+ generateAgentName: vi.fn(),
32
34
  }));
33
35
 
34
- const mockedListAgentProcesses = listAgentProcesses as jest.MockedFunction<typeof listAgentProcesses>;
35
- const mockedEnrichProcesses = enrichProcesses as jest.MockedFunction<typeof enrichProcesses>;
36
- const mockedBatchGetSessionFileBirthtimes = batchGetSessionFileBirthtimes as jest.MockedFunction<typeof batchGetSessionFileBirthtimes>;
37
- const mockedMatchProcessesToSessions = matchProcessesToSessions as jest.MockedFunction<typeof matchProcessesToSessions>;
38
- const mockedGenerateAgentName = generateAgentName as jest.MockedFunction<typeof generateAgentName>;
36
+ const mockedListAgentProcesses = listAgentProcesses as MockedFunction<typeof listAgentProcesses>;
37
+ const mockedEnrichProcesses = enrichProcesses as MockedFunction<typeof enrichProcesses>;
38
+ const mockedBatchGetSessionFileBirthtimes = batchGetSessionFileBirthtimes as MockedFunction<typeof batchGetSessionFileBirthtimes>;
39
+ const mockedMatchProcessesToSessions = matchProcessesToSessions as MockedFunction<typeof matchProcessesToSessions>;
40
+ const mockedGenerateAgentName = generateAgentName as MockedFunction<typeof generateAgentName>;
39
41
  describe('ClaudeCodeAdapter', () => {
40
42
  let adapter: ClaudeCodeAdapter;
41
43
 
@@ -163,7 +165,7 @@ describe('ClaudeCodeAdapter', () => {
163
165
  mockedEnrichProcesses.mockReturnValue(processes);
164
166
 
165
167
  // Set up projects dir with encoded directory name
166
- const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'claude-test-'));
168
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-test-'));
167
169
  const projectsDir = path.join(tmpDir, 'projects');
168
170
  // Claude encodes /Users/test/my-project → -Users-test-my-project
169
171
  const projDir = path.join(projectsDir, '-Users-test-my-project');
@@ -222,7 +224,7 @@ describe('ClaudeCodeAdapter', () => {
222
224
  mockedEnrichProcesses.mockReturnValue(processes);
223
225
 
224
226
  // Set up projects dir with encoded directory names
225
- const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'claude-test-'));
227
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-test-'));
226
228
  const projectsDir = path.join(tmpDir, 'projects');
227
229
  // /project-a → -project-a, /project-b → -project-b
228
230
  const projDirA = path.join(projectsDir, '-project-a');
@@ -303,7 +305,7 @@ describe('ClaudeCodeAdapter', () => {
303
305
  mockedListAgentProcesses.mockReturnValue(processes);
304
306
  mockedEnrichProcesses.mockReturnValue(processes);
305
307
 
306
- const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'claude-resume-'));
308
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-resume-'));
307
309
  const projectsDir = path.join(tmpDir, 'projects');
308
310
  const projDir = path.join(projectsDir, '-project-resumed');
309
311
  fs.mkdirSync(projDir, { recursive: true });
@@ -367,7 +369,7 @@ describe('ClaudeCodeAdapter', () => {
367
369
  mockedListAgentProcesses.mockReturnValue(processes);
368
370
  mockedEnrichProcesses.mockReturnValue(processes);
369
371
 
370
- const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'claude-pid-test-'));
372
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-pid-test-'));
371
373
  const sessionsDir = path.join(tmpDir, 'sessions');
372
374
  const projectsDir = path.join(tmpDir, 'projects');
373
375
  const projDir = path.join(projectsDir, '-project-direct');
@@ -416,7 +418,7 @@ describe('ClaudeCodeAdapter', () => {
416
418
  mockedListAgentProcesses.mockReturnValue(processes);
417
419
  mockedEnrichProcesses.mockReturnValue(processes);
418
420
 
419
- const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'claude-pid-wait-'));
421
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-pid-wait-'));
420
422
  const sessionsDir = path.join(tmpDir, 'sessions');
421
423
  const projectsDir = path.join(tmpDir, 'projects');
422
424
  const projDir = path.join(projectsDir, '-project-wait');
@@ -463,7 +465,7 @@ describe('ClaudeCodeAdapter', () => {
463
465
  mockedListAgentProcesses.mockReturnValue(processes);
464
466
  mockedEnrichProcesses.mockReturnValue(processes);
465
467
 
466
- const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'claude-pid-idle-'));
468
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-pid-idle-'));
467
469
  const sessionsDir = path.join(tmpDir, 'sessions');
468
470
  const projectsDir = path.join(tmpDir, 'projects');
469
471
  const projDir = path.join(projectsDir, '-project-idle');
@@ -508,7 +510,7 @@ describe('ClaudeCodeAdapter', () => {
508
510
  mockedListAgentProcesses.mockReturnValue(processes);
509
511
  mockedEnrichProcesses.mockReturnValue(processes);
510
512
 
511
- const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'claude-pid-nostat-'));
513
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-pid-nostat-'));
512
514
  const sessionsDir = path.join(tmpDir, 'sessions');
513
515
  const projectsDir = path.join(tmpDir, 'projects');
514
516
  const projDir = path.join(projectsDir, '-project-legacy');
@@ -560,7 +562,7 @@ describe('ClaudeCodeAdapter', () => {
560
562
  mockedListAgentProcesses.mockReturnValue(processes);
561
563
  mockedEnrichProcesses.mockReturnValue(processes);
562
564
 
563
- const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'claude-resume-wait-'));
565
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-resume-wait-'));
564
566
  const sessionsDir = path.join(tmpDir, 'sessions');
565
567
  const projectsDir = path.join(tmpDir, 'projects');
566
568
  const projDir = path.join(projectsDir, '-project-resume-wait');
@@ -604,7 +606,7 @@ describe('ClaudeCodeAdapter', () => {
604
606
  mockedListAgentProcesses.mockReturnValue(processes);
605
607
  mockedEnrichProcesses.mockReturnValue(processes);
606
608
 
607
- const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'claude-gone-'));
609
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-gone-'));
608
610
  const sessionsDir = path.join(tmpDir, 'sessions');
609
611
  const projectsDir = path.join(tmpDir, 'projects');
610
612
  const projDir = path.join(projectsDir, '-project-gone');
@@ -623,7 +625,7 @@ describe('ClaudeCodeAdapter', () => {
623
625
  (adapter as any).projectsDir = projectsDir;
624
626
 
625
627
  // Simulate JSONL disappearing between existence check and read
626
- jest.spyOn((adapter as any).parser, 'readSession').mockReturnValueOnce(null);
628
+ vi.spyOn((adapter as any).parser, 'readSession').mockReturnValueOnce(null);
627
629
 
628
630
  const agents = await adapter.detectAgents();
629
631
 
@@ -633,7 +635,7 @@ describe('ClaudeCodeAdapter', () => {
633
635
  expect(agents[0].status).toBe(AgentStatus.IDLE);
634
636
 
635
637
  fs.rmSync(tmpDir, { recursive: true, force: true });
636
- jest.restoreAllMocks();
638
+ vi.restoreAllMocks();
637
639
  });
638
640
 
639
641
  it('should fall back to process-only when legacy-matched JSONL becomes unreadable', async () => {
@@ -644,7 +646,7 @@ describe('ClaudeCodeAdapter', () => {
644
646
  mockedListAgentProcesses.mockReturnValue(processes);
645
647
  mockedEnrichProcesses.mockReturnValue(processes);
646
648
 
647
- const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'claude-lgone-'));
649
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-lgone-'));
648
650
  const projectsDir = path.join(tmpDir, 'projects');
649
651
  const projDir = path.join(projectsDir, '-project-legacy-gone');
650
652
  fs.mkdirSync(projDir, { recursive: true });
@@ -670,7 +672,7 @@ describe('ClaudeCodeAdapter', () => {
670
672
  ]);
671
673
 
672
674
  // Simulate JSONL disappearing between match and read
673
- jest.spyOn((adapter as any).parser, 'readSession').mockReturnValueOnce(null);
675
+ vi.spyOn((adapter as any).parser, 'readSession').mockReturnValueOnce(null);
674
676
 
675
677
  const agents = await adapter.detectAgents();
676
678
 
@@ -679,7 +681,7 @@ describe('ClaudeCodeAdapter', () => {
679
681
  expect(agents[0].status).toBe(AgentStatus.IDLE);
680
682
 
681
683
  fs.rmSync(tmpDir, { recursive: true, force: true });
682
- jest.restoreAllMocks();
684
+ vi.restoreAllMocks();
683
685
  });
684
686
 
685
687
  it('should mix direct PID-file matches and legacy matches across processes', async () => {
@@ -691,7 +693,7 @@ describe('ClaudeCodeAdapter', () => {
691
693
  mockedListAgentProcesses.mockReturnValue(processes);
692
694
  mockedEnrichProcesses.mockReturnValue(processes);
693
695
 
694
- const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'claude-mix-test-'));
696
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-mix-test-'));
695
697
  const sessionsDir = path.join(tmpDir, 'sessions');
696
698
  const projectsDir = path.join(tmpDir, 'projects');
697
699
  const projAlpha = path.join(projectsDir, '-project-alpha');
@@ -756,7 +758,7 @@ describe('ClaudeCodeAdapter', () => {
756
758
  let tmpDir: string;
757
759
 
758
760
  beforeEach(() => {
759
- tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'claude-test-'));
761
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-test-'));
760
762
  });
761
763
 
762
764
  afterEach(() => {
@@ -1071,7 +1073,7 @@ describe('ClaudeCodeAdapter', () => {
1071
1073
  let tmpDir: string;
1072
1074
 
1073
1075
  beforeEach(() => {
1074
- tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'claude-test-'));
1076
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-test-'));
1075
1077
  });
1076
1078
 
1077
1079
  afterEach(() => {
@@ -1419,7 +1421,7 @@ describe('ClaudeCodeAdapter', () => {
1419
1421
  let tmpDir: string;
1420
1422
 
1421
1423
  beforeEach(() => {
1422
- tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'claude-conv-'));
1424
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-conv-'));
1423
1425
  });
1424
1426
 
1425
1427
  afterEach(() => {
@@ -1594,7 +1596,7 @@ describe('ClaudeCodeAdapter', () => {
1594
1596
  let projectsDir: string;
1595
1597
 
1596
1598
  beforeEach(() => {
1597
- tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'claude-list-'));
1599
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claude-list-'));
1598
1600
  projectsDir = path.join(tmpDir, 'projects');
1599
1601
  fs.mkdirSync(projectsDir, { recursive: true });
1600
1602
  (adapter as any).projectsDir = projectsDir;
@@ -2,41 +2,43 @@
2
2
  * Tests for CodexAdapter
3
3
  */
4
4
 
5
+ import type { MockedFunction } from 'vitest';
5
6
  import * as fs from 'fs';
6
7
  import * as path from 'path';
7
- import { beforeEach, afterEach, describe, expect, it, jest } from '@jest/globals';
8
- import { CodexAdapter } from '../../adapters/CodexAdapter';
9
- import type { ProcessInfo } from '../../adapters/AgentAdapter';
10
- import { AgentStatus } from '../../adapters/AgentAdapter';
11
- import { listAgentProcesses, enrichProcesses } from '../../utils/process';
12
- import { batchGetSessionFileBirthtimes } from '../../utils/session';
13
- import type { SessionFile } from '../../utils/session';
14
- import { matchProcessesToSessions, generateAgentName } from '../../utils/matching';
15
- import type { MatchResult } from '../../utils/matching';
16
-
17
- jest.mock('../../utils/process', () => ({
18
- listAgentProcesses: jest.fn(),
19
- enrichProcesses: jest.fn(),
8
+
9
+ import { CodexAdapter } from '../../adapters/CodexAdapter.js';
10
+ import type { ProcessInfo } from '../../adapters/AgentAdapter.js';
11
+ import { AgentStatus } from '../../adapters/AgentAdapter.js';
12
+ import { listAgentProcesses, enrichProcesses } from '../../utils/process.js';
13
+ import { batchGetSessionFileBirthtimes } from '../../utils/session.js';
14
+ import type { SessionFile } from '../../utils/session.js';
15
+ import { matchProcessesToSessions, generateAgentName } from '../../utils/matching.js';
16
+ import type { MatchResult } from '../../utils/matching.js';
17
+ import * as os from 'os';
18
+
19
+ vi.mock('../../utils/process.js', () => ({
20
+ listAgentProcesses: vi.fn(),
21
+ enrichProcesses: vi.fn(),
20
22
  }));
21
23
 
22
- jest.mock('../../utils/session', () => {
23
- const actual = jest.requireActual('../../utils/session') as typeof import('../../utils/session');
24
+ vi.mock('../../utils/session.js', async () => {
25
+ const actual = await vi.importActual('../../utils/session') as typeof import('../../utils/session');
24
26
  return {
25
27
  ...actual,
26
- batchGetSessionFileBirthtimes: jest.fn(),
28
+ batchGetSessionFileBirthtimes: vi.fn(),
27
29
  };
28
30
  });
29
31
 
30
- jest.mock('../../utils/matching', () => ({
31
- matchProcessesToSessions: jest.fn(),
32
- generateAgentName: jest.fn(),
32
+ vi.mock('../../utils/matching.js', () => ({
33
+ matchProcessesToSessions: vi.fn(),
34
+ generateAgentName: vi.fn(),
33
35
  }));
34
36
 
35
- const mockedListAgentProcesses = listAgentProcesses as jest.MockedFunction<typeof listAgentProcesses>;
36
- const mockedEnrichProcesses = enrichProcesses as jest.MockedFunction<typeof enrichProcesses>;
37
- const mockedBatchGetSessionFileBirthtimes = batchGetSessionFileBirthtimes as jest.MockedFunction<typeof batchGetSessionFileBirthtimes>;
38
- const mockedMatchProcessesToSessions = matchProcessesToSessions as jest.MockedFunction<typeof matchProcessesToSessions>;
39
- const mockedGenerateAgentName = generateAgentName as jest.MockedFunction<typeof generateAgentName>;
37
+ const mockedListAgentProcesses = listAgentProcesses as MockedFunction<typeof listAgentProcesses>;
38
+ const mockedEnrichProcesses = enrichProcesses as MockedFunction<typeof enrichProcesses>;
39
+ const mockedBatchGetSessionFileBirthtimes = batchGetSessionFileBirthtimes as MockedFunction<typeof batchGetSessionFileBirthtimes>;
40
+ const mockedMatchProcessesToSessions = matchProcessesToSessions as MockedFunction<typeof matchProcessesToSessions>;
41
+ const mockedGenerateAgentName = generateAgentName as MockedFunction<typeof generateAgentName>;
40
42
 
41
43
  describe('CodexAdapter', () => {
42
44
  let adapter: CodexAdapter;
@@ -136,7 +138,7 @@ describe('CodexAdapter', () => {
136
138
  mockedEnrichProcesses.mockReturnValue(processes);
137
139
 
138
140
  // Set up sessions dir with date directory
139
- const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'codex-test-'));
141
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-test-'));
140
142
  const sessionsDir = path.join(tmpDir, 'sessions');
141
143
  const dateDir = path.join(sessionsDir, '2026', '03', '18');
142
144
  fs.mkdirSync(dateDir, { recursive: true });
@@ -195,7 +197,7 @@ describe('CodexAdapter', () => {
195
197
  mockedListAgentProcesses.mockReturnValue(processes);
196
198
  mockedEnrichProcesses.mockReturnValue(processes);
197
199
 
198
- const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'codex-test-'));
200
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-test-'));
199
201
  const sessionsDir = path.join(tmpDir, 'sessions');
200
202
  const now = new Date();
201
203
  const dateDir = path.join(
@@ -251,7 +253,7 @@ describe('CodexAdapter', () => {
251
253
  let tmpDir: string;
252
254
 
253
255
  beforeEach(() => {
254
- tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'codex-test-'));
256
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-test-'));
255
257
  });
256
258
 
257
259
  afterEach(() => {
@@ -399,7 +401,7 @@ describe('CodexAdapter', () => {
399
401
  let tmpDir: string;
400
402
 
401
403
  beforeEach(() => {
402
- tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'codex-test-'));
404
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-test-'));
403
405
  });
404
406
 
405
407
  afterEach(() => {
@@ -524,7 +526,7 @@ describe('CodexAdapter', () => {
524
526
  let tmpDir: string;
525
527
 
526
528
  beforeEach(() => {
527
- tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'codex-conv-'));
529
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-conv-'));
528
530
  });
529
531
 
530
532
  afterEach(() => {
@@ -640,7 +642,7 @@ describe('CodexAdapter', () => {
640
642
  let sessionsDir: string;
641
643
 
642
644
  beforeEach(() => {
643
- tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'codex-list-'));
645
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-list-'));
644
646
  sessionsDir = path.join(tmpDir, 'sessions');
645
647
  fs.mkdirSync(sessionsDir, { recursive: true });
646
648
  (adapter as any).codexSessionsDir = sessionsDir;
@@ -2,30 +2,32 @@
2
2
  * Tests for GeminiCliAdapter
3
3
  */
4
4
 
5
+ import type { MockedFunction } from 'vitest';
5
6
  import * as fs from 'fs';
6
7
  import * as os from 'os';
7
8
  import * as path from 'path';
8
- import { beforeEach, afterEach, describe, expect, it, jest } from '@jest/globals';
9
- import { GeminiCliAdapter } from '../../adapters/GeminiCliAdapter';
10
- import type { ProcessInfo } from '../../adapters/AgentAdapter';
11
- import { AgentStatus } from '../../adapters/AgentAdapter';
12
- import { listAgentProcesses, enrichProcesses } from '../../utils/process';
13
- import { matchProcessesToSessions, generateAgentName } from '../../utils/matching';
14
-
15
- jest.mock('../../utils/process', () => ({
16
- listAgentProcesses: jest.fn(),
17
- enrichProcesses: jest.fn(),
9
+
10
+ import { GeminiCliAdapter } from '../../adapters/GeminiCliAdapter.js';
11
+ import type { ProcessInfo } from '../../adapters/AgentAdapter.js';
12
+ import { AgentStatus } from '../../adapters/AgentAdapter.js';
13
+ import { listAgentProcesses, enrichProcesses } from '../../utils/process.js';
14
+ import { matchProcessesToSessions, generateAgentName } from '../../utils/matching.js';
15
+ import * as crypto from 'crypto';
16
+
17
+ vi.mock('../../utils/process.js', () => ({
18
+ listAgentProcesses: vi.fn(),
19
+ enrichProcesses: vi.fn(),
18
20
  }));
19
21
 
20
- jest.mock('../../utils/matching', () => ({
21
- matchProcessesToSessions: jest.fn(),
22
- generateAgentName: jest.fn(),
22
+ vi.mock('../../utils/matching.js', () => ({
23
+ matchProcessesToSessions: vi.fn(),
24
+ generateAgentName: vi.fn(),
23
25
  }));
24
26
 
25
- const mockedListAgentProcesses = listAgentProcesses as jest.MockedFunction<typeof listAgentProcesses>;
26
- const mockedEnrichProcesses = enrichProcesses as jest.MockedFunction<typeof enrichProcesses>;
27
- const mockedMatchProcessesToSessions = matchProcessesToSessions as jest.MockedFunction<typeof matchProcessesToSessions>;
28
- const mockedGenerateAgentName = generateAgentName as jest.MockedFunction<typeof generateAgentName>;
27
+ const mockedListAgentProcesses = listAgentProcesses as MockedFunction<typeof listAgentProcesses>;
28
+ const mockedEnrichProcesses = enrichProcesses as MockedFunction<typeof enrichProcesses>;
29
+ const mockedMatchProcessesToSessions = matchProcessesToSessions as MockedFunction<typeof matchProcessesToSessions>;
30
+ const mockedGenerateAgentName = generateAgentName as MockedFunction<typeof generateAgentName>;
29
31
 
30
32
  describe('GeminiCliAdapter', () => {
31
33
  let adapter: GeminiCliAdapter;
@@ -918,6 +920,5 @@ function writeSession(
918
920
  */
919
921
  function hashProjectRoot(projectRoot: string): string {
920
922
  // eslint-disable-next-line @typescript-eslint/no-require-imports
921
- const crypto = require('crypto');
922
923
  return crypto.createHash('sha256').update(projectRoot).digest('hex');
923
924
  }