@dugleelabs/copair 1.2.0 → 1.3.0
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 +632 -231
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { join as join15 } from "path";
|
|
5
|
+
import { existsSync as existsSync18, readFileSync as readFileSync10 } from "fs";
|
|
5
6
|
|
|
6
7
|
// src/cli/args.ts
|
|
7
8
|
import { Command } from "commander";
|
|
@@ -4757,14 +4758,65 @@ var AgentBridge = class extends EventEmitter {
|
|
|
4757
4758
|
};
|
|
4758
4759
|
|
|
4759
4760
|
// src/cli/ui/app.tsx
|
|
4760
|
-
import { useState as
|
|
4761
|
-
import { render, Box as
|
|
4761
|
+
import { useState as useState6, useEffect as useEffect4, useCallback as useCallback3, useImperativeHandle, forwardRef, useRef as useRef2 } from "react";
|
|
4762
|
+
import { render, Box as Box8, Text as Text10, Static, useApp, useInput as useInput4 } from "ink";
|
|
4762
4763
|
|
|
4763
4764
|
// src/cli/ui/bordered-input.tsx
|
|
4764
4765
|
import { useState, useEffect, useCallback, useRef } from "react";
|
|
4765
|
-
import { Box, Text, useStdout, useInput } from "ink";
|
|
4766
|
-
|
|
4767
|
-
|
|
4766
|
+
import { Box, Text as Text2, useStdout, useInput } from "ink";
|
|
4767
|
+
|
|
4768
|
+
// src/cli/ui/cursor-text.tsx
|
|
4769
|
+
import { Text } from "ink";
|
|
4770
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4771
|
+
function CursorText({ value, cursorPos, active }) {
|
|
4772
|
+
if (!active) return /* @__PURE__ */ jsx(Text, { children: value });
|
|
4773
|
+
const chars = [...value];
|
|
4774
|
+
const before = chars.slice(0, cursorPos).join("");
|
|
4775
|
+
const at = chars[cursorPos] ?? " ";
|
|
4776
|
+
const after = chars.slice(cursorPos + 1).join("");
|
|
4777
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4778
|
+
/* @__PURE__ */ jsx(Text, { children: before }),
|
|
4779
|
+
/* @__PURE__ */ jsx(Text, { inverse: true, children: at }),
|
|
4780
|
+
/* @__PURE__ */ jsx(Text, { children: after })
|
|
4781
|
+
] });
|
|
4782
|
+
}
|
|
4783
|
+
|
|
4784
|
+
// src/cli/ui/cursor-utils.ts
|
|
4785
|
+
function detectWordNav(input) {
|
|
4786
|
+
if (input === "\x1B[1;3D" || input === "\x1Bb" || input === "\x1B[1;5D") return "word-left";
|
|
4787
|
+
if (input === "\x1B[1;3C" || input === "\x1Bf" || input === "\x1B[1;5C") return "word-right";
|
|
4788
|
+
return null;
|
|
4789
|
+
}
|
|
4790
|
+
function detectWordDeletion(input, key) {
|
|
4791
|
+
const isAltBackspace = key.meta && key.backspace || input === "\x1B\x7F";
|
|
4792
|
+
const isCtrlW = key.ctrl && input === "w";
|
|
4793
|
+
return isAltBackspace || isCtrlW;
|
|
4794
|
+
}
|
|
4795
|
+
function isPasteInput(input, key) {
|
|
4796
|
+
if (key.ctrl || key.meta) return false;
|
|
4797
|
+
if (input.startsWith("[200~")) return true;
|
|
4798
|
+
return input.length > 1 && /[\n\r]/.test(input);
|
|
4799
|
+
}
|
|
4800
|
+
function cleanPastedInput(input) {
|
|
4801
|
+
return input.replace(/^\[200~/, "").replace(new RegExp(String.fromCharCode(27) + "\\[201~$"), "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
4802
|
+
}
|
|
4803
|
+
function wordBoundaryLeft(value, pos) {
|
|
4804
|
+
const chars = [...value];
|
|
4805
|
+
let i = pos;
|
|
4806
|
+
while (i > 0 && chars[i - 1] === " ") i--;
|
|
4807
|
+
while (i > 0 && chars[i - 1] !== " ") i--;
|
|
4808
|
+
return i;
|
|
4809
|
+
}
|
|
4810
|
+
function wordBoundaryRight(value, pos) {
|
|
4811
|
+
const chars = [...value];
|
|
4812
|
+
let i = pos;
|
|
4813
|
+
while (i < chars.length && chars[i] === " ") i++;
|
|
4814
|
+
while (i < chars.length && chars[i] !== " ") i++;
|
|
4815
|
+
return i;
|
|
4816
|
+
}
|
|
4817
|
+
|
|
4818
|
+
// src/cli/ui/bordered-input.tsx
|
|
4819
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
4768
4820
|
function supportsUnicode() {
|
|
4769
4821
|
const term = process.env.TERM ?? "";
|
|
4770
4822
|
const lang = process.env.LANG ?? "";
|
|
@@ -4785,16 +4837,18 @@ function BorderedInput({
|
|
|
4785
4837
|
completionEngine,
|
|
4786
4838
|
onSubmit,
|
|
4787
4839
|
onHistoryAppend,
|
|
4788
|
-
onSlashCommand
|
|
4840
|
+
onSlashCommand,
|
|
4841
|
+
activeSuggestion,
|
|
4842
|
+
injectedValue
|
|
4789
4843
|
}) {
|
|
4790
4844
|
const [value, setValue] = useState("");
|
|
4845
|
+
const [cursorPos, setCursorPos] = useState(0);
|
|
4791
4846
|
const [multiLineBuffer, setMultiLineBuffer] = useState(null);
|
|
4792
|
-
const [
|
|
4847
|
+
const [completionHint, setCompletionHint] = useState(null);
|
|
4793
4848
|
const { stdout } = useStdout();
|
|
4794
4849
|
const [columns, setColumns] = useState(stdout?.columns ?? 80);
|
|
4795
4850
|
const historyIdx = useRef(-1);
|
|
4796
4851
|
const savedInput = useRef("");
|
|
4797
|
-
const [completionHint, setCompletionHint] = useState(null);
|
|
4798
4852
|
useEffect(() => {
|
|
4799
4853
|
if (!stdout) return;
|
|
4800
4854
|
const onResize = () => setColumns(stdout.columns);
|
|
@@ -4803,15 +4857,79 @@ function BorderedInput({
|
|
|
4803
4857
|
stdout.off("resize", onResize);
|
|
4804
4858
|
};
|
|
4805
4859
|
}, [stdout]);
|
|
4806
|
-
|
|
4860
|
+
useEffect(() => {
|
|
4861
|
+
if (injectedValue != null) {
|
|
4862
|
+
setValue(injectedValue.value);
|
|
4863
|
+
setCursorPos([...injectedValue.value].length);
|
|
4864
|
+
}
|
|
4865
|
+
}, [injectedValue]);
|
|
4866
|
+
const processSubmit = useCallback((input) => {
|
|
4867
|
+
const trimmed = input.trim();
|
|
4868
|
+
if (!trimmed) return;
|
|
4869
|
+
historyIdx.current = -1;
|
|
4870
|
+
savedInput.current = "";
|
|
4871
|
+
setCompletionHint(null);
|
|
4872
|
+
if (trimmed === "/expand") {
|
|
4873
|
+
setValue("");
|
|
4874
|
+
setCursorPos(0);
|
|
4875
|
+
return;
|
|
4876
|
+
}
|
|
4877
|
+
if (trimmed === "/send" && multiLineBuffer) {
|
|
4878
|
+
onHistoryAppend?.(multiLineBuffer);
|
|
4879
|
+
onSubmit(multiLineBuffer);
|
|
4880
|
+
setMultiLineBuffer(null);
|
|
4881
|
+
setValue("");
|
|
4882
|
+
setCursorPos(0);
|
|
4883
|
+
return;
|
|
4884
|
+
}
|
|
4885
|
+
if (trimmed.startsWith("/") && onSlashCommand) {
|
|
4886
|
+
const spaceIdx = trimmed.indexOf(" ");
|
|
4887
|
+
const cmd = spaceIdx === -1 ? trimmed.slice(1) : trimmed.slice(1, spaceIdx);
|
|
4888
|
+
const args = spaceIdx === -1 ? void 0 : trimmed.slice(spaceIdx + 1);
|
|
4889
|
+
onHistoryAppend?.(trimmed);
|
|
4890
|
+
onSlashCommand(cmd, args);
|
|
4891
|
+
setValue("");
|
|
4892
|
+
setCursorPos(0);
|
|
4893
|
+
return;
|
|
4894
|
+
}
|
|
4895
|
+
onHistoryAppend?.(input);
|
|
4896
|
+
onSubmit(input);
|
|
4897
|
+
setValue("");
|
|
4898
|
+
setCursorPos(0);
|
|
4899
|
+
}, [multiLineBuffer, onSubmit, onSlashCommand, onHistoryAppend]);
|
|
4900
|
+
useInput((input, key) => {
|
|
4807
4901
|
if (!isActive) return;
|
|
4808
|
-
if (
|
|
4809
|
-
if (
|
|
4810
|
-
|
|
4902
|
+
if (multiLineBuffer !== null) {
|
|
4903
|
+
if (key.return) {
|
|
4904
|
+
onHistoryAppend?.(multiLineBuffer);
|
|
4905
|
+
onSubmit(multiLineBuffer);
|
|
4906
|
+
setMultiLineBuffer(null);
|
|
4907
|
+
setValue("");
|
|
4908
|
+
setCursorPos(0);
|
|
4909
|
+
historyIdx.current = -1;
|
|
4910
|
+
savedInput.current = "";
|
|
4911
|
+
return;
|
|
4912
|
+
}
|
|
4913
|
+
if (key.escape) {
|
|
4914
|
+
setMultiLineBuffer(null);
|
|
4915
|
+
setValue("");
|
|
4916
|
+
setCursorPos(0);
|
|
4917
|
+
return;
|
|
4811
4918
|
}
|
|
4919
|
+
}
|
|
4920
|
+
if (isPasteInput(input, key)) {
|
|
4921
|
+
setMultiLineBuffer(cleanPastedInput(input));
|
|
4922
|
+
setValue("");
|
|
4923
|
+
setCursorPos(0);
|
|
4924
|
+
return;
|
|
4925
|
+
}
|
|
4926
|
+
if (key.upArrow && history.length > 0) {
|
|
4927
|
+
if (historyIdx.current === -1) savedInput.current = value;
|
|
4812
4928
|
const newIdx = Math.min(historyIdx.current + 1, history.length - 1);
|
|
4813
4929
|
historyIdx.current = newIdx;
|
|
4814
|
-
|
|
4930
|
+
const newVal = history[history.length - 1 - newIdx];
|
|
4931
|
+
setValue(newVal);
|
|
4932
|
+
setCursorPos([...newVal].length);
|
|
4815
4933
|
setCompletionHint(null);
|
|
4816
4934
|
return;
|
|
4817
4935
|
}
|
|
@@ -4819,110 +4937,175 @@ function BorderedInput({
|
|
|
4819
4937
|
if (historyIdx.current <= 0) {
|
|
4820
4938
|
historyIdx.current = -1;
|
|
4821
4939
|
setValue(savedInput.current);
|
|
4940
|
+
setCursorPos([...savedInput.current].length);
|
|
4822
4941
|
} else {
|
|
4823
4942
|
historyIdx.current--;
|
|
4824
|
-
|
|
4943
|
+
const newVal = history[history.length - 1 - historyIdx.current];
|
|
4944
|
+
setValue(newVal);
|
|
4945
|
+
setCursorPos([...newVal].length);
|
|
4825
4946
|
}
|
|
4826
4947
|
setCompletionHint(null);
|
|
4827
4948
|
return;
|
|
4828
4949
|
}
|
|
4829
|
-
if (key.
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4950
|
+
if (key.return) {
|
|
4951
|
+
processSubmit(value);
|
|
4952
|
+
return;
|
|
4953
|
+
}
|
|
4954
|
+
const isHome = input === "\x1B[H" || input === "\x1B[1~";
|
|
4955
|
+
const isEnd = input === "\x1B[F" || input === "\x1B[4~";
|
|
4956
|
+
if (key.ctrl && input === "a" || isHome) {
|
|
4957
|
+
setCursorPos(0);
|
|
4958
|
+
return;
|
|
4959
|
+
}
|
|
4960
|
+
if (key.ctrl && input === "e" || isEnd) {
|
|
4961
|
+
setCursorPos([...value].length);
|
|
4962
|
+
return;
|
|
4963
|
+
}
|
|
4964
|
+
if (key.ctrl && input === "u") {
|
|
4965
|
+
const chars2 = [...value];
|
|
4966
|
+
setValue(chars2.slice(cursorPos).join(""));
|
|
4967
|
+
setCursorPos(0);
|
|
4968
|
+
historyIdx.current = -1;
|
|
4969
|
+
return;
|
|
4970
|
+
}
|
|
4971
|
+
if (key.ctrl && input === "k") {
|
|
4972
|
+
const chars2 = [...value];
|
|
4973
|
+
setValue(chars2.slice(0, cursorPos).join(""));
|
|
4974
|
+
historyIdx.current = -1;
|
|
4975
|
+
return;
|
|
4976
|
+
}
|
|
4977
|
+
const wordNav = detectWordNav(input);
|
|
4978
|
+
if (wordNav === "word-left") {
|
|
4979
|
+
setCursorPos(wordBoundaryLeft(value, cursorPos));
|
|
4980
|
+
return;
|
|
4981
|
+
}
|
|
4982
|
+
if (wordNav === "word-right") {
|
|
4983
|
+
setCursorPos(wordBoundaryRight(value, cursorPos));
|
|
4984
|
+
return;
|
|
4985
|
+
}
|
|
4986
|
+
if (detectWordDeletion(input, key)) {
|
|
4987
|
+
const chars2 = [...value];
|
|
4988
|
+
const newPos = wordBoundaryLeft(value, cursorPos);
|
|
4989
|
+
setValue([...chars2.slice(0, newPos), ...chars2.slice(cursorPos)].join(""));
|
|
4990
|
+
setCursorPos(newPos);
|
|
4991
|
+
historyIdx.current = -1;
|
|
4992
|
+
return;
|
|
4993
|
+
}
|
|
4994
|
+
if (key.backspace) {
|
|
4995
|
+
if (cursorPos > 0) {
|
|
4996
|
+
const chars2 = [...value];
|
|
4997
|
+
chars2.splice(cursorPos - 1, 1);
|
|
4998
|
+
setValue(chars2.join(""));
|
|
4999
|
+
setCursorPos(cursorPos - 1);
|
|
5000
|
+
historyIdx.current = -1;
|
|
4840
5001
|
}
|
|
4841
5002
|
return;
|
|
4842
5003
|
}
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
const firstLine = newValue.split("\n")[0];
|
|
4851
|
-
setValue(firstLine);
|
|
4852
|
-
} else {
|
|
4853
|
-
if (multiLineBuffer !== null && !newValue.startsWith(value)) {
|
|
4854
|
-
setMultiLineBuffer(null);
|
|
5004
|
+
if (key.delete) {
|
|
5005
|
+
if (cursorPos > 0) {
|
|
5006
|
+
const chars2 = [...value];
|
|
5007
|
+
chars2.splice(cursorPos - 1, 1);
|
|
5008
|
+
setValue(chars2.join(""));
|
|
5009
|
+
setCursorPos(cursorPos - 1);
|
|
5010
|
+
historyIdx.current = -1;
|
|
4855
5011
|
}
|
|
4856
|
-
|
|
5012
|
+
return;
|
|
4857
5013
|
}
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
const trimmed = input.trim();
|
|
4861
|
-
if (!trimmed) return;
|
|
4862
|
-
historyIdx.current = -1;
|
|
4863
|
-
savedInput.current = "";
|
|
4864
|
-
setCompletionHint(null);
|
|
4865
|
-
if (trimmed === "/expand" && multiLineBuffer) {
|
|
4866
|
-
setExpanded(!expanded);
|
|
4867
|
-
setValue("");
|
|
5014
|
+
if (key.leftArrow) {
|
|
5015
|
+
setCursorPos(Math.max(0, cursorPos - 1));
|
|
4868
5016
|
return;
|
|
4869
5017
|
}
|
|
4870
|
-
if (
|
|
4871
|
-
|
|
4872
|
-
onSubmit(multiLineBuffer);
|
|
4873
|
-
setMultiLineBuffer(null);
|
|
4874
|
-
setExpanded(false);
|
|
4875
|
-
setValue("");
|
|
5018
|
+
if (key.rightArrow) {
|
|
5019
|
+
setCursorPos(Math.min([...value].length, cursorPos + 1));
|
|
4876
5020
|
return;
|
|
4877
5021
|
}
|
|
4878
|
-
if (
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
5022
|
+
if (key.tab) {
|
|
5023
|
+
if (!value && activeSuggestion) {
|
|
5024
|
+
onHistoryAppend?.(activeSuggestion.action);
|
|
5025
|
+
onSubmit(activeSuggestion.action);
|
|
5026
|
+
historyIdx.current = -1;
|
|
5027
|
+
savedInput.current = "";
|
|
5028
|
+
return;
|
|
5029
|
+
}
|
|
5030
|
+
if (completionEngine && value) {
|
|
5031
|
+
const items = completionEngine.complete(value);
|
|
5032
|
+
if (items.length === 1) {
|
|
5033
|
+
setValue(items[0].value);
|
|
5034
|
+
setCursorPos([...items[0].value].length);
|
|
5035
|
+
setCompletionHint(null);
|
|
5036
|
+
} else if (items.length > 1) {
|
|
5037
|
+
const common = completionEngine.commonPrefix(items);
|
|
5038
|
+
if (common.length > value.length) {
|
|
5039
|
+
setValue(common);
|
|
5040
|
+
setCursorPos([...common].length);
|
|
5041
|
+
}
|
|
5042
|
+
setCompletionHint(items.map((i) => i.label).join(" "));
|
|
5043
|
+
}
|
|
5044
|
+
}
|
|
4885
5045
|
return;
|
|
4886
5046
|
}
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
5047
|
+
if (key.ctrl && input === "r") {
|
|
5048
|
+
onSlashCommand?.("history-search");
|
|
5049
|
+
return;
|
|
5050
|
+
}
|
|
5051
|
+
const cp = input.codePointAt(0);
|
|
5052
|
+
if (cp === void 0 || cp < 32 || cp === 127) return;
|
|
5053
|
+
if (key.ctrl || key.meta) return;
|
|
5054
|
+
const chars = [...value];
|
|
5055
|
+
const inputChars = [...input];
|
|
5056
|
+
chars.splice(cursorPos, 0, ...inputChars);
|
|
5057
|
+
setValue(chars.join(""));
|
|
5058
|
+
setCursorPos(cursorPos + inputChars.length);
|
|
5059
|
+
historyIdx.current = -1;
|
|
5060
|
+
setCompletionHint(null);
|
|
5061
|
+
}, { isActive });
|
|
5062
|
+
function renderMultilinePreview() {
|
|
5063
|
+
if (!multiLineBuffer) return null;
|
|
5064
|
+
const lines = multiLineBuffer.split("\n");
|
|
5065
|
+
const totalLines = lines.length;
|
|
5066
|
+
const byteLen = Buffer.byteLength(multiLineBuffer, "utf8");
|
|
5067
|
+
const sizeStr = byteLen >= 1024 ? `${(byteLen / 1024).toFixed(1)} KB` : `${byteLen} B`;
|
|
5068
|
+
const firstNonEmpty = lines.find((l) => l.trim()) ?? "";
|
|
5069
|
+
const sanitized = firstNonEmpty.replace(/[^\x20-\x7E]/g, "").trim();
|
|
5070
|
+
const maxHint = Math.max(20, columns - 14);
|
|
5071
|
+
const hint = sanitized.length > maxHint ? sanitized.slice(0, maxHint - 1) + "\u2026" : sanitized;
|
|
5072
|
+
return /* @__PURE__ */ jsxs2(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
5073
|
+
/* @__PURE__ */ jsxs2(Box, { gap: 1, children: [
|
|
5074
|
+
/* @__PURE__ */ jsx2(Text2, { color: "cyan", children: "\u2398" }),
|
|
5075
|
+
/* @__PURE__ */ jsxs2(Text2, { bold: true, children: [
|
|
5076
|
+
totalLines,
|
|
5077
|
+
" line",
|
|
5078
|
+
totalLines !== 1 ? "s" : ""
|
|
5079
|
+
] }),
|
|
5080
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "\xB7" }),
|
|
5081
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: sizeStr }),
|
|
5082
|
+
hint ? /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
5083
|
+
'\xB7 "',
|
|
5084
|
+
hint,
|
|
5085
|
+
'"'
|
|
5086
|
+
] }) : null
|
|
5087
|
+
] }),
|
|
5088
|
+
/* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "[Enter to send \xB7 Esc to discard]" })
|
|
5089
|
+
] });
|
|
5090
|
+
}
|
|
4895
5091
|
if (!bordered || columns < 40 || hasInkGhostingIssue()) {
|
|
4896
|
-
return /* @__PURE__ */
|
|
4897
|
-
/* @__PURE__ */
|
|
4898
|
-
/* @__PURE__ */
|
|
5092
|
+
return /* @__PURE__ */ jsxs2(Box, { flexDirection: "column", children: [
|
|
5093
|
+
/* @__PURE__ */ jsxs2(Box, { children: [
|
|
5094
|
+
/* @__PURE__ */ jsxs2(Text2, { color: "green", bold: true, children: [
|
|
4899
5095
|
">",
|
|
4900
5096
|
" "
|
|
4901
5097
|
] }),
|
|
4902
|
-
/* @__PURE__ */
|
|
4903
|
-
TextInput,
|
|
4904
|
-
{
|
|
4905
|
-
value,
|
|
4906
|
-
onChange: handleChange,
|
|
4907
|
-
onSubmit: handleSubmit,
|
|
4908
|
-
focus: isActive
|
|
4909
|
-
}
|
|
4910
|
-
),
|
|
4911
|
-
multiLineBuffer && !expanded && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
4912
|
-
" [",
|
|
4913
|
-
lineCount,
|
|
4914
|
-
" lines - /expand to view, /send to submit]"
|
|
4915
|
-
] })
|
|
5098
|
+
/* @__PURE__ */ jsx2(CursorText, { value, cursorPos, active: isActive })
|
|
4916
5099
|
] }),
|
|
4917
|
-
completionHint && /* @__PURE__ */
|
|
5100
|
+
completionHint && /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
4918
5101
|
" ",
|
|
4919
5102
|
completionHint
|
|
4920
5103
|
] }),
|
|
4921
|
-
|
|
5104
|
+
renderMultilinePreview()
|
|
4922
5105
|
] });
|
|
4923
5106
|
}
|
|
4924
5107
|
const borderStyle = supportsUnicode() ? "round" : "classic";
|
|
4925
|
-
return /* @__PURE__ */
|
|
5108
|
+
return /* @__PURE__ */ jsx2(Box, { flexDirection: "column", children: /* @__PURE__ */ jsxs2(
|
|
4926
5109
|
Box,
|
|
4927
5110
|
{
|
|
4928
5111
|
flexDirection: "column",
|
|
@@ -4932,34 +5115,18 @@ function BorderedInput({
|
|
|
4932
5115
|
paddingLeft: 1,
|
|
4933
5116
|
paddingRight: 1,
|
|
4934
5117
|
children: [
|
|
4935
|
-
/* @__PURE__ */
|
|
4936
|
-
/* @__PURE__ */
|
|
5118
|
+
/* @__PURE__ */ jsxs2(Box, { children: [
|
|
5119
|
+
/* @__PURE__ */ jsxs2(Text2, { color: "green", bold: true, children: [
|
|
4937
5120
|
">",
|
|
4938
5121
|
" "
|
|
4939
5122
|
] }),
|
|
4940
|
-
/* @__PURE__ */
|
|
4941
|
-
TextInput,
|
|
4942
|
-
{
|
|
4943
|
-
value,
|
|
4944
|
-
onChange: handleChange,
|
|
4945
|
-
onSubmit: handleSubmit,
|
|
4946
|
-
focus: isActive
|
|
4947
|
-
}
|
|
4948
|
-
)
|
|
5123
|
+
/* @__PURE__ */ jsx2(CursorText, { value, cursorPos, active: isActive })
|
|
4949
5124
|
] }),
|
|
4950
|
-
completionHint && /* @__PURE__ */
|
|
5125
|
+
completionHint && /* @__PURE__ */ jsx2(Box, { children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
4951
5126
|
" ",
|
|
4952
5127
|
completionHint
|
|
4953
5128
|
] }) }),
|
|
4954
|
-
|
|
4955
|
-
"[",
|
|
4956
|
-
lineCount,
|
|
4957
|
-
" lines pasted - /expand to view, /send to submit]"
|
|
4958
|
-
] }) }),
|
|
4959
|
-
expanded && multiLineBuffer && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
4960
|
-
multiLineBuffer.split("\n").map((line, i) => /* @__PURE__ */ jsx(Text, { dimColor: true, children: line }, i)),
|
|
4961
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "[/send to submit, /expand to collapse]" })
|
|
4962
|
-
] })
|
|
5129
|
+
renderMultilinePreview()
|
|
4963
5130
|
]
|
|
4964
5131
|
}
|
|
4965
5132
|
) });
|
|
@@ -4967,11 +5134,11 @@ function BorderedInput({
|
|
|
4967
5134
|
|
|
4968
5135
|
// src/cli/ui/status-bar.tsx
|
|
4969
5136
|
import { useState as useState2, useEffect as useEffect2 } from "react";
|
|
4970
|
-
import { Box as Box2, Text as
|
|
5137
|
+
import { Box as Box2, Text as Text4, useStdout as useStdout2 } from "ink";
|
|
4971
5138
|
|
|
4972
5139
|
// src/cli/ui/context-bar.tsx
|
|
4973
|
-
import { Text as
|
|
4974
|
-
import { jsxs as
|
|
5140
|
+
import { Text as Text3 } from "ink";
|
|
5141
|
+
import { jsxs as jsxs3 } from "react/jsx-runtime";
|
|
4975
5142
|
function ContextBar({ percent, segments = 10 }) {
|
|
4976
5143
|
const clamped = Math.max(0, Math.min(100, percent));
|
|
4977
5144
|
const filled = Math.round(clamped / 100 * segments);
|
|
@@ -4985,7 +5152,7 @@ function ContextBar({ percent, segments = 10 }) {
|
|
|
4985
5152
|
} else {
|
|
4986
5153
|
color2 = "green";
|
|
4987
5154
|
}
|
|
4988
|
-
return /* @__PURE__ */
|
|
5155
|
+
return /* @__PURE__ */ jsxs3(Text3, { color: color2, children: [
|
|
4989
5156
|
"[",
|
|
4990
5157
|
bar,
|
|
4991
5158
|
"] ",
|
|
@@ -4995,7 +5162,7 @@ function ContextBar({ percent, segments = 10 }) {
|
|
|
4995
5162
|
}
|
|
4996
5163
|
|
|
4997
5164
|
// src/cli/ui/status-bar.tsx
|
|
4998
|
-
import { Fragment, jsx as
|
|
5165
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
4999
5166
|
function StatusBar({ bridge, model, sessionIdentifier, visible = true }) {
|
|
5000
5167
|
const { stdout } = useStdout2();
|
|
5001
5168
|
const [usage, setUsage] = useState2({
|
|
@@ -5018,19 +5185,19 @@ function StatusBar({ bridge, model, sessionIdentifier, visible = true }) {
|
|
|
5018
5185
|
if (!stdout?.isTTY) return null;
|
|
5019
5186
|
const tokens = `${usage.sessionInputTokens.toLocaleString()} in / ${usage.sessionOutputTokens.toLocaleString()} out`;
|
|
5020
5187
|
const cost = `$${usage.sessionCost.toFixed(2)}`;
|
|
5021
|
-
return /* @__PURE__ */
|
|
5022
|
-
/* @__PURE__ */
|
|
5023
|
-
/* @__PURE__ */
|
|
5024
|
-
/* @__PURE__ */
|
|
5025
|
-
/* @__PURE__ */
|
|
5026
|
-
/* @__PURE__ */
|
|
5027
|
-
/* @__PURE__ */
|
|
5188
|
+
return /* @__PURE__ */ jsxs4(Box2, { width: "100%", justifyContent: "space-between", children: [
|
|
5189
|
+
/* @__PURE__ */ jsxs4(Box2, { children: [
|
|
5190
|
+
/* @__PURE__ */ jsx3(Text4, { color: "cyan", bold: true, children: model }),
|
|
5191
|
+
/* @__PURE__ */ jsx3(Text4, { dimColor: true, children: " | " }),
|
|
5192
|
+
/* @__PURE__ */ jsx3(Text4, { children: tokens }),
|
|
5193
|
+
/* @__PURE__ */ jsx3(Text4, { dimColor: true, children: " | " }),
|
|
5194
|
+
/* @__PURE__ */ jsx3(Text4, { color: "yellow", children: cost })
|
|
5028
5195
|
] }),
|
|
5029
|
-
/* @__PURE__ */
|
|
5030
|
-
/* @__PURE__ */
|
|
5031
|
-
sessionIdentifier && /* @__PURE__ */
|
|
5032
|
-
/* @__PURE__ */
|
|
5033
|
-
/* @__PURE__ */
|
|
5196
|
+
/* @__PURE__ */ jsxs4(Box2, { children: [
|
|
5197
|
+
/* @__PURE__ */ jsx3(ContextBar, { percent: contextPercent }),
|
|
5198
|
+
sessionIdentifier && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
5199
|
+
/* @__PURE__ */ jsx3(Text4, { dimColor: true, children: " | " }),
|
|
5200
|
+
/* @__PURE__ */ jsx3(Text4, { dimColor: true, children: sessionIdentifier })
|
|
5034
5201
|
] })
|
|
5035
5202
|
] })
|
|
5036
5203
|
] });
|
|
@@ -5041,11 +5208,11 @@ import React3, { useState as useState3, useCallback as useCallback2 } from "reac
|
|
|
5041
5208
|
import { Box as Box5, useInput as useInput2 } from "ink";
|
|
5042
5209
|
|
|
5043
5210
|
// src/cli/ui/approval-prompt.tsx
|
|
5044
|
-
import { Box as Box4, Text as
|
|
5211
|
+
import { Box as Box4, Text as Text6, useStdout as useStdout3 } from "ink";
|
|
5045
5212
|
|
|
5046
5213
|
// src/cli/ui/diff-view.tsx
|
|
5047
|
-
import { Box as Box3, Text as
|
|
5048
|
-
import { jsx as
|
|
5214
|
+
import { Box as Box3, Text as Text5 } from "ink";
|
|
5215
|
+
import { jsx as jsx4, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
5049
5216
|
function DiffView({ diff, maxLines = 30 }) {
|
|
5050
5217
|
let lineCount = 0;
|
|
5051
5218
|
let truncated = false;
|
|
@@ -5059,19 +5226,19 @@ function DiffView({ diff, maxLines = 30 }) {
|
|
|
5059
5226
|
lineCount++;
|
|
5060
5227
|
if (line.startsWith("+")) {
|
|
5061
5228
|
lines.push(
|
|
5062
|
-
/* @__PURE__ */
|
|
5229
|
+
/* @__PURE__ */ jsx4(Text5, { backgroundColor: "green", color: "black", children: line }, `${hunkIndex}-${lineCount}`)
|
|
5063
5230
|
);
|
|
5064
5231
|
} else if (line.startsWith("-")) {
|
|
5065
5232
|
lines.push(
|
|
5066
|
-
/* @__PURE__ */
|
|
5233
|
+
/* @__PURE__ */ jsx4(Text5, { backgroundColor: "red", color: "black", children: line }, `${hunkIndex}-${lineCount}`)
|
|
5067
5234
|
);
|
|
5068
5235
|
} else if (line.startsWith("@@")) {
|
|
5069
5236
|
lines.push(
|
|
5070
|
-
/* @__PURE__ */
|
|
5237
|
+
/* @__PURE__ */ jsx4(Text5, { color: "cyan", children: line }, `${hunkIndex}-${lineCount}`)
|
|
5071
5238
|
);
|
|
5072
5239
|
} else {
|
|
5073
5240
|
lines.push(
|
|
5074
|
-
/* @__PURE__ */
|
|
5241
|
+
/* @__PURE__ */ jsx4(Text5, { dimColor: true, children: line }, `${hunkIndex}-${lineCount}`)
|
|
5075
5242
|
);
|
|
5076
5243
|
}
|
|
5077
5244
|
}
|
|
@@ -5079,14 +5246,14 @@ function DiffView({ diff, maxLines = 30 }) {
|
|
|
5079
5246
|
};
|
|
5080
5247
|
const allLines = diff.hunks.flatMap((hunk, i) => renderHunk(hunk, i));
|
|
5081
5248
|
const totalLines = diff.hunks.reduce((sum, h) => sum + h.lines.length, 0);
|
|
5082
|
-
return /* @__PURE__ */
|
|
5083
|
-
/* @__PURE__ */
|
|
5249
|
+
return /* @__PURE__ */ jsxs5(Box3, { flexDirection: "column", children: [
|
|
5250
|
+
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
5084
5251
|
" -- ",
|
|
5085
5252
|
diff.filePath,
|
|
5086
5253
|
" --"
|
|
5087
5254
|
] }),
|
|
5088
5255
|
allLines,
|
|
5089
|
-
truncated && /* @__PURE__ */
|
|
5256
|
+
truncated && /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
5090
5257
|
" ...",
|
|
5091
5258
|
totalLines - maxLines,
|
|
5092
5259
|
" more lines"
|
|
@@ -5095,12 +5262,12 @@ function DiffView({ diff, maxLines = 30 }) {
|
|
|
5095
5262
|
}
|
|
5096
5263
|
|
|
5097
5264
|
// src/cli/ui/approval-prompt.tsx
|
|
5098
|
-
import { jsx as
|
|
5265
|
+
import { jsx as jsx5, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
5099
5266
|
function ApprovalPrompt({ request, onRespond: _onRespond }) {
|
|
5100
5267
|
const { stdout } = useStdout3();
|
|
5101
5268
|
const columns = stdout?.columns ?? 80;
|
|
5102
5269
|
const boxWidth = Math.min(columns - 4, 120);
|
|
5103
|
-
return /* @__PURE__ */
|
|
5270
|
+
return /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsxs6(
|
|
5104
5271
|
Box4,
|
|
5105
5272
|
{
|
|
5106
5273
|
flexDirection: "column",
|
|
@@ -5110,12 +5277,12 @@ function ApprovalPrompt({ request, onRespond: _onRespond }) {
|
|
|
5110
5277
|
paddingLeft: 1,
|
|
5111
5278
|
paddingRight: 1,
|
|
5112
5279
|
children: [
|
|
5113
|
-
/* @__PURE__ */
|
|
5114
|
-
/* @__PURE__ */
|
|
5280
|
+
/* @__PURE__ */ jsxs6(Box4, { children: [
|
|
5281
|
+
/* @__PURE__ */ jsxs6(Text6, { color: "yellow", bold: true, children: [
|
|
5115
5282
|
"\u26A0",
|
|
5116
5283
|
" Approval required"
|
|
5117
5284
|
] }),
|
|
5118
|
-
request.total > 1 && /* @__PURE__ */
|
|
5285
|
+
request.total > 1 && /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
5119
5286
|
" [",
|
|
5120
5287
|
request.index + 1,
|
|
5121
5288
|
"/",
|
|
@@ -5123,36 +5290,36 @@ function ApprovalPrompt({ request, onRespond: _onRespond }) {
|
|
|
5123
5290
|
"]"
|
|
5124
5291
|
] })
|
|
5125
5292
|
] }),
|
|
5126
|
-
request.warning && /* @__PURE__ */
|
|
5127
|
-
/* @__PURE__ */
|
|
5293
|
+
request.warning && /* @__PURE__ */ jsxs6(Box4, { marginTop: 1, children: [
|
|
5294
|
+
/* @__PURE__ */ jsxs6(Text6, { color: "red", bold: true, children: [
|
|
5128
5295
|
"\u26A0",
|
|
5129
5296
|
" WARNING: "
|
|
5130
5297
|
] }),
|
|
5131
|
-
/* @__PURE__ */
|
|
5298
|
+
/* @__PURE__ */ jsxs6(Text6, { wrap: "wrap", children: [
|
|
5132
5299
|
"This command accesses a sensitive system path outside the project root (",
|
|
5133
5300
|
request.warning,
|
|
5134
5301
|
")"
|
|
5135
5302
|
] })
|
|
5136
5303
|
] }),
|
|
5137
|
-
/* @__PURE__ */
|
|
5138
|
-
/* @__PURE__ */
|
|
5304
|
+
/* @__PURE__ */ jsxs6(Box4, { marginTop: 1, children: [
|
|
5305
|
+
/* @__PURE__ */ jsxs6(Text6, { bold: true, children: [
|
|
5139
5306
|
request.toolName,
|
|
5140
5307
|
": "
|
|
5141
5308
|
] }),
|
|
5142
|
-
/* @__PURE__ */
|
|
5309
|
+
/* @__PURE__ */ jsx5(Text6, { wrap: "wrap", children: request.summary })
|
|
5143
5310
|
] }),
|
|
5144
|
-
request.diff && /* @__PURE__ */
|
|
5145
|
-
/* @__PURE__ */
|
|
5146
|
-
/* @__PURE__ */
|
|
5147
|
-
/* @__PURE__ */
|
|
5148
|
-
/* @__PURE__ */
|
|
5149
|
-
/* @__PURE__ */
|
|
5150
|
-
/* @__PURE__ */
|
|
5151
|
-
/* @__PURE__ */
|
|
5152
|
-
/* @__PURE__ */
|
|
5153
|
-
/* @__PURE__ */
|
|
5154
|
-
/* @__PURE__ */
|
|
5155
|
-
/* @__PURE__ */
|
|
5311
|
+
request.diff && /* @__PURE__ */ jsx5(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx5(DiffView, { diff: request.diff, maxLines: 20 }) }),
|
|
5312
|
+
/* @__PURE__ */ jsxs6(Box4, { marginTop: 1, children: [
|
|
5313
|
+
/* @__PURE__ */ jsx5(Text6, { color: "green", children: "[y] " }),
|
|
5314
|
+
/* @__PURE__ */ jsx5(Text6, { children: "allow " }),
|
|
5315
|
+
/* @__PURE__ */ jsx5(Text6, { color: "cyan", children: "[a] " }),
|
|
5316
|
+
/* @__PURE__ */ jsx5(Text6, { children: "always " }),
|
|
5317
|
+
/* @__PURE__ */ jsx5(Text6, { color: "red", children: "[n] " }),
|
|
5318
|
+
/* @__PURE__ */ jsx5(Text6, { children: "deny " }),
|
|
5319
|
+
/* @__PURE__ */ jsx5(Text6, { color: "yellow", children: "[A] " }),
|
|
5320
|
+
/* @__PURE__ */ jsx5(Text6, { children: "all " }),
|
|
5321
|
+
/* @__PURE__ */ jsx5(Text6, { color: "magenta", children: "[s] " }),
|
|
5322
|
+
/* @__PURE__ */ jsx5(Text6, { children: "similar" })
|
|
5156
5323
|
] })
|
|
5157
5324
|
]
|
|
5158
5325
|
}
|
|
@@ -5160,7 +5327,7 @@ function ApprovalPrompt({ request, onRespond: _onRespond }) {
|
|
|
5160
5327
|
}
|
|
5161
5328
|
|
|
5162
5329
|
// src/cli/ui/approval-handler.tsx
|
|
5163
|
-
import { jsx as
|
|
5330
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
5164
5331
|
function ApprovalHandler({ bridge }) {
|
|
5165
5332
|
const [pending, setPending] = useState3(null);
|
|
5166
5333
|
React3.useEffect(() => {
|
|
@@ -5204,7 +5371,7 @@ function ApprovalHandler({ bridge }) {
|
|
|
5204
5371
|
{ isActive: pending !== null }
|
|
5205
5372
|
);
|
|
5206
5373
|
if (!pending) return null;
|
|
5207
|
-
return /* @__PURE__ */
|
|
5374
|
+
return /* @__PURE__ */ jsx6(Box5, { children: /* @__PURE__ */ jsx6(
|
|
5208
5375
|
ApprovalPrompt,
|
|
5209
5376
|
{
|
|
5210
5377
|
request: pending.request,
|
|
@@ -5213,8 +5380,189 @@ function ApprovalHandler({ bridge }) {
|
|
|
5213
5380
|
) });
|
|
5214
5381
|
}
|
|
5215
5382
|
|
|
5383
|
+
// src/cli/ui/activity-bar.tsx
|
|
5384
|
+
import { Text as Text7 } from "ink";
|
|
5385
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
5386
|
+
function ActivityBar({ phase, spinnerFrame, spinnerElapsed, liveTool }) {
|
|
5387
|
+
if (liveTool !== null) {
|
|
5388
|
+
return /* @__PURE__ */ jsxs7(Text7, { color: "green", children: [
|
|
5389
|
+
" ",
|
|
5390
|
+
"\u25CF",
|
|
5391
|
+
" ",
|
|
5392
|
+
liveTool
|
|
5393
|
+
] });
|
|
5394
|
+
}
|
|
5395
|
+
if (phase === "thinking") {
|
|
5396
|
+
return /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
5397
|
+
" ",
|
|
5398
|
+
/* @__PURE__ */ jsx7(Text7, { color: "magenta", children: spinnerFrame }),
|
|
5399
|
+
" ",
|
|
5400
|
+
/* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
5401
|
+
"thinking... ",
|
|
5402
|
+
/* @__PURE__ */ jsx7(Text7, { color: "gray", children: spinnerElapsed })
|
|
5403
|
+
] })
|
|
5404
|
+
] });
|
|
5405
|
+
}
|
|
5406
|
+
if (phase === "streaming") {
|
|
5407
|
+
return /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
5408
|
+
" ",
|
|
5409
|
+
spinnerFrame,
|
|
5410
|
+
" ..."
|
|
5411
|
+
] });
|
|
5412
|
+
}
|
|
5413
|
+
return /* @__PURE__ */ jsx7(Text7, { children: " " });
|
|
5414
|
+
}
|
|
5415
|
+
|
|
5416
|
+
// src/cli/ui/suggestion-hint.tsx
|
|
5417
|
+
import { useState as useState4, useEffect as useEffect3 } from "react";
|
|
5418
|
+
import { Box as Box6, Text as Text8 } from "ink";
|
|
5419
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
5420
|
+
var DEFAULT_RULES = [
|
|
5421
|
+
{
|
|
5422
|
+
id: "run-tests",
|
|
5423
|
+
condition: (ctx) => ctx.editCount > 0 && ctx.hasTestFramework && ctx.lastToolNames.includes("edit"),
|
|
5424
|
+
suggestion: "Run tests to verify changes?",
|
|
5425
|
+
action: "run the tests for the files I just changed"
|
|
5426
|
+
},
|
|
5427
|
+
{
|
|
5428
|
+
id: "commit-changes",
|
|
5429
|
+
condition: (ctx) => ctx.editCount >= 3,
|
|
5430
|
+
suggestion: "Commit these changes?",
|
|
5431
|
+
action: "commit the changes with a descriptive message"
|
|
5432
|
+
},
|
|
5433
|
+
{
|
|
5434
|
+
id: "resume-session",
|
|
5435
|
+
condition: (ctx) => ctx.sessionCount > 0 && ctx.editCount === 0,
|
|
5436
|
+
suggestion: "Resume previous session?",
|
|
5437
|
+
action: "/session resume"
|
|
5438
|
+
}
|
|
5439
|
+
];
|
|
5440
|
+
function SuggestionHint({
|
|
5441
|
+
bridge,
|
|
5442
|
+
enabled = true,
|
|
5443
|
+
rules = DEFAULT_RULES,
|
|
5444
|
+
initialContext,
|
|
5445
|
+
onSuggestionChange
|
|
5446
|
+
}) {
|
|
5447
|
+
const [context, setContext] = useState4({
|
|
5448
|
+
lastToolNames: [],
|
|
5449
|
+
editCount: 0,
|
|
5450
|
+
hasTestFramework: false,
|
|
5451
|
+
sessionCount: 0,
|
|
5452
|
+
...initialContext
|
|
5453
|
+
});
|
|
5454
|
+
useEffect3(() => {
|
|
5455
|
+
const onToolComplete = (tool) => {
|
|
5456
|
+
setContext((prev) => ({
|
|
5457
|
+
...prev,
|
|
5458
|
+
lastToolNames: [...prev.lastToolNames.slice(-5), tool.name],
|
|
5459
|
+
editCount: tool.name === "edit" || tool.name === "write" ? prev.editCount + 1 : prev.editCount
|
|
5460
|
+
}));
|
|
5461
|
+
};
|
|
5462
|
+
const onTurnComplete = () => {
|
|
5463
|
+
setContext((prev) => ({ ...prev, lastToolNames: [] }));
|
|
5464
|
+
};
|
|
5465
|
+
bridge.on("tool-complete", onToolComplete);
|
|
5466
|
+
bridge.on("turn-complete", onTurnComplete);
|
|
5467
|
+
return () => {
|
|
5468
|
+
bridge.off("tool-complete", onToolComplete);
|
|
5469
|
+
bridge.off("turn-complete", onTurnComplete);
|
|
5470
|
+
};
|
|
5471
|
+
}, [bridge]);
|
|
5472
|
+
const activeSuggestion = enabled ? rules.find((rule) => rule.condition(context)) ?? null : null;
|
|
5473
|
+
useEffect3(() => {
|
|
5474
|
+
onSuggestionChange?.(activeSuggestion);
|
|
5475
|
+
}, [activeSuggestion, onSuggestionChange]);
|
|
5476
|
+
if (!enabled || activeSuggestion === null) return null;
|
|
5477
|
+
return /* @__PURE__ */ jsx8(Box6, { marginLeft: 2, children: /* @__PURE__ */ jsxs8(Text8, { dimColor: true, italic: true, children: [
|
|
5478
|
+
activeSuggestion.suggestion,
|
|
5479
|
+
" [Tab to accept]"
|
|
5480
|
+
] }) });
|
|
5481
|
+
}
|
|
5482
|
+
|
|
5483
|
+
// src/cli/ui/history-search.tsx
|
|
5484
|
+
import { useState as useState5, useMemo } from "react";
|
|
5485
|
+
import { Box as Box7, Text as Text9, useInput as useInput3 } from "ink";
|
|
5486
|
+
import TextInput from "ink-text-input";
|
|
5487
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
5488
|
+
function HistorySearch({ history, visible, onSelect, onDismiss }) {
|
|
5489
|
+
const [query, setQuery] = useState5("");
|
|
5490
|
+
const [selectedIndex, setSelectedIndex] = useState5(0);
|
|
5491
|
+
const filtered = useMemo(() => {
|
|
5492
|
+
if (!query) return history.slice(0, 20);
|
|
5493
|
+
const lowerQuery = query.toLowerCase();
|
|
5494
|
+
return history.filter((entry) => {
|
|
5495
|
+
const lower = entry.toLowerCase();
|
|
5496
|
+
let qi = 0;
|
|
5497
|
+
for (let i = 0; i < lower.length && qi < lowerQuery.length; i++) {
|
|
5498
|
+
if (lower[i] === lowerQuery[qi]) qi++;
|
|
5499
|
+
}
|
|
5500
|
+
return qi === lowerQuery.length;
|
|
5501
|
+
}).slice(0, 20);
|
|
5502
|
+
}, [history, query]);
|
|
5503
|
+
useInput3(
|
|
5504
|
+
(_input, key) => {
|
|
5505
|
+
if (!visible) return;
|
|
5506
|
+
if (key.escape) {
|
|
5507
|
+
setQuery("");
|
|
5508
|
+
setSelectedIndex(0);
|
|
5509
|
+
onDismiss();
|
|
5510
|
+
return;
|
|
5511
|
+
}
|
|
5512
|
+
if (key.return) {
|
|
5513
|
+
if (filtered.length > 0) {
|
|
5514
|
+
onSelect(filtered[selectedIndex]);
|
|
5515
|
+
}
|
|
5516
|
+
setQuery("");
|
|
5517
|
+
setSelectedIndex(0);
|
|
5518
|
+
return;
|
|
5519
|
+
}
|
|
5520
|
+
if (key.upArrow) {
|
|
5521
|
+
setSelectedIndex((prev) => Math.max(prev - 1, 0));
|
|
5522
|
+
return;
|
|
5523
|
+
}
|
|
5524
|
+
if (key.downArrow) {
|
|
5525
|
+
setSelectedIndex((prev) => Math.min(prev + 1, filtered.length - 1));
|
|
5526
|
+
return;
|
|
5527
|
+
}
|
|
5528
|
+
},
|
|
5529
|
+
{ isActive: visible }
|
|
5530
|
+
);
|
|
5531
|
+
if (!visible) return null;
|
|
5532
|
+
const maxVisible = 10;
|
|
5533
|
+
const displayItems = filtered.slice(0, maxVisible);
|
|
5534
|
+
return /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", borderStyle: "single", borderColor: "yellow", paddingLeft: 1, paddingRight: 1, children: [
|
|
5535
|
+
/* @__PURE__ */ jsxs9(Box7, { children: [
|
|
5536
|
+
/* @__PURE__ */ jsx9(Text9, { color: "yellow", bold: true, children: "reverse-i-search: " }),
|
|
5537
|
+
/* @__PURE__ */ jsx9(TextInput, { value: query, onChange: (v) => {
|
|
5538
|
+
setQuery(v);
|
|
5539
|
+
setSelectedIndex(0);
|
|
5540
|
+
}, focus: visible })
|
|
5541
|
+
] }),
|
|
5542
|
+
displayItems.length > 0 ? /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", marginTop: 1, children: [
|
|
5543
|
+
displayItems.map((entry, i) => /* @__PURE__ */ jsxs9(
|
|
5544
|
+
Text9,
|
|
5545
|
+
{
|
|
5546
|
+
color: i === selectedIndex ? "cyan" : void 0,
|
|
5547
|
+
bold: i === selectedIndex,
|
|
5548
|
+
children: [
|
|
5549
|
+
i === selectedIndex ? "> " : " ",
|
|
5550
|
+
entry
|
|
5551
|
+
]
|
|
5552
|
+
},
|
|
5553
|
+
i
|
|
5554
|
+
)),
|
|
5555
|
+
filtered.length > maxVisible && /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
|
|
5556
|
+
" ...",
|
|
5557
|
+
filtered.length - maxVisible,
|
|
5558
|
+
" more matches"
|
|
5559
|
+
] })
|
|
5560
|
+
] }) : /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " No matches" })
|
|
5561
|
+
] });
|
|
5562
|
+
}
|
|
5563
|
+
|
|
5216
5564
|
// src/cli/ui/app.tsx
|
|
5217
|
-
import { Fragment as
|
|
5565
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
5218
5566
|
var DEFAULT_UI_CONFIG = {
|
|
5219
5567
|
bordered_input: true,
|
|
5220
5568
|
status_bar: true,
|
|
@@ -5227,10 +5575,10 @@ var DEFAULT_UI_CONFIG = {
|
|
|
5227
5575
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
5228
5576
|
var SPINNER_INTERVAL = 80;
|
|
5229
5577
|
function useSpinner(active) {
|
|
5230
|
-
const [frameIdx, setFrameIdx] =
|
|
5231
|
-
const [elapsed, setElapsed] =
|
|
5578
|
+
const [frameIdx, setFrameIdx] = useState6(0);
|
|
5579
|
+
const [elapsed, setElapsed] = useState6(0);
|
|
5232
5580
|
const startTime = useRef2(0);
|
|
5233
|
-
|
|
5581
|
+
useEffect4(() => {
|
|
5234
5582
|
if (!active) {
|
|
5235
5583
|
setFrameIdx(0);
|
|
5236
5584
|
setElapsed(0);
|
|
@@ -5254,19 +5602,19 @@ function renderInline(text) {
|
|
|
5254
5602
|
while (remaining.length > 0) {
|
|
5255
5603
|
const boldMatch = remaining.match(/^\*\*(.+?)\*\*/);
|
|
5256
5604
|
if (boldMatch) {
|
|
5257
|
-
parts.push(/* @__PURE__ */
|
|
5605
|
+
parts.push(/* @__PURE__ */ jsx10(Text10, { bold: true, children: boldMatch[1] }, key++));
|
|
5258
5606
|
remaining = remaining.slice(boldMatch[0].length);
|
|
5259
5607
|
continue;
|
|
5260
5608
|
}
|
|
5261
5609
|
const italicMatch = remaining.match(/^\*(.+?)\*/);
|
|
5262
5610
|
if (italicMatch) {
|
|
5263
|
-
parts.push(/* @__PURE__ */
|
|
5611
|
+
parts.push(/* @__PURE__ */ jsx10(Text10, { italic: true, children: italicMatch[1] }, key++));
|
|
5264
5612
|
remaining = remaining.slice(italicMatch[0].length);
|
|
5265
5613
|
continue;
|
|
5266
5614
|
}
|
|
5267
5615
|
const codeMatch = remaining.match(/^`([^`]+)`/);
|
|
5268
5616
|
if (codeMatch) {
|
|
5269
|
-
parts.push(/* @__PURE__ */
|
|
5617
|
+
parts.push(/* @__PURE__ */ jsx10(Text10, { color: "cyan", bold: true, children: codeMatch[1] }, key++));
|
|
5270
5618
|
remaining = remaining.slice(codeMatch[0].length);
|
|
5271
5619
|
continue;
|
|
5272
5620
|
}
|
|
@@ -5283,7 +5631,7 @@ function renderInline(text) {
|
|
|
5283
5631
|
remaining = remaining.slice(nextSpecial);
|
|
5284
5632
|
}
|
|
5285
5633
|
}
|
|
5286
|
-
return parts.length === 1 ? parts[0] : /* @__PURE__ */
|
|
5634
|
+
return parts.length === 1 ? parts[0] : /* @__PURE__ */ jsx10(Fragment3, { children: parts });
|
|
5287
5635
|
}
|
|
5288
5636
|
function renderMarkdownBlocks(text) {
|
|
5289
5637
|
const lines = text.split("\n");
|
|
@@ -5303,9 +5651,9 @@ function renderMarkdownBlocks(text) {
|
|
|
5303
5651
|
}
|
|
5304
5652
|
if (i < lines.length) i++;
|
|
5305
5653
|
elements.push(
|
|
5306
|
-
/* @__PURE__ */
|
|
5307
|
-
lang && /* @__PURE__ */
|
|
5308
|
-
/* @__PURE__ */
|
|
5654
|
+
/* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginY: 1, children: [
|
|
5655
|
+
lang && /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: lang }),
|
|
5656
|
+
/* @__PURE__ */ jsx10(Box8, { borderStyle: "single", borderColor: "gray", paddingX: 1, flexDirection: "column", children: codeLines.map((cl, ci) => /* @__PURE__ */ jsx10(Text10, { color: "white", children: cl }, ci)) })
|
|
5309
5657
|
] }, key++)
|
|
5310
5658
|
);
|
|
5311
5659
|
continue;
|
|
@@ -5315,7 +5663,7 @@ function renderMarkdownBlocks(text) {
|
|
|
5315
5663
|
const level = headerMatch[1].length;
|
|
5316
5664
|
const content = headerMatch[2];
|
|
5317
5665
|
elements.push(
|
|
5318
|
-
/* @__PURE__ */
|
|
5666
|
+
/* @__PURE__ */ jsxs10(Text10, { bold: true, color: level <= 2 ? "white" : void 0, children: [
|
|
5319
5667
|
level <= 2 ? "\n" : "",
|
|
5320
5668
|
content
|
|
5321
5669
|
] }, key++)
|
|
@@ -5325,7 +5673,7 @@ function renderMarkdownBlocks(text) {
|
|
|
5325
5673
|
}
|
|
5326
5674
|
if (/^[-*_]{3,}$/.test(trimmed)) {
|
|
5327
5675
|
elements.push(
|
|
5328
|
-
/* @__PURE__ */
|
|
5676
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "\u2500".repeat(40) }, key++)
|
|
5329
5677
|
);
|
|
5330
5678
|
i++;
|
|
5331
5679
|
continue;
|
|
@@ -5333,7 +5681,7 @@ function renderMarkdownBlocks(text) {
|
|
|
5333
5681
|
const ulMatch = trimmed.match(/^[-*+]\s+(.*)/);
|
|
5334
5682
|
if (ulMatch) {
|
|
5335
5683
|
elements.push(
|
|
5336
|
-
/* @__PURE__ */
|
|
5684
|
+
/* @__PURE__ */ jsxs10(Text10, { wrap: "wrap", children: [
|
|
5337
5685
|
" ",
|
|
5338
5686
|
"\u2022",
|
|
5339
5687
|
" ",
|
|
@@ -5346,7 +5694,7 @@ function renderMarkdownBlocks(text) {
|
|
|
5346
5694
|
const olMatch = trimmed.match(/^(\d+)[.)]\s+(.*)/);
|
|
5347
5695
|
if (olMatch) {
|
|
5348
5696
|
elements.push(
|
|
5349
|
-
/* @__PURE__ */
|
|
5697
|
+
/* @__PURE__ */ jsxs10(Text10, { wrap: "wrap", children: [
|
|
5350
5698
|
" ",
|
|
5351
5699
|
olMatch[1],
|
|
5352
5700
|
". ",
|
|
@@ -5359,7 +5707,7 @@ function renderMarkdownBlocks(text) {
|
|
|
5359
5707
|
if (trimmed.startsWith(">")) {
|
|
5360
5708
|
const content = trimmed.replace(/^>\s?/, "");
|
|
5361
5709
|
elements.push(
|
|
5362
|
-
/* @__PURE__ */
|
|
5710
|
+
/* @__PURE__ */ jsxs10(Text10, { dimColor: true, wrap: "wrap", children: [
|
|
5363
5711
|
" ",
|
|
5364
5712
|
"\u2502",
|
|
5365
5713
|
" ",
|
|
@@ -5374,7 +5722,7 @@ function renderMarkdownBlocks(text) {
|
|
|
5374
5722
|
continue;
|
|
5375
5723
|
}
|
|
5376
5724
|
elements.push(
|
|
5377
|
-
/* @__PURE__ */
|
|
5725
|
+
/* @__PURE__ */ jsx10(Text10, { wrap: "wrap", children: renderInline(line) }, key++)
|
|
5378
5726
|
);
|
|
5379
5727
|
i++;
|
|
5380
5728
|
}
|
|
@@ -5390,17 +5738,18 @@ var CopairApp = forwardRef(function CopairApp2({
|
|
|
5390
5738
|
onMessage,
|
|
5391
5739
|
onHistoryAppend,
|
|
5392
5740
|
onSlashCommand,
|
|
5393
|
-
onExit: _onExit
|
|
5741
|
+
onExit: _onExit,
|
|
5742
|
+
initialContext
|
|
5394
5743
|
}, ref) {
|
|
5395
5744
|
const config = { ...DEFAULT_UI_CONFIG, ...uiOverrides };
|
|
5396
5745
|
const { exit } = useApp();
|
|
5397
5746
|
const ctrlCCount = useRef2(0);
|
|
5398
5747
|
const ctrlCTimer = useRef2(null);
|
|
5399
5748
|
const nextId = useRef2(0);
|
|
5400
|
-
const [staticItems, setStaticItems] =
|
|
5401
|
-
const [liveText, setLiveText] =
|
|
5402
|
-
const [liveTool, setLiveTool] =
|
|
5403
|
-
const [state, setState] =
|
|
5749
|
+
const [staticItems, setStaticItems] = useState6([]);
|
|
5750
|
+
const [liveText, setLiveText] = useState6("");
|
|
5751
|
+
const [liveTool, setLiveTool] = useState6(null);
|
|
5752
|
+
const [state, setState] = useState6({
|
|
5404
5753
|
phase: "input",
|
|
5405
5754
|
model,
|
|
5406
5755
|
sessionIdentifier: sessionIdentifier ?? "",
|
|
@@ -5415,7 +5764,11 @@ var CopairApp = forwardRef(function CopairApp2({
|
|
|
5415
5764
|
contextWindowPercent: 0,
|
|
5416
5765
|
notification: null
|
|
5417
5766
|
});
|
|
5418
|
-
const spinner = useSpinner(state.phase === "thinking");
|
|
5767
|
+
const spinner = useSpinner(state.phase === "thinking" || state.phase === "streaming");
|
|
5768
|
+
const [activeSuggestion, setActiveSuggestion] = useState6(null);
|
|
5769
|
+
const [historySearchVisible, setHistorySearchVisible] = useState6(false);
|
|
5770
|
+
const [injectedInput, setInjectedInput] = useState6(void 0);
|
|
5771
|
+
const injectedNonce = useRef2(0);
|
|
5419
5772
|
useImperativeHandle(ref, () => ({
|
|
5420
5773
|
updateModel: (newModel) => {
|
|
5421
5774
|
setState((prev) => ({ ...prev, model: newModel }));
|
|
@@ -5424,7 +5777,7 @@ var CopairApp = forwardRef(function CopairApp2({
|
|
|
5424
5777
|
setState((prev) => ({ ...prev, sessionIdentifier: id }));
|
|
5425
5778
|
}
|
|
5426
5779
|
}));
|
|
5427
|
-
|
|
5780
|
+
useInput4((_input, key) => {
|
|
5428
5781
|
if (key.ctrl && _input === "c") {
|
|
5429
5782
|
ctrlCCount.current++;
|
|
5430
5783
|
if (ctrlCCount.current >= 2) {
|
|
@@ -5440,7 +5793,7 @@ var CopairApp = forwardRef(function CopairApp2({
|
|
|
5440
5793
|
}, 2e3);
|
|
5441
5794
|
}
|
|
5442
5795
|
});
|
|
5443
|
-
|
|
5796
|
+
useEffect4(() => {
|
|
5444
5797
|
const onStreamText = (text) => {
|
|
5445
5798
|
setState((prev) => prev.phase === "thinking" ? { ...prev, phase: "streaming" } : prev);
|
|
5446
5799
|
setLiveText((prev) => prev + text);
|
|
@@ -5543,48 +5896,71 @@ var CopairApp = forwardRef(function CopairApp2({
|
|
|
5543
5896
|
setState((prev) => ({ ...prev, phase: "input" }));
|
|
5544
5897
|
});
|
|
5545
5898
|
}, [onMessage, bridge]);
|
|
5546
|
-
|
|
5547
|
-
|
|
5899
|
+
const handleSlashCommand = useCallback3(async (command, args) => {
|
|
5900
|
+
if (command === "history-search") {
|
|
5901
|
+
setHistorySearchVisible(true);
|
|
5902
|
+
return;
|
|
5903
|
+
}
|
|
5904
|
+
await onSlashCommand?.(command, args);
|
|
5905
|
+
}, [onSlashCommand]);
|
|
5906
|
+
return /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", children: [
|
|
5907
|
+
/* @__PURE__ */ jsx10(Static, { items: staticItems, children: (item) => {
|
|
5548
5908
|
switch (item.type) {
|
|
5549
5909
|
case "user":
|
|
5550
|
-
return /* @__PURE__ */
|
|
5910
|
+
return /* @__PURE__ */ jsxs10(Text10, { color: "cyan", bold: true, children: [
|
|
5551
5911
|
"\u276F",
|
|
5552
5912
|
" ",
|
|
5553
5913
|
item.content
|
|
5554
5914
|
] }, item.id);
|
|
5555
5915
|
case "error":
|
|
5556
|
-
return /* @__PURE__ */
|
|
5916
|
+
return /* @__PURE__ */ jsx10(Text10, { color: "red", children: item.content }, item.id);
|
|
5557
5917
|
case "tool":
|
|
5558
|
-
return /* @__PURE__ */
|
|
5918
|
+
return /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
5559
5919
|
" ",
|
|
5560
5920
|
item.content
|
|
5561
5921
|
] }, item.id);
|
|
5562
5922
|
case "diff":
|
|
5563
|
-
return item.diff ? /* @__PURE__ */
|
|
5923
|
+
return item.diff ? /* @__PURE__ */ jsx10(DiffView, { diff: item.diff }, item.id) : null;
|
|
5564
5924
|
case "text":
|
|
5565
5925
|
default:
|
|
5566
|
-
return /* @__PURE__ */
|
|
5926
|
+
return /* @__PURE__ */ jsx10(Box8, { flexDirection: "column", children: renderMarkdownBlocks(item.content) }, item.id);
|
|
5567
5927
|
}
|
|
5568
5928
|
} }),
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5929
|
+
liveText && /* @__PURE__ */ jsx10(Box8, { flexDirection: "column", children: renderMarkdownBlocks(liveText) }),
|
|
5930
|
+
/* @__PURE__ */ jsx10(
|
|
5931
|
+
ActivityBar,
|
|
5932
|
+
{
|
|
5933
|
+
phase: state.phase,
|
|
5934
|
+
spinnerFrame: spinner.frame,
|
|
5935
|
+
spinnerElapsed: spinner.elapsed,
|
|
5936
|
+
liveTool
|
|
5937
|
+
}
|
|
5938
|
+
),
|
|
5939
|
+
config.suggestions && /* @__PURE__ */ jsx10(
|
|
5940
|
+
SuggestionHint,
|
|
5941
|
+
{
|
|
5942
|
+
bridge,
|
|
5943
|
+
enabled: config.suggestions,
|
|
5944
|
+
onSuggestionChange: setActiveSuggestion,
|
|
5945
|
+
initialContext
|
|
5946
|
+
}
|
|
5947
|
+
),
|
|
5948
|
+
/* @__PURE__ */ jsx10(
|
|
5949
|
+
HistorySearch,
|
|
5950
|
+
{
|
|
5951
|
+
history: history ?? [],
|
|
5952
|
+
visible: historySearchVisible,
|
|
5953
|
+
onSelect: (selected) => {
|
|
5954
|
+
setHistorySearchVisible(false);
|
|
5955
|
+
injectedNonce.current += 1;
|
|
5956
|
+
setInjectedInput({ value: selected, nonce: injectedNonce.current });
|
|
5957
|
+
},
|
|
5958
|
+
onDismiss: () => setHistorySearchVisible(false)
|
|
5959
|
+
}
|
|
5960
|
+
),
|
|
5961
|
+
/* @__PURE__ */ jsx10(ApprovalHandler, { bridge }),
|
|
5962
|
+
state.notification && /* @__PURE__ */ jsx10(Text10, { color: "yellow", children: state.notification }),
|
|
5963
|
+
state.phase === "input" && !historySearchVisible ? /* @__PURE__ */ jsx10(
|
|
5588
5964
|
BorderedInput,
|
|
5589
5965
|
{
|
|
5590
5966
|
sessionIdentifier: state.sessionIdentifier,
|
|
@@ -5594,10 +5970,12 @@ var CopairApp = forwardRef(function CopairApp2({
|
|
|
5594
5970
|
completionEngine,
|
|
5595
5971
|
onSubmit: handleSubmit,
|
|
5596
5972
|
onHistoryAppend,
|
|
5597
|
-
onSlashCommand
|
|
5973
|
+
onSlashCommand: handleSlashCommand,
|
|
5974
|
+
activeSuggestion,
|
|
5975
|
+
injectedValue: injectedInput
|
|
5598
5976
|
}
|
|
5599
5977
|
) : null,
|
|
5600
|
-
/* @__PURE__ */
|
|
5978
|
+
/* @__PURE__ */ jsx10(
|
|
5601
5979
|
StatusBar,
|
|
5602
5980
|
{
|
|
5603
5981
|
bridge,
|
|
@@ -5614,7 +5992,7 @@ function renderApp(bridge, model, options) {
|
|
|
5614
5992
|
imperativeHandle = handle;
|
|
5615
5993
|
};
|
|
5616
5994
|
const instance = render(
|
|
5617
|
-
/* @__PURE__ */
|
|
5995
|
+
/* @__PURE__ */ jsx10(
|
|
5618
5996
|
CopairApp,
|
|
5619
5997
|
{
|
|
5620
5998
|
ref: appRef,
|
|
@@ -5627,7 +6005,8 @@ function renderApp(bridge, model, options) {
|
|
|
5627
6005
|
onMessage: options?.onMessage,
|
|
5628
6006
|
onHistoryAppend: options?.onHistoryAppend,
|
|
5629
6007
|
onSlashCommand: options?.onSlashCommand,
|
|
5630
|
-
onExit: options?.onExit
|
|
6008
|
+
onExit: options?.onExit,
|
|
6009
|
+
initialContext: options?.initialContext
|
|
5631
6010
|
}
|
|
5632
6011
|
),
|
|
5633
6012
|
{ exitOnCtrlC: false }
|
|
@@ -5975,7 +6354,7 @@ import chalk6 from "chalk";
|
|
|
5975
6354
|
// package.json
|
|
5976
6355
|
var package_default = {
|
|
5977
6356
|
name: "@dugleelabs/copair",
|
|
5978
|
-
version: "1.
|
|
6357
|
+
version: "1.3.0",
|
|
5979
6358
|
description: "Model-agnostic AI coding agent for the terminal",
|
|
5980
6359
|
type: "module",
|
|
5981
6360
|
main: "dist/index.js",
|
|
@@ -6815,6 +7194,23 @@ async function runAuditCommand(argv) {
|
|
|
6815
7194
|
}
|
|
6816
7195
|
|
|
6817
7196
|
// src/index.ts
|
|
7197
|
+
function detectTestFramework(cwd) {
|
|
7198
|
+
const patterns = [
|
|
7199
|
+
"vitest.config.ts",
|
|
7200
|
+
"vitest.config.js",
|
|
7201
|
+
"vitest.config.mjs",
|
|
7202
|
+
"jest.config.ts",
|
|
7203
|
+
"jest.config.js",
|
|
7204
|
+
"jest.config.mjs"
|
|
7205
|
+
];
|
|
7206
|
+
if (patterns.some((f) => existsSync18(join15(cwd, f)))) return true;
|
|
7207
|
+
try {
|
|
7208
|
+
const pkg3 = JSON.parse(readFileSync10(join15(cwd, "package.json"), "utf8"));
|
|
7209
|
+
return Boolean(pkg3.scripts?.test);
|
|
7210
|
+
} catch {
|
|
7211
|
+
return false;
|
|
7212
|
+
}
|
|
7213
|
+
}
|
|
6818
7214
|
function resolveModel(config, modelOverride) {
|
|
6819
7215
|
const modelAlias = modelOverride ?? config.default_model;
|
|
6820
7216
|
if (!modelAlias) {
|
|
@@ -6906,19 +7302,6 @@ async function main() {
|
|
|
6906
7302
|
const agentBridge = new AgentBridge();
|
|
6907
7303
|
gate.setBridge(agentBridge);
|
|
6908
7304
|
const mcpManager = new McpClientManager();
|
|
6909
|
-
if (config.mcp_servers.length > 0) {
|
|
6910
|
-
setImmediate(async () => {
|
|
6911
|
-
try {
|
|
6912
|
-
await mcpManager.initialize(config.mcp_servers);
|
|
6913
|
-
const bridge = new McpBridge(mcpManager, toolRegistry);
|
|
6914
|
-
await bridge.registerAll();
|
|
6915
|
-
} catch (err) {
|
|
6916
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
6917
|
-
process.stderr.write(`[mcp] Failed to initialize MCP servers: ${msg}
|
|
6918
|
-
`);
|
|
6919
|
-
}
|
|
6920
|
-
});
|
|
6921
|
-
}
|
|
6922
7305
|
gate.addTrustedPath(join15(cwd, ".copair"));
|
|
6923
7306
|
const gitCtx = detectGitContext(cwd);
|
|
6924
7307
|
const knowledgeManager = new KnowledgeManager({
|
|
@@ -7060,6 +7443,12 @@ Environment:
|
|
|
7060
7443
|
uiConfig: config.ui,
|
|
7061
7444
|
history: inputHistory,
|
|
7062
7445
|
completionEngine,
|
|
7446
|
+
initialContext: {
|
|
7447
|
+
hasTestFramework: detectTestFramework(cwd),
|
|
7448
|
+
// Session picker already ran before ink — user chose resume or fresh.
|
|
7449
|
+
// No need to re-suggest resuming.
|
|
7450
|
+
sessionCount: 0
|
|
7451
|
+
},
|
|
7063
7452
|
onHistoryAppend: (entry) => {
|
|
7064
7453
|
inputHistory.push(entry);
|
|
7065
7454
|
appendHistory(historyPath, entry);
|
|
@@ -7146,6 +7535,18 @@ Environment:
|
|
|
7146
7535
|
agentBridge.emit("turn-complete");
|
|
7147
7536
|
}
|
|
7148
7537
|
});
|
|
7538
|
+
if (config.mcp_servers.length > 0) {
|
|
7539
|
+
setImmediate(async () => {
|
|
7540
|
+
try {
|
|
7541
|
+
await mcpManager.initialize(config.mcp_servers);
|
|
7542
|
+
const bridge = new McpBridge(mcpManager, toolRegistry);
|
|
7543
|
+
await bridge.registerAll();
|
|
7544
|
+
} catch (err) {
|
|
7545
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7546
|
+
agentBridge.emit("error", `[mcp] Failed to initialize MCP servers: ${msg}`);
|
|
7547
|
+
}
|
|
7548
|
+
});
|
|
7549
|
+
}
|
|
7149
7550
|
await appHandle.waitForExit().then(doExit);
|
|
7150
7551
|
}
|
|
7151
7552
|
if (process.argv[2] === "audit") {
|