codeam-cli 1.1.2 → 1.1.4

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 +66 -17
  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.2",
113
+ version: "1.1.4",
114
114
  description: "Remote control Claude Code from your mobile device",
115
115
  main: "dist/index.js",
116
116
  bin: {
@@ -840,27 +840,50 @@ function renderToLines(raw) {
840
840
  }
841
841
  return screen;
842
842
  }
843
+ function detectSelector(raw) {
844
+ const stripped = raw.replace(/\x1B\[[^@-~]*[@-~]/g, "").replace(/\x1B\][^\x07\x1B]*(?:\x07|\x1B\\)/g, "").replace(/\x1B[@-Z\\-_]/g, "");
845
+ if (!/Enter to select/i.test(stripped)) return null;
846
+ const segments = stripped.split(/[\r\n]+/);
847
+ const optionMap = /* @__PURE__ */ new Map();
848
+ for (const seg of segments) {
849
+ const t = seg.trim();
850
+ if (!t) continue;
851
+ const m = t.match(/^[❯>]?\s*(\d+)\.\s+(.+)/);
852
+ if (m) {
853
+ const num = parseInt(m[1], 10);
854
+ if (!optionMap.has(num)) optionMap.set(num, m[2].trim());
855
+ }
856
+ }
857
+ const options = [...optionMap.entries()].sort(([a], [b]) => a - b).map(([, label]) => label);
858
+ if (options.length === 0) return null;
859
+ let question = "";
860
+ for (let i = segments.length - 1; i >= 0; i--) {
861
+ const t = segments[i].trim();
862
+ if (t.endsWith("?") && !/Enter to select/i.test(t) && !/^[❯>]?\s*\d+\./.test(t)) {
863
+ question = t;
864
+ break;
865
+ }
866
+ }
867
+ return { question, options };
868
+ }
843
869
  function filterChrome(lines) {
844
870
  return lines.filter((line) => {
845
871
  const t = line.trim();
846
872
  if (!t) return false;
847
- if (/^[─━—═\-─]{3,}$/.test(t)) return false;
873
+ if (/^[─━—═─\-]{3,}$/.test(t)) return false;
848
874
  if (/^[✳✢✶✻✽✴✷✸✹⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏◐◑◒◓▁▂▃▄▅▆▇█]\s/.test(t)) return false;
849
875
  if (/esc.{0,5}to.{0,5}interrupt/i.test(t)) return false;
850
876
  if (/high\s*[·•]\s*\/effort/i.test(t)) return false;
851
877
  if (/^[❯>]\s*$/.test(t)) return false;
852
- if (/^[❯>]\s+\S/.test(t)) return false;
878
+ if (/^[❯>]\s+\S/.test(t) && !/^[❯>]\s*\d+\./.test(t)) return false;
853
879
  if (/^\(thinking\)\s*$/.test(t)) return false;
854
880
  if (/^\?\s.*shortcut/i.test(t)) return false;
855
881
  if (/spending limit|usage limit/i.test(t) && t.length < 80) return false;
882
+ if (/Enter to select/i.test(t)) return false;
883
+ if (/↑\s*\/\s*↓\s*to\s*navigate/i.test(t)) return false;
856
884
  return true;
857
885
  });
858
886
  }
