agileflow 4.0.0-alpha.14 → 4.0.0-alpha.16

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.14",
3
+ "version": "4.0.0-alpha.16",
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",
@@ -203,47 +203,80 @@ function applyTabFormat(sessionName, runner, opts = {}) {
203
203
  // ignores invalid status-format expressions on some versions, which
204
204
  // makes overrides hard to debug. window-status-format works on every
205
205
  // tmux 2.0+.
206
- const inactiveFormat = `#[fg=${theme.inactiveFg} bg=${theme.stripBg}] #I:#W `;
206
+ // jakobwesthoff/tmux-from-scratch inspired styling: active tab is a
207
+ // rounded "pill" (Powerline half-round glyphs) over a lighter
208
+ // background, with brand-orange accent on the index:name separator
209
+ // colon. Inactive tabs are plain text with the same accent colon.
210
+ // Session name appears as a rounded pill on the left, hostname as
211
+ // a rounded pill on the right.
212
+ //
213
+ // Requires a Nerd Font / Powerline-patched font in the user's
214
+ // terminal. Without one the glyphs render as tofu boxes; users on
215
+ // a vanilla font can switch back via a future `tabStyle: "flat"`
216
+ // pref.
217
+ const HALF_ROUND_OPEN = ""; //
218
+ const HALF_ROUND_CLOSE = ""; //
219
+ const TRIANGLE_OPEN = ""; //
220
+ const TRIANGLE_CLOSE = ""; //
221
+ const BG = theme.stripBg;
222
+ const PILL_BG = theme.activeNameBg;
223
+ const ACCENT = theme.activeBg;
224
+ const FG = theme.activeNameFg;
225
+
226
+ // Active tab uses the BRAND ACCENT (orange) as its pill background
227
+ // with the strip's dark color as text — maximum contrast against
228
+ // the muted inactive tabs so the user always knows which tab is
229
+ // focused after Alt+1..9 / Alt+Tab.
230
+ const inactiveFormat = ` #I:#W `;
207
231
  const activeFormat =
208
- `#[fg=${theme.activeFg} bg=${theme.activeBg} bold] #I ` +
209
- `#[fg=${theme.activeBg} bg=${theme.activeNameBg}]` +
210
- `#[fg=${theme.activeNameFg} bg=${theme.activeNameBg}] #W ` +
211
- `#[fg=${theme.activeNameBg} bg=${theme.stripBg}]`;
212
- // status-style sets the row's base background so empty space between
213
- // chips matches the strip color (no green leak from tmux's default).
214
- runner.runSync([
215
- "set-option",
216
- "-t",
217
- sessionName,
218
- "status-style",
219
- `bg=${theme.stripBg} fg=${theme.inactiveFg}`,
220
- ]);
221
- runner.runSync(["set-option", "-t", sessionName, "status-left", ""]);
222
- runner.runSync(["set-option", "-t", sessionName, "status-right", ""]);
223
- runner.runSync([
224
- "set-option",
225
- "-t",
226
- sessionName,
227
- "window-status-separator",
228
- "",
229
- ]);
230
- runner.runSync([
231
- "set-option",
232
- "-t",
233
- sessionName,
234
- "window-status-format",
235
- inactiveFormat,
236
- ]);
237
- const result = runner.runSync([
238
- "set-option",
239
- "-t",
240
- sessionName,
241
- "window-status-current-format",
242
- activeFormat,
243
- ]);
232
+ `#[fg=${ACCENT},bg=${BG}]${HALF_ROUND_OPEN}` +
233
+ `#[bg=${ACCENT},fg=${theme.activeFg},bold] #I:#W ` +
234
+ `#[fg=${ACCENT},bg=${BG}]${HALF_ROUND_CLOSE}`;
235
+ const statusLeft =
236
+ `#[fg=${PILL_BG},bg=${BG}]${HALF_ROUND_OPEN}` +
237
+ `#[bg=${PILL_BG},fg=${ACCENT}] #S ` +
238
+ `#[fg=${PILL_BG},bg=${BG}]${TRIANGLE_CLOSE}`;
239
+ const statusRight =
240
+ `#[fg=${PILL_BG},bg=${BG}]${TRIANGLE_OPEN}` +
241
+ `#[bg=${PILL_BG},fg=${ACCENT}] #h ` +
242
+ `#[fg=${PILL_BG},bg=${BG}]${HALF_ROUND_CLOSE}`;
243
+
244
+ // Apply each option and collect failures. If ANYTHING fails we surface
245
+ // it on stderr so users debugging "why doesn't my tab strip look right"
246
+ // can see the tmux complaint instead of staring at default formatting.
247
+ const ops = [
248
+ ["status-style", `bg=${BG},fg=${theme.inactiveFg}`],
249
+ ["status-justify", "centre"],
250
+ ["status-left", statusLeft],
251
+ ["status-left-length", "100"],
252
+ ["status-right", statusRight],
253
+ ["status-right-length", "100"],
254
+ ["window-status-separator", ""],
255
+ ["window-status-format", inactiveFormat],
256
+ ["window-status-current-format", activeFormat],
257
+ ];
258
+ let lastResult = { status: 0, stderr: "" };
259
+ const failures = [];
260
+ for (const [option, value] of ops) {
261
+ const r = runner.runSync(["set-option", "-t", sessionName, option, value]);
262
+ lastResult = r;
263
+ if (r.status !== 0) {
264
+ failures.push({ option, stderr: (r.stderr || "").trim() });
265
+ }
266
+ }
267
+ if (failures.length > 0) {
268
+ // eslint-disable-next-line no-console
269
+ console.error(
270
+ `agileflow launch: tab strip styling — ${failures.length} of ${ops.length} options rejected by tmux:`,
271
+ );
272
+ for (const f of failures) {
273
+ // eslint-disable-next-line no-console
274
+ console.error(` ${f.option}: ${f.stderr || "(no error message)"}`);
275
+ }
276
+ }
244
277
  return {
245
- applied: result.status === 0,
246
- stderr: result.stderr || "",
278
+ applied: lastResult.status === 0,
279
+ stderr: lastResult.stderr || "",
247
280
  };
248
281
  }
249
282
 
@@ -349,15 +382,15 @@ const KEYBIND_PRESET_BINDINGS = {
349
382
  },
350
383
  {
351
384
  // Prompt for a worktree name, then spawn. tmux's command-prompt
352
- // substitutes %% with the user's input. Single-quoting the
353
- // run-shell command keeps the shell parser happy when the name
354
- // contains odd characters; the inner double-quotes around %%
355
- // protect against shell word-splitting.
385
+ // natively cancels on Escape (or Ctrl+G) without firing the
386
+ // deferred command the prompt label calls that out so users
387
+ // know they can back out. The agileflow CLI also bails cleanly
388
+ // if `%%` came in empty (user hit Enter with no input).
356
389
  key: "M-n",
357
390
  action: [
358
391
  "command-prompt",
359
392
  "-p",
360
- "worktree name:",
393
+ "worktree name (esc to cancel):",
361
394
  "run-shell '%AGILEFLOW% launch new \"%%\"'",
362
395
  ],
363
396
  hint: "Alt+n → prompt for a name, create a worktree, spawn there",