@k1e1n04/mav 0.1.31 → 0.1.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -120,13 +120,15 @@ mav tracks each agent's working directory. When an agent changes directory (e.g.
120
120
 
121
121
  | Agent | CWD Tracking | Hook Auto-injection |
122
122
  |---|---|---|
123
- | `claude-code` | IPC + PostToolUse hook | Auto-injected via `--settings` |
123
+ | `claude-code` | child PID polling + OSC 7 | No injection needed |
124
124
  | `gemini-cli` | IPC + AfterTool hook | ✅ Auto-injected via `.gemini/settings.local.json` |
125
125
  | `codex` | IPC + PostToolUse hook | ✅ Auto-injected via `--profile-v2` |
126
126
  | `copilot` | IPC + PostToolUse hook | ✅ Auto-injected via `.github/hooks/*.json` |
127
127
  | `cursor` | lsof polling only | ❌ Not supported |
128
128
  | Other | lsof polling only | ❌ Not supported |
129
129
 
130
+ `claude-code` CWD tracking works by finding the Claude Code process (direct child of the shell PTY) and polling its own working directory. This correctly captures `process.chdir()` calls made by Claude Code when switching git worktrees, without any hook injection.
131
+
130
132
  > **Cursor** and unlisted agents use process polling to track the working directory.
131
133
  > Directory changes made in child processes (e.g., git worktrees) may not be detected.
132
134
  >
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@k1e1n04/mav",
3
- "version": "0.1.31",
3
+ "version": "0.1.32",
4
4
  "description": "Multi-agent view — manage multiple AI CLI sessions in one terminal",
5
5
  "type": "module",
6
6
  "bin": {
@@ -21,50 +21,108 @@ export function getProcessCwd(pid, platform = process.platform) {
21
21
  }
22
22
  return null;
23
23
  }
