@bike4mind/cli 0.2.7-basic-cli-shortcuts.17131 → 0.2.7-feat-cli-rewind-command.17128
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 +293 -189
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -80,12 +80,12 @@ import {
|
|
|
80
80
|
|
|
81
81
|
// src/index.tsx
|
|
82
82
|
import React14, { useState as useState5, useEffect as useEffect3, useCallback, useRef } from "react";
|
|
83
|
-
import { render, Box as
|
|
83
|
+
import { render, Box as Box14, Text as Text13, useApp, useInput as useInput4 } from "ink";
|
|
84
84
|
import { v4 as uuidv49 } from "uuid";
|
|
85
85
|
|
|
86
86
|
// src/components/App.tsx
|
|
87
|
-
import
|
|
88
|
-
import { Box as Box9, Text as
|
|
87
|
+
import React9 from "react";
|
|
88
|
+
import { Box as Box9, Text as Text8, Static } from "ink";
|
|
89
89
|
|
|
90
90
|
// src/components/StatusBar.tsx
|
|
91
91
|
import React from "react";
|
|
@@ -95,127 +95,27 @@ var StatusBar = React.memo(function StatusBar2({ sessionName, model, tokenUsage
|
|
|
95
95
|
});
|
|
96
96
|
|
|
97
97
|
// src/components/InputPrompt.tsx
|
|
98
|
-
import
|
|
99
|
-
import { Box as Box4, Text as
|
|
100
|
-
|
|
101
|
-
// src/components/CustomTextInput.tsx
|
|
102
|
-
import React2, { useState } from "react";
|
|
103
|
-
import { Text as Text2, useInput } from "ink";
|
|
104
|
-
function CustomTextInput({
|
|
105
|
-
value,
|
|
106
|
-
onChange,
|
|
107
|
-
onSubmit,
|
|
108
|
-
placeholder = "",
|
|
109
|
-
showCursor = true
|
|
110
|
-
}) {
|
|
111
|
-
const [cursorOffset, setCursorOffset] = useState(value.length);
|
|
112
|
-
useInput(
|
|
113
|
-
(input, key) => {
|
|
114
|
-
if (key.return && !key.meta && !key.shift) {
|
|
115
|
-
onSubmit(value);
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
if (key.ctrl) {
|
|
119
|
-
switch (input) {
|
|
120
|
-
case "u":
|
|
121
|
-
{
|
|
122
|
-
const lines = value.split("\n");
|
|
123
|
-
let currentLineStart = 0;
|
|
124
|
-
let currentLineIndex = 0;
|
|
125
|
-
let charCount = 0;
|
|
126
|
-
for (let i = 0; i < lines.length; i++) {
|
|
127
|
-
const lineLength = lines[i].length;
|
|
128
|
-
if (charCount + lineLength >= cursorOffset) {
|
|
129
|
-
currentLineIndex = i;
|
|
130
|
-
currentLineStart = charCount;
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
133
|
-
charCount += lineLength + 1;
|
|
134
|
-
}
|
|
135
|
-
lines.splice(currentLineIndex, 1);
|
|
136
|
-
const newValue = lines.join("\n");
|
|
137
|
-
onChange(newValue);
|
|
138
|
-
setCursorOffset(Math.min(currentLineStart, newValue.length));
|
|
139
|
-
}
|
|
140
|
-
return;
|
|
141
|
-
case "k":
|
|
142
|
-
onChange(value.slice(0, cursorOffset));
|
|
143
|
-
return;
|
|
144
|
-
case "w":
|
|
145
|
-
{
|
|
146
|
-
const beforeCursor2 = value.slice(0, cursorOffset);
|
|
147
|
-
const afterCursor2 = value.slice(cursorOffset);
|
|
148
|
-
const trimmed = beforeCursor2.trimEnd();
|
|
149
|
-
const lastSpace = trimmed.lastIndexOf(" ");
|
|
150
|
-
const newBefore = lastSpace === -1 ? "" : trimmed.slice(0, lastSpace + 1);
|
|
151
|
-
const newValue = newBefore + afterCursor2;
|
|
152
|
-
onChange(newValue);
|
|
153
|
-
setCursorOffset(newBefore.length);
|
|
154
|
-
}
|
|
155
|
-
return;
|
|
156
|
-
case "a":
|
|
157
|
-
setCursorOffset(0);
|
|
158
|
-
return;
|
|
159
|
-
case "e":
|
|
160
|
-
setCursorOffset(value.length);
|
|
161
|
-
return;
|
|
162
|
-
case "l":
|
|
163
|
-
onChange("");
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
if (key.backspace || key.delete) {
|
|
168
|
-
if (cursorOffset > 0) {
|
|
169
|
-
const newValue = value.slice(0, cursorOffset - 1) + value.slice(cursorOffset);
|
|
170
|
-
onChange(newValue);
|
|
171
|
-
setCursorOffset(cursorOffset - 1);
|
|
172
|
-
}
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
if (key.leftArrow) {
|
|
176
|
-
setCursorOffset(Math.max(0, cursorOffset - 1));
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
if (key.rightArrow) {
|
|
180
|
-
setCursorOffset(Math.min(value.length, cursorOffset + 1));
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
if (!key.ctrl && !key.meta && input.length === 1) {
|
|
184
|
-
const sanitizedInput = input === "\r" ? "\n" : input;
|
|
185
|
-
const newValue = value.slice(0, cursorOffset) + sanitizedInput + value.slice(cursorOffset);
|
|
186
|
-
onChange(newValue);
|
|
187
|
-
setCursorOffset(cursorOffset + 1);
|
|
188
|
-
}
|
|
189
|
-
},
|
|
190
|
-
{ isActive: true }
|
|
191
|
-
);
|
|
192
|
-
const hasValue = value.length > 0;
|
|
193
|
-
if (!hasValue) {
|
|
194
|
-
return /* @__PURE__ */ React2.createElement(Text2, null, showCursor ? /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, { inverse: true }, " "), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, placeholder)) : /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, placeholder));
|
|
195
|
-
}
|
|
196
|
-
const beforeCursor = value.slice(0, cursorOffset);
|
|
197
|
-
const cursorChar = value[cursorOffset] || " ";
|
|
198
|
-
const afterCursor = value.slice(cursorOffset + 1);
|
|
199
|
-
return /* @__PURE__ */ React2.createElement(Text2, null, beforeCursor, showCursor && /* @__PURE__ */ React2.createElement(Text2, { inverse: true }, cursorChar), !showCursor && cursorChar, afterCursor);
|
|
200
|
-
}
|
|
98
|
+
import React4, { useState, useMemo, useEffect } from "react";
|
|
99
|
+
import { Box as Box4, Text as Text4, useInput } from "ink";
|
|
100
|
+
import TextInput from "ink-text-input";
|
|
201
101
|
|
|
202
102
|
// src/components/CommandAutocomplete.tsx
|
|
203
|
-
import
|
|
204
|
-
import { Box as Box2, Text as
|
|
103
|
+
import React2 from "react";
|
|
104
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
205
105
|
function CommandAutocomplete({ commands, selectedIndex }) {
|
|
206
106
|
if (commands.length === 0) {
|
|
207
|
-
return /* @__PURE__ */
|
|
107
|
+
return /* @__PURE__ */ React2.createElement(Box2, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "No matching commands"));
|
|
208
108
|
}
|
|
209
|
-
return /* @__PURE__ */
|
|
109
|
+
return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React2.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, commands.length === 1 ? "1 command" : `${commands.length} commands`, " \u2022 Use \u2191\u2193 to navigate, Enter to select")), commands.map((cmd, index) => {
|
|
210
110
|
const args = cmd.args ? ` ${cmd.args}` : "";
|
|
211
111
|
const isSelected = index === selectedIndex;
|
|
212
|
-
return /* @__PURE__ */
|
|
112
|
+
return /* @__PURE__ */ React2.createElement(Box2, { key: cmd.name, marginLeft: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: isSelected ? "cyan" : void 0, bold: isSelected }, isSelected ? "\u25B8 " : " ", "/", cmd.name, args, " - ", cmd.description));
|
|
213
113
|
}));
|
|
214
114
|
}
|
|
215
115
|
|
|
216
116
|
// src/components/FileAutocomplete.tsx
|
|
217
|
-
import
|
|
218
|
-
import { Box as Box3, Text as
|
|
117
|
+
import React3 from "react";
|
|
118
|
+
import { Box as Box3, Text as Text3 } from "ink";
|
|
219
119
|
|
|
220
120
|
// src/utils/fileSearch.ts
|
|
221
121
|
import * as fs from "fs";
|
|
@@ -404,14 +304,14 @@ var MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
|
404
304
|
// src/components/FileAutocomplete.tsx
|
|
405
305
|
function FileAutocomplete({ files, selectedIndex, query }) {
|
|
406
306
|
if (files.length === 0) {
|
|
407
|
-
return /* @__PURE__ */
|
|
307
|
+
return /* @__PURE__ */ React3.createElement(Box3, { marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "No matching files", query ? ` for "${query}"` : ""));
|
|
408
308
|
}
|
|
409
|
-
return /* @__PURE__ */
|
|
309
|
+
return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column", marginLeft: 2, marginTop: 1 }, /* @__PURE__ */ React3.createElement(Box3, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, files.length === 1 ? "1 match" : `${files.length} matches`, " - Use up/down to navigate, Tab to select")), files.map((file, index) => {
|
|
410
310
|
const isSelected = index === selectedIndex;
|
|
411
311
|
const icon = file.isDirectory ? "[folder]" : "[file] ";
|
|
412
312
|
const sizeDisplay = file.isDirectory ? "" : file.size !== void 0 ? ` (${formatFileSize(file.size)})` : "";
|
|
413
313
|
const pathDisplay = file.isDirectory ? `${file.path}/` : file.path;
|
|
414
|
-
return /* @__PURE__ */
|
|
314
|
+
return /* @__PURE__ */ React3.createElement(Box3, { key: file.path, marginLeft: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: isSelected ? "cyan" : void 0, bold: isSelected }, isSelected ? "> " : " "), /* @__PURE__ */ React3.createElement(Text3, { color: file.isDirectory ? "yellow" : isSelected ? "cyan" : void 0, bold: isSelected }, icon), /* @__PURE__ */ React3.createElement(Text3, { color: isSelected ? "cyan" : void 0, bold: isSelected }, " ", pathDisplay), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, sizeDisplay));
|
|
415
315
|
}));
|
|
416
316
|
}
|
|
417
317
|
|
|
@@ -489,6 +389,11 @@ var COMMANDS = [
|
|
|
489
389
|
name: "clear",
|
|
490
390
|
description: "Start a new session",
|
|
491
391
|
aliases: ["new"]
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
name: "rewind",
|
|
395
|
+
description: "Rewind conversation to a previous point",
|
|
396
|
+
aliases: ["undo"]
|
|
492
397
|
}
|
|
493
398
|
];
|
|
494
399
|
|
|
@@ -725,13 +630,28 @@ function findAtTrigger(value) {
|
|
|
725
630
|
}
|
|
726
631
|
return null;
|
|
727
632
|
}
|
|
728
|
-
function InputPrompt({
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
633
|
+
function InputPrompt({
|
|
634
|
+
onSubmit,
|
|
635
|
+
onImageDetected,
|
|
636
|
+
disabled = false,
|
|
637
|
+
history = [],
|
|
638
|
+
prefillInput,
|
|
639
|
+
onPrefillConsumed
|
|
640
|
+
}) {
|
|
641
|
+
const [value, setValue] = useState("");
|
|
642
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
643
|
+
const [historyIndex, setHistoryIndex] = useState(-1);
|
|
644
|
+
const [tempInput, setTempInput] = useState("");
|
|
645
|
+
const [inputKey, setInputKey] = useState(0);
|
|
646
|
+
useEffect(() => {
|
|
647
|
+
if (prefillInput) {
|
|
648
|
+
setValue(prefillInput);
|
|
649
|
+
setInputKey((prev) => prev + 1);
|
|
650
|
+
onPrefillConsumed?.();
|
|
651
|
+
}
|
|
652
|
+
}, [prefillInput, onPrefillConsumed]);
|
|
653
|
+
const [fileAutocomplete, setFileAutocomplete] = useState(null);
|
|
654
|
+
const [fileSelectedIndex, setFileSelectedIndex] = useState(0);
|
|
735
655
|
const shouldShowCommandAutocomplete = value.startsWith("/") && !disabled && !fileAutocomplete?.active;
|
|
736
656
|
const commandQuery = shouldShowCommandAutocomplete ? value.slice(1) : "";
|
|
737
657
|
const filteredCommands = useMemo(() => {
|
|
@@ -748,7 +668,7 @@ function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = []
|
|
|
748
668
|
useEffect(() => {
|
|
749
669
|
setFileSelectedIndex(0);
|
|
750
670
|
}, [filteredFiles]);
|
|
751
|
-
|
|
671
|
+
useInput((input, key) => {
|
|
752
672
|
if (fileAutocomplete?.active && filteredFiles.length > 0) {
|
|
753
673
|
if (key.upArrow) {
|
|
754
674
|
setFileSelectedIndex((prev) => prev > 0 ? prev - 1 : filteredFiles.length - 1);
|
|
@@ -805,6 +725,7 @@ function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = []
|
|
|
805
725
|
const pathToInsert = file.isDirectory ? `@${file.path}/` : `@${file.path}`;
|
|
806
726
|
const newValue = beforeAt + pathToInsert + afterQuery;
|
|
807
727
|
setValue(newValue);
|
|
728
|
+
setInputKey((prev) => prev + 1);
|
|
808
729
|
if (file.isDirectory) {
|
|
809
730
|
setFileAutocomplete({
|
|
810
731
|
active: true,
|
|
@@ -831,6 +752,7 @@ function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = []
|
|
|
831
752
|
if (selectedCommand.args) {
|
|
832
753
|
setValue(commandText + " ");
|
|
833
754
|
setHistoryIndex(-1);
|
|
755
|
+
setInputKey((prev) => prev + 1);
|
|
834
756
|
return;
|
|
835
757
|
}
|
|
836
758
|
setValue("");
|
|
@@ -849,6 +771,7 @@ function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = []
|
|
|
849
771
|
if (imageEvent && onImageDetected) {
|
|
850
772
|
const placeholder = await onImageDetected(imageEvent.data);
|
|
851
773
|
setValue(`${placeholder} `);
|
|
774
|
+
setInputKey((prev) => prev + 1);
|
|
852
775
|
return;
|
|
853
776
|
}
|
|
854
777
|
}
|
|
@@ -868,28 +791,29 @@ function InputPrompt({ onSubmit, onImageDetected, disabled = false, history = []
|
|
|
868
791
|
setFileAutocomplete(null);
|
|
869
792
|
}
|
|
870
793
|
};
|
|
871
|
-
return /* @__PURE__ */
|
|
872
|
-
|
|
794
|
+
return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "cyan" }, "> "), /* @__PURE__ */ React4.createElement(
|
|
795
|
+
TextInput,
|
|
873
796
|
{
|
|
797
|
+
key: inputKey,
|
|
874
798
|
value,
|
|
875
799
|
onChange: handleChange,
|
|
876
800
|
onSubmit: handleSubmit,
|
|
877
801
|
placeholder: disabled ? "Thinking..." : "Type your message, /help for commands, or @file to reference",
|
|
878
802
|
showCursor: !disabled
|
|
879
803
|
}
|
|
880
|
-
)), shouldShowCommandAutocomplete && /* @__PURE__ */
|
|
804
|
+
)), shouldShowCommandAutocomplete && /* @__PURE__ */ React4.createElement(CommandAutocomplete, { commands: filteredCommands, selectedIndex }), fileAutocomplete?.active && /* @__PURE__ */ React4.createElement(FileAutocomplete, { files: filteredFiles, selectedIndex: fileSelectedIndex, query: fileAutocomplete.query }));
|
|
881
805
|
}
|
|
882
806
|
|
|
883
807
|
// src/components/AgentThinking.tsx
|
|
884
|
-
import
|
|
808
|
+
import React6 from "react";
|
|
885
809
|
import { Box as Box6 } from "ink";
|
|
886
810
|
|
|
887
811
|
// src/components/ThoughtStream.tsx
|
|
888
|
-
import
|
|
889
|
-
import { Box as Box5, Text as
|
|
812
|
+
import React5 from "react";
|
|
813
|
+
import { Box as Box5, Text as Text5 } from "ink";
|
|
890
814
|
import Spinner from "ink-spinner";
|
|
891
|
-
var ThoughtStream =
|
|
892
|
-
return /* @__PURE__ */
|
|
815
|
+
var ThoughtStream = React5.memo(function ThoughtStream2({ steps }) {
|
|
816
|
+
return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", gap: 1 }, steps.map((step, index) => /* @__PURE__ */ React5.createElement(Box5, { key: index, flexDirection: "column" }, step.type === "thought" && /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "magenta" }, "\u{1F914} Thought: "), /* @__PURE__ */ React5.createElement(Text5, null, step.content)), step.type === "action" && /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "yellow" }, "\u26A1 Action: "), /* @__PURE__ */ React5.createElement(Text5, { bold: true }, step.metadata?.toolName || "unknown")), step.metadata?.toolInput && /* @__PURE__ */ React5.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, JSON.stringify(step.metadata.toolInput, null, 2)))), step.type === "observation" && /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "blue" }, "\u{1F4CA} Observation: ")), /* @__PURE__ */ React5.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React5.createElement(Text5, null, step.content))), step.type === "final_answer" && /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "green", bold: true }, "\u2705 Final Answer:", " ")), /* @__PURE__ */ React5.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React5.createElement(Text5, null, step.content)), step.metadata?.tokenUsage && /* @__PURE__ */ React5.createElement(Box5, { paddingLeft: 2 }, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "(", step.metadata.tokenUsage.total, " tokens)"))))), !steps.some((s) => s.type === "final_answer") && /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, { color: "yellow" }, /* @__PURE__ */ React5.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React5.createElement(Text5, null, " ", steps.length === 0 ? "Thinking..." : "Processing...")));
|
|
893
817
|
});
|
|
894
818
|
|
|
895
819
|
// src/store/index.ts
|
|
@@ -918,32 +842,32 @@ var useCliStore = create((set) => ({
|
|
|
918
842
|
}));
|
|
919
843
|
|
|
920
844
|
// src/components/AgentThinking.tsx
|
|
921
|
-
var AgentThinking =
|
|
845
|
+
var AgentThinking = React6.memo(function AgentThinking2() {
|
|
922
846
|
const isThinking = useCliStore((state) => state.isThinking);
|
|
923
847
|
const agentSteps = useCliStore((state) => state.agentSteps);
|
|
924
848
|
if (!isThinking) {
|
|
925
849
|
return null;
|
|
926
850
|
}
|
|
927
|
-
return /* @__PURE__ */
|
|
851
|
+
return /* @__PURE__ */ React6.createElement(Box6, { borderStyle: "single", borderColor: "yellow", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React6.createElement(ThoughtStream, { steps: agentSteps }));
|
|
928
852
|
});
|
|
929
853
|
|
|
930
854
|
// src/components/PermissionPrompt.tsx
|
|
931
|
-
import
|
|
932
|
-
import { Box as Box7, Text as
|
|
855
|
+
import React7 from "react";
|
|
856
|
+
import { Box as Box7, Text as Text6 } from "ink";
|
|
933
857
|
import SelectInput from "ink-select-input";
|
|
934
858
|
function renderDiffPreview(preview) {
|
|
935
859
|
const lines = preview.split("\n");
|
|
936
860
|
return lines.map((line, index) => {
|
|
937
861
|
if (line.startsWith("+")) {
|
|
938
|
-
return /* @__PURE__ */
|
|
862
|
+
return /* @__PURE__ */ React7.createElement(Text6, { key: index, color: "green" }, line);
|
|
939
863
|
}
|
|
940
864
|
if (line.startsWith("-")) {
|
|
941
|
-
return /* @__PURE__ */
|
|
865
|
+
return /* @__PURE__ */ React7.createElement(Text6, { key: index, color: "red" }, line);
|
|
942
866
|
}
|
|
943
867
|
if (line.startsWith("@@")) {
|
|
944
|
-
return /* @__PURE__ */
|
|
868
|
+
return /* @__PURE__ */ React7.createElement(Text6, { key: index, color: "cyan" }, line);
|
|
945
869
|
}
|
|
946
|
-
return /* @__PURE__ */
|
|
870
|
+
return /* @__PURE__ */ React7.createElement(Text6, { key: index, dimColor: true }, line);
|
|
947
871
|
});
|
|
948
872
|
}
|
|
949
873
|
function PermissionPrompt({
|
|
@@ -963,12 +887,12 @@ function PermissionPrompt({
|
|
|
963
887
|
{ label: "\u2717 Deny", value: "deny" }
|
|
964
888
|
];
|
|
965
889
|
const argsString = typeof args === "string" ? args : JSON.stringify(args, null, 2);
|
|
966
|
-
return /* @__PURE__ */
|
|
890
|
+
return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", borderStyle: "bold", borderColor: "yellow", padding: 1, marginY: 1 }, /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text6, { bold: true, color: "yellow" }, "\u26A0\uFE0F Permission Required")), /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, "Tool: "), /* @__PURE__ */ React7.createElement(Text6, { bold: true, color: "cyan" }, toolName)), toolDescription && /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, "Action: "), /* @__PURE__ */ React7.createElement(Text6, null, toolDescription)), /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text6, { bold: true }, "Arguments:"), /* @__PURE__ */ React7.createElement(Box7, { paddingLeft: 2, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, argsString))), preview && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text6, { bold: true }, "Preview:"), /* @__PURE__ */ React7.createElement(Box7, { borderStyle: "single", borderColor: "gray", paddingX: 1, flexDirection: "column" }, renderDiffPreview(preview))), !canBeTrusted && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text6, { color: "red", dimColor: true }, "Note: This tool cannot be trusted due to its dangerous nature.")), /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(SelectInput, { items, onSelect: (item) => onResponse(item.value) })));
|
|
967
891
|
}
|
|
968
892
|
|
|
969
893
|
// src/components/ConfigEditor.tsx
|
|
970
|
-
import
|
|
971
|
-
import { Box as Box8, Text as
|
|
894
|
+
import React8, { useState as useState2, useMemo as useMemo2 } from "react";
|
|
895
|
+
import { Box as Box8, Text as Text7, useInput as useInput2 } from "ink";
|
|
972
896
|
var MAX_ITERATIONS_OPTIONS = [
|
|
973
897
|
{ label: "10", value: 10 },
|
|
974
898
|
{ label: "20", value: 20 },
|
|
@@ -1099,10 +1023,10 @@ function buildConfigItems(availableModels) {
|
|
|
1099
1023
|
return items;
|
|
1100
1024
|
}
|
|
1101
1025
|
function ConfigEditor({ config, availableModels, onSave, onClose }) {
|
|
1102
|
-
const [selectedIndex, setSelectedIndex] =
|
|
1103
|
-
const [editedConfig, setEditedConfig] =
|
|
1104
|
-
const [saveError, setSaveError] =
|
|
1105
|
-
const [isSaving, setIsSaving] =
|
|
1026
|
+
const [selectedIndex, setSelectedIndex] = useState2(0);
|
|
1027
|
+
const [editedConfig, setEditedConfig] = useState2(config);
|
|
1028
|
+
const [saveError, setSaveError] = useState2(null);
|
|
1029
|
+
const [isSaving, setIsSaving] = useState2(false);
|
|
1106
1030
|
const configItems = useMemo2(() => buildConfigItems(availableModels), [availableModels]);
|
|
1107
1031
|
const hasChanges = useMemo2(() => {
|
|
1108
1032
|
return JSON.stringify(config.preferences) !== JSON.stringify(editedConfig.preferences) || config.defaultModel !== editedConfig.defaultModel;
|
|
@@ -1122,7 +1046,7 @@ function ConfigEditor({ config, availableModels, onSave, onClose }) {
|
|
|
1122
1046
|
}
|
|
1123
1047
|
onClose();
|
|
1124
1048
|
};
|
|
1125
|
-
|
|
1049
|
+
useInput2((input, key) => {
|
|
1126
1050
|
const currentItem = configItems[selectedIndex];
|
|
1127
1051
|
if (key.upArrow) {
|
|
1128
1052
|
setSelectedIndex((prev) => prev > 0 ? prev - 1 : configItems.length - 1);
|
|
@@ -1187,13 +1111,13 @@ function ConfigEditor({ config, availableModels, onSave, onClose }) {
|
|
|
1187
1111
|
}
|
|
1188
1112
|
return String(value);
|
|
1189
1113
|
};
|
|
1190
|
-
return /* @__PURE__ */
|
|
1114
|
+
return /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, marginY: 1 }, /* @__PURE__ */ React8.createElement(Box8, { justifyContent: "space-between", marginBottom: 1 }, /* @__PURE__ */ React8.createElement(Text7, { bold: true, color: "cyan" }, "Settings"), isSaving ? /* @__PURE__ */ React8.createElement(Text7, { color: "yellow" }, "Saving...") : hasChanges ? /* @__PURE__ */ React8.createElement(Text7, { dimColor: true, color: "yellow" }, "(unsaved changes)") : null), saveError && /* @__PURE__ */ React8.createElement(Box8, { marginBottom: 1 }, /* @__PURE__ */ React8.createElement(Text7, { color: "red" }, "Error: ", saveError)), /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "column", marginBottom: 1 }, configItems.map((item, index) => {
|
|
1191
1115
|
const isSelected = index === selectedIndex;
|
|
1192
1116
|
const value = item.getValue(editedConfig);
|
|
1193
1117
|
const displayValue = formatValue(item, value);
|
|
1194
1118
|
const hasOptions = item.type === "select" || item.type === "boolean" || item.type === "number";
|
|
1195
|
-
return /* @__PURE__ */
|
|
1196
|
-
})), /* @__PURE__ */
|
|
1119
|
+
return /* @__PURE__ */ React8.createElement(Box8, { key: item.key }, /* @__PURE__ */ React8.createElement(Text7, { color: isSelected ? "cyan" : void 0 }, isSelected ? "> " : " "), /* @__PURE__ */ React8.createElement(Box8, { width: 18 }, /* @__PURE__ */ React8.createElement(Text7, { bold: isSelected, color: isSelected ? "white" : "gray" }, item.label, ":")), /* @__PURE__ */ React8.createElement(Box8, null, hasOptions && isSelected && /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, "< "), /* @__PURE__ */ React8.createElement(Text7, { bold: isSelected, color: isSelected ? "green" : void 0 }, displayValue), hasOptions && isSelected && /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, " >")));
|
|
1120
|
+
})), /* @__PURE__ */ React8.createElement(Box8, { borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, "\u2191/\u2193: Navigate | Space/\u2190/\u2192: Change | q/Esc: Save & Exit")));
|
|
1197
1121
|
}
|
|
1198
1122
|
|
|
1199
1123
|
// src/utils/processFileReferences.ts
|
|
@@ -1319,7 +1243,9 @@ function App({
|
|
|
1319
1243
|
commandHistory = [],
|
|
1320
1244
|
config,
|
|
1321
1245
|
availableModels = [],
|
|
1322
|
-
onSaveConfig
|
|
1246
|
+
onSaveConfig,
|
|
1247
|
+
prefillInput,
|
|
1248
|
+
onPrefillConsumed
|
|
1323
1249
|
}) {
|
|
1324
1250
|
const messages = useCliStore((state) => state.session?.messages || []);
|
|
1325
1251
|
const sessionName = useCliStore((state) => state.session?.name || "New Session");
|
|
@@ -1331,7 +1257,7 @@ function App({
|
|
|
1331
1257
|
const setShowConfigEditor = useCliStore((state) => state.setShowConfigEditor);
|
|
1332
1258
|
const exitRequested = useCliStore((state) => state.exitRequested);
|
|
1333
1259
|
const setIsThinking = useCliStore((state) => state.setIsThinking);
|
|
1334
|
-
const handleSubmit =
|
|
1260
|
+
const handleSubmit = React9.useCallback(
|
|
1335
1261
|
async (input) => {
|
|
1336
1262
|
const trimmed = input.trim();
|
|
1337
1263
|
if (!trimmed) return;
|
|
@@ -1360,7 +1286,7 @@ ${errorBlock}`;
|
|
|
1360
1286
|
},
|
|
1361
1287
|
[onMessage, onCommand, setIsThinking]
|
|
1362
1288
|
);
|
|
1363
|
-
return /* @__PURE__ */
|
|
1289
|
+
return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column" }, /* @__PURE__ */ React9.createElement(StatusBar, { sessionName, model: currentModel, tokenUsage: totalTokens }), showConfigEditor && config && onSaveConfig ? /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React9.createElement(
|
|
1364
1290
|
ConfigEditor,
|
|
1365
1291
|
{
|
|
1366
1292
|
config,
|
|
@@ -1368,7 +1294,7 @@ ${errorBlock}`;
|
|
|
1368
1294
|
onSave: onSaveConfig,
|
|
1369
1295
|
onClose: () => setShowConfigEditor(false)
|
|
1370
1296
|
}
|
|
1371
|
-
)) : permissionPrompt ? /* @__PURE__ */
|
|
1297
|
+
)) : permissionPrompt ? /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React9.createElement(
|
|
1372
1298
|
PermissionPrompt,
|
|
1373
1299
|
{
|
|
1374
1300
|
toolName: permissionPrompt.toolName,
|
|
@@ -1377,47 +1303,49 @@ ${errorBlock}`;
|
|
|
1377
1303
|
canBeTrusted: permissionPrompt.canBeTrusted,
|
|
1378
1304
|
onResponse: onPermissionResponse
|
|
1379
1305
|
}
|
|
1380
|
-
)) : /* @__PURE__ */
|
|
1306
|
+
)) : /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Static, { items: messages }, (message, index) => /* @__PURE__ */ React9.createElement(Box9, { key: index, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React9.createElement(MessageItem, { message }))), /* @__PURE__ */ React9.createElement(AgentThinking, null), exitRequested && /* @__PURE__ */ React9.createElement(Box9, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "yellow", bold: true }, "Press Ctrl+C again to exit")), /* @__PURE__ */ React9.createElement(Box9, { borderStyle: "single", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React9.createElement(
|
|
1381
1307
|
InputPrompt,
|
|
1382
1308
|
{
|
|
1383
1309
|
onSubmit: handleSubmit,
|
|
1384
1310
|
onImageDetected,
|
|
1385
1311
|
disabled: isThinking,
|
|
1386
|
-
history: commandHistory
|
|
1312
|
+
history: commandHistory,
|
|
1313
|
+
prefillInput,
|
|
1314
|
+
onPrefillConsumed
|
|
1387
1315
|
}
|
|
1388
1316
|
))));
|
|
1389
1317
|
}
|
|
1390
1318
|
function MessageItem({ message }) {
|
|
1391
|
-
return /* @__PURE__ */
|
|
1319
|
+
return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Box9, null, /* @__PURE__ */ React9.createElement(Text8, { bold: true, color: message.role === "user" ? "cyan" : "green" }, message.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), /* @__PURE__ */ React9.createElement(Box9, { paddingLeft: 2 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, "\u26A0\uFE0F ", message.content) : /* @__PURE__ */ React9.createElement(Text8, null, message.content)), message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React9.createElement(Box9, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " ", "tools used, ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
|
|
1392
1320
|
if (step.type === "thought") {
|
|
1393
|
-
return /* @__PURE__ */
|
|
1321
|
+
return /* @__PURE__ */ React9.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text8, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` ${step.content.slice(0, 200)}${step.content.length > 200 ? "..." : ""}`));
|
|
1394
1322
|
}
|
|
1395
1323
|
if (step.type === "action") {
|
|
1396
1324
|
const toolName = step.metadata?.toolName || "unknown";
|
|
1397
1325
|
const toolInput = step.metadata?.toolInput;
|
|
1398
1326
|
const observationStep = message.metadata.steps[idx + 1];
|
|
1399
1327
|
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
1400
|
-
return /* @__PURE__ */
|
|
1328
|
+
return /* @__PURE__ */ React9.createElement(Box9, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` Result: ${typeof result === "string" ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200)}${(typeof result === "string" ? result.length : JSON.stringify(result).length) > 200 ? "..." : ""}`));
|
|
1401
1329
|
}
|
|
1402
1330
|
return null;
|
|
1403
|
-
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */
|
|
1331
|
+
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React9.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)));
|
|
1404
1332
|
}
|
|
1405
1333
|
|
|
1406
1334
|
// src/components/MessageList.tsx
|
|
1407
|
-
import
|
|
1408
|
-
import { Box as Box10, Text as
|
|
1335
|
+
import React10 from "react";
|
|
1336
|
+
import { Box as Box10, Text as Text9 } from "ink";
|
|
1409
1337
|
function stripThinkingTags(content) {
|
|
1410
1338
|
return content.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
|
|
1411
1339
|
}
|
|
1412
|
-
var MessageList =
|
|
1340
|
+
var MessageList = React10.memo(
|
|
1413
1341
|
function MessageList2({ messages }) {
|
|
1414
1342
|
if (messages.length === 0) {
|
|
1415
|
-
return /* @__PURE__ */
|
|
1343
|
+
return /* @__PURE__ */ React10.createElement(Box10, { paddingY: 1 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "No messages yet. Type a message to start!"));
|
|
1416
1344
|
}
|
|
1417
|
-
return /* @__PURE__ */
|
|
1345
|
+
return /* @__PURE__ */ React10.createElement(Box10, { flexDirection: "column", gap: 1, paddingY: 1 }, messages.map((message, index) => /* @__PURE__ */ React10.createElement(Box10, { key: index, flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Box10, null, /* @__PURE__ */ React10.createElement(Text9, { bold: true, color: message.role === "user" ? "cyan" : "green" }, message.role === "user" ? "\u{1F464} You" : "\u{1F916} Assistant"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " \u2022 ", new Date(message.timestamp).toLocaleTimeString())), /* @__PURE__ */ React10.createElement(Box10, { paddingLeft: 2 }, message.metadata?.permissionDenied ? /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u26A0\uFE0F ", stripThinkingTags(message.content)) : /* @__PURE__ */ React10.createElement(Text9, null, stripThinkingTags(message.content))), message.metadata?.steps && message.metadata.steps.length > 0 && /* @__PURE__ */ React10.createElement(Box10, { paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true, bold: true }, "\u{1F527} Agent Reasoning Trace (", message.metadata.steps.filter((s) => s.type === "action").length, " tools used,", " ", message.metadata.steps.length, " total steps)", message.metadata.tokenUsage && ` \u2022 ${message.metadata.tokenUsage.total} tokens`), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, "Step types: ", message.metadata.steps.map((s) => s.type).join(", ")), message.metadata.steps.map((step, idx) => {
|
|
1418
1346
|
if (step.type === "thought") {
|
|
1419
|
-
return /* @__PURE__ */
|
|
1420
|
-
|
|
1347
|
+
return /* @__PURE__ */ React10.createElement(Box10, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "blue" }, "\u{1F4AD} Thought:"), /* @__PURE__ */ React10.createElement(
|
|
1348
|
+
Text9,
|
|
1421
1349
|
{
|
|
1422
1350
|
dimColor: true
|
|
1423
1351
|
},
|
|
@@ -1429,10 +1357,10 @@ var MessageList = React11.memo(
|
|
|
1429
1357
|
const toolInput = step.metadata?.toolInput;
|
|
1430
1358
|
const observationStep = message.metadata.steps[idx + 1];
|
|
1431
1359
|
const result = observationStep?.type === "observation" ? observationStep.content : null;
|
|
1432
|
-
return /* @__PURE__ */
|
|
1360
|
+
return /* @__PURE__ */ React10.createElement(Box10, { key: idx, paddingLeft: 2, marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, "\u{1F527} Action: ", toolName), toolInput && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` Input: ${typeof toolInput === "string" ? toolInput : JSON.stringify(toolInput).slice(0, 100)}`), result && /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` Result: ${typeof result === "string" ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200)}${(typeof result === "string" ? result.length : JSON.stringify(result).length) > 200 ? "..." : ""}`));
|
|
1433
1361
|
}
|
|
1434
1362
|
return null;
|
|
1435
|
-
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */
|
|
1363
|
+
}).filter(Boolean)), message.metadata?.tokenUsage && (!message.metadata.steps || message.metadata.steps.length === 0) && /* @__PURE__ */ React10.createElement(Box10, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, `${message.metadata.tokenUsage.total} tokens`)))));
|
|
1436
1364
|
},
|
|
1437
1365
|
(prevProps, nextProps) => {
|
|
1438
1366
|
if (prevProps.messages.length !== nextProps.messages.length) {
|
|
@@ -1443,8 +1371,8 @@ var MessageList = React11.memo(
|
|
|
1443
1371
|
);
|
|
1444
1372
|
|
|
1445
1373
|
// src/components/TrustLocationSelector.tsx
|
|
1446
|
-
import
|
|
1447
|
-
import { Box as Box11, Text as
|
|
1374
|
+
import React11 from "react";
|
|
1375
|
+
import { Box as Box11, Text as Text10 } from "ink";
|
|
1448
1376
|
import SelectInput2 from "ink-select-input";
|
|
1449
1377
|
function TrustLocationSelector({ inProject, onSelect, onCancel }) {
|
|
1450
1378
|
const items = [];
|
|
@@ -1465,19 +1393,88 @@ function TrustLocationSelector({ inProject, onSelect, onCancel }) {
|
|
|
1465
1393
|
const handleSelect = (item) => {
|
|
1466
1394
|
onSelect(item.value);
|
|
1467
1395
|
};
|
|
1468
|
-
return /* @__PURE__ */
|
|
1396
|
+
return /* @__PURE__ */ React11.createElement(Box11, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React11.createElement(Box11, { marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Text10, { bold: true }, "Where should this rule be saved?")), /* @__PURE__ */ React11.createElement(
|
|
1469
1397
|
SelectInput2,
|
|
1470
1398
|
{
|
|
1471
1399
|
items,
|
|
1472
1400
|
onSelect: handleSelect,
|
|
1473
|
-
itemComponent: ({ isSelected, label }) => /* @__PURE__ */
|
|
1401
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React11.createElement(Box11, null, /* @__PURE__ */ React11.createElement(Text10, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1402
|
+
}
|
|
1403
|
+
), /* @__PURE__ */ React11.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Ctrl+C to cancel")));
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
// src/components/RewindSelector.tsx
|
|
1407
|
+
import React12, { useState as useState3 } from "react";
|
|
1408
|
+
import { Box as Box12, Text as Text11, useInput as useInput3 } from "ink";
|
|
1409
|
+
import SelectInput3 from "ink-select-input";
|
|
1410
|
+
function RewindSelector({ messages, onSelect, onCancel }) {
|
|
1411
|
+
const [step, setStep] = useState3("selection");
|
|
1412
|
+
const [selectedMessageIndex, setSelectedMessageIndex] = useState3(null);
|
|
1413
|
+
useInput3((input, key) => {
|
|
1414
|
+
if (key.escape) {
|
|
1415
|
+
if (step === "confirmation") {
|
|
1416
|
+
setStep("selection");
|
|
1417
|
+
setSelectedMessageIndex(null);
|
|
1418
|
+
} else {
|
|
1419
|
+
onCancel();
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
});
|
|
1423
|
+
const userMessages = messages.map((msg, idx) => ({ msg, originalIndex: idx })).filter(({ msg }) => msg.role === "user");
|
|
1424
|
+
const items = userMessages.map(({ msg, originalIndex }, index) => {
|
|
1425
|
+
const preview = msg.content.length > 50 ? msg.content.substring(0, 50) + "..." : msg.content;
|
|
1426
|
+
const timestamp = new Date(msg.timestamp).toLocaleString();
|
|
1427
|
+
const position = index + 1;
|
|
1428
|
+
return {
|
|
1429
|
+
label: `[${position}] ${timestamp} - ${preview}`,
|
|
1430
|
+
value: originalIndex
|
|
1431
|
+
};
|
|
1432
|
+
});
|
|
1433
|
+
const handleSelectionSelect = (item) => {
|
|
1434
|
+
setSelectedMessageIndex(item.value);
|
|
1435
|
+
setStep("confirmation");
|
|
1436
|
+
};
|
|
1437
|
+
const handleConfirmationSelect = (item) => {
|
|
1438
|
+
if (item.value === "confirm" && selectedMessageIndex !== null) {
|
|
1439
|
+
onSelect(selectedMessageIndex);
|
|
1440
|
+
} else {
|
|
1441
|
+
setStep("selection");
|
|
1442
|
+
setSelectedMessageIndex(null);
|
|
1443
|
+
}
|
|
1444
|
+
};
|
|
1445
|
+
const messagesToRemove = selectedMessageIndex !== null && selectedMessageIndex >= 0 && selectedMessageIndex < messages.length ? messages.slice(selectedMessageIndex) : [];
|
|
1446
|
+
const tokensToRemove = messagesToRemove.reduce((sum2, msg) => {
|
|
1447
|
+
return sum2 + (msg.metadata?.tokenUsage?.total || 0);
|
|
1448
|
+
}, 0);
|
|
1449
|
+
if (step === "selection") {
|
|
1450
|
+
return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React12.createElement(Text11, { bold: true }, "Select a point to rewind to:")), /* @__PURE__ */ React12.createElement(
|
|
1451
|
+
SelectInput3,
|
|
1452
|
+
{
|
|
1453
|
+
items,
|
|
1454
|
+
onSelect: handleSelectionSelect,
|
|
1455
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text11, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1456
|
+
}
|
|
1457
|
+
), /* @__PURE__ */ React12.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to cancel")));
|
|
1458
|
+
}
|
|
1459
|
+
const confirmationItems = [
|
|
1460
|
+
{ label: "Yes, rewind to this point", value: "confirm" },
|
|
1461
|
+
{ label: "No, go back to selection", value: "cancel" }
|
|
1462
|
+
];
|
|
1463
|
+
const selectedMessage = selectedMessageIndex !== null && selectedMessageIndex >= 0 && selectedMessageIndex < messages.length ? messages[selectedMessageIndex] : null;
|
|
1464
|
+
const selectedPreview = selectedMessage ? selectedMessage.content.length > 50 ? selectedMessage.content.substring(0, 50) + "..." : selectedMessage.content : "";
|
|
1465
|
+
return /* @__PURE__ */ React12.createElement(Box12, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React12.createElement(Box12, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React12.createElement(Text11, { bold: true }, "Confirm rewind operation:"), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Message: ", selectedPreview), /* @__PURE__ */ React12.createElement(Text11, { color: "yellow" }, "This will remove ", messagesToRemove.length, " message(s) (", tokensToRemove.toLocaleString(), " tokens)"), /* @__PURE__ */ React12.createElement(Text11, { color: "cyan" }, "The selected message will be placed in your input for editing.")), /* @__PURE__ */ React12.createElement(
|
|
1466
|
+
SelectInput3,
|
|
1467
|
+
{
|
|
1468
|
+
items: confirmationItems,
|
|
1469
|
+
onSelect: handleConfirmationSelect,
|
|
1470
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ React12.createElement(Box12, null, /* @__PURE__ */ React12.createElement(Text11, { color: isSelected ? "cyan" : void 0 }, isSelected ? "\u276F " : " ", label))
|
|
1474
1471
|
}
|
|
1475
|
-
), /* @__PURE__ */ React12.createElement(
|
|
1472
|
+
), /* @__PURE__ */ React12.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "Use \u2191\u2193 arrows to navigate, Enter to select, Esc to go back")));
|
|
1476
1473
|
}
|
|
1477
1474
|
|
|
1478
1475
|
// src/components/LoginFlow.tsx
|
|
1479
1476
|
import React13, { useState as useState4, useEffect as useEffect2 } from "react";
|
|
1480
|
-
import { Box as
|
|
1477
|
+
import { Box as Box13, Text as Text12 } from "ink";
|
|
1481
1478
|
import Spinner2 from "ink-spinner";
|
|
1482
1479
|
import jwt from "jsonwebtoken";
|
|
1483
1480
|
import open from "open";
|
|
@@ -1640,15 +1637,15 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
|
|
|
1640
1637
|
}
|
|
1641
1638
|
}, [deviceFlow, status]);
|
|
1642
1639
|
if (status === "initiating") {
|
|
1643
|
-
return /* @__PURE__ */ React13.createElement(
|
|
1640
|
+
return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Box13, null, /* @__PURE__ */ React13.createElement(Text12, { color: "cyan" }, /* @__PURE__ */ React13.createElement(Spinner2, { type: "dots" }), " Initiating device authorization...")));
|
|
1644
1641
|
}
|
|
1645
1642
|
if (status === "error") {
|
|
1646
|
-
return /* @__PURE__ */ React13.createElement(
|
|
1643
|
+
return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "red", bold: true }, "\u2716 Authentication Failed")), /* @__PURE__ */ React13.createElement(Box13, null, /* @__PURE__ */ React13.createElement(Text12, { color: "red" }, error)));
|
|
1647
1644
|
}
|
|
1648
1645
|
if (status === "success") {
|
|
1649
|
-
return /* @__PURE__ */ React13.createElement(
|
|
1646
|
+
return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "green", bold: true }, "\u2714 Successfully authenticated!")), /* @__PURE__ */ React13.createElement(Box13, null, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "You can now use B4M CLI with your account.")));
|
|
1650
1647
|
}
|
|
1651
|
-
return /* @__PURE__ */ React13.createElement(
|
|
1648
|
+
return /* @__PURE__ */ React13.createElement(Box13, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan" }, /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "cyan", bold: true }, "\u{1F510} Device Authorization")), /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, null, "Opening browser automatically... If it doesn't open, please visit:")), /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React13.createElement(Text12, { color: "blue", bold: true }, deviceFlow?.verification_uri)), /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React13.createElement(Text12, null, "And enter this code when prompted:")), /* @__PURE__ */ React13.createElement(Box13, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React13.createElement(Text12, { color: "yellow", bold: true }, deviceFlow?.user_code)), /* @__PURE__ */ React13.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "cyan" }, /* @__PURE__ */ React13.createElement(Spinner2, { type: "dots" }), " ", statusMessage)), /* @__PURE__ */ React13.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "Expires in ", deviceFlow ? Math.floor(deviceFlow.expires_in / 60) : 0, " minutes")));
|
|
1652
1649
|
}
|
|
1653
1650
|
|
|
1654
1651
|
// src/storage/SessionStore.ts
|
|
@@ -10356,7 +10353,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
10356
10353
|
// package.json
|
|
10357
10354
|
var package_default = {
|
|
10358
10355
|
name: "@bike4mind/cli",
|
|
10359
|
-
version: "0.2.7-
|
|
10356
|
+
version: "0.2.7-feat-cli-rewind-command.17128+8d4de110f",
|
|
10360
10357
|
type: "module",
|
|
10361
10358
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
10362
10359
|
license: "UNLICENSED",
|
|
@@ -10459,10 +10456,10 @@ var package_default = {
|
|
|
10459
10456
|
},
|
|
10460
10457
|
devDependencies: {
|
|
10461
10458
|
"@bike4mind/agents": "0.1.0",
|
|
10462
|
-
"@bike4mind/common": "2.38.1-
|
|
10463
|
-
"@bike4mind/mcp": "1.20.1-
|
|
10464
|
-
"@bike4mind/services": "2.33.1-
|
|
10465
|
-
"@bike4mind/utils": "2.1.2-
|
|
10459
|
+
"@bike4mind/common": "2.38.1-feat-cli-rewind-command.17128+8d4de110f",
|
|
10460
|
+
"@bike4mind/mcp": "1.20.1-feat-cli-rewind-command.17128+8d4de110f",
|
|
10461
|
+
"@bike4mind/services": "2.33.1-feat-cli-rewind-command.17128+8d4de110f",
|
|
10462
|
+
"@bike4mind/utils": "2.1.2-feat-cli-rewind-command.17128+8d4de110f",
|
|
10466
10463
|
"@types/better-sqlite3": "^7.6.12",
|
|
10467
10464
|
"@types/diff": "^5.0.9",
|
|
10468
10465
|
"@types/jsonwebtoken": "^9.0.4",
|
|
@@ -10475,7 +10472,7 @@ var package_default = {
|
|
|
10475
10472
|
typescript: "^5.9.3",
|
|
10476
10473
|
vitest: "^3.2.4"
|
|
10477
10474
|
},
|
|
10478
|
-
gitHead: "
|
|
10475
|
+
gitHead: "8d4de110f7d8f55bff6bd26cb38b8ca1d4dbd996"
|
|
10479
10476
|
};
|
|
10480
10477
|
|
|
10481
10478
|
// src/config/constants.ts
|
|
@@ -10510,7 +10507,8 @@ function CliApp() {
|
|
|
10510
10507
|
mcpManager: null,
|
|
10511
10508
|
permissionManager: null,
|
|
10512
10509
|
permissionPrompt: null,
|
|
10513
|
-
trustLocationSelector: null
|
|
10510
|
+
trustLocationSelector: null,
|
|
10511
|
+
rewindSelector: null
|
|
10514
10512
|
});
|
|
10515
10513
|
const [isInitialized, setIsInitialized] = useState5(false);
|
|
10516
10514
|
const [initError, setInitError] = useState5(null);
|
|
@@ -10909,6 +10907,30 @@ Remember: Use context from previous messages to understand follow-up questions.`
|
|
|
10909
10907
|
throw error;
|
|
10910
10908
|
}
|
|
10911
10909
|
};
|
|
10910
|
+
const recalculateSessionMetadata = (messages) => {
|
|
10911
|
+
let totalTokens = 0;
|
|
10912
|
+
let totalCost = 0;
|
|
10913
|
+
let toolCallCount = 0;
|
|
10914
|
+
for (const msg of messages) {
|
|
10915
|
+
if (msg.metadata) {
|
|
10916
|
+
if (msg.metadata.tokenUsage) {
|
|
10917
|
+
totalTokens += msg.metadata.tokenUsage.total || 0;
|
|
10918
|
+
}
|
|
10919
|
+
if (msg.metadata.cost) {
|
|
10920
|
+
totalCost += msg.metadata.cost;
|
|
10921
|
+
}
|
|
10922
|
+
if (msg.metadata.steps) {
|
|
10923
|
+
const observations = msg.metadata.steps.filter((s) => s.type === "observation");
|
|
10924
|
+
toolCallCount += observations.length;
|
|
10925
|
+
}
|
|
10926
|
+
}
|
|
10927
|
+
}
|
|
10928
|
+
return {
|
|
10929
|
+
totalTokens,
|
|
10930
|
+
totalCost,
|
|
10931
|
+
toolCallCount
|
|
10932
|
+
};
|
|
10933
|
+
};
|
|
10912
10934
|
const handleCommand = async (command, args) => {
|
|
10913
10935
|
switch (command) {
|
|
10914
10936
|
case "help":
|
|
@@ -10917,6 +10939,7 @@ Available commands:
|
|
|
10917
10939
|
/help - Show this help message
|
|
10918
10940
|
/exit - Exit the CLI
|
|
10919
10941
|
/clear - Start a new session
|
|
10942
|
+
/rewind - Rewind conversation to a previous point
|
|
10920
10943
|
/login - Authenticate with your B4M account
|
|
10921
10944
|
/logout - Clear authentication and sign out
|
|
10922
10945
|
/whoami - Show current authenticated user
|
|
@@ -11192,6 +11215,65 @@ Project Configuration:
|
|
|
11192
11215
|
`);
|
|
11193
11216
|
break;
|
|
11194
11217
|
}
|
|
11218
|
+
case "rewind":
|
|
11219
|
+
case "undo": {
|
|
11220
|
+
if (!state.session) {
|
|
11221
|
+
console.log("No active session to rewind");
|
|
11222
|
+
return;
|
|
11223
|
+
}
|
|
11224
|
+
if (state.session.messages.length === 0) {
|
|
11225
|
+
console.log("\u26A0\uFE0F Conversation is empty. Nothing to rewind.");
|
|
11226
|
+
return;
|
|
11227
|
+
}
|
|
11228
|
+
const userMessages = state.session.messages.filter((msg) => msg.role === "user");
|
|
11229
|
+
if (userMessages.length === 0) {
|
|
11230
|
+
console.log("\u26A0\uFE0F No user messages found. Nothing to rewind.");
|
|
11231
|
+
return;
|
|
11232
|
+
}
|
|
11233
|
+
if (userMessages.length === 1) {
|
|
11234
|
+
console.log("\u26A0\uFE0F Only one exchange in the conversation. Use /clear to start a new session instead.");
|
|
11235
|
+
return;
|
|
11236
|
+
}
|
|
11237
|
+
const handleRewindSelect = async (messageIndex) => {
|
|
11238
|
+
setState((prev) => ({ ...prev, rewindSelector: null }));
|
|
11239
|
+
if (messageIndex === null) {
|
|
11240
|
+
console.log("\u274C Rewind operation cancelled");
|
|
11241
|
+
return;
|
|
11242
|
+
}
|
|
11243
|
+
if (!state.session) {
|
|
11244
|
+
console.log("\u274C No active session");
|
|
11245
|
+
return;
|
|
11246
|
+
}
|
|
11247
|
+
const selectedMessage = state.session.messages[messageIndex];
|
|
11248
|
+
const prefillContent = selectedMessage?.content || "";
|
|
11249
|
+
const rewindedMessages = state.session.messages.slice(0, messageIndex);
|
|
11250
|
+
const newMetadata = recalculateSessionMetadata(rewindedMessages);
|
|
11251
|
+
const rewindedSession = {
|
|
11252
|
+
...state.session,
|
|
11253
|
+
messages: rewindedMessages,
|
|
11254
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11255
|
+
metadata: newMetadata
|
|
11256
|
+
};
|
|
11257
|
+
setState((prev) => ({
|
|
11258
|
+
...prev,
|
|
11259
|
+
session: rewindedSession,
|
|
11260
|
+
prefillInput: prefillContent
|
|
11261
|
+
}));
|
|
11262
|
+
setStoreSession(rewindedSession);
|
|
11263
|
+
await state.sessionStore.save(rewindedSession);
|
|
11264
|
+
console.log("\u2705 Conversation rewound successfully");
|
|
11265
|
+
console.log(`\u{1F4CA} Current state: ${rewindedMessages.length} messages, ${newMetadata.totalTokens} tokens`);
|
|
11266
|
+
console.log(`\u{1F4DD} Your message has been placed in the input. Edit and send when ready.
|
|
11267
|
+
`);
|
|
11268
|
+
};
|
|
11269
|
+
setState((prev) => ({
|
|
11270
|
+
...prev,
|
|
11271
|
+
rewindSelector: {
|
|
11272
|
+
resolve: handleRewindSelect
|
|
11273
|
+
}
|
|
11274
|
+
}));
|
|
11275
|
+
break;
|
|
11276
|
+
}
|
|
11195
11277
|
case "usage": {
|
|
11196
11278
|
const usageAuthTokens = await state.configStore.getAuthTokens();
|
|
11197
11279
|
if (!usageAuthTokens || new Date(usageAuthTokens.expiresAt) <= /* @__PURE__ */ new Date()) {
|
|
@@ -11345,7 +11427,7 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11345
11427
|
setState((prev) => ({ ...prev, config: updatedConfig }));
|
|
11346
11428
|
};
|
|
11347
11429
|
if (initError) {
|
|
11348
|
-
return /* @__PURE__ */ React14.createElement(
|
|
11430
|
+
return /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React14.createElement(Text13, { color: "red", bold: true }, "\u274C Initialization Error"), /* @__PURE__ */ React14.createElement(Text13, null, initError), /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, "\n", "Tip: Run /config to set up your API keys"));
|
|
11349
11431
|
}
|
|
11350
11432
|
if (state.trustLocationSelector) {
|
|
11351
11433
|
const projectDir = state.configStore.getProjectConfigDir();
|
|
@@ -11367,6 +11449,24 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11367
11449
|
}
|
|
11368
11450
|
);
|
|
11369
11451
|
}
|
|
11452
|
+
if (state.rewindSelector && state.session) {
|
|
11453
|
+
return /* @__PURE__ */ React14.createElement(
|
|
11454
|
+
RewindSelector,
|
|
11455
|
+
{
|
|
11456
|
+
messages: state.session.messages,
|
|
11457
|
+
onSelect: (messageIndex) => {
|
|
11458
|
+
if (state.rewindSelector) {
|
|
11459
|
+
state.rewindSelector.resolve(messageIndex);
|
|
11460
|
+
}
|
|
11461
|
+
},
|
|
11462
|
+
onCancel: () => {
|
|
11463
|
+
if (state.rewindSelector) {
|
|
11464
|
+
state.rewindSelector.resolve(null);
|
|
11465
|
+
}
|
|
11466
|
+
}
|
|
11467
|
+
}
|
|
11468
|
+
);
|
|
11469
|
+
}
|
|
11370
11470
|
if (state.showLoginFlow) {
|
|
11371
11471
|
const loginApiUrl = getApiUrl(state.config?.apiConfig);
|
|
11372
11472
|
return /* @__PURE__ */ React14.createElement(
|
|
@@ -11392,7 +11492,7 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11392
11492
|
);
|
|
11393
11493
|
}
|
|
11394
11494
|
if (!isInitialized) {
|
|
11395
|
-
return /* @__PURE__ */ React14.createElement(
|
|
11495
|
+
return /* @__PURE__ */ React14.createElement(Box14, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React14.createElement(Text13, null, "\u{1F680} Initializing..."));
|
|
11396
11496
|
}
|
|
11397
11497
|
return /* @__PURE__ */ React14.createElement(
|
|
11398
11498
|
App,
|
|
@@ -11404,6 +11504,10 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
11404
11504
|
config: state.config,
|
|
11405
11505
|
availableModels: state.availableModels,
|
|
11406
11506
|
onSaveConfig: handleSaveConfig,
|
|
11507
|
+
prefillInput: state.prefillInput,
|
|
11508
|
+
onPrefillConsumed: () => {
|
|
11509
|
+
setState((prev) => ({ ...prev, prefillInput: void 0 }));
|
|
11510
|
+
},
|
|
11407
11511
|
onPermissionResponse: (response) => {
|
|
11408
11512
|
if (state.permissionPrompt) {
|
|
11409
11513
|
state.permissionPrompt.resolve({ action: response });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bike4mind/cli",
|
|
3
|
-
"version": "0.2.7-
|
|
3
|
+
"version": "0.2.7-feat-cli-rewind-command.17128+8d4de110f",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -103,10 +103,10 @@
|
|
|
103
103
|
},
|
|
104
104
|
"devDependencies": {
|
|
105
105
|
"@bike4mind/agents": "0.1.0",
|
|
106
|
-
"@bike4mind/common": "2.38.1-
|
|
107
|
-
"@bike4mind/mcp": "1.20.1-
|
|
108
|
-
"@bike4mind/services": "2.33.1-
|
|
109
|
-
"@bike4mind/utils": "2.1.2-
|
|
106
|
+
"@bike4mind/common": "2.38.1-feat-cli-rewind-command.17128+8d4de110f",
|
|
107
|
+
"@bike4mind/mcp": "1.20.1-feat-cli-rewind-command.17128+8d4de110f",
|
|
108
|
+
"@bike4mind/services": "2.33.1-feat-cli-rewind-command.17128+8d4de110f",
|
|
109
|
+
"@bike4mind/utils": "2.1.2-feat-cli-rewind-command.17128+8d4de110f",
|
|
110
110
|
"@types/better-sqlite3": "^7.6.12",
|
|
111
111
|
"@types/diff": "^5.0.9",
|
|
112
112
|
"@types/jsonwebtoken": "^9.0.4",
|
|
@@ -119,5 +119,5 @@
|
|
|
119
119
|
"typescript": "^5.9.3",
|
|
120
120
|
"vitest": "^3.2.4"
|
|
121
121
|
},
|
|
122
|
-
"gitHead": "
|
|
122
|
+
"gitHead": "8d4de110f7d8f55bff6bd26cb38b8ca1d4dbd996"
|
|
123
123
|
}
|