Feature
Expose tcgetpgrp(masterFd) from UnixTerminal as a foregroundPid accessor (number, or undefined when the call fails).
Why it's missing today
The native binding already calls tcgetpgrp(fd) internally to resolve the foreground process name — see src/unix/pty.cc pty_getproc darwin branch (line ~656) and linux branch (line ~614). The pgrp pid is computed and then thrown away — only kp.kp_proc.p_comm (or /proc/<pid>/cmdline) is returned to JS.
For a class of consumers — agent-aware terminal multiplexers, observability tooling, etc. — the name isn't enough because the foreground process is a script interpreter and the resolved name is something generic like node / python / ruby rather than the meaningful tool name. Having the pid lets the caller cross-reference its own state (e.g. session files on disk, IPC sockets) to identify what's actually running.
Concrete use case
kolu detects Claude Code sessions running in a kolu terminal. Claude writes ~/.claude/sessions/<pid>.json for every running instance. Today kolu has to walk ~/.claude/sessions/, then for each candidate pid read /proc/<pid>/fd/0 to check whether it shares a controlling tty with the kolu shell pid. That's:
- O(sessions × terminals) per poll
- Linux-only —
/proc doesn't exist on macOS, so the feature is completely silent on darwin despite Claude Code being one of its primary platforms
With term.foregroundPid exposed, kolu can ask each terminal "who's in your foreground?" and look up ~/.claude/sessions/<fgpid>.json directly. One syscall per terminal, no platform branch, no /proc dependency.
Proposed API
Mirroring the fd / ptsName accessors added in #516:
class UnixTerminal extends Terminal {
// existing
get fd(): number;
get ptsName(): string;
// new
get foregroundPid(): number | undefined;
}
undefined when tcgetpgrp(fd) === -1 (pty closed, EBADF, etc.).
Reference implementation
Working patch at juspay/node-pty@kolu/foreground-pid (~30 LOC, all in src/unix/pty.cc + src/unixTerminal.ts + src/native.d.ts):
- New
PtyGetForegroundPid(fd) Napi binding — calls tcgetpgrp(fd), returns Napi::Number or env.Undefined() on -1
- New
pty.foregroundPid export in init()
- New accessor on
UnixTerminal that wraps it
No new syscalls, no new dependencies. Cross-platform on Linux + macOS via tcgetpgrp(3) (POSIX). Not exposed on Windows since ConPTY has no equivalent concept — the accessor lives on UnixTerminal, not IPty.
Prior art
Same approach used by:
- tmux —
tcgetpgrp on the master fd
- ghostty — see
getProcessInfo with foreground_pid
- htop — reads
kp_eproc.e_tdev from kinfo_proc, same family of APIs
Happy to open a PR if this is something the maintainers would accept.
Feature
Expose
tcgetpgrp(masterFd)fromUnixTerminalas aforegroundPidaccessor (number, orundefinedwhen the call fails).Why it's missing today
The native binding already calls
tcgetpgrp(fd)internally to resolve the foreground process name — seesrc/unix/pty.ccpty_getprocdarwin branch (line ~656) and linux branch (line ~614). The pgrp pid is computed and then thrown away — onlykp.kp_proc.p_comm(or/proc/<pid>/cmdline) is returned to JS.For a class of consumers — agent-aware terminal multiplexers, observability tooling, etc. — the name isn't enough because the foreground process is a script interpreter and the resolved name is something generic like
node/python/rubyrather than the meaningful tool name. Having the pid lets the caller cross-reference its own state (e.g. session files on disk, IPC sockets) to identify what's actually running.Concrete use case
koludetects Claude Code sessions running in a kolu terminal. Claude writes~/.claude/sessions/<pid>.jsonfor every running instance. Today kolu has to walk~/.claude/sessions/, then for each candidate pid read/proc/<pid>/fd/0to check whether it shares a controlling tty with the kolu shell pid. That's:/procdoesn't exist on macOS, so the feature is completely silent on darwin despite Claude Code being one of its primary platformsWith
term.foregroundPidexposed, kolu can ask each terminal "who's in your foreground?" and look up~/.claude/sessions/<fgpid>.jsondirectly. One syscall per terminal, no platform branch, no/procdependency.Proposed API
Mirroring the
fd/ptsNameaccessors added in #516:undefinedwhentcgetpgrp(fd) === -1(pty closed, EBADF, etc.).Reference implementation
Working patch at
juspay/node-pty@kolu/foreground-pid(~30 LOC, all insrc/unix/pty.cc+src/unixTerminal.ts+src/native.d.ts):PtyGetForegroundPid(fd)Napi binding — callstcgetpgrp(fd), returnsNapi::Numberorenv.Undefined()on-1pty.foregroundPidexport ininit()UnixTerminalthat wraps itNo new syscalls, no new dependencies. Cross-platform on Linux + macOS via
tcgetpgrp(3)(POSIX). Not exposed on Windows since ConPTY has no equivalent concept — the accessor lives onUnixTerminal, notIPty.Prior art
Same approach used by:
tcgetpgrpon the master fdgetProcessInfowithforeground_pidkp_eproc.e_tdevfromkinfo_proc, same family of APIsHappy to open a PR if this is something the maintainers would accept.