codeam-cli 1.1.2 → 1.1.3

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