@ai-devkit/agent-manager 0.9.0 → 0.10.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.
@@ -2,15 +2,14 @@
2
2
  * Process Detection Utilities
3
3
  *
4
4
  * Shared shell command wrappers for detecting and inspecting running processes.
5
- * All execSync calls for process data live here — adapters must not call execSync directly.
5
+ * All execFileSync calls for process data live here — adapters must not call execFileSync directly.
6
6
  */
7
7
  import type { ProcessInfo } from '../adapters/AgentAdapter';
8
8
  /**
9
9
  * List running processes matching an agent executable name.
10
10
  *
11
- * Uses `ps aux | grep <pattern>` at shell level for performance, then post-filters
12
- * by checking that the executable basename matches exactly (avoids matching
13
- * `claude-helper`, `vscode-claude-extension`, or the grep process itself).
11
+ * Uses `ps aux` then filters in JS for exact executable basename match.
12
+ * This avoids shell pipelines and string interpolation.
14
13
  *
15
14
  * Returned ProcessInfo has pid, command, tty populated.
16
15
  * cwd and startTime are NOT populated — call enrichProcesses() to fill them.
@@ -1 +1 @@
1
- {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/utils/process.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,EAAE,CAkDrE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAoCvE;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAkC3E;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAavE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAWjD"}
1
+ {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/utils/process.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,EAAE,CA6CrE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAuCvE;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAkC3E;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAavE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAYjD"}
@@ -3,7 +3,7 @@
3
3
  * Process Detection Utilities
4
4
  *
5
5
  * Shared shell command wrappers for detecting and inspecting running processes.
6
- * All execSync calls for process data live here — adapters must not call execSync directly.
6
+ * All execFileSync calls for process data live here — adapters must not call execFileSync directly.
7
7
  */
8
8
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
9
  if (k2 === undefined) k2 = k;
@@ -49,9 +49,8 @@ const child_process_1 = require("child_process");
49
49
  /**
50
50
  * List running processes matching an agent executable name.
51
51
  *
52
- * Uses `ps aux | grep <pattern>` at shell level for performance, then post-filters
53
- * by checking that the executable basename matches exactly (avoids matching
54
- * `claude-helper`, `vscode-claude-extension`, or the grep process itself).
52
+ * Uses `ps aux` then filters in JS for exact executable basename match.
53
+ * This avoids shell pipelines and string interpolation.
55
54
  *
56
55
  * Returned ProcessInfo has pid, command, tty populated.
57
56
  * cwd and startTime are NOT populated — call enrichProcesses() to fill them.
@@ -62,9 +61,8 @@ function listAgentProcesses(namePattern) {
62
61
  return [];
63
62
  }
64
63
  try {
65
- // Use [c]laude trick to avoid matching the grep process itself
66
- const escapedPattern = `[${namePattern[0]}]${namePattern.slice(1)}`;
67
- const output = (0, child_process_1.execSync)(`ps aux | grep -i '${escapedPattern}'`, { encoding: 'utf-8' });
64
+ const output = (0, child_process_1.execFileSync)('ps', ['aux'], { encoding: 'utf-8' });
65
+ const lowerPattern = namePattern.toLowerCase();
68
66
  const processes = [];
69
67
  for (const line of output.trim().split('\n')) {
70
68
  if (!line.trim())
@@ -77,10 +75,10 @@ function listAgentProcesses(namePattern) {
77
75
  continue;
78
76
  const tty = parts[6];
79
77
  const command = parts.slice(10).join(' ');
80
- // Post-filter: check that the executable basename matches exactly
78
+ // Check that the executable basename matches exactly
81
79
  const executable = command.trim().split(/\s+/)[0] || '';
82
80
  const base = path.basename(executable).toLowerCase();
83
- if (base !== namePattern.toLowerCase() && base !== `${namePattern.toLowerCase()}.exe`) {
81
+ if (base !== lowerPattern && base !== `${lowerPattern}.exe`) {
84
82
  continue;
85
83
  }
86
84
  const ttyShort = tty.startsWith('/dev/') ? tty.slice(5) : tty;
@@ -108,7 +106,7 @@ function batchGetProcessCwds(pids) {
108
106
  if (pids.length === 0)
109
107
  return result;
110
108
  try {
111
- const output = (0, child_process_1.execSync)(`lsof -a -d cwd -Fn -p ${pids.join(',')} 2>/dev/null`, { encoding: 'utf-8' });
109
+ const output = (0, child_process_1.execFileSync)('lsof', ['-a', '-d', 'cwd', '-Fn', '-p', pids.join(',')], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] });
112
110
  // lsof output format: p{PID}\nn{path}\np{PID}\nn{path}...
113
111
  let currentPid = null;
114
112
  for (const line of output.trim().split('\n')) {
@@ -125,7 +123,7 @@ function batchGetProcessCwds(pids) {
125
123
  // Try per-PID fallback with pwdx (Linux)
126
124
  for (const pid of pids) {
127
125
  try {
128
- const output = (0, child_process_1.execSync)(`pwdx ${pid} 2>/dev/null`, { encoding: 'utf-8' });
126
+ const output = (0, child_process_1.execFileSync)('pwdx', [String(pid)], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] });
129
127
  const match = output.match(/^\d+:\s*(.+)$/);
130
128
  if (match) {
131
129
  result.set(pid, match[1].trim());
@@ -150,7 +148,7 @@ function batchGetProcessStartTimes(pids) {
150
148
  if (pids.length === 0)
151
149
  return result;
152
150
  try {
153
- const output = (0, child_process_1.execSync)(`ps -o pid=,lstart= -p ${pids.join(',')}`, { encoding: 'utf-8' });
151
+ const output = (0, child_process_1.execFileSync)('ps', ['-o', 'pid=,lstart=', '-p', pids.join(',')], { encoding: 'utf-8' });
154
152
  for (const rawLine of output.split('\n')) {
155
153
  const line = rawLine.trim();
156
154
  if (!line)
@@ -199,9 +197,7 @@ function enrichProcesses(processes) {
199
197
  */
