@ordewell/cli 0.4.3 → 0.4.5

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 (2) hide show
  1. package/dist/main.js +59 -11
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -3071,6 +3071,7 @@ var SLASH_COMMANDS = [
3071
3071
  { name: "delete", usage: "/delete <id>", description: "Delete a saved session", category: "session" },
3072
3072
  // System
3073
3073
  { name: "help", usage: "/help", description: "Show available commands", category: "system" },
3074
+ { name: "mouse", usage: "/mouse [on|off]", description: "Wheel scrolling instead of drag-to-select text", category: "system" },
3074
3075
  { name: "quit", usage: "/quit", description: "Exit the TUI", category: "system" }
3075
3076
  ];
3076
3077
  var BY_NAME = new Map(SLASH_COMMANDS.map((c) => [c.name, c]));
@@ -3162,6 +3163,7 @@ function initialState(overrides = {}) {
3162
3163
  configuredProviders: [],
3163
3164
  allowlist: {},
3164
3165
  autonomous: true,
3166
+ mouseCapture: false,
3165
3167
  workspace: process.cwd(),
3166
3168
  overlay: null,
3167
3169
  pendingApprovals: [],
@@ -3192,6 +3194,10 @@ function restoredMessages(history) {
3192
3194
  }));
3193
3195
  }
3194
3196
  var isPendingResearch = (m) => m.role === "research" && !m.research?.outcome;
3197
+ function alreadySpoken(messages, content) {
3198
+ const last = findLastIndex(messages, (m) => m.role === "assistant" || m.role === "user");
3199
+ return last >= 0 && messages[last].role === "assistant" && messages[last].content === content;
3200
+ }
3195
3201
  function researchLabel(messages, summary) {
3196
3202
  const others = messages.filter(isPendingResearch).length - 1;
3197
3203
  return others > 0 ? `${summary} (+${others} more)` : summary;
@@ -3257,9 +3263,12 @@ function reduce(state, action) {
3257
3263
  taskEditor: tasks.some((task) => task.id === state.expandedTaskId) ? state.taskEditor : null
3258
3264
  });
3259
3265
  }
3260
- case "plannerMessage":
3266
+ case "plannerMessage": {
3261
3267
  if (stale(state, action.sessionId)) return step(state);
3262
- return step({ ...say(state, "assistant", action.content), status: "idle", busyLabel: "", thinkingLine: "" });
3268
+ const settled = { ...state, status: "idle", busyLabel: "", thinkingLine: "" };
3269
+ if (alreadySpoken(state.messages, action.content)) return step(settled);
3270
+ return step(say(settled, "assistant", action.content));
3271
+ }
3263
3272
  case "researchStep": {
3264
3273
  if (stale(state, action.sessionId)) return step(state);
3265
3274
  const spoken = say(state, "research", action.summary, { toolCallId: action.toolCallId });
@@ -3605,11 +3614,13 @@ function handlePlanKey(state, key) {
3605
3614
  planScroll: 0
3606
3615
  });
3607
3616
  }
