agileflow 4.0.0-alpha.25 → 4.0.0-alpha.26

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agileflow",
3
- "version": "4.0.0-alpha.25",
3
+ "version": "4.0.0-alpha.26",
4
4
  "description": "AI-driven agile development toolkit for Claude Code — skills-first architecture with opt-in plugins (v4)",
5
5
  "keywords": [
6
6
  "agile",
@@ -983,7 +983,13 @@ async function runInternalCloseWindow(deps = {}) {
983
983
  const exit = deps.exit || ((code) => process.exit(code));
984
984
  // ASCII Unit Separator — never appears in a session/window name or
985
985
  // filesystem path, so splitting on it is unambiguous.
986
- const DELIM = "\x1f";
986
+ // tmux's format-string parser escape-encodes control bytes (0x00-0x1F)
987
+ // into the literal 4-char `\OOO` octal sequence in output, so a
988
+ // 0x1f byte delimiter comes back as the text "\037" and split()
989
+ // never finds it. Use TAB instead — it survives format-string
990
+ // processing untouched, and is impossible in tmux session/window
991
+ // names (tmux rejects them) and rare-to-impossible in cwd paths.
992
+ const DELIM = "\t";
987
993
  // When the tmux keybind passes session+index positionally, target
988
994
  // that exact window. This avoids a wrong-window kill if focus shifts
989
995
  // between Alt+w being pressed and this subprocess starting.
@@ -1153,7 +1159,13 @@ async function runInternalRestoreWindow(deps = {}) {
1153
1159
  async function runInternalSnapshotSession(sessionName, deps = {}) {
1154
1160
  if (!sessionName) return;
1155
1161
  const runner = deps.runner || defaultTmuxRunner();
1156
- const DELIM = "\x1f";
1162
+ // tmux's format-string parser escape-encodes control bytes (0x00-0x1F)
1163
+ // into the literal 4-char `\OOO` octal sequence in output, so a
1164
+ // 0x1f byte delimiter comes back as the text "\037" and split()
1165
+ // never finds it. Use TAB instead — it survives format-string
1166
+ // processing untouched, and is impossible in tmux session/window
1167
+ // names (tmux rejects them) and rare-to-impossible in cwd paths.
1168
+ const DELIM = "\t";
1157
1169
  const fmt = `#{window_index}${DELIM}#{window_name}${DELIM}#{pane_current_path}`;
1158
1170
  // Capture timestamp BEFORE list-windows so concurrent subprocesses
1159
1171
  // produce a stable ordering. The registry rejects writes whose
@@ -352,7 +352,16 @@ function installSessionHooks(sessionName, runner, opts = {}) {
352
352
  const snapshotCmd = `run-shell -b "${shellCmd.replace(/"/g, '\\"')}"`;
353
353
  /** @type {Array<{ event: string, stderr: string }>} */
354
354
  const failures = [];
355
- for (const event of ["window-linked", "window-unlinked", "window-renamed"]) {
355
+ // tmux 3.x hook names: `window-linked` and `window-unlinked` fire
356
+ // on add/remove. There is NO `window-renamed` event — that name is
357
+ // silently accepted by set-hook but never fires. The correct hook
358
+ // for rename is `after-rename-window` (the "after-<command>" family
359
+ // fires whenever the named command runs from any source).
360
+ for (const event of [
361
+ "window-linked",
362
+ "window-unlinked",
363
+ "after-rename-window",
364
+ ]) {
356
365
  const r = runner.runSync([
357
366
  "set-hook",
358
367
  "-t",