859
- function extractContent(raw) {
860
- const lines = renderToLines(raw);
861
- const filtered = filterChrome(lines);
862
- return filtered.join("\n").replace(/\n{3,}/g, "\n\n").trim();
863
- }
864
887
  var OutputService = class _OutputService {
865
888
  constructor(sessionId, pluginId) {
866
889
  this.sessionId = sessionId;
@@ -868,20 +891,17 @@ var OutputService = class _OutputService {
868
891
  }
869
892
  sessionId;
870
893
  pluginId;
871
- /** Raw PTY bytes — processed only when we need to send content. */
872
894
  rawBuffer = "";
873
895
  lastSentContent = "";
874
896
  pollTimer = null;
875
897
  startTime = 0;
876
898
  active = false;
877
- /** When the last chunk of printable raw content arrived. */
878
899
  lastPushTime = 0;
879
900
  static POLL_MS = 1e3;
880
- /** No new printable content for 3 s → Claude finished responding. */
881
901
  static IDLE_MS = 3e3;
882
- /** No content at all for 30 s give up. */
902
+ /** Shorter idle threshold for selector detection (UI is ready immediately). */
903
+ static SELECTOR_IDLE_MS = 1500;
883
904
  static EMPTY_TIMEOUT_MS = 3e4;
884
- /** Hard cap — clear typing state after 2 min no matter what. */
885
905
  static MAX_MS = 12e4;
886
906
  newTurn() {
887
907
  this.stopPoll();
@@ -912,7 +932,19 @@ var OutputService = class _OutputService {
912
932
  this.finalize();
913
933
  return;
914
934
  }
915
- const content = extractContent(this.rawBuffer);
935
+ const lines = renderToLines(this.rawBuffer);
936
+ const selector = detectSelector(this.rawBuffer);
937
+ if (selector) {
938
+ const idleMs2 = this.lastPushTime > 0 ? now - this.lastPushTime : elapsed;
939
+ if (idleMs2 >= _OutputService.SELECTOR_IDLE_MS) {
940
+ this.stopPoll();
941
+ this.active = false;
942
+ this.postChunk({ type: "select_prompt", content: selector.question, options: selector.options, done: true }).catch(() => {
943
+ });
944
+ }
945
+ return;
946
+ }
947
+ const content = filterChrome(lines).join("\n").replace(/\n{3,}/g, "\n\n").trim();
916
948
  if (!content) {
917
949
  if (elapsed >= _OutputService.EMPTY_TIMEOUT_MS) this.finalize();
918
950
  return;
@@ -929,11 +961,18 @@ var OutputService = class _OutputService {
929
961
  }
930
962
  }
931
963
  finalize() {
932
- const content = extractContent(this.rawBuffer);
964
+ const lines = renderToLines(this.rawBuffer);
965
+ const selector = detectSelector(this.rawBuffer);
933
966
  this.stopPoll();
934
967
  this.active = false;
935
- this.postChunk({ type: "text", content, done: true }).catch(() => {
936
- });
968
+ if (selector) {
969
+ this.postChunk({ type: "select_prompt", content: selector.question, options: selector.options, done: true }).catch(() => {
970
+ });
971
+ } else {
972
+ const content = filterChrome(lines).join("\n").replace(/\n{3,}/g, "\n\n").trim();
973
+ this.postChunk({ type: "text", content, done: true }).catch(() => {
974
+ });
975
+ }
937
976
  }
938
977
  stopPoll() {
939
978
  if (this.pollTimer) {
@@ -1022,6 +1061,12 @@ async function start() {
1022
1061
  if (input) sendPrompt(input);
1023
1062
  break;
1024
1063
  }
1064
+ case "select_option": {
1065
+ const index = cmd.payload.index ?? 0;
1066
+ const arrows = "\x1B[B".repeat(Math.max(0, index));
1067
+ sendPrompt(arrows);
1068
+ break;
1069
+ }
1025
1070
  case "stop_task":
1026
1071
  claude.interrupt();
1027
1072
  break;
@@ -1042,6 +1087,10 @@ async function start() {
1042
1087
  } else if (cmdType === "provide_input") {
1043
1088
  const input = inner.input;
1044
1089
  if (input) sendPrompt(input);
1090
+ } else if (cmdType === "select_option") {
1091
+ const index = inner.index ?? 0;
1092
+ const arrows = "\x1B[B".repeat(Math.max(0, index));
1093
+ sendPrompt(arrows);
1045
1094
  } else if (cmdType === "stop_task") {
1046
1095
  claude.interrupt();
1047
1096
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Remote control Claude Code from your mobile device",
5
5
  "main": "dist/index.js",
6
6
  "bin": {