3608
- if (key.name === "scrollup") {
3609
- return step({ ...state, planScroll: Math.max(0, state.planScroll - 3) });
3617
+ if (key.name === "scrollup" || key.name === "pageup") {
3618
+ const notch = key.name === "pageup" ? 10 : 3;
3619
+ return step({ ...state, planScroll: Math.max(0, state.planScroll - notch) });
3610
3620
  }
3611
- if (key.name === "scrolldown") {
3612
- return step({ ...state, planScroll: Math.min(planScrollBound(state), state.planScroll + 3) });
3621
+ if (key.name === "scrolldown" || key.name === "pagedown") {
3622
+ const notch = key.name === "pagedown" ? 10 : 3;
3623
+ return step({ ...state, planScroll: Math.min(planScrollBound(state), state.planScroll + notch) });
3613
3624
  }
3614
3625
  const task = state.tasks[state.selectedTask];
3615
3626
  if (!task) return step(state);
@@ -3915,6 +3926,8 @@ function runCommand(state, { name, args }) {
3915
3926
  return runners(state, args);
3916
3927
  case "auto":
3917
3928
  return setAutonomous(state, args[0]);
3929
+ case "mouse":
3930
+ return setMouseCapture(state, args[0]);
3918
3931
  case "sessions":
3919
3932
  return step(
3920
3933
  { ...state, overlay: { kind: "picker", picker: picker("Sessions", [], { kind: "load-session" }) } },
@@ -4211,6 +4224,16 @@ function setAutonomous(state, arg) {
4211
4224
  if (enabled === null) return fail2(state, "Usage: /auto [on|off]");
4212
4225
  return step({ ...state, autonomous: enabled }, [{ type: "setAutonomous", enabled }]);
4213
4226
  }
4227
+ function setMouseCapture(state, arg) {
4228
+ const enabled = resolveToggle(arg, state.mouseCapture);
4229
+ if (enabled === null) return fail2(state, "Usage: /mouse [on|off]");
4230
+ const noted = say(
4231
+ { ...state, mouseCapture: enabled },
4232
+ "system",
4233
+ enabled ? "Mouse capture on \u2014 the wheel scrolls, but dragging no longer selects text." : "Mouse capture off \u2014 drag to select and copy; scroll with pgup/pgdn."
4234
+ );
4235
+ return step(noted, [{ type: "setMouseCapture", enabled }]);
4236
+ }
4214
4237
  function openTaskRunnerPicker(state, task) {
4215
4238
  if (task.type !== "ai") return fail2(state, "Manual tasks do not run on an executor, so they have no runner.");
4216
4239
  const action = { kind: "set-task-runner", taskId: task.id };
@@ -4519,8 +4542,9 @@ function renderFooter(state, cols) {
4519
4542
  "a add",
4520
4543
  "d remove",
4521
4544
  "t terminal",
4545
+ "pgup/pgdn scroll",
4522
4546
  "tab chat"
4523
- ] : ["/help", "tab plan", "alt-enter newline", "ctrl-c quit"];
4547
+ ] : ["/help", "tab plan", "alt-enter newline", "pgup/pgdn scroll", "ctrl-c quit"];
4524
4548
  return packHints(hints, cols);
4525
4549
  }
4526
4550
  function packHints(hints, cols) {
@@ -5191,6 +5215,10 @@ async function perform(effect, deps) {
5191
5215
  deps.setEnvVar("ORDEWELL_AUTONOMOUS_MODE", String(effect.enabled));
5192
5216
  dispatch({ type: "notice", message: `Autonomous mode ${effect.enabled ? "on" : "off"}.` });
5193
5217
  return;
5218
+ case "setMouseCapture":
5219
+ deps.setMouseCapture(effect.enabled);
5220
+ deps.setEnvVar("ORDEWELL_TUI_MOUSE", String(effect.enabled));
5221
+ return;
5194
5222
  case "loadModels":
5195
5223
  await loadModels(deps);
5196
5224
  return;
@@ -5245,6 +5273,7 @@ async function withExecutionStream(deps, sessionId, request) {
5245
5273
  await stream;
5246
5274
  }
5247
5275
  async function converse(deps, sessionId, call) {
5276
+ let streamed = null;
5248
5277
  const stream = deps.api.streamPlanning(sessionId, (event) => {
5249
5278
  if (event?.type === "approval_request") {
5250
5279
  deps.dispatch({
@@ -5285,6 +5314,11 @@ async function converse(deps, sessionId, call) {
5285
5314
  });
5286
5315
  return;
5287
5316
  }
5317
+ if (event?.type === "planner_message") {
5318
+ streamed = String(event.content ?? "");
5319
+ deps.dispatch({ type: "plannerMessage", content: streamed, sessionId });
5320
+ return;
5321
+ }
5288
5322
  if (event?.type === "plan_thinking" && event.text) {
5289
5323
  deps.dispatch({ type: "plannerThinking", text: event.text, sessionId });
5290
5324
  }
@@ -5293,7 +5327,9 @@ async function converse(deps, sessionId, call) {
5293
5327
  const plan = await call();
5294
5328
  deps.dispatch({ type: "planUpdated", plan, sessionId });
5295
5329
  const question = lastAssistantMessage(plan);
5296
- if (question) deps.dispatch({ type: "plannerMessage", content: question, sessionId });
5330
+ if (question && question !== streamed) {
5331
+ deps.dispatch({ type: "plannerMessage", content: question, sessionId });
5332
+ }
5297
5333
  } finally {
5298
5334
  stream.close();
5299
5335
  }
@@ -5549,6 +5585,9 @@ var KITTY_KEYBOARD_ON = "\x1B[>1u";
5549
5585
  var KITTY_KEYBOARD_OFF = "\x1B[<u";
5550
5586
  var MOUSE_TRACKING_ON = "\x1B[?1000h\x1B[?1006h";
5551
5587
  var MOUSE_TRACKING_OFF = "\x1B[?1000l\x1B[?1006l";
5588
+ var ALT_SCROLL_SAVE = "\x1B[?1007s";
5589
+ var ALT_SCROLL_OFF = "\x1B[?1007l";
5590
+ var ALT_SCROLL_RESTORE = "\x1B[?1007r";
5552
5591
  var HOME = "\x1B[H";
5553
5592
  var CLEAR = "\x1B[2J";
5554
5593
  function openTerminal(options) {
@@ -5558,8 +5597,9 @@ function openTerminal(options) {
5558
5597
  if (input.isTTY) input.setRawMode(true);
5559
5598
  input.resume();
5560
5599
  input.setEncoding("utf8");
5600
+ let mouse = options.mouse === true;
5561
5601
  output.write(
5562
- ALT_SCREEN_ON + HIDE_CURSOR + PASTE_MODE_ON + KITTY_KEYBOARD_ON + MOUSE_TRACKING_ON + CLEAR
5602
+ ALT_SCREEN_ON + HIDE_CURSOR + PASTE_MODE_ON + KITTY_KEYBOARD_ON + ALT_SCROLL_SAVE + ALT_SCROLL_OFF + (mouse ? MOUSE_TRACKING_ON : "") + CLEAR
5563
5603
  );
5564
5604
  const decode = createKeyDecoder();
5565
5605
  const onData = (chunk) => {
@@ -5582,6 +5622,11 @@ function openTerminal(options) {
5582
5622
  if (closed) return;
5583
5623
  output.write(HOME + frame2.join("\n"));
5584
5624
  },
5625
+ setMouse(enabled) {
5626
+ if (closed || enabled === mouse) return;
5627
+ mouse = enabled;
5628
+ output.write(enabled ? MOUSE_TRACKING_ON : MOUSE_TRACKING_OFF);
5629
+ },
5585
5630
  close() {
5586
5631
  if (closed) return;
5587
5632
  closed = true;
@@ -5590,7 +5635,7 @@ function openTerminal(options) {
5590
5635
  if (input.isTTY) input.setRawMode(false);
5591
5636
  input.pause();
5592
5637
  output.write(
5593
- MOUSE_TRACKING_OFF + KITTY_KEYBOARD_OFF + PASTE_MODE_OFF + SHOW_CURSOR + ALT_SCREEN_OFF
5638
+ MOUSE_TRACKING_OFF + ALT_SCROLL_RESTORE + KITTY_KEYBOARD_OFF + PASTE_MODE_OFF + SHOW_CURSOR + ALT_SCREEN_OFF
5594
5639
  );
5595
5640
  }
5596
5641
  };
@@ -5609,8 +5654,9 @@ async function handleTui(subArgs) {
5609
5654
  const conversationQueue = new ConversationQueue();
5610
5655
  let terminal;
5611
5656
  const autoMode = process.env.ORDEWELL_AUTONOMOUS_MODE;
5657
+ const mouseCapture = process.env.ORDEWELL_TUI_MOUSE === "true" || process.env.ORDEWELL_TUI_MOUSE === "1";
5612
5658
  const app = createApp({
5613
- initial: { workspace, autonomous: autoMode !== "false" && autoMode !== "0" },
5659
+ initial: { workspace, autonomous: autoMode !== "false" && autoMode !== "0", mouseCapture },
5614
5660
  draw: (frame2) => terminal?.draw(frame2),
5615
5661
  perform: (effect) => runEffect(effect, {
5616
5662
  api,
@@ -5624,6 +5670,7 @@ async function handleTui(subArgs) {
5624
5670
  process.env[key] = value;
5625
5671
  },
5626
5672
  openTerminal: (sessionId, taskId) => openTaskTerminal(port, sessionId, taskId),
5673
+ setMouseCapture: (enabled) => terminal?.setMouse(enabled),
5627
5674
  reviveDaemon: async () => {
5628
5675
  const { owned: freshlyOwned } = await ensureDaemonOwned(port, { detached: false, quiet: true });
5629
5676
  if (freshlyOwned) owned = true;
@@ -5634,6 +5681,7 @@ async function handleTui(subArgs) {
5634
5681
  onExit: () => shutdown(0)
5635
5682
  });
5636
5683
  terminal = openTerminal({
5684
+ mouse: mouseCapture,
5637
5685
  onKey: (key) => app.dispatch({ type: "key", key }),
5638
5686
  onResize: (rows2, cols2) => app.dispatch({ type: "resize", rows: rows2, cols: cols2 })
5639
5687
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ordewell/cli",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "description": "Ordewell CLI and TUI — plan, run and verify coding-agent tasks from the terminal.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Alessandro Costanzo Ciano",