agileflow 4.0.0-alpha.10 → 4.0.0-alpha.11
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/package.json +1 -1
- package/src/cli/commands/launch.js +61 -25
- package/src/runtime/launch/tabs.js +12 -4
package/package.json
CHANGED
|
@@ -980,17 +980,40 @@ async function runWhere() {
|
|
|
980
980
|
async function runInternalCloseWindow(deps = {}) {
|
|
981
981
|
const runner = deps.runner || defaultTmuxRunner();
|
|
982
982
|
const pushClosedImpl = deps.pushClosedImpl || closedWindows.pushClosed;
|
|
983
|
+
const exit = deps.exit || ((code) => process.exit(code));
|
|
983
984
|
// ASCII Unit Separator — never appears in a session/window name or
|
|
984
985
|
// filesystem path, so splitting on it is unambiguous.
|
|
985
986
|
const DELIM = "\x1f";
|
|
986
|
-
|
|
987
|
-
|
|
987
|
+
// When the tmux keybind passes session+index positionally, target
|
|
988
|
+
// that exact window. This avoids a wrong-window kill if focus shifts
|
|
989
|
+
// between Alt+w being pressed and this subprocess starting.
|
|
990
|
+
const argSession = (deps.targetSession || "").trim();
|
|
991
|
+
const argIndex = (deps.targetIndex || "").trim();
|
|
992
|
+
let probeArgs;
|
|
993
|
+
if (argSession && argIndex) {
|
|
994
|
+
probeArgs = [
|
|
995
|
+
"display-message",
|
|
996
|
+
"-p",
|
|
997
|
+
"-t",
|
|
998
|
+
`${argSession}:${argIndex}`,
|
|
999
|
+
"-F",
|
|
1000
|
+
`#S${DELIM}#I${DELIM}#W${DELIM}#{pane_current_path}`,
|
|
1001
|
+
];
|
|
1002
|
+
} else {
|
|
1003
|
+
probeArgs = [
|
|
1004
|
+
"display-message",
|
|
1005
|
+
"-p",
|
|
1006
|
+
"-F",
|
|
1007
|
+
`#S${DELIM}#I${DELIM}#W${DELIM}#{pane_current_path}`,
|
|
1008
|
+
];
|
|
1009
|
+
}
|
|
1010
|
+
const probe = runner.runSync(probeArgs);
|
|
988
1011
|
if (probe.status !== 0) {
|
|
989
1012
|
// eslint-disable-next-line no-console
|
|
990
1013
|
console.error(
|
|
991
1014
|
`agileflow launch __close-window: tmux display-message failed: ${probe.stderr || "unknown"}`,
|
|
992
1015
|
);
|
|
993
|
-
return;
|
|
1016
|
+
return exit(1);
|
|
994
1017
|
}
|
|
995
1018
|
const parts = (probe.stdout || "").trimEnd().split(DELIM);
|
|
996
1019
|
if (parts.length !== 4) {
|
|
@@ -998,7 +1021,7 @@ async function runInternalCloseWindow(deps = {}) {
|
|
|
998
1021
|
console.error(
|
|
999
1022
|
`agileflow launch __close-window: unexpected display-message output (got ${parts.length} fields)`,
|
|
1000
1023
|
);
|
|
1001
|
-
return;
|
|
1024
|
+
return exit(1);
|
|
1002
1025
|
}
|
|
1003
1026
|
const [sessionName, windowIndex, windowName, cwd] = parts;
|
|
1004
1027
|
if (!sessionName || !windowIndex || !cwd) {
|
|
@@ -1006,7 +1029,7 @@ async function runInternalCloseWindow(deps = {}) {
|
|
|
1006
1029
|
console.error(
|
|
1007
1030
|
"agileflow launch __close-window: missing session/index/cwd; skipping kill",
|
|
1008
1031
|
);
|
|
1009
|
-
return;
|
|
1032
|
+
return exit(1);
|
|
1010
1033
|
}
|
|
1011
1034
|
// Kill first with the explicit target captured above. If this fails
|
|
1012
1035
|
// we abort without touching the log — the window is still alive and
|
|
@@ -1021,7 +1044,7 @@ async function runInternalCloseWindow(deps = {}) {
|
|
|
1021
1044
|
console.error(
|
|
1022
1045
|
`agileflow launch __close-window: kill-window failed: ${kill.stderr || "unknown"}`,
|
|
1023
1046
|
);
|
|
1024
|
-
return;
|
|
1047
|
+
return exit(1);
|
|
1025
1048
|
}
|
|
1026
1049
|
try {
|
|
1027
1050
|
pushClosedImpl({ sessionName, name: windowName || "", cwd });
|
|
@@ -1053,16 +1076,23 @@ async function runInternalRestoreWindow(deps = {}) {
|
|
|
1053
1076
|
const runner = deps.runner || defaultTmuxRunner();
|
|
1054
1077
|
const popClosedImpl = deps.popClosedImpl || closedWindows.popClosed;
|
|
1055
1078
|
const pushClosedImpl = deps.pushClosedImpl || closedWindows.pushClosed;
|
|
1056
|
-
const
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1079
|
+
const exit = deps.exit || ((code) => process.exit(code));
|
|
1080
|
+
// Keybind passes #{session_name} so the restore targets the session
|
|
1081
|
+
// the user actually pressed Alt+T from. Fall back to display-message
|
|
1082
|
+
// for manual invocations (which only works inside tmux).
|
|
1083
|
+
let sessionName = (deps.targetSession || "").trim();
|
|
1084
|
+
if (!sessionName) {
|
|
1085
|
+
const probe = runner.runSync(["display-message", "-p", "-F", "#S"]);
|
|
1086
|
+
if (probe.status !== 0) {
|
|
1087
|
+
// eslint-disable-next-line no-console
|
|
1088
|
+
console.error(
|
|
1089
|
+
`agileflow launch __restore-window: tmux display-message failed: ${probe.stderr || "unknown"}`,
|
|
1090
|
+
);
|
|
1091
|
+
return exit(1);
|
|
1092
|
+
}
|
|
1093
|
+
sessionName = (probe.stdout || "").trim();
|
|
1063
1094
|
}
|
|
1064
|
-
|
|
1065
|
-
if (!sessionName) return;
|
|
1095
|
+
if (!sessionName) return exit(1);
|
|
1066
1096
|
/** @type {ReturnType<typeof closedWindows.popClosed>} */
|
|
1067
1097
|
let entry;
|
|
1068
1098
|
try {
|
|
@@ -1072,7 +1102,7 @@ async function runInternalRestoreWindow(deps = {}) {
|
|
|
1072
1102
|
console.error(
|
|
1073
1103
|
`agileflow launch __restore-window: log pop failed: ${err && err.message ? err.message : err}`,
|
|
1074
1104
|
);
|
|
1075
|
-
return;
|
|
1105
|
+
return exit(1);
|
|
1076
1106
|
}
|
|
1077
1107
|
if (!entry) {
|
|
1078
1108
|
// Empty stack — silent. The user pressed Alt+T with nothing to undo.
|
|
@@ -1102,6 +1132,7 @@ async function runInternalRestoreWindow(deps = {}) {
|
|
|
1102
1132
|
`agileflow launch __restore-window: failed to re-push entry after new-window failure: ${pushErr && pushErr.message ? pushErr.message : pushErr}`,
|
|
1103
1133
|
);
|
|
1104
1134
|
}
|
|
1135
|
+
return exit(1);
|
|
1105
1136
|
}
|
|
1106
1137
|
}
|
|
1107
1138
|
|
|
@@ -1269,19 +1300,24 @@ async function launch(sub, nameArg, _options) {
|
|
|
1269
1300
|
return;
|
|
1270
1301
|
}
|
|
1271
1302
|
if (sub === "__close-window") {
|
|
1272
|
-
// Hidden subcommand invoked from tmux keybind (Alt+w).
|
|
1273
|
-
//
|
|
1274
|
-
//
|
|
1275
|
-
//
|
|
1276
|
-
|
|
1303
|
+
// Hidden subcommand invoked from tmux keybind (Alt+w). The keybind
|
|
1304
|
+
// passes session name + window index as positional args so we
|
|
1305
|
+
// target the exact tab the user pressed Alt+w on, regardless of
|
|
1306
|
+
// any focus shift during the confirmation prompt. nameArg is the
|
|
1307
|
+
// session name; we read the window index from raw argv since
|
|
1308
|
+
// commander's signature only declares two positionals.
|
|
1309
|
+
const targetSession = nameArg || "";
|
|
1310
|
+
const targetIndex = (process.argv && process.argv[5]) || "";
|
|
1311
|
+
await runInternalCloseWindow({ targetSession, targetIndex });
|
|
1277
1312
|
return;
|
|
1278
1313
|
}
|
|
1279
1314
|
if (sub === "__restore-window") {
|
|
1280
1315
|
// Hidden subcommand invoked from tmux keybind (Alt+T). Pops the
|
|
1281
|
-
// most recent closed entry for the
|
|
1282
|
-
//
|
|
1283
|
-
//
|
|
1284
|
-
|
|
1316
|
+
// most recent closed entry for the session the user pressed
|
|
1317
|
+
// Alt+T from (passed positionally via #{session_name}) and
|
|
1318
|
+
// spawns a new window in that cwd with the original name. No-op
|
|
1319
|
+
// when the log is empty for this session.
|
|
1320
|
+
await runInternalRestoreWindow({ targetSession: nameArg || "" });
|
|
1285
1321
|
return;
|
|
1286
1322
|
}
|
|
1287
1323
|
if (sub && sub !== "setup") {
|
|
@@ -272,14 +272,16 @@ const TAB_KEYBINDS = [
|
|
|
272
272
|
},
|
|
273
273
|
{
|
|
274
274
|
// confirm-before runs the command on `y` and does nothing on `n`.
|
|
275
|
-
//
|
|
276
|
-
//
|
|
275
|
+
// We pass session+index as positional args so the callback targets
|
|
276
|
+
// the exact window the user pressed Alt+w on — without this, the
|
|
277
|
+
// callback would re-probe display-message and could close the
|
|
278
|
+
// wrong tab if focus moved during the confirmation prompt.
|
|
277
279
|
key: "M-w",
|
|
278
280
|
action: [
|
|
279
281
|
"confirm-before",
|
|
280
282
|
"-p",
|
|
281
283
|
"kill tab #W? (y/n)",
|
|
282
|
-
"run-shell '%AGILEFLOW% launch __close-window'",
|
|
284
|
+
"run-shell '%AGILEFLOW% launch __close-window #{session_name} #{window_index}'",
|
|
283
285
|
],
|
|
284
286
|
hint: "Alt+w → close current tab (with confirm)",
|
|
285
287
|
},
|
|
@@ -291,8 +293,14 @@ const TAB_KEYBINDS = [
|
|
|
291
293
|
hint: "Alt+W → tab picker",
|
|
292
294
|
},
|
|
293
295
|
{
|
|
296
|
+
// Pass session name explicitly so the callback restores into the
|
|
297
|
+
// session the user actually triggered from — works even if the
|
|
298
|
+
// active session shifts before run-shell fires.
|
|
294
299
|
key: "M-T",
|
|
295
|
-
action: [
|
|
300
|
+
action: [
|
|
301
|
+
"run-shell",
|
|
302
|
+
"%AGILEFLOW% launch __restore-window #{session_name}",
|
|
303
|
+
],
|
|
296
304
|
hint: "Alt+T → reopen last closed tab",
|
|
297
305
|
},
|
|
298
306
|
// Numeric switchers Alt+1..Alt+9 → select-window -t :N
|