200
198
  function getProcessTty(pid) {
201
199
  try {
202
- const output = (0, child_process_1.execSync)(`ps -p ${pid} -o tty=`, {
203
- encoding: 'utf-8',
204
- });
200
+ const output = (0, child_process_1.execFileSync)('ps', ['-p', String(pid), '-o', 'tty='], { encoding: 'utf-8' });
205
201
  const tty = output.trim();
206
202
  return tty.startsWith('/dev/') ? tty.slice(5) : tty;
207
203
  }
@@ -1 +1 @@
1
- {"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/utils/process.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/utils/process.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-devkit/agent-manager",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Standalone agent detection and management utilities for AI DevKit",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -66,9 +66,19 @@ export class AgentManager {
66
66
  return Array.from(this.adapters.values());
67
67
  }
68
68
 
69
+ /**
70
+ * Get a registered adapter by type
71
+ *
72
+ * @param type Agent type to look up
73
+ * @returns The adapter, or undefined if not registered
74
+ */
75
+ getAdapter(type: string): AgentAdapter | undefined {
76
+ return this.adapters.get(type);
77
+ }
78
+
69
79
  /**
70
80
  * Check if an adapter is registered for a specific type
71
- *
81
+ *
72
82
  * @param type Agent type to check
73
83
  * @returns True if adapter is registered
74
84
  */
@@ -361,7 +361,7 @@ describe('ClaudeCodeAdapter', () => {
361
361
  (adapter as any).projectsDir = projectsDir;
362
362
 
363
363
  // Simulate JSONL disappearing between existence check and read
364
- jest.spyOn(adapter as any, 'readSession').mockReturnValueOnce(null);
364
+ jest.spyOn((adapter as any).parser, 'readSession').mockReturnValueOnce(null);
365
365
 
366
366
  const agents = await adapter.detectAgents();
367
367
 
@@ -408,7 +408,7 @@ describe('ClaudeCodeAdapter', () => {
408
408
  ]);
409
409
 
410
410
  // Simulate JSONL disappearing between match and read
411
- jest.spyOn(adapter as any, 'readSession').mockReturnValueOnce(null);
411
+ jest.spyOn((adapter as any).parser, 'readSession').mockReturnValueOnce(null);
412
412
 
413
413
  const agents = await adapter.detectAgents();
414
414
 
@@ -601,7 +601,7 @@ describe('ClaudeCodeAdapter', () => {
601
601
  describe('helper methods', () => {
602
602
  describe('determineStatus', () => {
603
603
  it('should return "unknown" for sessions with no last entry type', () => {
604
- const determineStatus = (adapter as any).determineStatus.bind(adapter);
604
+ const determineStatus = (adapter as any).parser.determineStatus.bind((adapter as any).parser);
605
605
 
606
606
  const session = {
607
607
  sessionId: 'test',
@@ -615,7 +615,7 @@ describe('ClaudeCodeAdapter', () => {
615
615
  });
616
616
 
617
617
  it('should return "waiting" for assistant entries', () => {
618
- const determineStatus = (adapter as any).determineStatus.bind(adapter);
618
+ const determineStatus = (adapter as any).parser.determineStatus.bind((adapter as any).parser);
619
619
 
620
620
  const session = {
621
621
  sessionId: 'test',
@@ -630,7 +630,7 @@ describe('ClaudeCodeAdapter', () => {
630
630
  });
631
631
 
632
632
  it('should return "waiting" for user interruption', () => {
633
- const determineStatus = (adapter as any).determineStatus.bind(adapter);
633
+ const determineStatus = (adapter as any).parser.determineStatus.bind((adapter as any).parser);
634
634
 
635
635
  const session = {
636
636
  sessionId: 'test',
@@ -645,7 +645,7 @@ describe('ClaudeCodeAdapter', () => {
645
645
  });
646
646
 
647
647
  it('should return "running" for user/progress entries', () => {
648
- const determineStatus = (adapter as any).determineStatus.bind(adapter);
648
+ const determineStatus = (adapter as any).parser.determineStatus.bind((adapter as any).parser);
649
649
 
650
650
  const session = {
651
651
  sessionId: 'test',
@@ -660,7 +660,7 @@ describe('ClaudeCodeAdapter', () => {
660
660
  });
661
661
 
662
662
  it('should not override status based on age (process is running)', () => {
663
- const determineStatus = (adapter as any).determineStatus.bind(adapter);
663
+ const determineStatus = (adapter as any).parser.determineStatus.bind((adapter as any).parser);
664
664
 
665
665
  const oldDate = new Date(Date.now() - 10 * 60 * 1000);
666
666
  const session = {
@@ -676,7 +676,7 @@ describe('ClaudeCodeAdapter', () => {
676
676
  });
677
677
 
678
678
  it('should return "idle" for system entries', () => {
679
- const determineStatus = (adapter as any).determineStatus.bind(adapter);
679
+ const determineStatus = (adapter as any).parser.determineStatus.bind((adapter as any).parser);
680
680
 
681
681
  const session = {
682
682
  sessionId: 'test',
@@ -691,7 +691,7 @@ describe('ClaudeCodeAdapter', () => {
691
691
  });
692
692
 
693
693
  it('should return "running" for thinking entries', () => {
694
- const determineStatus = (adapter as any).determineStatus.bind(adapter);
694
+ const determineStatus = (adapter as any).parser.determineStatus.bind((adapter as any).parser);
695
695
 
696
696
  const session = {
697
697
  sessionId: 'test',
@@ -706,7 +706,7 @@ describe('ClaudeCodeAdapter', () => {
706
706
  });
707
707
 
708
708
  it('should return "running" for progress entries', () => {
709
- const determineStatus = (adapter as any).determineStatus.bind(adapter);
709
+ const determineStatus = (adapter as any).parser.determineStatus.bind((adapter as any).parser);
710
710
 
711
711
  const session = {
712
712
  sessionId: 'test',
@@ -721,7 +721,7 @@ describe('ClaudeCodeAdapter', () => {
721
721
  });
722
722
 
723
723
  it('should return "unknown" for unrecognized entry types', () => {
724
- const determineStatus = (adapter as any).determineStatus.bind(adapter);
724
+ const determineStatus = (adapter as any).parser.determineStatus.bind((adapter as any).parser);
725
725
 
726
726
  const session = {
727
727
  sessionId: 'test',
@@ -738,12 +738,12 @@ describe('ClaudeCodeAdapter', () => {
738
738
 
739
739
  describe('extractUserMessageText', () => {
740
740
  it('should extract plain string content', () => {
741
- const extract = (adapter as any).extractUserMessageText.bind(adapter);
741
+ const extract = (adapter as any).parser['extractUserMessageText'].bind((adapter as any).parser);
742
742
  expect(extract('hello world')).toBe('hello world');
743
743
  });
744
744
 
745
745
  it('should extract text from array content blocks', () => {
746
- const extract = (adapter as any).extractUserMessageText.bind(adapter);
746
+ const extract = (adapter as any).parser['extractUserMessageText'].bind((adapter as any).parser);
747
747
 
748
748
  const content = [
749
749
  { type: 'tool_result', content: 'some result' },
@@ -753,7 +753,7 @@ describe('ClaudeCodeAdapter', () => {
753
753
  });
754
754
 
755
755
  it('should return undefined for empty/null content', () => {
756
- const extract = (adapter as any).extractUserMessageText.bind(adapter);
756
+ const extract = (adapter as any).parser['extractUserMessageText'].bind((adapter as any).parser);
757
757
 
758
758
  expect(extract(undefined)).toBeUndefined();
759
759
  expect(extract('')).toBeUndefined();
@@ -761,35 +761,35 @@ describe('ClaudeCodeAdapter', () => {
761
761
  });
762
762
 
763
763
  it('should parse command-message tags', () => {
764
- const extract = (adapter as any).extractUserMessageText.bind(adapter);
764
+ const extract = (adapter as any).parser['extractUserMessageText'].bind((adapter as any).parser);
765
765
 
766
766
  const msg = '<command-message><command-name>commit</command-name><command-args>fix bug</command-args></command-message>';
767
767
  expect(extract(msg)).toBe('commit fix bug');
768
768
  });
769
769
 
770
770
  it('should parse command-message without args', () => {
771
- const extract = (adapter as any).extractUserMessageText.bind(adapter);
771
+ const extract = (adapter as any).parser['extractUserMessageText'].bind((adapter as any).parser);
772
772
 
773
773
  const msg = '<command-message><command-name>help</command-name></command-message>';
774
774
  expect(extract(msg)).toBe('help');
775
775
  });
776
776
 
777
777
  it('should extract ARGUMENTS from skill expansion', () => {
778
- const extract = (adapter as any).extractUserMessageText.bind(adapter);
778
+ const extract = (adapter as any).parser['extractUserMessageText'].bind((adapter as any).parser);
779
779
 
780
780
  const msg = 'Base directory for this skill: /some/path\n\nSome instructions\n\nARGUMENTS: implement the feature';
781
781
  expect(extract(msg)).toBe('implement the feature');
782
782
  });
783
783
 
784
784
  it('should return undefined for skill expansion without ARGUMENTS', () => {
785
- const extract = (adapter as any).extractUserMessageText.bind(adapter);
785
+ const extract = (adapter as any).parser['extractUserMessageText'].bind((adapter as any).parser);
786
786
 
787
787
  const msg = 'Base directory for this skill: /some/path\n\nSome instructions only';
788
788
  expect(extract(msg)).toBeUndefined();
789
789
  });
790
790
 
791
791
  it('should filter noise messages', () => {
792
- const extract = (adapter as any).extractUserMessageText.bind(adapter);
792
+ const extract = (adapter as any).parser['extractUserMessageText'].bind((adapter as any).parser);
793
793
 
794
794
  expect(extract('[Request interrupted by user]')).toBeUndefined();
795
795
  expect(extract('Tool loaded.')).toBeUndefined();
@@ -799,7 +799,7 @@ describe('ClaudeCodeAdapter', () => {
799
799
 
800
800
  describe('parseCommandMessage', () => {
801
801
  it('should return undefined for malformed command-message', () => {
802
- const parse = (adapter as any).parseCommandMessage.bind(adapter);
802
+ const parse = (adapter as any).parser['parseCommandMessage'].bind((adapter as any).parser);
803
803
  expect(parse('<command-message>no tags</command-message>')).toBeUndefined();
804
804
  });
805
805
  });
@@ -977,7 +977,7 @@ describe('ClaudeCodeAdapter', () => {
977
977
 
978
978
  describe('readSession', () => {
979
979
  it('should parse session file with timestamps, cwd, and entry type', () => {
980
- const readSession = (adapter as any).readSession.bind(adapter);
980
+ const readSession = (adapter as any).parser.readSession.bind((adapter as any).parser);
981
981
 
982
982
  const filePath = path.join(tmpDir, 'test-session.jsonl');
983
983
  const lines = [
@@ -999,7 +999,7 @@ describe('ClaudeCodeAdapter', () => {
999
999
  });
1000
1000
 
1001
1001
  it('should detect user interruption', () => {
1002
- const readSession = (adapter as any).readSession.bind(adapter);
1002
+ const readSession = (adapter as any).parser.readSession.bind((adapter as any).parser);
1003
1003
 
1004
1004
  const filePath = path.join(tmpDir, 'interrupted.jsonl');
1005
1005
  const lines = [
@@ -1019,7 +1019,7 @@ describe('ClaudeCodeAdapter', () => {
1019
1019
  });
1020
1020
 
1021
1021
  it('should return session with defaults for empty file', () => {
1022
- const readSession = (adapter as any).readSession.bind(adapter);
1022
+ const readSession = (adapter as any).parser.readSession.bind((adapter as any).parser);
1023
1023
 
1024
1024
  const filePath = path.join(tmpDir, 'empty.jsonl');
1025
1025
  fs.writeFileSync(filePath, '');
@@ -1030,12 +1030,12 @@ describe('ClaudeCodeAdapter', () => {
1030
1030
  });
1031
1031
 
1032
1032
  it('should return null for non-existent file', () => {
1033
- const readSession = (adapter as any).readSession.bind(adapter);
1033
+ const readSession = (adapter as any).parser.readSession.bind((adapter as any).parser);
1034
1034
  expect(readSession(path.join(tmpDir, 'nonexistent.jsonl'), '/test')).toBeNull();
1035
1035
  });
1036
1036
 
1037
1037
  it('should skip metadata entry types for lastEntryType', () => {
1038
- const readSession = (adapter as any).readSession.bind(adapter);
1038
+ const readSession = (adapter as any).parser.readSession.bind((adapter as any).parser);
1039
1039
 
1040
1040
  const filePath = path.join(tmpDir, 'metadata-test.jsonl');
1041
1041
  const lines = [
@@ -1051,7 +1051,7 @@ describe('ClaudeCodeAdapter', () => {
1051
1051
  });
1052
1052
 
1053
1053
  it('should parse snapshot.timestamp from file-history-snapshot first entry', () => {
1054
- const readSession = (adapter as any).readSession.bind(adapter);
1054
+ const readSession = (adapter as any).parser.readSession.bind((adapter as any).parser);
1055
1055
 
1056
1056
  const filePath = path.join(tmpDir, 'snapshot-ts.jsonl');
1057
1057
  const lines = [
@@ -1070,7 +1070,7 @@ describe('ClaudeCodeAdapter', () => {
1070
1070
  });
1071
1071
 
1072
1072
  it('should extract lastUserMessage from session entries', () => {
1073
- const readSession = (adapter as any).readSession.bind(adapter);
1073
+ const readSession = (adapter as any).parser.readSession.bind((adapter as any).parser);
1074
1074
 
1075
1075
  const filePath = path.join(tmpDir, 'user-msg.jsonl');
1076
1076
  const lines = [
@@ -1086,7 +1086,7 @@ describe('ClaudeCodeAdapter', () => {
1086
1086
  });
1087
1087
 
1088
1088
  it('should use lastCwd as projectPath when projectPath is empty', () => {
1089
- const readSession = (adapter as any).readSession.bind(adapter);
1089
+ const readSession = (adapter as any).parser.readSession.bind((adapter as any).parser);
1090
1090
 
1091
1091
  const filePath = path.join(tmpDir, 'no-project.jsonl');
1092
1092
  const lines = [
@@ -1099,7 +1099,7 @@ describe('ClaudeCodeAdapter', () => {
1099
1099
  });
1100
1100
 
1101
1101
  it('should handle malformed JSON lines gracefully', () => {
1102
- const readSession = (adapter as any).readSession.bind(adapter);
1102
+ const readSession = (adapter as any).parser.readSession.bind((adapter as any).parser);
1103
1103
 
1104
1104
  const filePath = path.join(tmpDir, 'malformed.jsonl');
1105
1105
  const lines = [
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import { describe, it, expect, jest, beforeEach } from '@jest/globals';
6
- import { execSync } from 'child_process';
6
+ import { execFileSync } from 'child_process';
7
7
  import {
8
8
  listAgentProcesses,
9
9
  batchGetProcessCwds,
@@ -12,18 +12,18 @@ import {
12
12
  } from '../../utils/process';
13
13
 
14
14
  jest.mock('child_process', () => ({
15
- execSync: jest.fn(),
15
+ execFileSync: jest.fn(),
16
16
  }));
17
17
 
18
- const mockedExecSync = execSync as jest.MockedFunction<typeof execSync>;
18
+ const mockedExecFileSync = execFileSync as jest.MockedFunction<typeof execFileSync>;
19
19
 
20
20
  describe('listAgentProcesses', () => {
21
21
  beforeEach(() => {
22
- mockedExecSync.mockReset();
22
+ mockedExecFileSync.mockReset();
23
23
  });
24
24
 
25
25
  it('should parse ps aux | grep output and post-filter by executable name', () => {
26
- mockedExecSync.mockReturnValue(
26
+ mockedExecFileSync.mockReturnValue(
27
27
  'user 78070 1.0 0.5 485636016 245952 s018 S+ 11:18PM 1:55.14 claude\n' +
28
28
  'user 55106 0.1 0.4 485620368 72496 s015 S+ 9Mar26 8:06.36 claude\n',
29
29
  );
@@ -38,7 +38,7 @@ describe('listAgentProcesses', () => {
38
38
  });
39
39
 
40
40
  it('should filter out non-matching executables', () => {
41
- mockedExecSync.mockReturnValue(
41
+ mockedExecFileSync.mockReturnValue(
42
42
  'user 100 0.0 0.0 0 0 s001 S 1:00PM 0:00 claude\n' +
43
43
  'user 200 0.0 0.0 0 0 s002 S 1:00PM 0:00 claude-helper --pid 100\n' +
44
44
  'user 300 0.0 0.0 0 0 s003 S 1:00PM 0:00 /usr/bin/claude\n',
@@ -50,46 +50,46 @@ describe('listAgentProcesses', () => {
50
50
  });
51
51
 
52
52
  it('should return empty array on command failure', () => {
53
- mockedExecSync.mockImplementation(() => { throw new Error('fail'); });
53
+ mockedExecFileSync.mockImplementation(() => { throw new Error('fail'); });
54
54
  expect(listAgentProcesses('claude')).toEqual([]);
55
55
  });
56
56
 
57
57
  it('should handle empty output', () => {
58
- mockedExecSync.mockReturnValue('');
58
+ mockedExecFileSync.mockReturnValue('');
59
59
  expect(listAgentProcesses('claude')).toEqual([]);
60
60
  });
61
61
 
62
62
  it('should reject empty pattern', () => {
63
63
  expect(listAgentProcesses('')).toEqual([]);
64
- expect(mockedExecSync).not.toHaveBeenCalled();
64
+ expect(mockedExecFileSync).not.toHaveBeenCalled();
65
65
  });
66
66
 
67
67
  it('should reject patterns with shell injection characters', () => {
68
68
  expect(listAgentProcesses('claude; rm -rf /')).toEqual([]);
69
69
  expect(listAgentProcesses("claude' || true")).toEqual([]);
70
70
  expect(listAgentProcesses('$(whoami)')).toEqual([]);
71
- expect(mockedExecSync).not.toHaveBeenCalled();
71
+ expect(mockedExecFileSync).not.toHaveBeenCalled();
72
72
  });
73
73
 
74
74
  it('should accept valid patterns with dashes and underscores', () => {
75
- mockedExecSync.mockReturnValue('');
75
+ mockedExecFileSync.mockReturnValue('');
76
76
  listAgentProcesses('claude-code');
77
- expect(mockedExecSync).toHaveBeenCalled();
77
+ expect(mockedExecFileSync).toHaveBeenCalled();
78
78
 
79
- mockedExecSync.mockReset();
80
- mockedExecSync.mockReturnValue('');
79
+ mockedExecFileSync.mockReset();
80
+ mockedExecFileSync.mockReturnValue('');
81
81
  listAgentProcesses('my_agent');
82
- expect(mockedExecSync).toHaveBeenCalled();
82
+ expect(mockedExecFileSync).toHaveBeenCalled();
83
83
  });
84
84
  });
85
85
 
86
86
  describe('batchGetProcessCwds', () => {
87
87
  beforeEach(() => {
88
- mockedExecSync.mockReset();
88
+ mockedExecFileSync.mockReset();
89
89
  });
90
90
 
91
91
  it('should parse batched lsof output', () => {
92
- mockedExecSync.mockReturnValue(
92
+ mockedExecFileSync.mockReturnValue(
93
93
  'p78070\nn/Users/user/ai-devkit\np55106\nn/Users/user/other-project\n',
94
94
  );
95
95
 
@@ -104,7 +104,7 @@ describe('batchGetProcessCwds', () => {
104
104
 
105
105
  it('should return partial results when lsof succeeds for some PIDs', () => {
106
106
  // lsof might not return entries for dead processes
107
- mockedExecSync.mockReturnValue(
107
+ mockedExecFileSync.mockReturnValue(
108
108
  'p78070\nn/Users/user/ai-devkit\n',
109
109
  );
110
110
 
@@ -114,7 +114,7 @@ describe('batchGetProcessCwds', () => {
114
114
  });
115
115
 
116
116
  it('should return empty map on total failure', () => {
117
- mockedExecSync.mockImplementation(() => { throw new Error('fail'); });
117
+ mockedExecFileSync.mockImplementation(() => { throw new Error('fail'); });
118
118
  const cwds = batchGetProcessCwds([78070]);
119
119
  // Falls through to pwdx fallback which also fails
120
120
  expect(cwds.size).toBe(0);
@@ -123,11 +123,11 @@ describe('batchGetProcessCwds', () => {
123
123
 
124
124
  describe('batchGetProcessStartTimes', () => {
125
125
  beforeEach(() => {
126
- mockedExecSync.mockReset();
126
+ mockedExecFileSync.mockReset();
127
127
  });
128
128
 
129
129
  it('should parse ps lstart output', () => {
130
- mockedExecSync.mockReturnValue(
130
+ mockedExecFileSync.mockReturnValue(
131
131
  ' 78070 Wed Mar 18 23:18:01 2026\n' +
132
132
  ' 55106 Mon Mar 9 21:41:42 2026\n',
133
133
  );
@@ -143,7 +143,7 @@ describe('batchGetProcessStartTimes', () => {
143
143
  });
144
144
 
145
145
  it('should skip lines with unparseable dates', () => {
146
- mockedExecSync.mockReturnValue(
146
+ mockedExecFileSync.mockReturnValue(
147
147
  ' 78070 Wed Mar 18 23:18:01 2026\n' +
148
148
  ' 99999 INVALID_DATE\n',
149
149
  );
@@ -154,20 +154,20 @@ describe('batchGetProcessStartTimes', () => {
154
154
  });
155
155
 
156
156
  it('should return empty map on failure', () => {
157
- mockedExecSync.mockImplementation(() => { throw new Error('fail'); });
157
+ mockedExecFileSync.mockImplementation(() => { throw new Error('fail'); });
158
158
  expect(batchGetProcessStartTimes([78070])).toEqual(new Map());
159
159
  });
160
160
  });
161
161
 
162
162
  describe('enrichProcesses', () => {
163
163
  beforeEach(() => {
164
- mockedExecSync.mockReset();
164
+ mockedExecFileSync.mockReset();
165
165
  });
166
166
 
167
167
  it('should populate cwd and startTime on processes', () => {
168
168
  // First call: batchGetProcessCwds (lsof)
169
169
  // Second call: batchGetProcessStartTimes (ps lstart)
170
- mockedExecSync
170
+ mockedExecFileSync
171
171
  .mockReturnValueOnce('p100\nn/projects/app\n')
172
172
  .mockReturnValueOnce(' 100 Wed Mar 18 23:18:01 2026\n');
173
173
 
@@ -182,12 +182,12 @@ describe('enrichProcesses', () => {
182
182
 
183
183
  it('should return empty array for empty input', () => {
184
184
  expect(enrichProcesses([])).toEqual([]);
185
- expect(mockedExecSync).not.toHaveBeenCalled();
185
+ expect(mockedExecFileSync).not.toHaveBeenCalled();
186
186
  });
187
187
 
188
188
  it('should handle partial failures', () => {
189
189
  // lsof succeeds, ps lstart fails
190
- mockedExecSync
190
+ mockedExecFileSync
191
191
  .mockReturnValueOnce('p100\nn/projects/app\n')
192
192
  .mockImplementationOnce(() => { throw new Error('fail'); });
193
193