codeam-cli 1.1.9 → 1.2.1

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/index.js +41 -21
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -110,7 +110,7 @@ var import_picocolors = __toESM(require("picocolors"));
110
110
  // package.json
111
111
  var package_default = {
112
112
  name: "codeam-cli",
113
- version: "1.1.9",
113
+ version: "1.2.1",
114
114
  description: "Remote control Claude Code from your mobile device",
115
115
  main: "dist/index.js",
116
116
  bin: {
@@ -678,6 +678,30 @@ var ClaudeService = class {
678
678
  sendCommand(text) {
679
679
  this.proc?.stdin?.write(text + "\r");
680
680
  }
681
+ /**
682
+ * Navigate a React Ink selector to the given 0-based index and confirm.
683
+ *
684
+ * Why not just sendCommand(arrows)? When all arrow keys + Enter arrive in one
685
+ * write() call, Node's readline splits them into individual keypress events that
686
+ * all fire in the same synchronous run. React Ink's SelectInput captures
687
+ * selectedIndex in a closure — because React hasn't re-rendered between the
688
+ * arrows and the Enter, the closure still sees the initial index (0) when Enter
689
+ * fires, so it always submits option 0.
690
+ *
691
+ * Fix: send the arrows first, then wait 150 ms for React to process + re-render,
692
+ * then send the confirming Enter.
693
+ */
694
+ selectOption(index) {
695
+ const arrows = "\x1B[B".repeat(Math.max(0, index));
696
+ if (arrows) {
697
+ this.proc?.stdin?.write(arrows);
698
+ setTimeout(() => {
699
+ this.proc?.stdin?.write("\r");
700
+ }, 150);
701
+ } else {
702
+ this.proc?.stdin?.write("\r");
703
+ }
704
+ }
681
705
  /** Send Ctrl+C to Claude. */
682
706
  interrupt() {
683
707
  this.proc?.stdin?.write("");
@@ -840,25 +864,21 @@ function renderToLines(raw) {
840
864
  }
841
865
  return screen;
842
866
  }
843
- function detectSelector(raw) {
844
- const stripped = raw.replace(/\x1B\[[^@-~]*[@-~]/g, "").replace(/\x1B\][^\x07\x1B]*(?:\x07|\x1B\\)/g, "").replace(/\x1B[@-Z\\-_]/g, "");
845
- if (!stripped.includes("\u276F")) return null;
846
- const segments = stripped.split(/[\r\n]+/);
867
+ function detectSelector(lines) {
868
+ if (!lines.some((l) => l.includes("\u276F"))) return null;
847
869
  const optionMap = /* @__PURE__ */ new Map();
848
870
  let question = "";
849
- const optRe = /(?:❯\s*)?(\d+)\.\s+((?:(?!\s*\d+\.\s).)+)/g;
850
- for (const seg of segments) {
851
- if (!seg.trim()) continue;
852
- for (const m of seg.matchAll(optRe)) {
871
+ for (const line of lines) {
872
+ const t = line.trim();
873
+ if (!t) continue;
874
+ const m = t.match(/^(?:❯\s*)?(\d+)\.\s+(.+)/);
875
+ if (m) {
853
876
  const num = parseInt(m[1], 10);
854
- const label = m[2].replace(/\s+/g, " ").trim();
877
+ const label = m[2].trim();
855
878
  if (label && !optionMap.has(num)) optionMap.set(num, label);
879
+ continue;
856
880
  }
857
- const beforeOptions = seg.replace(/(?:❯\s*)?\d+\.\s+.*/g, "").trim();
858
- if (beforeOptions.endsWith("?")) {
859
- const q = beforeOptions.match(/([^?]+\?)$/);
860
- if (q) question = q[1].trim();
861
- }
881
+ if (t.endsWith("?")) question = t;
862
882
  }
863
883
  const keys = [...optionMap.keys()].sort((a, b) => a - b);
864
884
  if (keys.length < 2 || keys[0] !== 1) return null;
@@ -930,7 +950,7 @@ var OutputService = class _OutputService {
930
950
  return;
931
951
  }
932
952
  const lines = renderToLines(this.rawBuffer);
933
- const selector = detectSelector(this.rawBuffer);
953
+ const selector = detectSelector(lines);
934
954
  if (selector) {
935
955
  const idleMs2 = this.lastPushTime > 0 ? now - this.lastPushTime : elapsed;
936
956
  if (idleMs2 >= _OutputService.SELECTOR_IDLE_MS) {
@@ -959,7 +979,7 @@ var OutputService = class _OutputService {
959
979
  }
960
980
  finalize() {
961
981
  const lines = renderToLines(this.rawBuffer);
962
- const selector = detectSelector(this.rawBuffer);
982
+ const selector = detectSelector(lines);
963
983
  this.stopPoll();
964
984
  this.active = false;
965
985
  if (selector) {
@@ -1060,8 +1080,8 @@ async function start() {
1060
1080
  }
1061
1081
  case "select_option": {
1062
1082
  const index = cmd.payload.index ?? 0;
1063
- const arrows = "\x1B[B".repeat(Math.max(0, index));
1064
- sendPrompt(arrows);
1083
+ outputSvc.newTurn();
1084
+ claude.selectOption(index);
1065
1085
  break;
1066
1086
  }
1067
1087
  case "stop_task":
@@ -1086,8 +1106,8 @@ async function start() {
1086
1106
  if (input) sendPrompt(input);
1087
1107
  } else if (cmdType === "select_option") {
1088
1108
  const index = inner.index ?? 0;
1089
- const arrows = "\x1B[B".repeat(Math.max(0, index));
1090
- sendPrompt(arrows);
1109
+ outputSvc.newTurn();
1110
+ claude.selectOption(index);
1091
1111
  } else if (cmdType === "stop_task") {
1092
1112
  claude.interrupt();
1093
1113
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "1.1.9",
3
+ "version": "1.2.1",
4
4
  "description": "Remote control Claude Code from your mobile device",
5
5
  "main": "dist/index.js",
6
6
  "bin": {