micro-models-agent 0.32.0 → 0.33.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/main.js +907 -147
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -2162,7 +2162,7 @@ var init_defaults = __esm(() => {
|
|
|
2162
2162
|
init_config();
|
|
2163
2163
|
DEFAULTS = {
|
|
2164
2164
|
version: "2.0.0",
|
|
2165
|
-
model: "qwen3.5-9b",
|
|
2165
|
+
model: "qwen/qwen3.5-9b",
|
|
2166
2166
|
provider: {
|
|
2167
2167
|
type: "openai-compat",
|
|
2168
2168
|
baseUrl: "http://localhost:1234/v1",
|
|
@@ -2177,18 +2177,18 @@ var init_defaults = __esm(() => {
|
|
|
2177
2177
|
},
|
|
2178
2178
|
experts: {
|
|
2179
2179
|
code: {
|
|
2180
|
-
model: "qwen3.5-9b",
|
|
2180
|
+
model: "qwen/qwen3.5-9b",
|
|
2181
2181
|
tool_tags: ["file", "code", "shell"],
|
|
2182
2182
|
max_attempts: 3
|
|
2183
2183
|
},
|
|
2184
|
-
research: { model: "qwen3.5-9b", tool_tags: ["research"], max_attempts: 3 },
|
|
2184
|
+
research: { model: "qwen/qwen3.5-9b", tool_tags: ["research"], max_attempts: 3 },
|
|
2185
2185
|
browser: {
|
|
2186
|
-
model: "qwen3.5-9b",
|
|
2186
|
+
model: "qwen/qwen3.5-9b",
|
|
2187
2187
|
tool_tags: ["browser", "vision"],
|
|
2188
2188
|
max_attempts: 3
|
|
2189
2189
|
},
|
|
2190
2190
|
vision: {
|
|
2191
|
-
model: "qwen3.5-9b",
|
|
2191
|
+
model: "qwen/qwen3.5-9b",
|
|
2192
2192
|
tool_tags: ["browser", "vision", "file"],
|
|
2193
2193
|
max_attempts: 2
|
|
2194
2194
|
}
|
|
@@ -2458,7 +2458,7 @@ Command: {command}`,
|
|
|
2458
2458
|
"plan.step_status": "Step {step}: {status}",
|
|
2459
2459
|
"plan.no_active": "No active plan",
|
|
2460
2460
|
"plan.step_not_found": "Step not found",
|
|
2461
|
-
"plan.step_already_done": "Step {step} is already
|
|
2461
|
+
"plan.step_already_done": "Step {step} is already done — nothing to do. Plan progress:",
|
|
2462
2462
|
"plan.order_blocked": 'Cannot mark step {step} done: step {first} ("{desc}") is not finished yet. Complete earlier steps first, or mark step {first} status=skipped if it is not needed.',
|
|
2463
2463
|
"plan.show_header": "Plan status:",
|
|
2464
2464
|
"plan.show_empty": "(plan has no steps)",
|
|
@@ -3035,7 +3035,7 @@ var init_ru = __esm(() => {
|
|
|
3035
3035
|
"plan.step_status": "Шаг {step}: {status}",
|
|
3036
3036
|
"plan.no_active": "Нет активного плана",
|
|
3037
3037
|
"plan.step_not_found": "Шаг не найден",
|
|
3038
|
-
"plan.step_already_done": "Шаг {step} уже
|
|
3038
|
+
"plan.step_already_done": "Шаг {step} уже выполнен — ничего делать не нужно. Прогресс плана:",
|
|
3039
3039
|
"plan.order_blocked": "Нельзя отметить шаг {step} выполненным: шаг {first} («{desc}») ещё не завершён. Сначала завершите предыдущие шаги или отметьте шаг {first} status=skipped, если он не нужен.",
|
|
3040
3040
|
"plan.show_header": "Статус плана:",
|
|
3041
3041
|
"plan.show_empty": "(в плане нет шагов)",
|
|
@@ -10435,6 +10435,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
10435
10435
|
this.abortController = new AbortController;
|
|
10436
10436
|
let iteration = 0;
|
|
10437
10437
|
let lastText = "";
|
|
10438
|
+
let lastModelText = "";
|
|
10438
10439
|
let lastForcedCompactionIteration = -FORCED_COMPACTION_COOLDOWN;
|
|
10439
10440
|
let hallucinationRetries = 0;
|
|
10440
10441
|
let lastToolSignature = "";
|
|
@@ -10451,6 +10452,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
10451
10452
|
let emptyResponseExhausted = false;
|
|
10452
10453
|
let auditFailed = false;
|
|
10453
10454
|
let lastAuditSummary = "";
|
|
10455
|
+
let suppressRepetitionRetry = false;
|
|
10454
10456
|
let repeatedToolCount = 0;
|
|
10455
10457
|
const MAX_REPEATED_TOOL_CALLS = 2;
|
|
10456
10458
|
const allToolsForBudget = toolExecutor.getToolDefinitions(this.deps.toolTags);
|
|
@@ -10738,9 +10740,12 @@ ${taskReminder}</system-summary>`
|
|
|
10738
10740
|
`));
|
|
10739
10741
|
}
|
|
10740
10742
|
}
|
|
10743
|
+
suppressRepetitionRetry = false;
|
|
10741
10744
|
continue;
|
|
10742
10745
|
}
|
|
10743
|
-
hallucinationDetector.getConfidenceCheck().setPreviousResponse(
|
|
10746
|
+
hallucinationDetector.getConfidenceCheck().setPreviousResponse(lastModelText);
|
|
10747
|
+
if (textContent)
|
|
10748
|
+
lastModelText = textContent;
|
|
10744
10749
|
const hallucinationResult = await hallucinationDetector.validate(textContent);
|
|
10745
10750
|
if (hallucinationResult.status === "block") {
|
|
10746
10751
|
logger.warn(`Response blocked: ${hallucinationResult.reason}`);
|
|
@@ -10772,30 +10777,45 @@ ${warnLine}
|
|
|
10772
10777
|
lastText = textContent;
|
|
10773
10778
|
break;
|
|
10774
10779
|
}
|
|
10775
|
-
if (
|
|
10776
|
-
|
|
10777
|
-
|
|
10778
|
-
|
|
10779
|
-
|
|
10780
|
-
|
|
10781
|
-
|
|
10782
|
-
|
|
10783
|
-
|
|
10784
|
-
|
|
10785
|
-
|
|
10786
|
-
|
|
10787
|
-
|
|
10788
|
-
|
|
10780
|
+
if (suppressRepetitionRetry && hallucinationResult.kind === "repetition") {
|
|
10781
|
+
suppressRepetitionRetry = false;
|
|
10782
|
+
logger.warn(`Audit-triggered re-answer repetition — not counted as hallucination retry`);
|
|
10783
|
+
const warnLine = `${t("hall.uncertainty_prefix").trim()} ${hallucinationResult.reason ?? ""}`;
|
|
10784
|
+
if (onMeta) {
|
|
10785
|
+
onMeta(`
|
|
10786
|
+
${pc2.yellow(warnLine)}
|
|
10787
|
+
`);
|
|
10788
|
+
} else if (onChunk) {
|
|
10789
|
+
onChunk(`
|
|
10790
|
+
${warnLine}
|
|
10791
|
+
`);
|
|
10792
|
+
}
|
|
10793
|
+
} else {
|
|
10794
|
+
if (hallucinationRetries >= MAX_HALLUCINATION_RETRIES) {
|
|
10795
|
+
logger.warn(`Hallucination retries exhausted (${MAX_HALLUCINATION_RETRIES}), returning error`);
|
|
10796
|
+
return {
|
|
10797
|
+
success: false,
|
|
10798
|
+
text: lastText,
|
|
10799
|
+
error: t("error.response_blocked", {
|
|
10800
|
+
reason: t("hall.max_retries_exhausted")
|
|
10801
|
+
}),
|
|
10802
|
+
iterationCount: iteration
|
|
10803
|
+
};
|
|
10804
|
+
}
|
|
10805
|
+
hallucinationRetries++;
|
|
10806
|
+
logger.warn(`Hallucination retry (${hallucinationRetries}/${MAX_HALLUCINATION_RETRIES}): ${hallucinationResult.reason}`);
|
|
10807
|
+
if (textContent) {
|
|
10808
|
+
contextManager.addMessage({
|
|
10809
|
+
role: "assistant",
|
|
10810
|
+
content: textContent
|
|
10811
|
+
});
|
|
10812
|
+
}
|
|
10789
10813
|
contextManager.addMessage({
|
|
10790
|
-
role: "
|
|
10791
|
-
content:
|
|
10814
|
+
role: "user",
|
|
10815
|
+
content: `<system-summary>[Retry context: ${hallucinationResult.reason}. Original task: "${input}". You must either call a needed tool or provide a substantive response. Empty replies are not allowed.]</system-summary>`
|
|
10792
10816
|
});
|
|
10817
|
+
continue;
|
|
10793
10818
|
}
|
|
10794
|
-
contextManager.addMessage({
|
|
10795
|
-
role: "user",
|
|
10796
|
-
content: `<system-summary>[Retry context: ${hallucinationResult.reason}. Original task: "${input}". You must either call a needed tool or provide a substantive response. Empty replies are not allowed.]</system-summary>`
|
|
10797
|
-
});
|
|
10798
|
-
continue;
|
|
10799
10819
|
}
|
|
10800
10820
|
if (textContent) {
|
|
10801
10821
|
contextManager.addMessage({ role: "assistant", content: textContent });
|
|
@@ -10847,6 +10867,7 @@ ${warnLine}
|
|
|
10847
10867
|
auditFailed = true;
|
|
10848
10868
|
break;
|
|
10849
10869
|
}
|
|
10870
|
+
suppressRepetitionRetry = true;
|
|
10850
10871
|
continue;
|
|
10851
10872
|
}
|
|
10852
10873
|
}
|
|
@@ -11296,13 +11317,18 @@ class ConfidenceCheck {
|
|
|
11296
11317
|
}
|
|
11297
11318
|
validate(response) {
|
|
11298
11319
|
if (!response || response.length < MIN_CHARS) {
|
|
11299
|
-
return {
|
|
11320
|
+
return {
|
|
11321
|
+
status: "retry",
|
|
11322
|
+
kind: "short",
|
|
11323
|
+
reason: t("hall.short_response")
|
|
11324
|
+
};
|
|
11300
11325
|
}
|
|
11301
11326
|
const wordCount = response.split(/\s+/).filter(Boolean).length;
|
|
11302
11327
|
const hasStructure = /```|^\s*[-*]\s|^\s*\d+\.\s|<[^>]+>/m.test(response);
|
|
11303
11328
|
if (wordCount < MIN_WORDS && !hasStructure) {
|
|
11304
11329
|
return {
|
|
11305
11330
|
status: "warn",
|
|
11331
|
+
kind: "short",
|
|
11306
11332
|
reason: t("hall.short_response")
|
|
11307
11333
|
};
|
|
11308
11334
|
}
|
|
@@ -11311,6 +11337,7 @@ class ConfidenceCheck {
|
|
|
11311
11337
|
if (overlap > 0.5) {
|
|
11312
11338
|
return {
|
|
11313
11339
|
status: "retry",
|
|
11340
|
+
kind: "repetition",
|
|
11314
11341
|
reason: t("hall.repetitive", { pct: Math.round(overlap * 100) })
|
|
11315
11342
|
};
|
|
11316
11343
|
}
|
|
@@ -11322,6 +11349,7 @@ class ConfidenceCheck {
|
|
|
11322
11349
|
if (diversity < 0.25) {
|
|
11323
11350
|
return {
|
|
11324
11351
|
status: "warn",
|
|
11352
|
+
kind: "diversity",
|
|
11325
11353
|
reason: t("hall.repetitive", {
|
|
11326
11354
|
pct: Math.round((1 - diversity) * 100)
|
|
11327
11355
|
})
|
|
@@ -11350,7 +11378,25 @@ function isJsMemberAccess(candidate) {
|
|
|
11350
11378
|
const m = lower.match(/^([\w-]+)\./);
|
|
11351
11379
|
return m !== null && JS_GLOBALS.has(m[1]);
|
|
11352
11380
|
}
|
|
11353
|
-
|
|
11381
|
+
function isFilePathLike(s) {
|
|
11382
|
+
const lower = s.toLowerCase();
|
|
11383
|
+
if (TECH_NAMES.has(lower))
|
|
11384
|
+
return false;
|
|
11385
|
+
if (!s.includes("/") && !s.includes("\\") && !/^\w+\.[a-z]{2,4}$/i.test(s))
|
|
11386
|
+
return false;
|
|
11387
|
+
if (isJsMemberAccess(s))
|
|
11388
|
+
return false;
|
|
11389
|
+
const ext = s.split(".").pop()?.toLowerCase() || "";
|
|
11390
|
+
return !NON_FILE_EXTENSIONS.has(ext);
|
|
11391
|
+
}
|
|
11392
|
+
function extractFileLikeTokens(text) {
|
|
11393
|
+
const matches = text.match(FILE_TOKEN_RE) || [];
|
|
11394
|
+
return [...new Set(matches.filter(isFilePathLike))];
|
|
11395
|
+
}
|
|
11396
|
+
function stripUrls(text) {
|
|
11397
|
+
return text.replace(/\bhttps?:\/\/[^\s"'<>]+/gi, " ");
|
|
11398
|
+
}
|
|
11399
|
+
var JS_GLOBALS, NON_FILE_EXTENSIONS, TECH_NAMES, FILE_TOKEN_RE;
|
|
11354
11400
|
var init_js_identifiers = __esm(() => {
|
|
11355
11401
|
JS_GLOBALS = new Set([
|
|
11356
11402
|
"process",
|
|
@@ -11408,22 +11454,46 @@ var init_js_identifiers = __esm(() => {
|
|
|
11408
11454
|
"deno",
|
|
11409
11455
|
"node"
|
|
11410
11456
|
]);
|
|
11457
|
+
NON_FILE_EXTENSIONS = new Set([
|
|
11458
|
+
"com",
|
|
11459
|
+
"org",
|
|
11460
|
+
"net",
|
|
11461
|
+
"gov",
|
|
11462
|
+
"edu",
|
|
11463
|
+
"io",
|
|
11464
|
+
"dev",
|
|
11465
|
+
"app",
|
|
11466
|
+
"co",
|
|
11467
|
+
"ai",
|
|
11468
|
+
"ru",
|
|
11469
|
+
"de",
|
|
11470
|
+
"fr",
|
|
11471
|
+
"uk",
|
|
11472
|
+
"us",
|
|
11473
|
+
"es",
|
|
11474
|
+
"it",
|
|
11475
|
+
"jp",
|
|
11476
|
+
"cn",
|
|
11477
|
+
"br"
|
|
11478
|
+
]);
|
|
11479
|
+
TECH_NAMES = new Set([
|
|
11480
|
+
"node.js",
|
|
11481
|
+
"react.js",
|
|
11482
|
+
"vue.js",
|
|
11483
|
+
"next.js",
|
|
11484
|
+
"angular.js",
|
|
11485
|
+
"express.js",
|
|
11486
|
+
"deno.js",
|
|
11487
|
+
"qwik.js",
|
|
11488
|
+
"svelte.js",
|
|
11489
|
+
"jquery.js"
|
|
11490
|
+
]);
|
|
11491
|
+
FILE_TOKEN_RE = /\b(?:[a-zA-Z]:[\\/])?[\w./\\-]+\.[a-z]{2,6}\b/gi;
|
|
11411
11492
|
});
|
|
11412
11493
|
|
|
11413
11494
|
// src/modules/hallucination/factual.ts
|
|
11414
11495
|
import { existsSync as existsSync22, readdirSync as readdirSync5 } from "fs";
|
|
11415
11496
|
import { resolve as resolve12, isAbsolute as isAbsolute2, join as join13 } from "path";
|
|
11416
|
-
function looksLikeFilePath(s) {
|
|
11417
|
-
const lower = s.toLowerCase();
|
|
11418
|
-
if (TECH_NAMES.has(lower))
|
|
11419
|
-
return false;
|
|
11420
|
-
if (!s.includes("/") && !s.includes("\\") && !s.match(/^\w+\.[a-z]{2,4}$/i))
|
|
11421
|
-
return false;
|
|
11422
|
-
if (isJsMemberAccess(s))
|
|
11423
|
-
return false;
|
|
11424
|
-
const ext = s.split(".").pop()?.toLowerCase() || "";
|
|
11425
|
-
return !NON_FILE_EXTENSIONS.has(ext);
|
|
11426
|
-
}
|
|
11427
11497
|
|
|
11428
11498
|
class FactualCheck {
|
|
11429
11499
|
baseDir;
|
|
@@ -11522,12 +11592,10 @@ class FactualCheck {
|
|
|
11522
11592
|
return count;
|
|
11523
11593
|
}
|
|
11524
11594
|
extractFilePaths(text) {
|
|
11525
|
-
|
|
11526
|
-
const matches = text.match(pattern) || [];
|
|
11527
|
-
return [...new Set(matches)].filter(looksLikeFilePath);
|
|
11595
|
+
return extractFileLikeTokens(stripUrls(text));
|
|
11528
11596
|
}
|
|
11529
11597
|
}
|
|
11530
|
-
var IGNORED_DIRS
|
|
11598
|
+
var IGNORED_DIRS;
|
|
11531
11599
|
var init_factual = __esm(() => {
|
|
11532
11600
|
init_i18n();
|
|
11533
11601
|
init_js_identifiers();
|
|
@@ -11540,40 +11608,6 @@ var init_factual = __esm(() => {
|
|
|
11540
11608
|
".mma",
|
|
11541
11609
|
"_testing"
|
|
11542
11610
|
]);
|
|
11543
|
-
NON_FILE_EXTENSIONS = new Set([
|
|
11544
|
-
"com",
|
|
11545
|
-
"org",
|
|
11546
|
-
"net",
|
|
11547
|
-
"gov",
|
|
11548
|
-
"edu",
|
|
11549
|
-
"io",
|
|
11550
|
-
"dev",
|
|
11551
|
-
"app",
|
|
11552
|
-
"co",
|
|
11553
|
-
"ai",
|
|
11554
|
-
"ru",
|
|
11555
|
-
"de",
|
|
11556
|
-
"fr",
|
|
11557
|
-
"uk",
|
|
11558
|
-
"us",
|
|
11559
|
-
"es",
|
|
11560
|
-
"it",
|
|
11561
|
-
"jp",
|
|
11562
|
-
"cn",
|
|
11563
|
-
"br"
|
|
11564
|
-
]);
|
|
11565
|
-
TECH_NAMES = new Set([
|
|
11566
|
-
"node.js",
|
|
11567
|
-
"react.js",
|
|
11568
|
-
"vue.js",
|
|
11569
|
-
"next.js",
|
|
11570
|
-
"angular.js",
|
|
11571
|
-
"express.js",
|
|
11572
|
-
"deno.js",
|
|
11573
|
-
"qwik.js",
|
|
11574
|
-
"svelte.js",
|
|
11575
|
-
"jquery.js"
|
|
11576
|
-
]);
|
|
11577
11611
|
});
|
|
11578
11612
|
|
|
11579
11613
|
// src/modules/hallucination/llm-judge.ts
|
|
@@ -15480,10 +15514,7 @@ class Auditor {
|
|
|
15480
15514
|
}
|
|
15481
15515
|
async audit(plan) {
|
|
15482
15516
|
const allStepText = plan.steps.map((s) => s.description.replace(/\([^)]*\)/g, " ")).join(" ");
|
|
15483
|
-
const
|
|
15484
|
-
const uniqueFiles = [
|
|
15485
|
-
...new Set(fileMatches.filter((f) => !isJsMemberAccess(f)))
|
|
15486
|
-
];
|
|
15517
|
+
const uniqueFiles = extractFileLikeTokens(stripUrls(allStepText));
|
|
15487
15518
|
const missingFiles = [];
|
|
15488
15519
|
const existingFiles = [];
|
|
15489
15520
|
for (const filePath of uniqueFiles) {
|
|
@@ -16031,11 +16062,15 @@ ${display}`,
|
|
|
16031
16062
|
}
|
|
16032
16063
|
const status = String(args.status || "done");
|
|
16033
16064
|
if (status === "done" && target.status === "done") {
|
|
16065
|
+
const progress2 = this.tracker.getProgressString();
|
|
16066
|
+
const display2 = this.tracker.toPromptBlock();
|
|
16034
16067
|
return {
|
|
16035
|
-
success:
|
|
16036
|
-
output: t("plan.step_already_done", {
|
|
16068
|
+
success: true,
|
|
16069
|
+
output: `${t("plan.step_already_done", {
|
|
16037
16070
|
step: String(stepId)
|
|
16038
|
-
})
|
|
16071
|
+
})}
|
|
16072
|
+
${progress2}`,
|
|
16073
|
+
display: display2
|
|
16039
16074
|
};
|
|
16040
16075
|
}
|
|
16041
16076
|
if (status === "done") {
|
|
@@ -19295,7 +19330,7 @@ async function runSetup(externalRl) {
|
|
|
19295
19330
|
const idx = Math.max(0, Math.min(models.length - 1, (parseInt(choice) || 1) - 1));
|
|
19296
19331
|
model = models[idx];
|
|
19297
19332
|
} else {
|
|
19298
|
-
model = await ask(rl, t("setup.model_name"), "qwen3.5-9b");
|
|
19333
|
+
model = await ask(rl, t("setup.model_name"), "qwen/qwen3.5-9b");
|
|
19299
19334
|
}
|
|
19300
19335
|
const connected = await withSpinner(t("setup.testing_spinner", { model }), () => testChat(apiBase, apiKey, model));
|
|
19301
19336
|
if (connected) {
|
|
@@ -28583,7 +28618,697 @@ init_bootstrap();
|
|
|
28583
28618
|
|
|
28584
28619
|
// src/cli/repl.ts
|
|
28585
28620
|
init_colors();
|
|
28621
|
+
import * as readline3 from "readline";
|
|
28622
|
+
|
|
28623
|
+
// src/ui/line-editor.ts
|
|
28624
|
+
init_string_width();
|
|
28586
28625
|
import * as readline2 from "readline";
|
|
28626
|
+
|
|
28627
|
+
// src/ui/line-math.ts
|
|
28628
|
+
init_string_width();
|
|
28629
|
+
var ANSI_RE = /\x1b\[[0-9;?]*[ -/]*[@-~]/g;
|
|
28630
|
+
function stripAnsi2(s) {
|
|
28631
|
+
return s.replace(ANSI_RE, "");
|
|
28632
|
+
}
|
|
28633
|
+
function charLen(s) {
|
|
28634
|
+
return Array.from(s).length;
|
|
28635
|
+
}
|
|
28636
|
+
function wrapLine(line, width) {
|
|
28637
|
+
const w = width > 0 ? width : 80;
|
|
28638
|
+
const chars = Array.from(line);
|
|
28639
|
+
const rows = [];
|
|
28640
|
+
let start = 0;
|
|
28641
|
+
let cells = 0;
|
|
28642
|
+
for (let i = 0;i < chars.length; i++) {
|
|
28643
|
+
const ch = chars[i];
|
|
28644
|
+
if (ch === `
|
|
28645
|
+
`) {
|
|
28646
|
+
rows.push({ text: chars.slice(start, i).join(""), charStart: start });
|
|
28647
|
+
start = i + 1;
|
|
28648
|
+
cells = 0;
|
|
28649
|
+
continue;
|
|
28650
|
+
}
|
|
28651
|
+
const cw = stringWidth(ch);
|
|
28652
|
+
if (cells + cw > w && cells > 0) {
|
|
28653
|
+
rows.push({ text: chars.slice(start, i).join(""), charStart: start });
|
|
28654
|
+
start = i;
|
|
28655
|
+
cells = cw;
|
|
28656
|
+
} else {
|
|
28657
|
+
cells += cw;
|
|
28658
|
+
}
|
|
28659
|
+
}
|
|
28660
|
+
rows.push({ text: chars.slice(start).join(""), charStart: start });
|
|
28661
|
+
return rows;
|
|
28662
|
+
}
|
|
28663
|
+
function buildLayout(lines, width) {
|
|
28664
|
+
const rows = [];
|
|
28665
|
+
for (let b = 0;b < lines.length; b++) {
|
|
28666
|
+
for (const r of wrapLine(lines[b], width)) {
|
|
28667
|
+
rows.push({ bufRow: b, charStart: r.charStart, text: r.text });
|
|
28668
|
+
}
|
|
28669
|
+
}
|
|
28670
|
+
return rows;
|
|
28671
|
+
}
|
|
28672
|
+
function layoutIndexFor(rows, bufRow, col) {
|
|
28673
|
+
let idx = 0;
|
|
28674
|
+
for (let i = 0;i < rows.length; i++) {
|
|
28675
|
+
if (rows[i].bufRow === bufRow && rows[i].charStart <= col)
|
|
28676
|
+
idx = i;
|
|
28677
|
+
}
|
|
28678
|
+
return idx;
|
|
28679
|
+
}
|
|
28680
|
+
function visualColAt(rows, idx, col) {
|
|
28681
|
+
const r = rows[idx];
|
|
28682
|
+
const upTo = col - r.charStart;
|
|
28683
|
+
return stringWidth(Array.from(r.text).slice(0, upTo).join(""));
|
|
28684
|
+
}
|
|
28685
|
+
function charOffsetFor(rows, idx, visualCol) {
|
|
28686
|
+
const r = rows[idx];
|
|
28687
|
+
const chars = Array.from(r.text);
|
|
28688
|
+
let cells = 0;
|
|
28689
|
+
for (let i = 0;i < chars.length; i++) {
|
|
28690
|
+
const cw = stringWidth(chars[i]);
|
|
28691
|
+
if (cells + cw > visualCol)
|
|
28692
|
+
return r.charStart + i;
|
|
28693
|
+
cells += cw;
|
|
28694
|
+
}
|
|
28695
|
+
return r.charStart + chars.length;
|
|
28696
|
+
}
|
|
28697
|
+
|
|
28698
|
+
// src/ui/line-editor.ts
|
|
28699
|
+
class LineEditor {
|
|
28700
|
+
input;
|
|
28701
|
+
output;
|
|
28702
|
+
onKeyInput;
|
|
28703
|
+
promptStr;
|
|
28704
|
+
completer;
|
|
28705
|
+
history;
|
|
28706
|
+
historySize;
|
|
28707
|
+
lines = [""];
|
|
28708
|
+
row = 0;
|
|
28709
|
+
col = 0;
|
|
28710
|
+
historyIndex = -1;
|
|
28711
|
+
stash = null;
|
|
28712
|
+
isDone = false;
|
|
28713
|
+
isPaste = false;
|
|
28714
|
+
lastWasCR = false;
|
|
28715
|
+
questionCb = null;
|
|
28716
|
+
rawMode = false;
|
|
28717
|
+
listeners = {};
|
|
28718
|
+
constructor(opts) {
|
|
28719
|
+
this.input = opts.input;
|
|
28720
|
+
this.output = opts.output;
|
|
28721
|
+
this.promptStr = opts.prompt ?? "";
|
|
28722
|
+
this.completer = opts.completer;
|
|
28723
|
+
this.history = opts.history ?? [];
|
|
28724
|
+
this.historySize = opts.historySize ?? 50;
|
|
28725
|
+
if (this.history.length > this.historySize) {
|
|
28726
|
+
this.history = this.history.slice(-this.historySize);
|
|
28727
|
+
}
|
|
28728
|
+
if (this.input.isTTY) {
|
|
28729
|
+
this.rawMode = typeof this.input.setRawMode === "function";
|
|
28730
|
+
if (this.rawMode)
|
|
28731
|
+
this.input.setRawMode(true);
|
|
28732
|
+
this.enableTerminalProtocols();
|
|
28733
|
+
readline2.emitKeypressEvents(this.input);
|
|
28734
|
+
this.input.on("keypress", (str, key) => this.onKey(str, key));
|
|
28735
|
+
} else {
|
|
28736
|
+
this.input.on("data", (buf) => this.onData(buf));
|
|
28737
|
+
this.input.on("end", () => {
|
|
28738
|
+
const last = this.lines.join(`
|
|
28739
|
+
`);
|
|
28740
|
+
this.lines = [""];
|
|
28741
|
+
this.row = 0;
|
|
28742
|
+
this.col = 0;
|
|
28743
|
+
if (last)
|
|
28744
|
+
this.emitLine(last);
|
|
28745
|
+
this.emitClose();
|
|
28746
|
+
});
|
|
28747
|
+
}
|
|
28748
|
+
}
|
|
28749
|
+
on(event, listener) {
|
|
28750
|
+
if (event === "line") {
|
|
28751
|
+
this.listeners.line = listener;
|
|
28752
|
+
} else {
|
|
28753
|
+
this.listeners.close = listener;
|
|
28754
|
+
}
|
|
28755
|
+
return this;
|
|
28756
|
+
}
|
|
28757
|
+
setPrompt(prompt) {
|
|
28758
|
+
this.promptStr = prompt;
|
|
28759
|
+
if (this.input.isTTY)
|
|
28760
|
+
this.render();
|
|
28761
|
+
}
|
|
28762
|
+
prompt(_preserveCursor) {
|
|
28763
|
+
if (this.input.isTTY)
|
|
28764
|
+
this.render();
|
|
28765
|
+
}
|
|
28766
|
+
write(s) {
|
|
28767
|
+
this.insertText(s);
|
|
28768
|
+
}
|
|
28769
|
+
reset() {
|
|
28770
|
+
this.lines = [""];
|
|
28771
|
+
this.row = 0;
|
|
28772
|
+
this.col = 0;
|
|
28773
|
+
this.historyIndex = -1;
|
|
28774
|
+
this.stash = null;
|
|
28775
|
+
if (this.input.isTTY)
|
|
28776
|
+
this.render();
|
|
28777
|
+
}
|
|
28778
|
+
question(query, cb) {
|
|
28779
|
+
this.questionCb = cb;
|
|
28780
|
+
this.promptStr = query;
|
|
28781
|
+
this.lines = [""];
|
|
28782
|
+
this.row = 0;
|
|
28783
|
+
this.col = 0;
|
|
28784
|
+
if (this.input.isTTY)
|
|
28785
|
+
this.render();
|
|
28786
|
+
}
|
|
28787
|
+
close() {
|
|
28788
|
+
if (this.isDone)
|
|
28789
|
+
return;
|
|
28790
|
+
this.isDone = true;
|
|
28791
|
+
this.disableTerminalProtocols();
|
|
28792
|
+
if (this.rawMode && this.input.setRawMode) {
|
|
28793
|
+
this.input.setRawMode(false);
|
|
28794
|
+
}
|
|
28795
|
+
this.emitClose();
|
|
28796
|
+
}
|
|
28797
|
+
onData(buf) {
|
|
28798
|
+
if (this.isDone)
|
|
28799
|
+
return;
|
|
28800
|
+
let text = buf.toString("utf-8");
|
|
28801
|
+
text = text.replace(/\r\n/g, `
|
|
28802
|
+
`).replace(/\r/g, `
|
|
28803
|
+
`);
|
|
28804
|
+
const parts = text.split(`
|
|
28805
|
+
`);
|
|
28806
|
+
for (let i = 0;i < parts.length; i++) {
|
|
28807
|
+
if (i < parts.length - 1) {
|
|
28808
|
+
this.emitLine(parts[i]);
|
|
28809
|
+
this.lines = [""];
|
|
28810
|
+
this.row = 0;
|
|
28811
|
+
this.col = 0;
|
|
28812
|
+
} else if (parts[i]) {
|
|
28813
|
+
this.emitLine(parts[i]);
|
|
28814
|
+
this.lines = [""];
|
|
28815
|
+
this.row = 0;
|
|
28816
|
+
this.col = 0;
|
|
28817
|
+
}
|
|
28818
|
+
}
|
|
28819
|
+
}
|
|
28820
|
+
onKey(str, key) {
|
|
28821
|
+
if (this.isDone)
|
|
28822
|
+
return;
|
|
28823
|
+
if (this.onKeyInput && this.onKeyInput(str, key))
|
|
28824
|
+
return;
|
|
28825
|
+
const k = key;
|
|
28826
|
+
const name = k?.name ?? "undefined";
|
|
28827
|
+
const seq2 = k?.sequence ?? str ?? "";
|
|
28828
|
+
if (name === "undefined" && /^\x1b\[\d+(?:;\d+)?u$/.test(seq2)) {
|
|
28829
|
+
const m = seq2.match(/^\x1b\[(\d+)(?:;(\d+))?u$/);
|
|
28830
|
+
if (m && Number(m[1]) === 13 && Number(m[2] ?? 0) !== 0) {
|
|
28831
|
+
this.insertNewline();
|
|
28832
|
+
}
|
|
28833
|
+
return;
|
|
28834
|
+
}
|
|
28835
|
+
if (name === "paste-start" || seq2 === "\x1B[200~") {
|
|
28836
|
+
this.isPaste = true;
|
|
28837
|
+
return;
|
|
28838
|
+
}
|
|
28839
|
+
if (name === "paste-end" || seq2 === "\x1B[201~") {
|
|
28840
|
+
this.isPaste = false;
|
|
28841
|
+
this.render();
|
|
28842
|
+
return;
|
|
28843
|
+
}
|
|
28844
|
+
if (this.isPaste) {
|
|
28845
|
+
if (name === "return" || seq2 === "\r" || seq2 === `
|
|
28846
|
+
`) {
|
|
28847
|
+
this.insertText(`
|
|
28848
|
+
`);
|
|
28849
|
+
} else if (str) {
|
|
28850
|
+
this.insertText(str);
|
|
28851
|
+
}
|
|
28852
|
+
this.render();
|
|
28853
|
+
return;
|
|
28854
|
+
}
|
|
28855
|
+
if (name === "return" && (seq2 === "\x1B\r" || k.meta)) {
|
|
28856
|
+
this.insertNewline();
|
|
28857
|
+
return;
|
|
28858
|
+
}
|
|
28859
|
+
if (name === "undefined" && seq2 === "\x1B\r") {
|
|
28860
|
+
this.insertNewline();
|
|
28861
|
+
return;
|
|
28862
|
+
}
|
|
28863
|
+
if (k?.ctrl && name === "j") {
|
|
28864
|
+
this.insertNewline();
|
|
28865
|
+
return;
|
|
28866
|
+
}
|
|
28867
|
+
if (name === "escape")
|
|
28868
|
+
return;
|
|
28869
|
+
switch (name) {
|
|
28870
|
+
case "return":
|
|
28871
|
+
this.lastWasCR = true;
|
|
28872
|
+
this.submit();
|
|
28873
|
+
return;
|
|
28874
|
+
case "enter":
|
|
28875
|
+
if (this.lastWasCR) {
|
|
28876
|
+
this.lastWasCR = false;
|
|
28877
|
+
return;
|
|
28878
|
+
}
|
|
28879
|
+
this.insertNewline();
|
|
28880
|
+
return;
|
|
28881
|
+
case "backspace":
|
|
28882
|
+
this.lastWasCR = false;
|
|
28883
|
+
this.backspace();
|
|
28884
|
+
return;
|
|
28885
|
+
case "delete":
|
|
28886
|
+
this.lastWasCR = false;
|
|
28887
|
+
this.deleteForward();
|
|
28888
|
+
return;
|
|
28889
|
+
case "left":
|
|
28890
|
+
this.lastWasCR = false;
|
|
28891
|
+
this.moveLeft();
|
|
28892
|
+
return;
|
|
28893
|
+
case "right":
|
|
28894
|
+
this.lastWasCR = false;
|
|
28895
|
+
this.moveRight();
|
|
28896
|
+
return;
|
|
28897
|
+
case "up":
|
|
28898
|
+
this.lastWasCR = false;
|
|
28899
|
+
this.moveUp();
|
|
28900
|
+
return;
|
|
28901
|
+
case "down":
|
|
28902
|
+
this.lastWasCR = false;
|
|
28903
|
+
this.moveDown();
|
|
28904
|
+
return;
|
|
28905
|
+
case "home":
|
|
28906
|
+
this.lastWasCR = false;
|
|
28907
|
+
this.moveHome();
|
|
28908
|
+
return;
|
|
28909
|
+
case "end":
|
|
28910
|
+
this.lastWasCR = false;
|
|
28911
|
+
this.moveEnd();
|
|
28912
|
+
return;
|
|
28913
|
+
case "pageup":
|
|
28914
|
+
this.lastWasCR = false;
|
|
28915
|
+
this.moveToStart();
|
|
28916
|
+
return;
|
|
28917
|
+
case "pagedown":
|
|
28918
|
+
this.lastWasCR = false;
|
|
28919
|
+
this.moveToEnd();
|
|
28920
|
+
return;
|
|
28921
|
+
case "tab":
|
|
28922
|
+
this.lastWasCR = false;
|
|
28923
|
+
this.tabComplete();
|
|
28924
|
+
return;
|
|
28925
|
+
}
|
|
28926
|
+
if (k?.ctrl) {
|
|
28927
|
+
this.lastWasCR = false;
|
|
28928
|
+
switch (name) {
|
|
28929
|
+
case "c":
|
|
28930
|
+
this.ctrlC();
|
|
28931
|
+
return;
|
|
28932
|
+
case "d":
|
|
28933
|
+
this.ctrlD();
|
|
28934
|
+
return;
|
|
28935
|
+
case "a":
|
|
28936
|
+
this.moveHome();
|
|
28937
|
+
return;
|
|
28938
|
+
case "e":
|
|
28939
|
+
this.moveEnd();
|
|
28940
|
+
return;
|
|
28941
|
+
case "u":
|
|
28942
|
+
this.killToStart();
|
|
28943
|
+
return;
|
|
28944
|
+
case "k":
|
|
28945
|
+
this.killToEnd();
|
|
28946
|
+
return;
|
|
28947
|
+
case "w":
|
|
28948
|
+
this.killWordBefore();
|
|
28949
|
+
return;
|
|
28950
|
+
case "l":
|
|
28951
|
+
this.clearScreen();
|
|
28952
|
+
return;
|
|
28953
|
+
default:
|
|
28954
|
+
return;
|
|
28955
|
+
}
|
|
28956
|
+
}
|
|
28957
|
+
if (k?.meta) {
|
|
28958
|
+
this.lastWasCR = false;
|
|
28959
|
+
switch (name) {
|
|
28960
|
+
case "b":
|
|
28961
|
+
this.moveWordLeft();
|
|
28962
|
+
return;
|
|
28963
|
+
case "f":
|
|
28964
|
+
this.moveWordRight();
|
|
28965
|
+
return;
|
|
28966
|
+
case "d":
|
|
28967
|
+
this.killWordAfter();
|
|
28968
|
+
return;
|
|
28969
|
+
default:
|
|
28970
|
+
return;
|
|
28971
|
+
}
|
|
28972
|
+
}
|
|
28973
|
+
if (str && !/^[\u0000-\u001f\u007f]/.test(str)) {
|
|
28974
|
+
this.lastWasCR = false;
|
|
28975
|
+
this.insertText(str);
|
|
28976
|
+
}
|
|
28977
|
+
}
|
|
28978
|
+
submit() {
|
|
28979
|
+
if (this.questionCb) {
|
|
28980
|
+
const cb = this.questionCb;
|
|
28981
|
+
this.questionCb = null;
|
|
28982
|
+
const answer = this.lines.join(`
|
|
28983
|
+
`).trim();
|
|
28984
|
+
this.lines = [""];
|
|
28985
|
+
this.row = 0;
|
|
28986
|
+
this.col = 0;
|
|
28987
|
+
this.render();
|
|
28988
|
+
cb(answer);
|
|
28989
|
+
return;
|
|
28990
|
+
}
|
|
28991
|
+
const text = this.lines.join(`
|
|
28992
|
+
`);
|
|
28993
|
+
if (text.trim()) {
|
|
28994
|
+
this.history.push(text);
|
|
28995
|
+
if (this.history.length > this.historySize) {
|
|
28996
|
+
this.history = this.history.slice(-this.historySize);
|
|
28997
|
+
}
|
|
28998
|
+
}
|
|
28999
|
+
this.historyIndex = -1;
|
|
29000
|
+
this.stash = null;
|
|
29001
|
+
this.lines = [""];
|
|
29002
|
+
this.row = 0;
|
|
29003
|
+
this.col = 0;
|
|
29004
|
+
this.render();
|
|
29005
|
+
this.emitLine(text);
|
|
29006
|
+
}
|
|
29007
|
+
ctrlC() {
|
|
29008
|
+
this.lines = [""];
|
|
29009
|
+
this.row = 0;
|
|
29010
|
+
this.col = 0;
|
|
29011
|
+
this.render();
|
|
29012
|
+
process.emit("SIGINT");
|
|
29013
|
+
}
|
|
29014
|
+
ctrlD() {
|
|
29015
|
+
if (this.lines.length === 1 && this.lines[0] === "") {
|
|
29016
|
+
this.emitClose();
|
|
29017
|
+
} else {
|
|
29018
|
+
this.deleteForward();
|
|
29019
|
+
}
|
|
29020
|
+
}
|
|
29021
|
+
currentChars() {
|
|
29022
|
+
return Array.from(this.lines[this.row]);
|
|
29023
|
+
}
|
|
29024
|
+
setCurrentChars(chars) {
|
|
29025
|
+
this.lines[this.row] = chars.join("");
|
|
29026
|
+
}
|
|
29027
|
+
insertText(text) {
|
|
29028
|
+
const sanitized = text.replace(/\r\n/g, `
|
|
29029
|
+
`).replace(/\r/g, "");
|
|
29030
|
+
const chars = this.currentChars();
|
|
29031
|
+
const t2 = Array.from(sanitized);
|
|
29032
|
+
chars.splice(this.col, 0, ...t2);
|
|
29033
|
+
this.setCurrentChars(chars);
|
|
29034
|
+
this.col += t2.length;
|
|
29035
|
+
this.render();
|
|
29036
|
+
}
|
|
29037
|
+
insertNewline() {
|
|
29038
|
+
const chars = this.currentChars();
|
|
29039
|
+
const rest = chars.slice(this.col).join("");
|
|
29040
|
+
this.setCurrentChars(chars.slice(0, this.col));
|
|
29041
|
+
this.lines.splice(this.row + 1, 0, rest);
|
|
29042
|
+
this.row++;
|
|
29043
|
+
this.col = 0;
|
|
29044
|
+
this.render();
|
|
29045
|
+
}
|
|
29046
|
+
backspace() {
|
|
29047
|
+
const chars = this.currentChars();
|
|
29048
|
+
if (this.col > 0) {
|
|
29049
|
+
chars.splice(this.col - 1, 1);
|
|
29050
|
+
this.setCurrentChars(chars);
|
|
29051
|
+
this.col--;
|
|
29052
|
+
} else if (this.row > 0) {
|
|
29053
|
+
const prevLen = charLen(this.lines[this.row - 1]);
|
|
29054
|
+
this.lines[this.row - 1] = this.lines[this.row - 1] + this.lines[this.row];
|
|
29055
|
+
this.lines.splice(this.row, 1);
|
|
29056
|
+
this.row--;
|
|
29057
|
+
this.col = prevLen;
|
|
29058
|
+
}
|
|
29059
|
+
this.render();
|
|
29060
|
+
}
|
|
29061
|
+
deleteForward() {
|
|
29062
|
+
const chars = this.currentChars();
|
|
29063
|
+
if (this.col < chars.length) {
|
|
29064
|
+
chars.splice(this.col, 1);
|
|
29065
|
+
this.setCurrentChars(chars);
|
|
29066
|
+
} else if (this.row < this.lines.length - 1) {
|
|
29067
|
+
this.lines[this.row] = this.lines[this.row] + this.lines[this.row + 1];
|
|
29068
|
+
this.lines.splice(this.row + 1, 1);
|
|
29069
|
+
}
|
|
29070
|
+
this.render();
|
|
29071
|
+
}
|
|
29072
|
+
killToStart() {
|
|
29073
|
+
const chars = this.currentChars();
|
|
29074
|
+
this.setCurrentChars(chars.slice(this.col));
|
|
29075
|
+
this.col = 0;
|
|
29076
|
+
this.render();
|
|
29077
|
+
}
|
|
29078
|
+
killToEnd() {
|
|
29079
|
+
const chars = this.currentChars();
|
|
29080
|
+
this.setCurrentChars(chars.slice(0, this.col));
|
|
29081
|
+
this.render();
|
|
29082
|
+
}
|
|
29083
|
+
killWordBefore() {
|
|
29084
|
+
const chars = this.currentChars();
|
|
29085
|
+
let i = this.col;
|
|
29086
|
+
while (i > 0 && /\s/.test(chars[i - 1]))
|
|
29087
|
+
i--;
|
|
29088
|
+
while (i > 0 && !/\s/.test(chars[i - 1]))
|
|
29089
|
+
i--;
|
|
29090
|
+
chars.splice(i, this.col - i);
|
|
29091
|
+
this.setCurrentChars(chars);
|
|
29092
|
+
this.col = i;
|
|
29093
|
+
this.render();
|
|
29094
|
+
}
|
|
29095
|
+
killWordAfter() {
|
|
29096
|
+
const chars = this.currentChars();
|
|
29097
|
+
const n = chars.length;
|
|
29098
|
+
let i = this.col;
|
|
29099
|
+
while (i < n && /\s/.test(chars[i]))
|
|
29100
|
+
i++;
|
|
29101
|
+
while (i < n && !/\s/.test(chars[i]))
|
|
29102
|
+
i++;
|
|
29103
|
+
chars.splice(this.col, i - this.col);
|
|
29104
|
+
this.setCurrentChars(chars);
|
|
29105
|
+
this.render();
|
|
29106
|
+
}
|
|
29107
|
+
moveLeft() {
|
|
29108
|
+
if (this.col > 0) {
|
|
29109
|
+
this.col--;
|
|
29110
|
+
} else if (this.row > 0) {
|
|
29111
|
+
this.row--;
|
|
29112
|
+
this.col = charLen(this.lines[this.row]);
|
|
29113
|
+
}
|
|
29114
|
+
this.render();
|
|
29115
|
+
}
|
|
29116
|
+
moveRight() {
|
|
29117
|
+
if (this.col < charLen(this.lines[this.row])) {
|
|
29118
|
+
this.col++;
|
|
29119
|
+
} else if (this.row < this.lines.length - 1) {
|
|
29120
|
+
this.row++;
|
|
29121
|
+
this.col = 0;
|
|
29122
|
+
}
|
|
29123
|
+
this.render();
|
|
29124
|
+
}
|
|
29125
|
+
moveUp() {
|
|
29126
|
+
const layout = this.layout();
|
|
29127
|
+
const idx = layoutIndexFor(layout, this.row, this.col);
|
|
29128
|
+
const vcol = visualColAt(layout, idx, this.col);
|
|
29129
|
+
if (idx > 0) {
|
|
29130
|
+
const target = layout[idx - 1];
|
|
29131
|
+
this.row = target.bufRow;
|
|
29132
|
+
this.col = charOffsetFor(layout, idx - 1, vcol);
|
|
29133
|
+
this.render();
|
|
29134
|
+
} else {
|
|
29135
|
+
this.historyPrev();
|
|
29136
|
+
}
|
|
29137
|
+
}
|
|
29138
|
+
moveDown() {
|
|
29139
|
+
const layout = this.layout();
|
|
29140
|
+
const idx = layoutIndexFor(layout, this.row, this.col);
|
|
29141
|
+
const vcol = visualColAt(layout, idx, this.col);
|
|
29142
|
+
if (idx < layout.length - 1) {
|
|
29143
|
+
const target = layout[idx + 1];
|
|
29144
|
+
this.row = target.bufRow;
|
|
29145
|
+
this.col = charOffsetFor(layout, idx + 1, vcol);
|
|
29146
|
+
this.render();
|
|
29147
|
+
} else {
|
|
29148
|
+
this.historyNext();
|
|
29149
|
+
}
|
|
29150
|
+
}
|
|
29151
|
+
moveHome() {
|
|
29152
|
+
this.col = 0;
|
|
29153
|
+
this.render();
|
|
29154
|
+
}
|
|
29155
|
+
moveEnd() {
|
|
29156
|
+
this.col = charLen(this.lines[this.row]);
|
|
29157
|
+
this.render();
|
|
29158
|
+
}
|
|
29159
|
+
moveToStart() {
|
|
29160
|
+
this.row = 0;
|
|
29161
|
+
this.col = 0;
|
|
29162
|
+
this.render();
|
|
29163
|
+
}
|
|
29164
|
+
moveToEnd() {
|
|
29165
|
+
this.row = this.lines.length - 1;
|
|
29166
|
+
this.col = charLen(this.lines[this.row]);
|
|
29167
|
+
this.render();
|
|
29168
|
+
}
|
|
29169
|
+
moveWordLeft() {
|
|
29170
|
+
const chars = this.currentChars();
|
|
29171
|
+
let i = this.col;
|
|
29172
|
+
while (i > 0 && /\s/.test(chars[i - 1]))
|
|
29173
|
+
i--;
|
|
29174
|
+
while (i > 0 && !/\s/.test(chars[i - 1]))
|
|
29175
|
+
i--;
|
|
29176
|
+
this.col = i;
|
|
29177
|
+
this.render();
|
|
29178
|
+
}
|
|
29179
|
+
moveWordRight() {
|
|
29180
|
+
const chars = this.currentChars();
|
|
29181
|
+
const n = chars.length;
|
|
29182
|
+
let i = this.col;
|
|
29183
|
+
while (i < n && !/\s/.test(chars[i]))
|
|
29184
|
+
i++;
|
|
29185
|
+
while (i < n && /\s/.test(chars[i]))
|
|
29186
|
+
i++;
|
|
29187
|
+
this.col = i;
|
|
29188
|
+
this.render();
|
|
29189
|
+
}
|
|
29190
|
+
historyPrev() {
|
|
29191
|
+
if (this.history.length === 0)
|
|
29192
|
+
return;
|
|
29193
|
+
if (this.historyIndex === -1) {
|
|
29194
|
+
this.stash = {
|
|
29195
|
+
lines: this.lines.slice(),
|
|
29196
|
+
row: this.row,
|
|
29197
|
+
col: this.col
|
|
29198
|
+
};
|
|
29199
|
+
this.historyIndex = 0;
|
|
29200
|
+
} else if (this.historyIndex < this.history.length - 1) {
|
|
29201
|
+
this.historyIndex++;
|
|
29202
|
+
} else {
|
|
29203
|
+
return;
|
|
29204
|
+
}
|
|
29205
|
+
this.applyHistoryEntry();
|
|
29206
|
+
}
|
|
29207
|
+
historyNext() {
|
|
29208
|
+
if (this.historyIndex === -1)
|
|
29209
|
+
return;
|
|
29210
|
+
if (this.historyIndex > 0) {
|
|
29211
|
+
this.historyIndex--;
|
|
29212
|
+
this.applyHistoryEntry();
|
|
29213
|
+
} else if (this.stash) {
|
|
29214
|
+
this.lines = this.stash.lines.slice();
|
|
29215
|
+
this.row = this.stash.row;
|
|
29216
|
+
this.col = this.stash.col;
|
|
29217
|
+
this.historyIndex = -1;
|
|
29218
|
+
this.stash = null;
|
|
29219
|
+
this.render();
|
|
29220
|
+
}
|
|
29221
|
+
}
|
|
29222
|
+
applyHistoryEntry() {
|
|
29223
|
+
const entry = this.history[this.history.length - 1 - this.historyIndex];
|
|
29224
|
+
this.lines = entry.split(`
|
|
29225
|
+
`);
|
|
29226
|
+
if (this.lines.length === 0)
|
|
29227
|
+
this.lines = [""];
|
|
29228
|
+
this.row = this.lines.length - 1;
|
|
29229
|
+
this.col = charLen(this.lines[this.row]);
|
|
29230
|
+
this.render();
|
|
29231
|
+
}
|
|
29232
|
+
tabComplete() {
|
|
29233
|
+
if (!this.completer)
|
|
29234
|
+
return;
|
|
29235
|
+
const line = this.lines[this.row];
|
|
29236
|
+
const [matches, partial] = this.completer(line);
|
|
29237
|
+
if (matches.length === 0) {
|
|
29238
|
+
this.output.write("\x07");
|
|
29239
|
+
return;
|
|
29240
|
+
}
|
|
29241
|
+
if (matches.length === 1) {
|
|
29242
|
+
const from = line.lastIndexOf(partial);
|
|
29243
|
+
const head = line.slice(0, from);
|
|
29244
|
+
const tail = line.slice(from + partial.length);
|
|
29245
|
+
this.lines[this.row] = head + matches[0] + tail;
|
|
29246
|
+
this.col = charLen(head) + charLen(matches[0]);
|
|
29247
|
+
this.render();
|
|
29248
|
+
return;
|
|
29249
|
+
}
|
|
29250
|
+
const lines = matches.map((m) => " " + m);
|
|
29251
|
+
this.output.write(`\r
|
|
29252
|
+
` + lines.join(`\r
|
|
29253
|
+
`) + `\r
|
|
29254
|
+
`);
|
|
29255
|
+
this.output.write(`\x1B[${lines.length + 1}A`);
|
|
29256
|
+
this.render();
|
|
29257
|
+
}
|
|
29258
|
+
clearScreen() {
|
|
29259
|
+
this.output.write("\x1B[2J\x1B[H");
|
|
29260
|
+
this.render();
|
|
29261
|
+
}
|
|
29262
|
+
columns() {
|
|
29263
|
+
return this.output.columns || 80;
|
|
29264
|
+
}
|
|
29265
|
+
layout() {
|
|
29266
|
+
return buildLayout(this.lines, this.columns());
|
|
29267
|
+
}
|
|
29268
|
+
render() {
|
|
29269
|
+
if (!this.input.isTTY || this.isDone)
|
|
29270
|
+
return;
|
|
29271
|
+
const layout = this.layout();
|
|
29272
|
+
const promptWidth = stringWidth(stripAnsi2(this.promptStr));
|
|
29273
|
+
const idx = layoutIndexFor(layout, this.row, this.col);
|
|
29274
|
+
const vcol = visualColAt(layout, idx, this.col);
|
|
29275
|
+
const rowsAfter = layout.length - idx - 1;
|
|
29276
|
+
const ccol = (idx === 0 ? promptWidth : 0) + vcol;
|
|
29277
|
+
const out = ["\r"];
|
|
29278
|
+
for (let i = 0;i < layout.length; i++) {
|
|
29279
|
+
out.push("\x1B[2K");
|
|
29280
|
+
if (i === 0)
|
|
29281
|
+
out.push(this.promptStr);
|
|
29282
|
+
out.push(layout[i].text);
|
|
29283
|
+
if (i < layout.length - 1)
|
|
29284
|
+
out.push(`\r
|
|
29285
|
+
`);
|
|
29286
|
+
}
|
|
29287
|
+
out.push("\x1B[J");
|
|
29288
|
+
if (rowsAfter > 0)
|
|
29289
|
+
out.push(`\x1B[${rowsAfter}A`);
|
|
29290
|
+
out.push("\r");
|
|
29291
|
+
if (ccol > 0)
|
|
29292
|
+
out.push(`\x1B[${ccol}C`);
|
|
29293
|
+
this.output.write(out.join(""));
|
|
29294
|
+
}
|
|
29295
|
+
enableTerminalProtocols() {
|
|
29296
|
+
this.output.write("\x1B[?2004h");
|
|
29297
|
+
this.output.write("\x1B[>1u");
|
|
29298
|
+
}
|
|
29299
|
+
disableTerminalProtocols() {
|
|
29300
|
+
this.output.write("\x1B[?2004l");
|
|
29301
|
+
this.output.write("\x1B[<u");
|
|
29302
|
+
}
|
|
29303
|
+
emitLine(line) {
|
|
29304
|
+
this.listeners.line?.(line);
|
|
29305
|
+
}
|
|
29306
|
+
emitClose() {
|
|
29307
|
+
this.listeners.close?.();
|
|
29308
|
+
}
|
|
29309
|
+
}
|
|
29310
|
+
|
|
29311
|
+
// src/cli/repl.ts
|
|
28587
29312
|
import { existsSync as existsSync45, readFileSync as readFileSync31, writeFileSync as writeFileSync16 } from "fs";
|
|
28588
29313
|
import { join as join40, dirname as dirname13 } from "path";
|
|
28589
29314
|
import { homedir as homedir17 } from "os";
|
|
@@ -28885,14 +29610,6 @@ class FormattingStream {
|
|
|
28885
29610
|
return result;
|
|
28886
29611
|
}
|
|
28887
29612
|
}
|
|
28888
|
-
function formatMarkdown(text) {
|
|
28889
|
-
const lines = [];
|
|
28890
|
-
const stream = new FormattingStream((l) => lines.push(l));
|
|
28891
|
-
stream.write(text);
|
|
28892
|
-
stream.flush();
|
|
28893
|
-
return lines.join(`
|
|
28894
|
-
`);
|
|
28895
|
-
}
|
|
28896
29613
|
|
|
28897
29614
|
// src/ui/renderer.ts
|
|
28898
29615
|
init_spinner();
|
|
@@ -29145,6 +29862,7 @@ class Repl {
|
|
|
29145
29862
|
baseDir;
|
|
29146
29863
|
noAgentsMd;
|
|
29147
29864
|
pendingClipboardImage = null;
|
|
29865
|
+
exitOnClose = false;
|
|
29148
29866
|
rl;
|
|
29149
29867
|
agent;
|
|
29150
29868
|
config;
|
|
@@ -29152,9 +29870,10 @@ class Repl {
|
|
|
29152
29870
|
skillsModule;
|
|
29153
29871
|
pluginManager;
|
|
29154
29872
|
logger;
|
|
29155
|
-
constructor(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd, logger) {
|
|
29873
|
+
constructor(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd, logger, exitOnClose) {
|
|
29156
29874
|
this.agent = agent;
|
|
29157
29875
|
this.config = config;
|
|
29876
|
+
this.exitOnClose = exitOnClose === true;
|
|
29158
29877
|
this.sessionManager = sessionManager;
|
|
29159
29878
|
this.skillsModule = skillsModule;
|
|
29160
29879
|
this.pluginManager = pluginManager;
|
|
@@ -29164,7 +29883,19 @@ class Repl {
|
|
|
29164
29883
|
this.noAgentsMd = noAgentsMd === true;
|
|
29165
29884
|
this.historyPath = join40(homedir17(), ".mma", "repl-history");
|
|
29166
29885
|
this.loadHistory();
|
|
29167
|
-
this.rl =
|
|
29886
|
+
this.rl = process.stdin.isTTY ? new LineEditor({
|
|
29887
|
+
input: process.stdin,
|
|
29888
|
+
output: process.stdout,
|
|
29889
|
+
prompt: pc2.cyan("> "),
|
|
29890
|
+
history: this.history,
|
|
29891
|
+
historySize: this.maxHistory,
|
|
29892
|
+
completer: (line) => {
|
|
29893
|
+
const [matches, partial] = this.completer.complete(line);
|
|
29894
|
+
if (matches.length > 0)
|
|
29895
|
+
return [matches, partial];
|
|
29896
|
+
return [[], line];
|
|
29897
|
+
}
|
|
29898
|
+
}) : readline3.createInterface({
|
|
29168
29899
|
input: process.stdin,
|
|
29169
29900
|
output: process.stdout,
|
|
29170
29901
|
prompt: pc2.cyan("> "),
|
|
@@ -29281,46 +30012,22 @@ class Repl {
|
|
|
29281
30012
|
});
|
|
29282
30013
|
this.rl.on("close", () => {
|
|
29283
30014
|
this.running = false;
|
|
30015
|
+
this.saveHistory();
|
|
30016
|
+
this.agent.shutdown();
|
|
30017
|
+
if (this.exitOnClose)
|
|
30018
|
+
process.exit(0);
|
|
29284
30019
|
});
|
|
29285
|
-
if (process.stdin.isTTY) {
|
|
29286
|
-
|
|
29287
|
-
|
|
29288
|
-
|
|
29289
|
-
|
|
29290
|
-
|
|
29291
|
-
const withinWindow = now - this.lastEscTime < this.doubleEscDelay;
|
|
29292
|
-
this.lastEscTime = now;
|
|
29293
|
-
if (escBytes >= 2 || withinWindow) {
|
|
29294
|
-
this.lastEscTime = 0;
|
|
29295
|
-
if (this.agentRunning) {
|
|
29296
|
-
process.stdout.write(pc2.yellow(`
|
|
29297
|
-
${t("repl.interrupt")}
|
|
29298
|
-
`));
|
|
29299
|
-
this.agent.shutdown();
|
|
29300
|
-
}
|
|
29301
|
-
}
|
|
29302
|
-
return;
|
|
29303
|
-
}
|
|
29304
|
-
if (key.ctrl && key.name === "v" && !this.agentRunning) {
|
|
29305
|
-
try {
|
|
29306
|
-
const { readClipboardImage: readClipboardImage2, bufferToDataUrl: bufferToDataUrl2 } = await Promise.resolve().then(() => (init_image_utils(), exports_image_utils));
|
|
29307
|
-
const clipBuf = await readClipboardImage2();
|
|
29308
|
-
if (clipBuf) {
|
|
29309
|
-
const { dataUrl } = await bufferToDataUrl2(clipBuf);
|
|
29310
|
-
this.pendingClipboardImage = dataUrl;
|
|
29311
|
-
const sizeKb = Math.round(dataUrl.length * 3 / 4 / 1024);
|
|
29312
|
-
console.log(pc2.green(`
|
|
29313
|
-
${t("image.attached", { source: "clipboard", size: `${sizeKb} KB` })}`));
|
|
29314
|
-
this.rl.prompt();
|
|
29315
|
-
} else {
|
|
29316
|
-
console.log(pc2.yellow(`
|
|
29317
|
-
${t("image.clipboard_empty")}`));
|
|
29318
|
-
this.rl.prompt();
|
|
29319
|
-
}
|
|
29320
|
-
} catch {}
|
|
29321
|
-
}
|
|
30020
|
+
if (this.exitOnClose && !process.stdin.isTTY) {
|
|
30021
|
+
process.stdin.on("end", () => {
|
|
30022
|
+
this.running = false;
|
|
30023
|
+
this.saveHistory();
|
|
30024
|
+
this.agent.shutdown();
|
|
30025
|
+
process.exit(0);
|
|
29322
30026
|
});
|
|
29323
30027
|
}
|
|
30028
|
+
if (this.rl instanceof LineEditor) {
|
|
30029
|
+
this.rl.onKeyInput = (str, key) => this.handleSpecialKey(str, key);
|
|
30030
|
+
}
|
|
29324
30031
|
let forceExitTimer = null;
|
|
29325
30032
|
process.on("SIGINT", () => {
|
|
29326
30033
|
if (this.agentRunning) {
|
|
@@ -29336,6 +30043,51 @@ ${t("image.clipboard_empty")}`));
|
|
|
29336
30043
|
}
|
|
29337
30044
|
});
|
|
29338
30045
|
}
|
|
30046
|
+
handleSpecialKey(str, key) {
|
|
30047
|
+
if (key.name === "escape") {
|
|
30048
|
+
const escBytes = key.sequence ? (key.sequence.match(/\x1b/g) || []).length : 1;
|
|
30049
|
+
const now = Date.now();
|
|
30050
|
+
const withinWindow = now - this.lastEscTime < this.doubleEscDelay;
|
|
30051
|
+
this.lastEscTime = now;
|
|
30052
|
+
if (escBytes >= 2 || withinWindow) {
|
|
30053
|
+
this.lastEscTime = 0;
|
|
30054
|
+
if (this.agentRunning) {
|
|
30055
|
+
process.stdout.write(pc2.yellow(`
|
|
30056
|
+
${t("repl.interrupt")}
|
|
30057
|
+
`));
|
|
30058
|
+
this.agent.shutdown();
|
|
30059
|
+
}
|
|
30060
|
+
}
|
|
30061
|
+
return true;
|
|
30062
|
+
}
|
|
30063
|
+
if (key.ctrl && key.name === "v" && !this.agentRunning) {
|
|
30064
|
+
this.pasteClipboardImage();
|
|
30065
|
+
return true;
|
|
30066
|
+
}
|
|
30067
|
+
return false;
|
|
30068
|
+
}
|
|
30069
|
+
async pasteClipboardImage() {
|
|
30070
|
+
try {
|
|
30071
|
+
const { readClipboardImage: readClipboardImage2, bufferToDataUrl: bufferToDataUrl2 } = await Promise.resolve().then(() => (init_image_utils(), exports_image_utils));
|
|
30072
|
+
const clipBuf = await readClipboardImage2();
|
|
30073
|
+
if (clipBuf) {
|
|
30074
|
+
const { dataUrl } = await bufferToDataUrl2(clipBuf);
|
|
30075
|
+
this.pendingClipboardImage = dataUrl;
|
|
30076
|
+
const sizeKb = Math.round(dataUrl.length * 3 / 4 / 1024);
|
|
30077
|
+
console.log(pc2.green(`
|
|
30078
|
+
${t("image.attached", { source: "clipboard", size: `${sizeKb} KB` })}`));
|
|
30079
|
+
if (this.rl instanceof LineEditor)
|
|
30080
|
+
this.rl.reset();
|
|
30081
|
+
this.rl.prompt();
|
|
30082
|
+
} else {
|
|
30083
|
+
console.log(pc2.yellow(`
|
|
30084
|
+
${t("image.clipboard_empty")}`));
|
|
30085
|
+
if (this.rl instanceof LineEditor)
|
|
30086
|
+
this.rl.reset();
|
|
30087
|
+
this.rl.prompt();
|
|
30088
|
+
}
|
|
30089
|
+
} catch {}
|
|
30090
|
+
}
|
|
29339
30091
|
isMultiLineInput(line) {
|
|
29340
30092
|
if (line.endsWith("\\"))
|
|
29341
30093
|
return true;
|
|
@@ -29361,6 +30113,8 @@ ${t("image.clipboard_empty")}`));
|
|
|
29361
30113
|
this.pendingClipboardImage = null;
|
|
29362
30114
|
}
|
|
29363
30115
|
this.logger?.logREPL("user", input);
|
|
30116
|
+
if (this.rl instanceof LineEditor)
|
|
30117
|
+
this.rl.reset();
|
|
29364
30118
|
process.stdout.write(`
|
|
29365
30119
|
` + pc2.green(t("repl.agent")));
|
|
29366
30120
|
const renderer = new Renderer({
|
|
@@ -29552,6 +30306,21 @@ ${t("image.clipboard_empty")}`));
|
|
|
29552
30306
|
}
|
|
29553
30307
|
}
|
|
29554
30308
|
|
|
30309
|
+
// src/cli/run-result.ts
|
|
30310
|
+
init_i18n();
|
|
30311
|
+
init_colors();
|
|
30312
|
+
function printRunResult(result, flush) {
|
|
30313
|
+
flush();
|
|
30314
|
+
if (result.success) {
|
|
30315
|
+
if (!result.text) {
|
|
30316
|
+
console.log(pc2.yellow(t("cli.no_output")));
|
|
30317
|
+
}
|
|
30318
|
+
return 0;
|
|
30319
|
+
}
|
|
30320
|
+
console.error(`${t("error.prefix")}${result.error}`);
|
|
30321
|
+
return 1;
|
|
30322
|
+
}
|
|
30323
|
+
|
|
29555
30324
|
// src/cli/main.ts
|
|
29556
30325
|
init_setup();
|
|
29557
30326
|
init_config2();
|
|
@@ -29784,18 +30553,9 @@ async function main() {
|
|
|
29784
30553
|
}
|
|
29785
30554
|
});
|
|
29786
30555
|
renderer.flush();
|
|
29787
|
-
|
|
29788
|
-
if (!result.text) {
|
|
29789
|
-
console.log(pc2.yellow(t("cli.no_output")));
|
|
29790
|
-
}
|
|
29791
|
-
} else {
|
|
29792
|
-
console.error(`${t("error.prefix")}${result.error}`);
|
|
29793
|
-
if (result.text) {
|
|
29794
|
-
console.log(formatMarkdown(result.text));
|
|
29795
|
-
}
|
|
29796
|
-
process.exit(1);
|
|
29797
|
-
}
|
|
30556
|
+
const exitCode = printRunResult(result, () => renderer.flush());
|
|
29798
30557
|
agent.shutdown();
|
|
30558
|
+
process.exit(exitCode);
|
|
29799
30559
|
} else {
|
|
29800
30560
|
const configPath = join41(homedir18(), ".mma", "config.json");
|
|
29801
30561
|
if (!existsSync46(configPath)) {
|
|
@@ -29858,7 +30618,7 @@ async function main() {
|
|
|
29858
30618
|
logger
|
|
29859
30619
|
} = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
|
|
29860
30620
|
startAutoUpdate(config);
|
|
29861
|
-
const repl = new Repl(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd, logger);
|
|
30621
|
+
const repl = new Repl(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd, logger, true);
|
|
29862
30622
|
repl.start();
|
|
29863
30623
|
}
|
|
29864
30624
|
}
|