@ordewell/cli 0.4.9 → 0.4.10

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 +48 -32
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -2729,9 +2729,21 @@ var import_core13 = require("@ordewell/core");
2729
2729
 
2730
2730
  // src/tui/ansi.ts
2731
2731
  var import_string_width = __toESM(require("string-width"));
2732
- var ANSI = /\x1b\[[0-9;]*m/g;
2732
+ var ESCAPE = /\x1b(?:\][\s\S]*?(?:\x07|\x1b\\|$)|[P^_][\s\S]*?(?:\x1b\\|$)|\[[0-?]*[ -/]*[@-~]?|[ -/]*[0-~]?)/g;
2733
+ var ESCAPE_SPLIT = new RegExp(`(${ESCAPE.source})`);
2734
+ var CONTROL = /[\x00-\x08\x0b-\x1f\x7f-\x9f]/g;
2735
+ var CONTROL_IN_ROW = /[\x00-\x1f\x7f-\x9f]/g;
2736
+ var HAS_CONTROL = /[\x00-\x1f\x7f-\x9f]/;
2737
+ var PAINT_ESCAPE = /^\x1b\[[0-9;]*[mGK]$/;
2733
2738
  function stripAnsi(text) {
2734
- return text.replace(ANSI, "");
2739
+ return text.replace(ESCAPE, "");
2740
+ }
2741
+ function sanitize(text) {
2742
+ return text.replace(ESCAPE, "").replace(/\r\n?/g, "\n").replace(/\t/g, " ").replace(CONTROL, "");
2743
+ }
2744
+ function paintOnly(line) {
2745
+ if (!HAS_CONTROL.test(line)) return line;
2746
+ return line.split(ESCAPE_SPLIT).map((piece) => piece.startsWith("\x1B") ? PAINT_ESCAPE.test(piece) ? piece : "" : piece.replace(CONTROL_IN_ROW, "")).join("");
2735
2747
  }
2736
2748
  function width(text) {
2737
2749
  return (0, import_string_width.default)(stripAnsi(text));
@@ -2749,9 +2761,6 @@ function truncate(text, max) {
2749
2761
  }
2750
2762
  return out + "\u2026";
2751
2763
  }
2752
- function stripTabs(text) {
2753
- return text.replace(/\t/g, " ");
2754
- }
2755
2764
  function pad(text, target) {
2756
2765
  const current = width(text);
2757
2766
  if (current > target) return truncate(text, target);
@@ -3676,13 +3685,16 @@ function cells(line) {
3676
3685
  }
3677
3686
  var INVERSE_ON = "\x1B[7m";
3678
3687
  var INVERSE_OFF = "\x1B[27m";
3679
- var SGR_SPLIT = /(\x1b\[[0-9;]*m)/;
3688
+ var ESCAPE_SPLIT2 = /(\x1b\[[0-9;]*[A-Za-z])/;
3689
+ var CURSOR_COLUMN = /^\x1b\[([0-9]*)G$/;
3680
3690
  function invertCells(line, from, to) {
3681
3691
  let out = "";
3682
3692
  let col = 1;
3683
3693
  let inside = false;
3684
- for (const piece of line.split(SGR_SPLIT)) {
3685
- if (SGR_SPLIT.test(piece)) {
3694
+ for (const piece of line.split(ESCAPE_SPLIT2)) {
3695
+ if (ESCAPE_SPLIT2.test(piece)) {
3696
+ const jump = CURSOR_COLUMN.exec(piece);
3697
+ if (jump) col = Number(jump[1] || "1");
3686
3698
  out += piece + (inside ? INVERSE_ON : "");
3687
3699
  continue;
3688
3700
  }
@@ -3782,15 +3794,18 @@ function suggest(state, room) {
3782
3794
  function renderFooter(state, cols) {
3783
3795
  return packHints(footerHints(state), cols).map((line) => truncate(style.grey(line), cols));
3784
3796
  }
3797
+ var ERASE_TO_END = "\x1B[K";
3798
+ var atColumn = (col) => `\x1B[${col}G`;
3785
3799
  function renderBody(state, rows, cols) {
3786
3800
  const planCols = planPaneWidth(state);
3787
3801
  if (planCols === 0) return renderChat(state, rows, cols);
3788
3802
  const chatCols = chatPaneWidth(state);
3789
3803
  const chat = renderChat(state, rows, chatCols);
3790
3804
  const plan = renderPlan(state, rows, planCols);
3805
+ const divider = `${ERASE_TO_END}${atColumn(chatCols + 1)}${style.grey("\u2502")}`;
3791
3806
  return Array.from(
3792
3807
  { length: rows },
3793
- (_, i) => `${pad(chat[i] ?? "", chatCols)}${style.grey("\u2502")}${pad(plan[i] ?? "", planCols)}`
3808
+ (_, i) => `${pad(chat[i] ?? "", chatCols)}${divider}${pad(plan[i] ?? "", planCols)}`
3794
3809
  );
3795
3810
  }
3796
3811
  function renderChat(state, rows, cols) {
@@ -3901,13 +3916,13 @@ function step(state, effects = []) {
3901
3916
  return { state, effects };
3902
3917
  }
3903
3918
  function say(state, role, content, research) {
3904
- const message = { role, content: stripTabs(content), timestamp: (/* @__PURE__ */ new Date()).toISOString(), ...research ? { research } : {} };
3919
+ const message = { role, content: sanitize(content), timestamp: (/* @__PURE__ */ new Date()).toISOString(), ...research ? { research } : {} };
3905
3920
  return { ...state, messages: [...state.messages, message], scroll: 0 };
3906
3921
  }
3907
3922
  function restoredMessages(history) {
3908
3923
  return history.map((entry) => ({
3909
3924
  role: entry.kind === "plan_generated" ? "system" : entry.kind === "system" ? "system" : entry.role,
3910
- content: stripTabs(entry.kind === "plan_generated" ? "Plan generated." : entry.content),
3925
+ content: sanitize(entry.kind === "plan_generated" ? "Plan generated." : entry.content),
3911
3926
  timestamp: entry.timestamp
3912
3927
  }));
3913
3928
  }
@@ -3921,13 +3936,13 @@ function researchLabel(messages, summary) {
3921
3936
  return others > 0 ? `${summary} (+${others} more)` : summary;
3922
3937
  }
3923
3938
  function settleResearchStep(messages, action) {
3924
- const summary = stripTabs(action.summary);
3939
+ const summary = sanitize(action.summary);
3925
3940
  const index = findLastIndex(messages, (m) => isPendingResearch(m) && (action.toolCallId ? m.research?.toolCallId === action.toolCallId : m.content === summary));
3926
3941
  if (index < 0) return messages;
3927
3942
  const next = [...messages];
3928
3943
  next[index] = {
3929
3944
  ...next[index],
3930
- research: { ...next[index].research, outcome: action.outcome, result: stripTabs(action.result) }
3945
+ research: { ...next[index].research, outcome: action.outcome, result: sanitize(action.result) }
3931
3946
  };
3932
3947
  return next;
3933
3948
  }
@@ -3985,13 +4000,13 @@ function reduce(state, action) {
3985
4000
  case "plannerMessage": {
3986
4001
  if (stale(state, action.sessionId)) return step(state);
3987
4002
  const settled = { ...state, status: "idle", busyLabel: "", thinkingLine: "" };
3988
- const content = stripTabs(action.content);
4003
+ const content = sanitize(action.content);
3989
4004
  if (alreadySpoken(state.messages, content)) return step(settled);
3990
4005
  return step(say(settled, "assistant", content));
3991
4006
  }
3992
4007
  case "researchStep": {
3993
4008
  if (stale(state, action.sessionId)) return step(state);
3994
- const summary = stripTabs(action.summary);
4009
+ const summary = sanitize(action.summary);
3995
4010
  const spoken = say(state, "research", summary, { toolCallId: action.toolCallId });
3996
4011
  const status = state.status === "planning" ? "researching" : state.status;
3997
4012
  return step({ ...spoken, status, busyLabel: researchLabel(spoken.messages, summary) });
@@ -4010,7 +4025,7 @@ function reduce(state, action) {
4010
4025
  }
4011
4026
  case "plannerThinking": {
4012
4027
  if (stale(state, action.sessionId)) return step(state);
4013
- return step({ ...state, thinkingLine: (state.thinkingLine + action.text).slice(-THINKING_TAIL) });
4028
+ return step({ ...state, thinkingLine: (state.thinkingLine + sanitize(action.text)).slice(-THINKING_TAIL) });
4014
4029
  }
4015
4030
  case "taskStatus": {
4016
4031
  if (stale(state, action.sessionId) || !state.tasks.some((t) => t.id === action.taskId)) return step(state);
@@ -4099,6 +4114,8 @@ function reduce(state, action) {
4099
4114
  return step({ ...state, spinnerFrame: (state.spinnerFrame + 1) % 10 });
4100
4115
  }
4101
4116
  }
4117
+ var planText = (value, fallback = "") => sanitize(String(value ?? fallback));
4118
+ var planLabel = (value, fallback = "") => planText(value, fallback).replace(/\n+/g, " ");
4102
4119
  function normalizeTasks(plan) {
4103
4120
  const source = plan;
4104
4121
  const byId = /* @__PURE__ */ new Map();
@@ -4111,19 +4128,19 @@ function normalizeTasks(plan) {
4111
4128
  return raw.map((t, i) => ({
4112
4129
  id: String(t.id ?? `task-${i + 1}`),
4113
4130
  order: typeof t.order === "number" ? t.order : i + 1,
4114
- title: String(t.title ?? "Untitled task"),
4115
- description: String(t.description ?? t.title ?? ""),
4116
- prompt: typeof t.prompt === "string" ? t.prompt : void 0,
4131
+ title: planLabel(t.title, "Untitled task"),
4132
+ description: planText(t.description ?? t.title),
4133
+ prompt: typeof t.prompt === "string" ? sanitize(t.prompt) : void 0,
4117
4134
  type: t.type === "user" ? "user" : "ai",
4118
- status: String(t.status ?? "pending"),
4119
- dependencies: Array.isArray(t.dependencies) ? t.dependencies.map(String) : [],
4120
- assignedRunner: typeof t.assignedRunner === "string" ? t.assignedRunner : void 0,
4121
- taskMode: typeof t.taskMode === "string" ? t.taskMode : void 0,
4135
+ status: planLabel(t.status, "pending"),
4136
+ dependencies: Array.isArray(t.dependencies) ? t.dependencies.map((id) => planLabel(id)) : [],
4137
+ assignedRunner: typeof t.assignedRunner === "string" ? planLabel(t.assignedRunner) : void 0,
4138
+ taskMode: typeof t.taskMode === "string" ? planLabel(t.taskMode) : void 0,
4122
4139
  assignedModel: t.assignedModel && typeof t.assignedModel === "object" ? {
4123
- modelId: String(t.assignedModel.modelId ?? ""),
4124
- modelLabel: String(t.assignedModel.modelLabel ?? t.assignedModel.modelId ?? ""),
4125
- thinkingEffort: typeof t.assignedModel.thinkingEffort === "string" ? t.assignedModel.thinkingEffort : typeof t.thinkingEffort === "string" ? t.thinkingEffort : void 0,
4126
- availableVariants: Array.isArray(t.assignedModel.availableVariants) ? t.assignedModel.availableVariants.map(String) : void 0
4140
+ modelId: planLabel(t.assignedModel.modelId),
4141
+ modelLabel: planLabel(t.assignedModel.modelLabel ?? t.assignedModel.modelId),
4142
+ thinkingEffort: typeof t.assignedModel.thinkingEffort === "string" ? planLabel(t.assignedModel.thinkingEffort) : typeof t.thinkingEffort === "string" ? planLabel(t.thinkingEffort) : void 0,
4143
+ availableVariants: Array.isArray(t.assignedModel.availableVariants) ? t.assignedModel.availableVariants.map((variant) => planLabel(variant)) : void 0
4127
4144
  } : void 0
4128
4145
  }));
4129
4146
  }
@@ -5745,7 +5762,7 @@ async function loadSettings(deps) {
5745
5762
  }
5746
5763
 
5747
5764
  // src/tui/keys.ts
5748
- var CONTROL = {
5765
+ var CONTROL2 = {
5749
5766
  "\r": "enter",
5750
5767
  "\n": "enter",
5751
5768
  " ": "tab",
@@ -5850,7 +5867,7 @@ function decodeMouse(seq) {
5850
5867
  function decodeKey(seq) {
5851
5868
  const escaped = ESCAPES[seq];
5852
5869
  if (escaped) return { name: escaped };
5853
- const control = CONTROL[seq];
5870
+ const control = CONTROL2[seq];
5854
5871
  if (control) return { name: control };
5855
5872
  if (seq.startsWith("\x1B[")) {
5856
5873
  const mouse = decodeMouse(seq);
@@ -5862,8 +5879,7 @@ function decodeKey(seq) {
5862
5879
  var PASTE_START = "\x1B[200~";
5863
5880
  var PASTE_END = "\x1B[201~";
5864
5881
  function pasteKey(raw) {
5865
- const text = stripTabs(raw.replace(/\r\n?/g, "\n").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, ""));
5866
- return { name: "paste", text };
5882
+ return { name: "paste", text: sanitize(raw) };
5867
5883
  }
5868
5884
  function createKeyDecoder() {
5869
5885
  let paste = null;
@@ -5997,7 +6013,7 @@ function openTerminal(options) {
5997
6013
  draw(frame2) {
5998
6014
  if (closed) return;
5999
6015
  const { cols } = size();
6000
- const rows = frame2.map((line, i) => `\x1B[${i + 1};1H${clampToCols(line, cols)}\x1B[K`).join("");
6016
+ const rows = frame2.map((line, i) => `\x1B[${i + 1};1H${clampToCols(paintOnly(line), cols)}\x1B[K`).join("");
6001
6017
  output.write(AUTOWRAP_OFF + rows + AUTOWRAP_ON);
6002
6018
  },
6003
6019
  setMouse(enabled) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ordewell/cli",
3
- "version": "0.4.9",
3
+ "version": "0.4.10",
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",