micro-models-agent 0.35.4 → 0.36.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 +1263 -923
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -2472,6 +2472,10 @@ Command: {command}`,
|
|
|
2472
2472
|
"plan.step_already_done": "Step {step} is already done — nothing to do. Plan progress:",
|
|
2473
2473
|
"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.',
|
|
2474
2474
|
"plan.deliverables_missing": "Cannot mark step {step} done: the files it names do not exist yet: {files}. Create these files first (or mark the step status=skipped if they are not actually needed).",
|
|
2475
|
+
"plan.kinds_mismatch": "kinds array length must match steps",
|
|
2476
|
+
"plan.kinds_invalid": 'Invalid step kind(s): {kinds}. Use "create" or "delete".',
|
|
2477
|
+
"plan.kinds_not_array": 'kinds must be an array of "create" or "delete"',
|
|
2478
|
+
"plan.deliverables_remain": "Cannot mark step {step} done: the files it names still exist: {files}. This step is kind=delete — delete these files first (or set status=skipped if they should stay).",
|
|
2475
2479
|
"plan.show_header": "Plan status:",
|
|
2476
2480
|
"plan.show_empty": "(plan has no steps)",
|
|
2477
2481
|
"plan.list_header": "Plans:",
|
|
@@ -2768,6 +2772,7 @@ Use this knowledge to answer the user's question.`,
|
|
|
2768
2772
|
"exec.audit_pass": "[✓] Task complete: {done}/{total} steps done, {files} files verified",
|
|
2769
2773
|
"exec.audit_pending": "[✗] Task incomplete: {done}/{total} steps done — remaining steps are not marked done",
|
|
2770
2774
|
"exec.audit_fail": "[✗] Task incomplete: {done}/{total} steps done, {files} files missing",
|
|
2775
|
+
"exec.audit_leftovers": "[✗] Task incomplete: {done}/{total} steps done, {files} files from delete-steps still exist",
|
|
2771
2776
|
"exec.audit_fail_tests": "[✗] Task incomplete: {done}/{total} steps done, tests FAILING: {failed} failed / {passed} passed — {detail}",
|
|
2772
2777
|
"exec.audit_fail_typecheck": "[✗] Task incomplete: {done}/{total} steps done, {missing} files missing, typecheck error: {typeError}",
|
|
2773
2778
|
"exec.audit_incomplete": "[⚠ Final audit incomplete: {summary}. Task is NOT finished — continue working. Remaining steps: {steps}]",
|
|
@@ -2779,6 +2784,7 @@ Use this knowledge to answer the user's question.`,
|
|
|
2779
2784
|
[Hints]
|
|
2780
2785
|
{hints}`,
|
|
2781
2786
|
"exec.file_rewrite_warning": "⚠️ File {file} has been rewritten {count} times. Consider a different approach — the current fix strategy is not working.",
|
|
2787
|
+
"exec.forbidden_cmd": 'STOP using "{cmd}" via the bash tool — it is not a native Windows cmd.exe command and has failed repeatedly this session. Use the dedicated tool instead: grep → the grep tool, ls/dir → list_dir, find → glob, rm → delete_file, sed → edit_file, touch → write_file, which → `where`, cp/mv → move_file, diff → read_file. Do NOT call bash for this purpose again.',
|
|
2782
2788
|
"hall.max_retries_exhausted": "Model returned empty or insufficient responses after multiple retries",
|
|
2783
2789
|
"hall.short_response": "Response too short or empty",
|
|
2784
2790
|
"hall.repetitive": "Response too repetitive ({pct}% overlap)",
|
|
@@ -3064,6 +3070,10 @@ var init_ru = __esm(() => {
|
|
|
3064
3070
|
"plan.step_already_done": "Шаг {step} уже выполнен — ничего делать не нужно. Прогресс плана:",
|
|
3065
3071
|
"plan.order_blocked": "Нельзя отметить шаг {step} выполненным: шаг {first} («{desc}») ещё не завершён. Сначала завершите предыдущие шаги или отметьте шаг {first} status=skipped, если он не нужен.",
|
|
3066
3072
|
"plan.deliverables_missing": "Нельзя отметить шаг {step} выполненным: указанные в нём файлы ещё не существуют: {files}. Сначала создайте эти файлы (или отметьте шаг status=skipped, если они на самом деле не нужны).",
|
|
3073
|
+
"plan.kinds_mismatch": "Длина kinds должна совпадать с числом шагов",
|
|
3074
|
+
"plan.kinds_invalid": 'Недопустимый kind шага: {kinds}. Используй "create" или "delete".',
|
|
3075
|
+
"plan.kinds_not_array": 'kinds должен быть массивом значений "create" или "delete"',
|
|
3076
|
+
"plan.deliverables_remain": "Нельзя пометить шаг {step} как выполненный: указанные файлы всё ещё существуют: {files}. Шаг имеет kind=delete — сначала удали их (или поставь status=skipped, если они должны остаться).",
|
|
3067
3077
|
"plan.show_header": "Статус плана:",
|
|
3068
3078
|
"plan.show_empty": "(в плане нет шагов)",
|
|
3069
3079
|
"plan.list_header": "Планы:",
|
|
@@ -3360,6 +3370,7 @@ var init_ru = __esm(() => {
|
|
|
3360
3370
|
"exec.audit_pass": "[✓] Задача выполнена: {done}/{total} шагов, {files} файлов проверено",
|
|
3361
3371
|
"exec.audit_pending": "[✗] Задача не выполнена: {done}/{total} шагов — оставшиеся шаги не отмечены выполненными",
|
|
3362
3372
|
"exec.audit_fail": "[✗] Задача не выполнена: {done}/{total} шагов, {files} файлов отсутствует",
|
|
3373
|
+
"exec.audit_leftovers": "[✗] Задача не выполнена: {done}/{total} шагов, {files} файлов из delete-шагов всё ещё существуют",
|
|
3363
3374
|
"exec.audit_fail_tests": "[✗] Задача не выполнена: {done}/{total} шагов, тесты ПАДАЮТ: {failed} failed / {passed} passed — {detail}",
|
|
3364
3375
|
"exec.audit_fail_typecheck": "[✗] Задача не выполнена: {done}/{total} шагов, {missing} файлов отсутствует, ошибка typecheck: {typeError}",
|
|
3365
3376
|
"exec.audit_incomplete": "[⚠ Финальная проверка не пройдена: {summary}. Задача НЕ завершена — продолжайте работу. Оставшиеся шаги: {steps}]",
|
|
@@ -3371,6 +3382,7 @@ var init_ru = __esm(() => {
|
|
|
3371
3382
|
[Подсказки]
|
|
3372
3383
|
{hints}`,
|
|
3373
3384
|
"exec.file_rewrite_warning": "⚠️ Файл {file} был перезаписан {count} раз. Попробуйте другой подход — текущая стратегия исправлений не работает.",
|
|
3385
|
+
"exec.forbidden_cmd": 'ПРЕКРАТИ использовать "{cmd}" через bash — это не команда Windows cmd.exe, и она уже неоднократно падала в этой сессии. Используй предназначенный тул: grep → тул grep, ls/dir → list_dir, find → glob, rm → delete_file, sed → edit_file, touch → write_file, which → `where`, cp/mv → move_file, diff → read_file. Больше не вызывай bash для этого.',
|
|
3374
3386
|
"hall.max_retries_exhausted": "Модель вернула пустой или недостаточный ответ после нескольких попыток",
|
|
3375
3387
|
"hall.short_response": "Слишком короткий или пустой ответ",
|
|
3376
3388
|
"hall.repetitive": "Слишком повторяющийся ответ ({pct}% совпадение)",
|
|
@@ -4902,6 +4914,30 @@ var exports_openai_compat = {};
|
|
|
4902
4914
|
__export(exports_openai_compat, {
|
|
4903
4915
|
OpenAICompatProvider: () => OpenAICompatProvider
|
|
4904
4916
|
});
|
|
4917
|
+
function buildRequestBody(opts) {
|
|
4918
|
+
const body = {
|
|
4919
|
+
model: opts.model,
|
|
4920
|
+
messages: opts.messages,
|
|
4921
|
+
stream: opts.stream
|
|
4922
|
+
};
|
|
4923
|
+
if (opts.maxTokens !== undefined)
|
|
4924
|
+
body.max_tokens = opts.maxTokens;
|
|
4925
|
+
if (opts.reasoningEffort) {
|
|
4926
|
+
body.reasoning_effort = opts.reasoningEffort;
|
|
4927
|
+
}
|
|
4928
|
+
if (opts.tools && opts.tools.length > 0) {
|
|
4929
|
+
body.tools = opts.tools.map((t2) => ({
|
|
4930
|
+
type: "function",
|
|
4931
|
+
function: {
|
|
4932
|
+
name: t2.name,
|
|
4933
|
+
description: t2.description,
|
|
4934
|
+
parameters: t2.parameters
|
|
4935
|
+
}
|
|
4936
|
+
}));
|
|
4937
|
+
body.tool_choice = "auto";
|
|
4938
|
+
}
|
|
4939
|
+
return body;
|
|
4940
|
+
}
|
|
4905
4941
|
|
|
4906
4942
|
class OpenAICompatProvider {
|
|
4907
4943
|
model;
|
|
@@ -4949,26 +4985,14 @@ class OpenAICompatProvider {
|
|
|
4949
4985
|
}
|
|
4950
4986
|
}
|
|
4951
4987
|
async* doStream(messages, tools, signal, options) {
|
|
4952
|
-
const body = {
|
|
4988
|
+
const body = buildRequestBody({
|
|
4953
4989
|
model: this.model,
|
|
4954
4990
|
messages,
|
|
4991
|
+
tools,
|
|
4955
4992
|
stream: true,
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
body.reasoning_effort = options.reasoningEffort;
|
|
4960
|
-
}
|
|
4961
|
-
if (tools && tools.length > 0) {
|
|
4962
|
-
body.tools = tools.map((t2) => ({
|
|
4963
|
-
type: "function",
|
|
4964
|
-
function: {
|
|
4965
|
-
name: t2.name,
|
|
4966
|
-
description: t2.description,
|
|
4967
|
-
parameters: t2.parameters
|
|
4968
|
-
}
|
|
4969
|
-
}));
|
|
4970
|
-
body.tool_choice = "auto";
|
|
4971
|
-
}
|
|
4993
|
+
maxTokens: options?.maxTokens ?? this.config.maxCompletionTokens ?? 4096,
|
|
4994
|
+
reasoningEffort: options?.reasoningEffort
|
|
4995
|
+
});
|
|
4972
4996
|
const headers = {
|
|
4973
4997
|
"Content-Type": "application/json"
|
|
4974
4998
|
};
|
|
@@ -5094,26 +5118,14 @@ class OpenAICompatProvider {
|
|
|
5094
5118
|
}
|
|
5095
5119
|
}
|
|
5096
5120
|
async doNonStreaming(messages, tools, signal, options) {
|
|
5097
|
-
const body = {
|
|
5121
|
+
const body = buildRequestBody({
|
|
5098
5122
|
model: this.model,
|
|
5099
5123
|
messages,
|
|
5124
|
+
tools,
|
|
5100
5125
|
stream: false,
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
body.reasoning_effort = options.reasoningEffort;
|
|
5105
|
-
}
|
|
5106
|
-
if (tools && tools.length > 0) {
|
|
5107
|
-
body.tools = tools.map((t2) => ({
|
|
5108
|
-
type: "function",
|
|
5109
|
-
function: {
|
|
5110
|
-
name: t2.name,
|
|
5111
|
-
description: t2.description,
|
|
5112
|
-
parameters: t2.parameters
|
|
5113
|
-
}
|
|
5114
|
-
}));
|
|
5115
|
-
body.tool_choice = "auto";
|
|
5116
|
-
}
|
|
5126
|
+
maxTokens: options?.maxTokens ?? this.config.maxCompletionTokens ?? 4096,
|
|
5127
|
+
reasoningEffort: options?.reasoningEffort
|
|
5128
|
+
});
|
|
5117
5129
|
const headers = {
|
|
5118
5130
|
"Content-Type": "application/json"
|
|
5119
5131
|
};
|
|
@@ -9374,13 +9386,14 @@ class StuckDetector {
|
|
|
9374
9386
|
if (desc.includes("deploy") || desc.includes("publish")) {
|
|
9375
9387
|
hints.push("Verify credentials and network access before deploying.");
|
|
9376
9388
|
}
|
|
9377
|
-
|
|
9389
|
+
const primary = this.getPrimaryReason();
|
|
9390
|
+
if (this.hasRepetitiveToolCalls() && primary !== "repetitive") {
|
|
9378
9391
|
hints.push("You are calling the same tool repeatedly with the same arguments. Try a different approach.");
|
|
9379
9392
|
}
|
|
9380
|
-
if (this.hasConsecutiveFailures()) {
|
|
9393
|
+
if (this.hasConsecutiveFailures() && primary !== "consecutive") {
|
|
9381
9394
|
hints.push("Multiple different tools are failing. Check if the environment is set up correctly.");
|
|
9382
9395
|
}
|
|
9383
|
-
if (this.hasReadOnlyLoop()) {
|
|
9396
|
+
if (this.hasReadOnlyLoop() && primary !== "read-only") {
|
|
9384
9397
|
hints.push("You have made many read-only tool calls (read_file/glob/grep/browser) without writing anything. Stop exploring — make the edit/write the task needs, or use the plan tool to decide next steps.");
|
|
9385
9398
|
}
|
|
9386
9399
|
return hints;
|
|
@@ -9506,35 +9519,50 @@ class StuckDetector {
|
|
|
9506
9519
|
return "";
|
|
9507
9520
|
}
|
|
9508
9521
|
getRecoveryMessage() {
|
|
9509
|
-
|
|
9522
|
+
const reason = this.getPrimaryReason();
|
|
9523
|
+
if (reason === "stuck") {
|
|
9510
9524
|
return t("exec.stuck_recovery", {
|
|
9511
9525
|
iterations: this.iterationsOnCurrentStep,
|
|
9512
9526
|
stepId: String(this.currentStepId ?? "?"),
|
|
9513
9527
|
description: this.currentStepDescription
|
|
9514
9528
|
});
|
|
9515
9529
|
}
|
|
9516
|
-
|
|
9517
|
-
|
|
9530
|
+
if (reason === "tool-errors") {
|
|
9531
|
+
const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
|
|
9518
9532
|
return t("exec.tool_errors_recovery", {
|
|
9519
9533
|
tool: errorTool[0],
|
|
9520
9534
|
count: errorTool[1]
|
|
9521
9535
|
});
|
|
9522
9536
|
}
|
|
9523
|
-
if (
|
|
9537
|
+
if (reason === "consecutive") {
|
|
9524
9538
|
return t("exec.consecutive_failures_recovery", {
|
|
9525
9539
|
count: this.consecutiveFailures
|
|
9526
9540
|
});
|
|
9527
9541
|
}
|
|
9528
|
-
if (
|
|
9542
|
+
if (reason === "repetitive") {
|
|
9529
9543
|
return this.getRepetitiveToolMessage();
|
|
9530
9544
|
}
|
|
9531
|
-
if (
|
|
9545
|
+
if (reason === "read-only") {
|
|
9532
9546
|
return t("exec.read_only_loop_recovery", {
|
|
9533
9547
|
count: String(this.readOnlyStreak)
|
|
9534
9548
|
});
|
|
9535
9549
|
}
|
|
9536
9550
|
return "";
|
|
9537
9551
|
}
|
|
9552
|
+
getPrimaryReason() {
|
|
9553
|
+
if (this.isStuck())
|
|
9554
|
+
return "stuck";
|
|
9555
|
+
const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
|
|
9556
|
+
if (errorTool)
|
|
9557
|
+
return "tool-errors";
|
|
9558
|
+
if (this.hasConsecutiveFailures())
|
|
9559
|
+
return "consecutive";
|
|
9560
|
+
if (this.hasRepetitiveToolCalls())
|
|
9561
|
+
return "repetitive";
|
|
9562
|
+
if (this.hasReadOnlyLoop())
|
|
9563
|
+
return "read-only";
|
|
9564
|
+
return null;
|
|
9565
|
+
}
|
|
9538
9566
|
reset() {
|
|
9539
9567
|
this.currentStepId = null;
|
|
9540
9568
|
this.iterationsOnCurrentStep = 0;
|
|
@@ -10382,7 +10410,11 @@ class Agent {
|
|
|
10382
10410
|
${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
10383
10411
|
}
|
|
10384
10412
|
emitPhase(iteration, phase, onPhase) {
|
|
10385
|
-
this.deps.pluginManager.runOnPhase?.({
|
|
10413
|
+
this.deps.pluginManager.runOnPhase?.({
|
|
10414
|
+
iteration,
|
|
10415
|
+
logger: this.deps.logger,
|
|
10416
|
+
contextManager: this.deps.contextManager
|
|
10417
|
+
}, phase);
|
|
10386
10418
|
onPhase?.(phase);
|
|
10387
10419
|
}
|
|
10388
10420
|
async run(input, onChunk, onMeta, onTool, onPhase) {
|
|
@@ -10412,7 +10444,8 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
10412
10444
|
}
|
|
10413
10445
|
pluginManager.runOnSessionStart({
|
|
10414
10446
|
logger,
|
|
10415
|
-
sessionManager: sessionManager?.getActiveMeta()
|
|
10447
|
+
sessionManager: sessionManager?.getActiveMeta(),
|
|
10448
|
+
contextManager
|
|
10416
10449
|
});
|
|
10417
10450
|
if (config.session?.baselineCheck !== false) {
|
|
10418
10451
|
const verifier = new StepVerifier(baseDir);
|
|
@@ -10545,7 +10578,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
10545
10578
|
if (chunk.type === "reasoning" && chunk.content) {
|
|
10546
10579
|
reasoningContent += chunk.content;
|
|
10547
10580
|
if (config.showReasoning) {
|
|
10548
|
-
const metaOut = pluginManager.runOnMeta({ iteration, logger }, chunk.content);
|
|
10581
|
+
const metaOut = pluginManager.runOnMeta({ iteration, logger, contextManager }, chunk.content);
|
|
10549
10582
|
if (metaOut) {
|
|
10550
10583
|
onMeta?.(pc2.dim(metaOut));
|
|
10551
10584
|
}
|
|
@@ -10579,7 +10612,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
10579
10612
|
logger.logLLMResponse(config.model, textContent.length, Date.now() - llmStart, err.message, "agent");
|
|
10580
10613
|
logger.error(`LLM call failed: ${err.message}`);
|
|
10581
10614
|
slog.logError(err.message);
|
|
10582
|
-
pluginManager.runOnError({ iteration, logger }, err);
|
|
10615
|
+
pluginManager.runOnError({ iteration, logger, contextManager }, err);
|
|
10583
10616
|
return {
|
|
10584
10617
|
success: false,
|
|
10585
10618
|
text: lastText,
|
|
@@ -10598,7 +10631,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
10598
10631
|
const showText = textChunks.length > 0 && (!sawToolCall || toolComments && !isToolCallJson(textContent));
|
|
10599
10632
|
if (showText) {
|
|
10600
10633
|
for (const chunk of textChunks) {
|
|
10601
|
-
const textOut = pluginManager.runOnText({ iteration, logger }, chunk);
|
|
10634
|
+
const textOut = pluginManager.runOnText({ iteration, logger, contextManager }, chunk);
|
|
10602
10635
|
onChunk?.(textOut);
|
|
10603
10636
|
}
|
|
10604
10637
|
}
|
|
@@ -10610,7 +10643,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
10610
10643
|
} else if (reasoningContent) {
|
|
10611
10644
|
llmResponse = { type: "reasoning", content: reasoningContent };
|
|
10612
10645
|
}
|
|
10613
|
-
pluginManager.runOnAfterThink({ iteration, logger }, llmResponse);
|
|
10646
|
+
pluginManager.runOnAfterThink({ iteration, logger, contextManager }, llmResponse);
|
|
10614
10647
|
if (this.deps.exitOnComplete && sawToolCall) {
|
|
10615
10648
|
const signature = toolCalls.map((tc) => `${tc.name}:${JSON.stringify(tc.arguments)}`).join("|");
|
|
10616
10649
|
if (signature && signature === lastToolSignature) {
|
|
@@ -10651,7 +10684,7 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
10651
10684
|
toolName: call.name,
|
|
10652
10685
|
args: call.arguments
|
|
10653
10686
|
});
|
|
10654
|
-
pluginManager.runOnToolStart({ iteration, logger }, { id: call.id, name: call.name, arguments: call.arguments });
|
|
10687
|
+
pluginManager.runOnToolStart({ iteration, logger, contextManager }, { id: call.id, name: call.name, arguments: call.arguments });
|
|
10655
10688
|
onTool?.({ type: "start", tool: call.name, args: call.arguments });
|
|
10656
10689
|
slog.logToolCall(call, iteration);
|
|
10657
10690
|
const tokensBeforeTool = contextManager.getEstimatedTokens();
|
|
@@ -10667,13 +10700,13 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
10667
10700
|
hallucinationDetector.getConsistencyCheck().trackDeletedFile(filePath);
|
|
10668
10701
|
}
|
|
10669
10702
|
}
|
|
10670
|
-
pluginManager.runOnToolEnd({ iteration, logger }, { id: call.id, name: call.name, arguments: call.arguments }, result, duration);
|
|
10703
|
+
pluginManager.runOnToolEnd({ iteration, logger, contextManager }, { id: call.id, name: call.name, arguments: call.arguments }, result, duration);
|
|
10671
10704
|
if (result.display) {
|
|
10672
10705
|
onMeta?.(`
|
|
10673
10706
|
` + result.display + `
|
|
10674
10707
|
`);
|
|
10675
10708
|
} else {
|
|
10676
|
-
const metaOut = pluginManager.runOnMeta({ iteration, logger }, result.output);
|
|
10709
|
+
const metaOut = pluginManager.runOnMeta({ iteration, logger, contextManager }, result.output);
|
|
10677
10710
|
onMeta?.(`
|
|
10678
10711
|
` + pc2.dim(metaOut) + `
|
|
10679
10712
|
`);
|
|
@@ -10994,7 +11027,8 @@ ${warnLine}
|
|
|
10994
11027
|
logger.closeSessionLog();
|
|
10995
11028
|
pluginManager.runOnSessionEnd({
|
|
10996
11029
|
logger,
|
|
10997
|
-
sessionManager: sessionManager?.getActiveMeta()
|
|
11030
|
+
sessionManager: sessionManager?.getActiveMeta(),
|
|
11031
|
+
contextManager
|
|
10998
11032
|
});
|
|
10999
11033
|
}
|
|
11000
11034
|
}
|
|
@@ -11009,6 +11043,105 @@ var init_agent = __esm(() => {
|
|
|
11009
11043
|
init_verifier();
|
|
11010
11044
|
});
|
|
11011
11045
|
|
|
11046
|
+
// src/modules/context/fact-extractor.ts
|
|
11047
|
+
class FactExtractor {
|
|
11048
|
+
fileFacts = [];
|
|
11049
|
+
deletedFacts = [];
|
|
11050
|
+
decisionFacts = [];
|
|
11051
|
+
errorFacts = [];
|
|
11052
|
+
static MAX_FACTS = 20;
|
|
11053
|
+
get errorCount() {
|
|
11054
|
+
return this.errorFacts.length;
|
|
11055
|
+
}
|
|
11056
|
+
extract(turns) {
|
|
11057
|
+
const DRIVE = "(?:[a-zA-Z]:[\\\\/])?";
|
|
11058
|
+
const createdPatterns = [
|
|
11059
|
+
new RegExp(`(?:Created|Updated|Written|Moved|Replaced in) ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi"),
|
|
11060
|
+
new RegExp(`Файл (?:создан|обновлён|записан|перемещён):? ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi"),
|
|
11061
|
+
new RegExp(`file (?:created|updated|written|moved):? ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi")
|
|
11062
|
+
];
|
|
11063
|
+
const deletedPatterns = [
|
|
11064
|
+
new RegExp(`Deleted ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi"),
|
|
11065
|
+
new RegExp(`(?:Удалён|Файл удалён):? ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi")
|
|
11066
|
+
];
|
|
11067
|
+
const newFiles = [];
|
|
11068
|
+
const newDeleted = [];
|
|
11069
|
+
const newDecisions = [];
|
|
11070
|
+
const newErrors = [];
|
|
11071
|
+
const lastEvent = new Map;
|
|
11072
|
+
for (const msg of turns) {
|
|
11073
|
+
const content = getMessageText(msg.content);
|
|
11074
|
+
if (msg.role === "user" && (content.startsWith("<system-summary>") || content.includes("[Compressed:"))) {
|
|
11075
|
+
continue;
|
|
11076
|
+
}
|
|
11077
|
+
if (msg.role === "tool") {
|
|
11078
|
+
for (const pattern of createdPatterns) {
|
|
11079
|
+
for (const match of content.matchAll(pattern)) {
|
|
11080
|
+
newFiles.push(match[1]);
|
|
11081
|
+
lastEvent.set(match[1], "file");
|
|
11082
|
+
}
|
|
11083
|
+
}
|
|
11084
|
+
for (const pattern of deletedPatterns) {
|
|
11085
|
+
for (const match of content.matchAll(pattern)) {
|
|
11086
|
+
newDeleted.push(match[1]);
|
|
11087
|
+
lastEvent.set(match[1], "deleted");
|
|
11088
|
+
}
|
|
11089
|
+
}
|
|
11090
|
+
if (content.includes("Plan:") && content.includes("[")) {
|
|
11091
|
+
const planLine = content.split(`
|
|
11092
|
+
`).find((line) => line.includes("Plan:"));
|
|
11093
|
+
if (planLine)
|
|
11094
|
+
newDecisions.push(planLine.trim());
|
|
11095
|
+
}
|
|
11096
|
+
}
|
|
11097
|
+
if (msg.role === "assistant") {
|
|
11098
|
+
if (content.includes("decided:") || content.includes("decision:")) {
|
|
11099
|
+
const line = content.split(`
|
|
11100
|
+
`).find((l) => l.includes("decided:") || l.includes("decision:"));
|
|
11101
|
+
if (line)
|
|
11102
|
+
newDecisions.push(line.trim().slice(0, 200));
|
|
11103
|
+
}
|
|
11104
|
+
}
|
|
11105
|
+
if (content.includes("Error:") || content.includes("failed") || content.includes("Ошибка:") || content.includes("не удалось")) {
|
|
11106
|
+
const line = content.split(`
|
|
11107
|
+
`).find((l) => l.includes("Error:") || l.includes("failed") || l.includes("Ошибка:") || l.includes("не удалось"));
|
|
11108
|
+
if (line)
|
|
11109
|
+
newErrors.push(line.trim().slice(0, 250));
|
|
11110
|
+
}
|
|
11111
|
+
}
|
|
11112
|
+
const dedup = (arr) => [...new Set(arr)];
|
|
11113
|
+
const finalFiles = dedup(newFiles.filter((p) => lastEvent.get(p) === "file"));
|
|
11114
|
+
const finalDeleted = dedup(newDeleted.filter((p) => lastEvent.get(p) === "deleted"));
|
|
11115
|
+
this.fileFacts = dedup([...this.fileFacts, ...finalFiles].filter((p) => !finalDeleted.includes(p))).slice(-FactExtractor.MAX_FACTS);
|
|
11116
|
+
this.deletedFacts = dedup([...this.deletedFacts, ...finalDeleted].filter((p) => !finalFiles.includes(p))).slice(-FactExtractor.MAX_FACTS);
|
|
11117
|
+
this.decisionFacts = dedup([...this.decisionFacts, ...newDecisions]).slice(-FactExtractor.MAX_FACTS);
|
|
11118
|
+
this.errorFacts = dedup([...this.errorFacts, ...newErrors]).slice(-FactExtractor.MAX_FACTS);
|
|
11119
|
+
}
|
|
11120
|
+
filesLine() {
|
|
11121
|
+
const deletedSet = new Set(this.deletedFacts);
|
|
11122
|
+
const liveFiles = this.fileFacts.filter((p) => !deletedSet.has(p));
|
|
11123
|
+
if (liveFiles.length === 0)
|
|
11124
|
+
return null;
|
|
11125
|
+
return `[Files: ${liveFiles.slice(-15).join("; ")}]`;
|
|
11126
|
+
}
|
|
11127
|
+
deletedLine() {
|
|
11128
|
+
if (this.deletedFacts.length === 0)
|
|
11129
|
+
return null;
|
|
11130
|
+
return `[Deleted: ${this.deletedFacts.slice(-10).join("; ")}]`;
|
|
11131
|
+
}
|
|
11132
|
+
decisionsLine() {
|
|
11133
|
+
if (this.decisionFacts.length === 0)
|
|
11134
|
+
return null;
|
|
11135
|
+
return `[Decisions: ${this.decisionFacts.slice(-5).join("; ")}]`;
|
|
11136
|
+
}
|
|
11137
|
+
errorsLine() {
|
|
11138
|
+
if (this.errorFacts.length === 0)
|
|
11139
|
+
return null;
|
|
11140
|
+
return `[Errors: ${this.errorFacts.slice(-3).join("; ")}]`;
|
|
11141
|
+
}
|
|
11142
|
+
}
|
|
11143
|
+
var init_fact_extractor = () => {};
|
|
11144
|
+
|
|
11012
11145
|
// src/modules/context/manager.ts
|
|
11013
11146
|
function summarizeArgs(args) {
|
|
11014
11147
|
if (!args)
|
|
@@ -11027,6 +11160,18 @@ function truncate(s, max) {
|
|
|
11027
11160
|
return s;
|
|
11028
11161
|
return s.slice(0, max - 3) + "...";
|
|
11029
11162
|
}
|
|
11163
|
+
function looksLikeErrorPaste(text) {
|
|
11164
|
+
if (/(?:^|\s)ERROR|Error:|error TS\d+|Transform failed|\[plugin:|SyntaxError|Cannot find|Uncaught|exception/i.test(text)) {
|
|
11165
|
+
return true;
|
|
11166
|
+
}
|
|
11167
|
+
if (/(?:^|\n)\s*\d+\s*\|/.test(text))
|
|
11168
|
+
return true;
|
|
11169
|
+
if (/[\w./\\-]+\.[a-z]{1,6}:\d+:\d+/.test(text))
|
|
11170
|
+
return true;
|
|
11171
|
+
if (/(?:^|\n)\s*at\s+\S+/.test(text))
|
|
11172
|
+
return true;
|
|
11173
|
+
return false;
|
|
11174
|
+
}
|
|
11030
11175
|
function extractTriedAndFailed(messages) {
|
|
11031
11176
|
const failures = new Map;
|
|
11032
11177
|
for (const msg of messages) {
|
|
@@ -11058,11 +11203,10 @@ class ContextManager {
|
|
|
11058
11203
|
peakTokens = 0;
|
|
11059
11204
|
budget;
|
|
11060
11205
|
compactionThreshold;
|
|
11061
|
-
|
|
11062
|
-
decisionFacts = [];
|
|
11063
|
-
errorFacts = [];
|
|
11206
|
+
facts = new FactExtractor;
|
|
11064
11207
|
lastUserTask = "";
|
|
11065
|
-
|
|
11208
|
+
sessionMission = "";
|
|
11209
|
+
lastUserFeedback = "";
|
|
11066
11210
|
tokenCounter;
|
|
11067
11211
|
pendingImageParts = [];
|
|
11068
11212
|
toolTokens = 0;
|
|
@@ -11101,6 +11245,7 @@ class ContextManager {
|
|
|
11101
11245
|
resetUserTurn() {
|
|
11102
11246
|
this.iterationsSinceCompaction = 0;
|
|
11103
11247
|
this.lastUserTask = "";
|
|
11248
|
+
this.lastUserFeedback = "";
|
|
11104
11249
|
}
|
|
11105
11250
|
getQuality() {
|
|
11106
11251
|
const usedTokens = this.getEstimatedTokens();
|
|
@@ -11108,16 +11253,22 @@ class ContextManager {
|
|
|
11108
11253
|
const COMPACTION_PENALTY_FLOOR = 0.5;
|
|
11109
11254
|
const compactionLoss = Math.max(COMPACTION_PENALTY_FLOOR, 1 - this.compactionCount * 0.15);
|
|
11110
11255
|
const msgCount = this.messages.length || 1;
|
|
11111
|
-
const errorDensity = Math.max(0, 1 - Math.min(1, this.
|
|
11256
|
+
const errorDensity = Math.max(0, 1 - Math.min(1, this.facts.errorCount / msgCount));
|
|
11112
11257
|
const freshness = Math.max(0, 1 - this.iterationsSinceCompaction / COMPACTION_INTERVAL);
|
|
11113
11258
|
const score = tokenLoad * 0.4 + compactionLoss * 0.25 + errorDensity * 0.2 + freshness * 0.15;
|
|
11114
11259
|
return Math.round(Math.min(100, Math.max(0, score * 100)));
|
|
11115
11260
|
}
|
|
11116
11261
|
addMessage(msg) {
|
|
11117
|
-
if (msg.role === "user"
|
|
11262
|
+
if (msg.role === "user") {
|
|
11118
11263
|
const text = typeof msg.content === "string" ? msg.content : getMessageText(msg.content);
|
|
11119
11264
|
if (text.trim() && !text.startsWith("<system-summary>") && !text.includes("[Compressed:")) {
|
|
11120
|
-
this.lastUserTask
|
|
11265
|
+
if (looksLikeErrorPaste(text) && (this.lastUserTask || this.sessionMission)) {
|
|
11266
|
+
this.lastUserFeedback = text.trim().slice(0, 400);
|
|
11267
|
+
} else if (!this.lastUserTask && !this.lastUserFeedback) {
|
|
11268
|
+
this.lastUserTask = text.trim();
|
|
11269
|
+
if (!this.sessionMission)
|
|
11270
|
+
this.sessionMission = text.trim();
|
|
11271
|
+
}
|
|
11121
11272
|
}
|
|
11122
11273
|
}
|
|
11123
11274
|
if (msg.role === "user" && this.pendingImageParts.length > 0) {
|
|
@@ -11206,22 +11357,27 @@ class ContextManager {
|
|
|
11206
11357
|
const cutoff = this.messages.length - KEEP_LAST_N * 2;
|
|
11207
11358
|
const oldTurns = this.messages.slice(0, cutoff);
|
|
11208
11359
|
const recentTurns = this.messages.slice(cutoff);
|
|
11209
|
-
this.
|
|
11360
|
+
this.facts.extract(oldTurns);
|
|
11210
11361
|
const parts = [];
|
|
11211
11362
|
parts.push(`[Compressed: ${oldTurns.length} old turns removed]`);
|
|
11212
|
-
|
|
11213
|
-
|
|
11363
|
+
const task = (this.lastUserTask || this.sessionMission).slice(0, 400);
|
|
11364
|
+
if (task)
|
|
11214
11365
|
parts.push(`[Task: ${task}]`);
|
|
11215
|
-
|
|
11216
|
-
|
|
11217
|
-
|
|
11218
|
-
|
|
11219
|
-
if (
|
|
11220
|
-
parts.push(
|
|
11221
|
-
|
|
11222
|
-
if (
|
|
11223
|
-
parts.push(
|
|
11224
|
-
|
|
11366
|
+
if (this.lastUserFeedback && !task.startsWith(this.lastUserFeedback)) {
|
|
11367
|
+
parts.push(`[User feedback: ${this.lastUserFeedback}]`);
|
|
11368
|
+
}
|
|
11369
|
+
const filesLine = this.facts.filesLine();
|
|
11370
|
+
if (filesLine)
|
|
11371
|
+
parts.push(filesLine);
|
|
11372
|
+
const deletedLine = this.facts.deletedLine();
|
|
11373
|
+
if (deletedLine)
|
|
11374
|
+
parts.push(deletedLine);
|
|
11375
|
+
const decisionsLine = this.facts.decisionsLine();
|
|
11376
|
+
if (decisionsLine)
|
|
11377
|
+
parts.push(decisionsLine);
|
|
11378
|
+
const errorsLine = this.facts.errorsLine();
|
|
11379
|
+
if (errorsLine)
|
|
11380
|
+
parts.push(errorsLine);
|
|
11225
11381
|
const triedAndFailed = extractTriedAndFailed(oldTurns);
|
|
11226
11382
|
if (triedAndFailed.length > 0) {
|
|
11227
11383
|
const lines = triedAndFailed.map((t2) => `- ${t2.tool}(${t2.args}): ${t2.error} (failed ${t2.count}x)`);
|
|
@@ -11251,55 +11407,6 @@ ${lines.join(`
|
|
|
11251
11407
|
this.onCompact(summary);
|
|
11252
11408
|
}
|
|
11253
11409
|
}
|
|
11254
|
-
extractFacts(turns) {
|
|
11255
|
-
const DRIVE = "(?:[a-zA-Z]:[\\\\/])?";
|
|
11256
|
-
const filePatterns = [
|
|
11257
|
-
new RegExp(`(?:Created|Updated|Written|Deleted|Moved) ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi"),
|
|
11258
|
-
new RegExp(`Replaced in ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi"),
|
|
11259
|
-
new RegExp(`Файл (?:создан|обновлён|записан|удалён|перемещён):? ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi"),
|
|
11260
|
-
new RegExp(`file (?:created|updated|written|deleted|moved):? ${DRIVE}([\\w./\\\\-]+\\.[a-z]+)`, "gi")
|
|
11261
|
-
];
|
|
11262
|
-
const newFiles = [];
|
|
11263
|
-
const newDecisions = [];
|
|
11264
|
-
const newErrors = [];
|
|
11265
|
-
for (const msg of turns) {
|
|
11266
|
-
const content = getMessageText(msg.content);
|
|
11267
|
-
if (msg.role === "user" && (content.startsWith("<system-summary>") || content.includes("[Compressed:"))) {
|
|
11268
|
-
continue;
|
|
11269
|
-
}
|
|
11270
|
-
if (msg.role === "tool") {
|
|
11271
|
-
for (const pattern of filePatterns) {
|
|
11272
|
-
for (const match of content.matchAll(pattern)) {
|
|
11273
|
-
newFiles.push(match[1]);
|
|
11274
|
-
}
|
|
11275
|
-
}
|
|
11276
|
-
if (content.includes("Plan:") && content.includes("[")) {
|
|
11277
|
-
const planLine = content.split(`
|
|
11278
|
-
`).find((line) => line.includes("Plan:"));
|
|
11279
|
-
if (planLine)
|
|
11280
|
-
newDecisions.push(planLine.trim());
|
|
11281
|
-
}
|
|
11282
|
-
}
|
|
11283
|
-
if (msg.role === "assistant") {
|
|
11284
|
-
if (content.includes("decided:") || content.includes("decision:")) {
|
|
11285
|
-
const line = content.split(`
|
|
11286
|
-
`).find((l) => l.includes("decided:") || l.includes("decision:"));
|
|
11287
|
-
if (line)
|
|
11288
|
-
newDecisions.push(line.trim().slice(0, 200));
|
|
11289
|
-
}
|
|
11290
|
-
}
|
|
11291
|
-
if (content.includes("Error:") || content.includes("failed") || content.includes("Ошибка:") || content.includes("не удалось")) {
|
|
11292
|
-
const line = content.split(`
|
|
11293
|
-
`).find((l) => l.includes("Error:") || l.includes("failed") || l.includes("Ошибка:") || l.includes("не удалось"));
|
|
11294
|
-
if (line)
|
|
11295
|
-
newErrors.push(line.trim().slice(0, 250));
|
|
11296
|
-
}
|
|
11297
|
-
}
|
|
11298
|
-
const dedup = (arr) => [...new Set(arr)];
|
|
11299
|
-
this.fileFacts = dedup([...this.fileFacts, ...newFiles]).slice(-ContextManager.MAX_FACTS);
|
|
11300
|
-
this.decisionFacts = dedup([...this.decisionFacts, ...newDecisions]).slice(-ContextManager.MAX_FACTS);
|
|
11301
|
-
this.errorFacts = dedup([...this.errorFacts, ...newErrors]).slice(-ContextManager.MAX_FACTS);
|
|
11302
|
-
}
|
|
11303
11410
|
updateSystemPrompt(content) {
|
|
11304
11411
|
const idx = this.messages.findIndex((m) => m.role === "system");
|
|
11305
11412
|
if (idx >= 0) {
|
|
@@ -11318,6 +11425,8 @@ ${lines.join(`
|
|
|
11318
11425
|
this.compactionCount = 0;
|
|
11319
11426
|
this.peakTokens = 0;
|
|
11320
11427
|
this.lastUserTask = "";
|
|
11428
|
+
this.lastUserFeedback = "";
|
|
11429
|
+
this.sessionMission = "";
|
|
11321
11430
|
}
|
|
11322
11431
|
getEstimatedTokens() {
|
|
11323
11432
|
return this.messages.reduce((sum, m) => sum + this.estimateMessageTokens(m), 0) + this.toolTokens;
|
|
@@ -11334,7 +11443,9 @@ ${lines.join(`
|
|
|
11334
11443
|
}
|
|
11335
11444
|
}
|
|
11336
11445
|
var COMPACTION_INTERVAL = 15, KEEP_LAST_N = 6;
|
|
11337
|
-
var init_manager = () => {
|
|
11446
|
+
var init_manager = __esm(() => {
|
|
11447
|
+
init_fact_extractor();
|
|
11448
|
+
});
|
|
11338
11449
|
|
|
11339
11450
|
// src/modules/hallucination/consistency.ts
|
|
11340
11451
|
class ConsistencyCheck {
|
|
@@ -11445,6 +11556,10 @@ function isFilePathLike(s) {
|
|
|
11445
11556
|
return false;
|
|
11446
11557
|
return FILE_EXTENSIONS.has(ext);
|
|
11447
11558
|
}
|
|
11559
|
+
const last = s.split(/[\\/]/).pop() ?? "";
|
|
11560
|
+
const lastDot = last.lastIndexOf(".");
|
|
11561
|
+
if (lastDot <= 0)
|
|
11562
|
+
return false;
|
|
11448
11563
|
return true;
|
|
11449
11564
|
}
|
|
11450
11565
|
function extractFileLikeTokens(text) {
|
|
@@ -15708,7 +15823,7 @@ class PlanCreator {
|
|
|
15708
15823
|
const hasActionWord = actionWords.some((w) => task.toLowerCase().includes(w));
|
|
15709
15824
|
return hasActionWord && words.length > 8;
|
|
15710
15825
|
}
|
|
15711
|
-
static createPlan(title, stepDescriptions, baseDir) {
|
|
15826
|
+
static createPlan(title, stepDescriptions, baseDir, kinds) {
|
|
15712
15827
|
const stepCount = stepDescriptions.length;
|
|
15713
15828
|
return {
|
|
15714
15829
|
id: generatePlanId(),
|
|
@@ -15716,7 +15831,8 @@ class PlanCreator {
|
|
|
15716
15831
|
steps: stepDescriptions.map((desc, i) => ({
|
|
15717
15832
|
id: i + 1,
|
|
15718
15833
|
description: desc,
|
|
15719
|
-
status: "pending"
|
|
15834
|
+
status: "pending",
|
|
15835
|
+
kind: kinds?.[i] ?? "create"
|
|
15720
15836
|
})),
|
|
15721
15837
|
createdAt: new Date().toISOString(),
|
|
15722
15838
|
baseDir
|
|
@@ -15730,7 +15846,8 @@ class PlanCreator {
|
|
|
15730
15846
|
const added = newSteps.map((desc, i) => ({
|
|
15731
15847
|
id: kept.length + i + 1,
|
|
15732
15848
|
description: desc,
|
|
15733
|
-
status: "pending"
|
|
15849
|
+
status: "pending",
|
|
15850
|
+
kind: "create"
|
|
15734
15851
|
}));
|
|
15735
15852
|
return {
|
|
15736
15853
|
id: plan.id,
|
|
@@ -15822,42 +15939,9 @@ class PlanTracker {
|
|
|
15822
15939
|
}
|
|
15823
15940
|
var init_tracker = () => {};
|
|
15824
15941
|
|
|
15825
|
-
// src/modules/execution/
|
|
15826
|
-
import {
|
|
15827
|
-
import {
|
|
15828
|
-
function findExistingFile(baseDir, filePath) {
|
|
15829
|
-
const direct = resolve17(baseDir, filePath);
|
|
15830
|
-
if (existsSync28(direct))
|
|
15831
|
-
return direct;
|
|
15832
|
-
const name = basename2(filePath).toLowerCase();
|
|
15833
|
-
const suffix = filePath.replace(/\\/g, "/").toLowerCase();
|
|
15834
|
-
let found = null;
|
|
15835
|
-
const walk = (dir, depth) => {
|
|
15836
|
-
if (found || depth > RESOLVE_MAX_DEPTH)
|
|
15837
|
-
return;
|
|
15838
|
-
let entries;
|
|
15839
|
-
try {
|
|
15840
|
-
entries = readdirSync8(dir, { withFileTypes: true });
|
|
15841
|
-
} catch {
|
|
15842
|
-
return;
|
|
15843
|
-
}
|
|
15844
|
-
for (const e of entries) {
|
|
15845
|
-
if (found)
|
|
15846
|
-
return;
|
|
15847
|
-
const full = join23(dir, e.name);
|
|
15848
|
-
if (e.isDirectory()) {
|
|
15849
|
-
if (SKIP_DIRS.has(e.name))
|
|
15850
|
-
continue;
|
|
15851
|
-
walk(full, depth + 1);
|
|
15852
|
-
} else if (e.name.toLowerCase() === name || full.replace(/\\/g, "/").toLowerCase().endsWith("/" + suffix)) {
|
|
15853
|
-
found = full;
|
|
15854
|
-
return;
|
|
15855
|
-
}
|
|
15856
|
-
}
|
|
15857
|
-
};
|
|
15858
|
-
walk(baseDir, 0);
|
|
15859
|
-
return found;
|
|
15860
|
-
}
|
|
15942
|
+
// src/modules/execution/audit-runners.ts
|
|
15943
|
+
import { readdirSync as readdirSync8 } from "fs";
|
|
15944
|
+
import { join as join23 } from "path";
|
|
15861
15945
|
function findTestFile(dir, depth = 0) {
|
|
15862
15946
|
if (depth > 5)
|
|
15863
15947
|
return null;
|
|
@@ -15932,6 +16016,76 @@ async function runTests(baseDir) {
|
|
|
15932
16016
|
command: "bun test"
|
|
15933
16017
|
};
|
|
15934
16018
|
}
|
|
16019
|
+
function parseTypecheckErrors(output) {
|
|
16020
|
+
const line = output.split(`
|
|
16021
|
+
`).find((l) => /error TS\d+/.test(l));
|
|
16022
|
+
return line ? line.trim().slice(0, 300) : null;
|
|
16023
|
+
}
|
|
16024
|
+
async function runTypecheck(baseDir) {
|
|
16025
|
+
const entry = processRegistry.start("npx --no-install tsc --noEmit --skipLibCheck", baseDir);
|
|
16026
|
+
const exited = await processRegistry.waitForExit(entry.id, 90000);
|
|
16027
|
+
const output = entry.log.join(`
|
|
16028
|
+
`);
|
|
16029
|
+
processRegistry.remove(entry.id);
|
|
16030
|
+
if (!exited)
|
|
16031
|
+
return null;
|
|
16032
|
+
return parseTypecheckErrors(output);
|
|
16033
|
+
}
|
|
16034
|
+
var SKIP_DIRS, TEST_EXT_RE, TEST_STEP_RE;
|
|
16035
|
+
var init_audit_runners = __esm(() => {
|
|
16036
|
+
init_bash();
|
|
16037
|
+
init_processes();
|
|
16038
|
+
SKIP_DIRS = new Set([
|
|
16039
|
+
"node_modules",
|
|
16040
|
+
".git",
|
|
16041
|
+
".mma",
|
|
16042
|
+
"dist",
|
|
16043
|
+
"build",
|
|
16044
|
+
"coverage",
|
|
16045
|
+
".next",
|
|
16046
|
+
".nuxt",
|
|
16047
|
+
"vendor"
|
|
16048
|
+
]);
|
|
16049
|
+
TEST_EXT_RE = /\.(test|spec)\.[jt]sx?$/i;
|
|
16050
|
+
TEST_STEP_RE = /\b(test(ing|s)?|тест(ы|ирование|ировать)?|провер\w*\s+тест|запустить\s+тест)\b|bun test|npm test|vitest|pytest|go test|jest|mocha/i;
|
|
16051
|
+
});
|
|
16052
|
+
|
|
16053
|
+
// src/modules/execution/auditor.ts
|
|
16054
|
+
import { existsSync as existsSync28, readdirSync as readdirSync9 } from "fs";
|
|
16055
|
+
import { resolve as resolve17, join as join24, basename as basename2 } from "path";
|
|
16056
|
+
function findExistingFile(baseDir, filePath) {
|
|
16057
|
+
const direct = resolve17(baseDir, filePath);
|
|
16058
|
+
if (existsSync28(direct))
|
|
16059
|
+
return direct;
|
|
16060
|
+
const name = basename2(filePath).toLowerCase();
|
|
16061
|
+
const suffix = filePath.replace(/\\/g, "/").toLowerCase();
|
|
16062
|
+
let found = null;
|
|
16063
|
+
const walk = (dir, depth) => {
|
|
16064
|
+
if (found || depth > RESOLVE_MAX_DEPTH)
|
|
16065
|
+
return;
|
|
16066
|
+
let entries;
|
|
16067
|
+
try {
|
|
16068
|
+
entries = readdirSync9(dir, { withFileTypes: true });
|
|
16069
|
+
} catch {
|
|
16070
|
+
return;
|
|
16071
|
+
}
|
|
16072
|
+
for (const e of entries) {
|
|
16073
|
+
if (found)
|
|
16074
|
+
return;
|
|
16075
|
+
const full = join24(dir, e.name);
|
|
16076
|
+
if (e.isDirectory()) {
|
|
16077
|
+
if (SKIP_DIRS.has(e.name))
|
|
16078
|
+
continue;
|
|
16079
|
+
walk(full, depth + 1);
|
|
16080
|
+
} else if (e.name.toLowerCase() === name || full.replace(/\\/g, "/").toLowerCase().endsWith("/" + suffix)) {
|
|
16081
|
+
found = full;
|
|
16082
|
+
return;
|
|
16083
|
+
}
|
|
16084
|
+
}
|
|
16085
|
+
};
|
|
16086
|
+
walk(baseDir, 0);
|
|
16087
|
+
return found;
|
|
16088
|
+
}
|
|
15935
16089
|
|
|
15936
16090
|
class Auditor {
|
|
15937
16091
|
baseDir;
|
|
@@ -15939,18 +16093,27 @@ class Auditor {
|
|
|
15939
16093
|
this.baseDir = baseDir;
|
|
15940
16094
|
}
|
|
15941
16095
|
async audit(plan) {
|
|
15942
|
-
const
|
|
15943
|
-
const
|
|
16096
|
+
const doneCreate = plan.steps.filter((s) => s.status === "done" && s.kind !== "delete");
|
|
16097
|
+
const doneDelete = plan.steps.filter((s) => s.status === "done" && s.kind === "delete");
|
|
16098
|
+
const createText = doneCreate.map((s) => s.description.replace(/\([^)]*\)/g, " ")).join(" ");
|
|
16099
|
+
const deleteText = doneDelete.map((s) => s.description.replace(/\([^)]*\)/g, " ")).join(" ");
|
|
16100
|
+
const createFiles = extractFileLikeTokens(stripUrls(createText));
|
|
16101
|
+
const deleteFiles = extractFileLikeTokens(stripUrls(deleteText));
|
|
15944
16102
|
const missingFiles = [];
|
|
15945
16103
|
const existingFiles = [];
|
|
15946
|
-
|
|
15947
|
-
|
|
15948
|
-
if (
|
|
16104
|
+
const leftoverFiles = [];
|
|
16105
|
+
for (const filePath of createFiles) {
|
|
16106
|
+
if (findExistingFile(this.baseDir, filePath)) {
|
|
15949
16107
|
existingFiles.push(filePath);
|
|
15950
16108
|
} else {
|
|
15951
16109
|
missingFiles.push(filePath);
|
|
15952
16110
|
}
|
|
15953
16111
|
}
|
|
16112
|
+
for (const filePath of deleteFiles) {
|
|
16113
|
+
if (findExistingFile(this.baseDir, filePath)) {
|
|
16114
|
+
leftoverFiles.push(filePath);
|
|
16115
|
+
}
|
|
16116
|
+
}
|
|
15954
16117
|
let testRun = null;
|
|
15955
16118
|
if (hasTestStep(plan) && findTestFile(this.baseDir)) {
|
|
15956
16119
|
try {
|
|
@@ -15959,17 +16122,27 @@ class Auditor {
|
|
|
15959
16122
|
testRun = null;
|
|
15960
16123
|
}
|
|
15961
16124
|
}
|
|
16125
|
+
let typecheckError = null;
|
|
16126
|
+
if (missingFiles.length === 0 && leftoverFiles.length === 0 && existsSync28(join24(this.baseDir, "tsconfig.json"))) {
|
|
16127
|
+
try {
|
|
16128
|
+
typecheckError = await runTypecheck(this.baseDir);
|
|
16129
|
+
} catch {
|
|
16130
|
+
typecheckError = null;
|
|
16131
|
+
}
|
|
16132
|
+
}
|
|
15962
16133
|
const doneSteps = plan.steps.filter((s) => s.status === "done").length;
|
|
15963
16134
|
const terminalSteps = plan.steps.filter((s) => s.status === "done" || s.status === "skipped").length;
|
|
15964
16135
|
const totalSteps = plan.steps.length;
|
|
16136
|
+
const auditedFiles = [...new Set([...createFiles, ...deleteFiles])];
|
|
15965
16137
|
let massEditWarning = null;
|
|
15966
|
-
if (
|
|
16138
|
+
if (auditedFiles.length > MASS_EDIT_THRESHOLD) {
|
|
15967
16139
|
massEditWarning = t("exec.mass_edit_warning", {
|
|
15968
|
-
count: String(
|
|
16140
|
+
count: String(auditedFiles.length)
|
|
15969
16141
|
});
|
|
15970
16142
|
}
|
|
15971
16143
|
const testsFailing = testRun !== null && testRun.failed > 0;
|
|
15972
|
-
const
|
|
16144
|
+
const typecheckFailing = typecheckError !== null;
|
|
16145
|
+
const passed = missingFiles.length === 0 && leftoverFiles.length === 0 && !testsFailing && !typecheckFailing;
|
|
15973
16146
|
const stepsPending = terminalSteps < totalSteps;
|
|
15974
16147
|
let summary;
|
|
15975
16148
|
if (missingFiles.length > 0) {
|
|
@@ -15978,6 +16151,12 @@ class Auditor {
|
|
|
15978
16151
|
total: totalSteps,
|
|
15979
16152
|
files: missingFiles.length
|
|
15980
16153
|
});
|
|
16154
|
+
} else if (leftoverFiles.length > 0) {
|
|
16155
|
+
summary = t("exec.audit_leftovers", {
|
|
16156
|
+
done: doneSteps,
|
|
16157
|
+
total: totalSteps,
|
|
16158
|
+
files: leftoverFiles.length
|
|
16159
|
+
});
|
|
15981
16160
|
} else if (testsFailing) {
|
|
15982
16161
|
summary = t("exec.audit_fail_tests", {
|
|
15983
16162
|
done: doneSteps,
|
|
@@ -15986,6 +16165,13 @@ class Auditor {
|
|
|
15986
16165
|
passed: String(testRun.passedCount),
|
|
15987
16166
|
detail: testRun.detail
|
|
15988
16167
|
});
|
|
16168
|
+
} else if (typecheckFailing) {
|
|
16169
|
+
summary = t("exec.audit_fail_typecheck", {
|
|
16170
|
+
done: doneSteps,
|
|
16171
|
+
total: totalSteps,
|
|
16172
|
+
missing: String(missingFiles.length),
|
|
16173
|
+
typeError: typecheckError
|
|
16174
|
+
});
|
|
15989
16175
|
} else if (stepsPending) {
|
|
15990
16176
|
summary = t("exec.audit_pending", {
|
|
15991
16177
|
done: doneSteps,
|
|
@@ -16001,38 +16187,27 @@ class Auditor {
|
|
|
16001
16187
|
return {
|
|
16002
16188
|
passed,
|
|
16003
16189
|
missingFiles,
|
|
16190
|
+
leftoverFiles,
|
|
16004
16191
|
createdFiles: existingFiles,
|
|
16005
16192
|
modifiedFiles: [],
|
|
16006
16193
|
summary,
|
|
16007
16194
|
massEditWarning,
|
|
16008
|
-
testRun
|
|
16195
|
+
testRun,
|
|
16196
|
+
typecheckError
|
|
16009
16197
|
};
|
|
16010
16198
|
}
|
|
16011
16199
|
}
|
|
16012
|
-
var MASS_EDIT_THRESHOLD = 10,
|
|
16200
|
+
var MASS_EDIT_THRESHOLD = 10, RESOLVE_MAX_DEPTH = 8;
|
|
16013
16201
|
var init_auditor = __esm(() => {
|
|
16014
16202
|
init_i18n();
|
|
16015
16203
|
init_js_identifiers();
|
|
16016
|
-
|
|
16017
|
-
|
|
16018
|
-
SKIP_DIRS = new Set([
|
|
16019
|
-
"node_modules",
|
|
16020
|
-
".git",
|
|
16021
|
-
".mma",
|
|
16022
|
-
"dist",
|
|
16023
|
-
"build",
|
|
16024
|
-
"coverage",
|
|
16025
|
-
".next",
|
|
16026
|
-
".nuxt",
|
|
16027
|
-
"vendor"
|
|
16028
|
-
]);
|
|
16029
|
-
TEST_EXT_RE = /\.(test|spec)\.[jt]sx?$/i;
|
|
16030
|
-
TEST_STEP_RE = /\b(test(ing|s)?|тест(ы|ирование|ировать)?|провер\w*\s+тест|запустить\s+тест)\b|bun test|npm test|vitest|pytest|go test|jest|mocha/i;
|
|
16204
|
+
init_audit_runners();
|
|
16205
|
+
init_audit_runners();
|
|
16031
16206
|
});
|
|
16032
16207
|
|
|
16033
16208
|
// src/modules/execution/plan-store.ts
|
|
16034
|
-
import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, mkdirSync as mkdirSync14, existsSync as existsSync29, readdirSync as
|
|
16035
|
-
import { join as
|
|
16209
|
+
import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, mkdirSync as mkdirSync14, existsSync as existsSync29, readdirSync as readdirSync10, rmSync } from "fs";
|
|
16210
|
+
import { join as join25 } from "path";
|
|
16036
16211
|
function readPlanFile(path, fallbackBaseDir) {
|
|
16037
16212
|
try {
|
|
16038
16213
|
const raw = readFileSync16(path, "utf-8");
|
|
@@ -16059,8 +16234,8 @@ function writePlanFile(path, plan) {
|
|
|
16059
16234
|
function listDir(dir, baseDir) {
|
|
16060
16235
|
if (!existsSync29(dir))
|
|
16061
16236
|
return [];
|
|
16062
|
-
const files =
|
|
16063
|
-
return files.map((f) => readPlanFile(
|
|
16237
|
+
const files = readdirSync10(dir).filter((f) => f.endsWith(".json"));
|
|
16238
|
+
return files.map((f) => readPlanFile(join25(dir, f), baseDir)).filter((p) => p !== null);
|
|
16064
16239
|
}
|
|
16065
16240
|
function toMeta(plan, status) {
|
|
16066
16241
|
return {
|
|
@@ -16081,21 +16256,21 @@ class PlanStore {
|
|
|
16081
16256
|
archiveDir;
|
|
16082
16257
|
legacyPath;
|
|
16083
16258
|
constructor(baseDir) {
|
|
16084
|
-
const mmaDir =
|
|
16259
|
+
const mmaDir = join25(baseDir, ".mma");
|
|
16085
16260
|
if (!existsSync29(mmaDir))
|
|
16086
16261
|
mkdirSync14(mmaDir, { recursive: true });
|
|
16087
16262
|
this.baseDir = baseDir;
|
|
16088
|
-
this.plansDir =
|
|
16089
|
-
this.draftsDir =
|
|
16090
|
-
this.archiveDir =
|
|
16091
|
-
this.legacyPath =
|
|
16263
|
+
this.plansDir = join25(mmaDir, "plans");
|
|
16264
|
+
this.draftsDir = join25(this.plansDir, "drafts");
|
|
16265
|
+
this.archiveDir = join25(this.plansDir, "archive");
|
|
16266
|
+
this.legacyPath = join25(mmaDir, LEGACY_FILE);
|
|
16092
16267
|
for (const dir of [this.plansDir, this.draftsDir, this.archiveDir]) {
|
|
16093
16268
|
if (!existsSync29(dir))
|
|
16094
16269
|
mkdirSync14(dir, { recursive: true });
|
|
16095
16270
|
}
|
|
16096
16271
|
}
|
|
16097
16272
|
activePath() {
|
|
16098
|
-
return
|
|
16273
|
+
return join25(this.plansDir, "active.json");
|
|
16099
16274
|
}
|
|
16100
16275
|
saveActive(plan) {
|
|
16101
16276
|
writePlanFile(this.activePath(), plan);
|
|
@@ -16125,14 +16300,14 @@ class PlanStore {
|
|
|
16125
16300
|
rmSync(p, { force: true });
|
|
16126
16301
|
}
|
|
16127
16302
|
saveDraft(plan) {
|
|
16128
|
-
writePlanFile(
|
|
16303
|
+
writePlanFile(join25(this.draftsDir, `${plan.id}.json`), plan);
|
|
16129
16304
|
}
|
|
16130
16305
|
loadDraft(id) {
|
|
16131
|
-
const p =
|
|
16306
|
+
const p = join25(this.draftsDir, `${id}.json`);
|
|
16132
16307
|
return existsSync29(p) ? readPlanFile(p, this.baseDir) : null;
|
|
16133
16308
|
}
|
|
16134
16309
|
removeDraft(id) {
|
|
16135
|
-
const p =
|
|
16310
|
+
const p = join25(this.draftsDir, `${id}.json`);
|
|
16136
16311
|
if (existsSync29(p))
|
|
16137
16312
|
rmSync(p, { force: true });
|
|
16138
16313
|
}
|
|
@@ -16140,7 +16315,7 @@ class PlanStore {
|
|
|
16140
16315
|
return listDir(this.draftsDir, this.baseDir);
|
|
16141
16316
|
}
|
|
16142
16317
|
archivePlan(plan) {
|
|
16143
|
-
writePlanFile(
|
|
16318
|
+
writePlanFile(join25(this.archiveDir, `${plan.id}.json`), plan);
|
|
16144
16319
|
this.removeDraft(plan.id);
|
|
16145
16320
|
const active = this.loadActive();
|
|
16146
16321
|
if (active && active.id === plan.id) {
|
|
@@ -16151,7 +16326,7 @@ class PlanStore {
|
|
|
16151
16326
|
return listDir(this.archiveDir, this.baseDir);
|
|
16152
16327
|
}
|
|
16153
16328
|
removeArchived(id) {
|
|
16154
|
-
const p =
|
|
16329
|
+
const p = join25(this.archiveDir, `${id}.json`);
|
|
16155
16330
|
if (existsSync29(p))
|
|
16156
16331
|
rmSync(p, { force: true });
|
|
16157
16332
|
}
|
|
@@ -16245,91 +16420,12 @@ var init_plan_coverage = __esm(() => {
|
|
|
16245
16420
|
]);
|
|
16246
16421
|
});
|
|
16247
16422
|
|
|
16248
|
-
// src/modules/execution/
|
|
16249
|
-
|
|
16250
|
-
|
|
16251
|
-
|
|
16252
|
-
|
|
16253
|
-
|
|
16254
|
-
tracker = null;
|
|
16255
|
-
verifier;
|
|
16256
|
-
stuckDetector;
|
|
16257
|
-
auditor;
|
|
16258
|
-
store;
|
|
16259
|
-
baseDir;
|
|
16260
|
-
lastRecoveryIteration = -STUCK_RECOVERY_COOLDOWN;
|
|
16261
|
-
consecutivePlanWarnings = 0;
|
|
16262
|
-
lastStepId = -1;
|
|
16263
|
-
depsGateHints = new Map;
|
|
16264
|
-
_auditSkipsRemaining = 0;
|
|
16265
|
-
stuckNotified = false;
|
|
16266
|
-
pendingMessages = [];
|
|
16267
|
-
constructor(baseDir, stuckThreshold = 6) {
|
|
16268
|
-
this.baseDir = baseDir;
|
|
16269
|
-
this.verifier = new StepVerifier(baseDir);
|
|
16270
|
-
this.stuckDetector = new StuckDetector(stuckThreshold);
|
|
16271
|
-
this.auditor = new Auditor(baseDir);
|
|
16272
|
-
this.store = new PlanStore(baseDir);
|
|
16273
|
-
}
|
|
16274
|
-
setPlan(plan) {
|
|
16275
|
-
this.tracker = new PlanTracker(plan);
|
|
16276
|
-
this.store.saveActive(plan);
|
|
16277
|
-
this._auditSkipsRemaining = 0;
|
|
16278
|
-
}
|
|
16279
|
-
restorePlan() {
|
|
16280
|
-
const plan = this.store.loadActive();
|
|
16281
|
-
if (!plan)
|
|
16282
|
-
return false;
|
|
16283
|
-
this.tracker = new PlanTracker(plan);
|
|
16284
|
-
this.tracker.syncCurrentStep();
|
|
16285
|
-
this._auditSkipsRemaining = 1;
|
|
16286
|
-
return true;
|
|
16287
|
-
}
|
|
16288
|
-
getTracker() {
|
|
16289
|
-
return this.tracker;
|
|
16290
|
-
}
|
|
16291
|
-
getStore() {
|
|
16292
|
-
return this.store;
|
|
16293
|
-
}
|
|
16294
|
-
getStuckDetector() {
|
|
16295
|
-
return this.stuckDetector;
|
|
16296
|
-
}
|
|
16297
|
-
async runFinalAudit() {
|
|
16298
|
-
if (!this.tracker)
|
|
16299
|
-
return null;
|
|
16300
|
-
if (this._auditSkipsRemaining > 0) {
|
|
16301
|
-
this._auditSkipsRemaining--;
|
|
16302
|
-
return null;
|
|
16303
|
-
}
|
|
16304
|
-
const plan = this.tracker.getPlan();
|
|
16305
|
-
const audit = await this.auditor.audit(plan);
|
|
16306
|
-
const pendingSteps = plan.steps.flatMap((s) => s.status !== "done" && s.status !== "skipped" ? [`${s.id}. ${s.description}`] : []);
|
|
16307
|
-
const done = plan.steps.filter((s) => s.status === "done").length;
|
|
16308
|
-
const passed = audit.passed && pendingSteps.length === 0;
|
|
16309
|
-
return {
|
|
16310
|
-
passed,
|
|
16311
|
-
done,
|
|
16312
|
-
total: plan.steps.length,
|
|
16313
|
-
pendingSteps,
|
|
16314
|
-
missingFiles: audit.missingFiles,
|
|
16315
|
-
summary: audit.summary
|
|
16316
|
-
};
|
|
16317
|
-
}
|
|
16318
|
-
getSystemPromptBlock() {
|
|
16319
|
-
if (!this.tracker)
|
|
16320
|
-
return null;
|
|
16321
|
-
return {
|
|
16322
|
-
content: this.tracker.toPromptBlock(),
|
|
16323
|
-
priority: "critical",
|
|
16324
|
-
essential: true,
|
|
16325
|
-
estimatedTokens: 200
|
|
16326
|
-
};
|
|
16327
|
-
}
|
|
16328
|
-
getToolDefinitions() {
|
|
16329
|
-
return [
|
|
16330
|
-
{
|
|
16331
|
-
name: "plan",
|
|
16332
|
-
description: `Create, update, show, abort, list, switch, or re-plan multi-step plans.
|
|
16423
|
+
// src/modules/execution/plan-tool.ts
|
|
16424
|
+
function createPlanToolDefinitions(deps) {
|
|
16425
|
+
return [
|
|
16426
|
+
{
|
|
16427
|
+
name: "plan",
|
|
16428
|
+
description: `Create, update, show, abort, list, switch, or re-plan multi-step plans.
|
|
16333
16429
|
|
|
16334
16430
|
Actions:
|
|
16335
16431
|
- create: Start a new plan. Previous active plan is auto-preserved: incomplete → draft, complete → archive.
|
|
@@ -16346,198 +16442,222 @@ Write CONCRETE steps with exact file paths and commands:
|
|
|
16346
16442
|
- Specify WHICH CLI commands to run with exact arguments
|
|
16347
16443
|
- Each step should include at least one file extension (.ts, .tsx, .json, etc.) or a command verb (install, create, build, run, add, init)
|
|
16348
16444
|
- Good: "Создать src/components/Header.tsx с навигацией и логотипом"
|
|
16349
|
-
- Bad: "Настройка проекта" (too vague — what exactly needs to be configured?)
|
|
16350
|
-
|
|
16351
|
-
|
|
16352
|
-
|
|
16353
|
-
|
|
16354
|
-
|
|
16355
|
-
|
|
16356
|
-
|
|
16357
|
-
|
|
16358
|
-
|
|
16359
|
-
|
|
16360
|
-
|
|
16361
|
-
|
|
16362
|
-
|
|
16363
|
-
|
|
16364
|
-
|
|
16365
|
-
|
|
16366
|
-
|
|
16367
|
-
|
|
16368
|
-
|
|
16369
|
-
|
|
16370
|
-
|
|
16445
|
+
- Bad: "Настройка проекта" (too vague — what exactly needs to be configured?)
|
|
16446
|
+
- For steps that REMOVE files, pass kinds: ["delete"] aligned with steps (the done-gate verifies those files are gone instead of demanding they exist).`,
|
|
16447
|
+
parameters: {
|
|
16448
|
+
type: "object",
|
|
16449
|
+
properties: {
|
|
16450
|
+
action: {
|
|
16451
|
+
type: "string",
|
|
16452
|
+
enum: [
|
|
16453
|
+
"create",
|
|
16454
|
+
"update",
|
|
16455
|
+
"show",
|
|
16456
|
+
"abort",
|
|
16457
|
+
"list",
|
|
16458
|
+
"switch",
|
|
16459
|
+
"re-plan"
|
|
16460
|
+
]
|
|
16461
|
+
},
|
|
16462
|
+
title: { type: "string" },
|
|
16463
|
+
steps: { type: "array", items: { type: "string" } },
|
|
16464
|
+
kinds: {
|
|
16465
|
+
type: "array",
|
|
16466
|
+
items: { type: "string", enum: ["create", "delete"] },
|
|
16467
|
+
description: 'Optional per-step intent, aligned 1:1 with steps. Pass "delete" for steps whose purpose is REMOVING files — the done-gate then requires those files to be GONE (not present). Defaults to "create".'
|
|
16371
16468
|
},
|
|
16372
|
-
|
|
16469
|
+
step: { type: "number" },
|
|
16470
|
+
status: { type: "string", enum: ["done", "failed", "skipped"] },
|
|
16471
|
+
note: { type: "string" },
|
|
16472
|
+
id: { type: "string", description: "Plan id (for switch action)" }
|
|
16373
16473
|
},
|
|
16374
|
-
|
|
16375
|
-
|
|
16376
|
-
|
|
16377
|
-
|
|
16378
|
-
|
|
16379
|
-
|
|
16380
|
-
|
|
16381
|
-
|
|
16382
|
-
|
|
16383
|
-
|
|
16384
|
-
|
|
16385
|
-
});
|
|
16386
|
-
return {
|
|
16387
|
-
|
|
16388
|
-
|
|
16474
|
+
required: ["action"]
|
|
16475
|
+
},
|
|
16476
|
+
handler: async (_ctx, args) => {
|
|
16477
|
+
const action = String(args.action);
|
|
16478
|
+
if (action === "list") {
|
|
16479
|
+
const metas = deps.store.listAll();
|
|
16480
|
+
if (metas.length === 0) {
|
|
16481
|
+
return { success: true, output: t("plan.list_empty") };
|
|
16482
|
+
}
|
|
16483
|
+
const lines = metas.map((m) => {
|
|
16484
|
+
const icon = m.status === "active" ? "[*]" : m.status === "draft" ? "[ ]" : "[-]";
|
|
16485
|
+
const namePart = m.name ? ` (${m.name})` : "";
|
|
16486
|
+
return `${icon} ${m.id}${namePart} — ${m.title} ${m.doneCount}/${m.stepCount}`;
|
|
16487
|
+
});
|
|
16488
|
+
return {
|
|
16489
|
+
success: true,
|
|
16490
|
+
output: `${t("plan.list_header")}
|
|
16389
16491
|
${lines.join(`
|
|
16390
16492
|
`)}`
|
|
16493
|
+
};
|
|
16494
|
+
}
|
|
16495
|
+
if (action === "switch") {
|
|
16496
|
+
const planId = String(args.id || "");
|
|
16497
|
+
if (!planId) {
|
|
16498
|
+
return { success: false, output: t("plan.switch_no_id") };
|
|
16499
|
+
}
|
|
16500
|
+
const found = deps.store.find(planId);
|
|
16501
|
+
if (!found) {
|
|
16502
|
+
return {
|
|
16503
|
+
success: false,
|
|
16504
|
+
output: t("plan.not_found", { id: planId })
|
|
16391
16505
|
};
|
|
16392
16506
|
}
|
|
16393
|
-
|
|
16394
|
-
|
|
16395
|
-
|
|
16396
|
-
|
|
16507
|
+
deps.preserveActive();
|
|
16508
|
+
if (found.status === "draft") {
|
|
16509
|
+
deps.store.removeDraft(found.plan.id);
|
|
16510
|
+
} else if (found.status === "archived") {
|
|
16511
|
+
deps.store.removeArchived(found.plan.id);
|
|
16512
|
+
}
|
|
16513
|
+
deps.setPlan(found.plan);
|
|
16514
|
+
const display = PlanCreator.toPromptBlock(found.plan, 0);
|
|
16515
|
+
return {
|
|
16516
|
+
success: true,
|
|
16517
|
+
output: t("plan.switched", {
|
|
16518
|
+
id: found.plan.id,
|
|
16519
|
+
title: found.plan.title
|
|
16520
|
+
}),
|
|
16521
|
+
display
|
|
16522
|
+
};
|
|
16523
|
+
}
|
|
16524
|
+
if (action === "re-plan") {
|
|
16525
|
+
if (!deps.trackerRef.current) {
|
|
16526
|
+
return { success: false, output: t("plan.no_active") };
|
|
16527
|
+
}
|
|
16528
|
+
const newSteps = Array.isArray(args.steps) ? args.steps.map(String) : [];
|
|
16529
|
+
if (newSteps.length === 0) {
|
|
16530
|
+
return { success: false, output: t("plan.replan_no_steps") };
|
|
16531
|
+
}
|
|
16532
|
+
const tracker = deps.trackerRef.current;
|
|
16533
|
+
const oldPlan = tracker.getPlan();
|
|
16534
|
+
const replanned = PlanCreator.replan(oldPlan, newSteps, args.title ? String(args.title) : undefined);
|
|
16535
|
+
const keptCount = replanned.steps.length - newSteps.length;
|
|
16536
|
+
deps.setPlan(replanned);
|
|
16537
|
+
const display = PlanCreator.toPromptBlock(replanned, keptCount);
|
|
16538
|
+
return {
|
|
16539
|
+
success: true,
|
|
16540
|
+
output: t("plan.replanned", {
|
|
16541
|
+
kept: String(keptCount),
|
|
16542
|
+
steps: String(newSteps.length)
|
|
16543
|
+
}),
|
|
16544
|
+
display
|
|
16545
|
+
};
|
|
16546
|
+
}
|
|
16547
|
+
if (action === "create") {
|
|
16548
|
+
const title = String(args.title || "Task Plan");
|
|
16549
|
+
const steps = Array.isArray(args.steps) ? args.steps.map(String) : [];
|
|
16550
|
+
if (steps.length === 0) {
|
|
16551
|
+
return { success: false, output: t("plan.no_steps") };
|
|
16552
|
+
}
|
|
16553
|
+
const parsed = deps.parseKinds(args, steps);
|
|
16554
|
+
if (parsed.error) {
|
|
16555
|
+
return { success: false, output: parsed.error };
|
|
16556
|
+
}
|
|
16557
|
+
deps.preserveActive();
|
|
16558
|
+
const plan = PlanCreator.createPlan(title, steps, deps.baseDir, parsed.kinds ?? []);
|
|
16559
|
+
deps.setPlan(plan);
|
|
16560
|
+
const display = PlanCreator.toPromptBlock(plan, 0);
|
|
16561
|
+
let output = t("plan.created", {
|
|
16562
|
+
title,
|
|
16563
|
+
steps: String(steps.length)
|
|
16564
|
+
});
|
|
16565
|
+
let displayOut = display;
|
|
16566
|
+
const taskText = deps.getTaskText(_ctx);
|
|
16567
|
+
if (taskText) {
|
|
16568
|
+
const { missing } = checkPlanCoverage(taskText, steps);
|
|
16569
|
+
if (missing.length > 0) {
|
|
16570
|
+
const warn = t("plan.coverage_warning", {
|
|
16571
|
+
missing: missing.join(", ")
|
|
16572
|
+
});
|
|
16573
|
+
output += `
|
|
16574
|
+
⚠️ ${warn}`;
|
|
16575
|
+
displayOut = `${display}
|
|
16576
|
+
⚠️ ${warn}`;
|
|
16397
16577
|
}
|
|
16398
|
-
|
|
16578
|
+
}
|
|
16579
|
+
return {
|
|
16580
|
+
success: true,
|
|
16581
|
+
output,
|
|
16582
|
+
display: displayOut
|
|
16583
|
+
};
|
|
16584
|
+
}
|
|
16585
|
+
if (action === "show") {
|
|
16586
|
+
const planId = args.id ? String(args.id) : "";
|
|
16587
|
+
if (planId && planId !== deps.trackerRef.current?.getPlan()?.id) {
|
|
16588
|
+
const found = deps.store.find(planId);
|
|
16399
16589
|
if (!found) {
|
|
16400
16590
|
return {
|
|
16401
16591
|
success: false,
|
|
16402
16592
|
output: t("plan.not_found", { id: planId })
|
|
16403
16593
|
};
|
|
16404
16594
|
}
|
|
16405
|
-
|
|
16406
|
-
if (found.status === "draft") {
|
|
16407
|
-
this.store.removeDraft(found.plan.id);
|
|
16408
|
-
} else if (found.status === "archived") {
|
|
16409
|
-
this.store.removeArchived(found.plan.id);
|
|
16410
|
-
}
|
|
16411
|
-
this.setPlan(found.plan);
|
|
16412
|
-
const display = PlanCreator.toPromptBlock(found.plan, 0);
|
|
16595
|
+
const display2 = PlanCreator.toPromptBlock(found.plan, 0);
|
|
16413
16596
|
return {
|
|
16414
16597
|
success: true,
|
|
16415
|
-
output: t("plan.
|
|
16416
|
-
|
|
16417
|
-
|
|
16418
|
-
}),
|
|
16419
|
-
display
|
|
16598
|
+
output: `${t("plan.show_header")}
|
|
16599
|
+
${display2}`,
|
|
16600
|
+
display: display2
|
|
16420
16601
|
};
|
|
16421
16602
|
}
|
|
16422
|
-
if (
|
|
16423
|
-
|
|
16424
|
-
return { success: false, output: t("plan.no_active") };
|
|
16425
|
-
}
|
|
16426
|
-
const newSteps = Array.isArray(args.steps) ? args.steps.map(String) : [];
|
|
16427
|
-
if (newSteps.length === 0) {
|
|
16428
|
-
return { success: false, output: t("plan.replan_no_steps") };
|
|
16429
|
-
}
|
|
16430
|
-
const oldPlan = this.tracker.getPlan();
|
|
16431
|
-
const replanned = PlanCreator.replan(oldPlan, newSteps, args.title ? String(args.title) : undefined);
|
|
16432
|
-
const keptCount = replanned.steps.length - newSteps.length;
|
|
16433
|
-
this.setPlan(replanned);
|
|
16434
|
-
const display = PlanCreator.toPromptBlock(replanned, keptCount);
|
|
16435
|
-
return {
|
|
16436
|
-
success: true,
|
|
16437
|
-
output: t("plan.replanned", {
|
|
16438
|
-
kept: String(keptCount),
|
|
16439
|
-
steps: String(newSteps.length)
|
|
16440
|
-
}),
|
|
16441
|
-
display
|
|
16442
|
-
};
|
|
16603
|
+
if (!deps.trackerRef.current) {
|
|
16604
|
+
return { success: false, output: t("plan.no_active") };
|
|
16443
16605
|
}
|
|
16444
|
-
|
|
16445
|
-
|
|
16446
|
-
|
|
16447
|
-
|
|
16448
|
-
|
|
16449
|
-
|
|
16450
|
-
|
|
16451
|
-
|
|
16452
|
-
|
|
16453
|
-
|
|
16454
|
-
|
|
16455
|
-
|
|
16456
|
-
|
|
16457
|
-
}
|
|
16458
|
-
let displayOut = display;
|
|
16459
|
-
const taskText = this.getTaskText(_ctx);
|
|
16460
|
-
if (taskText) {
|
|
16461
|
-
const { missing } = checkPlanCoverage(taskText, steps);
|
|
16462
|
-
if (missing.length > 0) {
|
|
16463
|
-
const warn = t("plan.coverage_warning", {
|
|
16464
|
-
missing: missing.join(", ")
|
|
16465
|
-
});
|
|
16466
|
-
output += `
|
|
16467
|
-
⚠️ ${warn}`;
|
|
16468
|
-
displayOut = `${display}
|
|
16469
|
-
⚠️ ${warn}`;
|
|
16470
|
-
}
|
|
16471
|
-
}
|
|
16472
|
-
return {
|
|
16473
|
-
success: true,
|
|
16474
|
-
output,
|
|
16475
|
-
display: displayOut
|
|
16476
|
-
};
|
|
16606
|
+
const display = deps.trackerRef.current.toPromptBlock();
|
|
16607
|
+
return {
|
|
16608
|
+
success: true,
|
|
16609
|
+
output: `${t("plan.show_header")}
|
|
16610
|
+
${display}`,
|
|
16611
|
+
display
|
|
16612
|
+
};
|
|
16613
|
+
}
|
|
16614
|
+
if (action === "update" && args.step && deps.trackerRef.current) {
|
|
16615
|
+
const tracker = deps.trackerRef.current;
|
|
16616
|
+
const stepId = Number(args.step);
|
|
16617
|
+
const target = tracker.getStep(stepId);
|
|
16618
|
+
if (!target) {
|
|
16619
|
+
return { success: false, output: t("plan.step_not_found") };
|
|
16477
16620
|
}
|
|
16478
|
-
|
|
16479
|
-
|
|
16480
|
-
|
|
16481
|
-
|
|
16482
|
-
if (!found) {
|
|
16483
|
-
return {
|
|
16484
|
-
success: false,
|
|
16485
|
-
output: t("plan.not_found", { id: planId })
|
|
16486
|
-
};
|
|
16487
|
-
}
|
|
16488
|
-
const display2 = PlanCreator.toPromptBlock(found.plan, 0);
|
|
16489
|
-
return {
|
|
16490
|
-
success: true,
|
|
16491
|
-
output: `${t("plan.show_header")}
|
|
16492
|
-
${display2}`,
|
|
16493
|
-
display: display2
|
|
16494
|
-
};
|
|
16495
|
-
}
|
|
16496
|
-
if (!this.tracker) {
|
|
16497
|
-
return { success: false, output: t("plan.no_active") };
|
|
16498
|
-
}
|
|
16499
|
-
const display = this.tracker.toPromptBlock();
|
|
16621
|
+
const status = String(args.status || "done");
|
|
16622
|
+
if (status === "done" && target.status === "done") {
|
|
16623
|
+
const progress2 = tracker.getProgressString();
|
|
16624
|
+
const display2 = tracker.toPromptBlock();
|
|
16500
16625
|
return {
|
|
16501
16626
|
success: true,
|
|
16502
|
-
output: `${t("plan.
|
|
16503
|
-
|
|
16504
|
-
|
|
16627
|
+
output: `${t("plan.step_already_done", {
|
|
16628
|
+
step: String(stepId)
|
|
16629
|
+
})}
|
|
16630
|
+
${progress2}`,
|
|
16631
|
+
display: display2
|
|
16505
16632
|
};
|
|
16506
16633
|
}
|
|
16507
|
-
if (
|
|
16508
|
-
const
|
|
16509
|
-
|
|
16510
|
-
if (!target) {
|
|
16511
|
-
return { success: false, output: t("plan.step_not_found") };
|
|
16512
|
-
}
|
|
16513
|
-
const status = String(args.status || "done");
|
|
16514
|
-
if (status === "done" && target.status === "done") {
|
|
16515
|
-
const progress2 = this.tracker.getProgressString();
|
|
16516
|
-
const display2 = this.tracker.toPromptBlock();
|
|
16634
|
+
if (status === "done") {
|
|
16635
|
+
const blockers = tracker.getPlan().steps.filter((s) => s.id < stepId && s.status !== "done" && s.status !== "skipped");
|
|
16636
|
+
if (blockers.length > 0) {
|
|
16517
16637
|
return {
|
|
16518
|
-
success:
|
|
16519
|
-
output:
|
|
16520
|
-
step: String(stepId)
|
|
16521
|
-
|
|
16522
|
-
|
|
16523
|
-
|
|
16638
|
+
success: false,
|
|
16639
|
+
output: t("plan.order_blocked", {
|
|
16640
|
+
step: String(stepId),
|
|
16641
|
+
first: String(blockers[0].id),
|
|
16642
|
+
desc: blockers[0].description
|
|
16643
|
+
})
|
|
16524
16644
|
};
|
|
16525
16645
|
}
|
|
16526
|
-
|
|
16527
|
-
|
|
16528
|
-
|
|
16646
|
+
}
|
|
16647
|
+
if (status === "done") {
|
|
16648
|
+
if (target.kind === "delete") {
|
|
16649
|
+
const leftovers = deps.stillExistingDeliverables(target);
|
|
16650
|
+
if (leftovers.length > 0) {
|
|
16529
16651
|
return {
|
|
16530
16652
|
success: false,
|
|
16531
|
-
output: t("plan.
|
|
16653
|
+
output: t("plan.deliverables_remain", {
|
|
16532
16654
|
step: String(stepId),
|
|
16533
|
-
|
|
16534
|
-
desc: blockers[0].description
|
|
16655
|
+
files: leftovers.join(", ")
|
|
16535
16656
|
})
|
|
16536
16657
|
};
|
|
16537
16658
|
}
|
|
16538
|
-
}
|
|
16539
|
-
|
|
16540
|
-
const missing = this.missingStepDeliverables(target);
|
|
16659
|
+
} else {
|
|
16660
|
+
const missing = deps.missingStepDeliverables(target);
|
|
16541
16661
|
if (missing.length > 0) {
|
|
16542
16662
|
return {
|
|
16543
16663
|
success: false,
|
|
@@ -16548,373 +16668,559 @@ ${progress2}`,
|
|
|
16548
16668
|
};
|
|
16549
16669
|
}
|
|
16550
16670
|
}
|
|
16551
|
-
this.tracker.updateStepStatus(stepId, status);
|
|
16552
|
-
if (args.note)
|
|
16553
|
-
this.tracker.addNote(Number(args.step), String(args.note));
|
|
16554
|
-
this.tracker.syncCurrentStep();
|
|
16555
|
-
this.store.saveActive(this.tracker.getPlan());
|
|
16556
|
-
const progress = this.tracker.getProgressString();
|
|
16557
|
-
const display = this.tracker.toPromptBlock();
|
|
16558
|
-
_ctx.sessionLog?.plan("step-update", `${t("plan.step_status", { step: String(args.step), status: String(args.status || "done") })} | ${progress} | current: step ${this.tracker.getCurrentStepIndex() + 1}`);
|
|
16559
|
-
return {
|
|
16560
|
-
success: true,
|
|
16561
|
-
output: `${t("plan.step_status", { step: String(args.step), status: String(args.status || "done") })}
|
|
16562
|
-
${progress}`,
|
|
16563
|
-
display
|
|
16564
|
-
};
|
|
16565
16671
|
}
|
|
16566
|
-
|
|
16567
|
-
|
|
16568
|
-
|
|
16569
|
-
|
|
16570
|
-
|
|
16571
|
-
|
|
16572
|
-
|
|
16573
|
-
|
|
16574
|
-
|
|
16575
|
-
|
|
16576
|
-
|
|
16577
|
-
|
|
16578
|
-
|
|
16579
|
-
|
|
16580
|
-
|
|
16581
|
-
|
|
16582
|
-
|
|
16583
|
-
|
|
16584
|
-
|
|
16585
|
-
|
|
16586
|
-
|
|
16587
|
-
|
|
16588
|
-
return { success: true, output: t("plan.aborted") };
|
|
16589
|
-
}
|
|
16590
|
-
if (!this.tracker) {
|
|
16591
|
-
return { success: false, output: t("plan.no_active") };
|
|
16672
|
+
tracker.updateStepStatus(stepId, status);
|
|
16673
|
+
if (args.note)
|
|
16674
|
+
tracker.addNote(Number(args.step), String(args.note));
|
|
16675
|
+
tracker.syncCurrentStep();
|
|
16676
|
+
deps.store.saveActive(tracker.getPlan());
|
|
16677
|
+
const progress = tracker.getProgressString();
|
|
16678
|
+
const display = tracker.toPromptBlock();
|
|
16679
|
+
_ctx.sessionLog?.plan("step-update", `${t("plan.step_status", { step: String(args.step), status: String(args.status || "done") })} | ${progress} | current: step ${tracker.getCurrentStepIndex() + 1}`);
|
|
16680
|
+
return {
|
|
16681
|
+
success: true,
|
|
16682
|
+
output: `${t("plan.step_status", { step: String(args.step), status: String(args.status || "done") })}
|
|
16683
|
+
${progress}`,
|
|
16684
|
+
display
|
|
16685
|
+
};
|
|
16686
|
+
}
|
|
16687
|
+
if (action === "update" && Array.isArray(args.steps) && args.steps.length > 0 && deps.trackerRef.current) {
|
|
16688
|
+
const tracker = deps.trackerRef.current;
|
|
16689
|
+
const title = String(args.title || tracker.getPlan().title);
|
|
16690
|
+
const steps = args.steps.map(String);
|
|
16691
|
+
const parsed = deps.parseKinds(args, steps);
|
|
16692
|
+
if (parsed.error) {
|
|
16693
|
+
return { success: false, output: parsed.error };
|
|
16592
16694
|
}
|
|
16695
|
+
const plan = PlanCreator.createPlan(title, steps, deps.baseDir, parsed.kinds ?? []);
|
|
16696
|
+
deps.setPlan(plan);
|
|
16697
|
+
const display = PlanCreator.toPromptBlock(plan, 0);
|
|
16698
|
+
const output = t("plan.updated", {
|
|
16699
|
+
title,
|
|
16700
|
+
steps: String(steps.length)
|
|
16701
|
+
});
|
|
16593
16702
|
return {
|
|
16594
|
-
success:
|
|
16595
|
-
output
|
|
16703
|
+
success: true,
|
|
16704
|
+
output,
|
|
16705
|
+
display
|
|
16596
16706
|
};
|
|
16597
16707
|
}
|
|
16598
|
-
|
|
16599
|
-
|
|
16600
|
-
|
|
16601
|
-
|
|
16602
|
-
|
|
16603
|
-
|
|
16604
|
-
|
|
16605
|
-
|
|
16606
|
-
|
|
16607
|
-
|
|
16608
|
-
|
|
16708
|
+
if (action === "abort") {
|
|
16709
|
+
const tracker = deps.trackerRef.current;
|
|
16710
|
+
if (tracker) {
|
|
16711
|
+
deps.store.archivePlan(tracker.getPlan());
|
|
16712
|
+
}
|
|
16713
|
+
deps.trackerRef.current = null;
|
|
16714
|
+
deps.store.clearActive();
|
|
16715
|
+
return { success: true, output: t("plan.aborted") };
|
|
16716
|
+
}
|
|
16717
|
+
if (!deps.trackerRef.current) {
|
|
16718
|
+
return { success: false, output: t("plan.no_active") };
|
|
16719
|
+
}
|
|
16720
|
+
return {
|
|
16721
|
+
success: false,
|
|
16722
|
+
output: t("plan.unknown_action", { action })
|
|
16723
|
+
};
|
|
16724
|
+
}
|
|
16725
|
+
},
|
|
16726
|
+
{
|
|
16727
|
+
name: "todo",
|
|
16728
|
+
description: "Manage sub-tasks within current plan step. Use to break down complex steps into smaller tasks.",
|
|
16729
|
+
parameters: {
|
|
16730
|
+
type: "object",
|
|
16731
|
+
properties: {
|
|
16732
|
+
action: { type: "string", enum: ["add", "done", "list"] },
|
|
16733
|
+
items: { type: "array", items: { type: "string" } }
|
|
16609
16734
|
},
|
|
16610
|
-
|
|
16611
|
-
|
|
16612
|
-
|
|
16735
|
+
required: ["action"]
|
|
16736
|
+
},
|
|
16737
|
+
handler: async (_ctx, args) => {
|
|
16738
|
+
if (!deps.trackerRef.current) {
|
|
16739
|
+
return { success: false, output: t("plan.no_active") };
|
|
16740
|
+
}
|
|
16741
|
+
const tracker = deps.trackerRef.current;
|
|
16742
|
+
const action = String(args.action);
|
|
16743
|
+
const currentStep = tracker.getCurrentStep();
|
|
16744
|
+
if (!currentStep) {
|
|
16745
|
+
return { success: false, output: t("plan.step_not_found") };
|
|
16746
|
+
}
|
|
16747
|
+
if (action === "add" && Array.isArray(args.items)) {
|
|
16748
|
+
const items = args.items.map(String);
|
|
16749
|
+
const existing = currentStep.subtasks ?? [];
|
|
16750
|
+
let nextId = existing.reduce((m, s) => Math.max(m, s.id), 0) + 1;
|
|
16751
|
+
for (const item of items) {
|
|
16752
|
+
existing.push({ id: nextId++, text: item, done: false });
|
|
16613
16753
|
}
|
|
16614
|
-
|
|
16615
|
-
|
|
16616
|
-
|
|
16617
|
-
|
|
16754
|
+
currentStep.subtasks = existing;
|
|
16755
|
+
deps.store.saveActive(tracker.getPlan());
|
|
16756
|
+
const display = tracker.toPromptBlock();
|
|
16757
|
+
return {
|
|
16758
|
+
success: true,
|
|
16759
|
+
output: t("todo.added", {
|
|
16760
|
+
count: String(items.length),
|
|
16761
|
+
items: items.join(", ")
|
|
16762
|
+
}),
|
|
16763
|
+
display
|
|
16764
|
+
};
|
|
16765
|
+
}
|
|
16766
|
+
if (action === "done") {
|
|
16767
|
+
const items = Array.isArray(args.items) ? args.items.map(String) : [];
|
|
16768
|
+
if (items.length === 0) {
|
|
16769
|
+
return {
|
|
16770
|
+
success: false,
|
|
16771
|
+
output: t("todo.no_items")
|
|
16772
|
+
};
|
|
16618
16773
|
}
|
|
16619
|
-
|
|
16620
|
-
|
|
16621
|
-
|
|
16622
|
-
|
|
16623
|
-
|
|
16624
|
-
|
|
16774
|
+
const subs = currentStep.subtasks ?? [];
|
|
16775
|
+
let marked = 0;
|
|
16776
|
+
for (const item of items) {
|
|
16777
|
+
const sub = subs.find((s) => !s.done && s.text.toLowerCase() === item.toLowerCase());
|
|
16778
|
+
if (sub) {
|
|
16779
|
+
sub.done = true;
|
|
16780
|
+
marked++;
|
|
16625
16781
|
}
|
|
16626
|
-
|
|
16627
|
-
|
|
16628
|
-
const display = this.tracker.toPromptBlock();
|
|
16782
|
+
}
|
|
16783
|
+
if (marked === 0) {
|
|
16629
16784
|
return {
|
|
16630
|
-
success:
|
|
16631
|
-
output: t("todo.
|
|
16632
|
-
count: String(items.length),
|
|
16785
|
+
success: false,
|
|
16786
|
+
output: t("todo.subtask_not_found", {
|
|
16633
16787
|
items: items.join(", ")
|
|
16634
|
-
})
|
|
16635
|
-
display
|
|
16788
|
+
})
|
|
16636
16789
|
};
|
|
16637
16790
|
}
|
|
16638
|
-
|
|
16639
|
-
|
|
16640
|
-
|
|
16641
|
-
|
|
16642
|
-
|
|
16643
|
-
|
|
16644
|
-
|
|
16645
|
-
|
|
16646
|
-
|
|
16647
|
-
|
|
16648
|
-
for (const item of items) {
|
|
16649
|
-
const sub = subs.find((s) => !s.done && s.text.toLowerCase() === item.toLowerCase());
|
|
16650
|
-
if (sub) {
|
|
16651
|
-
sub.done = true;
|
|
16652
|
-
marked++;
|
|
16653
|
-
}
|
|
16654
|
-
}
|
|
16655
|
-
if (marked === 0) {
|
|
16656
|
-
return {
|
|
16657
|
-
success: false,
|
|
16658
|
-
output: t("todo.subtask_not_found", {
|
|
16659
|
-
items: items.join(", ")
|
|
16660
|
-
})
|
|
16661
|
-
};
|
|
16662
|
-
}
|
|
16663
|
-
currentStep.subtasks = subs;
|
|
16664
|
-
this.store.saveActive(this.tracker.getPlan());
|
|
16665
|
-
const doneCount = subs.filter((s) => s.done).length;
|
|
16666
|
-
const display = this.tracker.toPromptBlock();
|
|
16667
|
-
let output = t("todo.marked_done", {
|
|
16668
|
-
count: String(marked)
|
|
16669
|
-
});
|
|
16670
|
-
output += ` (${doneCount}/${subs.length})`;
|
|
16671
|
-
if (doneCount === subs.length) {
|
|
16672
|
-
output += `
|
|
16791
|
+
currentStep.subtasks = subs;
|
|
16792
|
+
deps.store.saveActive(tracker.getPlan());
|
|
16793
|
+
const doneCount = subs.filter((s) => s.done).length;
|
|
16794
|
+
const display = tracker.toPromptBlock();
|
|
16795
|
+
let output = t("todo.marked_done", {
|
|
16796
|
+
count: String(marked)
|
|
16797
|
+
});
|
|
16798
|
+
output += ` (${doneCount}/${subs.length})`;
|
|
16799
|
+
if (doneCount === subs.length) {
|
|
16800
|
+
output += `
|
|
16673
16801
|
All sub-tasks done — call plan update step=${currentStep.id} status=done to complete this step.`;
|
|
16674
|
-
}
|
|
16675
|
-
return { success: true, output, display };
|
|
16676
16802
|
}
|
|
16677
|
-
|
|
16678
|
-
|
|
16679
|
-
|
|
16680
|
-
|
|
16681
|
-
|
|
16682
|
-
|
|
16803
|
+
return { success: true, output, display };
|
|
16804
|
+
}
|
|
16805
|
+
if (action === "list") {
|
|
16806
|
+
const subs = currentStep.subtasks ?? [];
|
|
16807
|
+
const lines = subs.length ? subs.map((s) => `${s.done ? "[x]" : "[ ]"} ${s.text}`) : ["(no sub-tasks)"];
|
|
16808
|
+
return {
|
|
16809
|
+
success: true,
|
|
16810
|
+
output: `Step ${currentStep.id}: ${currentStep.description}
|
|
16683
16811
|
${lines.join(`
|
|
16684
16812
|
`)}`
|
|
16685
|
-
};
|
|
16686
|
-
}
|
|
16687
|
-
return {
|
|
16688
|
-
success: false,
|
|
16689
|
-
output: t("todo.unknown_action", { action })
|
|
16690
16813
|
};
|
|
16691
16814
|
}
|
|
16692
|
-
|
|
16693
|
-
|
|
16694
|
-
|
|
16695
|
-
|
|
16696
|
-
|
|
16697
|
-
|
|
16698
|
-
|
|
16699
|
-
|
|
16700
|
-
|
|
16701
|
-
|
|
16702
|
-
|
|
16703
|
-
|
|
16704
|
-
|
|
16705
|
-
const step = args.step ? this.tracker.getStep(Number(args.step)) : this.tracker.getCurrentStep();
|
|
16706
|
-
if (!step)
|
|
16707
|
-
return { success: false, output: t("plan.step_not_found") };
|
|
16708
|
-
const result = await this.verifier.verifyStep(step.description);
|
|
16709
|
-
return {
|
|
16710
|
-
success: result.passed,
|
|
16711
|
-
output: result.passed ? t("verify.passed") : t("verify.failed", {
|
|
16712
|
-
details: result.failed.map((f) => f.message).join("; ")
|
|
16713
|
-
})
|
|
16714
|
-
};
|
|
16815
|
+
return {
|
|
16816
|
+
success: false,
|
|
16817
|
+
output: t("todo.unknown_action", { action })
|
|
16818
|
+
};
|
|
16819
|
+
}
|
|
16820
|
+
},
|
|
16821
|
+
{
|
|
16822
|
+
name: "verify",
|
|
16823
|
+
description: "Run verification for the current step. Checks files mentioned in the step description.",
|
|
16824
|
+
parameters: {
|
|
16825
|
+
type: "object",
|
|
16826
|
+
properties: {
|
|
16827
|
+
step: { type: "number", description: "Step number to verify" }
|
|
16715
16828
|
}
|
|
16829
|
+
},
|
|
16830
|
+
handler: async (_ctx, args) => {
|
|
16831
|
+
if (!deps.trackerRef.current)
|
|
16832
|
+
return { success: false, output: t("plan.no_active") };
|
|
16833
|
+
const tracker = deps.trackerRef.current;
|
|
16834
|
+
const step = args.step ? tracker.getStep(Number(args.step)) : tracker.getCurrentStep();
|
|
16835
|
+
if (!step)
|
|
16836
|
+
return { success: false, output: t("plan.step_not_found") };
|
|
16837
|
+
const result = await deps.verifier.verifyStep(step.description);
|
|
16838
|
+
return {
|
|
16839
|
+
success: result.passed,
|
|
16840
|
+
output: result.passed ? t("verify.passed") : t("verify.failed", {
|
|
16841
|
+
details: result.failed.map((f) => f.message).join("; ")
|
|
16842
|
+
})
|
|
16843
|
+
};
|
|
16716
16844
|
}
|
|
16717
|
-
|
|
16718
|
-
|
|
16719
|
-
|
|
16720
|
-
|
|
16721
|
-
|
|
16722
|
-
|
|
16723
|
-
|
|
16724
|
-
|
|
16725
|
-
|
|
16845
|
+
}
|
|
16846
|
+
];
|
|
16847
|
+
}
|
|
16848
|
+
var init_plan_tool = __esm(() => {
|
|
16849
|
+
init_i18n();
|
|
16850
|
+
init_plan_coverage();
|
|
16851
|
+
});
|
|
16852
|
+
|
|
16853
|
+
// src/modules/execution/windows-commands.ts
|
|
16854
|
+
function firstWord(command) {
|
|
16855
|
+
const segs = command.trim().split(/[\s;|&]+/);
|
|
16856
|
+
return (segs[0] || "").replace(/[^\w-]/g, "").toLowerCase();
|
|
16857
|
+
}
|
|
16858
|
+
function forbiddenWindowsCommand(command) {
|
|
16859
|
+
const m = command.match(/^\s*cd\s+\S+\s*&&\s*(\S+)/);
|
|
16860
|
+
if (m)
|
|
16861
|
+
return forbiddenWindowsCommand(m[1]);
|
|
16862
|
+
const word = firstWord(command);
|
|
16863
|
+
if (word && FORBIDDEN_COMMANDS.has(word))
|
|
16864
|
+
return word;
|
|
16865
|
+
return null;
|
|
16866
|
+
}
|
|
16867
|
+
var FORBIDDEN_COMMANDS;
|
|
16868
|
+
var init_windows_commands = __esm(() => {
|
|
16869
|
+
FORBIDDEN_COMMANDS = new Set([
|
|
16870
|
+
"grep",
|
|
16871
|
+
"sed",
|
|
16872
|
+
"ls",
|
|
16873
|
+
"find",
|
|
16874
|
+
"rm",
|
|
16875
|
+
"touch",
|
|
16876
|
+
"which",
|
|
16877
|
+
"diff",
|
|
16878
|
+
"cp",
|
|
16879
|
+
"mv"
|
|
16880
|
+
]);
|
|
16881
|
+
});
|
|
16882
|
+
|
|
16883
|
+
// src/modules/execution/execution-plugin.ts
|
|
16884
|
+
import { platform as platform4 } from "os";
|
|
16885
|
+
function createExecutionPlugin(deps) {
|
|
16886
|
+
return {
|
|
16887
|
+
name: "execution",
|
|
16888
|
+
onBeforeThink: (ctx) => {
|
|
16889
|
+
if (ctx.contextManager && deps.pendingMessages.length > 0) {
|
|
16890
|
+
for (const m of deps.pendingMessages.splice(0)) {
|
|
16891
|
+
ctx.contextManager.addMessage(m);
|
|
16892
|
+
}
|
|
16893
|
+
}
|
|
16894
|
+
if (deps.trackerRef.current?.isComplete()) {
|
|
16895
|
+
deps.stuckDetector.resetStepProgress();
|
|
16896
|
+
deps.state.consecutivePlanWarnings = 0;
|
|
16897
|
+
deps.state.lastStepId = -1;
|
|
16898
|
+
deps.state.stuckNotified = false;
|
|
16899
|
+
} else {
|
|
16900
|
+
const step = deps.trackerRef.current?.getCurrentStep();
|
|
16901
|
+
if (deps.trackerRef.current && step) {
|
|
16902
|
+
if (step.id !== deps.state.lastStepId) {
|
|
16903
|
+
deps.state.consecutivePlanWarnings = 0;
|
|
16904
|
+
deps.state.lastStepId = step.id;
|
|
16905
|
+
deps.state.stuckNotified = false;
|
|
16726
16906
|
}
|
|
16727
|
-
|
|
16728
|
-
|
|
16729
|
-
this.stuckDetector.resetStepProgress();
|
|
16730
|
-
this.consecutivePlanWarnings = 0;
|
|
16731
|
-
this.lastStepId = -1;
|
|
16732
|
-
this.stuckNotified = false;
|
|
16907
|
+
deps.stuckDetector.setCurrentStep(step.id, step.description);
|
|
16908
|
+
deps.stuckDetector.recordIteration(step.id);
|
|
16733
16909
|
} else {
|
|
16734
|
-
|
|
16735
|
-
|
|
16736
|
-
|
|
16737
|
-
|
|
16738
|
-
|
|
16739
|
-
|
|
16740
|
-
|
|
16741
|
-
|
|
16742
|
-
|
|
16743
|
-
|
|
16744
|
-
this.stuckDetector.reset();
|
|
16745
|
-
this.consecutivePlanWarnings = 0;
|
|
16746
|
-
this.lastStepId = -1;
|
|
16747
|
-
this.stuckNotified = false;
|
|
16748
|
-
const iter = typeof ctx.iteration === "number" ? ctx.iteration : 0;
|
|
16749
|
-
if (iter === 3 && !this.tracker && ctx.contextManager) {
|
|
16750
|
-
ctx.contextManager.addMessage({
|
|
16751
|
-
role: "user",
|
|
16752
|
-
content: `<system-summary>You have made 3 tool calls without creating a plan. For any task that involves creating files, installing packages, or multiple steps — you MUST use plan create BEFORE continuing. Use the plan tool now with concrete steps (exact filenames, commands, deliverables). Do NOT make any more write/edit/bash calls until you have a plan.</system-summary>`
|
|
16753
|
-
});
|
|
16754
|
-
}
|
|
16755
|
-
if (iter >= 6 && !this.tracker && ctx.contextManager) {
|
|
16756
|
-
ctx.contextManager.addMessage({
|
|
16757
|
-
role: "user",
|
|
16758
|
-
content: `<system-summary>STOP. ${iter} iterations without a plan. You MUST call plan create RIGHT NOW. No more tool calls until you create a plan.</system-summary>`
|
|
16759
|
-
});
|
|
16760
|
-
}
|
|
16910
|
+
deps.stuckDetector.reset();
|
|
16911
|
+
deps.state.consecutivePlanWarnings = 0;
|
|
16912
|
+
deps.state.lastStepId = -1;
|
|
16913
|
+
deps.state.stuckNotified = false;
|
|
16914
|
+
const iter = typeof ctx.iteration === "number" ? ctx.iteration : 0;
|
|
16915
|
+
if (iter === 3 && !deps.trackerRef.current && ctx.contextManager) {
|
|
16916
|
+
ctx.contextManager.addMessage({
|
|
16917
|
+
role: "user",
|
|
16918
|
+
content: `<system-summary>You have made 3 tool calls without creating a plan. For any task that involves creating files, installing packages, or multiple steps — you MUST use plan create BEFORE continuing. Use the plan tool now with concrete steps (exact filenames, commands, deliverables). Do NOT make any more write/edit/bash calls until you have a plan.</system-summary>`
|
|
16919
|
+
});
|
|
16761
16920
|
}
|
|
16762
|
-
|
|
16763
|
-
|
|
16764
|
-
|
|
16765
|
-
|
|
16766
|
-
|
|
16767
|
-
ctx.logger?.warn(stuckReason);
|
|
16768
|
-
ctx.sessionLog?.plan("stuck-warning", stuckReason, typeof ctx.iteration === "number" ? ctx.iteration : undefined);
|
|
16769
|
-
if (this.stuckDetector.isStuck())
|
|
16770
|
-
this.stuckNotified = true;
|
|
16921
|
+
if (iter >= 6 && !deps.trackerRef.current && ctx.contextManager) {
|
|
16922
|
+
ctx.contextManager.addMessage({
|
|
16923
|
+
role: "user",
|
|
16924
|
+
content: `<system-summary>STOP. ${iter} iterations without a plan. You MUST call plan create RIGHT NOW. No more tool calls until you create a plan.</system-summary>`
|
|
16925
|
+
});
|
|
16771
16926
|
}
|
|
16772
16927
|
}
|
|
16773
|
-
|
|
16774
|
-
|
|
16775
|
-
|
|
16776
|
-
|
|
16777
|
-
|
|
16778
|
-
|
|
16779
|
-
|
|
16928
|
+
}
|
|
16929
|
+
const stuckReason = deps.stuckDetector.getStuckReason();
|
|
16930
|
+
if (stuckReason) {
|
|
16931
|
+
const logIt = deps.stuckDetector.isStuck() ? !deps.state.stuckNotified : true;
|
|
16932
|
+
if (logIt) {
|
|
16933
|
+
ctx.logger?.warn(stuckReason);
|
|
16934
|
+
ctx.sessionLog?.plan("stuck-warning", stuckReason, typeof ctx.iteration === "number" ? ctx.iteration : undefined);
|
|
16935
|
+
if (deps.stuckDetector.isStuck())
|
|
16936
|
+
deps.state.stuckNotified = true;
|
|
16937
|
+
}
|
|
16938
|
+
}
|
|
16939
|
+
if (deps.stuckDetector.isStuck() || deps.stuckDetector.hasRepetitiveToolCalls() || deps.stuckDetector.hasReadOnlyLoop()) {
|
|
16940
|
+
const currentIter = typeof ctx.iteration === "number" ? ctx.iteration : 0;
|
|
16941
|
+
if (currentIter - deps.state.lastRecoveryIteration >= STUCK_RECOVERY_COOLDOWN) {
|
|
16942
|
+
const recovery = deps.stuckDetector.getRecoveryMessage();
|
|
16943
|
+
if (recovery && ctx.contextManager) {
|
|
16944
|
+
const lastError = deps.stuckDetector.getLastErrorOutput();
|
|
16945
|
+
const skillHint = lastError ? `
|
|
16780
16946
|
If you have relevant skills available, consider loading one with load_skill for expert guidance.` : "";
|
|
16781
|
-
|
|
16782
|
-
|
|
16947
|
+
const actionableHints = deps.stuckDetector.getActionableHints();
|
|
16948
|
+
const actionableHintStr = actionableHints.length > 0 ? `
|
|
16783
16949
|
${t("exec.hints", { hints: actionableHints.map((h) => `- ${h}`).join(`
|
|
16784
16950
|
`) })}` : "";
|
|
16785
|
-
|
|
16786
|
-
|
|
16787
|
-
Tool "${
|
|
16788
|
-
|
|
16789
|
-
|
|
16790
|
-
|
|
16791
|
-
|
|
16792
|
-
|
|
16793
|
-
|
|
16794
|
-
|
|
16795
|
-
|
|
16796
|
-
|
|
16951
|
+
const alternative = deps.stuckDetector.getToolAlternative();
|
|
16952
|
+
const altHint = alternative ? `
|
|
16953
|
+
Tool "${deps.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}" instead.` : "";
|
|
16954
|
+
ctx.contextManager.addMessage({
|
|
16955
|
+
role: "user",
|
|
16956
|
+
content: `<system-summary>${recovery}${skillHint}${actionableHintStr}${altHint}</system-summary>`
|
|
16957
|
+
});
|
|
16958
|
+
}
|
|
16959
|
+
const hints = deps.stuckDetector.getHints();
|
|
16960
|
+
if (hints.length > 0 && ctx.contextManager) {
|
|
16961
|
+
const hintMsg = t("exec.hints", {
|
|
16962
|
+
hints: hints.map((h) => `- ${h}`).join(`
|
|
16797
16963
|
`)
|
|
16798
|
-
|
|
16799
|
-
|
|
16800
|
-
|
|
16801
|
-
|
|
16802
|
-
|
|
16803
|
-
|
|
16804
|
-
|
|
16805
|
-
|
|
16806
|
-
|
|
16807
|
-
|
|
16808
|
-
|
|
16809
|
-
|
|
16810
|
-
|
|
16811
|
-
|
|
16812
|
-
|
|
16813
|
-
|
|
16814
|
-
|
|
16815
|
-
|
|
16964
|
+
});
|
|
16965
|
+
ctx.contextManager.addMessage({
|
|
16966
|
+
role: "user",
|
|
16967
|
+
content: `<system-summary>${hintMsg}</system-summary>`
|
|
16968
|
+
});
|
|
16969
|
+
}
|
|
16970
|
+
deps.stuckDetector.recordEscalation();
|
|
16971
|
+
deps.state.lastRecoveryIteration = currentIter;
|
|
16972
|
+
if (deps.stuckDetector.shouldEscalate() && ctx.onMeta) {
|
|
16973
|
+
const escalation = t("exec.escalation", {
|
|
16974
|
+
stepId: String(deps.trackerRef.current?.getCurrentStep()?.id ?? "?"),
|
|
16975
|
+
description: deps.trackerRef.current?.getCurrentStep()?.description ?? ""
|
|
16976
|
+
});
|
|
16977
|
+
ctx.onMeta(escalation);
|
|
16978
|
+
}
|
|
16979
|
+
if (deps.stuckDetector.getIterationsOnCurrentStep() >= FORCE_SKIP_THRESHOLD && ctx.contextManager) {
|
|
16980
|
+
const step = deps.trackerRef.current?.getCurrentStep();
|
|
16981
|
+
ctx.contextManager.addMessage({
|
|
16982
|
+
role: "user",
|
|
16983
|
+
content: `<system-summary>STOP. Step ${step?.id ?? "?"} ("${step?.description ?? ""}") took ${deps.stuckDetector.getIterationsOnCurrentStep()} iterations with no progress. DO NOT continue this step. Immediately call: plan update step=${step?.id ?? "?"} status=done (if code works despite warnings) OR plan update step=${step?.id ?? "?"} status=skipped note="reason". Do NOT make any other tool calls before updating the plan.</system-summary>`
|
|
16984
|
+
});
|
|
16985
|
+
}
|
|
16986
|
+
}
|
|
16987
|
+
}
|
|
16988
|
+
},
|
|
16989
|
+
onBeforeTool: (_ctx, call) => {
|
|
16990
|
+
const warning = deps.checkPlanAlignment(call);
|
|
16991
|
+
if (warning) {
|
|
16992
|
+
deps.pendingMessages.push({
|
|
16993
|
+
role: "user",
|
|
16994
|
+
content: `<system-summary>${warning}</system-summary>`
|
|
16995
|
+
});
|
|
16996
|
+
deps.state.consecutivePlanWarnings++;
|
|
16997
|
+
if (deps.state.consecutivePlanWarnings >= MAX_PLAN_WARNINGS_BEFORE_BLOCK) {
|
|
16998
|
+
deps.pendingMessages.push({
|
|
16999
|
+
role: "user",
|
|
17000
|
+
content: `<system-summary>${t("exec.plan_blocked", { step: String(deps.trackerRef.current?.getCurrentStep()?.id ?? "?"), max: MAX_PLAN_WARNINGS_BEFORE_BLOCK })}</system-summary>`
|
|
17001
|
+
});
|
|
17002
|
+
return t("exec.plan_blocked", {
|
|
17003
|
+
step: String(deps.trackerRef.current?.getCurrentStep()?.id ?? "?"),
|
|
17004
|
+
max: MAX_PLAN_WARNINGS_BEFORE_BLOCK
|
|
17005
|
+
});
|
|
17006
|
+
}
|
|
17007
|
+
} else {
|
|
17008
|
+
deps.state.consecutivePlanWarnings = 0;
|
|
17009
|
+
}
|
|
17010
|
+
return true;
|
|
17011
|
+
},
|
|
17012
|
+
onToolCall: (ctx) => {
|
|
17013
|
+
const toolName = ctx?.toolName;
|
|
17014
|
+
const args = ctx?.args;
|
|
17015
|
+
if (toolName && args) {
|
|
17016
|
+
deps.stuckDetector.recordToolCall(toolName, args);
|
|
17017
|
+
}
|
|
17018
|
+
},
|
|
17019
|
+
onAfterTool: (ctx, call, result) => {
|
|
17020
|
+
if (call.name === "bash") {
|
|
17021
|
+
deps.stuckDetector.recordBashOutput(String(call.arguments?.command ?? ""), String(result.output ?? ""));
|
|
17022
|
+
}
|
|
17023
|
+
const toolText = String(result.output ?? "");
|
|
17024
|
+
if (/error TS\d+|\[Project typecheck failed\]|\[Syntax check failed\]/.test(toolText)) {
|
|
17025
|
+
deps.stuckDetector.recordToolError(call.name, toolText.slice(0, 300));
|
|
17026
|
+
}
|
|
17027
|
+
if (!result.success) {
|
|
17028
|
+
if (call.name === "bash" && platform4() === "win32") {
|
|
17029
|
+
const cmd = String(call.arguments?.command ?? "");
|
|
17030
|
+
const forbidden = forbiddenWindowsCommand(cmd);
|
|
17031
|
+
if (forbidden) {
|
|
17032
|
+
const n = (deps.forbiddenBashFailures.get(forbidden) || 0) + 1;
|
|
17033
|
+
deps.forbiddenBashFailures.set(forbidden, n);
|
|
17034
|
+
if (n === 2) {
|
|
17035
|
+
deps.pendingMessages.push({
|
|
16816
17036
|
role: "user",
|
|
16817
|
-
content: `<system-summary
|
|
17037
|
+
content: `<system-summary>${t("exec.forbidden_cmd", { cmd: forbidden })}</system-summary>`
|
|
16818
17038
|
});
|
|
16819
17039
|
}
|
|
16820
17040
|
}
|
|
16821
17041
|
}
|
|
16822
|
-
|
|
16823
|
-
|
|
16824
|
-
const
|
|
16825
|
-
if (
|
|
16826
|
-
|
|
17042
|
+
deps.stuckDetector.recordToolError(call.name, result.output);
|
|
17043
|
+
const actionableHints = deps.stuckDetector.getActionableHints();
|
|
17044
|
+
const alternative = deps.stuckDetector.getToolAlternative();
|
|
17045
|
+
if (actionableHints.length > 0 || alternative) {
|
|
17046
|
+
const parts = [...actionableHints];
|
|
17047
|
+
if (alternative) {
|
|
17048
|
+
parts.push(`Tool "${call.name}" crashed. Try "${alternative}" instead.`);
|
|
17049
|
+
}
|
|
17050
|
+
deps.pendingMessages.push({
|
|
16827
17051
|
role: "user",
|
|
16828
|
-
content: `<system-summary>${
|
|
17052
|
+
content: `<system-summary>${t("exec.hints", { hints: parts.map((h) => `- ${h}`).join(`
|
|
17053
|
+
`) })}</system-summary>`
|
|
16829
17054
|
});
|
|
16830
|
-
|
|
16831
|
-
|
|
16832
|
-
|
|
17055
|
+
}
|
|
17056
|
+
} else {
|
|
17057
|
+
deps.stuckDetector.recordToolSuccess();
|
|
17058
|
+
if (call.name === "bash" && result.success) {
|
|
17059
|
+
const cmd = String(call.arguments?.command ?? "");
|
|
17060
|
+
const testRun = detectTestResults(String(result.output ?? ""));
|
|
17061
|
+
if (testRun && testRun.failed > 0) {
|
|
17062
|
+
deps.pendingMessages.push({
|
|
16833
17063
|
role: "user",
|
|
16834
|
-
content: `<system-summary>${
|
|
17064
|
+
content: `<system-summary>${testRun.framework} reported ${testRun.failed} FAILING test(s) (${testRun.passed} passing). Do NOT mark the current step as done — fix the failing tests (read the failure output, correct the code) and re-run them until all pass.</system-summary>`
|
|
16835
17065
|
});
|
|
16836
|
-
|
|
16837
|
-
|
|
16838
|
-
|
|
17066
|
+
} else if (testRun && testRun.failed === 0 && testRun.passed > 0) {
|
|
17067
|
+
deps.pendingMessages.push({
|
|
17068
|
+
role: "user",
|
|
17069
|
+
content: `<system-summary>${testRun.framework}: all ${testRun.passed} test(s) passed for "${cmd}". You may mark the current step as done via plan update step=N status=done.</system-summary>`
|
|
16839
17070
|
});
|
|
16840
|
-
}
|
|
16841
|
-
|
|
16842
|
-
this.consecutivePlanWarnings = 0;
|
|
16843
|
-
}
|
|
16844
|
-
return true;
|
|
16845
|
-
},
|
|
16846
|
-
onToolCall: (ctx) => {
|
|
16847
|
-
const toolName = ctx?.toolName;
|
|
16848
|
-
const args = ctx?.args;
|
|
16849
|
-
if (toolName && args) {
|
|
16850
|
-
this.stuckDetector.recordToolCall(toolName, args);
|
|
16851
|
-
}
|
|
16852
|
-
},
|
|
16853
|
-
onAfterTool: (ctx, call, result) => {
|
|
16854
|
-
if (call.name === "bash") {
|
|
16855
|
-
this.stuckDetector.recordBashOutput(String(call.arguments?.command ?? ""), String(result.output ?? ""));
|
|
16856
|
-
}
|
|
16857
|
-
const toolText = String(result.output ?? "");
|
|
16858
|
-
if (/error TS\d+|\[Project typecheck failed\]|\[Syntax check failed\]/.test(toolText)) {
|
|
16859
|
-
this.stuckDetector.recordToolError(call.name, toolText.slice(0, 300));
|
|
16860
|
-
}
|
|
16861
|
-
if (!result.success) {
|
|
16862
|
-
this.stuckDetector.recordToolError(call.name, result.output);
|
|
16863
|
-
const actionableHints = this.stuckDetector.getActionableHints();
|
|
16864
|
-
const alternative = this.stuckDetector.getToolAlternative();
|
|
16865
|
-
if (actionableHints.length > 0 || alternative) {
|
|
16866
|
-
const parts = [...actionableHints];
|
|
16867
|
-
if (alternative) {
|
|
16868
|
-
parts.push(`Tool "${call.name}" crashed. Try "${alternative}" instead.`);
|
|
16869
|
-
}
|
|
16870
|
-
this.pendingMessages.push({
|
|
17071
|
+
} else if (/node|tsx|ts-node|python|npm\s+(start|test|run)/.test(cmd)) {
|
|
17072
|
+
deps.pendingMessages.push({
|
|
16871
17073
|
role: "user",
|
|
16872
|
-
content: `<system-summary
|
|
16873
|
-
`) })}</system-summary>`
|
|
17074
|
+
content: `<system-summary>The command "${cmd}" completed successfully. If this was testing your code, mark the current step as done via plan update step=N status=done.</system-summary>`
|
|
16874
17075
|
});
|
|
16875
17076
|
}
|
|
16876
|
-
} else {
|
|
16877
|
-
this.stuckDetector.recordToolSuccess();
|
|
16878
|
-
if (call.name === "bash" && result.success) {
|
|
16879
|
-
const cmd = String(call.arguments?.command ?? "");
|
|
16880
|
-
const testRun = detectTestResults(String(result.output ?? ""));
|
|
16881
|
-
if (testRun && testRun.failed > 0) {
|
|
16882
|
-
this.pendingMessages.push({
|
|
16883
|
-
role: "user",
|
|
16884
|
-
content: `<system-summary>${testRun.framework} reported ${testRun.failed} FAILING test(s) (${testRun.passed} passing). Do NOT mark the current step as done — fix the failing tests (read the failure output, correct the code) and re-run them until all pass.</system-summary>`
|
|
16885
|
-
});
|
|
16886
|
-
} else if (testRun && testRun.failed === 0 && testRun.passed > 0) {
|
|
16887
|
-
this.pendingMessages.push({
|
|
16888
|
-
role: "user",
|
|
16889
|
-
content: `<system-summary>${testRun.framework}: all ${testRun.passed} test(s) passed for "${cmd}". You may mark the current step as done via plan update step=N status=done.</system-summary>`
|
|
16890
|
-
});
|
|
16891
|
-
} else if (/node|tsx|ts-node|python|npm\s+(start|test|run)/.test(cmd)) {
|
|
16892
|
-
this.pendingMessages.push({
|
|
16893
|
-
role: "user",
|
|
16894
|
-
content: `<system-summary>The command "${cmd}" completed successfully. If this was testing your code, mark the current step as done via plan update step=N status=done.</system-summary>`
|
|
16895
|
-
});
|
|
16896
|
-
}
|
|
16897
|
-
}
|
|
16898
17077
|
}
|
|
16899
|
-
|
|
16900
|
-
|
|
16901
|
-
|
|
16902
|
-
|
|
16903
|
-
|
|
16904
|
-
|
|
16905
|
-
|
|
16906
|
-
|
|
16907
|
-
|
|
16908
|
-
|
|
16909
|
-
|
|
16910
|
-
|
|
16911
|
-
}
|
|
17078
|
+
}
|
|
17079
|
+
if (result.success && (call.name === "write_file" || call.name === "edit_file")) {
|
|
17080
|
+
const filePath = call.arguments?.path;
|
|
17081
|
+
if (filePath) {
|
|
17082
|
+
deps.stuckDetector.recordFileRewrite(filePath);
|
|
17083
|
+
if (deps.stuckDetector.hasExcessiveRewrites()) {
|
|
17084
|
+
const file = deps.stuckDetector.getExcessiveRewriteFile();
|
|
17085
|
+
const count = deps.stuckDetector.getFileRewriteCount(file);
|
|
17086
|
+
if (ctx.onMeta) {
|
|
17087
|
+
ctx.onMeta(t("exec.file_rewrite_warning", {
|
|
17088
|
+
file,
|
|
17089
|
+
count: String(count)
|
|
17090
|
+
}));
|
|
16912
17091
|
}
|
|
16913
17092
|
}
|
|
16914
|
-
this.advancePlanIfStepComplete(ctx.contextManager, ctx.sessionLog);
|
|
16915
17093
|
}
|
|
17094
|
+
deps.advancePlanIfStepComplete(ctx.contextManager, ctx.sessionLog);
|
|
17095
|
+
}
|
|
17096
|
+
}
|
|
17097
|
+
};
|
|
17098
|
+
}
|
|
17099
|
+
var STUCK_RECOVERY_COOLDOWN = 5, MAX_PLAN_WARNINGS_BEFORE_BLOCK = 3, FORCE_SKIP_THRESHOLD = 10;
|
|
17100
|
+
var init_execution_plugin = __esm(() => {
|
|
17101
|
+
init_i18n();
|
|
17102
|
+
init_bash();
|
|
17103
|
+
init_windows_commands();
|
|
17104
|
+
});
|
|
17105
|
+
|
|
17106
|
+
// src/modules/execution/module.ts
|
|
17107
|
+
import { existsSync as existsSync30, readFileSync as readFileSync17 } from "fs";
|
|
17108
|
+
import { resolve as resolve18 } from "path";
|
|
17109
|
+
|
|
17110
|
+
class ExecutionModule {
|
|
17111
|
+
name = "execution";
|
|
17112
|
+
tracker = null;
|
|
17113
|
+
verifier;
|
|
17114
|
+
stuckDetector;
|
|
17115
|
+
auditor;
|
|
17116
|
+
store;
|
|
17117
|
+
baseDir;
|
|
17118
|
+
_auditSkipsRemaining = 0;
|
|
17119
|
+
pendingMessages = [];
|
|
17120
|
+
forbiddenBashFailures = new Map;
|
|
17121
|
+
state = {
|
|
17122
|
+
lastRecoveryIteration: -STUCK_RECOVERY_COOLDOWN,
|
|
17123
|
+
consecutivePlanWarnings: 0,
|
|
17124
|
+
lastStepId: -1,
|
|
17125
|
+
stuckNotified: false,
|
|
17126
|
+
depsGateHints: new Map
|
|
17127
|
+
};
|
|
17128
|
+
trackerRef = (() => {
|
|
17129
|
+
const self = this;
|
|
17130
|
+
return {
|
|
17131
|
+
get current() {
|
|
17132
|
+
return self.tracker;
|
|
17133
|
+
},
|
|
17134
|
+
set current(v) {
|
|
17135
|
+
self.tracker = v;
|
|
16916
17136
|
}
|
|
16917
17137
|
};
|
|
17138
|
+
})();
|
|
17139
|
+
constructor(baseDir, stuckThreshold = 6) {
|
|
17140
|
+
this.baseDir = baseDir;
|
|
17141
|
+
this.verifier = new StepVerifier(baseDir);
|
|
17142
|
+
this.stuckDetector = new StuckDetector(stuckThreshold);
|
|
17143
|
+
this.auditor = new Auditor(baseDir);
|
|
17144
|
+
this.store = new PlanStore(baseDir);
|
|
17145
|
+
}
|
|
17146
|
+
setPlan(plan) {
|
|
17147
|
+
this.tracker = new PlanTracker(plan);
|
|
17148
|
+
this.store.saveActive(plan);
|
|
17149
|
+
this._auditSkipsRemaining = 0;
|
|
17150
|
+
}
|
|
17151
|
+
restorePlan() {
|
|
17152
|
+
const plan = this.store.loadActive();
|
|
17153
|
+
if (!plan)
|
|
17154
|
+
return false;
|
|
17155
|
+
this.tracker = new PlanTracker(plan);
|
|
17156
|
+
this.tracker.syncCurrentStep();
|
|
17157
|
+
this._auditSkipsRemaining = 1;
|
|
17158
|
+
return true;
|
|
17159
|
+
}
|
|
17160
|
+
getTracker() {
|
|
17161
|
+
return this.tracker;
|
|
17162
|
+
}
|
|
17163
|
+
getStore() {
|
|
17164
|
+
return this.store;
|
|
17165
|
+
}
|
|
17166
|
+
getStuckDetector() {
|
|
17167
|
+
return this.stuckDetector;
|
|
17168
|
+
}
|
|
17169
|
+
async runFinalAudit() {
|
|
17170
|
+
if (!this.tracker)
|
|
17171
|
+
return null;
|
|
17172
|
+
if (this._auditSkipsRemaining > 0) {
|
|
17173
|
+
this._auditSkipsRemaining--;
|
|
17174
|
+
return null;
|
|
17175
|
+
}
|
|
17176
|
+
const plan = this.tracker.getPlan();
|
|
17177
|
+
const audit = await this.auditor.audit(plan);
|
|
17178
|
+
const pendingSteps = plan.steps.flatMap((s) => s.status !== "done" && s.status !== "skipped" ? [`${s.id}. ${s.description}`] : []);
|
|
17179
|
+
const done = plan.steps.filter((s) => s.status === "done").length;
|
|
17180
|
+
const passed = audit.passed && pendingSteps.length === 0;
|
|
17181
|
+
return {
|
|
17182
|
+
passed,
|
|
17183
|
+
done,
|
|
17184
|
+
total: plan.steps.length,
|
|
17185
|
+
pendingSteps,
|
|
17186
|
+
missingFiles: audit.missingFiles,
|
|
17187
|
+
summary: audit.summary
|
|
17188
|
+
};
|
|
17189
|
+
}
|
|
17190
|
+
getSystemPromptBlock() {
|
|
17191
|
+
if (!this.tracker)
|
|
17192
|
+
return null;
|
|
17193
|
+
return {
|
|
17194
|
+
content: this.tracker.toPromptBlock(),
|
|
17195
|
+
priority: "critical",
|
|
17196
|
+
essential: true,
|
|
17197
|
+
estimatedTokens: 200
|
|
17198
|
+
};
|
|
17199
|
+
}
|
|
17200
|
+
getToolDefinitions() {
|
|
17201
|
+
return createPlanToolDefinitions({
|
|
17202
|
+
store: this.store,
|
|
17203
|
+
verifier: this.verifier,
|
|
17204
|
+
trackerRef: this.trackerRef,
|
|
17205
|
+
preserveActive: () => this.preserveActive(),
|
|
17206
|
+
setPlan: (p) => this.setPlan(p),
|
|
17207
|
+
missingStepDeliverables: (s) => this.missingStepDeliverables(s),
|
|
17208
|
+
stillExistingDeliverables: (s) => this.stillExistingDeliverables(s),
|
|
17209
|
+
parseKinds: (a, st) => this.parseKinds(a, st),
|
|
17210
|
+
getTaskText: (c) => this.getTaskText(c),
|
|
17211
|
+
baseDir: this.baseDir
|
|
17212
|
+
});
|
|
17213
|
+
}
|
|
17214
|
+
getPlugin() {
|
|
17215
|
+
return createExecutionPlugin({
|
|
17216
|
+
stuckDetector: this.stuckDetector,
|
|
17217
|
+
trackerRef: this.trackerRef,
|
|
17218
|
+
pendingMessages: this.pendingMessages,
|
|
17219
|
+
forbiddenBashFailures: this.forbiddenBashFailures,
|
|
17220
|
+
state: this.state,
|
|
17221
|
+
checkPlanAlignment: (call) => this.checkPlanAlignment(call),
|
|
17222
|
+
advancePlanIfStepComplete: (cm, sl) => this.advancePlanIfStepComplete(cm, sl)
|
|
17223
|
+
});
|
|
16918
17224
|
}
|
|
16919
17225
|
preserveActive() {
|
|
16920
17226
|
if (!this.tracker)
|
|
@@ -16945,19 +17251,18 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
|
|
|
16945
17251
|
];
|
|
16946
17252
|
if (allowedAlways.includes(call.name))
|
|
16947
17253
|
return null;
|
|
16948
|
-
const stepPaths = (step.description
|
|
17254
|
+
const stepPaths = extractFileLikeTokens(stripUrls(step.description)).map((p) => p.toLowerCase());
|
|
16949
17255
|
if (stepPaths.length === 0)
|
|
16950
17256
|
return null;
|
|
16951
|
-
const argStr = JSON.stringify(call.arguments)
|
|
16952
|
-
const callPaths = (argStr
|
|
17257
|
+
const argStr = JSON.stringify(call.arguments);
|
|
17258
|
+
const callPaths = extractFileLikeTokens(stripUrls(argStr)).map((p) => p.toLowerCase());
|
|
16953
17259
|
if (callPaths.length === 0)
|
|
16954
17260
|
return null;
|
|
16955
17261
|
const plan = this.tracker.getPlan();
|
|
16956
17262
|
const finishedPaths = new Set;
|
|
16957
17263
|
for (const s of plan.steps) {
|
|
16958
17264
|
if (s.status === "done" || s.status === "skipped") {
|
|
16959
|
-
|
|
16960
|
-
ps.forEach((p) => finishedPaths.add(p));
|
|
17265
|
+
extractFileLikeTokens(stripUrls(s.description)).map((p) => p.toLowerCase()).forEach((p) => finishedPaths.add(p));
|
|
16961
17266
|
}
|
|
16962
17267
|
}
|
|
16963
17268
|
const offPath = callPaths.some((p) => {
|
|
@@ -16979,7 +17284,7 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
|
|
|
16979
17284
|
if (!step)
|
|
16980
17285
|
return;
|
|
16981
17286
|
const stepText = step.description.toLowerCase();
|
|
16982
|
-
const stepPaths = step.description
|
|
17287
|
+
const stepPaths = extractFileLikeTokens(stripUrls(step.description)) || [];
|
|
16983
17288
|
const isDepsStep = stepText.includes("install") || stepText.includes("зависим") || stepText.includes("init") || stepText.includes("инициализац");
|
|
16984
17289
|
if (isDepsStep) {
|
|
16985
17290
|
const lockFiles = [
|
|
@@ -16997,8 +17302,8 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
|
|
|
16997
17302
|
const hasLockFile = lockFiles.some((f) => existsSync30(resolve18(this.baseDir, f)));
|
|
16998
17303
|
if (!hasLockFile) {
|
|
16999
17304
|
if (contextManager) {
|
|
17000
|
-
const hints = this.depsGateHints.get(step.id) || 0;
|
|
17001
|
-
this.depsGateHints.set(step.id, hints + 1);
|
|
17305
|
+
const hints = this.state.depsGateHints.get(step.id) || 0;
|
|
17306
|
+
this.state.depsGateHints.set(step.id, hints + 1);
|
|
17002
17307
|
const force = hints >= 1;
|
|
17003
17308
|
const content = force ? t("exec.step_gate_deps_force", { step: String(step.id) }) : t("exec.step_gate_deps", {
|
|
17004
17309
|
step: String(step.id),
|
|
@@ -17015,23 +17320,27 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
|
|
|
17015
17320
|
if (stepPaths.length === 0)
|
|
17016
17321
|
return;
|
|
17017
17322
|
const allExist = stepPaths.every((p) => existsSync30(resolve18(this.baseDir, p)));
|
|
17018
|
-
|
|
17019
|
-
|
|
17020
|
-
|
|
17021
|
-
for (const p of stepPaths) {
|
|
17022
|
-
try {
|
|
17023
|
-
const content = readFileSync17(resolve18(this.baseDir, p), "utf-8");
|
|
17024
|
-
if (content.trim().length < 10) {
|
|
17025
|
-
emptyFiles.push(p);
|
|
17026
|
-
}
|
|
17027
|
-
} catch {}
|
|
17028
|
-
}
|
|
17029
|
-
if (emptyFiles.length > 0 && contextManager) {
|
|
17030
|
-
contextManager.addMessage({
|
|
17031
|
-
role: "user",
|
|
17032
|
-
content: `<system-summary>${t("exec.step_gate_empty", { step: String(step.id), files: emptyFiles.join(", ") })}</system-summary>`
|
|
17033
|
-
});
|
|
17323
|
+
const allGone = stepPaths.every((p) => !existsSync30(resolve18(this.baseDir, p)));
|
|
17324
|
+
const satisfied = step.kind === "delete" ? allGone && !allExist : allExist;
|
|
17325
|
+
if (!satisfied)
|
|
17034
17326
|
return;
|
|
17327
|
+
if (step.kind !== "delete") {
|
|
17328
|
+
const emptyFiles = [];
|
|
17329
|
+
for (const p of stepPaths) {
|
|
17330
|
+
try {
|
|
17331
|
+
const content = readFileSync17(resolve18(this.baseDir, p), "utf-8");
|
|
17332
|
+
if (content.trim().length < 10) {
|
|
17333
|
+
emptyFiles.push(p);
|
|
17334
|
+
}
|
|
17335
|
+
} catch {}
|
|
17336
|
+
}
|
|
17337
|
+
if (emptyFiles.length > 0 && contextManager) {
|
|
17338
|
+
contextManager.addMessage({
|
|
17339
|
+
role: "user",
|
|
17340
|
+
content: `<system-summary>${t("exec.step_gate_empty", { step: String(step.id), files: emptyFiles.join(", ") })}</system-summary>`
|
|
17341
|
+
});
|
|
17342
|
+
return;
|
|
17343
|
+
}
|
|
17035
17344
|
}
|
|
17036
17345
|
this.tracker?.updateStepStatus(step.id, "done");
|
|
17037
17346
|
const hadNext = this.tracker?.advance() ?? false;
|
|
@@ -17058,18 +17367,49 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
|
|
|
17058
17367
|
return [];
|
|
17059
17368
|
return tokens.filter((p) => !findExistingFile(this.baseDir, p));
|
|
17060
17369
|
}
|
|
17370
|
+
stillExistingDeliverables(step) {
|
|
17371
|
+
const tokens = extractFileLikeTokens(stripUrls(step.description));
|
|
17372
|
+
if (tokens.length === 0)
|
|
17373
|
+
return [];
|
|
17374
|
+
return tokens.filter((p) => findExistingFile(this.baseDir, p));
|
|
17375
|
+
}
|
|
17376
|
+
parseKinds(args, steps) {
|
|
17377
|
+
if (args.kinds === undefined)
|
|
17378
|
+
return { kinds: null, error: null };
|
|
17379
|
+
if (!Array.isArray(args.kinds)) {
|
|
17380
|
+
return { kinds: null, error: t("plan.kinds_not_array") };
|
|
17381
|
+
}
|
|
17382
|
+
const kindsRaw = args.kinds.map(String);
|
|
17383
|
+
if (kindsRaw.length !== steps.length) {
|
|
17384
|
+
return { kinds: null, error: t("plan.kinds_mismatch") };
|
|
17385
|
+
}
|
|
17386
|
+
const invalid = kindsRaw.filter((k) => k !== "create" && k !== "delete");
|
|
17387
|
+
if (invalid.length > 0) {
|
|
17388
|
+
return {
|
|
17389
|
+
kinds: null,
|
|
17390
|
+
error: t("plan.kinds_invalid", {
|
|
17391
|
+
kinds: invalid.join(", ")
|
|
17392
|
+
})
|
|
17393
|
+
};
|
|
17394
|
+
}
|
|
17395
|
+
return { kinds: kindsRaw, error: null };
|
|
17396
|
+
}
|
|
17061
17397
|
getTaskText(ctx) {
|
|
17062
17398
|
const manager = ctx?.contextManager;
|
|
17063
17399
|
if (!manager)
|
|
17064
17400
|
return "";
|
|
17065
17401
|
const history = manager.getActiveHistory();
|
|
17066
|
-
const firstUser = history.find((m) =>
|
|
17402
|
+
const firstUser = history.find((m) => {
|
|
17403
|
+
if (m.role !== "user")
|
|
17404
|
+
return false;
|
|
17405
|
+
const text = getMessageText(m.content);
|
|
17406
|
+
return !text.startsWith("<system-summary>") && !text.includes("[Compressed:");
|
|
17407
|
+
});
|
|
17067
17408
|
if (!firstUser)
|
|
17068
17409
|
return "";
|
|
17069
17410
|
return getMessageText(firstUser.content).trim();
|
|
17070
17411
|
}
|
|
17071
17412
|
}
|
|
17072
|
-
var STUCK_RECOVERY_COOLDOWN = 5, MAX_PLAN_WARNINGS_BEFORE_BLOCK = 3, FORCE_SKIP_THRESHOLD = 10;
|
|
17073
17413
|
var init_module = __esm(() => {
|
|
17074
17414
|
init_i18n();
|
|
17075
17415
|
init_tracker();
|
|
@@ -17077,9 +17417,9 @@ var init_module = __esm(() => {
|
|
|
17077
17417
|
init_stuck_detector();
|
|
17078
17418
|
init_auditor();
|
|
17079
17419
|
init_plan_store();
|
|
17080
|
-
init_plan_coverage();
|
|
17081
|
-
init_bash();
|
|
17082
17420
|
init_js_identifiers();
|
|
17421
|
+
init_plan_tool();
|
|
17422
|
+
init_execution_plugin();
|
|
17083
17423
|
});
|
|
17084
17424
|
|
|
17085
17425
|
// src/modules/security/session-encryption.ts
|
|
@@ -17087,10 +17427,10 @@ import {
|
|
|
17087
17427
|
readFileSync as readFileSync18,
|
|
17088
17428
|
writeFileSync as writeFileSync11,
|
|
17089
17429
|
existsSync as existsSync31,
|
|
17090
|
-
readdirSync as
|
|
17430
|
+
readdirSync as readdirSync11,
|
|
17091
17431
|
unlinkSync as unlinkSync4
|
|
17092
17432
|
} from "fs";
|
|
17093
|
-
import { join as
|
|
17433
|
+
import { join as join26 } from "path";
|
|
17094
17434
|
import { homedir as homedir8 } from "os";
|
|
17095
17435
|
|
|
17096
17436
|
class SessionFileEncryptor {
|
|
@@ -17099,7 +17439,7 @@ class SessionFileEncryptor {
|
|
|
17099
17439
|
constructor(config) {
|
|
17100
17440
|
this.config = { ...DEFAULT_SESSION_ENCRYPTION, ...config };
|
|
17101
17441
|
this.encryptor = new ConfigEncryptor({
|
|
17102
|
-
keyPath: config?.keyPath ||
|
|
17442
|
+
keyPath: config?.keyPath || join26(homedir8(), ".mma", ".session-encryption-key")
|
|
17103
17443
|
});
|
|
17104
17444
|
}
|
|
17105
17445
|
isEnabled() {
|
|
@@ -17183,9 +17523,9 @@ class SessionFileEncryptor {
|
|
|
17183
17523
|
encryptSessionDirectory(sessionDir) {
|
|
17184
17524
|
if (!this.config.enabled)
|
|
17185
17525
|
return;
|
|
17186
|
-
const files =
|
|
17526
|
+
const files = readdirSync11(sessionDir);
|
|
17187
17527
|
for (const file of files) {
|
|
17188
|
-
const filePath =
|
|
17528
|
+
const filePath = join26(sessionDir, file);
|
|
17189
17529
|
if (existsSync31(filePath) && !file.endsWith(".enc")) {
|
|
17190
17530
|
try {
|
|
17191
17531
|
const content = readFileSync18(filePath, "utf8");
|
|
@@ -17199,10 +17539,10 @@ class SessionFileEncryptor {
|
|
|
17199
17539
|
decryptSessionDirectory(sessionDir) {
|
|
17200
17540
|
if (!this.config.enabled)
|
|
17201
17541
|
return;
|
|
17202
|
-
const files =
|
|
17542
|
+
const files = readdirSync11(sessionDir);
|
|
17203
17543
|
for (const file of files) {
|
|
17204
17544
|
if (file.endsWith(".enc")) {
|
|
17205
|
-
const encFilePath =
|
|
17545
|
+
const encFilePath = join26(sessionDir, file);
|
|
17206
17546
|
const decFilePath = encFilePath.slice(0, -4);
|
|
17207
17547
|
try {
|
|
17208
17548
|
const content = readFileSync18(encFilePath, "utf8");
|
|
@@ -17229,13 +17569,13 @@ var init_session_encryption = __esm(() => {
|
|
|
17229
17569
|
import {
|
|
17230
17570
|
existsSync as existsSync32,
|
|
17231
17571
|
mkdirSync as mkdirSync15,
|
|
17232
|
-
readdirSync as
|
|
17572
|
+
readdirSync as readdirSync12,
|
|
17233
17573
|
readFileSync as readFileSync19,
|
|
17234
17574
|
rmSync as rmSync2,
|
|
17235
17575
|
writeFileSync as writeFileSync12,
|
|
17236
17576
|
appendFileSync as appendFileSync6
|
|
17237
17577
|
} from "fs";
|
|
17238
|
-
import { join as
|
|
17578
|
+
import { join as join27 } from "path";
|
|
17239
17579
|
import { gzipSync } from "zlib";
|
|
17240
17580
|
|
|
17241
17581
|
class SessionStore {
|
|
@@ -17249,7 +17589,7 @@ class SessionStore {
|
|
|
17249
17589
|
}
|
|
17250
17590
|
}
|
|
17251
17591
|
getSessionDir(id) {
|
|
17252
|
-
return
|
|
17592
|
+
return join27(this.baseDir, id);
|
|
17253
17593
|
}
|
|
17254
17594
|
updateEncryption(config) {
|
|
17255
17595
|
if (config?.enabled) {
|
|
@@ -17265,16 +17605,16 @@ class SessionStore {
|
|
|
17265
17605
|
mkdirSync15(this.baseDir, { recursive: true });
|
|
17266
17606
|
}
|
|
17267
17607
|
sessionDir(id) {
|
|
17268
|
-
return
|
|
17608
|
+
return join27(this.baseDir, id);
|
|
17269
17609
|
}
|
|
17270
17610
|
metaPath(id) {
|
|
17271
|
-
return
|
|
17611
|
+
return join27(this.sessionDir(id), "meta.json");
|
|
17272
17612
|
}
|
|
17273
17613
|
historyPath(id) {
|
|
17274
|
-
return
|
|
17614
|
+
return join27(this.sessionDir(id), "history.jsonl");
|
|
17275
17615
|
}
|
|
17276
17616
|
sessionLogPath(id) {
|
|
17277
|
-
return
|
|
17617
|
+
return join27(this.sessionDir(id), "session.jsonl");
|
|
17278
17618
|
}
|
|
17279
17619
|
sessionExists(id) {
|
|
17280
17620
|
return existsSync32(this.metaPath(id));
|
|
@@ -17398,7 +17738,7 @@ class SessionStore {
|
|
|
17398
17738
|
listSessions() {
|
|
17399
17739
|
if (!existsSync32(this.baseDir))
|
|
17400
17740
|
return [];
|
|
17401
|
-
const entries =
|
|
17741
|
+
const entries = readdirSync12(this.baseDir, { withFileTypes: true });
|
|
17402
17742
|
const sessions = [];
|
|
17403
17743
|
for (const entry of entries) {
|
|
17404
17744
|
if (entry.isDirectory()) {
|
|
@@ -17428,7 +17768,7 @@ class SessionStore {
|
|
|
17428
17768
|
if (existsSync32(historyPath)) {
|
|
17429
17769
|
const content = readFileSync19(historyPath, "utf-8");
|
|
17430
17770
|
const compressed = gzipSync(content);
|
|
17431
|
-
const gzPath =
|
|
17771
|
+
const gzPath = join27(this.baseDir, `${session2.id}.jsonl.gz`);
|
|
17432
17772
|
writeFileSync12(gzPath, compressed);
|
|
17433
17773
|
rmSync2(historyPath);
|
|
17434
17774
|
}
|
|
@@ -17639,8 +17979,8 @@ class ProfileCompressor {
|
|
|
17639
17979
|
|
|
17640
17980
|
// src/modules/user-profile/profile.ts
|
|
17641
17981
|
import { readFileSync as readFileSync20, writeFileSync as writeFileSync13, existsSync as existsSync33, mkdirSync as mkdirSync16 } from "fs";
|
|
17642
|
-
import { join as
|
|
17643
|
-
import { homedir as homedir9, hostname, platform as
|
|
17982
|
+
import { join as join28 } from "path";
|
|
17983
|
+
import { homedir as homedir9, hostname, platform as platform5, type } from "os";
|
|
17644
17984
|
import { env } from "process";
|
|
17645
17985
|
|
|
17646
17986
|
class UserProfile {
|
|
@@ -17652,7 +17992,7 @@ class UserProfile {
|
|
|
17652
17992
|
}
|
|
17653
17993
|
collect() {
|
|
17654
17994
|
this.info = {
|
|
17655
|
-
platform:
|
|
17995
|
+
platform: platform5(),
|
|
17656
17996
|
os: `${type()} ${hostname()}`,
|
|
17657
17997
|
hostname: hostname(),
|
|
17658
17998
|
shell: env.SHELL || env.ComSpec || "unknown",
|
|
@@ -17666,10 +18006,10 @@ class UserProfile {
|
|
|
17666
18006
|
if (!existsSync33(this.profileDir)) {
|
|
17667
18007
|
mkdirSync16(this.profileDir, { recursive: true });
|
|
17668
18008
|
}
|
|
17669
|
-
writeFileSync13(
|
|
18009
|
+
writeFileSync13(join28(this.profileDir, "profile.json"), JSON.stringify({ ...this.info, preferences: this.preferences }, null, 2), "utf-8");
|
|
17670
18010
|
}
|
|
17671
18011
|
load() {
|
|
17672
|
-
const path =
|
|
18012
|
+
const path = join28(this.profileDir, "profile.json");
|
|
17673
18013
|
if (!existsSync33(path))
|
|
17674
18014
|
return null;
|
|
17675
18015
|
try {
|
|
@@ -17708,8 +18048,8 @@ class UserProfile {
|
|
|
17708
18048
|
var init_profile = () => {};
|
|
17709
18049
|
|
|
17710
18050
|
// src/modules/skills/loader.ts
|
|
17711
|
-
import { readdirSync as
|
|
17712
|
-
import { join as
|
|
18051
|
+
import { readdirSync as readdirSync13, readFileSync as readFileSync21, existsSync as existsSync34, statSync as statSync6 } from "fs";
|
|
18052
|
+
import { join as join29 } from "path";
|
|
17713
18053
|
|
|
17714
18054
|
class SkillsLoader {
|
|
17715
18055
|
loadFromDir(dirPath) {
|
|
@@ -17720,9 +18060,9 @@ class SkillsLoader {
|
|
|
17720
18060
|
return skills;
|
|
17721
18061
|
}
|
|
17722
18062
|
scanDir(dirPath, skills) {
|
|
17723
|
-
const entries =
|
|
18063
|
+
const entries = readdirSync13(dirPath);
|
|
17724
18064
|
for (const entry of entries) {
|
|
17725
|
-
const fullPath =
|
|
18065
|
+
const fullPath = join29(dirPath, entry);
|
|
17726
18066
|
const stat = statSync6(fullPath);
|
|
17727
18067
|
if (stat.isDirectory()) {
|
|
17728
18068
|
this.scanDir(fullPath, skills);
|
|
@@ -17966,10 +18306,10 @@ var init_browser2 = __esm(() => {
|
|
|
17966
18306
|
});
|
|
17967
18307
|
|
|
17968
18308
|
// src/modules/lsp/command.ts
|
|
17969
|
-
import { delimiter, join as
|
|
18309
|
+
import { delimiter, join as join30 } from "path";
|
|
17970
18310
|
import { existsSync as existsSync35 } from "fs";
|
|
17971
|
-
import { platform as
|
|
17972
|
-
function resolveSpawnCommand(command, platformName =
|
|
18311
|
+
import { platform as platform6 } from "os";
|
|
18312
|
+
function resolveSpawnCommand(command, platformName = platform6(), pathEnv = process.env.PATH ?? "") {
|
|
17973
18313
|
if (platformName !== "win32")
|
|
17974
18314
|
return command;
|
|
17975
18315
|
if (command.includes("/") || command.includes("\\") || WIN_EXTS.some((ext) => command.toLowerCase().endsWith(ext))) {
|
|
@@ -17978,7 +18318,7 @@ function resolveSpawnCommand(command, platformName = platform5(), pathEnv = proc
|
|
|
17978
18318
|
const dirs = pathEnv.split(delimiter).filter(Boolean);
|
|
17979
18319
|
for (const dir of dirs) {
|
|
17980
18320
|
for (const ext of WIN_EXTS) {
|
|
17981
|
-
const candidate =
|
|
18321
|
+
const candidate = join30(dir, `${command}${ext}`);
|
|
17982
18322
|
if (existsSync35(candidate))
|
|
17983
18323
|
return `${command}${ext}`;
|
|
17984
18324
|
}
|
|
@@ -18006,7 +18346,7 @@ import {
|
|
|
18006
18346
|
execSync as execSync2
|
|
18007
18347
|
} from "child_process";
|
|
18008
18348
|
import { resolve as resolve19 } from "path";
|
|
18009
|
-
import { platform as
|
|
18349
|
+
import { platform as platform7 } from "os";
|
|
18010
18350
|
|
|
18011
18351
|
class LspClient {
|
|
18012
18352
|
process = null;
|
|
@@ -18069,7 +18409,7 @@ class LspClient {
|
|
|
18069
18409
|
}
|
|
18070
18410
|
return new Promise((resolve20, reject) => {
|
|
18071
18411
|
const args = config.args ?? [];
|
|
18072
|
-
const isWin =
|
|
18412
|
+
const isWin = platform7() === "win32";
|
|
18073
18413
|
let spawnCommand = resolveSpawnCommand(config.command);
|
|
18074
18414
|
let spawnArgs = args;
|
|
18075
18415
|
const spawnOpts = {
|
|
@@ -18359,8 +18699,8 @@ var init_lsp = __esm(() => {
|
|
|
18359
18699
|
});
|
|
18360
18700
|
|
|
18361
18701
|
// src/modules/indexer/walker.ts
|
|
18362
|
-
import { readdirSync as
|
|
18363
|
-
import { join as
|
|
18702
|
+
import { readdirSync as readdirSync14, readFileSync as readFileSync22, statSync as statSync7, existsSync as existsSync37, watch } from "fs";
|
|
18703
|
+
import { join as join31, relative as relative2, extname as extname5 } from "path";
|
|
18364
18704
|
|
|
18365
18705
|
class Indexer {
|
|
18366
18706
|
baseDir;
|
|
@@ -18391,14 +18731,14 @@ class Indexer {
|
|
|
18391
18731
|
return;
|
|
18392
18732
|
let entries;
|
|
18393
18733
|
try {
|
|
18394
|
-
entries =
|
|
18734
|
+
entries = readdirSync14(dir);
|
|
18395
18735
|
} catch {
|
|
18396
18736
|
return;
|
|
18397
18737
|
}
|
|
18398
18738
|
for (const entry of entries) {
|
|
18399
18739
|
if (count >= this.MAX_FILES)
|
|
18400
18740
|
return;
|
|
18401
|
-
const fullPath =
|
|
18741
|
+
const fullPath = join31(dir, entry);
|
|
18402
18742
|
const relPath = relative2(this.baseDir, fullPath);
|
|
18403
18743
|
const stat = statSync7(fullPath);
|
|
18404
18744
|
if (stat.isDirectory()) {
|
|
@@ -18462,13 +18802,13 @@ var init_walker = __esm(() => {
|
|
|
18462
18802
|
|
|
18463
18803
|
// src/modules/indexer/cache.ts
|
|
18464
18804
|
import { readFileSync as readFileSync23, writeFileSync as writeFileSync14, existsSync as existsSync38, mkdirSync as mkdirSync17, rmSync as rmSync3 } from "fs";
|
|
18465
|
-
import { join as
|
|
18805
|
+
import { join as join32 } from "path";
|
|
18466
18806
|
|
|
18467
18807
|
class IndexCache {
|
|
18468
18808
|
cachePath;
|
|
18469
18809
|
cache = null;
|
|
18470
18810
|
constructor(cacheDir) {
|
|
18471
|
-
this.cachePath =
|
|
18811
|
+
this.cachePath = join32(cacheDir, "index-cache.json");
|
|
18472
18812
|
}
|
|
18473
18813
|
load() {
|
|
18474
18814
|
if (this.cache)
|
|
@@ -18484,7 +18824,7 @@ class IndexCache {
|
|
|
18484
18824
|
}
|
|
18485
18825
|
save(result) {
|
|
18486
18826
|
this.cache = result;
|
|
18487
|
-
const dir =
|
|
18827
|
+
const dir = join32(this.cachePath, "..");
|
|
18488
18828
|
if (!existsSync38(dir))
|
|
18489
18829
|
mkdirSync17(dir, { recursive: true });
|
|
18490
18830
|
writeFileSync14(this.cachePath, JSON.stringify(result), "utf-8");
|
|
@@ -18502,10 +18842,10 @@ var init_cache = () => {};
|
|
|
18502
18842
|
|
|
18503
18843
|
// src/modules/indexer/project-profile.ts
|
|
18504
18844
|
import { readFileSync as readFileSync24, existsSync as existsSync39 } from "fs";
|
|
18505
|
-
import { join as
|
|
18845
|
+
import { join as join33 } from "path";
|
|
18506
18846
|
function detectManifest(baseDir) {
|
|
18507
18847
|
for (const manifest of MANIFEST_ORDER) {
|
|
18508
|
-
if (existsSync39(
|
|
18848
|
+
if (existsSync39(join33(baseDir, manifest)))
|
|
18509
18849
|
return manifest;
|
|
18510
18850
|
}
|
|
18511
18851
|
return null;
|
|
@@ -18522,7 +18862,7 @@ function cleanDependency(entry) {
|
|
|
18522
18862
|
}
|
|
18523
18863
|
function readPackageJson(baseDir) {
|
|
18524
18864
|
try {
|
|
18525
|
-
const raw = JSON.parse(readFileSync24(
|
|
18865
|
+
const raw = JSON.parse(readFileSync24(join33(baseDir, "package.json"), "utf-8"));
|
|
18526
18866
|
if (!raw || typeof raw !== "object")
|
|
18527
18867
|
return null;
|
|
18528
18868
|
const profile = {
|
|
@@ -18546,7 +18886,7 @@ function readPackageJson(baseDir) {
|
|
|
18546
18886
|
}
|
|
18547
18887
|
function readPyproject(baseDir) {
|
|
18548
18888
|
try {
|
|
18549
|
-
const content = readFileSync24(
|
|
18889
|
+
const content = readFileSync24(join33(baseDir, "pyproject.toml"), "utf-8");
|
|
18550
18890
|
const profile = { runtime: "python", deps: [], devDeps: [], scripts: {} };
|
|
18551
18891
|
const nameMatch = content.match(/^\s*name\s*=\s*"([^"]+)"/m);
|
|
18552
18892
|
if (nameMatch)
|
|
@@ -18562,7 +18902,7 @@ function readPyproject(baseDir) {
|
|
|
18562
18902
|
}
|
|
18563
18903
|
function readCargo(baseDir) {
|
|
18564
18904
|
try {
|
|
18565
|
-
const content = readFileSync24(
|
|
18905
|
+
const content = readFileSync24(join33(baseDir, "Cargo.toml"), "utf-8");
|
|
18566
18906
|
const profile = { runtime: "rust", deps: [], devDeps: [], scripts: {} };
|
|
18567
18907
|
const nameMatch = content.match(/^\s*name\s*=\s*"([^"]+)"/m);
|
|
18568
18908
|
if (nameMatch)
|
|
@@ -18586,7 +18926,7 @@ function readCargo(baseDir) {
|
|
|
18586
18926
|
}
|
|
18587
18927
|
function readGoMod(baseDir) {
|
|
18588
18928
|
try {
|
|
18589
|
-
const content = readFileSync24(
|
|
18929
|
+
const content = readFileSync24(join33(baseDir, "go.mod"), "utf-8");
|
|
18590
18930
|
const profile = { runtime: "go", deps: [], devDeps: [], scripts: {} };
|
|
18591
18931
|
const moduleMatch = content.match(/^module\s+(\S+)/m);
|
|
18592
18932
|
if (moduleMatch)
|
|
@@ -18604,7 +18944,7 @@ function readGoMod(baseDir) {
|
|
|
18604
18944
|
}
|
|
18605
18945
|
function readRequirements(baseDir) {
|
|
18606
18946
|
try {
|
|
18607
|
-
const content = readFileSync24(
|
|
18947
|
+
const content = readFileSync24(join33(baseDir, "requirements.txt"), "utf-8");
|
|
18608
18948
|
const profile = { runtime: "python", deps: [], devDeps: [], scripts: {} };
|
|
18609
18949
|
for (const line of content.split(`
|
|
18610
18950
|
`)) {
|
|
@@ -19058,7 +19398,7 @@ var init_mcp = __esm(() => {
|
|
|
19058
19398
|
|
|
19059
19399
|
// src/modules/memory/module.ts
|
|
19060
19400
|
import { homedir as homedir10 } from "os";
|
|
19061
|
-
import { join as
|
|
19401
|
+
import { join as join34 } from "path";
|
|
19062
19402
|
|
|
19063
19403
|
class MemoryModule {
|
|
19064
19404
|
name = "memory";
|
|
@@ -19067,7 +19407,7 @@ class MemoryModule {
|
|
|
19067
19407
|
if (storeOrDir instanceof MemoryStore) {
|
|
19068
19408
|
this.store = storeOrDir;
|
|
19069
19409
|
} else {
|
|
19070
|
-
const dir = storeOrDir ||
|
|
19410
|
+
const dir = storeOrDir || join34(homedir10(), ".mma", "memory");
|
|
19071
19411
|
this.store = new MemoryStore(dir);
|
|
19072
19412
|
}
|
|
19073
19413
|
}
|
|
@@ -19159,7 +19499,7 @@ __export(exports_bootstrap, {
|
|
|
19159
19499
|
bootstrap: () => bootstrap
|
|
19160
19500
|
});
|
|
19161
19501
|
import { homedir as homedir11 } from "os";
|
|
19162
|
-
import { join as
|
|
19502
|
+
import { join as join35, resolve as resolve21 } from "path";
|
|
19163
19503
|
import { existsSync as existsSync40, readFileSync as readFileSync25, writeFileSync as writeFileSync15 } from "fs";
|
|
19164
19504
|
function buildSystemInfo(config, baseDir, profileCompressed) {
|
|
19165
19505
|
const now = new Date().toISOString().replace("T", " ").slice(0, 19);
|
|
@@ -19186,8 +19526,8 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
|
|
|
19186
19526
|
`);
|
|
19187
19527
|
}
|
|
19188
19528
|
async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
19189
|
-
const dir = configDir ||
|
|
19190
|
-
const projectConfigPath = projectDir ?
|
|
19529
|
+
const dir = configDir || join35(homedir11(), ".mma");
|
|
19530
|
+
const projectConfigPath = projectDir ? join35(projectDir, ".mmrc") : join35(process.cwd(), ".mmrc");
|
|
19191
19531
|
const config = loadConfig({ configDir: dir, projectConfigPath });
|
|
19192
19532
|
setLocale(config.locale);
|
|
19193
19533
|
try {
|
|
@@ -19197,7 +19537,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
19197
19537
|
}
|
|
19198
19538
|
} catch {}
|
|
19199
19539
|
const logger = new Logger(config.logLevel);
|
|
19200
|
-
logger.setLogDir(
|
|
19540
|
+
logger.setLogDir(join35(dir, "logs"));
|
|
19201
19541
|
logger.debug("MMA bootstrap", {
|
|
19202
19542
|
version: config.version,
|
|
19203
19543
|
model: config.model
|
|
@@ -19219,7 +19559,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
19219
19559
|
logger.info(`Model ${config.model} loaded in ${loadResult.loadTime}s`);
|
|
19220
19560
|
}
|
|
19221
19561
|
}
|
|
19222
|
-
const profile = new UserProfile(
|
|
19562
|
+
const profile = new UserProfile(join35(dir));
|
|
19223
19563
|
profile.load() || profile.collect();
|
|
19224
19564
|
profile.save();
|
|
19225
19565
|
const llmProvider = new OpenAICompatProvider({
|
|
@@ -19231,7 +19571,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
19231
19571
|
rateLimits: config.security?.rateLimits
|
|
19232
19572
|
});
|
|
19233
19573
|
const baseDir = projectDir ? resolve21(projectDir) : process.cwd();
|
|
19234
|
-
const projectMapCacheDir =
|
|
19574
|
+
const projectMapCacheDir = join35(baseDir, ".mma");
|
|
19235
19575
|
const indexerModule = new IndexerModule({
|
|
19236
19576
|
baseDir,
|
|
19237
19577
|
cacheDir: projectMapCacheDir
|
|
@@ -19242,9 +19582,9 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
19242
19582
|
logger.warn(`Project indexing failed: ${err.message}`);
|
|
19243
19583
|
}
|
|
19244
19584
|
const skillsLoader = new SkillsLoader;
|
|
19245
|
-
const builtinDir =
|
|
19246
|
-
const globalDir =
|
|
19247
|
-
const projectSkillsDir =
|
|
19585
|
+
const builtinDir = join35(import.meta.dirname, "skills", "builtin");
|
|
19586
|
+
const globalDir = join35(homedir11(), ".agents", "skills");
|
|
19587
|
+
const projectSkillsDir = join35(baseDir, ".mma", "skills");
|
|
19248
19588
|
const availableSkills = skillsLoader.loadFromAllSources(builtinDir, globalDir, projectSkillsDir);
|
|
19249
19589
|
const skillsBudget = Math.floor(config.contextWindow * config.skills.budget);
|
|
19250
19590
|
const skillsModule = new SkillsModule(availableSkills, skillsBudget);
|
|
@@ -19258,11 +19598,11 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
19258
19598
|
essential: true,
|
|
19259
19599
|
estimatedTokens: Math.ceil(systemInfoContent.length / 4)
|
|
19260
19600
|
};
|
|
19261
|
-
const agentsMdGlobal =
|
|
19601
|
+
const agentsMdGlobal = join35(dir, "AGENTS.md");
|
|
19262
19602
|
if (!existsSync40(agentsMdGlobal)) {
|
|
19263
19603
|
writeFileSync15(agentsMdGlobal, "", "utf-8");
|
|
19264
19604
|
}
|
|
19265
|
-
const sessionDir =
|
|
19605
|
+
const sessionDir = join35(dir, "sessions");
|
|
19266
19606
|
const sessionStore = new SessionStore(sessionDir);
|
|
19267
19607
|
sessionStore.init();
|
|
19268
19608
|
const sessionManager = new SessionManager(sessionStore, {
|
|
@@ -19329,7 +19669,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
19329
19669
|
const mcpModule = new MCPModule(config);
|
|
19330
19670
|
await mcpModule.initialize();
|
|
19331
19671
|
moduleRegistry.register(mcpModule);
|
|
19332
|
-
const memoryStore = new MemoryStore(
|
|
19672
|
+
const memoryStore = new MemoryStore(join35(dir, "memory"));
|
|
19333
19673
|
const memoryModule = new MemoryModule(memoryStore);
|
|
19334
19674
|
moduleRegistry.register(memoryModule);
|
|
19335
19675
|
if (config.browser.enabled) {
|
|
@@ -19380,8 +19720,8 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
19380
19720
|
pluginManager.register(plugin);
|
|
19381
19721
|
pluginManager.register(plugin2);
|
|
19382
19722
|
const pluginLoader = new PluginLoader;
|
|
19383
|
-
const globalPluginsDir =
|
|
19384
|
-
const projectPluginsDir =
|
|
19723
|
+
const globalPluginsDir = join35(homedir11(), ".mma", "plugins");
|
|
19724
|
+
const projectPluginsDir = join35(baseDir, ".mma", "plugins");
|
|
19385
19725
|
pluginLoader.loadFromDir(globalPluginsDir, pluginManager, logger);
|
|
19386
19726
|
pluginLoader.loadFromDir(projectPluginsDir, pluginManager, logger);
|
|
19387
19727
|
contextManager.onCompact = (summary) => {
|
|
@@ -19399,9 +19739,9 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
19399
19739
|
const skipAgentsMd = noAgentsMd === true;
|
|
19400
19740
|
if (!skipAgentsMd) {
|
|
19401
19741
|
const agentsMdCandidates = [
|
|
19402
|
-
|
|
19403
|
-
|
|
19404
|
-
|
|
19742
|
+
join35(baseDir, "AGENTS.md"),
|
|
19743
|
+
join35(baseDir, ".mma", "AGENTS.md"),
|
|
19744
|
+
join35(dir, "AGENTS.md")
|
|
19405
19745
|
];
|
|
19406
19746
|
for (const p of agentsMdCandidates) {
|
|
19407
19747
|
if (existsSync40(p)) {
|
|
@@ -20261,7 +20601,7 @@ __export(exports_manifest, {
|
|
|
20261
20601
|
});
|
|
20262
20602
|
import { existsSync as existsSync41, readFileSync as readFileSync26, mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "fs";
|
|
20263
20603
|
import { homedir as homedir13 } from "os";
|
|
20264
|
-
import { join as
|
|
20604
|
+
import { join as join37 } from "path";
|
|
20265
20605
|
function readManifest(path = MANIFEST_PATH) {
|
|
20266
20606
|
try {
|
|
20267
20607
|
if (existsSync41(path)) {
|
|
@@ -20272,7 +20612,7 @@ function readManifest(path = MANIFEST_PATH) {
|
|
|
20272
20612
|
return { version: 1, certifications: [] };
|
|
20273
20613
|
}
|
|
20274
20614
|
function saveManifest(m, path = MANIFEST_PATH) {
|
|
20275
|
-
mkdirSync18(
|
|
20615
|
+
mkdirSync18(join37(homedir13(), ".mma"), { recursive: true });
|
|
20276
20616
|
writeFileSync16(path, JSON.stringify(m, null, 2), "utf-8");
|
|
20277
20617
|
}
|
|
20278
20618
|
function upsertCertification(entry, path = MANIFEST_PATH) {
|
|
@@ -20307,7 +20647,7 @@ function getCertMark(model, providerUrl, currentVersion, path = MANIFEST_PATH) {
|
|
|
20307
20647
|
}
|
|
20308
20648
|
var MANIFEST_PATH;
|
|
20309
20649
|
var init_manifest = __esm(() => {
|
|
20310
|
-
MANIFEST_PATH =
|
|
20650
|
+
MANIFEST_PATH = join37(homedir13(), ".mma", "certifications.json");
|
|
20311
20651
|
});
|
|
20312
20652
|
|
|
20313
20653
|
// node_modules/yaml/dist/nodes/identity.js
|
|
@@ -27430,8 +27770,8 @@ var init_scenarios = __esm(() => {
|
|
|
27430
27770
|
});
|
|
27431
27771
|
|
|
27432
27772
|
// src/modules/certification/loader.ts
|
|
27433
|
-
import { existsSync as existsSync42, readdirSync as
|
|
27434
|
-
import { join as
|
|
27773
|
+
import { existsSync as existsSync42, readdirSync as readdirSync15, readFileSync as readFileSync27 } from "fs";
|
|
27774
|
+
import { join as join38 } from "path";
|
|
27435
27775
|
function validateScenario(s) {
|
|
27436
27776
|
const errors2 = [];
|
|
27437
27777
|
const isSkip = s.mode === "skip";
|
|
@@ -27481,11 +27821,11 @@ function loadScenarios(userDir) {
|
|
|
27481
27821
|
scenarios.push(s);
|
|
27482
27822
|
}
|
|
27483
27823
|
if (userDir && existsSync42(userDir)) {
|
|
27484
|
-
for (const file of
|
|
27824
|
+
for (const file of readdirSync15(userDir)) {
|
|
27485
27825
|
if (!file.endsWith(".yaml") && !file.endsWith(".yml"))
|
|
27486
27826
|
continue;
|
|
27487
27827
|
try {
|
|
27488
|
-
const raw = readFileSync27(
|
|
27828
|
+
const raw = readFileSync27(join38(userDir, file), "utf-8");
|
|
27489
27829
|
const data = $parse(raw);
|
|
27490
27830
|
const parsed = normalizeScenario(data, file);
|
|
27491
27831
|
const errs = validateScenario(parsed);
|
|
@@ -27539,7 +27879,7 @@ var init_loader3 = __esm(() => {
|
|
|
27539
27879
|
|
|
27540
27880
|
// src/modules/certification/fact-checker.ts
|
|
27541
27881
|
import { existsSync as existsSync43, readFileSync as readFileSync28, statSync as statSync8 } from "fs";
|
|
27542
|
-
import { join as
|
|
27882
|
+
import { join as join39 } from "path";
|
|
27543
27883
|
function checkSandbox(sandboxDir, checks, exitCode, output) {
|
|
27544
27884
|
const failures = [];
|
|
27545
27885
|
for (const check of checks) {
|
|
@@ -27556,13 +27896,13 @@ function runCheck(sandboxDir, check, exitCode, output) {
|
|
|
27556
27896
|
case "outputContains":
|
|
27557
27897
|
return output.includes(check.text);
|
|
27558
27898
|
case "fileExists":
|
|
27559
|
-
return isFile(
|
|
27899
|
+
return isFile(join39(sandboxDir, check.path));
|
|
27560
27900
|
case "fileNotExists":
|
|
27561
|
-
return !existsSync43(
|
|
27901
|
+
return !existsSync43(join39(sandboxDir, check.path));
|
|
27562
27902
|
case "dirExists":
|
|
27563
|
-
return isDir(
|
|
27903
|
+
return isDir(join39(sandboxDir, check.path));
|
|
27564
27904
|
case "fileContent": {
|
|
27565
|
-
const abs =
|
|
27905
|
+
const abs = join39(sandboxDir, check.path);
|
|
27566
27906
|
if (!isFile(abs))
|
|
27567
27907
|
return false;
|
|
27568
27908
|
const content = readFileSync28(abs, "utf-8");
|
|
@@ -27573,7 +27913,7 @@ function runCheck(sandboxDir, check, exitCode, output) {
|
|
|
27573
27913
|
return false;
|
|
27574
27914
|
}
|
|
27575
27915
|
case "fileRegex": {
|
|
27576
|
-
const abs =
|
|
27916
|
+
const abs = join39(sandboxDir, check.path);
|
|
27577
27917
|
if (!isFile(abs))
|
|
27578
27918
|
return false;
|
|
27579
27919
|
return new RegExp(check.pattern).test(readFileSync28(abs, "utf-8"));
|
|
@@ -27623,8 +27963,8 @@ var init_fact_checker = () => {};
|
|
|
27623
27963
|
// src/modules/certification/runner.ts
|
|
27624
27964
|
import { spawn as spawn7 } from "child_process";
|
|
27625
27965
|
import { existsSync as existsSync44, mkdirSync as mkdirSync19, rmSync as rmSync4, cpSync as cpSync2 } from "fs";
|
|
27626
|
-
import { platform as
|
|
27627
|
-
import { join as
|
|
27966
|
+
import { platform as platform8 } from "os";
|
|
27967
|
+
import { join as join40, resolve as resolve22, dirname as dirname12 } from "path";
|
|
27628
27968
|
async function runScenario(scenario, opts) {
|
|
27629
27969
|
if (scenario.mode === "skip") {
|
|
27630
27970
|
return {
|
|
@@ -27643,7 +27983,7 @@ async function runScenario(scenario, opts) {
|
|
|
27643
27983
|
let passed = 0;
|
|
27644
27984
|
let firstError;
|
|
27645
27985
|
for (let i = 1;i <= reps; i++) {
|
|
27646
|
-
const sandbox =
|
|
27986
|
+
const sandbox = join40(opts.sandboxBase, `run-${scenario.id}-${i}`);
|
|
27647
27987
|
let failures = [];
|
|
27648
27988
|
let exitCode = -1;
|
|
27649
27989
|
let output = "";
|
|
@@ -27704,20 +28044,20 @@ function prepareSandbox(sandbox, scenario, mmaRoot) {
|
|
|
27704
28044
|
rmSync4(sandbox, { recursive: true, force: true });
|
|
27705
28045
|
mkdirSync19(sandbox, { recursive: true });
|
|
27706
28046
|
for (const f of scenario.fixtures ?? []) {
|
|
27707
|
-
const src =
|
|
28047
|
+
const src = join40(mmaRoot, f.source);
|
|
27708
28048
|
if (!existsSync44(src)) {
|
|
27709
28049
|
throw new Error(`fixture missing: ${f.source}`);
|
|
27710
28050
|
}
|
|
27711
|
-
const dest =
|
|
28051
|
+
const dest = join40(sandbox, f.dest);
|
|
27712
28052
|
mkdirSync19(dirname12(dest), { recursive: true });
|
|
27713
28053
|
cpSync2(src, dest);
|
|
27714
28054
|
}
|
|
27715
28055
|
}
|
|
27716
28056
|
function resolveMmaEntry(mmaRoot) {
|
|
27717
|
-
const dev =
|
|
28057
|
+
const dev = join40(mmaRoot, "src", "cli", "main.ts");
|
|
27718
28058
|
if (existsSync44(dev))
|
|
27719
28059
|
return dev;
|
|
27720
|
-
return
|
|
28060
|
+
return join40(mmaRoot, "dist", "main.js");
|
|
27721
28061
|
}
|
|
27722
28062
|
function findMmaRoot(fromDir) {
|
|
27723
28063
|
const candidates = [
|
|
@@ -27725,7 +28065,7 @@ function findMmaRoot(fromDir) {
|
|
|
27725
28065
|
resolve22(fromDir, "..")
|
|
27726
28066
|
];
|
|
27727
28067
|
for (const c of candidates) {
|
|
27728
|
-
if (existsSync44(
|
|
28068
|
+
if (existsSync44(join40(c, "package.json")))
|
|
27729
28069
|
return c;
|
|
27730
28070
|
}
|
|
27731
28071
|
return process.cwd();
|
|
@@ -27734,7 +28074,7 @@ function killTree2(child) {
|
|
|
27734
28074
|
const pid = child.pid;
|
|
27735
28075
|
if (!pid)
|
|
27736
28076
|
return;
|
|
27737
|
-
if (
|
|
28077
|
+
if (platform8() === "win32") {
|
|
27738
28078
|
spawn7("taskkill", ["/pid", String(pid), "/T", "/F"], {
|
|
27739
28079
|
windowsHide: true,
|
|
27740
28080
|
stdio: "ignore"
|
|
@@ -27793,11 +28133,11 @@ __export(exports_cli, {
|
|
|
27793
28133
|
});
|
|
27794
28134
|
import { rmSync as rmSync5 } from "fs";
|
|
27795
28135
|
import { homedir as homedir14 } from "os";
|
|
27796
|
-
import { join as
|
|
28136
|
+
import { join as join41, dirname as dirname13 } from "path";
|
|
27797
28137
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
27798
28138
|
import { existsSync as existsSync45, readFileSync as readFileSync29 } from "fs";
|
|
27799
28139
|
function readVersion() {
|
|
27800
|
-
const candidates = [
|
|
28140
|
+
const candidates = [join41(MMA_ROOT, "package.json")];
|
|
27801
28141
|
for (const p of candidates) {
|
|
27802
28142
|
if (existsSync45(p)) {
|
|
27803
28143
|
try {
|
|
@@ -27837,7 +28177,7 @@ async function certify(opts) {
|
|
|
27837
28177
|
return;
|
|
27838
28178
|
}
|
|
27839
28179
|
console.log(t("cli.cert_started", { model: opts.name, provider: providerUrl }));
|
|
27840
|
-
const sandboxBase =
|
|
28180
|
+
const sandboxBase = join41(process.cwd(), ".mma", "certification");
|
|
27841
28181
|
const results = [];
|
|
27842
28182
|
const total = selected.length;
|
|
27843
28183
|
let idx = 0;
|
|
@@ -27951,7 +28291,7 @@ var init_cli = __esm(() => {
|
|
|
27951
28291
|
init_manifest();
|
|
27952
28292
|
HERE = dirname13(fileURLToPath2(import.meta.url));
|
|
27953
28293
|
MMA_ROOT = findMmaRoot(HERE);
|
|
27954
|
-
USER_SCENARIO_DIR =
|
|
28294
|
+
USER_SCENARIO_DIR = join41(homedir14(), ".mma", "certification", "scenarios");
|
|
27955
28295
|
});
|
|
27956
28296
|
|
|
27957
28297
|
// src/cli/repl-commands.ts
|
|
@@ -27960,15 +28300,15 @@ __export(exports_repl_commands, {
|
|
|
27960
28300
|
registerAllCommands: () => registerAllCommands,
|
|
27961
28301
|
COMMAND_GROUPS: () => COMMAND_GROUPS
|
|
27962
28302
|
});
|
|
27963
|
-
import { join as
|
|
28303
|
+
import { join as join43, dirname as dirname15 } from "path";
|
|
27964
28304
|
import { homedir as homedir16 } from "os";
|
|
27965
28305
|
import { existsSync as existsSync47, readFileSync as readFileSync31 } from "fs";
|
|
27966
28306
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
27967
28307
|
function readVersion3() {
|
|
27968
28308
|
const here = dirname15(fileURLToPath4(import.meta.url));
|
|
27969
28309
|
const candidates = [
|
|
27970
|
-
|
|
27971
|
-
|
|
28310
|
+
join43(here, "..", "..", "package.json"),
|
|
28311
|
+
join43(here, "..", "package.json")
|
|
27972
28312
|
];
|
|
27973
28313
|
for (const p of candidates) {
|
|
27974
28314
|
if (existsSync47(p)) {
|
|
@@ -28136,7 +28476,7 @@ function registerMmaCommands(ctx) {
|
|
|
28136
28476
|
console.log(pc2.yellow(t("repl.wizard_running")));
|
|
28137
28477
|
await ctx.withExclusiveInput(async () => {
|
|
28138
28478
|
const answers = await runSetup(ctx.rl);
|
|
28139
|
-
const configPath =
|
|
28479
|
+
const configPath = join43(homedir16(), ".mma", "config.json");
|
|
28140
28480
|
ctx.config.provider.type = answers.provider;
|
|
28141
28481
|
ctx.config.provider.baseUrl = answers.apiBase;
|
|
28142
28482
|
ctx.config.provider.apiKey = answers.apiKey;
|
|
@@ -28190,7 +28530,7 @@ Excluded blocks: ${info.excluded.length}`));
|
|
|
28190
28530
|
return;
|
|
28191
28531
|
}
|
|
28192
28532
|
ctx.config.provider.type = name;
|
|
28193
|
-
const configPath =
|
|
28533
|
+
const configPath = join43(homedir16(), ".mma", "config.json");
|
|
28194
28534
|
saveConfig(ctx.config, configPath);
|
|
28195
28535
|
await ctx.agent.reconfigure(ctx.config);
|
|
28196
28536
|
console.log(pc2.green(t("repl.provider_set", { name })));
|
|
@@ -28246,7 +28586,7 @@ Excluded blocks: ${info.excluded.length}`));
|
|
|
28246
28586
|
return;
|
|
28247
28587
|
}
|
|
28248
28588
|
ctx.config.model = name;
|
|
28249
|
-
const configPath =
|
|
28589
|
+
const configPath = join43(homedir16(), ".mma", "config.json");
|
|
28250
28590
|
saveConfig(ctx.config, configPath);
|
|
28251
28591
|
await ctx.agent.reconfigure(ctx.config);
|
|
28252
28592
|
console.log(pc2.green(t("repl.model_set", { name })));
|
|
@@ -28271,7 +28611,7 @@ Excluded blocks: ${info.excluded.length}`));
|
|
|
28271
28611
|
return;
|
|
28272
28612
|
}
|
|
28273
28613
|
ctx.config.contextWindow = size;
|
|
28274
|
-
const configPath =
|
|
28614
|
+
const configPath = join43(homedir16(), ".mma", "config.json");
|
|
28275
28615
|
saveConfig(ctx.config, configPath);
|
|
28276
28616
|
await ctx.agent.reconfigure(ctx.config);
|
|
28277
28617
|
console.log(pc2.green(t("cli.context_set", { size })));
|
|
@@ -28290,10 +28630,10 @@ Excluded blocks: ${info.excluded.length}`));
|
|
|
28290
28630
|
ctx.agent.shutdown();
|
|
28291
28631
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config2(), exports_config));
|
|
28292
28632
|
const { homedir: homedir17 } = await import("os");
|
|
28293
|
-
const { join:
|
|
28633
|
+
const { join: join44 } = await import("path");
|
|
28294
28634
|
const configDir = ctx.configDir;
|
|
28295
28635
|
const baseDir = ctx.baseDir;
|
|
28296
|
-
const projectConfigPath =
|
|
28636
|
+
const projectConfigPath = join44(baseDir, ".mmrc");
|
|
28297
28637
|
const freshConfig = loadConfig2({ configDir, projectConfigPath });
|
|
28298
28638
|
Object.assign(ctx.config, freshConfig);
|
|
28299
28639
|
const { bootstrap: bootstrap2 } = await Promise.resolve().then(() => (init_bootstrap(), exports_bootstrap));
|
|
@@ -28593,14 +28933,14 @@ init_bootstrap();
|
|
|
28593
28933
|
init_config2();
|
|
28594
28934
|
init_setup();
|
|
28595
28935
|
init_i18n();
|
|
28596
|
-
import { join as
|
|
28936
|
+
import { join as join42, dirname as dirname14 } from "path";
|
|
28597
28937
|
import { homedir as homedir15 } from "os";
|
|
28598
28938
|
import { existsSync as existsSync46, readFileSync as readFileSync30 } from "fs";
|
|
28599
28939
|
|
|
28600
28940
|
// src/cli/security-commands.ts
|
|
28601
28941
|
init_bootstrap();
|
|
28602
28942
|
init_config2();
|
|
28603
|
-
import { join as
|
|
28943
|
+
import { join as join36 } from "path";
|
|
28604
28944
|
import { homedir as homedir12 } from "os";
|
|
28605
28945
|
|
|
28606
28946
|
// src/modules/security/security-policies.ts
|
|
@@ -29130,7 +29470,7 @@ function createSecurityCommand(program2) {
|
|
|
29130
29470
|
}
|
|
29131
29471
|
});
|
|
29132
29472
|
securityCmd.command("set-policy").argument("<preset>", t("cli.security.preset")).description(t("cli.security.set_policy")).action(async (preset) => {
|
|
29133
|
-
const configPath =
|
|
29473
|
+
const configPath = join36(homedir12(), ".mma", "config.json");
|
|
29134
29474
|
const { config: appConfig } = await bootstrap();
|
|
29135
29475
|
const validPresets = ["strict", "balanced", "permissive"];
|
|
29136
29476
|
if (!validPresets.includes(preset)) {
|
|
@@ -29145,7 +29485,7 @@ function createSecurityCommand(program2) {
|
|
|
29145
29485
|
console.log(t("cli.security.policy_description", { description: policy.description }));
|
|
29146
29486
|
});
|
|
29147
29487
|
securityCmd.command("enable-encryption").description(t("cli.security.enable_encryption")).action(async () => {
|
|
29148
|
-
const configPath =
|
|
29488
|
+
const configPath = join36(homedir12(), ".mma", "config.json");
|
|
29149
29489
|
const { config: appConfig } = await bootstrap();
|
|
29150
29490
|
appConfig.security = appConfig.security || {};
|
|
29151
29491
|
appConfig.security.sessionEncryption = {
|
|
@@ -29157,7 +29497,7 @@ function createSecurityCommand(program2) {
|
|
|
29157
29497
|
console.log(t("cli.security.encryption_enabled"));
|
|
29158
29498
|
});
|
|
29159
29499
|
securityCmd.command("disable-encryption").description(t("cli.security.disable_encryption")).action(async () => {
|
|
29160
|
-
const configPath =
|
|
29500
|
+
const configPath = join36(homedir12(), ".mma", "config.json");
|
|
29161
29501
|
const { config: appConfig } = await bootstrap();
|
|
29162
29502
|
appConfig.security = appConfig.security || {};
|
|
29163
29503
|
appConfig.security.sessionEncryption = {
|
|
@@ -29169,7 +29509,7 @@ function createSecurityCommand(program2) {
|
|
|
29169
29509
|
console.log(t("cli.security.encryption_disabled"));
|
|
29170
29510
|
});
|
|
29171
29511
|
securityCmd.command("enable-audit").description(t("cli.security.enable_audit")).action(async () => {
|
|
29172
|
-
const configPath =
|
|
29512
|
+
const configPath = join36(homedir12(), ".mma", "config.json");
|
|
29173
29513
|
const { config: appConfig } = await bootstrap();
|
|
29174
29514
|
appConfig.security = appConfig.security || {};
|
|
29175
29515
|
appConfig.security.auditNotifier = {
|
|
@@ -29183,7 +29523,7 @@ function createSecurityCommand(program2) {
|
|
|
29183
29523
|
console.log(t("cli.security.audit_enabled"));
|
|
29184
29524
|
});
|
|
29185
29525
|
securityCmd.command("disable-audit").description(t("cli.security.disable_audit")).action(async () => {
|
|
29186
|
-
const configPath =
|
|
29526
|
+
const configPath = join36(homedir12(), ".mma", "config.json");
|
|
29187
29527
|
const { config: appConfig } = await bootstrap();
|
|
29188
29528
|
appConfig.security = appConfig.security || {};
|
|
29189
29529
|
appConfig.security.auditNotifier = {
|
|
@@ -29219,8 +29559,8 @@ import { fileURLToPath as fileURLToPath3 } from "url";
|
|
|
29219
29559
|
function readVersion2() {
|
|
29220
29560
|
const here = dirname14(fileURLToPath3(import.meta.url));
|
|
29221
29561
|
const candidates = [
|
|
29222
|
-
|
|
29223
|
-
|
|
29562
|
+
join42(here, "..", "..", "package.json"),
|
|
29563
|
+
join42(here, "..", "package.json")
|
|
29224
29564
|
];
|
|
29225
29565
|
for (const p of candidates) {
|
|
29226
29566
|
if (existsSync46(p)) {
|
|
@@ -29238,7 +29578,7 @@ function createProgram() {
|
|
|
29238
29578
|
const program2 = new Command().name("mma").description(t("cli.description")).version(version).option("--no-agents-md", t("cli.no_agents_md")).option("-d, --dir <path>", t("cli.dir")).option("-e, --exit-on-complete", t("cli.exit_on_complete")).option("-j, --json", t("cli.json"));
|
|
29239
29579
|
program2.command("init").description(t("cli.init")).action(async () => {
|
|
29240
29580
|
const answers = await runSetup();
|
|
29241
|
-
const configPath =
|
|
29581
|
+
const configPath = join42(homedir15(), ".mma", "config.json");
|
|
29242
29582
|
const { config } = await bootstrap();
|
|
29243
29583
|
config.provider.type = answers.provider;
|
|
29244
29584
|
config.provider.baseUrl = answers.apiBase;
|
|
@@ -29283,7 +29623,7 @@ function createProgram() {
|
|
|
29283
29623
|
});
|
|
29284
29624
|
const configCmd = program2.command("config").description(t("cli.manage_config"));
|
|
29285
29625
|
configCmd.command("set").argument("<key>", t("cli.config_key")).argument("<value>", "Config value").description(t("cli.set_value")).action(async (key, value) => {
|
|
29286
|
-
const configPath =
|
|
29626
|
+
const configPath = join42(homedir15(), ".mma", "config.json");
|
|
29287
29627
|
const { config } = await bootstrap();
|
|
29288
29628
|
const keys = key.split(".");
|
|
29289
29629
|
let obj = config;
|
|
@@ -29346,7 +29686,7 @@ function createProgram() {
|
|
|
29346
29686
|
console.log(t("cli.model_hint"));
|
|
29347
29687
|
});
|
|
29348
29688
|
model.command("use").argument("<name>", "Model name").description(t("cli.set_model")).action(async (name) => {
|
|
29349
|
-
const configPath =
|
|
29689
|
+
const configPath = join42(homedir15(), ".mma", "config.json");
|
|
29350
29690
|
const { config } = await bootstrap();
|
|
29351
29691
|
config.model = name;
|
|
29352
29692
|
saveConfig(config, configPath);
|
|
@@ -29382,7 +29722,7 @@ function createProgram() {
|
|
|
29382
29722
|
await uncertify2(name, config);
|
|
29383
29723
|
});
|
|
29384
29724
|
program2.command("context").description(t("cli.manage_context")).argument("<size>", "Context window size in tokens").action(async (size) => {
|
|
29385
|
-
const configPath =
|
|
29725
|
+
const configPath = join42(homedir15(), ".mma", "config.json");
|
|
29386
29726
|
const { config } = await bootstrap();
|
|
29387
29727
|
const contextWindow = parseInt(size, 10);
|
|
29388
29728
|
if (isNaN(contextWindow) || contextWindow < 1024) {
|
|
@@ -29400,7 +29740,7 @@ function createProgram() {
|
|
|
29400
29740
|
console.log(t("cli.base_url"), config.provider.baseUrl);
|
|
29401
29741
|
});
|
|
29402
29742
|
provider.command("use").argument("<name>", "Provider name").description(t("cli.set_provider")).action(async (name) => {
|
|
29403
|
-
const configPath =
|
|
29743
|
+
const configPath = join42(homedir15(), ".mma", "config.json");
|
|
29404
29744
|
const { config } = await bootstrap();
|
|
29405
29745
|
config.provider.type = name;
|
|
29406
29746
|
saveConfig(config, configPath);
|
|
@@ -30171,7 +30511,7 @@ class LineEditor {
|
|
|
30171
30511
|
|
|
30172
30512
|
// src/cli/repl.ts
|
|
30173
30513
|
import { existsSync as existsSync48, readFileSync as readFileSync32, writeFileSync as writeFileSync17 } from "fs";
|
|
30174
|
-
import { join as
|
|
30514
|
+
import { join as join44, dirname as dirname16 } from "path";
|
|
30175
30515
|
import { homedir as homedir17 } from "os";
|
|
30176
30516
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
30177
30517
|
|
|
@@ -30677,8 +31017,8 @@ init_repl_commands();
|
|
|
30677
31017
|
function readVersion4() {
|
|
30678
31018
|
const here = dirname16(fileURLToPath5(import.meta.url));
|
|
30679
31019
|
const candidates = [
|
|
30680
|
-
|
|
30681
|
-
|
|
31020
|
+
join44(here, "..", "..", "package.json"),
|
|
31021
|
+
join44(here, "..", "package.json")
|
|
30682
31022
|
];
|
|
30683
31023
|
for (const p of candidates) {
|
|
30684
31024
|
if (existsSync48(p)) {
|
|
@@ -30739,10 +31079,10 @@ class Repl {
|
|
|
30739
31079
|
this.skillsModule = skillsModule;
|
|
30740
31080
|
this.pluginManager = pluginManager;
|
|
30741
31081
|
this.logger = logger;
|
|
30742
|
-
this.configDir = configDir ||
|
|
31082
|
+
this.configDir = configDir || join44(homedir17(), ".mma");
|
|
30743
31083
|
this.baseDir = baseDir || process.cwd();
|
|
30744
31084
|
this.noAgentsMd = noAgentsMd === true;
|
|
30745
|
-
this.historyPath =
|
|
31085
|
+
this.historyPath = join44(homedir17(), ".mma", "repl-history");
|
|
30746
31086
|
this.loadHistory();
|
|
30747
31087
|
this.rl = process.stdin.isTTY ? new LineEditor({
|
|
30748
31088
|
input: process.stdin,
|
|
@@ -31134,9 +31474,9 @@ ${t("image.clipboard_empty")}`));
|
|
|
31134
31474
|
row(t("repl.agents_label"), pc2.red(t("repl.disabled")));
|
|
31135
31475
|
} else {
|
|
31136
31476
|
const agentsMdCandidates = [
|
|
31137
|
-
|
|
31138
|
-
|
|
31139
|
-
|
|
31477
|
+
join44(this.baseDir, "AGENTS.md"),
|
|
31478
|
+
join44(this.baseDir, ".mma", "AGENTS.md"),
|
|
31479
|
+
join44(this.configDir, "AGENTS.md")
|
|
31140
31480
|
];
|
|
31141
31481
|
const foundAgents = agentsMdCandidates.filter((p) => existsSync48(p));
|
|
31142
31482
|
if (foundAgents.length > 0) {
|
|
@@ -31149,7 +31489,7 @@ ${t("image.clipboard_empty")}`));
|
|
|
31149
31489
|
}
|
|
31150
31490
|
const meta = this.sessionManager?.getActiveMeta();
|
|
31151
31491
|
if (meta) {
|
|
31152
|
-
const sessionPath =
|
|
31492
|
+
const sessionPath = join44(this.configDir, "sessions", meta.id);
|
|
31153
31493
|
row(t("repl.session_label"), `${pc2.cyan(meta.name)} ${pc2.dim(`(${meta.id.slice(0, 12)})`)} — ${meta.messageCount} msgs ${pc2.dim(sessionPath)}`);
|
|
31154
31494
|
}
|
|
31155
31495
|
const headerWidth = Math.max(50, Math.min(96, process.stdout.columns || 96));
|
|
@@ -31191,13 +31531,13 @@ init_config2();
|
|
|
31191
31531
|
init_i18n();
|
|
31192
31532
|
init_colors();
|
|
31193
31533
|
import { existsSync as existsSync49, readFileSync as readFileSync33 } from "fs";
|
|
31194
|
-
import { join as
|
|
31534
|
+
import { join as join45, dirname as dirname17 } from "path";
|
|
31195
31535
|
import { homedir as homedir18 } from "os";
|
|
31196
31536
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
31197
31537
|
|
|
31198
31538
|
// src/modules/updater/checker.ts
|
|
31199
31539
|
init_command();
|
|
31200
|
-
import { platform as
|
|
31540
|
+
import { platform as platform9 } from "os";
|
|
31201
31541
|
var defaultRunner2 = async (command, args, options) => {
|
|
31202
31542
|
const { execFile } = await import("child_process");
|
|
31203
31543
|
return new Promise((resolve23) => {
|
|
@@ -31259,7 +31599,7 @@ class Updater {
|
|
|
31259
31599
|
buildInstallCommand(latest) {
|
|
31260
31600
|
const npmArgs = ["install", "-g", `${this.packageName}@${latest}`];
|
|
31261
31601
|
const command = resolveSpawnCommand("npm");
|
|
31262
|
-
if (
|
|
31602
|
+
if (platform9() === "win32" && /\.(cmd|bat)$/i.test(command)) {
|
|
31263
31603
|
return { command: "cmd.exe", args: ["/c", command, ...npmArgs] };
|
|
31264
31604
|
}
|
|
31265
31605
|
return { command, args: npmArgs };
|
|
@@ -31355,8 +31695,8 @@ class UpdaterModule {
|
|
|
31355
31695
|
function readVersion5() {
|
|
31356
31696
|
const here = dirname17(fileURLToPath6(import.meta.url));
|
|
31357
31697
|
const candidates = [
|
|
31358
|
-
|
|
31359
|
-
|
|
31698
|
+
join45(here, "..", "..", "package.json"),
|
|
31699
|
+
join45(here, "..", "package.json")
|
|
31360
31700
|
];
|
|
31361
31701
|
for (const p of candidates) {
|
|
31362
31702
|
if (existsSync49(p)) {
|
|
@@ -31432,15 +31772,15 @@ async function main() {
|
|
|
31432
31772
|
agent.shutdown();
|
|
31433
31773
|
process.exit(exitCode);
|
|
31434
31774
|
} else {
|
|
31435
|
-
const configPath =
|
|
31775
|
+
const configPath = join45(homedir18(), ".mma", "config.json");
|
|
31436
31776
|
if (!existsSync49(configPath)) {
|
|
31437
31777
|
console.log(pc2.yellow(`
|
|
31438
31778
|
` + t("cli.first_run") + `
|
|
31439
31779
|
`));
|
|
31440
31780
|
const answers = await runSetup();
|
|
31441
31781
|
const config2 = loadConfig({
|
|
31442
|
-
configDir:
|
|
31443
|
-
projectConfigPath: projectDir ?
|
|
31782
|
+
configDir: join45(homedir18(), ".mma"),
|
|
31783
|
+
projectConfigPath: projectDir ? join45(projectDir, ".mmrc") : join45(process.cwd(), ".mmrc")
|
|
31444
31784
|
});
|
|
31445
31785
|
config2.provider.type = answers.provider;
|
|
31446
31786
|
config2.provider.baseUrl = answers.apiBase;
|