@ordewell/cli 0.4.8 → 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 +55 -34
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -1409,7 +1409,8 @@ function normalizeCatalog(result) {
1409
1409
  (modes ?? []).map((m) => ({
1410
1410
  id: String(m.id),
1411
1411
  label: String(m.label ?? m.id),
1412
- description: m.description ? String(m.description) : void 0
1412
+ description: m.description ? String(m.description) : void 0,
1413
+ autonomous: m.autonomous ? true : void 0
1413
1414
  }))
1414
1415
  ])
1415
1416
  )
@@ -2728,9 +2729,21 @@ var import_core13 = require("@ordewell/core");
2728
2729
 
2729
2730
  // src/tui/ansi.ts
2730
2731
  var import_string_width = __toESM(require("string-width"));
2731
- 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]$/;
2732
2738
  function stripAnsi(text) {
2733
- 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("");
2734
2747
  }
2735
2748
  function width(text) {
2736
2749
  return (0, import_string_width.default)(stripAnsi(text));
@@ -2748,9 +2761,6 @@ function truncate(text, max) {
2748
2761
  }
2749
2762
  return out + "\u2026";
2750
2763
  }
2751
- function stripTabs(text) {
2752
- return text.replace(/\t/g, " ");
2753
- }
2754
2764
  function pad(text, target) {
2755
2765
  const current = width(text);
2756
2766
  if (current > target) return truncate(text, target);
@@ -3553,7 +3563,8 @@ function taskLines(state, task, index, cols) {
3553
3563
  });
3554
3564
  const model = task.assignedModel?.modelLabel ?? (task.type === "ai" ? "default model" : "");
3555
3565
  const effort = task.type === "ai" ? `effort: ${task.assignedModel?.thinkingEffort ?? "default"}` : "";
3556
- const mode = task.type === "ai" ? `mode: ${task.taskMode ?? "default"}` : "";
3566
+ const modeInfo = task.type === "ai" ? modesForTask(state.modesByRunner, task).find((m) => m.id === task.taskMode) : void 0;
3567
+ const mode = task.type === "ai" ? `mode: ${task.taskMode ?? "default"}${modeInfo?.autonomous ? style.yellow(" \u26A1") : ""}` : "";
3557
3568
  const runner = task.assignedRunner ?? "";
3558
3569
  const meta = [running ? "working" : "", runner, model].filter(Boolean).join(" \xB7 ");
3559
3570
  if (meta) lines.push(style.grey(truncate(` ${meta}`, cols)));
