@k1e1n04/mav 0.1.31 → 0.1.33

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.33",
4
4
  "description": "Multi-agent view — manage multiple AI CLI sessions in one terminal",
5
5
  "type": "module",
6
6
  "bin": {
@@ -21,50 +21,110 @@ 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
+ binary === 'claude' || binary.endsWith('/claude')) {
62
+ return parseInt(item.pidStr, 10);
63
+ }
45
64
  }
46
65
  }
47
- return null;
48
66
  }
49
- if (platform === 'linux') {
50
- const childPids = readFileSync(`/proc/${shellPid}/task/${shellPid}/children`, 'utf8')
51
- .split(' ')
67
+ catch {
68
+ continue;
69
+ }
70
+ // Not claude — enqueue children for next level
71
+ try {
72
+ const children = execFileSync('pgrep', ['-P', item.pidStr], { encoding: 'utf8' })
73
+ .split('\n')
52
74
  .map((s) => s.trim())
53
75
  .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
- }
76
+ for (const child of children) {
77
+ queue.push({ pidStr: child, depth: item.depth + 1 });
61
78
  }
62
- return null;
63
79
  }
80
+ catch {
81
+ // No children — continue
82
+ }
83
+ }
84
+ return null;
85
+ }
86
+ function findClaudeChildLinux(shellPid) {
87
+ let initialChildren;
88
+ try {
89
+ initialChildren = readFileSync(`/proc/${shellPid}/task/${shellPid}/children`, 'utf8')
90
+ .split(' ')
91
+ .map((s) => s.trim())
92
+ .filter(Boolean);
64
93
  }
65
94
  catch {
66
95
  return null;
67
96
  }
97
+ const queue = initialChildren.map((p) => ({ pidStr: p, depth: 0 }));
98
+ while (queue.length > 0) {
99
+ const item = queue.shift();
100
+ if (item.depth >= CLAUDE_CHILD_MAX_DEPTH)
101
+ continue;
102
+ try {
103
+ const cmdline = readFileSync(`/proc/${item.pidStr}/cmdline`, 'utf8');
104
+ const parts = cmdline.split('\0').filter(Boolean);
105
+ const binary = parts[0] ?? '';
106
+ if (((binary === 'node' || binary.endsWith('/node')) && cmdline.includes('claude')) ||
107
+ binary === 'claude' || binary.endsWith('/claude')) {
108
+ return parseInt(item.pidStr, 10);
109
+ }
110
+ }
111
+ catch {
112
+ continue;
113
+ }
114
+ // Not claude — enqueue children for next level
115
+ try {
116
+ const children = readFileSync(`/proc/${item.pidStr}/task/${item.pidStr}/children`, 'utf8')
117
+ .split(' ')
118
+ .map((s) => s.trim())
119
+ .filter(Boolean);
120
+ for (const child of children) {
121
+ queue.push({ pidStr: child, depth: item.depth + 1 });
122
+ }
123
+ }
124
+ catch {
125
+ // No children — continue
126
+ }
127
+ }
68
128
  return null;
69
129
  }
70
130
  //# 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.33",
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,114 @@ 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 (
79
+ ((binary === 'node' || binary.endsWith('/node')) && args.includes('claude')) ||
80
+ binary === 'claude' || binary.endsWith('/claude')
81
+ ) {
82
+ return parseInt(item.pidStr, 10)
83
+ }
57
84
  }
58
85
  }
59
- return null
86
+ } catch {
87
+ continue
60
88
  }
61
89
 
62
- if (platform === 'linux') {
63
- const childPids = readFileSync(`/proc/${shellPid}/task/${shellPid}/children`, 'utf8')
64
- .split(' ')
90
+ // Not claude enqueue children for next level
91
+ try {
92
+ const children = execFileSync('pgrep', ['-P', item.pidStr], { encoding: 'utf8' })
93
+ .split('\n')
65
94
  .map((s) => s.trim())
66
95
  .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
- }
96
+ for (const child of children) {
97
+ queue.push({ pidStr: child, depth: item.depth + 1 })
75
98
  }
76
- return null
99
+ } catch {
100
+ // No children — continue
77
101
  }
102
+ }
103
+
104
+ return null
105
+ }
106
+
107
+ function findClaudeChildLinux(shellPid: number): number | null {
108
+ let initialChildren: string[]
109
+ try {
110
+ initialChildren = readFileSync(`/proc/${shellPid}/task/${shellPid}/children`, 'utf8')
111
+ .split(' ')
112
+ .map((s) => s.trim())
113
+ .filter(Boolean)
78
114
  } catch {
79
115
  return null
80
116
  }
81
117
 
118
+ const queue: Array<{ pidStr: string; depth: number }> = initialChildren.map((p) => ({ pidStr: p, depth: 0 }))
119
+
120
+ while (queue.length > 0) {
121
+ const item = queue.shift()!
122
+ if (item.depth >= CLAUDE_CHILD_MAX_DEPTH) continue
123
+
124
+ try {
125
+ const cmdline = readFileSync(`/proc/${item.pidStr}/cmdline`, 'utf8')
126
+ const parts = cmdline.split('\0').filter(Boolean)
127
+ const binary = parts[0] ?? ''
128
+ if (
129
+ ((binary === 'node' || binary.endsWith('/node')) && cmdline.includes('claude')) ||
130
+ binary === 'claude' || binary.endsWith('/claude')
131
+ ) {
132
+ return parseInt(item.pidStr, 10)
133
+ }
134
+ } catch {
135
+ continue
136
+ }
137
+
138
+ // Not claude — enqueue children for next level
139
+ try {
140
+ const children = readFileSync(`/proc/${item.pidStr}/task/${item.pidStr}/children`, 'utf8')
141
+ .split(' ')
142
+ .map((s) => s.trim())
143
+ .filter(Boolean)
144
+ for (const child of children) {
145
+ queue.push({ pidStr: child, depth: item.depth + 1 })
146
+ }
147
+ } catch {
148
+ // No children — continue
149
+ }
150
+ }
151
+
82
152
  return null
83
153
  }