@heyhuynhgiabuu/pi-task 0.1.3 → 0.1.4

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/CHANGELOG.md CHANGED
@@ -4,6 +4,29 @@ All notable changes to `@heyhuynhgiabuu/pi-task` are documented here.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [0.1.4] — 2026-06-21
8
+
9
+ ### Fixed
10
+
11
+ - Detect the current tmux pane size before launching a task pane and choose
12
+ the split direction based on available space: side-by-side for wide panes,
13
+ stacked for narrow panes.
14
+ - Target the exact pane that was measured when running `tmux split-window`,
15
+ avoiding focus races where a different pane could be split.
16
+ - Apply the same pane-size-aware split logic to the subagent tmux helper.
17
+
18
+ ### Verified
19
+
20
+ - `npm test` passes
21
+ - `npm run typecheck` passes
22
+ - `npm run build` passes
23
+ - `npm run smoke` passes
24
+ - `npm pack --dry-run` succeeds
25
+ - Real tmux integration check passed for narrow `120x40` and wide `200x40`
26
+ sessions.
27
+
28
+ [0.1.4]: https://github.com/heyhuynhgiabuu/pi-task/releases/tag/v0.1.4
29
+
7
30
  ## [0.1.3] — 2026-06-21
8
31
 
9
32
  ### Fixed
package/dist/helpers.d.ts CHANGED
@@ -50,7 +50,9 @@ export declare function parseResultXml(raw: string): ParsedResult;
50
50
  export declare function formatMs(ms: number): string;
51
51
  export declare function parseIdTimestamp(id: string): number;
52
52
  export declare function shellQuote(value: string): string;