@@ -3561,6 +3572,9 @@ function taskLines(state, task, index, cols) {
3561
3572
  let editorLine;
3562
3573
  if (expanded) {
3563
3574
  lines.push(style.grey(` ${task.status.replace(/_/g, " ")}`));
3575
+ if (modeInfo?.autonomous) {
3576
+ lines.push(...taskText("Autonomy", "Runs without permission prompts. Toggle with /auto.", cols));
3577
+ }
3564
3578
  const editableSeed = task.prompt ?? task.description ?? task.title;
3565
3579
  if (task.description && task.description !== task.title && task.description !== editableSeed) {
3566
3580
  lines.push(...taskText("Description", task.description, cols));
@@ -3671,13 +3685,16 @@ function cells(line) {
3671
3685
  }
3672
3686
  var INVERSE_ON = "\x1B[7m";
3673
3687
  var INVERSE_OFF = "\x1B[27m";
3674
- 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$/;
3675
3690
  function invertCells(line, from, to) {
3676
3691
  let out = "";
3677
3692
  let col = 1;
3678
3693
  let inside = false;
3679
- for (const piece of line.split(SGR_SPLIT)) {
3680
- 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");
3681
3698
  out += piece + (inside ? INVERSE_ON : "");
3682
3699
  continue;
3683
3700
  }
@@ -3777,15 +3794,18 @@ function suggest(state, room) {
3777
3794
  function renderFooter(state, cols) {
3778
3795
  return packHints(footerHints(state), cols).map((line) => truncate(style.grey(line), cols));
3779
3796
  }
3797
+ var ERASE_TO_END = "\x1B[K";
3798
+ var atColumn = (col) => `\x1B[${col}G`;
3780
3799
  function renderBody(state, rows, cols) {
3781
3800
  const planCols = planPaneWidth(state);
3782
3801
  if (planCols === 0) return renderChat(state, rows, cols);
3783
3802
  const chatCols = chatPaneWidth(state);
3784
3803
  const chat = renderChat(state, rows, chatCols);
3785
3804
  const plan = renderPlan(state, rows, planCols);
3805
+ const divider = `${ERASE_TO_END}${atColumn(chatCols + 1)}${style.grey("\u2502")}`;
3786
3806
  return Array.from(
3787
3807
  { length: rows },
3788
- (_, i) => `${pad(chat[i] ?? "", chatCols)}${style.grey("\u2502")}${pad(plan[i] ?? "", planCols)}`
3808
+ (_, i) => `${pad(chat[i] ?? "", chatCols)}${divider}${pad(plan[i] ?? "", planCols)}`
3789
3809
  );
3790
3810
  }
3791
3811
  function renderChat(state, rows, cols) {
@@ -3896,13 +3916,13 @@ function step(state, effects = []) {
3896
3916
  return { state, effects };
3897
3917
  }
3898
3918
  function say(state, role, content, research) {
3899
- 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 } : {} };
3900
3920
  return { ...state, messages: [...state.messages, message], scroll: 0 };
3901
3921
  }
3902
3922
  function restoredMessages(history) {
3903
3923
  return history.map((entry) => ({
3904
3924
  role: entry.kind === "plan_generated" ? "system" : entry.kind === "system" ? "system" : entry.role,
3905
- content: stripTabs(entry.kind === "plan_generated" ? "Plan generated." : entry.content),
3925
+ content: sanitize(entry.kind === "plan_generated" ? "Plan generated." : entry.content),
3906
3926
  timestamp: entry.timestamp
3907
3927
  }));
3908
3928
  }
@@ -3916,13 +3936,13 @@ function researchLabel(messages, summary) {
3916
3936
  return others > 0 ? `${summary} (+${others} more)` : summary;
3917
3937
  }
3918
3938
  function settleResearchStep(messages, action) {
3919
- const summary = stripTabs(action.summary);
3939
+ const summary = sanitize(action.summary);
3920
3940
  const index = findLastIndex(messages, (m) => isPendingResearch(m) && (action.toolCallId ? m.research?.toolCallId === action.toolCallId : m.content === summary));
3921
3941
  if (index < 0) return messages;
3922
3942
  const next = [...messages];
3923
3943
  next[index] = {
3924
3944
  ...next[index],
3925
- research: { ...next[index].research, outcome: action.outcome, result: stripTabs(action.result) }
3945
+ research: { ...next[index].research, outcome: action.outcome, result: sanitize(action.result) }
3926
3946
  };
3927
3947
  return next;
3928
3948
  }
@@ -3980,13 +4000,13 @@ function reduce(state, action) {
3980
4000
  case "plannerMessage": {
3981
4001
  if (stale(state, action.sessionId)) return step(state);
3982
4002
  const settled = { ...state, status: "idle", busyLabel: "", thinkingLine: "" };
3983
- const content = stripTabs(action.content);
4003
+ const content = sanitize(action.content);
3984
4004
  if (alreadySpoken(state.messages, content)) return step(settled);
3985
4005
  return step(say(settled, "assistant", content));
3986
4006
  }
3987
4007
  case "researchStep": {
3988
4008
  if (stale(state, action.sessionId)) return step(state);
3989
- const summary = stripTabs(action.summary);
4009
+ const summary = sanitize(action.summary);
3990
4010
  const spoken = say(state, "research", summary, { toolCallId: action.toolCallId });
3991
4011
  const status = state.status === "planning" ? "researching" : state.status;
3992
4012
  return step({ ...spoken, status, busyLabel: researchLabel(spoken.messages, summary) });
@@ -4005,7 +4025,7 @@ function reduce(state, action) {
4005
4025
  }
4006
4026
  case "plannerThinking": {
4007
4027
  if (stale(state, action.sessionId)) return step(state);
4008
- return step({ ...state, thinkingLine: (state.thinkingLine + action.text).slice(-THINKING_TAIL) });
4028
+ return step({ ...state, thinkingLine: (state.thinkingLine + sanitize(action.text)).slice(-THINKING_TAIL) });
4009
4029
  }
4010
4030
  case "taskStatus": {
4011
4031
  if (stale(state, action.sessionId) || !state.tasks.some((t) => t.id === action.taskId)) return step(state);
@@ -4094,6 +4114,8 @@ function reduce(state, action) {
4094
4114
  return step({ ...state, spinnerFrame: (state.spinnerFrame + 1) % 10 });
4095
4115
  }
4096
4116
  }
4117
+ var planText = (value, fallback = "") => sanitize(String(value ?? fallback));
4118
+ var planLabel = (value, fallback = "") => planText(value, fallback).replace(/\n+/g, " ");
4097
4119
  function normalizeTasks(plan) {
4098
4120
  const source = plan;
4099
4121
  const byId = /* @__PURE__ */ new Map();
@@ -4106,19 +4128,19 @@ function normalizeTasks(plan) {
4106
4128
  return raw.map((t, i) => ({
4107
4129
  id: String(t.id ?? `task-${i + 1}`),
4108
4130
  order: typeof t.order === "number" ? t.order : i + 1,
4109
- title: String(t.title ?? "Untitled task"),
4110
- description: String(t.description ?? t.title ?? ""),
4111
- 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,
4112
4134
  type: t.type === "user" ? "user" : "ai",
4113
- status: String(t.status ?? "pending"),
4114
- dependencies: Array.isArray(t.dependencies) ? t.dependencies.map(String) : [],
4115
- assignedRunner: typeof t.assignedRunner === "string" ? t.assignedRunner : void 0,
4116
- 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,
4117
4139
  assignedModel: t.assignedModel && typeof t.assignedModel === "object" ? {
4118
- modelId: String(t.assignedModel.modelId ?? ""),
4119
- modelLabel: String(t.assignedModel.modelLabel ?? t.assignedModel.modelId ?? ""),
4120
- thinkingEffort: typeof t.assignedModel.thinkingEffort === "string" ? t.assignedModel.thinkingEffort : typeof t.thinkingEffort === "string" ? t.thinkingEffort : void 0,
4121
- 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
4122
4144
  } : void 0
4123
4145
  }));
4124
4146
  }
@@ -5740,7 +5762,7 @@ async function loadSettings(deps) {
5740
5762
  }
5741
5763
 
5742
5764
  // src/tui/keys.ts
5743
- var CONTROL = {
5765
+ var CONTROL2 = {
5744
5766
  "\r": "enter",
5745
5767
  "\n": "enter",
5746
5768
  " ": "tab",
@@ -5845,7 +5867,7 @@ function decodeMouse(seq) {
5845
5867
  function decodeKey(seq) {
5846
5868
  const escaped = ESCAPES[seq];
5847
5869
  if (escaped) return { name: escaped };
5848
- const control = CONTROL[seq];
5870
+ const control = CONTROL2[seq];
5849
5871
  if (control) return { name: control };
5850
5872
  if (seq.startsWith("\x1B[")) {
5851
5873
  const mouse = decodeMouse(seq);
@@ -5857,8 +5879,7 @@ function decodeKey(seq) {
5857
5879
  var PASTE_START = "\x1B[200~";
5858
5880
  var PASTE_END = "\x1B[201~";
5859
5881
  function pasteKey(raw) {
5860
- const text = stripTabs(raw.replace(/\r\n?/g, "\n").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, ""));
5861
- return { name: "paste", text };
5882
+ return { name: "paste", text: sanitize(raw) };
5862
5883
  }
5863
5884
  function createKeyDecoder() {
5864
5885
  let paste = null;
@@ -5992,7 +6013,7 @@ function openTerminal(options) {
5992
6013
  draw(frame2) {
5993
6014
  if (closed) return;
5994
6015
  const { cols } = size();
5995
- 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("");
5996
6017
  output.write(AUTOWRAP_OFF + rows + AUTOWRAP_ON);
5997
6018
  },
5998
6019
  setMouse(enabled) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ordewell/cli",
3
- "version": "0.4.8",
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",