24
+ const CLAUDE_CHILD_MAX_DEPTH = 5;
24
25
  export function getClaudeChildPid(shellPid, platform = process.platform) {
25
26
  if (!Number.isInteger(shellPid) || shellPid <= 0) {
26
27
  return null;
27
28
  }
29
+ if (platform === 'darwin') {
30
+ return findClaudeChildDarwin(shellPid);
31
+ }
32
+ if (platform === 'linux') {
33
+ return findClaudeChildLinux(shellPid);
34
+ }
35
+ return null;
36
+ }
37
+ function findClaudeChildDarwin(shellPid) {
38
+ let initialChildren;
28
39
  try {
29
- if (platform === 'darwin') {
30
- const childPids = execFileSync('pgrep', ['-P', String(shellPid)], { encoding: 'utf8' })
31
- .split('\n')
32
- .map((s) => s.trim())
33
- .filter(Boolean);
34
- for (const pidStr of childPids) {
35
- const line = execFileSync('ps', ['-o', 'pid=,args=', '-p', pidStr], { encoding: 'utf8' }).trim();
36
- if (!line)
37
- continue;
40
+ initialChildren = execFileSync('pgrep', ['-P', String(shellPid)], { encoding: 'utf8' })
41
+ .split('\n')
42
+ .map((s) => s.trim())
43
+ .filter(Boolean);
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ const queue = initialChildren.map((p) => ({ pidStr: p, depth: 0 }));
49
+ while (queue.length > 0) {
50
+ const item = queue.shift();
51
+ if (item.depth >= CLAUDE_CHILD_MAX_DEPTH)
52
+ continue;
53
+ try {
54
+ const line = execFileSync('ps', ['-o', 'pid=,args=', '-p', item.pidStr], { encoding: 'utf8' }).trim();
55
+ if (line) {
38
56
  const spaceIdx = line.search(/\s/);
39
- if (spaceIdx === -1)
40
- continue;
41
- const args = line.slice(spaceIdx).trim();
42
- const binary = args.split(' ')[0] ?? '';
43
- if ((binary === 'node' || binary.endsWith('/node')) && args.includes('claude')) {
44
- return parseInt(pidStr, 10);
57
+ if (spaceIdx !== -1) {
58
+ const args = line.slice(spaceIdx).trim();
59
+ const binary = args.split(' ')[0] ?? '';
60
+ if ((binary === 'node' || binary.endsWith('/node')) && args.includes('claude')) {
61
+ return parseInt(item.pidStr, 10);
62
+ }
45
63
  }
46
64
  }
47
- return null;
48
65
  }
49
- if (platform === 'linux') {
50
- const childPids = readFileSync(`/proc/${shellPid}/task/${shellPid}/children`, 'utf8')
51
- .split(' ')
66
+ catch {
67
+ continue;
68
+ }
69
+ // Not claude — enqueue children for next level
70
+ try {
71
+ const children = execFileSync('pgrep', ['-P', item.pidStr], { encoding: 'utf8' })
72
+ .split('\n')
52
73
  .map((s) => s.trim())
53
74
  .filter(Boolean);
54
- for (const pidStr of childPids) {
55
- const cmdline = readFileSync(`/proc/${pidStr}/cmdline`, 'utf8');
56
- const parts = cmdline.split('\0').filter(Boolean);
57
- const binary = parts[0] ?? '';
58
- if ((binary === 'node' || binary.endsWith('/node')) && cmdline.includes('claude')) {
59
- return parseInt(pidStr, 10);
60
- }
75
+ for (const child of children) {
76
+ queue.push({ pidStr: child, depth: item.depth + 1 });
61
77
  }
62
- return null;
63
78
  }
79
+ catch {
80
+ // No children — continue
81
+ }
82
+ }
83
+ return null;
84
+ }
85
+ function findClaudeChildLinux(shellPid) {
86
+ let initialChildren;
87
+ try {
88
+ initialChildren = readFileSync(`/proc/${shellPid}/task/${shellPid}/children`, 'utf8')
89
+ .split(' ')
90
+ .map((s) => s.trim())
91
+ .filter(Boolean);
64
92
  }
65
93
  catch {
66
94
  return null;
67
95
  }
96
+ const queue = initialChildren.map((p) => ({ pidStr: p, depth: 0 }));
97
+ while (queue.length > 0) {
98
+ const item = queue.shift();
99
+ if (item.depth >= CLAUDE_CHILD_MAX_DEPTH)
100
+ continue;
101
+ try {
102
+ const cmdline = readFileSync(`/proc/${item.pidStr}/cmdline`, 'utf8');
103
+ const parts = cmdline.split('\0').filter(Boolean);
104
+ const binary = parts[0] ?? '';
105
+ if ((binary === 'node' || binary.endsWith('/node')) && cmdline.includes('claude')) {
106
+ return parseInt(item.pidStr, 10);
107
+ }
108
+ }
109
+ catch {
110
+ continue;
111
+ }
112
+ // Not claude — enqueue children for next level
113
+ try {
114
+ const children = readFileSync(`/proc/${item.pidStr}/task/${item.pidStr}/children`, 'utf8')
115
+ .split(' ')
116
+ .map((s) => s.trim())
117
+ .filter(Boolean);
118
+ for (const child of children) {
119
+ queue.push({ pidStr: child, depth: item.depth + 1 });
120
+ }
121
+ }
122
+ catch {
123
+ // No children — continue
124
+ }
125
+ }
68
126
  return null;
69
127
  }
70
128
  //# sourceMappingURL=process-cwd.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"process-cwd.js","sourceRoot":"","sources":["../../src/process-cwd.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"process-cwd.js","sourceRoot":"","sources":["../../src/process-cwd.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@k1e1n04/mav",
3
- "version": "0.1.31",
3
+ "version": "0.1.32",
4
4
  "description": "Multi-agent view — manage multiple AI CLI sessions in one terminal",
5
5
  "type": "module",
6
6
  "bin": {
@@ -30,6 +30,8 @@ export function getProcessCwd(pid: number, platform: NodeJS.Platform = process.p
30
30
  return null
31
31
  }
32
32
 
33
+ const CLAUDE_CHILD_MAX_DEPTH = 5
34
+
33
35
  export function getClaudeChildPid(
34
36
  shellPid: number,
35
37
  platform: NodeJS.Platform = process.platform,
@@ -38,46 +40,108 @@ export function getClaudeChildPid(
38
40
  return null
39
41
  }
40
42
 
43
+ if (platform === 'darwin') {
44
+ return findClaudeChildDarwin(shellPid)
45
+ }
46
+
47
+ if (platform === 'linux') {
48
+ return findClaudeChildLinux(shellPid)
49
+ }
50
+
51
+ return null
52
+ }
53
+
54
+ function findClaudeChildDarwin(shellPid: number): number | null {
55
+ let initialChildren: string[]
41
56
  try {
42
- if (platform === 'darwin') {
43
- const childPids = execFileSync('pgrep', ['-P', String(shellPid)], { encoding: 'utf8' })
44
- .split('\n')
45
- .map((s) => s.trim())
46
- .filter(Boolean)
57
+ initialChildren = execFileSync('pgrep', ['-P', String(shellPid)], { encoding: 'utf8' })
58
+ .split('\n')
59
+ .map((s) => s.trim())
60
+ .filter(Boolean)
61
+ } catch {
62
+ return null
63
+ }
47
64
 
48
- for (const pidStr of childPids) {
49
- const line = execFileSync('ps', ['-o', 'pid=,args=', '-p', pidStr], { encoding: 'utf8' }).trim()
50
- if (!line) continue
65
+ const queue: Array<{ pidStr: string; depth: number }> = initialChildren.map((p) => ({ pidStr: p, depth: 0 }))
66
+
67
+ while (queue.length > 0) {
68
+ const item = queue.shift()!
69
+ if (item.depth >= CLAUDE_CHILD_MAX_DEPTH) continue
70
+
71
+ try {
72
+ const line = execFileSync('ps', ['-o', 'pid=,args=', '-p', item.pidStr], { encoding: 'utf8' }).trim()
73
+ if (line) {
51
74
  const spaceIdx = line.search(/\s/)
52
- if (spaceIdx === -1) continue
53
- const args = line.slice(spaceIdx).trim()
54
- const binary = args.split(' ')[0] ?? ''
55
- if ((binary === 'node' || binary.endsWith('/node')) && args.includes('claude')) {
56
- return parseInt(pidStr, 10)
75
+ if (spaceIdx !== -1) {
76
+ const args = line.slice(spaceIdx).trim()
77
+ const binary = args.split(' ')[0] ?? ''
78
+ if ((binary === 'node' || binary.endsWith('/node')) && args.includes('claude')) {
79
+ return parseInt(item.pidStr, 10)
80
+ }
57
81
  }
58
82
  }
59
- return null
83
+ } catch {
84
+ continue
60
85
  }
61
86
 
62
- if (platform === 'linux') {
63
- const childPids = readFileSync(`/proc/${shellPid}/task/${shellPid}/children`, 'utf8')
64
- .split(' ')
87
+ // Not claude enqueue children for next level
88
+ try {
89
+ const children = execFileSync('pgrep', ['-P', item.pidStr], { encoding: 'utf8' })
90
+ .split('\n')
65
91
  .map((s) => s.trim())
66
92
  .filter(Boolean)
67
-
68
- for (const pidStr of childPids) {
69
- const cmdline = readFileSync(`/proc/${pidStr}/cmdline`, 'utf8')
70
- const parts = cmdline.split('\0').filter(Boolean)
71
- const binary = parts[0] ?? ''
72
- if ((binary === 'node' || binary.endsWith('/node')) && cmdline.includes('claude')) {
73
- return parseInt(pidStr, 10)
74
- }
93
+ for (const child of children) {
94
+ queue.push({ pidStr: child, depth: item.depth + 1 })
75
95
  }
76
- return null
96
+ } catch {
97
+ // No children — continue
77
98
  }
99
+ }
100
+
101
+ return null
102
+ }
103
+
104
+ function findClaudeChildLinux(shellPid: number): number | null {
105
+ let initialChildren: string[]
106
+ try {
107
+ initialChildren = readFileSync(`/proc/${shellPid}/task/${shellPid}/children`, 'utf8')
108
+ .split(' ')
109
+ .map((s) => s.trim())
110
+ .filter(Boolean)
78
111
  } catch {
79
112
  return null
80
113
  }
81
114
 
115
+ const queue: Array<{ pidStr: string; depth: number }> = initialChildren.map((p) => ({ pidStr: p, depth: 0 }))
116
+
117
+ while (queue.length > 0) {
118
+ const item = queue.shift()!
119
+ if (item.depth >= CLAUDE_CHILD_MAX_DEPTH) continue
120
+
121
+ try {
122
+ const cmdline = readFileSync(`/proc/${item.pidStr}/cmdline`, 'utf8')
123
+ const parts = cmdline.split('\0').filter(Boolean)
124
+ const binary = parts[0] ?? ''
125
+ if ((binary === 'node' || binary.endsWith('/node')) && cmdline.includes('claude')) {
126
+ return parseInt(item.pidStr, 10)
127
+ }
128
+ } catch {
129
+ continue
130
+ }
131
+
132
+ // Not claude — enqueue children for next level
133
+ try {
134
+ const children = readFileSync(`/proc/${item.pidStr}/task/${item.pidStr}/children`, 'utf8')
135
+ .split(' ')
136
+ .map((s) => s.trim())
137
+ .filter(Boolean)
138
+ for (const child of children) {
139
+ queue.push({ pidStr: child, depth: item.depth + 1 })
140
+ }
141
+ } catch {
142
+ // No children — continue
143
+ }
144
+ }
145
+
82
146
  return null
83
147
  }