cli-wechat-bridge 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.
- package/README.md +129 -150
- package/bin/_run-entry.mjs +12 -0
- package/bin/wechat-claude-start.mjs +7 -2
- package/bin/wechat-claude.mjs +1 -1
- package/bin/wechat-codex-start.mjs +7 -2
- package/bin/wechat-codex.mjs +1 -1
- package/bin/wechat-opencode-start.mjs +7 -2
- package/bin/wechat-opencode.mjs +1 -1
- package/bin/wechat-pi-start.mjs +10 -0
- package/bin/wechat-pi.mjs +5 -0
- package/dist/bridge/bridge-adapter-common.js +2 -0
- package/dist/bridge/bridge-adapters.claude.js +21 -1
- package/dist/bridge/bridge-adapters.codex.js +85 -6
- package/dist/bridge/bridge-adapters.core.js +112 -6
- package/dist/bridge/bridge-adapters.js +5 -3
- package/dist/bridge/bridge-adapters.opencode.js +390 -52
- package/dist/bridge/bridge-adapters.pi.js +707 -0
- package/dist/bridge/bridge-adapters.shared.js +22 -102
- package/dist/bridge/bridge-final-reply.js +17 -2
- package/dist/bridge/bridge-process-reaper.js +14 -4
- package/dist/bridge/bridge-state.js +42 -6
- package/dist/bridge/bridge-utils.js +94 -237
- package/dist/bridge/wechat-bridge.js +77 -131
- package/dist/bridge/wechat-forwarding.js +58 -0
- package/dist/companion/codex-remote-client.js +20 -6
- package/dist/companion/local-companion-link.js +13 -3
- package/dist/companion/local-companion-start.js +64 -16
- package/dist/companion/local-companion.js +8 -5
- package/dist/companion/pi-tui-bridge-extension.js +256 -0
- package/dist/daemon/daemon-link.js +25 -3
- package/dist/daemon/wechat-daemon.js +264 -77
- package/dist/i18n/index.js +0 -3
- package/dist/i18n/messages-en.js +4 -5
- package/dist/i18n/messages-zh.js +4 -5
- package/dist/utils/atomic-file.js +15 -0
- package/dist/utils/doctor.js +27 -11
- package/dist/wechat/channel-config.js +0 -2
- package/dist/wechat/setup.js +5 -9
- package/dist/wechat/wechat-transport.js +14 -8
- package/package.json +113 -102
- package/bin/wechat-bridge-claude.mjs +0 -5
- package/bin/wechat-bridge-codex.mjs +0 -5
- package/bin/wechat-bridge-opencode.mjs +0 -5
- package/bin/wechat-bridge-shell.mjs +0 -5
- package/bin/wechat-bridge.mjs +0 -5
- package/dist/bridge/bridge-adapters.shell.js +0 -363
|
@@ -61,45 +61,40 @@ const INLINE_VOICE_EXTENSIONS = new Set([
|
|
|
61
61
|
".ogg",
|
|
62
62
|
".aac",
|
|
63
63
|
]);
|
|
64
|
+
// Directly executable / installer file types are never auto-uploaded from a
|
|
65
|
+
// path mentioned in a reply: handing these to WeChat contacts by accident is
|
|
66
|
+
// a real hazard. Everything else — source code, documents, archives, data
|
|
67
|
+
// files — stays auto-sendable so common workflows keep working.
|
|
64
68
|
const INLINE_REFERENCE_ONLY_FILE_EXTENSIONS = new Set([
|
|
69
|
+
// Windows binaries and installers
|
|
70
|
+
".exe",
|
|
71
|
+
".dll",
|
|
72
|
+
".sys",
|
|
73
|
+
".msi",
|
|
74
|
+
".msp",
|
|
75
|
+
".scr",
|
|
76
|
+
".com",
|
|
77
|
+
".pif",
|
|
78
|
+
".cpl",
|
|
79
|
+
".ocx",
|
|
80
|
+
".hta",
|
|
81
|
+
".jar",
|
|
82
|
+
// Windows one-click script launchers
|
|
65
83
|
".bat",
|
|
66
|
-
".c",
|
|
67
|
-
".cc",
|
|
68
|
-
".cjs",
|
|
69
84
|
".cmd",
|
|
70
|
-
".cpp",
|
|
71
|
-
".cs",
|
|
72
|
-
".cts",
|
|
73
|
-
".cxx",
|
|
74
|
-
".go",
|
|
75
|
-
".h",
|
|
76
|
-
".hh",
|
|
77
|
-
".hpp",
|
|
78
|
-
".java",
|
|
79
|
-
".js",
|
|
80
|
-
".jsx",
|
|
81
|
-
".kt",
|
|
82
|
-
".kts",
|
|
83
|
-
".lua",
|
|
84
|
-
".m",
|
|
85
|
-
".mjs",
|
|
86
|
-
".mm",
|
|
87
|
-
".mts",
|
|
88
|
-
".php",
|
|
89
|
-
".pl",
|
|
90
85
|
".ps1",
|
|
91
|
-
".
|
|
92
|
-
".
|
|
93
|
-
".
|
|
94
|
-
".
|
|
95
|
-
".
|
|
96
|
-
|
|
86
|
+
".vbs",
|
|
87
|
+
".vbe",
|
|
88
|
+
".jse",
|
|
89
|
+
".wsf",
|
|
90
|
+
".wsh",
|
|
91
|
+
// Unix one-click script launchers and installable packages
|
|
97
92
|
".sh",
|
|
98
|
-
".
|
|
99
|
-
".
|
|
100
|
-
".
|
|
101
|
-
".
|
|
102
|
-
".
|
|
93
|
+
".bash",
|
|
94
|
+
".deb",
|
|
95
|
+
".rpm",
|
|
96
|
+
".apk",
|
|
97
|
+
".dmg",
|
|
103
98
|
]);
|
|
104
99
|
const INLINE_MAAS_URL_RE = /https?:\/\/[^\s]*?\/([A-Za-z]:\\.+?(?:\.\s*[A-Za-z0-9]{2,8})+)(?:\?[^\n]*)?/g;
|
|
105
100
|
const INLINE_WINDOWS_PATH_RE = /(^|[^\w])`?([A-Za-z]:\\(?:[^\\/:*?"<>|\r\n`]+\\)*[^\\/:*?"<>|\r\n`]+?(?:\.\s*[A-Za-z0-9]{2,8})+)`?(?=$|[^\w])/gm;
|
|
@@ -451,176 +446,6 @@ export function isHighRiskShellCommand(command) {
|
|
|
451
446
|
}
|
|
452
447
|
return HIGH_RISK_PATTERNS.some((pattern) => pattern.test(normalized));
|
|
453
448
|
}
|
|
454
|
-
const ALWAYS_INTERACTIVE_SHELL_COMMANDS = new Set([
|
|
455
|
-
"ftp",
|
|
456
|
-
"htop",
|
|
457
|
-
"irb",
|
|
458
|
-
"less",
|
|
459
|
-
"more",
|
|
460
|
-
"mongosh",
|
|
461
|
-
"mysql",
|
|
462
|
-
"nano",
|
|
463
|
-
"nvim",
|
|
464
|
-
"psql",
|
|
465
|
-
"redis-cli",
|
|
466
|
-
"screen",
|
|
467
|
-
"sftp",
|
|
468
|
-
"sqlite3",
|
|
469
|
-
"ssh",
|
|
470
|
-
"telnet",
|
|
471
|
-
"tmux",
|
|
472
|
-
"top",
|
|
473
|
-
"vi",
|
|
474
|
-
"vim",
|
|
475
|
-
"watch",
|
|
476
|
-
]);
|
|
477
|
-
function tokenizeShellCommand(command, maxTokens = 16) {
|
|
478
|
-
const tokens = [];
|
|
479
|
-
let current = "";
|
|
480
|
-
let quote = null;
|
|
481
|
-
for (const char of command.trim()) {
|
|
482
|
-
if (quote) {
|
|
483
|
-
if (char === quote) {
|
|
484
|
-
quote = null;
|
|
485
|
-
}
|
|
486
|
-
else {
|
|
487
|
-
current += char;
|
|
488
|
-
}
|
|
489
|
-
continue;
|
|
490
|
-
}
|
|
491
|
-
if (char === '"' || char === "'") {
|
|
492
|
-
quote = char;
|
|
493
|
-
continue;
|
|
494
|
-
}
|
|
495
|
-
if (/\s/u.test(char)) {
|
|
496
|
-
if (current) {
|
|
497
|
-
tokens.push(current);
|
|
498
|
-
current = "";
|
|
499
|
-
if (tokens.length >= maxTokens) {
|
|
500
|
-
return tokens;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
continue;
|
|
504
|
-
}
|
|
505
|
-
current += char;
|
|
506
|
-
}
|
|
507
|
-
if (current) {
|
|
508
|
-
tokens.push(current);
|
|
509
|
-
}
|
|
510
|
-
return tokens;
|
|
511
|
-
}
|
|
512
|
-
function normalizeShellExecutableToken(token) {
|
|
513
|
-
const trimmed = token.trim().replace(/^["']|["']$/g, "");
|
|
514
|
-
if (!trimmed) {
|
|
515
|
-
return "";
|
|
516
|
-
}
|
|
517
|
-
return path.parse(trimmed).name.toLowerCase();
|
|
518
|
-
}
|
|
519
|
-
function findCommandFlagIndex(args, supportedFlags) {
|
|
520
|
-
return args.findIndex((arg) => supportedFlags.includes(arg.toLowerCase()));
|
|
521
|
-
}
|
|
522
|
-
function hasScriptLikeArg(args) {
|
|
523
|
-
return args.some((arg) => Boolean(arg) && !arg.startsWith("-") && !arg.startsWith("/"));
|
|
524
|
-
}
|
|
525
|
-
function hasAnyCommandFlag(args, supportedFlags) {
|
|
526
|
-
return findCommandFlagIndex(args, supportedFlags) >= 0;
|
|
527
|
-
}
|
|
528
|
-
function buildInteractiveShellCommandMessage(executable, suggestion) {
|
|
529
|
-
return `Interactive command "${executable}" is not supported in shell mode yet. This shell bridge currently only supports non-interactive commands and scripts. ${suggestion}`;
|
|
530
|
-
}
|
|
531
|
-
export function getInteractiveShellCommandRejectionMessage(command) {
|
|
532
|
-
const tokens = tokenizeShellCommand(command);
|
|
533
|
-
if (!tokens.length) {
|
|
534
|
-
return null;
|
|
535
|
-
}
|
|
536
|
-
const firstToken = tokens[0];
|
|
537
|
-
if (!firstToken) {
|
|
538
|
-
return null;
|
|
539
|
-
}
|
|
540
|
-
const executable = normalizeShellExecutableToken(firstToken);
|
|
541
|
-
const args = tokens.slice(1);
|
|
542
|
-
const lowerArgs = args.map((arg) => arg.toLowerCase());
|
|
543
|
-
if (!executable) {
|
|
544
|
-
return null;
|
|
545
|
-
}
|
|
546
|
-
if (ALWAYS_INTERACTIVE_SHELL_COMMANDS.has(executable)) {
|
|
547
|
-
return buildInteractiveShellCommandMessage(executable, "Run a non-interactive command or script instead.");
|
|
548
|
-
}
|
|
549
|
-
switch (executable) {
|
|
550
|
-
case "python":
|
|
551
|
-
case "python3":
|
|
552
|
-
case "py": {
|
|
553
|
-
if (!args.length) {
|
|
554
|
-
return buildInteractiveShellCommandMessage(executable, 'Try "python script.py" or "python -c \\"...\\"" instead.');
|
|
555
|
-
}
|
|
556
|
-
if (lowerArgs.includes("-i") || lowerArgs.includes("--interactive")) {
|
|
557
|
-
return buildInteractiveShellCommandMessage(executable, 'Try "python script.py" or "python -c \\"...\\"" instead.');
|
|
558
|
-
}
|
|
559
|
-
if (hasAnyCommandFlag(lowerArgs, ["-c", "-h", "--help", "-v", "--version"])) {
|
|
560
|
-
return null;
|
|
561
|
-
}
|
|
562
|
-
const moduleFlagIndex = findCommandFlagIndex(lowerArgs, ["-m"]);
|
|
563
|
-
if (moduleFlagIndex >= 0) {
|
|
564
|
-
return moduleFlagIndex < args.length - 1
|
|
565
|
-
? null
|
|
566
|
-
: buildInteractiveShellCommandMessage(executable, 'Try "python script.py" or "python -m module_name" instead.');
|
|
567
|
-
}
|
|
568
|
-
return hasScriptLikeArg(args)
|
|
569
|
-
? null
|
|
570
|
-
: buildInteractiveShellCommandMessage(executable, 'Try "python script.py" or "python -c \\"...\\"" instead.');
|
|
571
|
-
}
|
|
572
|
-
case "node":
|
|
573
|
-
if (!args.length || lowerArgs.includes("-i") || lowerArgs.includes("--interactive")) {
|
|
574
|
-
return buildInteractiveShellCommandMessage(executable, 'Try "node script.js" or "node -e \\"...\\"" instead.');
|
|
575
|
-
}
|
|
576
|
-
if (hasAnyCommandFlag(lowerArgs, ["-e", "--eval", "-p", "--print", "-h", "--help", "-v", "--version"])) {
|
|
577
|
-
return null;
|
|
578
|
-
}
|
|
579
|
-
return hasScriptLikeArg(args)
|
|
580
|
-
? null
|
|
581
|
-
: buildInteractiveShellCommandMessage(executable, 'Try "node script.js" or "node -e \\"...\\"" instead.');
|
|
582
|
-
case "cmd":
|
|
583
|
-
if (lowerArgs.includes("/?")) {
|
|
584
|
-
return null;
|
|
585
|
-
}
|
|
586
|
-
if (lowerArgs.includes("/k") || !lowerArgs.includes("/c")) {
|
|
587
|
-
return buildInteractiveShellCommandMessage(executable, 'Try "cmd /c <command>" or run the command directly instead.');
|
|
588
|
-
}
|
|
589
|
-
return null;
|
|
590
|
-
case "powershell":
|
|
591
|
-
case "pwsh":
|
|
592
|
-
if (!args.length ||
|
|
593
|
-
lowerArgs.includes("-noexit") ||
|
|
594
|
-
lowerArgs.includes("-nologo") && args.length === 1) {
|
|
595
|
-
return buildInteractiveShellCommandMessage(executable, `Try "${executable} -Command \\"...\\"" or "${executable} -File script.ps1" instead.`);
|
|
596
|
-
}
|
|
597
|
-
if (hasAnyCommandFlag(lowerArgs, ["-c", "-command", "-enc", "-encodedcommand", "-f", "-file", "-h", "-help", "-v", "-version", "-?"])) {
|
|
598
|
-
return null;
|
|
599
|
-
}
|
|
600
|
-
return hasScriptLikeArg(args)
|
|
601
|
-
? null
|
|
602
|
-
: buildInteractiveShellCommandMessage(executable, `Try "${executable} -Command \\"...\\"" or "${executable} -File script.ps1" instead.`);
|
|
603
|
-
case "bash":
|
|
604
|
-
case "dash":
|
|
605
|
-
case "ksh":
|
|
606
|
-
case "sh":
|
|
607
|
-
case "zsh":
|
|
608
|
-
if (!args.length || lowerArgs.includes("-i")) {
|
|
609
|
-
return buildInteractiveShellCommandMessage(executable, `Try "${executable} -c '...'" or "${executable} script.sh" instead.`);
|
|
610
|
-
}
|
|
611
|
-
if (findCommandFlagIndex(lowerArgs, ["-c", "-lc"]) >= 0) {
|
|
612
|
-
return null;
|
|
613
|
-
}
|
|
614
|
-
if (hasAnyCommandFlag(lowerArgs, ["-h", "--help", "--version"])) {
|
|
615
|
-
return null;
|
|
616
|
-
}
|
|
617
|
-
return hasScriptLikeArg(args)
|
|
618
|
-
? null
|
|
619
|
-
: buildInteractiveShellCommandMessage(executable, `Try "${executable} -c '...'" or "${executable} script.sh" instead.`);
|
|
620
|
-
default:
|
|
621
|
-
return null;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
449
|
export function detectCliApproval(text) {
|
|
625
450
|
const normalized = normalizeOutput(text);
|
|
626
451
|
const compact = normalized.replace(/\s+/g, " ").trim();
|
|
@@ -802,22 +627,19 @@ export function formatMirroredUserInputMessage(adapter, text) {
|
|
|
802
627
|
? "Local Claude input"
|
|
803
628
|
: adapter === "opencode"
|
|
804
629
|
? "Local OpenCode input"
|
|
805
|
-
:
|
|
630
|
+
: adapter === "pi"
|
|
631
|
+
? "Local Pi input"
|
|
632
|
+
: "Local input";
|
|
806
633
|
return `${label}:\n${truncatePreview(text, 500)}`;
|
|
807
634
|
}
|
|
808
|
-
export function formatFinalReplyMessage(
|
|
809
|
-
|
|
810
|
-
return text;
|
|
811
|
-
}
|
|
812
|
-
// After the early return above, only "shell" remains.
|
|
813
|
-
const label = adapter === "codex" ? "Codex" : adapter === "claude" ? "Claude" : adapter === "opencode" ? "OpenCode" : adapter;
|
|
814
|
-
return `${label} final reply:\n${text}`;
|
|
635
|
+
export function formatFinalReplyMessage(_adapter, text) {
|
|
636
|
+
return text;
|
|
815
637
|
}
|
|
816
638
|
const OPENCODE_WORKING_NOTICE_RE = /^OpenCode is still working on:\s*$/i;
|
|
817
639
|
const OPENCODE_TRANSIENT_NOTICE_RES = [
|
|
818
640
|
/^Bridge error: opencode companion is not connected\./i,
|
|
819
641
|
/^OpenCode companion is not connected(?: for bridge workspace)?:?$/i,
|
|
820
|
-
/^Run "wechat-
|
|
642
|
+
/^Run "wechat-opencode".*$/i,
|
|
821
643
|
/^OpenCode session switched to \S+ from the local terminal\.$/i,
|
|
822
644
|
/^Local OpenCode input:\s*$/i,
|
|
823
645
|
];
|
|
@@ -912,7 +734,7 @@ export function sanitizeWechatFinalReplyText(adapter, text) {
|
|
|
912
734
|
const resolved = tail || cleaned;
|
|
913
735
|
return isOpenCodeReasoningResidue(resolved) ? "" : resolved;
|
|
914
736
|
}
|
|
915
|
-
function extractInlineWechatAttachments(text) {
|
|
737
|
+
export function extractInlineWechatAttachments(text) {
|
|
916
738
|
const sanitized = text
|
|
917
739
|
.replace(/\\\n\s*/g, "\\")
|
|
918
740
|
.replace(/\.\s*\n?\s*([A-Za-z0-9]{2,8})(?=\?)/g, ".$1")
|
|
@@ -1000,8 +822,9 @@ function inferInlineWechatAttachmentKind(filePath) {
|
|
|
1000
822
|
if (INLINE_VOICE_EXTENSIONS.has(extension)) {
|
|
1001
823
|
return "voice";
|
|
1002
824
|
}
|
|
1003
|
-
//
|
|
1004
|
-
//
|
|
825
|
+
// Ordinary local files stay auto-sendable; only directly executable /
|
|
826
|
+
// installer types are excluded so a path mentioned in prose cannot
|
|
827
|
+
// accidentally ship a runnable binary to a WeChat contact.
|
|
1005
828
|
if (!extension || INLINE_REFERENCE_ONLY_FILE_EXTENSIONS.has(extension)) {
|
|
1006
829
|
return null;
|
|
1007
830
|
}
|
|
@@ -1016,7 +839,15 @@ function cleanupVisibleWechatReplyText(text) {
|
|
|
1016
839
|
.trim();
|
|
1017
840
|
}
|
|
1018
841
|
export function formatTaskFailedMessage(adapter, text) {
|
|
1019
|
-
const label = adapter === "codex"
|
|
842
|
+
const label = adapter === "codex"
|
|
843
|
+
? "Codex"
|
|
844
|
+
: adapter === "claude"
|
|
845
|
+
? "Claude"
|
|
846
|
+
: adapter === "opencode"
|
|
847
|
+
? "OpenCode"
|
|
848
|
+
: adapter === "pi"
|
|
849
|
+
? "Pi"
|
|
850
|
+
: adapter;
|
|
1020
851
|
return `${label} task failed:\n${text}`;
|
|
1021
852
|
}
|
|
1022
853
|
export function formatApprovalMessage(pending, adapterState) {
|
|
@@ -1035,7 +866,7 @@ export function formatApprovalMessage(pending, adapterState) {
|
|
|
1035
866
|
].join("\n");
|
|
1036
867
|
}
|
|
1037
868
|
return [
|
|
1038
|
-
|
|
869
|
+
"CLI approval is required.",
|
|
1039
870
|
`adapter: ${adapterState.kind}`,
|
|
1040
871
|
`summary: ${pending.summary}`,
|
|
1041
872
|
`target: ${pending.commandPreview}`,
|
|
@@ -1090,22 +921,35 @@ function parseSingleUserInputAnswer(question, rawValue) {
|
|
|
1090
921
|
answers: [trimmed],
|
|
1091
922
|
};
|
|
1092
923
|
}
|
|
1093
|
-
const
|
|
924
|
+
const customAnswerMode = question.customAnswerMode ?? "note";
|
|
925
|
+
const separatorIndex = customAnswerMode === "note" ? trimmed.indexOf("|") : -1;
|
|
1094
926
|
const selection = separatorIndex >= 0 ? trimmed.slice(0, separatorIndex).trim() : trimmed;
|
|
1095
927
|
let note = separatorIndex >= 0 ? trimmed.slice(separatorIndex + 1).trim() : "";
|
|
1096
928
|
const answers = [];
|
|
1097
929
|
if (selection) {
|
|
1098
|
-
const
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
930
|
+
const selections = question.multiple
|
|
931
|
+
? selection.split(",").map((value) => value.trim()).filter(Boolean)
|
|
932
|
+
: [selection];
|
|
933
|
+
for (const value of selections) {
|
|
934
|
+
const selectedLabel = resolveUserInputOptionLabel(question, value);
|
|
935
|
+
if (selectedLabel) {
|
|
936
|
+
if (!answers.includes(selectedLabel)) {
|
|
937
|
+
answers.push(selectedLabel);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
else if (question.isOther && customAnswerMode === "value") {
|
|
941
|
+
if (!answers.includes(value)) {
|
|
942
|
+
answers.push(value);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
else if (question.isOther) {
|
|
946
|
+
note = note ? `${value}; ${note}` : value;
|
|
947
|
+
}
|
|
948
|
+
else {
|
|
949
|
+
return {
|
|
950
|
+
error: `Question "${question.header}" expects an option number or label.`,
|
|
951
|
+
};
|
|
952
|
+
}
|
|
1109
953
|
}
|
|
1110
954
|
}
|
|
1111
955
|
if (note) {
|
|
@@ -1210,8 +1054,13 @@ export function formatUserInputRequestMessage(pending, adapterState) {
|
|
|
1210
1054
|
lines.push(` ${optionIndex + 1}. ${option.label} - ${truncatePreview(option.description, 160)}`);
|
|
1211
1055
|
});
|
|
1212
1056
|
}
|
|
1057
|
+
if (question.multiple) {
|
|
1058
|
+
lines.push("multiple selections: allowed (separate values with commas)");
|
|
1059
|
+
}
|
|
1213
1060
|
if (question.isOther) {
|
|
1214
|
-
lines.push(
|
|
1061
|
+
lines.push(question.customAnswerMode === "value"
|
|
1062
|
+
? "custom answer: allowed"
|
|
1063
|
+
: "custom note: allowed");
|
|
1215
1064
|
}
|
|
1216
1065
|
});
|
|
1217
1066
|
lines.push("");
|
|
@@ -1221,8 +1070,13 @@ export function formatUserInputRequestMessage(pending, adapterState) {
|
|
|
1221
1070
|
return lines.join("\n");
|
|
1222
1071
|
}
|
|
1223
1072
|
if (question.options?.length) {
|
|
1224
|
-
lines.push(
|
|
1225
|
-
|
|
1073
|
+
lines.push(question.multiple
|
|
1074
|
+
? "Reply with /answer <option numbers or exact labels separated by commas>."
|
|
1075
|
+
: "Reply with /answer <option number or exact label>.");
|
|
1076
|
+
if (question.isOther && question.customAnswerMode === "value") {
|
|
1077
|
+
lines.push("You may enter a custom answer instead of an option.");
|
|
1078
|
+
}
|
|
1079
|
+
else if (question.isOther) {
|
|
1226
1080
|
lines.push('Add "| your note" to include extra context.');
|
|
1227
1081
|
}
|
|
1228
1082
|
}
|
|
@@ -1234,7 +1088,10 @@ export function formatUserInputRequestMessage(pending, adapterState) {
|
|
|
1234
1088
|
lines.push("Reply with /answer questionId=value; questionId2=value.");
|
|
1235
1089
|
lines.push("You can use question numbers instead of ids.");
|
|
1236
1090
|
lines.push("For option questions, value can be the option number or exact label.");
|
|
1237
|
-
lines.push(
|
|
1091
|
+
lines.push("For multi-select questions, separate option values with commas.");
|
|
1092
|
+
if (pending.questions.some((question) => question.customAnswerMode !== "value")) {
|
|
1093
|
+
lines.push('Add "| your note" after an option answer to include extra context.');
|
|
1094
|
+
}
|
|
1238
1095
|
}
|
|
1239
1096
|
lines.push("Use /stop to interrupt the active turn instead.");
|
|
1240
1097
|
return lines.join("\n");
|
|
@@ -1243,11 +1100,11 @@ export function formatPendingUserInputReminder(pending) {
|
|
|
1243
1100
|
if (pending.questions.length === 1) {
|
|
1244
1101
|
const question = pending.questions[0];
|
|
1245
1102
|
if (!question) {
|
|
1246
|
-
return "
|
|
1103
|
+
return "The active CLI is waiting for user input. Reply with /answer and your response, or use /stop to interrupt.";
|
|
1247
1104
|
}
|
|
1248
|
-
return `
|
|
1105
|
+
return `The active CLI is waiting for user input for ${question.header}. Reply with /answer and your response, or use /stop to interrupt.`;
|
|
1249
1106
|
}
|
|
1250
|
-
return `
|
|
1107
|
+
return `The active CLI is waiting for answers to ${pending.questions.length} questions. Reply with /answer questionId=value; questionId2=value, or use /stop to interrupt.`;
|
|
1251
1108
|
}
|
|
1252
1109
|
export class OutputBatcher {
|
|
1253
1110
|
onFlush;
|