@mentiko/pty-mgr 1.2.3

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 ADDED
@@ -0,0 +1,206 @@
1
+ # pty-mgr
2
+
3
+ PTY session manager with terminal emulation for programmatic session control.
4
+
5
+ Spawn commands in real pseudo-terminals, capture rendered screen output (not raw
6
+ bytes), and manage sessions through a persistent daemon. Single binary, no
7
+ external dependencies.
8
+
9
+ ## How It Works
10
+
11
+ `Bun.spawn({ terminal })` allocates a native PTY for each session. An
12
+ `@xterm/headless` terminal emulator parses escape codes, cursor movements, and
13
+ screen redraws so `capture()` returns exactly what you'd see on screen.
14
+
15
+ Compiles to a single self-contained binary via `bun build --compile`.
16
+
17
+ ## Install
18
+
19
+ ### Binary (no dependencies)
20
+
21
+ ```
22
+ curl -fsSL https://raw.githubusercontent.com/maarco/pty-mgr/main/install.sh | sh
23
+ ```
24
+
25
+ Installs to `~/.pty-mgr/bin/` and adds to PATH. Works on Linux and macOS (x64, arm64).
26
+
27
+ ### Bun package (for library use)
28
+
29
+ ```
30
+ bun add pty-mgr
31
+ ```
32
+
33
+ Requires [Bun](https://bun.sh) runtime.
34
+
35
+ ## Quick Start
36
+
37
+ ### As a library
38
+
39
+ ```js
40
+ import { PtyManager } from 'pty-mgr';
41
+
42
+ const mgr = new PtyManager();
43
+ mgr.spawn('my-session', 'zsh', [], { cols: 120, rows: 30 });
44
+ mgr.sendKeys('my-session', 'echo hello\r');
45
+
46
+ // rename a running session
47
+ mgr.rename('my-session', 'my-session-renamed');
48
+
49
+ // wait for output, then capture rendered screen
50
+ setTimeout(() => {
51
+ console.log(mgr.capture('my-session-renamed', 5));
52
+ mgr.kill('my-session-renamed');
53
+ }, 1000);
54
+ ```
55
+
56
+ ### As a CLI
57
+
58
+ ```
59
+ # start the daemon (forks to background)
60
+ p daemon
61
+
62
+ # named daemon for isolated environments
63
+ p daemon @myproject
64
+
65
+ # spawn a session
66
+ # auto-incrementing session named after current directory
67
+ p wrap # spawns as pty-mgr-1
68
+ p wrap # spawns as pty-mgr-2
69
+ p wrap # spawns as pty-mgr-3
70
+ p spawn agent-1 claude --print
71
+
72
+ # send keystrokes
73
+ p send agent-1 "fix the login bug"
74
+
75
+ # capture rendered screen (last 20 lines)
76
+ p capture agent-1 20
77
+
78
+ # attach interactively (ctrl-] to detach)
79
+ p attach agent-1
80
+
81
+ # rename a session
82
+ p rename agent-1 agent-refactored
83
+
84
+ # bulk operations with globs
85
+ p capture all 50
86
+ p kill refa*
87
+
88
+ # stop daemon
89
+ p stop
90
+ ```
91
+
92
+ ### CLI Aliases
93
+
94
+ | Short | Full | Short | Full |
95
+ |-------|---------|-------|---------|
96
+ | n/new | spawn | w/wrap | wrap |
97
+ | s | send | k | kill |
98
+ | c/cap | capture | l/ls | list |
99
+ | a | attach | r/rm | remove |
100
+ | st | status | mv/ren | rename |
101
+ | i | info | d | daemon |
102
+ | cfg | config | x | stop |
103
+
104
+ ## Managed CLI Sessions
105
+
106
+ Wrap any CLI tool (claude, codex, gemini, etc.) in managed PTY sessions.
107
+ Run the interactive setup:
108
+
109
+ ```
110
+ pty-mgr setup
111
+ ```
112
+
113
+ It asks which commands to wrap, then adds shell functions to your rc file.
114
+ After that, just type `claude` like normal. What you get:
115
+
116
+ - Claude runs inside a managed PTY session named `<folder>-1`
117
+ - If you open another claude in the same folder, it gets `<folder>-2`
118
+ - `ctrl-]` to detach -- Claude keeps running in the background
119
+ - `p attach <name>` to jump back in
120
+ - `p capture <name> 50` to check on it from another terminal
121
+ - `p list` to see all your Claude sessions across all projects
122
+
123
+ ```
124
+ $ cd ~/dev/my-app
125
+ $ claude # spawns as my-app-1, attaches
126
+ ctrl-] # detach
127
+ $ claude # spawns as my-app-2
128
+ ctrl-]
129
+ $ p list
130
+ my-app-1 pid=1234 120x40 alive claude
131
+ my-app-2 pid=1235 120x40 alive claude
132
+ $ p capture my-app-1 20 # peek at what agent 1 is doing
133
+ $ p attach my-app-1 # jump back into agent 1
134
+ ```
135
+
136
+ ### Programmatic (parallel agents)
137
+
138
+ ```js
139
+ import { PtyManager } from 'pty-mgr';
140
+
141
+ const mgr = new PtyManager();
142
+
143
+ // launch 3 Claude agents in parallel
144
+ const agents = ['auth-fix', 'api-tests', 'docs-update'];
145
+ for (const name of agents) {
146
+ mgr.spawn(name, 'claude', ['--print'], { cols: 120, rows: 40 });
147
+ }
148
+
149
+ mgr.sendKeys('auth-fix', 'fix the login bug in src/auth.ts\r');
150
+ mgr.sendKeys('api-tests', 'write tests for the /users endpoint\r');
151
+ mgr.sendKeys('docs-update', 'update the API docs in README.md\r');
152
+
153
+ // poll until all agents finish
154
+ for (const name of agents) {
155
+ await mgr.waitFor(name, /[✔✓]|completed|done/i, 120000);
156
+ console.log(`${name} done:\n${mgr.capture(name, 10)}\n`);
157
+ }
158
+
159
+ mgr.destroyAll();
160
+ ```
161
+
162
+ ## Daemon Protocol
163
+
164
+ The daemon listens on a Unix socket at `~/.pty-manager/<name>.sock`.
165
+ Communication is newline-delimited JSON:
166
+
167
+ ```json
168
+ {"cmd": "spawn", "name": "agent-1", "args": {"cmd": "zsh"}}
169
+ {"cmd": "wrap", "args": {"cmd": "zsh", "cwd": "/Users/you/dev/myproject"}}
170
+ {"cmd": "send", "name": "agent-1", "args": {"text": "echo hi\r"}}
171
+ {"cmd": "capture", "name": "agent-1", "args": {"lines": 20}}
172
+ {"cmd": "list"}
173
+ {"cmd": "kill", "name": "agent-1"}
174
+ {"cmd": "rename", "name": "agent-1", "args": {"newName": "agent-refactored"}}
175
+ {"cmd": "shutdown"}
176
+ ```
177
+
178
+ The `attach` command switches the connection to raw streaming mode for
179
+ interactive use.
180
+
181
+ ## Configuration
182
+
183
+ ```
184
+ p config screen 120x40 # default terminal size for new sessions
185
+ p config cap-on-send on # return capture with every send command
186
+ ```
187
+
188
+ ## Logging
189
+
190
+ ```
191
+ p spawn agent-1 --log claude # spawn with auto-logging (jsonl)
192
+ p log agent-1 on jsonl # start logging an existing session
193
+ p log agent-1 off # stop logging
194
+ ```
195
+
196
+ Formats: `jsonl` (timestamped events), `raw` (PTY bytes), `rendered` (screen snapshots).
197
+
198
+ ## Build
199
+
200
+ ```
201
+ bun run build # compiles to dist/pty-mgr (single binary, ~60MB)
202
+ ```
203
+
204
+ ## License
205
+
206
+ MIT
package/bin/pty-mgr ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Binary launcher for pty-mgr.
4
+ // Finds the platform-specific binary installed via optionalDependencies
5
+ // and executes it with the same args.
6
+
7
+ import { execFileSync } from "child_process";
8
+ import { existsSync } from "fs";
9
+ import { dirname, join } from "path";
10
+ import { createRequire } from "module";
11
+ import { fileURLToPath } from "url";
12
+
13
+ const require = createRequire(import.meta.url);
14
+
15
+ const PLATFORMS = {
16
+ "linux-x64": "@mentiko/pty-mgr-linux-x64",
17
+ "linux-arm64": "@mentiko/pty-mgr-linux-arm64",
18
+ "darwin-x64": "@mentiko/pty-mgr-darwin-x64",
19
+ "darwin-arm64": "@mentiko/pty-mgr-darwin-arm64",
20
+ };
21
+
22
+ function getBinaryPath() {
23
+ const key = `${process.platform}-${process.arch}`;
24
+ const pkg = PLATFORMS[key];
25
+
26
+ if (!pkg) {
27
+ console.error(
28
+ `pty-mgr: unsupported platform ${process.platform}-${process.arch}`
29
+ );
30
+ console.error("supported: linux-x64, linux-arm64, darwin-x64, darwin-arm64");
31
+ process.exit(1);
32
+ }
33
+
34
+ // try to find the binary from the installed optional dep
35
+ try {
36
+ const pkgDir = dirname(require.resolve(`${pkg}/package.json`));
37
+ const bin = join(pkgDir, "pty-mgr");
38
+ if (existsSync(bin)) return bin;
39
+ } catch {}
40
+
41
+ // fallback: check ~/.pty-mgr/bin/ (install.sh location)
42
+ const homeBin = join(
43
+ process.env.HOME || process.env.USERPROFILE || "",
44
+ ".pty-mgr",
45
+ "bin",
46
+ "pty-mgr"
47
+ );
48
+ if (existsSync(homeBin)) return homeBin;
49
+
50
+ console.error("pty-mgr: binary not found");
51
+ console.error("");
52
+ console.error("install it:");
53
+ console.error(
54
+ " curl -fsSL https://raw.githubusercontent.com/kollaborai/pty-mgr/main/install.sh | sh"
55
+ );
56
+ console.error("");
57
+ console.error("or reinstall this package:");
58
+ console.error(" npm install -g @mentiko/pty-mgr");
59
+ process.exit(1);
60
+ }
61
+
62
+ const bin = getBinaryPath();
63
+
64
+ try {
65
+ execFileSync(bin, process.argv.slice(2), {
66
+ stdio: "inherit",
67
+ env: process.env,
68
+ });
69
+ } catch (e) {
70
+ process.exit(e.status || 1);
71
+ }