codeam-cli 1.3.8 → 1.3.9
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.
- package/dist/index.js +82 -18
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -114,7 +114,7 @@ var import_picocolors = __toESM(require("picocolors"));
|
|
|
114
114
|
// package.json
|
|
115
115
|
var package_default = {
|
|
116
116
|
name: "codeam-cli",
|
|
117
|
-
version: "1.3.
|
|
117
|
+
version: "1.3.9",
|
|
118
118
|
description: "Remote control Claude Code from your mobile device",
|
|
119
119
|
main: "dist/index.js",
|
|
120
120
|
bin: {
|
|
@@ -802,32 +802,41 @@ var ClaudeService = class {
|
|
|
802
802
|
this.strategy.write(text + "\r");
|
|
803
803
|
}
|
|
804
804
|
/**
|
|
805
|
-
* Navigate a React Ink selector to the given 0-based index and confirm.
|
|
805
|
+
* Navigate a React Ink selector to the given 0-based target index and confirm.
|
|
806
|
+
*
|
|
807
|
+
* `fromIndex` is the current highlighted position (defaults to 0 for
|
|
808
|
+
* numbered selectors which always start at the first option). For list-style
|
|
809
|
+
* selectors (e.g. /mcp), the CLI sends `currentIndex` in the select_prompt
|
|
810
|
+
* chunk so the client can pass it back here as `fromIndex`, enabling both
|
|
811
|
+
* up-arrow and down-arrow navigation without always rewinding to position 0.
|
|
806
812
|
*
|
|
807
813
|
* Why not sendCommand(arrows + Enter) in one write()?
|
|
808
814
|
* All bytes arrive as one chunk → readline fires all keypress events in the
|
|
809
815
|
* same synchronous run → React Ink batches the state updates → each arrow
|
|
810
816
|
* sees selectedIndex=0 → final state is still 0 or 1 → wrong option selected.
|
|
811
817
|
*
|
|
812
|
-
* Fix: send each
|
|
813
|
-
*
|
|
818
|
+
* Fix: send each arrow in a separate write(), ARROW_MS apart, so React has
|
|
819
|
+
* time to process and re-render between each keystroke. Enter is sent
|
|
814
820
|
* ENTER_MS after the last arrow.
|
|
815
821
|
*/
|
|
816
|
-
selectOption(
|
|
817
|
-
|
|
822
|
+
selectOption(targetIndex, fromIndex = 0) {
|
|
823
|
+
const delta = targetIndex - fromIndex;
|
|
824
|
+
const steps = Math.abs(delta);
|
|
825
|
+
const arrow = delta >= 0 ? "\x1B[B" : "\x1B[A";
|
|
826
|
+
const ARROW_MS = 80;
|
|
827
|
+
const ENTER_MS = 200;
|
|
828
|
+
if (steps === 0) {
|
|
818
829
|
this.strategy.write("\r");
|
|
819
830
|
return;
|
|
820
831
|
}
|
|
821
|
-
|
|
822
|
-
const ENTER_MS = 200;
|
|
823
|
-
for (let i = 0; i < index; i++) {
|
|
832
|
+
for (let i = 0; i < steps; i++) {
|
|
824
833
|
setTimeout(() => {
|
|
825
|
-
this.strategy.write(
|
|
834
|
+
this.strategy.write(arrow);
|
|
826
835
|
}, i * ARROW_MS);
|
|
827
836
|
}
|
|
828
837
|
setTimeout(() => {
|
|
829
838
|
this.strategy.write("\r");
|
|
830
|
-
},
|
|
839
|
+
}, steps * ARROW_MS + ENTER_MS);
|
|
831
840
|
}
|
|
832
841
|
/** Send Escape key to Claude (cancels interactive prompts). */
|
|
833
842
|
sendEscape() {
|
|
@@ -995,7 +1004,60 @@ function detectSelector(lines) {
|
|
|
995
1004
|
return {
|
|
996
1005
|
question,
|
|
997
1006
|
options: keys.map((k) => optionLabels.get(k)),
|
|
998
|
-
optionDescriptions: keys.map((k) => (optionDescs.get(k) ?? []).join(" ").trim())
|
|
1007
|
+
optionDescriptions: keys.map((k) => (optionDescs.get(k) ?? []).join(" ").trim()),
|
|
1008
|
+
currentIndex: 0
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
function detectListSelector(lines) {
|
|
1012
|
+
if (!lines.some((l) => /[↑↓].*navigate/i.test(l.trim()))) return null;
|
|
1013
|
+
if (lines.some((l) => /^❯\s*\d+\./.test(l.trim()))) return null;
|
|
1014
|
+
if (!lines.some((l) => /^\s+❯\s+\S/.test(l))) return null;
|
|
1015
|
+
const isSelected = (line) => /^\s+❯\s+\S/.test(line);
|
|
1016
|
+
const isUnselected = (line) => /^ \S/.test(line);
|
|
1017
|
+
const isItem = (line) => isSelected(line) || isUnselected(line);
|
|
1018
|
+
let optionStartIdx = -1;
|
|
1019
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1020
|
+
if (isItem(lines[i])) {
|
|
1021
|
+
optionStartIdx = i;
|
|
1022
|
+
break;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
if (optionStartIdx === -1) return null;
|
|
1026
|
+
const questionParts = [];
|
|
1027
|
+
for (let i = 0; i < optionStartIdx; i++) {
|
|
1028
|
+
const t = lines[i].trim();
|
|
1029
|
+
if (!t) continue;
|
|
1030
|
+
if (/^[─━—═\-]{3,}$/.test(t)) continue;
|
|
1031
|
+
if (/[┌└│┐┘├┤┬┴┼]/.test(t)) {
|
|
1032
|
+
const inner = t.replace(/[│┌└┐┘├┤┬┴┼─]/g, "").trim();
|
|
1033
|
+
if (inner) questionParts.push(inner);
|
|
1034
|
+
continue;
|
|
1035
|
+
}
|
|
1036
|
+
if (/^[>❯]\s/.test(t)) continue;
|
|
1037
|
+
if (/[↑↓].*navigate/i.test(t)) continue;
|
|
1038
|
+
questionParts.push(t);
|
|
1039
|
+
}
|
|
1040
|
+
const question = questionParts.join(" ").trim();
|
|
1041
|
+
const options = [];
|
|
1042
|
+
let currentIndex = 0;
|
|
1043
|
+
for (const line of lines.slice(optionStartIdx)) {
|
|
1044
|
+
const t = line.trim();
|
|
1045
|
+
if (!t) continue;
|
|
1046
|
+
if (/[↑↓].*navigate/i.test(t)) break;
|
|
1047
|
+
if (/^[─━—═\-]{3,}$/.test(t)) continue;
|
|
1048
|
+
if (isSelected(line)) {
|
|
1049
|
+
currentIndex = options.length;
|
|
1050
|
+
options.push(t.replace(/^❯\s+/, "").trim());
|
|
1051
|
+
} else if (isUnselected(line)) {
|
|
1052
|
+
options.push(t);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
if (options.length < 2) return null;
|
|
1056
|
+
return {
|
|
1057
|
+
question,
|
|
1058
|
+
options,
|
|
1059
|
+
optionDescriptions: options.map(() => ""),
|
|
1060
|
+
currentIndex
|
|
999
1061
|
};
|
|
1000
1062
|
}
|
|
1001
1063
|
function filterChrome(lines) {
|
|
@@ -1077,13 +1139,13 @@ var OutputService = class _OutputService {
|
|
|
1077
1139
|
return;
|
|
1078
1140
|
}
|
|
1079
1141
|
const lines = renderToLines(this.rawBuffer);
|
|
1080
|
-
const selector = detectSelector(lines);
|
|
1142
|
+
const selector = detectSelector(lines) ?? detectListSelector(lines);
|
|
1081
1143
|
if (selector) {
|
|
1082
1144
|
const idleMs2 = this.lastPushTime > 0 ? now - this.lastPushTime : elapsed;
|
|
1083
1145
|
if (idleMs2 >= _OutputService.SELECTOR_IDLE_MS) {
|
|
1084
1146
|
this.stopPoll();
|
|
1085
1147
|
this.active = false;
|
|
1086
|
-
this.postChunk({ type: "select_prompt", content: selector.question, options: selector.options, optionDescriptions: selector.optionDescriptions, done: true }).catch(() => {
|
|
1148
|
+
this.postChunk({ type: "select_prompt", content: selector.question, options: selector.options, optionDescriptions: selector.optionDescriptions, currentIndex: selector.currentIndex, done: true }).catch(() => {
|
|
1087
1149
|
});
|
|
1088
1150
|
}
|
|
1089
1151
|
return;
|
|
@@ -1106,11 +1168,11 @@ var OutputService = class _OutputService {
|
|
|
1106
1168
|
}
|
|
1107
1169
|
finalize() {
|
|
1108
1170
|
const lines = renderToLines(this.rawBuffer);
|
|
1109
|
-
const selector = detectSelector(lines);
|
|
1171
|
+
const selector = detectSelector(lines) ?? detectListSelector(lines);
|
|
1110
1172
|
this.stopPoll();
|
|
1111
1173
|
this.active = false;
|
|
1112
1174
|
if (selector) {
|
|
1113
|
-
this.postChunk({ type: "select_prompt", content: selector.question, options: selector.options, optionDescriptions: selector.optionDescriptions, done: true }).catch(() => {
|
|
1175
|
+
this.postChunk({ type: "select_prompt", content: selector.question, options: selector.options, optionDescriptions: selector.optionDescriptions, currentIndex: selector.currentIndex, done: true }).catch(() => {
|
|
1114
1176
|
});
|
|
1115
1177
|
} else {
|
|
1116
1178
|
const content = filterChrome(lines).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
@@ -1232,8 +1294,9 @@ async function start() {
|
|
|
1232
1294
|
}
|
|
1233
1295
|
case "select_option": {
|
|
1234
1296
|
const index = cmd.payload.index ?? 0;
|
|
1297
|
+
const from = cmd.payload.from ?? 0;
|
|
1235
1298
|
outputSvc.newTurn();
|
|
1236
|
-
claude.selectOption(index);
|
|
1299
|
+
claude.selectOption(index, from);
|
|
1237
1300
|
break;
|
|
1238
1301
|
}
|
|
1239
1302
|
case "escape_key":
|
|
@@ -1279,8 +1342,9 @@ async function start() {
|
|
|
1279
1342
|
if (input) sendPrompt(input);
|
|
1280
1343
|
} else if (cmdType === "select_option") {
|
|
1281
1344
|
const index = inner.index ?? 0;
|
|
1345
|
+
const from = inner.from ?? 0;
|
|
1282
1346
|
outputSvc.newTurn();
|
|
1283
|
-
claude.selectOption(index);
|
|
1347
|
+
claude.selectOption(index, from);
|
|
1284
1348
|
} else if (cmdType === "escape_key") {
|
|
1285
1349
|
outputSvc.newTurn();
|
|
1286
1350
|
claude.sendEscape();
|