@lazyingart/agintiflow 0.8.10 → 0.8.11
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/package.json +1 -1
- package/src/interactive-cli.js +159 -16
package/package.json
CHANGED
package/src/interactive-cli.js
CHANGED
|
@@ -60,7 +60,7 @@ function label(name, bgCode) {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function userPrompt() {
|
|
63
|
-
return `\n${label("user>", ansi.userBg)} `;
|
|
63
|
+
return `\n${label("user>", ansi.userBg)} ${color("|", ansi.userBg)} `;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
function commandCompleter(line = "") {
|
|
@@ -70,6 +70,21 @@ function commandCompleter(line = "") {
|
|
|
70
70
|
return [hits.length > 0 ? hits : SLASH_COMMANDS, trimmed];
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
function stripAnsi(value) {
|
|
74
|
+
return String(value || "").replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function promptGutter() {
|
|
78
|
+
const visible = stripAnsi(userPrompt()).replace(/^\n/, "").length;
|
|
79
|
+
return " ".repeat(Math.max(visible - 2, 0)) + `${color("|", ansi.userBg)} `;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function commandSuggestions(line = "") {
|
|
83
|
+
const trimmed = String(line || "");
|
|
84
|
+
if (!trimmed.startsWith("/") || /\s/.test(trimmed)) return [];
|
|
85
|
+
return SLASH_COMMANDS.filter((command) => command.startsWith(trimmed)).slice(0, 8);
|
|
86
|
+
}
|
|
87
|
+
|
|
73
88
|
function stripMarkdown(text) {
|
|
74
89
|
const lines = String(text || "").split(/\r?\n/);
|
|
75
90
|
let inFence = false;
|
|
@@ -79,16 +94,26 @@ function stripMarkdown(text) {
|
|
|
79
94
|
let line = rawLine;
|
|
80
95
|
if (/^\s*```/.test(line)) {
|
|
81
96
|
inFence = !inFence;
|
|
82
|
-
if (inFence)
|
|
97
|
+
if (inFence) {
|
|
98
|
+
const language = line.replace(/^\s*```/, "").trim();
|
|
99
|
+
rendered.push(color(language ? `code ${language}` : "code", ansi.dim));
|
|
100
|
+
}
|
|
83
101
|
continue;
|
|
84
102
|
}
|
|
85
103
|
|
|
86
104
|
if (!inFence) {
|
|
87
105
|
if (/^\s*[-*_]{3,}\s*$/.test(line)) {
|
|
88
|
-
rendered.push("");
|
|
106
|
+
rendered.push(color("-".repeat(42), ansi.dim));
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const heading = line.match(/^\s{0,3}(#{1,6})\s+(.+)$/);
|
|
110
|
+
if (heading) {
|
|
111
|
+
rendered.push(color(heading[2].replace(/\s+#*$/, ""), ansi.bold, ansi.cyan));
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (/^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?\s*$/.test(line)) {
|
|
89
115
|
continue;
|
|
90
116
|
}
|
|
91
|
-
line = line.replace(/^\s{0,3}#{1,6}\s+/, "");
|
|
92
117
|
line = line.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1 ($2)");
|
|
93
118
|
line = line.replace(/\*\*([^*]+)\*\*/g, (_, value) => color(value, ansi.bold));
|
|
94
119
|
line = line.replace(/__([^_]+)__/g, (_, value) => color(value, ansi.bold));
|
|
@@ -96,7 +121,9 @@ function stripMarkdown(text) {
|
|
|
96
121
|
line = line.replace(/(^|[^\w])_([^_\n]+)_/g, "$1$2");
|
|
97
122
|
line = line.replace(/`([^`]+)`/g, (_, value) => color(value, ansi.yellow));
|
|
98
123
|
line = line.replace(/^(\s*)[-*+]\s+/, "$1- ");
|
|
99
|
-
line = line.replace(/^\s*>\s
|
|
124
|
+
line = line.replace(/^\s*>\s?(.+)$/, (_, value) => color(`| ${value}`, ansi.dim));
|
|
125
|
+
} else {
|
|
126
|
+
line = color(` ${line}`, ansi.yellow);
|
|
100
127
|
}
|
|
101
128
|
|
|
102
129
|
rendered.push(line);
|
|
@@ -105,10 +132,15 @@ function stripMarkdown(text) {
|
|
|
105
132
|
return rendered.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd();
|
|
106
133
|
}
|
|
107
134
|
|
|
108
|
-
function
|
|
135
|
+
function rolePrefix(name, bgCode) {
|
|
136
|
+
return `${label(name, bgCode)} ${color("|", bgCode)} `;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function printWrapped(prefix, text, { stripCode = "" } = {}) {
|
|
109
140
|
const rendered = stripMarkdown(text);
|
|
110
141
|
const lines = rendered.split("\n");
|
|
111
|
-
const
|
|
142
|
+
const visible = stripAnsi(prefix).length;
|
|
143
|
+
const gutter = `${" ".repeat(Math.max(visible - 2, 0))}${stripCode ? color("|", stripCode) : "|"} `;
|
|
112
144
|
console.log(`${prefix}${lines[0] || ""}`);
|
|
113
145
|
for (const line of lines.slice(1)) {
|
|
114
146
|
console.log(`${gutter}${line}`);
|
|
@@ -116,7 +148,7 @@ function printWrapped(prefix, text) {
|
|
|
116
148
|
}
|
|
117
149
|
|
|
118
150
|
function printAgentMessage(text) {
|
|
119
|
-
printWrapped(
|
|
151
|
+
printWrapped(rolePrefix("aginti>", ansi.agentBg), text, { stripCode: ansi.agentBg });
|
|
120
152
|
}
|
|
121
153
|
|
|
122
154
|
function printSystemLine(text) {
|
|
@@ -197,6 +229,113 @@ function printHelp() {
|
|
|
197
229
|
);
|
|
198
230
|
}
|
|
199
231
|
|
|
232
|
+
function renderPromptBuffer(buffer, previousLineCount = 0) {
|
|
233
|
+
for (let index = 0; index < previousLineCount; index += 1) {
|
|
234
|
+
output.write(`\r${ansi.clearLine}`);
|
|
235
|
+
if (index < previousLineCount - 1) output.write("\x1b[1A");
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const lines = String(buffer || "").split("\n");
|
|
239
|
+
const suggestions = commandSuggestions(lines[0] || "");
|
|
240
|
+
const rendered = [];
|
|
241
|
+
rendered.push(`${userPrompt().replace(/^\n/, "")}${lines[0] || ""}`);
|
|
242
|
+
for (const line of lines.slice(1)) {
|
|
243
|
+
rendered.push(`${promptGutter()}${line}`);
|
|
244
|
+
}
|
|
245
|
+
if (suggestions.length > 0) {
|
|
246
|
+
rendered.push(`${promptGutter()}${color(`suggest: ${suggestions.join(" ")}`, ansi.dim)}`);
|
|
247
|
+
}
|
|
248
|
+
output.write(rendered.join("\n"));
|
|
249
|
+
return rendered.length;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function createAbortError(message = "Aborted with Ctrl+C") {
|
|
253
|
+
const error = new Error(message);
|
|
254
|
+
error.code = "ABORT_ERR";
|
|
255
|
+
error.name = "AbortError";
|
|
256
|
+
return error;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function readTtyPrompt() {
|
|
260
|
+
return new Promise((resolve, reject) => {
|
|
261
|
+
emitKeypressEvents(input);
|
|
262
|
+
const wasRaw = Boolean(input.isRaw);
|
|
263
|
+
let buffer = "";
|
|
264
|
+
let renderedLines = 0;
|
|
265
|
+
|
|
266
|
+
const cleanup = () => {
|
|
267
|
+
input.off("keypress", handler);
|
|
268
|
+
if (typeof input.setRawMode === "function") input.setRawMode(wasRaw);
|
|
269
|
+
input.pause();
|
|
270
|
+
output.write(ansi.cursorShow);
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const redraw = () => {
|
|
274
|
+
renderedLines = renderPromptBuffer(buffer, renderedLines);
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const submit = () => {
|
|
278
|
+
cleanup();
|
|
279
|
+
output.write("\n");
|
|
280
|
+
resolve(buffer);
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const handler = (str = "", key = {}) => {
|
|
284
|
+
if (key.ctrl && key.name === "c") {
|
|
285
|
+
cleanup();
|
|
286
|
+
output.write("\n");
|
|
287
|
+
reject(createAbortError());
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
if ((key.ctrl && key.name === "j") || (key.sequence === "\n" && key.name !== "return" && key.name !== "enter")) {
|
|
291
|
+
buffer += "\n";
|
|
292
|
+
redraw();
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (key.name === "return" || key.name === "enter" || key.sequence === "\r" || str === "\r") {
|
|
296
|
+
submit();
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (key.name === "backspace") {
|
|
300
|
+
buffer = buffer.slice(0, -1);
|
|
301
|
+
redraw();
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (key.name === "tab") {
|
|
305
|
+
const suggestions = commandSuggestions(buffer.split("\n")[0] || "");
|
|
306
|
+
if (suggestions.length === 1) {
|
|
307
|
+
buffer = suggestions[0];
|
|
308
|
+
}
|
|
309
|
+
redraw();
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (key.name === "escape") {
|
|
313
|
+
buffer = "";
|
|
314
|
+
redraw();
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (key.ctrl || key.meta) return;
|
|
318
|
+
if (str && !key.sequence?.startsWith("\x1b")) {
|
|
319
|
+
buffer += str;
|
|
320
|
+
redraw();
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
input.resume();
|
|
325
|
+
input.setRawMode(true);
|
|
326
|
+
output.write(ansi.cursorHide);
|
|
327
|
+
input.on("keypress", handler);
|
|
328
|
+
redraw();
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
async function readPromptAnswer(rl) {
|
|
333
|
+
if (input.isTTY && output.isTTY && typeof input.setRawMode === "function") {
|
|
334
|
+
return readTtyPrompt();
|
|
335
|
+
}
|
|
336
|
+
return rl.question(userPrompt());
|
|
337
|
+
}
|
|
338
|
+
|
|
200
339
|
function printStatus(state) {
|
|
201
340
|
printSystemLine(`project=${process.cwd()}`);
|
|
202
341
|
printSystemLine(`cwd=${state.commandCwd || process.cwd()}`);
|
|
@@ -237,6 +376,7 @@ function attachRunInterrupts(controller) {
|
|
|
237
376
|
|
|
238
377
|
emitKeypressEvents(input);
|
|
239
378
|
const wasRaw = Boolean(input.isRaw);
|
|
379
|
+
input.resume();
|
|
240
380
|
input.setRawMode(true);
|
|
241
381
|
const handler = (_str, key = {}) => {
|
|
242
382
|
const isEscape = key.name === "escape";
|
|
@@ -564,12 +704,15 @@ async function runPrompt(prompt, state, packageDir) {
|
|
|
564
704
|
|
|
565
705
|
export async function startInteractiveCli(args = {}, { packageDir, packageVersion } = {}) {
|
|
566
706
|
const state = createState(args);
|
|
567
|
-
const rl =
|
|
568
|
-
input
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
707
|
+
const rl =
|
|
708
|
+
input.isTTY && output.isTTY
|
|
709
|
+
? null
|
|
710
|
+
: readline.createInterface({
|
|
711
|
+
input,
|
|
712
|
+
output,
|
|
713
|
+
terminal: false,
|
|
714
|
+
completer: commandCompleter,
|
|
715
|
+
});
|
|
573
716
|
|
|
574
717
|
await renderLaunchHeader(packageVersion);
|
|
575
718
|
printSystemLine(`Project: ${process.cwd()}`);
|
|
@@ -581,7 +724,7 @@ export async function startInteractiveCli(args = {}, { packageDir, packageVersio
|
|
|
581
724
|
while (true) {
|
|
582
725
|
let answer = "";
|
|
583
726
|
try {
|
|
584
|
-
answer = await rl
|
|
727
|
+
answer = await readPromptAnswer(rl);
|
|
585
728
|
} catch (error) {
|
|
586
729
|
if (error?.code === "ERR_USE_AFTER_CLOSE") break;
|
|
587
730
|
if (isAbortError(error)) {
|
|
@@ -609,6 +752,6 @@ export async function startInteractiveCli(args = {}, { packageDir, packageVersio
|
|
|
609
752
|
}
|
|
610
753
|
}
|
|
611
754
|
} finally {
|
|
612
|
-
rl
|
|
755
|
+
rl?.close();
|
|
613
756
|
}
|
|
614
757
|
}
|