tmux-agent 0.1.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.
Files changed (161) hide show
  1. package/.codex/skills/speckit/SKILL.md +173 -0
  2. package/.codex/skills/speckit/assets/templates/checklist-template.md +49 -0
  3. package/.codex/skills/speckit/assets/templates/notes-entrypoints-template.md +11 -0
  4. package/.codex/skills/speckit/assets/templates/notes-questions-template.md +7 -0
  5. package/.codex/skills/speckit/assets/templates/notes-readme-template.md +36 -0
  6. package/.codex/skills/speckit/assets/templates/notes-session-template.md +21 -0
  7. package/.codex/skills/speckit/assets/templates/plan-template.md +126 -0
  8. package/.codex/skills/speckit/assets/templates/spec-template.md +135 -0
  9. package/.codex/skills/speckit/assets/templates/tasks-template.md +269 -0
  10. package/.codex/skills/speckit/references/acceptance.md +183 -0
  11. package/.codex/skills/speckit/references/analyze.md +186 -0
  12. package/.codex/skills/speckit/references/checklist.md +302 -0
  13. package/.codex/skills/speckit/references/clarify-auto.md +69 -0
  14. package/.codex/skills/speckit/references/clarify-detailed.md +78 -0
  15. package/.codex/skills/speckit/references/clarify.md +189 -0
  16. package/.codex/skills/speckit/references/constitution.md +90 -0
  17. package/.codex/skills/speckit/references/group.md +89 -0
  18. package/.codex/skills/speckit/references/implement-task.md +115 -0
  19. package/.codex/skills/speckit/references/implement.md +129 -0
  20. package/.codex/skills/speckit/references/notes.md +82 -0
  21. package/.codex/skills/speckit/references/plan-deep.md +87 -0
  22. package/.codex/skills/speckit/references/plan-from-questions.md +115 -0
  23. package/.codex/skills/speckit/references/plan-from-review.md +89 -0
  24. package/.codex/skills/speckit/references/plan.md +97 -0
  25. package/.codex/skills/speckit/references/review-plan.md +156 -0
  26. package/.codex/skills/speckit/references/specify.md +246 -0
  27. package/.codex/skills/speckit/references/tasks.md +155 -0
  28. package/.codex/skills/speckit/references/taskstoissues.md +33 -0
  29. package/.codex/skills/speckit/scripts/bash/check-prerequisites.sh +206 -0
  30. package/.codex/skills/speckit/scripts/bash/common.sh +191 -0
  31. package/.codex/skills/speckit/scripts/bash/create-new-feature.sh +259 -0
  32. package/.codex/skills/speckit/scripts/bash/extract-coded-points.sh +322 -0
  33. package/.codex/skills/speckit/scripts/bash/extract-spec-ids.sh +238 -0
  34. package/.codex/skills/speckit/scripts/bash/extract-tasks.sh +295 -0
  35. package/.codex/skills/speckit/scripts/bash/extract-user-stories.sh +312 -0
  36. package/.codex/skills/speckit/scripts/bash/setup-notes.sh +182 -0
  37. package/.codex/skills/speckit/scripts/bash/setup-plan.sh +110 -0
  38. package/.codex/skills/speckit/scripts/bash/show-todo-tasks.sh +257 -0
  39. package/.codex/skills/speckit/scripts/bash/spec-group-checklist.sh +402 -0
  40. package/.codex/skills/speckit/scripts/bash/spec-group-members.sh +215 -0
  41. package/.codex/skills/speckit/scripts/bash/spec-registry-graph.sh +399 -0
  42. package/.specify/memory/constitution.md +67 -0
  43. package/.specify/templates/agent-file-template.md +28 -0
  44. package/.specify/templates/checklist-template.md +49 -0
  45. package/.specify/templates/plan-template.md +126 -0
  46. package/.specify/templates/spec-template.md +135 -0
  47. package/.specify/templates/tasks-template.md +269 -0
  48. package/README.md +128 -0
  49. package/README.zh-CN.md +127 -0
  50. package/bun.lock +269 -0
  51. package/dist/cli/commands/codex/forkHome.js +88 -0
  52. package/dist/cli/commands/codex/send.js +55 -0
  53. package/dist/cli/commands/codex/sessionInfo.js +42 -0
  54. package/dist/cli/commands/codex/spawn.js +68 -0
  55. package/dist/cli/commands/find.js +26 -0
  56. package/dist/cli/commands/paneKill.js +33 -0
  57. package/dist/cli/commands/paneSpawn.js +40 -0
  58. package/dist/cli/commands/paneTitle.js +33 -0
  59. package/dist/cli/commands/read.js +34 -0
  60. package/dist/cli/commands/send.js +51 -0
  61. package/dist/cli/commands/snapshot.js +19 -0
  62. package/dist/cli/commands/ui/select.js +41 -0
  63. package/dist/cli/commands/windowKill.js +25 -0
  64. package/dist/cli/commands/windowLs.js +15 -0
  65. package/dist/cli/commands/windowNew.js +28 -0
  66. package/dist/cli/commands/windowRename.js +25 -0
  67. package/dist/cli/index.js +365 -0
  68. package/dist/cli/parse.js +39 -0
  69. package/dist/lib/codex/forkHome.js +101 -0
  70. package/dist/lib/codex/isCodexPane.js +55 -0
  71. package/dist/lib/codex/send.js +58 -0
  72. package/dist/lib/codex/sessionInfo.js +449 -0
  73. package/dist/lib/codex/spawn.js +246 -0
  74. package/dist/lib/contracts/types.js +2 -0
  75. package/dist/lib/fs/safeRm.js +32 -0
  76. package/dist/lib/io/readStdin.js +14 -0
  77. package/dist/lib/os/process.js +55 -0
  78. package/dist/lib/output/format.js +95 -0
  79. package/dist/lib/proc/lsof.js +42 -0
  80. package/dist/lib/proc/ps.js +60 -0
  81. package/dist/lib/targeting/errors.js +13 -0
  82. package/dist/lib/targeting/resolvePaneTarget.js +91 -0
  83. package/dist/lib/targeting/resolveWindowTarget.js +40 -0
  84. package/dist/lib/targeting/scope.js +58 -0
  85. package/dist/lib/tmux/capturePane.js +20 -0
  86. package/dist/lib/tmux/exec.js +66 -0
  87. package/dist/lib/tmux/paneOps.js +29 -0
  88. package/dist/lib/tmux/paste.js +23 -0
  89. package/dist/lib/tmux/sendKeys.js +47 -0
  90. package/dist/lib/tmux/session.js +29 -0
  91. package/dist/lib/tmux/snapshotPanes.js +46 -0
  92. package/dist/lib/tmux/snapshotWindows.js +24 -0
  93. package/dist/lib/tmux/windowOps.js +32 -0
  94. package/dist/lib/ui/popupSelect.js +432 -0
  95. package/dist/lib/ui/popupSupport.js +76 -0
  96. package/package.json +23 -0
  97. package/src/cli/commands/codex/forkHome.ts +141 -0
  98. package/src/cli/commands/codex/send.ts +83 -0
  99. package/src/cli/commands/codex/sessionInfo.ts +59 -0
  100. package/src/cli/commands/codex/spawn.ts +90 -0
  101. package/src/cli/commands/find.ts +40 -0
  102. package/src/cli/commands/paneKill.ts +49 -0
  103. package/src/cli/commands/paneSpawn.ts +53 -0
  104. package/src/cli/commands/paneTitle.ts +50 -0
  105. package/src/cli/commands/read.ts +48 -0
  106. package/src/cli/commands/send.ts +71 -0
  107. package/src/cli/commands/snapshot.ts +28 -0
  108. package/src/cli/commands/ui/select.ts +49 -0
  109. package/src/cli/commands/windowKill.ts +35 -0
  110. package/src/cli/commands/windowLs.ts +20 -0
  111. package/src/cli/commands/windowNew.ts +40 -0
  112. package/src/cli/commands/windowRename.ts +36 -0
  113. package/src/cli/index.ts +430 -0
  114. package/src/lib/codex/forkHome.ts +148 -0
  115. package/src/lib/codex/isCodexPane.ts +56 -0
  116. package/src/lib/codex/send.ts +84 -0
  117. package/src/lib/codex/sessionInfo.ts +521 -0
  118. package/src/lib/codex/spawn.ts +305 -0
  119. package/src/lib/contracts/types.ts +30 -0
  120. package/src/lib/fs/safeRm.ts +32 -0
  121. package/src/lib/io/readStdin.ts +11 -0
  122. package/src/lib/output/format.ts +105 -0
  123. package/src/lib/proc/lsof.ts +44 -0
  124. package/src/lib/proc/ps.ts +70 -0
  125. package/src/lib/targeting/errors.ts +25 -0
  126. package/src/lib/targeting/resolvePaneTarget.ts +106 -0
  127. package/src/lib/targeting/resolveWindowTarget.ts +45 -0
  128. package/src/lib/targeting/scope.ts +76 -0
  129. package/src/lib/tmux/capturePane.ts +21 -0
  130. package/src/lib/tmux/exec.ts +90 -0
  131. package/src/lib/tmux/paneOps.ts +35 -0
  132. package/src/lib/tmux/paste.ts +20 -0
  133. package/src/lib/tmux/sendKeys.ts +72 -0
  134. package/src/lib/tmux/session.ts +27 -0
  135. package/src/lib/tmux/snapshotPanes.ts +52 -0
  136. package/src/lib/tmux/snapshotWindows.ts +23 -0
  137. package/src/lib/tmux/windowOps.ts +43 -0
  138. package/src/lib/ui/popupSelect.ts +561 -0
  139. package/src/lib/ui/popupSupport.ts +84 -0
  140. package/tests/e2e/codexForkHome.test.ts +146 -0
  141. package/tests/e2e/codexSessionInfo.test.ts +112 -0
  142. package/tests/e2e/codexTuiSend.test.ts +68 -0
  143. package/tests/integration/codexSpawn.test.ts +113 -0
  144. package/tests/integration/paneOps.test.ts +60 -0
  145. package/tests/integration/sendRead.test.ts +52 -0
  146. package/tests/integration/snapshot.test.ts +39 -0
  147. package/tests/integration/tmuxHarness.ts +39 -0
  148. package/tests/integration/windowOps.test.ts +60 -0
  149. package/tests/unit/codexSend.test.ts +105 -0
  150. package/tests/unit/codexSessionInfo.test.ts +88 -0
  151. package/tests/unit/codexSpawn.test.ts +34 -0
  152. package/tests/unit/keys.test.ts +30 -0
  153. package/tests/unit/outputFormat.test.ts +52 -0
  154. package/tests/unit/popupSelect.test.ts +77 -0
  155. package/tests/unit/popupSupport.test.ts +109 -0
  156. package/tests/unit/resolvePaneTarget.test.ts +43 -0
  157. package/tests/unit/resolveWindowTarget.test.ts +36 -0
  158. package/tests/unit/safeRm.test.ts +41 -0
  159. package/tests/unit/scope.test.ts +57 -0
  160. package/tsconfig.json +14 -0
  161. package/vitest.config.ts +16 -0
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.safeRemove = safeRemove;
7
+ const node_fs_1 = require("node:fs");
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ function resolvePath(input) {
10
+ return node_path_1.default.resolve(input);
11
+ }
12
+ async function safeRemove(targetPath, allowedRoot) {
13
+ const resolvedTarget = resolvePath(targetPath);
14
+ const resolvedRoot = resolvePath(allowedRoot);
15
+ const relative = node_path_1.default.relative(resolvedRoot, resolvedTarget);
16
+ if (!relative || relative === "" || relative === ".") {
17
+ throw new Error("refuse to remove allowed root");
18
+ }
19
+ if (relative.startsWith("..") || node_path_1.default.isAbsolute(relative)) {
20
+ throw new Error("path is outside allowed_root");
21
+ }
22
+ try {
23
+ await node_fs_1.promises.rm(resolvedTarget, { recursive: true, force: true });
24
+ return true;
25
+ }
26
+ catch (error) {
27
+ if (error instanceof Error && error.code === "ENOENT") {
28
+ return false;
29
+ }
30
+ throw error;
31
+ }
32
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readStdin = readStdin;
4
+ async function readStdin() {
5
+ return await new Promise((resolve, reject) => {
6
+ let data = "";
7
+ process.stdin.setEncoding("utf8");
8
+ process.stdin.on("data", (chunk) => {
9
+ data += chunk;
10
+ });
11
+ process.stdin.on("end", () => resolve(data));
12
+ process.stdin.on("error", (error) => reject(error));
13
+ });
14
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listProcesses = listProcesses;
4
+ const node_child_process_1 = require("node:child_process");
5
+ function parseProcessLine(line) {
6
+ const trimmed = line.trim();
7
+ if (!trimmed) {
8
+ return null;
9
+ }
10
+ const match = trimmed.match(/^(\d+)\s+(\d+)\s+(.*)$/);
11
+ if (!match) {
12
+ return null;
13
+ }
14
+ const pid = Number(match[1]);
15
+ const ppid = Number(match[2]);
16
+ if (!Number.isFinite(pid) || !Number.isFinite(ppid)) {
17
+ return null;
18
+ }
19
+ return { pid, ppid, command: match[3] };
20
+ }
21
+ async function listProcesses() {
22
+ return await new Promise((resolve, reject) => {
23
+ const child = (0, node_child_process_1.spawn)("ps", ["-ax", "-o", "pid=,ppid=,command="]);
24
+ let stdout = "";
25
+ let stderr = "";
26
+ if (child.stdout) {
27
+ child.stdout.on("data", (data) => {
28
+ stdout += data.toString();
29
+ });
30
+ }
31
+ if (child.stderr) {
32
+ child.stderr.on("data", (data) => {
33
+ stderr += data.toString();
34
+ });
35
+ }
36
+ child.on("error", (error) => {
37
+ reject(error);
38
+ });
39
+ child.on("close", (code) => {
40
+ const exitCode = code ?? 0;
41
+ if (exitCode !== 0) {
42
+ reject(new Error(stderr.trim() || stdout.trim() || `ps exited with ${exitCode}`));
43
+ return;
44
+ }
45
+ const processes = [];
46
+ for (const line of stdout.split(/\r?\n/)) {
47
+ const parsed = parseProcessLine(line);
48
+ if (parsed) {
49
+ processes.push(parsed);
50
+ }
51
+ }
52
+ resolve(processes);
53
+ });
54
+ });
55
+ }
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatWindowStatus = formatWindowStatus;
4
+ exports.formatPaneStatus = formatPaneStatus;
5
+ exports.formatCommandTitle = formatCommandTitle;
6
+ exports.formatWindowTable = formatWindowTable;
7
+ exports.formatPaneTable = formatPaneTable;
8
+ exports.formatJson = formatJson;
9
+ exports.ensureErrorPrefix = ensureErrorPrefix;
10
+ const TITLE_SEPARATOR = " \u00b7 ";
11
+ function buildTable(columns) {
12
+ const widths = columns.map((col) => Math.max(col.header.length, ...col.values.map((value) => value.length)));
13
+ const header = columns
14
+ .map((col, index) => col.header.padEnd(widths[index]))
15
+ .join(" ");
16
+ const rows = columns[0].values.map((_, rowIndex) => columns
17
+ .map((col, colIndex) => col.values[rowIndex].padEnd(widths[colIndex]))
18
+ .join(" "));
19
+ return [header, ...rows].join("\n");
20
+ }
21
+ function formatWindowStatus(status) {
22
+ return status === "active" ? "[Active]" : "[Idle]";
23
+ }
24
+ function formatPaneStatus(status) {
25
+ if (status === "active") {
26
+ return "[Active]";
27
+ }
28
+ if (status === "dead") {
29
+ return "[Dead]";
30
+ }
31
+ return "[Idle]";
32
+ }
33
+ function formatCommandTitle(command, title) {
34
+ if (title) {
35
+ if (!command) {
36
+ return title;
37
+ }
38
+ return `${command}${TITLE_SEPARATOR}${title}`;
39
+ }
40
+ return command;
41
+ }
42
+ function formatWindowTable(windows) {
43
+ return buildTable([
44
+ {
45
+ header: "WIDX",
46
+ values: windows.map((window) => String(window.widx))
47
+ },
48
+ {
49
+ header: "WID",
50
+ values: windows.map((window) => window.wid)
51
+ },
52
+ {
53
+ header: "STATUS",
54
+ values: windows.map((window) => formatWindowStatus(window.status))
55
+ },
56
+ {
57
+ header: "NAME",
58
+ values: windows.map((window) => window.name)
59
+ }
60
+ ]);
61
+ }
62
+ function formatPaneTable(panes) {
63
+ return buildTable([
64
+ {
65
+ header: "IDX",
66
+ values: panes.map((pane) => String(pane.idx))
67
+ },
68
+ {
69
+ header: "ID",
70
+ values: panes.map((pane) => pane.id)
71
+ },
72
+ {
73
+ header: "STATUS",
74
+ values: panes.map((pane) => formatPaneStatus(pane.status))
75
+ },
76
+ {
77
+ header: "COMMAND/TITLE",
78
+ values: panes.map((pane) => formatCommandTitle(pane.command, pane.title))
79
+ }
80
+ ]);
81
+ }
82
+ function formatJson(data) {
83
+ return JSON.stringify(data);
84
+ }
85
+ function ensureErrorPrefix(message) {
86
+ const trimmed = message.trim();
87
+ if (!trimmed) {
88
+ return "Error: unknown error";
89
+ }
90
+ if (trimmed.toLowerCase().startsWith("error:")) {
91
+ const rest = trimmed.slice(trimmed.indexOf(":") + 1);
92
+ return `Error:${rest}`;
93
+ }
94
+ return `Error: ${trimmed}`;
95
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listOpenFiles = listOpenFiles;
4
+ const node_child_process_1 = require("node:child_process");
5
+ async function listOpenFiles(pid) {
6
+ return await new Promise((resolve, reject) => {
7
+ const child = (0, node_child_process_1.spawn)("lsof", ["-p", String(pid), "-Fn"]);
8
+ let stdout = "";
9
+ let stderr = "";
10
+ if (child.stdout) {
11
+ child.stdout.on("data", (data) => {
12
+ stdout += data.toString();
13
+ });
14
+ }
15
+ if (child.stderr) {
16
+ child.stderr.on("data", (data) => {
17
+ stderr += data.toString();
18
+ });
19
+ }
20
+ child.on("error", (error) => {
21
+ reject(error);
22
+ });
23
+ child.on("close", (code) => {
24
+ const exitCode = code ?? 0;
25
+ if (exitCode !== 0) {
26
+ reject(new Error(stderr.trim() || stdout.trim() || `lsof exited with ${exitCode}`));
27
+ return;
28
+ }
29
+ const paths = [];
30
+ for (const line of stdout.split(/\r?\n/)) {
31
+ if (!line.startsWith("n")) {
32
+ continue;
33
+ }
34
+ const path = line.slice(1);
35
+ if (path) {
36
+ paths.push(path);
37
+ }
38
+ }
39
+ resolve(paths);
40
+ });
41
+ });
42
+ }
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listProcesses = listProcesses;
4
+ const node_child_process_1 = require("node:child_process");
5
+ function parseLine(line) {
6
+ const trimmed = line.trim();
7
+ if (!trimmed) {
8
+ return null;
9
+ }
10
+ const match = trimmed.match(/^(\d+)\s+(\d+)\s+(\S+)\s+(.*)$/);
11
+ if (!match) {
12
+ return null;
13
+ }
14
+ const pid = Number(match[1]);
15
+ const ppid = Number(match[2]);
16
+ if (!Number.isFinite(pid) || !Number.isFinite(ppid)) {
17
+ return null;
18
+ }
19
+ return {
20
+ pid,
21
+ ppid,
22
+ tty: match[3],
23
+ command: match[4]
24
+ };
25
+ }
26
+ async function listProcesses() {
27
+ return await new Promise((resolve, reject) => {
28
+ const child = (0, node_child_process_1.spawn)("ps", ["-axo", "pid=,ppid=,tty=,command=", "-ww"]);
29
+ let stdout = "";
30
+ let stderr = "";
31
+ if (child.stdout) {
32
+ child.stdout.on("data", (data) => {
33
+ stdout += data.toString();
34
+ });
35
+ }
36
+ if (child.stderr) {
37
+ child.stderr.on("data", (data) => {
38
+ stderr += data.toString();
39
+ });
40
+ }
41
+ child.on("error", (error) => {
42
+ reject(error);
43
+ });
44
+ child.on("close", (code) => {
45
+ const exitCode = code ?? 0;
46
+ if (exitCode !== 0) {
47
+ reject(new Error(stderr.trim() || stdout.trim() || `ps exited with ${exitCode}`));
48
+ return;
49
+ }
50
+ const entries = [];
51
+ for (const line of stdout.split(/\r?\n/)) {
52
+ const parsed = parseLine(line);
53
+ if (parsed) {
54
+ entries.push(parsed);
55
+ }
56
+ }
57
+ resolve(entries);
58
+ });
59
+ });
60
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TargetResolutionError = void 0;
4
+ class TargetResolutionError extends Error {
5
+ constructor(options) {
6
+ super(options.message);
7
+ this.targetKind = options.targetKind;
8
+ this.kind = options.kind;
9
+ this.target = options.target;
10
+ this.candidates = options.candidates;
11
+ }
12
+ }
13
+ exports.TargetResolutionError = TargetResolutionError;
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolvePaneTarget = resolvePaneTarget;
4
+ const errors_1 = require("./errors");
5
+ function isNumeric(value) {
6
+ return /^\d+$/.test(value);
7
+ }
8
+ const COMPOSITE_REGEX = /^(@[^%]+)(%[^%]+)$/;
9
+ function resolvePaneTarget(panes, target, options = {}) {
10
+ const compositeMatch = target.match(COMPOSITE_REGEX);
11
+ if (compositeMatch) {
12
+ const [, windowId, paneId] = compositeMatch;
13
+ const matches = panes.filter((pane) => pane.id === paneId);
14
+ const inWindow = matches.find((pane) => pane.windowId === windowId);
15
+ if (!inWindow) {
16
+ throw new errors_1.TargetResolutionError({
17
+ targetKind: "pane",
18
+ kind: "invalid",
19
+ target,
20
+ message: `pane ${paneId} is not in window ${windowId}`
21
+ });
22
+ }
23
+ return { id: inWindow.id, idx: inWindow.idx };
24
+ }
25
+ if (target.startsWith("%")) {
26
+ const matches = panes.filter((pane) => pane.id === target);
27
+ if (matches.length === 0) {
28
+ throw new errors_1.TargetResolutionError({
29
+ targetKind: "pane",
30
+ kind: "not_found",
31
+ target,
32
+ message: `pane target "${target}" not found`
33
+ });
34
+ }
35
+ return { id: matches[0].id, idx: matches[0].idx };
36
+ }
37
+ if (!options.windowId) {
38
+ throw new errors_1.TargetResolutionError({
39
+ targetKind: "pane",
40
+ kind: "invalid",
41
+ target,
42
+ message: "window scope is required for IDX/keyword targets"
43
+ });
44
+ }
45
+ const scoped = panes.filter((pane) => pane.windowId === options.windowId);
46
+ if (isNumeric(target)) {
47
+ const idx = Number(target);
48
+ const matches = scoped.filter((pane) => pane.idx === idx);
49
+ if (matches.length === 0) {
50
+ throw new errors_1.TargetResolutionError({
51
+ targetKind: "pane",
52
+ kind: "not_found",
53
+ target,
54
+ message: `pane target "${target}" not found`
55
+ });
56
+ }
57
+ if (matches.length > 1) {
58
+ throw new errors_1.TargetResolutionError({
59
+ targetKind: "pane",
60
+ kind: "ambiguous",
61
+ target,
62
+ message: `target "${target}" matched ${matches.length} panes; use IDX or %pane_id`,
63
+ candidates: matches
64
+ });
65
+ }
66
+ return { id: matches[0].id, idx: matches[0].idx };
67
+ }
68
+ const query = target.toLowerCase();
69
+ const matches = scoped.filter((pane) => {
70
+ return (pane.command.toLowerCase().includes(query) ||
71
+ pane.title.toLowerCase().includes(query));
72
+ });
73
+ if (matches.length === 0) {
74
+ throw new errors_1.TargetResolutionError({
75
+ targetKind: "pane",
76
+ kind: "not_found",
77
+ target,
78
+ message: `pane target "${target}" not found`
79
+ });
80
+ }
81
+ if (matches.length > 1) {
82
+ throw new errors_1.TargetResolutionError({
83
+ targetKind: "pane",
84
+ kind: "ambiguous",
85
+ target,
86
+ message: `target "${target}" matched ${matches.length} panes; use IDX or %pane_id`,
87
+ candidates: matches
88
+ });
89
+ }
90
+ return { id: matches[0].id, idx: matches[0].idx };
91
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveWindowTarget = resolveWindowTarget;
4
+ const errors_1 = require("./errors");
5
+ function isNumeric(value) {
6
+ return /^\d+$/.test(value);
7
+ }
8
+ function resolveWindowTarget(windows, target) {
9
+ let matches = [];
10
+ if (target.startsWith("@")) {
11
+ matches = windows.filter((window) => window.wid === target);
12
+ }
13
+ else if (isNumeric(target)) {
14
+ const index = Number(target);
15
+ matches = windows.filter((window) => window.widx === index);
16
+ }
17
+ else {
18
+ const query = target.toLowerCase();
19
+ matches = windows.filter((window) => window.name.toLowerCase().includes(query));
20
+ }
21
+ if (matches.length === 0) {
22
+ throw new errors_1.TargetResolutionError({
23
+ targetKind: "window",
24
+ kind: "not_found",
25
+ target,
26
+ message: `window target "${target}" not found`
27
+ });
28
+ }
29
+ if (matches.length > 1) {
30
+ throw new errors_1.TargetResolutionError({
31
+ targetKind: "window",
32
+ kind: "ambiguous",
33
+ target,
34
+ message: `target "${target}" matched ${matches.length} windows; use WIDX or @window_id`,
35
+ candidates: matches
36
+ });
37
+ }
38
+ const match = matches[0];
39
+ return { widx: match.widx, wid: match.wid, name: match.name };
40
+ }
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveSessionScope = resolveSessionScope;
4
+ exports.resolveWindowScope = resolveWindowScope;
5
+ const snapshotWindows_1 = require("../tmux/snapshotWindows");
6
+ const exec_1 = require("../tmux/exec");
7
+ const errors_1 = require("./errors");
8
+ const resolveWindowTarget_1 = require("./resolveWindowTarget");
9
+ async function resolveSessionScope(sessionArg, env = process.env) {
10
+ if (sessionArg) {
11
+ return { session: sessionArg, source: "explicit" };
12
+ }
13
+ if (env.TMUX) {
14
+ const result = await (0, exec_1.tmuxExec)([
15
+ "display-message",
16
+ "-p",
17
+ "#{session_name}"
18
+ ]);
19
+ const session = result.stdout.trim();
20
+ if (!session) {
21
+ throw new Error("unable to resolve current session");
22
+ }
23
+ return { session, source: "tmux" };
24
+ }
25
+ const result = await (0, exec_1.tmuxExec)(["list-sessions", "-F", "#{session_name}"]);
26
+ const sessions = result.stdout
27
+ .trim()
28
+ .split(/\r?\n/)
29
+ .map((line) => line.trim())
30
+ .filter(Boolean);
31
+ if (sessions.length === 0) {
32
+ throw new Error("no tmux sessions found");
33
+ }
34
+ if (sessions.length > 1) {
35
+ throw new Error("default session is ambiguous; use --session <name>");
36
+ }
37
+ return { session: sessions[0], source: "unique" };
38
+ }
39
+ async function resolveWindowScope(session, windowTarget) {
40
+ const windows = await (0, snapshotWindows_1.snapshotWindows)(session);
41
+ if (windows.length === 0) {
42
+ throw new Error(`session ${session} has no windows`);
43
+ }
44
+ if (windowTarget) {
45
+ return (0, resolveWindowTarget_1.resolveWindowTarget)(windows, windowTarget);
46
+ }
47
+ const active = windows.find((window) => window.status === "active");
48
+ if (!active) {
49
+ throw new errors_1.TargetResolutionError({
50
+ targetKind: "window",
51
+ kind: "not_found",
52
+ target: "active",
53
+ message: "no active window found",
54
+ candidates: windows
55
+ });
56
+ }
57
+ return { widx: active.widx, wid: active.wid, name: active.name };
58
+ }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.capturePane = capturePane;
4
+ exports.splitLines = splitLines;
5
+ const exec_1 = require("./exec");
6
+ async function capturePane(paneId, lines) {
7
+ const args = ["capture-pane", "-p", "-t", paneId];
8
+ if (Number.isFinite(lines) && lines > 0) {
9
+ args.push("-S", `-${lines}`);
10
+ }
11
+ const result = await (0, exec_1.tmuxExec)(args);
12
+ return result.stdout.replace(/\r?\n$/, "");
13
+ }
14
+ function splitLines(text) {
15
+ const lines = text.split(/\r?\n/);
16
+ if (lines.length && lines[lines.length - 1] === "") {
17
+ lines.pop();
18
+ }
19
+ return lines;
20
+ }
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TmuxExecError = void 0;
4
+ exports.tmuxExec = tmuxExec;
5
+ const node_child_process_1 = require("node:child_process");
6
+ class TmuxExecError extends Error {
7
+ constructor(message, result) {
8
+ super(message);
9
+ this.stdout = result.stdout;
10
+ this.stderr = result.stderr;
11
+ this.exitCode = result.exitCode;
12
+ }
13
+ }
14
+ exports.TmuxExecError = TmuxExecError;
15
+ function getServerArgs(env) {
16
+ const server = env.AGENT_TMUX_SERVER || env.AGENT_TMUX_SOCKET;
17
+ if (!server) {
18
+ return [];
19
+ }
20
+ return ["-L", server];
21
+ }
22
+ async function tmuxExec(args, options = {}) {
23
+ const baseEnv = options.env ?? process.env;
24
+ const serverArgs = getServerArgs(baseEnv);
25
+ const env = serverArgs.length > 0 ? { ...baseEnv } : baseEnv;
26
+ if (serverArgs.length > 0 && "TMUX" in env) {
27
+ delete env.TMUX;
28
+ }
29
+ const finalArgs = [...serverArgs, ...args];
30
+ return await new Promise((resolve, reject) => {
31
+ const child = (0, node_child_process_1.spawn)("tmux", finalArgs, { env });
32
+ let stdout = "";
33
+ let stderr = "";
34
+ if (child.stdout) {
35
+ child.stdout.on("data", (data) => {
36
+ stdout += data.toString();
37
+ });
38
+ }
39
+ if (child.stderr) {
40
+ child.stderr.on("data", (data) => {
41
+ stderr += data.toString();
42
+ });
43
+ }
44
+ child.on("error", (error) => {
45
+ reject(new TmuxExecError(error.message, {
46
+ stdout,
47
+ stderr,
48
+ exitCode: 1
49
+ }));
50
+ });
51
+ child.on("close", (code) => {
52
+ const exitCode = code ?? 0;
53
+ const result = { stdout, stderr, exitCode };
54
+ if (exitCode === 0) {
55
+ resolve(result);
56
+ return;
57
+ }
58
+ const message = stderr.trim() || stdout.trim() || `tmux exited with ${exitCode}`;
59
+ reject(new TmuxExecError(message, result));
60
+ });
61
+ if (options.input !== undefined) {
62
+ child.stdin?.write(options.input);
63
+ child.stdin?.end();
64
+ }
65
+ });
66
+ }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.paneSpawn = paneSpawn;
4
+ exports.paneTitle = paneTitle;
5
+ exports.paneKill = paneKill;
6
+ const exec_1 = require("./exec");
7
+ async function paneSpawn(windowId) {
8
+ const args = [
9
+ "split-window",
10
+ "-P",
11
+ "-F",
12
+ "#{pane_id}\t#{pane_index}",
13
+ "-t",
14
+ windowId,
15
+ "-d"
16
+ ];
17
+ const result = await (0, exec_1.tmuxExec)(args);
18
+ const [id, idxRaw] = result.stdout.trim().split("\t");
19
+ return {
20
+ id,
21
+ idx: Number(idxRaw)
22
+ };
23
+ }
24
+ async function paneTitle(paneId, title) {
25
+ await (0, exec_1.tmuxExec)(["select-pane", "-t", paneId, "-T", title]);
26
+ }
27
+ async function paneKill(paneId) {
28
+ await (0, exec_1.tmuxExec)(["kill-pane", "-t", paneId]);
29
+ }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pasteText = pasteText;
4
+ const exec_1 = require("./exec");
5
+ function createBufferName() {
6
+ const rand = Math.random().toString(36).slice(2, 10);
7
+ return `__agent_tmux_${process.pid}_${Date.now()}_${rand}`;
8
+ }
9
+ async function pasteText(paneId, text) {
10
+ const bufferName = createBufferName();
11
+ await (0, exec_1.tmuxExec)(["load-buffer", "-b", bufferName, "-"], { input: text });
12
+ try {
13
+ await (0, exec_1.tmuxExec)(["paste-buffer", "-p", "-d", "-b", bufferName, "-t", paneId]);
14
+ }
15
+ finally {
16
+ try {
17
+ await (0, exec_1.tmuxExec)(["delete-buffer", "-b", bufferName]);
18
+ }
19
+ catch {
20
+ // ignore best-effort cleanup failures
21
+ }
22
+ }
23
+ }