53
- export declare function buildTmuxSplitWindowArgs(cwd: string, command: string): string[];
53
+ export type TmuxSplitDirection = "-h" | "-v";
54
+ export declare function chooseTmuxSplitDirection(paneWidth: number, paneHeight: number): TmuxSplitDirection;
55
+ export declare function buildTmuxSplitWindowArgs(cwd: string, command: string, direction?: TmuxSplitDirection, targetPane?: string | null): string[];
54
56
  export interface BackgroundReceiptInput {
55
57
  taskId: string;
56
58
  agentType: string;
package/dist/helpers.js CHANGED
@@ -128,8 +128,29 @@ export function parseIdTimestamp(id) {
128
128
  export function shellQuote(value) {
129
129
  return `'${value.replace(/'/g, `'"'"'`)}'`;
130
130
  }
131
- export function buildTmuxSplitWindowArgs(cwd, command) {
132
- return ["split-window", "-h", "-P", "-F", "#{pane_id}", "-c", cwd, command];
131
+ export function chooseTmuxSplitDirection(paneWidth, paneHeight) {
132
+ const minSideBySideWidth = 160;
133
+ const minStackedHeight = 24;
134
+ if (Number.isFinite(paneWidth) && paneWidth >= minSideBySideWidth) {
135
+ return "-h";
136
+ }
137
+ if (Number.isFinite(paneHeight) && paneHeight >= minStackedHeight) {
138
+ return "-v";
139
+ }
140
+ return "-h";
141
+ }
142
+ export function buildTmuxSplitWindowArgs(cwd, command, direction = "-h", targetPane) {
143
+ const args = [
144
+ "split-window",
145
+ direction,
146
+ "-P",
147
+ "-F",
148
+ "#{pane_id}",
149
+ ];
150
+ if (targetPane)
151
+ args.push("-t", targetPane);
152
+ args.push("-c", cwd, command);
153
+ return args;
133
154
  }
134
155
  export function formatBackgroundReceipt(input) {
135
156
  return [
package/dist/index.js CHANGED
@@ -22,7 +22,7 @@ import { dirname, join } from "node:path";
22
22
  import { fileURLToPath } from "node:url";
23
23
  import { Type } from "@sinclair/typebox";
24
24
  import { Text, truncateToWidth } from "@earendil-works/pi-tui";
25
- import { TASK_BACKGROUND_DEFAULT, TASK_RESULT_XML_INSTRUCTIONS, TASK_TOOL_DESCRIPTION, buildTmuxSplitWindowArgs, formatBackgroundReceipt, buildPiArgs, parseResultXml, formatMs, shellQuote, discoverAgents, formatAgentList, countToolUses, readRecentToolCalls, } from "./helpers.js";
25
+ import { TASK_BACKGROUND_DEFAULT, TASK_RESULT_XML_INSTRUCTIONS, TASK_TOOL_DESCRIPTION, buildTmuxSplitWindowArgs, chooseTmuxSplitDirection, formatBackgroundReceipt, buildPiArgs, parseResultXml, formatMs, shellQuote, discoverAgents, formatAgentList, countToolUses, readRecentToolCalls, } from "./helpers.js";
26
26
  import { runSdkSubagent } from "./subagent/runSdk.js";
27
27
  import { checkTaskCompletion, waitForTaskCompletion as waitForSessionTaskCompletion, } from "./subagent/waitCompletion.js";
28
28
  import { buildAgentToolSelection } from "./agent-tools.js";
@@ -79,9 +79,28 @@ function getCurrentPaneId() {
79
79
  return null;
80
80
  }
81
81
  }
82
+ function getCurrentPaneSize(targetPane) {
83
+ try {
84
+ const args = ["display-message", "-p", "#{pane_width} #{pane_height}"];
85
+ if (targetPane)
86
+ args.splice(1, 0, "-t", targetPane);
87
+ const raw = tmuxCmd(args);
88
+ const [widthRaw, heightRaw] = raw.trim().split(/\s+/, 2);
89
+ const width = Number(widthRaw);
90
+ const height = Number(heightRaw);
91
+ if (!Number.isFinite(width) || !Number.isFinite(height))
92
+ return null;
93
+ return { width, height };
94
+ }
95
+ catch {
96
+ return null;
97
+ }
98
+ }
82
99
  function splitWindowPane(cwd, command) {
83
100
  const originalPane = getCurrentPaneId();
84
- const paneId = tmuxCmd(buildTmuxSplitWindowArgs(cwd, command));
101
+ const paneSize = getCurrentPaneSize(originalPane);
102
+ const direction = chooseTmuxSplitDirection(paneSize?.width ?? 0, paneSize?.height ?? 0);
103
+ const paneId = tmuxCmd(buildTmuxSplitWindowArgs(cwd, command, direction, originalPane));
85
104
  return { paneId, originalPane };
86
105
  }
87
106
  function killAgentPane(paneId, originalPane) {
@@ -5,6 +5,10 @@ export declare function tmuxCmd(args: string[]): string;
5
5
  export declare function hasTmux(): boolean;
6
6
  export declare function paneExists(paneId: string): boolean;
7
7
  export declare function getCurrentPaneId(): string | null;
8
+ export declare function getCurrentPaneSize(targetPane?: string | null): {
9
+ width: number;
10
+ height: number;
11
+ } | null;
8
12
  export declare function splitWindowPane(cwd: string, command: string): {
9
13
  paneId: string;
10
14
  originalPane: string | null;
@@ -2,6 +2,7 @@
2
2
  * Tmux helpers for subagent panes (shared by task extension).
3
3
  */
4
4
  import { execFileSync } from "node:child_process";
5
+ import { buildTmuxSplitWindowArgs, chooseTmuxSplitDirection } from "../helpers.js";
5
6
  export function tmuxCmd(args) {
6
7
  return execFileSync("tmux", args, {
7
8
  encoding: "utf-8",
@@ -34,18 +35,28 @@ export function getCurrentPaneId() {
34
35
  return null;
35
36
  }
36
37
  }
38
+ export function getCurrentPaneSize(targetPane) {
39
+ try {
40
+ const args = ["display-message", "-p", "#{pane_width} #{pane_height}"];
41
+ if (targetPane)
42
+ args.splice(1, 0, "-t", targetPane);
43
+ const raw = tmuxCmd(args);
44
+ const [widthRaw, heightRaw] = raw.trim().split(/\s+/, 2);
45
+ const width = Number(widthRaw);
46
+ const height = Number(heightRaw);
47
+ if (!Number.isFinite(width) || !Number.isFinite(height))
48
+ return null;
49
+ return { width, height };
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ }
37
55
  export function splitWindowPane(cwd, command) {
38
56
  const originalPane = getCurrentPaneId();
39
- const paneId = tmuxCmd([
40
- "split-window",
41
- "-h",
42
- "-P",
43
- "-F",
44
- "#{pane_id}",
45
- "-c",
46
- cwd,
47
- command,
48
- ]);
57
+ const paneSize = getCurrentPaneSize(originalPane);
58
+ const direction = chooseTmuxSplitDirection(paneSize?.width ?? 0, paneSize?.height ?? 0);
59
+ const paneId = tmuxCmd(buildTmuxSplitWindowArgs(cwd, command, direction, originalPane));
49
60
  return { paneId, originalPane };
50
61
  }
51
62
  export function killAgentPane(paneId, originalPane) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyhuynhgiabuu/pi-task",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Delegating task/subagent extension for Pi: foreground/background subagents, widgets, tmux observability, SDK fallback.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",